diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/AUTHORS ns-3.38/AUTHORS
--- ns-3.37/AUTHORS	2022-11-01 17:37:24.359394800 -0700
+++ ns-3.38/AUTHORS	2023-03-16 20:54:23.484868000 -0700
@@ -72,6 +72,7 @@
 Christoph Döpmann (doepmanc@informatik.hu-berlin.de)
 Jordan Dorham (dorham1@llnl.gov)
 Craig Dowell (craigdo@ee.washington.edu)
+Matteo Drago (matteo.drago1994@gmail.com)
 Gilaras Drakeson (gilaras@gmail.com)
 Jared Dulmage (jared.dulmage@wpli.net)
 Mathias Ettinger (mettinger@toulouse.viveris.com)
@@ -209,13 +210,13 @@
 Anh Nguyen (annguyen@ittc.ku.edu)
 Duy Nguyen (duy@soe.ucsc.edu)
 Alfonso Oliveira (af.oliveira.16@gmail.com)
+Wouter Overmeire (lodagro@gmail.com)
 Luis Pacheco (luisbelem@gmail.com)
+Matteo Pagin (mattpagg@gmail.com)
 Jendaipou Palmei (jendaipoupalmei@gmail.com)
-Lluís Parcerisa (parcerisa@gmail.com)
 Parth Pandya (parthpandyappp@gmail.com)
+Lluís Parcerisa (parcerisa@gmail.com)
 Mingyu Park (darkpmg@naver.com)
-Jendaipou Palmei (jendaipoupalmei@gmail.com)
-Parth Pandya (parthpandyappp@gmail.com)
 Harsh Patel (thadodaharsh10@gmail.com)
 Natale Patriciello (natale.patriciello@gmail.com)
 Pavinberg (pavin0702@gmail.com)
@@ -295,10 +296,11 @@
 Dave Taht (dave.taht@bufferbloat.net)
 Marcos Talau (talau@users.sourceforge.net)
 Adrian S. W. Tam (adrian.sw.tam@gmail.com)
+Bill Tao (toyjet8@gmail.com)
 Cristiano Tapparello (cristiano.tapparello@rochester.edu)
 Hajime Tazaki (tazaki@sfc.wide.ad.jp)
 Wilson Thong (wilsonwk@ee.cityu.edu.hk)
-Lars Toenning (lars.toenning@gmail.com)
+Lars Toenning (dev@ltoenning.de)
 Omer Topal (omer.topal@airties.com)
 Mauro Tortonesi (mauro.tortonesi@unife.it)
 Quincy Tse (quincy.tse@gmail.com)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/bindings/python/ns__init__.py ns-3.38/bindings/python/ns__init__.py
--- ns-3.37/bindings/python/ns__init__.py	2022-11-01 17:37:24.367394700 -0700
+++ ns-3.38/bindings/python/ns__init__.py	2023-03-16 20:54:23.492868200 -0700
@@ -74,6 +74,10 @@
     library_search_paths += [os.path.dirname(library_search_paths[-1])]
     library_search_paths += [os.path.dirname(library_search_paths[-1])]
 
+    # Filter unique search paths and those that are not part of system directories
+    library_search_paths = list(filter(lambda x: x not in SYSTEM_LIBRARY_DIRECTORIES,
+                                       set(library_search_paths)))
+
     # Search for the core library in the search paths
     libraries = []
     for search_path in library_search_paths:
@@ -82,6 +86,7 @@
     # 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)
 
     del search_path, library_search_paths
 
@@ -343,7 +348,8 @@
         for linked_lib_include_dir in extract_library_include_dirs(library, prefix):
             if linked_lib_include_dir not in known_include_dirs:
                 known_include_dirs.add(linked_lib_include_dir)
-                cppyy.add_include_path(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)
@@ -390,17 +396,6 @@
 
     cppyy.gbl.ns3.Node.__del__ = Node_del
 
-    # Define ns.cppyy.gbl.addressFromIpv4Address and others
-    cppyy.cppdef("""using namespace ns3;
-                    Address addressFromIpv4Address(Ipv4Address ip){ return Address(ip); };
-                    Address addressFromInetSocketAddress(InetSocketAddress addr){ return Address(addr); };
-                    Address addressFromPacketSocketAddress(PacketSocketAddress addr){ return Address(addr); };
-                    """)
-    # Expose addressFromIpv4Address as a member of the ns3 namespace (equivalent to ns)
-    setattr(cppyy.gbl.ns3, "addressFromIpv4Address", cppyy.gbl.addressFromIpv4Address)
-    setattr(cppyy.gbl.ns3, "addressFromInetSocketAddress", cppyy.gbl.addressFromInetSocketAddress)
-    setattr(cppyy.gbl.ns3, "addressFromPacketSocketAddress", cppyy.gbl.addressFromPacketSocketAddress)
-
     cppyy.cppdef("""
         using namespace ns3;
         std::tuple<bool, TypeId> LookupByNameFailSafe(std::string name)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/3rd-party/colored-messages.cmake ns-3.38/build-support/3rd-party/colored-messages.cmake
--- ns-3.37/build-support/3rd-party/colored-messages.cmake	2022-11-01 17:37:24.367394700 -0700
+++ ns-3.38/build-support/3rd-party/colored-messages.cmake	2023-03-16 20:54:23.492868200 -0700
@@ -71,25 +71,25 @@
   # Replace the default message macro with a custom colored one
   function(message)
     list(GET ARGV 0 MessageType)
+    list(REMOVE_AT ARGV 0)
+
+    # Transform list of arguments into a single line.
+    string(REPLACE ";" " " ARGV "${ARGV}")
+
     if((${MessageType} STREQUAL FATAL_ERROR) OR (${MessageType} STREQUAL
                                                  SEND_ERROR)
     )
-      list(REMOVE_AT ARGV 0)
       _message(${MessageType} "${BoldRed}${ARGV}${ColourReset}")
     elseif(MessageType STREQUAL WARNING)
-      list(REMOVE_AT ARGV 0)
       _message(${MessageType} "${Yellow}${ARGV}${ColourReset}")
     elseif(MessageType STREQUAL AUTHOR_WARNING)
-      list(REMOVE_AT ARGV 0)
       _message(${MessageType} "${BoldCyan}${ARGV}${ColourReset}")
     elseif(MessageType STREQUAL HIGHLIGHTED_STATUS) # Custom message type
-      list(REMOVE_AT ARGV 0)
       _message(STATUS "${Yellow}${ARGV}${ColourReset}")
     elseif(MessageType STREQUAL STATUS)
-      list(REMOVE_AT ARGV 0)
       _message(${MessageType} "${ARGV}")
     else()
-      _message(${ARGV})
+      _message(${MessageType} "${ARGV}")
     endif()
   endfunction()
 endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/3rd-party/FindEigen3.cmake ns-3.38/build-support/3rd-party/FindEigen3.cmake
--- ns-3.37/build-support/3rd-party/FindEigen3.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/build-support/3rd-party/FindEigen3.cmake	2023-03-16 20:54:23.492868200 -0700
@@ -0,0 +1,109 @@
+# cmake-format: off
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+#
+# and the following imported target:
+#
+#  Eigen3::Eigen - The header-only Eigen library
+#
+# This module reads hints about search locations from
+# the following environment variables:
+#
+# EIGEN3_ROOT
+# EIGEN3_ROOT_DIR
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif()
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif()
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif()
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif()
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else()
+    set(EIGEN3_VERSION_OK TRUE)
+  endif()
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  endif()
+endmacro()
+
+if (EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+  set(Eigen3_FOUND ${EIGEN3_VERSION_OK})
+
+else ()
+
+  # search first if an Eigen3Config.cmake is available in the system,
+  # if successful this would set EIGEN3_INCLUDE_DIR and the rest of
+  # the script will work as usual
+  find_package(Eigen3 ${Eigen3_FIND_VERSION} NO_MODULE QUIET)
+
+  if(NOT EIGEN3_INCLUDE_DIR)
+    find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+        HINTS
+        ENV EIGEN3_ROOT
+        ENV EIGEN3_ROOT_DIR
+        PATHS
+        ${CMAKE_INSTALL_PREFIX}/include
+        ${KDE4_INCLUDE_DIR}
+        PATH_SUFFIXES eigen3 eigen
+      )
+  endif()
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif()
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif()
+
+if(EIGEN3_FOUND AND NOT TARGET Eigen3::Eigen)
+  add_library(Eigen3::Eigen INTERFACE IMPORTED)
+  set_target_properties(Eigen3::Eigen PROPERTIES
+    INTERFACE_INCLUDE_DIRECTORIES "${EIGEN3_INCLUDE_DIR}")
+endif()
+# cmake-format: on
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/3rd-party/find-program-hints.cmake ns-3.38/build-support/3rd-party/find-program-hints.cmake
--- ns-3.37/build-support/3rd-party/find-program-hints.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/build-support/3rd-party/find-program-hints.cmake	2023-03-16 20:54:23.492868200 -0700
@@ -0,0 +1,20 @@
+set(3RD_PARTY_FIND_PROGRAM_HINTS
+    # find_program HINTS for Doxygen
+    # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\doxygen_is1;Inno Setup: App Path]/bin"
+    /Applications/Doxygen.app/Contents/Resources
+    /Applications/Doxygen.app/Contents/MacOS
+    /Applications/Utilities/Doxygen.app/Contents/Resources
+    /Applications/Utilities/Doxygen.app/Contents/MacOS
+    # find_program HINTS for Dia
+    # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake
+    "$ENV{ProgramFiles}/Dia"
+    "$ENV{ProgramFiles\(x86\)}/Dia"
+    # find_program HINTS for Graphviz
+    # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake
+    "$ENV{ProgramFiles}/ATT/Graphviz/bin"
+    "C:/Program Files/ATT/Graphviz/bin"
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ATT\\Graphviz;InstallPath]/bin"
+    /Applications/Graphviz.app/Contents/MacOS
+    /Applications/Utilities/Graphviz.app/Contents/MacOS
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/compiler-workarounds/ostream-operator-nullptr.h ns-3.38/build-support/compiler-workarounds/ostream-operator-nullptr.h
--- ns-3.37/build-support/compiler-workarounds/ostream-operator-nullptr.h	2022-11-01 17:37:24.367394700 -0700
+++ ns-3.38/build-support/compiler-workarounds/ostream-operator-nullptr.h	2023-03-16 20:54:23.492868200 -0700
@@ -7,7 +7,7 @@
 namespace std
 {
 inline std::ostream&
-operator<<(std::ostream& os, std::nullptr_t ptr)
+operator<<(std::ostream& os, std::nullptr_t)
 {
     return os << "nullptr"; // whatever you want nullptr to show up as in the console
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/custom-modules/ns3-configtable.cmake ns-3.38/build-support/custom-modules/ns3-configtable.cmake
--- ns-3.37/build-support/custom-modules/ns3-configtable.cmake	2022-11-01 17:37:24.367394700 -0700
+++ ns-3.38/build-support/custom-modules/ns3-configtable.cmake	2023-03-16 20:54:23.492868200 -0700
@@ -113,12 +113,18 @@
 endfunction()
 
 macro(write_configtable)
-  set(out "---- Summary of optional ns-3 features:\n")
+  set(out "---- Summary of ns-3 settings:\n")
   string(APPEND out "Build profile                 : ${build_profile}\n")
   string(APPEND out
          "Build directory               : ${CMAKE_OUTPUT_DIRECTORY}\n"
   )
 
+  string(APPEND out "Build with runtime asserts    : ")
+  check_on_or_off("${NS3_ASSERT}" "${NS3_ASSERT}")
+
+  string(APPEND out "Build with runtime logging    : ")
+  check_on_or_off("${NS3_LOG}" "${NS3_LOG}")
+
   string(APPEND out "Build version embedding       : ")
   check_on_or_off("${NS3_ENABLE_BUILD_VERSION}" "${ENABLE_BUILD_VERSION}")
 
@@ -170,6 +176,9 @@
   string(APPEND out "SQLite support                : ")
   check_on_or_off("${NS3_SQLITE}" "${ENABLE_SQLITE}")
 
+  string(APPEND out "Eigen3 support                : ")
+  check_on_or_off("${NS3_EIGEN}" "${ENABLE_EIGEN}")
+
   string(APPEND out "Tap Bridge                    : ")
   check_on_or_off("${ENABLE_TAP}" "${ENABLE_TAP}")
 
@@ -211,4 +220,8 @@
 
   file(WRITE ${PROJECT_BINARY_DIR}/ns3config.txt ${out})
   message(STATUS ${out})
+
+  if(NOT (${NS3RC} STREQUAL "NS3RC-NOTFOUND"))
+    message(STATUS "Applying configuration override from: ${NS3RC}")
+  endif()
 endmacro()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake ns-3.38/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake
--- ns-3.37/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake	2023-03-16 20:54:23.492868200 -0700
@@ -0,0 +1,75 @@
+include(ExternalProject)
+
+ExternalProject_Add(
+  brite_dep
+  URL https://code.nsnam.org/BRITE/archive/30338f4f63b9.zip
+  URL_HASH MD5=b36ecf8f6b5f2cfae936ba1f1bfcff5c
+  PREFIX brite_dep
+  BUILD_IN_SOURCE TRUE
+  CONFIGURE_COMMAND make
+  BUILD_COMMAND make
+  INSTALL_COMMAND make install PREFIX=${CMAKE_OUTPUT_DIRECTORY}
+)
+
+ExternalProject_Add(
+  click_dep
+  GIT_REPOSITORY https://github.com/kohler/click.git
+  GIT_TAG 9197a594b1c314264935106297aff08d97cbe7ee
+  PREFIX click_dep
+  BUILD_IN_SOURCE TRUE
+  UPDATE_DISCONNECTED TRUE
+  CONFIGURE_COMMAND ./configure --disable-linuxmodule --enable-nsclick
+                    --enable-wifi --prefix ${CMAKE_OUTPUT_DIRECTORY}
+  BUILD_COMMAND make -j${NumThreads}
+  INSTALL_COMMAND make install
+)
+
+ExternalProject_Add(
+  openflow_dep
+  URL https://code.nsnam.org/openflow/archive/d45e7d184151.zip
+  URL_HASH MD5=a068cdaec5523586921b2f1f81f10916
+  PREFIX openflow_dep
+  BUILD_IN_SOURCE TRUE
+  CONFIGURE_COMMAND ./waf configure --prefix ${CMAKE_OUTPUT_DIRECTORY}
+  BUILD_COMMAND ./waf build
+  INSTALL_COMMAND ./waf install
+)
+
+install(
+  DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/include/openflow
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include
+  USE_SOURCE_PERMISSIONS
+  PATTERN "openflow/*"
+)
+
+find_file(
+  BOOST_STATIC_ASSERT
+  NAMES static_assert.hpp
+  PATH_SUFFIXES boost
+  HINTS /usr/local
+)
+get_filename_component(boost_dir ${BOOST_STATIC_ASSERT} DIRECTORY)
+
+install(
+  DIRECTORY ${boost_dir}
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+  USE_SOURCE_PERMISSIONS
+  PATTERN "boost/*"
+)
+install(
+  DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/lib/
+  DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  USE_SOURCE_PERMISSIONS
+  PATTERN "lib/*"
+)
+
+macro(add_dependency_to_optional_modules_dependencies)
+  add_dependencies(${libbrite} brite_dep)
+  add_dependencies(${libclick} click_dep)
+  add_dependencies(${libopenflow} openflow_dep)
+  if(NOT ${XCODE})
+    add_dependencies(${libbrite}-obj brite_dep)
+    add_dependencies(${libclick}-obj click_dep)
+    add_dependencies(${libopenflow}-obj openflow_dep)
+  endif()
+endmacro()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/custom-modules/ns3-module-macros.cmake ns-3.38/build-support/custom-modules/ns3-module-macros.cmake
--- ns-3.37/build-support/custom-modules/ns3-module-macros.cmake	2022-11-01 17:37:24.367394700 -0700
+++ ns-3.38/build-support/custom-modules/ns3-module-macros.cmake	2023-03-16 20:54:23.492868200 -0700
@@ -125,7 +125,8 @@
     ${lib${BLIB_LIBNAME}}
     PROPERTIES
       PUBLIC_HEADER
-      "${BLIB_HEADER_FILES};${BLIB_DEPRECATED_HEADER_FILES};${config_headers};${BLIB_PRIVATE_HEADER_FILES};${CMAKE_HEADER_OUTPUT_DIRECTORY}/${BLIB_LIBNAME}-module.h"
+      "${BLIB_HEADER_FILES};${BLIB_DEPRECATED_HEADER_FILES};${config_headers};${CMAKE_HEADER_OUTPUT_DIRECTORY}/${BLIB_LIBNAME}-module.h"
+      PRIVATE_HEADER "${BLIB_PRIVATE_HEADER_FILES}"
       RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} # set output
                                                                  # directory for
                                                                  # DLLs
@@ -225,11 +226,35 @@
   # Write a module header that includes all headers from that module
   write_module_header("${BLIB_LIBNAME}" "${BLIB_HEADER_FILES}")
 
+  # Check if headers actually exist to prevent copying errors during
+  # installation
+  get_target_property(headers_to_check ${lib${BLIB_LIBNAME}} PUBLIC_HEADER)
+  set(missing_headers)
+  foreach(header ${headers_to_check})
+    if(NOT ((EXISTS ${header}) OR (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${header})
+           )
+    )
+      list(APPEND missing_headers ${header})
+    endif()
+  endforeach()
+  if(missing_headers)
+    message(
+      FATAL_ERROR "Missing header files for ${BLIB_LIBNAME}: ${missing_headers}"
+    )
+  endif()
+
   # Copy all header files to outputfolder/include before each build
   copy_headers_before_building_lib(
-    ${BLIB_LIBNAME} ${CMAKE_HEADER_OUTPUT_DIRECTORY}
-    "${BLIB_HEADER_FILES};${BLIB_PRIVATE_HEADER_FILES}" public
+    ${BLIB_LIBNAME} ${CMAKE_HEADER_OUTPUT_DIRECTORY} "${BLIB_HEADER_FILES}"
+    public
   )
+  if(BLIB_PRIVATE_HEADER_FILES)
+    copy_headers_before_building_lib(
+      ${BLIB_LIBNAME} ${CMAKE_HEADER_OUTPUT_DIRECTORY}
+      "${BLIB_PRIVATE_HEADER_FILES}" private
+    )
+  endif()
+
   if(BLIB_DEPRECATED_HEADER_FILES)
     copy_headers_before_building_lib(
       ${BLIB_LIBNAME} ${CMAKE_HEADER_OUTPUT_DIRECTORY}
@@ -337,6 +362,7 @@
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/
     RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/
     PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ns3"
+    PRIVATE_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ns3"
   )
   if(${NS3_VERBOSE})
     message(STATUS "Processed ${FOLDER}")
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/empty-main.cc ns-3.38/build-support/empty-main.cc
--- ns-3.37/build-support/empty-main.cc	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/build-support/empty-main.cc	2023-03-16 20:54:23.492868200 -0700
@@ -1,4 +1,5 @@
 int
 main()
 {
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/macros-and-definitions.cmake ns-3.38/build-support/macros-and-definitions.cmake
--- ns-3.37/build-support/macros-and-definitions.cmake	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/build-support/macros-and-definitions.cmake	2023-03-16 20:54:23.496868100 -0700
@@ -59,6 +59,12 @@
 
 if(APPLE)
   add_definitions(-D__APPLE__)
+  # cmake-format: off
+  # Configure find_program to search for AppBundles only if programs are not found in PATH.
+  # This prevents Doxywizard from being launched when the Doxygen.app is installed.
+  # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake
+  # cmake-format: on
+  set(CMAKE_FIND_APPBUNDLE "LAST")
 endif()
 
 if(WIN32)
@@ -135,6 +141,7 @@
 set(CMAKE_HEADER_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/include/ns3)
 set(THIRD_PARTY_DIRECTORY ${PROJECT_SOURCE_DIR}/3rd-party)
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib)
 
 # Get installation folder default values for each platform and include package
 # configuration macro
@@ -234,33 +241,35 @@
   set(STATIC_LINK_FLAGS)
 endif()
 
-# Search for faster linkers mold and lld, and use them if available
-mark_as_advanced(MOLD LLD)
-find_program(MOLD mold)
-find_program(LLD ld.lld)
-
-# USING_FAST_LINKER will be defined if a fast linker is being used and its
-# content will correspond to the fast linker name
-
-# Mold support was added in GCC 12.1.0
-if(NOT USING_FAST_LINKER
-   AND NOT (${MOLD} STREQUAL "MOLD-NOTFOUND")
-   AND LINUX
-   AND ${GCC}
-   AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)
-)
-  set(USING_FAST_LINKER MOLD)
-  add_link_options("-fuse-ld=mold")
-endif()
+if(${NS3_FAST_LINKERS})
+  # Search for faster linkers mold and lld, and use them if available
+  mark_as_advanced(MOLD LLD)
+  find_program(MOLD mold)
+  find_program(LLD ld.lld)
+
+  # USING_FAST_LINKER will be defined if a fast linker is being used and its
+  # content will correspond to the fast linker name
+
+  # Mold support was added in GCC 12.1.0
+  if(NOT USING_FAST_LINKER
+     AND NOT (${MOLD} STREQUAL "MOLD-NOTFOUND")
+     AND LINUX
+     AND ${GCC}
+     AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)
+  )
+    set(USING_FAST_LINKER MOLD)
+    add_link_options("-fuse-ld=mold")
+  endif()
 
-if(NOT USING_FAST_LINKER AND NOT (${LLD} STREQUAL "LLD-NOTFOUND")
-   AND (${GCC} OR ${CLANG})
-)
-  set(USING_FAST_LINKER LLD)
-  add_link_options("-fuse-ld=lld")
-  if(WIN32)
-    # Clear unsupported linker flags on Windows
-    set(LIB_AS_NEEDED_PRE)
+  if(NOT USING_FAST_LINKER AND NOT (${LLD} STREQUAL "LLD-NOTFOUND")
+     AND (${GCC} OR ${CLANG})
+  )
+    set(USING_FAST_LINKER LLD)
+    add_link_options("-fuse-ld=lld")
+    if(WIN32)
+      # Clear unsupported linker flags on Windows
+      set(LIB_AS_NEEDED_PRE)
+    endif()
   endif()
 endif()
 
@@ -317,6 +326,9 @@
   )
 endmacro()
 
+# Include CMake file with common find_program HINTS
+include(build-support/3rd-party/find-program-hints.cmake)
+
 # function used to search for package and program dependencies than store list
 # of missing dependencies in the list whose name is stored in missing_deps
 function(check_deps package_deps program_deps missing_deps)
@@ -335,7 +347,9 @@
     # here or it won't check other dependencies
     string(TOUPPER ${program} upper_${program})
     mark_as_advanced(${upper_${program}})
-    find_program(${upper_${program}} ${program})
+    find_program(
+      ${upper_${program}} ${program} HINTS ${3RD_PARTY_FIND_PROGRAM_HINTS}
+    )
     if("${${upper_${program}}}" STREQUAL "${upper_${program}}-NOTFOUND")
       list(APPEND local_missing_deps ${program})
     endif()
@@ -465,6 +479,18 @@
     if("${CLANG_TIDY}" STREQUAL "CLANG_TIDY-NOTFOUND")
       message(FATAL_ERROR "Clang-tidy was not found")
     else()
+      if((${CMAKE_VERSION} VERSION_LESS "3.12.0") AND ${NS3_CCACHE}
+         AND (NOT ("${CCACHE}" STREQUAL "CCACHE-NOTFOUND"))
+      )
+        # CMake <3.12 puts CMAKE_CXX_COMPILER_LAUNCHER in the incorrect place
+        # and CCache ends up being unable to cache anything if calling
+        # clang-tidy https://gitlab.kitware.com/cmake/cmake/-/issues/18266
+        message(
+          FATAL_ERROR
+            "The current CMake ${CMAKE_VERSION} won't ccache objects correctly when running with clang-tidy."
+            "Update CMake to at least version 3.12, or disable either ccache or clang-tidy to continue."
+        )
+      endif()
       set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY}")
     endif()
   else()
@@ -479,6 +505,7 @@
         ClangBuildAnalyzer
         GIT_REPOSITORY "https://github.com/aras-p/ClangBuildAnalyzer.git"
         GIT_TAG "47406981a1c5a89e8f8c62802b924c3e163e7cb4"
+        CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
         INSTALL_COMMAND cmake -E copy_if_different ClangBuildAnalyzer
                         ${PROJECT_BINARY_DIR}
       )
@@ -511,6 +538,7 @@
   else()
     file(GLOB_RECURSE MODULES_CMAKE_FILES src/**/CMakeLists.txt
          contrib/**/CMakeLists.txt examples/**/CMakeLists.txt
+         scratch/**/CMakeLists.txt
     )
     file(
       GLOB
@@ -688,6 +716,20 @@
   )
   list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/3rd-party")
 
+  set(ENABLE_EIGEN False)
+  if(${NS3_EIGEN})
+    find_package(Eigen3 QUIET)
+
+    if(${EIGEN3_FOUND})
+      set(ENABLE_EIGEN True)
+      add_definitions(-DHAVE_EIGEN3)
+      add_definitions(-DEIGEN_MPL2_ONLY)
+      include_directories(${EIGEN3_INCLUDE_DIR})
+    else()
+      message(${HIGHLIGHTED_STATUS} "Eigen was not found")
+    endif()
+  endif()
+
   # GTK3 Don't search for it if you don't have it installed, as it take an
   # insane amount of time
   if(${NS3_GTK3})
@@ -755,25 +797,35 @@
   set(Python3_EXECUTABLE)
   set(Python3_FOUND FALSE)
   set(Python3_INCLUDE_DIRS)
-  if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0")
-    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)
-    # cmake-format: on
-    find_package(PythonInterp)
-    find_package(PythonLibs)
+  if(${NS3_PYTHON_BINDINGS})
+    if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0")
+      find_package(Python3 COMPONENTS Interpreter Development)
+    else()
+      # cmake-format: off
+      set(Python_ADDITIONAL_VERSIONS 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9)
+      # cmake-format: on
+      find_package(PythonInterp)
+      find_package(PythonLibs)
 
-    # Move deprecated results into the FindPython3 resulting variables
-    set(Python3_Interpreter_FOUND ${PYTHONINTERP_FOUND})
-    set(Python3_Development_FOUND ${PYTHONLIBS_FOUND})
-    if(${PYTHONINTERP_FOUND})
-      set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
-      set(Python3_FOUND TRUE)
+      # Move deprecated results into the FindPython3 resulting variables
+      set(Python3_Interpreter_FOUND ${PYTHONINTERP_FOUND})
+      set(Python3_Development_FOUND ${PYTHONLIBS_FOUND})
+      if(${PYTHONINTERP_FOUND})
+        set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
+        set(Python3_FOUND TRUE)
+      endif()
+      if(${PYTHONLIBS_FOUND})
+        set(Python3_LIBRARIES ${PYTHON_LIBRARIES})
+        set(Python3_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
+      endif()
     endif()
-    if(${PYTHONLIBS_FOUND})
-      set(Python3_LIBRARIES ${PYTHON_LIBRARIES})
-      set(Python3_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
+  else()
+    # If Python was not set yet, use the version found by check_deps
+    check_deps("" "python3" python3_deps)
+    if(python3_deps)
+      message(FATAL_ERROR "Python3 was not found")
+    else()
+      set(Python3_EXECUTABLE ${PYTHON3})
     endif()
   endif()
 
@@ -866,6 +918,37 @@
     endif()
   endif()
 
+  if(${NS3_NINJA_TRACING})
+    if(${CMAKE_GENERATOR} STREQUAL Ninja)
+      include(ExternalProject)
+      ExternalProject_Add(
+        NinjaTracing
+        GIT_REPOSITORY "https://github.com/nico/ninjatracing.git"
+        GIT_TAG "f9d21e973cfdeafa913b83a927fef56258f70b9a"
+        CONFIGURE_COMMAND ""
+        BUILD_COMMAND ""
+        INSTALL_COMMAND ""
+      )
+      ExternalProject_Get_Property(NinjaTracing SOURCE_DIR)
+      set(embed_time_trace)
+      if(${NS3_CLANG_TIMETRACE} AND ${CLANG})
+        set(embed_time_trace --embed-time-trace)
+      endif()
+      add_custom_target(
+        ninjaTrace
+        COMMAND
+          ${Python3_EXECUTABLE} ${SOURCE_DIR}/ninjatracing -a
+          ${embed_time_trace} ${PROJECT_BINARY_DIR}/.ninja_log >
+          ${PROJECT_SOURCE_DIR}/ninja_performance_trace.json
+        DEPENDS NinjaTracing
+      )
+      unset(embed_time_trace)
+      unset(SOURCE_DIR)
+    else()
+      message(FATAL_ERROR "Ninjatracing requires the Ninja generator")
+    endif()
+  endif()
+
   # Disable the below warning from bindings built in debug mode with clang++:
   # "expression with side effects will be evaluated despite being used as an
   # operand to 'typeid'"
@@ -975,63 +1058,13 @@
     add_custom_target(doxygen-no-build COMMAND ${doxygen_missing_msg})
   else()
     # We checked this already exists, but we need the path to the executable
-    find_package(Doxygen QUIET)
-
-    # Get introspected doxygen
-    add_custom_target(
-      run-print-introspected-doxygen
-      COMMAND
-        ${CMAKE_OUTPUT_DIRECTORY}/utils/ns${NS3_VER}-print-introspected-doxygen${build_profile_suffix}
-        > ${PROJECT_SOURCE_DIR}/doc/introspected-doxygen.h
-      COMMAND
-        ${CMAKE_OUTPUT_DIRECTORY}/utils/ns${NS3_VER}-print-introspected-doxygen${build_profile_suffix}
-        --output-text > ${PROJECT_SOURCE_DIR}/doc/ns3-object.txt
-      DEPENDS print-introspected-doxygen
-    )
-    add_custom_target(
-      run-introspected-command-line
-      COMMAND ${CMAKE_COMMAND} -E env NS_COMMANDLINE_INTROSPECTION=..
-              ${Python3_EXECUTABLE} ./test.py --no-build --constrain=example
-      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
-      DEPENDS all-test-targets # all-test-targets only exists if ENABLE_TESTS is
-                               # set to ON
-    )
-
-    file(
-      WRITE ${CMAKE_BINARY_DIR}/introspected-command-line-preamble.h
-      "/* This file is automatically generated by
-  CommandLine::PrintDoxygenUsage() from the CommandLine configuration
-  in various example programs.  Do not edit this file!  Edit the
-  CommandLine configuration in those files instead.
-  */\n"
-    )
-    add_custom_target(
-      assemble-introspected-command-line
-      # works on CMake 3.18 or newer > COMMAND ${CMAKE_COMMAND} -E cat
-      # ${PROJECT_SOURCE_DIR}/testpy-output/*.command-line >
-      # ${PROJECT_SOURCE_DIR}/doc/introspected-command-line.h
-      COMMAND
-        ${cat_command} ${CMAKE_BINARY_DIR}/introspected-command-line-preamble.h
-        ${PROJECT_SOURCE_DIR}/testpy-output/*.command-line >
-        ${PROJECT_SOURCE_DIR}/doc/introspected-command-line.h 2> NULL
-      DEPENDS run-introspected-command-line
-    )
+    set(DOXYGEN_EXECUTABLE ${DOXYGEN})
 
     add_custom_target(
       update_doxygen_version
       COMMAND bash ${PROJECT_SOURCE_DIR}/doc/ns3_html_theme/get_version.sh
       WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
     )
-
-    add_custom_target(
-      doxygen
-      COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_SOURCE_DIR}/doc/doxygen.conf
-      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
-      DEPENDS update_doxygen_version run-print-introspected-doxygen
-              assemble-introspected-command-line
-      USES_TERMINAL
-    )
-
     add_custom_target(
       doxygen-no-build
       COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_SOURCE_DIR}/doc/doxygen.conf
@@ -1039,6 +1072,69 @@
       DEPENDS update_doxygen_version
       USES_TERMINAL
     )
+    # The `doxygen` target only really works if we have tests enabled, so emit a
+    # warning to use `doxygen-no-build` instead.
+    if((NOT ${ENABLE_TESTS}) AND (NOT ${ENABLE_EXAMPLES}))
+      # cmake-format: off
+      set(doxygen_target_requires_tests_msg
+              echo The \\'doxygen\\' target called by \\'./ns3 docs doxygen\\' or \\'./ns3 docs all\\' commands
+              require examples and tests to generate introspected documentation.
+              Enable examples and tests, or use \\'doxygen-no-build\\'.
+              )
+      # cmake-format: on
+      add_custom_target(doxygen COMMAND ${doxygen_target_requires_tests_msg})
+      unset(doxygen_target_requires_tests_msg)
+    else()
+      # Get introspected doxygen
+      add_custom_target(
+        run-print-introspected-doxygen
+        COMMAND
+          ${CMAKE_OUTPUT_DIRECTORY}/utils/ns${NS3_VER}-print-introspected-doxygen${build_profile_suffix}
+          > ${PROJECT_SOURCE_DIR}/doc/introspected-doxygen.h
+        COMMAND
+          ${CMAKE_OUTPUT_DIRECTORY}/utils/ns${NS3_VER}-print-introspected-doxygen${build_profile_suffix}
+          --output-text > ${PROJECT_SOURCE_DIR}/doc/ns3-object.txt
+        DEPENDS print-introspected-doxygen
+      )
+      add_custom_target(
+        run-introspected-command-line
+        COMMAND ${CMAKE_COMMAND} -E env NS_COMMANDLINE_INTROSPECTION=..
+                ${Python3_EXECUTABLE} ./test.py --no-build --constrain=example
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+        DEPENDS all-test-targets # all-test-targets only exists if ENABLE_TESTS
+                                 # is set to ON
+      )
+
+      file(
+        WRITE ${CMAKE_BINARY_DIR}/introspected-command-line-preamble.h
+        "/* This file is automatically generated by
+  CommandLine::PrintDoxygenUsage() from the CommandLine configuration
+  in various example programs.  Do not edit this file!  Edit the
+  CommandLine configuration in those files instead.
+  */\n"
+      )
+      add_custom_target(
+        assemble-introspected-command-line
+        # works on CMake 3.18 or newer > COMMAND ${CMAKE_COMMAND} -E cat
+        # ${PROJECT_SOURCE_DIR}/testpy-output/*.command-line >
+        # ${PROJECT_SOURCE_DIR}/doc/introspected-command-line.h
+        COMMAND
+          ${cat_command}
+          ${CMAKE_BINARY_DIR}/introspected-command-line-preamble.h
+          ${PROJECT_SOURCE_DIR}/testpy-output/*.command-line >
+          ${PROJECT_SOURCE_DIR}/doc/introspected-command-line.h 2> NULL
+        DEPENDS run-introspected-command-line
+      )
+
+      add_custom_target(
+        doxygen
+        COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_SOURCE_DIR}/doc/doxygen.conf
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+        DEPENDS update_doxygen_version run-print-introspected-doxygen
+                assemble-introspected-command-line
+        USES_TERMINAL
+      )
+    endif()
   endif()
 
   # Now we check for sphinx dependencies
@@ -1071,6 +1167,7 @@
     add_custom_target(sphinx_models COMMAND ${sphinx_missing_msg})
     add_custom_target(sphinx_tutorial COMMAND ${sphinx_missing_msg})
     add_custom_target(sphinx_contributing COMMAND ${sphinx_missing_msg})
+    add_custom_target(sphinx_installation COMMAND ${sphinx_missing_msg})
   else()
     add_custom_target(sphinx COMMENT "Building sphinx documents")
     mark_as_advanced(MAKE)
@@ -1118,6 +1215,7 @@
     sphinx_target(models)
     sphinx_target(tutorial)
     sphinx_target(contributing)
+    sphinx_target(installation)
   endif()
   # end of checking for documentation dependencies and creating targets
 
@@ -1128,9 +1226,8 @@
   if(${NS3_INT64X64} MATCHES "INT128")
     check_cxx_source_compiles(
       "#include <stdint.h>
-       int main(int argc, char **argv)
+       int main()
          {
-            (void)argc; (void)argv;
             if ((uint128_t *) 0) return 0;
             if (sizeof (uint128_t)) return 0;
             return 1;
@@ -1139,9 +1236,8 @@
     )
     check_cxx_source_compiles(
       "#include <stdint.h>
-       int main(int argc, char **argv)
+       int main()
          {
-           (void)argc; (void)argv;
            if ((__uint128_t *) 0) return 0;
            if (sizeof (__uint128_t)) return 0;
            return 1;
@@ -1278,8 +1374,35 @@
         "Clang-tidy is incompatible with precompiled headers. Continuing without them."
       )
     elseif(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
-      set(PRECOMPILE_HEADERS_ENABLED ON)
-      message(STATUS "Precompiled headers were enabled")
+      # If ccache is not enable or was not found, we can continue with
+      # precompiled headers
+      if((NOT ${NS3_CCACHE}) OR ("${CCACHE}" STREQUAL "CCACHE-NOTFOUND"))
+        set(PRECOMPILE_HEADERS_ENABLED ON)
+        message(STATUS "Precompiled headers were enabled")
+      else()
+        # If ccache was found, we need to check if it is ccache >= 4
+        execute_process(COMMAND ${CCACHE} -V OUTPUT_VARIABLE CCACHE_OUT)
+        # Extract ccache version
+        if(CCACHE_OUT MATCHES "ccache version ([0-9\.]*)")
+          # If an incompatible version is found, do not enable precompiled
+          # headers
+          if("${CMAKE_MATCH_1}" VERSION_LESS "4.0.0")
+            set(PRECOMPILE_HEADERS_ENABLED OFF)
+            message(
+              ${HIGHLIGHTED_STATUS}
+              "Precompiled headers are incompatible with ccache ${CMAKE_MATCH_1} and will be disabled."
+            )
+          else()
+            set(PRECOMPILE_HEADERS_ENABLED ON)
+            message(STATUS "Precompiled headers were enabled.")
+          endif()
+        else()
+          message(
+            FATAL_ERROR
+              "Failed to extract the ccache version while enabling precompiled headers."
+          )
+        endif()
+      endif()
     else()
       message(
         STATUS
@@ -1320,10 +1443,15 @@
         <unordered_map>
         <vector>
     )
-    add_library(stdlib_pch OBJECT ${PROJECT_SOURCE_DIR}/build-support/empty.cc)
+    add_library(
+      stdlib_pch${build_profile_suffix} OBJECT
+      ${PROJECT_SOURCE_DIR}/build-support/empty.cc
+    )
     target_precompile_headers(
-      stdlib_pch PUBLIC "${precompiled_header_libraries}"
+      stdlib_pch${build_profile_suffix} PUBLIC
+      "${precompiled_header_libraries}"
     )
+    add_library(stdlib_pch ALIAS stdlib_pch${build_profile_suffix})
 
     add_executable(
       stdlib_pch_exec ${PROJECT_SOURCE_DIR}/build-support/empty-main.cc
@@ -1360,6 +1488,12 @@
     )
     add_subdirectory(${netanim_SOURCE_DIR} ${netanim_BINARY_DIR})
   endif()
+
+  if(${NS3_FETCH_OPTIONAL_COMPONENTS})
+    include(
+      build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake
+    )
+  endif()
 endmacro()
 
 function(set_runtime_outputdirectory target_name output_directory target_prefix)
@@ -1420,6 +1554,17 @@
   endif()
 endfunction(set_runtime_outputdirectory)
 
+function(get_scratch_prefix prefix)
+  # /path/to/ns-3-dev/scratch/nested-subdir
+  set(temp ${CMAKE_CURRENT_SOURCE_DIR})
+  # remove /path/to/ns-3-dev/ to get scratch/nested-subdir
+  string(REPLACE "${PROJECT_SOURCE_DIR}/" "" temp "${temp}")
+  # replace path separators with underlines
+  string(REPLACE "/" "_" temp "${temp}")
+  # save the prefix value to the passed variable
+  set(${prefix} ${temp}_ PARENT_SCOPE)
+endfunction()
+
 function(build_exec)
   # Argument parsing
   set(options IGNORE_PCH STANDALONE)
@@ -1431,6 +1576,13 @@
     "BEXEC" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
   )
 
+  # Resolve nested scratch prefixes without user intervention
+  if("${CMAKE_CURRENT_SOURCE_DIR}" MATCHES "scratch"
+     AND "${BEXEC_EXECNAME_PREFIX}" STREQUAL ""
+  )
+    get_scratch_prefix(BEXEC_EXECNAME_PREFIX)
+  endif()
+
   add_executable(
     ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} "${BEXEC_SOURCE_FILES}"
   )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/build-support/version-defines-template.h ns-3.38/build-support/version-defines-template.h
--- ns-3.37/build-support/version-defines-template.h	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/build-support/version-defines-template.h	2023-03-16 20:54:23.496868100 -0700
@@ -1,4 +1,3 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
  * Copyright (c) 2018 Lawrence Livermore National Laboratory
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/CHANGES.md ns-3.38/CHANGES.md
--- ns-3.37/CHANGES.md	2022-11-01 17:39:10.947703100 -0700
+++ ns-3.38/CHANGES.md	2023-03-16 20:54:51.628957000 -0700
@@ -13,6 +13,48 @@
 
 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.37 to ns-3.38
+-------------------------------
+
+### New API
+
+* (core) Added new template classes `ValArray` and `MatrixArray` to represent efficiently 1D, 2D and 3D arrays. `ValArray` implements basic efficient storage of such structures and the basic operations, while `MatrixArray` allows to represent 3D arrays as arrays of mathematical matrices and invoke different mathematical operations on the arrays of matrices: multiplication, transpose, hermitian transpose, etc. `MatrixArray` can use Eigen to perform computationally complex operations.
+* (core) Added several macros in **warnings.h** to silence compiler warnings in specific sections of code. Their use is discouraged, unless really necessary.
+* (lr-wpan) Added beacon payload handle support (MLME-SET.request) in  **LrWpanMac**.
+* (lr-wpan) `LrWpanPhy::SetRxSensitivity` now supports the setting of Rx sensitivity.
+* (lr-wpan) `LrWpanNetDevice::SetPanAssociation` is introduced to create more complex topologies (multi-hop) using a manual association.
+* (netanim) Added a helper function to update the size of a node
+* (network) Added class `TimestampTag` for associating a timestamp with a packet.
+* (spectrum) A new fast-fading model `TwoRaySpectrumPropagationLossModel` has been added. This model serves as a performance-oriented alternative to the `ThreeGppSpectrumPropagationLossModel` and `ThreeGppChannelModel` classes, and it has been designed with the goal of providing end-to-end channel samples which are statistically close to the ones generated by the latter.
+* (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
+
+### Changes to existing API
+
+* (antenna, spectrum) `ComplexVector` definition has changed. Its API is implemented in `MatrixArray`. Some functions such as `push_back` and `resize` are not supported any more. On the other hand, the size initialization through constructor and access operator[] are maintained. Instead of `size ()` users can call `GetSize()`.
+* (internet) TCP Westwood model has been removed due to a bug in BW estimation documented in <https://gitlab.com/nsnam/ns-3-dev/-/issues/579>. The TCP Westwood+ model is now named **TcpWestwoodPlus** and can be instantiated like all the other TCP flavors.
+* (internet) `TcpCubic` attribute `HyStartDetect` changed from `int` to `enum HybridSSDetectionMode`.
+* (internet-apps) Added class `Ping` for a ping model that works for both IPv4 and IPv6.  Classes `v4Ping` and `Ping6` will be deprecated and removed in the future, replaced by the new `Ping` class.
+* (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).
+
+### Changes to build system
+
+* Added NinjaTracing support.
+* Check if the ccache version is equal or higher than 4.0 before enabling precompiled headers.
+* Improved bindings search for linked libraries and their include directories.
+* Added `./ns3 distclean` option. It removes the same build artifacts as `./ns3 clean`, along with documentation, python and test artifacts.
+
+### Changed behavior
+
+* (applications) **UdpClient** and **UdpEchoClient** MaxPackets attribute is aligned with other applications, in that the value zero means infinite packets.
+* (network) **Ipv4Address** and **Ipv6Address** now do not raise an exception if built from an invalid string. Instead the address is marked as not initialized.
+* (tests) The test runner test.py will exit if no TestSuite is specified.
+* (wifi) Control frames (specifically, BlockAckRequest and MU-BAR Trigger Frames) are stored in the wifi MAC queue and no longer in a dedicated BlockAckManager queue
+* (wifi) BSSIDs are no longer hashed by the ApInfo comparator because it may lead to different results on different platforms
+
 Changes from ns-3.36 to ns-3.37
 -------------------------------
 
@@ -379,7 +421,7 @@
 * It is now possible to know the size of the SpectrumValue underlying std::vector, as well as accessing read-only every element of it.
 * The `GetClosestSide` method of the Rectangle class returns the correct closest side also for positions outside the rectangle.
 * The trace sources `BackoffTrace` and `CwTrace` were moved from class QosTxop to base class Txop, allowing these values to be traced for DCF operation. In addition, the trace signature for BackoffTrace was changed from TracedValue to TracedCallback (callback taking one argument instead of two). Most users of CwTrace for QosTxop configurations will not need to change existing programs, but users of BackoffTrace will need to adjust the callback signature to match.
-* New trace sources, namely `DrbCreated`, `Srb1Created` and `DrbCreated` have beed implemented in LteEnbRrc and LteUeRrc classes repectively. These new traces are used to improve the connection of the RLC and PDCP stats in the RadioBearerStatsConnector API.
+* New trace sources, namely `DrbCreated`, `Srb1Created` and `DrbCreated` have been implemented in LteEnbRrc and LteUeRrc classes respectively. These new traces are used to improve the connection of the RLC and PDCP stats in the RadioBearerStatsConnector API.
 * `TraceFadingLossModel` has been moved from lte to spectrum module.
 
 ### Changes to build system
@@ -407,10 +449,10 @@
 * New `ATTACH_REQUEST` state to wait for finalization of the S1 signalling with the core network.
 * New InitialContextSetupRequest primitive of the S1 SAP that is received by the eNB RRC when the S1 signalling from the core network is finished.
 
-* A new buffer has been introduced in the LteEnbRrc class. This buffer will be used by a target eNB during handover to buffer the packets comming from a source eNB on X2 inteface. The target eNB will buffer this data until it receives RRC Connection Reconfiguration Complete from a UE.
+* A new buffer has been introduced in the LteEnbRrc class. This buffer will be used by a target eNB during handover to buffer the packets coming from a source eNB on X2 interface. The target eNB will buffer this data until it receives RRC Connection Reconfiguration Complete from a UE.
 * The default qdisc installed on single-queue devices (such as PointToPoint, Csma and Simple) is now `FqCoDel` (instead of PfifoFast). On multi-queue devices (such as Wifi), the default root qdisc is now `Mq` with as many FqCoDel child qdiscs as the number of device queues. The new defaults are motivated by the willingness to align with the behavior of major Linux distributions and by the need to preserve the effectiveness of Wifi EDCA Functions in differentiating Access Categories (see issue #35).
 * LTE RLC TM mode does not report anymore the layer-to-layer delay, as it misses (by standard) an header to which attach the timestamp tag. Users can switch to the PDCP layer delay measurements, which must be the same.
-* Token Bank Fair Queue Scheduler (`ns3::FdTbfqFfMacScheduler`) will not anymore schedule a UE, which does not have any RBG left after removng the RBG from its allocation map if the computed TB size is greater than the "budget" computed in the scheduler.
+* Token Bank Fair Queue Scheduler (`ns3::FdTbfqFfMacScheduler`) will not anymore schedule a UE, which does not have any RBG left after removing the RBG from its allocation map if the computed TB size is greater than the "budget" computed in the scheduler.
 * LTE module now supports the **Radio Link Failure (RLF)** functionality. This implementation introduced following key behavioral changes:
   * The UE RRC state will not remain in `CONNECTED_NORMALLY` state if the DL control channel SINR is below a set threshold.
   * The LTE RRC protocol APIs of UE i.e., LteUeRrcProtocolIdeal, LteUeRrcProtocolReal have been extended to send an ideal (i.e., using SAPs instead to transmitting over the air) UE context remove request to the eNB. Similarly, the eNB RRC protocol APIs, i.e, LteEnbRrcProtocolIdeal and LteEnbRrcProtocolReal have been extended to receive this ideal UE context remove request.
@@ -906,7 +948,7 @@
 * For the TapBridge device, in UseLocal mode there is a MAC learning function. TapBridge has been waiting for the first packet received from tap interface to set the address of the bridged device to the source address of the first packet. This has caused problems with WiFi. The new behavior is that after connection to the tap interface, ns-3 learns the MAC address of that interface with a system call and immediately sets the address of the bridged device to the learned one. See [bug 1777](https://www.nsnam.org/bugzilla/show_bug.cgi?id=1777) for more details.
 * TapBridge device now correctly implements IsLinkUp() method.
 * IPv6 addresses and routing tables are printed like in Linux `route -A inet6` command.
-* A change in `Ipv[4,6]Interface` enforces the correct behaviour of IP when a device do not support the minimum MTU requirements. This is set to 68 and 1280 octects respectively. IP simulations that may have run over devices with smaller MTUs than 68 or 1280, respectively, will no longer be able to use such devices.
+* A change in `Ipv[4,6]Interface` enforces the correct behaviour of IP when a device do not support the minimum MTU requirements. This is set to 68 and 1280 octets respectively. IP simulations that may have run over devices with smaller MTUs than 68 or 1280, respectively, will no longer be able to use such devices.
 
 Changes from ns-3.18 to ns-3.18.1
 ---------------------------------
@@ -947,7 +989,7 @@
   * 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
-* InternetStackHelper has two new functions:SetIpv4ArpJitter (bool enable) and SetIpv6NsRsJitter (bool enable) to enable/disable the random jitter on the tranmission of IPv4 ARP Request and IPv6 NS/RS.
+* 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.
 * New Mac16Address has been added. It can be used with IPv6 to make an Autoconfigured address.
@@ -1516,7 +1558,7 @@
 * **MPI Interface for distributed simulation:** Enables access to necessary MPI information such as MPI rank and size.
 * **Point-to-point remote channel:** Enables point-to-point connection between net-devices on different simulators, for use with distributed simulation.
 * **GetSystemId in simulator:** For use with distributed simulation, GetSystemId returns zero by non-distributed simulators. For the distributed simulator, it returns the MPI rank.
-* **Enhancements to src/core/random-variable.cc/h:** New Zeta random variable generator. The Zeta random distribution is tightly related to the Zipf distribution (already in ns-3.7). See the documentation, especially because sometimes the Zeta distribution is called Zipf and viceversa. Here we conform to the Wikipedia naming convention, i.e., Zipf is bounded while Zeta isn't.
+* **Enhancements to src/core/random-variable.cc/h:** New Zeta random variable generator. The Zeta random distribution is tightly related to the Zipf distribution (already in ns-3.7). See the documentation, especially because sometimes the Zeta distribution is called Zipf and vice-versa. Here we conform to the Wikipedia naming convention, i.e., Zipf is bounded while Zeta isn't.
 * **Two-ray ground propagation loss model:** Calculates the crossover distance under which Friis is used. The antenna height is set to the nodes z coordinate, but can be added to using the model parameter SetHeightAboveZ, which will affect ALL stations
 * **Pareto random variable** has two new constructors to specify scale and shape:
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/.clang-tidy ns-3.38/.clang-tidy
--- ns-3.37/.clang-tidy	2022-11-01 17:37:24.359394800 -0700
+++ ns-3.38/.clang-tidy	2023-03-16 20:54:23.484868000 -0700
@@ -29,17 +29,22 @@
   performance-inefficient-vector-operation,
   performance-trivially-destructible,
   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-subscript-expr,
+  readability-static-accessed-through-instance,
   readability-static-definition-in-anonymous-namespace,
   readability-string-compare,
   readability-uppercase-literal-suffix,
 
 FormatStyle: "file"
-HeaderFilterRegex: ".*h$"
+HeaderFilterRegex: ".*(ns|NS).*/(contrib|examples|src|scratch|utils)/*/.*h"
+UseColor: true
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/CMakeLists.txt ns-3.38/CMakeLists.txt
--- ns-3.37/CMakeLists.txt	2022-11-01 17:37:24.363394700 -0700
+++ ns-3.38/CMakeLists.txt	2023-03-16 20:54:23.488868200 -0700
@@ -3,19 +3,6 @@
 # ##############################################################################
 cmake_minimum_required(VERSION 3.10..3.10)
 
-# Use ccache if available
-mark_as_advanced(CCACHE)
-find_program(CCACHE ccache)
-if(NOT ("${CCACHE}" STREQUAL "CCACHE-NOTFOUND"))
-  message(STATUS "CCache is enabled.")
-  set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
-  execute_process(
-    COMMAND
-      ${CCACHE} --set-config
-      sloppiness=pch_defines,time_macros,include_file_mtime,include_file_ctime
-  )
-endif()
-
 # ##############################################################################
 # Project name                        #
 # ##############################################################################
@@ -66,6 +53,11 @@
 
 # other options
 option(NS3_ENABLE_BUILD_VERSION "Embed version info into libraries" OFF)
+option(NS3_CCACHE "Use Ccache to speed up recompilation" ON)
+option(NS3_FAST_LINKERS "Use Mold or LLD to speed up linking if available" ON)
+option(NS3_FETCH_OPTIONAL_COMPONENTS
+       "Fetch Brite, Click and Openflow dependencies" OFF
+)
 option(NS3_GSL "Build with GSL support" ON)
 option(NS3_GTK3 "Build with GTK3 support" ON)
 option(NS3_LINK_TIME_OPTIMIZATION "Build with link-time optimization" OFF)
@@ -74,12 +66,18 @@
 )
 option(NS3_MPI "Build with MPI support" OFF)
 option(NS3_NATIVE_OPTIMIZATIONS "Build with -march=native -mtune=native" OFF)
+option(
+  NS3_NINJA_TRACING
+  "Use ninjatracing to convert Ninja's build log to the about://tracing format"
+  OFF
+)
 set(NS3_OUTPUT_DIRECTORY "" CACHE STRING "Directory to store built artifacts")
 option(NS3_PRECOMPILE_HEADERS
        "Precompile module headers to speed up compilation" ON
 )
 option(NS3_PYTHON_BINDINGS "Build ns-3 python bindings" OFF)
 option(NS3_SQLITE "Build with SQLite support" ON)
+option(NS3_EIGEN "Build with Eigen support" ON)
 option(NS3_STATIC "Build a static ns-3 library and link it against executables"
        OFF
 )
@@ -106,6 +104,21 @@
       "List of modules that should have their examples and tests built (e.g. lte;wifi)"
 )
 
+if(${NS3_CCACHE})
+  # Use ccache if available
+  mark_as_advanced(CCACHE)
+  find_program(CCACHE ccache)
+  if(NOT ("${CCACHE}" STREQUAL "CCACHE-NOTFOUND"))
+    message(STATUS "CCache is enabled.")
+    set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
+    execute_process(
+      COMMAND
+        ${CCACHE} --set-config
+        sloppiness=pch_defines,time_macros,include_file_mtime,include_file_ctime
+    )
+  endif()
+endif()
+
 # Include macros used below
 include(build-support/macros-and-definitions.cmake)
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/CONTRIBUTING.md ns-3.38/CONTRIBUTING.md
--- ns-3.37/CONTRIBUTING.md	2022-11-01 17:37:24.363394700 -0700
+++ ns-3.38/CONTRIBUTING.md	2023-03-16 20:54:23.488868200 -0700
@@ -61,7 +61,7 @@
 
 ### Design Decisions
 
-When we make a significant decision in how we maintain the project and what we can or cannot support, we will document it through the [ns-developers mailing list](https://mailman.isi.edu/mailman/listinfo/ns-developers). If you have a question around how we do things, please subscribe and lurk around to see how we proceed. If you have a development problem, please open a new topic and ask your question.
+When we make a significant decision in how we maintain the project and what we can or cannot support, we will document it through the [ns-developers mailing list](https://groups.google.com/g/ns-developers). If you have a question around how we do things, please subscribe and lurk around to see how we proceed. If you have a development problem, please open a new topic and ask your question.
 
 ## How Can I Contribute?
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/contributing/source/coding-style.rst ns-3.38/doc/contributing/source/coding-style.rst
--- ns-3.37/doc/contributing/source/coding-style.rst	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/doc/contributing/source/coding-style.rst	2023-03-16 20:54:23.496868100 -0700
@@ -97,8 +97,7 @@
 =============================================
 
 To disable formatting in specific lines, surround them with the following
-C++ comments [example adopted from
-`official clang-format documentation <https://clang.llvm.org/docs/ClangFormat.html>`_]:
+C++ comments:
 
 .. sourcecode:: cpp
 
@@ -326,8 +325,8 @@
 .. sourcecode:: cpp
 
   MyClass::MyClass(int x, int y)
-      : m_x (x),
-        m_y (y)
+      : m_x(x),
+        m_y(y)
   {
   }
 
@@ -798,6 +797,70 @@
       MyClass(int n);
   };
 
+All the functions and variables must be documented, with the exception of
+member functions inherited from parent classes (the documentation is copied
+automatically from the parent class), and default constructor/destructor.
+
+It is strongly suggested to use grouping to bind together logically related
+classes (e.g., all the classes in a module). E.g.;
+
+.. sourcecode:: cpp
+
+  /**
+   * \defgroup mynewmodule This is a new module
+   */
+
+  /**
+   * \ingroup mynewmodule
+   *
+   * MyClassOne description.
+   */
+  class MyClassOne
+  {
+  };
+
+  /**
+   * \ingroup mynewmodule
+   *
+   * MyClassTwo description.
+   */
+  class MyClassTwo
+  {
+  };
+
+In the tests for the module, it is suggested to add an ancillary group:
+
+.. sourcecode:: cpp
+
+  /**
+   * \defgroup mynewmodule-test Tests for new module
+   * \ingroup mynewmodule
+   * \ingroup tests
+   */
+
+  /**
+   * \ingroup mynewmodule-tests
+   * \brief MyNewModule Test
+   */
+  class MyNewModuleTest : public TestCase
+  {
+  };
+
+  /**
+   * \ingroup mynewmodule-tests
+   * \brief MyNewModule TestSuite
+   */
+  class MyNewModuleTestSuite : public TestSuite
+  {
+    public:
+      MyNewModuleTestSuite();
+  };
+
+  /**
+   * \ingroup mynewmodule-tests
+   * Static variable for test initialization
+   */
+  static MyNewModuleTestSuite g_myNewModuleTestSuite;
 
 As for comments within the code, comments should be used to describe intention
 or algorithmic overview where is it not immediately obvious from reading the
@@ -837,7 +900,7 @@
 =====
 
 Where casts are necessary, use the Google C++ guidance:  "Use C++-style casts
-like ``static_cast<float> (double_value)``, or brace initialization for
+like ``static_cast<float>(double_value)``, or brace initialization for
 conversion of arithmetic types like ``int64 y = int64{1} << 42``."
 Do not use C-style casts, since they can be unsafe.
 
@@ -853,17 +916,21 @@
 
 Avoid unnecessary casts if minor changes to variable declarations can solve
 the issue. In the following example, ``x`` can be declared as ``float`` instead of
-``int`` to avoid the cast:
+``int`` to avoid the cast, or write numbers in decimal format:
 
 .. sourcecode:: cpp
 
   // Do not declare x as int, to avoid casting it to float
   int x = 3;
-  return 1 / static_cast<float>(x);
+  float y = 1 / static_cast<float>(x);
 
   // Prefer to declare x as float
   float x = 3.0;
-  return 1 / x;
+  float y = 1 / x;
+
+  // Or use 1.0 instead of just 1
+  int x = 3;
+  float y = 1.0 / x;
 
 Namespaces
 ==========
@@ -1023,7 +1090,7 @@
 
   if (n < 0)
   {
-      return n;
+      return false;
   }
 
   n += 3;
@@ -1047,13 +1114,13 @@
   if (p == NULL)    {...}
   if (p == 0)       {...}
 
-  NS_ASSERT... (p != nullptr, ...)    NS_ASSERT... (p, ...)
-  NS_ABORT...  (p != nullptr, ...)    NS_ABORT...  (p, ...)
+  NS_ASSERT...(p != nullptr, ...)    NS_ASSERT...(p, ...)
+  NS_ABORT... (p != nullptr, ...)    NS_ABORT... (p, ...)
 
-  NS_ASSERT... (p == nullptr, ...)    NS_ASSERT... (!p, ...)
-  NS_ABORT...  (p == nullptr, ...)    NS_ABORT...  (!p, ...)
+  NS_ASSERT...(p == nullptr, ...)    NS_ASSERT...(!p, ...)
+  NS_ABORT... (p == nullptr, ...)    NS_ABORT... (!p, ...)
 
-  NS_TEST...   (p, nullptr, ...)      NS_TEST...   (p, nullptr, ...)
+  NS_TEST...  (p, nullptr, ...)      NS_TEST...  (p, nullptr, ...)
 
 C++ standard
 ============
@@ -1069,9 +1136,9 @@
 ===================
 
 - ``NS_LOG_COMPONENT_DEFINE("log-component-name");`` statements should be
-  placed within namespace ns3 (for module code) and after the
+  placed within ``namespace ns3`` (for module code) and after the
   ``using namespace ns3;``.  In examples,
-  ``NS_OBJECT_ENSURE_REGISTERED()`` should also be placed within namespace ``ns3``.
+  ``NS_OBJECT_ENSURE_REGISTERED()`` should also be placed within ``namespace ns3``.
 
 - Pointers and references are left-aligned:
 
@@ -1081,6 +1148,23 @@
     int* ptr = &x;
     int& ref = x;
 
+- Use a trailing comma in braced-init-lists, so that each item is positioned in a new line.
+
+  .. sourcecode:: cpp
+
+    const std::vector<std::string> myVector{
+      "string-1",
+      "string-2",
+      "string-3",
+    };
+
+    const std::map<int, std::string> myMap{
+      {1, "string-1"},
+      {2, "string-2"},
+      {3, "string-3"},
+    };
+
+
 - Const reference syntax:
 
   .. sourcecode:: cpp
@@ -1132,6 +1216,24 @@
 
 - Do not use the C++ ``goto`` statement.
 
+- Do not add the ``enum`` or ``struct`` specifiers when declaring the variable's type.
+
+- Do not unnecessarily add ``typedef`` to ``struct`` or ``enum``.
+
+  .. sourcecode:: cpp
+
+    // Avoid
+    typedef struct
+    {
+        ...
+    } MyStruct;
+
+    // Prefer
+    struct MyStruct
+    {
+        ...
+    };
+
 Clang-tidy rules
 ================
 
@@ -1200,6 +1302,10 @@
     myVector.emplace_back(2);
     myVector.emplace_back(3);
 
+- Prefer to use the ``empty()`` function of STL containers (e.g., ``std::vector``),
+  instead of the condition ``size() > 0``, to avoid unnecessarily calculating the
+  size of the container.
+
 - Avoid unnecessary calls to the functions ``.c_str()`` and ``.data()`` of
   ``std::string``.
 
@@ -1219,5 +1325,17 @@
 
 - Avoid declaring trivial destructors, to optimize performance.
 
+- Avoid accessing class static functions and members through objects.
+  Instead, prefer to access them through the class.
+
+  .. sourcecode:: cpp
+
+    // OK
+    MyClass::StaticFunction();
+
+    // Avoid
+    MyClass myClass;
+    MyClass.StaticFunction();
+
 - Prefer to use ``static_assert()`` over ``NS_ASSERT()`` when conditions can be
   evaluated at compile-time.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/contributing/source/conf.py ns-3.38/doc/contributing/source/conf.py
--- ns-3.37/doc/contributing/source/conf.py	2022-11-01 17:39:10.947703100 -0700
+++ ns-3.38/doc/contributing/source/conf.py	2023-03-16 20:54:51.628957000 -0700
@@ -48,9 +48,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3.37'
+version = 'ns-3.38'
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3.37'
+release = 'ns-3.38'
 
 # 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.37/doc/contributing/source/external.rst ns-3.38/doc/contributing/source/external.rst
--- ns-3.37/doc/contributing/source/external.rst	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/doc/contributing/source/external.rst	2023-03-16 20:54:23.496868100 -0700
@@ -19,12 +19,12 @@
 If the contributor does not want to maintain the external code
 but wants to make the community aware that it is available with
 no ongoing support, links to the code can be posted on the |ns3|
-wiki contributed code page.  A typical example is the graduating
+`wiki`_ contributed code page.  A typical example is the graduating
 student who wishes to make his or her thesis-related code available
 but who does not plan to update it further.
 See :ref:`sec-unmaintained-contributed` below.
 
-However, much of the emphasis of this chaper is on hosting |ns3| extensions
+However, much of the emphasis of this chapter is on hosting |ns3| extensions
 in the |ns3| `App Store <https://apps.nsnam.org>`_.
 
 Rationale for the app store
@@ -227,7 +227,7 @@
 Some projects choose to maintain their own version of |ns3|, or maintain models
 outside of the main tree of the code.  In this case, the way to find out
 about these is to look at the Related Projects page on the |ns3|
-`wiki <https://www.nsnam.org/wiki/Related_Projects>`_.
+`wiki`_.
 
 If you know of externally maintained code that the project does not know about,
 please email ``webmaster@nsnam.org`` to request that it be added to the
@@ -250,7 +250,7 @@
 adopted by a future contributor.
 
 We ask anyone who wishes to do this to provide at least this information
-on our wiki:
+on our `wiki`_:
 
 * Authors,
 * Name and description of the extension,
@@ -259,6 +259,7 @@
 * Status (how it is maintained)
 
 Please also make clear in your code the applicable software license.
-The contribution will be stored on our `wiki <https://www.nsnam.org/wiki/Contributed_Code>`_. If you need web server
+The contribution will be stored on our `wiki`_. If you need web server
 space for your archive, please contact ``webmaster@nsnam.org``.
 
+.. _wiki: https://www.nsnam.org/wiki/Contributed_Code
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/contributing/source/index.rst ns-3.38/doc/contributing/source/index.rst
--- ns-3.37/doc/contributing/source/index.rst	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/doc/contributing/source/index.rst	2023-03-16 20:54:23.496868100 -0700
@@ -3,16 +3,24 @@
 Contributing to ns-3
 ====================
 
-This is the guide to *Contributing to ns-3*. Primary documentation for the ns-3 project is available in several forms:
+This is the *ns-3 Contributing Guide*. Primary documentation for the ns-3 project is organized as
+follows:
 
-* `ns-3 Doxygen <http://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
-* Tutorial for the `latest release <http://www.nsnam.org/documentation/latest/>`_ and `development tree <https://www.nsnam.org/documentation/>`_
-* Contributing to ns-3 (this document)
-* Manual, and Model Library for the `latest release <http://www.nsnam.org/documentation/latest/>`_ and `development tree <https://www.nsnam.org/documentation/>`_
-* `ns-3 wiki <http://www.nsnam.org/wiki/Main_Page>`_
+* Several guides that are version controlled for each release (the
+  `latest release <https://www.nsnam.org/documentation/latest/>`_) and
+  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+
+  * Tutorial
+  * Installation Guide
+  * Manual
+  * Model Library
+  * Contributing Guide *(this document)*
+* `ns-3 Doxygen <https://www.nsnam.org/docs/doxygen/index.html>`_: Documentation of the public APIs of
+  the simulator
+* `ns-3 wiki <https://www.nsnam.org/wiki/Main_Page>`_
 
 This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <https://www.sphinx-doc.org/>`_ and is maintained in the
-``doc/contributing`` directory of ns-3's source code.
+``doc/contributing`` directory of ns-3's source code.  Source file column width is 100 columns.
 
 .. toctree::
    :maxdepth: 2
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/contributing/source/models.rst ns-3.38/doc/contributing/source/models.rst
--- ns-3.37/doc/contributing/source/models.rst	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/doc/contributing/source/models.rst	2023-03-16 20:54:23.496868100 -0700
@@ -148,7 +148,7 @@
 course of doing so, you fixed a bug in another module, make sure you separate
 these two sets of changes in two separate submissions.
 
-When splitting a large submission into seprate submissions, (ideally) these
+When splitting a large submission into separate submissions, (ideally) these
 submissions should be prefaced (in the Merge Request description) by a
 detailed explanation of the overall plan
 such that code reviewers can review each submission separately but within a
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/doxygen.conf ns-3.38/doc/doxygen.conf
--- ns-3.37/doc/doxygen.conf	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/doc/doxygen.conf	2023-03-16 20:54:23.500868000 -0700
@@ -262,7 +262,7 @@
 ##   Inferred template parameters are clear from the context:
 ##     template <typename T> void Func (T x);
 ##   Explicit template parameters must be given at the call site:
-##     template <typename T> void Func (void);
+##     template <typename T> void Func ();
 ##   which is called as
 ##     Func<MyClass> ();
 
@@ -2691,3 +2691,7 @@
 # The default value is: YES.
 
 DOT_CLEANUP            = YES
+
+# Disable dark mode in the generated HTML in Doxygen 1.9.6 or greater.
+HTML_COLORSTYLE        = LIGHT
+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/doxygen.warnings.report.sh ns-3.38/doc/doxygen.warnings.report.sh
--- ns-3.37/doc/doxygen.warnings.report.sh	2022-11-01 17:37:24.371394600 -0700
+++ ns-3.38/doc/doxygen.warnings.report.sh	2023-03-16 20:54:23.500868000 -0700
@@ -559,6 +559,12 @@
     echo "Filtered Warnings"
     echo "========================================"
     echo "$filterin"
+elif [ "$filter_log_results" != "" ] ; then
+    echo
+    echo
+    echo "Warnings"
+    echo "========================================"
+    echo "$filter_log_results"
 fi
 
 status_report 0 $me
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/Makefile ns-3.38/doc/installation/Makefile
--- ns-3.37/doc/installation/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/Makefile	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,165 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+# Additional variables for figures, not sphinx default:
+DIA = dia
+EPSTOPDF = epstopdf
+FIGURES = source/figures
+IMAGES_EPS = \
+
+IMAGES_PNG = ${IMAGES_EPS:.eps=.png}
+IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
+
+IMAGES = $(IMAGES_EPS) $(IMAGES_PNG) $(IMAGES_PDF)
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+.NOTPARALLEL:
+
+%.eps : %.dia
+	@echo dia $(notdir $<)
+	@$(DIA) -t eps $< -e $@ >/dev/null
+
+%.png : %.dia
+	@echo dia $(notdir $<)
+	@$(DIA) -t png $< -e $@ >/dev/null
+
+%.png : %.eps
+	@echo convert $(notdir $<)
+	@$(CONVERT) $< $@ >/dev/null
+
+%.pdf : %.eps
+	@echo epstopdf $(notdir $<)
+	@$(EPSTOPDF) $< -o=$@ >/dev/null
+	@if test x$($@_width) != x; then $(RESCALE) $($@_width) $@ ; fi
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+frag: pickle
+	@if test ! -d $(BUILDDIR)/frag; then mkdir $(BUILDDIR)/frag; fi
+	pushd $(BUILDDIR)/frag && ../../pickle-to-xml.py ../pickle/index.fpickle  > navigation.xml && popd
+	cp -r $(BUILDDIR)/pickle/_images $(BUILDDIR)/frag
+
+html: $(IMAGES)
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml: $(IMAGES)
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml: $(IMAGES)
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle: $(IMAGES)
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json: $(IMAGES)
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp: $(IMAGES)
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp: $(IMAGES)
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ns-3.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ns-3.qhc"
+
+devhelp: $(IMAGES)
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/ns-3"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ns-3"
+	@echo "# devhelp"
+
+epub: $(IMAGES)
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex: $(IMAGES)
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf: $(IMAGES)
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	make -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text: $(IMAGES)
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man: $(IMAGES)
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes: $(IMAGES)
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck: $(IMAGEs)
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest: $(IMAGES)
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/bake.rst ns-3.38/doc/installation/source/bake.rst
--- ns-3.37/doc/installation/source/bake.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/bake.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,23 @@
+.. include:: replace.txt
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+.. _Bake:
+
+Installing Bake
+---------------
+
+Bake is a build system orchestration tool that was primarily designed for installing ns-3
+`Direct Code Execution <https://ns-3-dce.readthedocs.io/en/latest/getting-started.html>`_ but
+can be used more generally to install ns-3 third-party libraries and apps.
+
+Bake is a Python 3 program that requires the ``distro`` and ``requests`` packages; to start
+using Bake, it is generally sufficient to add those packages to your Python 3 installation
+using ``pip``.
+
+Bake is further documented elsewhere, including
+`here <https://www.nsnam.org/docs/bake/tutorial/html/index.html>`_ and in the
+`Getting Started <https://www.nsnam.org/docs/tutorial/html/getting-started.html#downloading-ns-3-using-bake>`_ chapter of the |ns3| tutorial.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/conf.py ns-3.38/doc/installation/source/conf.py
--- ns-3.37/doc/installation/source/conf.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/conf.py	2023-03-16 20:54:51.628957000 -0700
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+#
+# ns-3 documentation build configuration file, created by
+# sphinx-quickstart on Tue Dec 14 09:00:39 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.imgmath']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'ns-3'
+copyright = u'2018, ns-3 project'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = 'ns-3.38'
+# The full version, including alpha/beta/rc tags.
+release = 'ns-3.38'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'ns3_html_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ['../..']
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+html_title = 'Installation guide'
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y %H:%M'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'ns-3doc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'ns-3-installation.tex', u'ns-3 Installation Guide',
+   u'ns-3 project', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+latex_logo = '../../ns3_html_theme/static/ns-3.png'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+latex_preamble = '\\usepackage{amssymb}'
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'ns-3-installation', u'ns-3 Installation',
+     [u'ns-3 project'], 1)
+]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/figures/README.txt ns-3.38/doc/installation/source/figures/README.txt
--- ns-3.37/doc/installation/source/figures/README.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/figures/README.txt	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,18 @@
+Please write image files in a vector graphics format, when possible, and
+generate the .png and .pdf versions on the fly (see ../Makefile).
+
+The currently supported tool is dia.  xfig could be added similarly
+if someone wants to add it.  The main requirement for adding another format
+is that the tool to edit it is freely available and that a cron script can
+autogenerate the pdf and png from the figure source.  Tgif (.obj) files
+were once used but the file conversions require a valid X display to
+be running, and are therefore to be avoided since our code server
+does not run such a server.  Tgif pdf conversions were also cumbersome.
+
+Store the .dia versions in this directory, but not the .png or .pdfs.
+If the figure is not available in a vector graphics format, store both
+a .png and a .pdf version in this directory.
+
+If you add a source (.dia) file here, remember to add it to
+the list of figure sources in the Makefile in the directory above
+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/index.rst ns-3.38/doc/installation/source/index.rst
--- ns-3.37/doc/installation/source/index.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/index.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,34 @@
+.. only:: html or latex
+
+ns-3 Installation Guide
+=======================
+
+This is the *ns-3 Installation Guide*. Primary documentation for the ns-3 project is organized as
+follows:
+
+* Several guides that are version controlled for each release (the
+  `latest release <https://www.nsnam.org/documentation/latest/>`_) and
+  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+
+  * Tutorial
+  * Installation Guide *(this document)*
+  * Manual
+  * Model Library
+  * Contributing Guide
+* `ns-3 Doxygen <https://www.nsnam.org/docs/doxygen/index.html>`_: Documentation of the public APIs of
+  the simulator
+* `ns-3 wiki <https://www.nsnam.org/wiki/Main_Page>`_
+
+This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_
+for `Sphinx <http://sphinx.pocoo.org/>`_ and is maintained in the ``doc/installation`` directory of ns-3's source code.  Source file column width is 100 columns.
+
+.. toctree::
+   :maxdepth: 2
+
+   overview
+   quick-start
+   system
+   linux
+   macos
+   windows
+   bake
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/linux.rst ns-3.38/doc/installation/source/linux.rst
--- ns-3.37/doc/installation/source/linux.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/linux.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,167 @@
+.. include:: replace.txt
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+.. _Linux:
+
+Linux
+-----
+
+This chapter describes Linux-specific installation commands to install the options described
+in the previous chapter.  The chapter is written initially with Ubuntu (Debian-based) Linux
+examples (Ubuntu is the most frequently used Linux distribution by |ns3| users) but should
+translate fairly well to derivatives (e.g. Linux Mint).
+
+The list of packages depends on which version of ns-3 you are trying to build, and on which
+extensions you need; please review the previous chapter if you need more information.
+
+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).
+
+  +--------------------+---------------------------------------------------------------------+
+  | **ns-3 Version**   | **apt Packages**                                                    |
+  +====================+==================+==================================================+
+  | 3.36 and later     | ``g++ python3 cmake ninja-build git``                               |
+  +--------------------+---------------------------------------------------------------------+
+  | 3.30-3.35          | ``g++ python3 git``                                                 |
+  +--------------------+---------------------------------------------------------------------+
+  | 3.29 and earlier   | ``g++ python2``                                                     |
+  +--------------------+---------------------------------------------------------------------+
+
+.. 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>`_.
+
+Recommended
+***********
+
+  +-----------------------------+------------------------------------------------------------+
+  | **Feature**                 | **apt Packages**                                           |
+  +=============================+============================================================+
+  | Compiler cache optimization | ``ccache``                                                 |
+  +-----------------------------+------------------------------------------------------------+
+  | Code linting                | ``clang-format clang-tidy``                                |
+  +-----------------------------+------------------------------------------------------------+
+  | Debugging                   | ``gdb valgrind``                                           |
+  +-----------------------------+------------------------------------------------------------+
+
+.. note::
+  For Ubuntu 20.04 release and earlier, the version of ccache provided by apt
+  (3.7.7 or earlier) may not provide performance benefits, and users are recommended to install
+  version 4 or later, possibly as a source install. For Ubuntu 22.04 and later, ccache can be
+  installed using apt.
+
+.. note::
+  clang-format-14 through clang-format-16 version is required.
+
+Optional
+********
+
+Please see below subsections for Python-related package requirements.
+
+  +-----------------------------+------------------------------------------------------------+
+  | **Feature**                 | **apt Packages**                                           |
+  +=============================+============================================================+
+  | Reading pcap traces         | ``tcpdump wireshark``                                      |
+  +-----------------------------+------------------------------------------------------------+
+  | Database support            | ``sqlite sqlite3 libsqlite3-dev``                          |
+  +-----------------------------+------------------------------------------------------------+
+  | NetAnim animator            | ``qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools``      |
+  +-----------------------------+------------------------------------------------------------+
+  | MPI-based distributed       |                                                            |
+  | simulation                  | ``openmpi-bin openmpi-common openmpi-doc libopenmpi-dev``  |
+  +-----------------------------+------------------------------------------------------------+
+  | Building Doxygen            | ``doxygen graphviz imagemagick``                           |
+  +-----------------------------+------------------------------------------------------------+
+  | Sphinx documentation        | ``python3-sphinx dia imagemagick texlive dvipng latexmk``  |
+  |                             | ``texlive-extra-utils texlive-latex-extra``                |
+  |                             | ``texlive-font-utils``                                     |
+  +-----------------------------+------------------------------------------------------------+
+  | Eigen3                      | ``libeigen3-dev``                                          |
+  +-----------------------------+------------------------------------------------------------+
+  | GNU Scientific Library      | ``gsl-bin libgsl-dev libgslcblas0``                        |
+  +-----------------------------+------------------------------------------------------------+
+  | XML config store            | ``libxml2 libxml2-dev``                                    |
+  +-----------------------------+------------------------------------------------------------+
+  | GTK-based config store      | ``libgtk-3-dev``                                           |
+  +-----------------------------+------------------------------------------------------------+
+  | Emulation with virtual      | ``lxc-utils lxc-templates``                                |
+  | machines and tap bridge     | ``vtun uml-utilities ebtables bridge-utils``               |
+  +-----------------------------+------------------------------------------------------------+
+  | Support for openflow        | ``libxml2 libxml2-dev libboost-all-dev``                   |
+  +-----------------------------+------------------------------------------------------------+
+
+.. note::
+  For Ubuntu 20.10 and earlier, the single 'qt5-default' package suffices for NetAnim (``apt install qt5-default``)
+
+Python bindings
+===============
+
+ns-3.37 and newer::
+
+  python3 -m pip install --user cppyy
+
+ns-3.30-3.36 (also requires pybindgen, found in the ``allinone`` directory)::
+
+  apt install python3-dev pkg-config python3-setuptools
+
+PyViz visualizer
+================
+
+The PyViz visualizer uses a variety of Python packages supporting GraphViz.::
+
+  apt install gir1.2-goocanvas-2.0 python3-gi python3-gi-cairo python3-pygraphviz gir1.2-gtk-3.0 ipython3
+
+For Ubuntu 18.04 and later, python-pygoocanvas is no longer provided. The ns-3.29 release and later upgrades the support to GTK+ version 3, and requires these packages:
+
+For ns-3.28 and earlier releases, PyViz is based on Python2, GTK+ 2, GooCanvas, and GraphViz::
+
+  apt install python-pygraphviz python-kiwi python-pygoocanvas libgoocanvas-dev ipython
+
+Generating modified python bindings (ns-3.36 and earlier)
+=========================================================
+
+To modify the Python bindings found in release 3.36 and earlier (not needed for modern releases,
+or if you do not use Python).::
+
+  apt install cmake libc6-dev libc6-dev-i386 libclang-dev llvm-dev automake python3-pip
+  python3 -m pip install --user cxxfilt
+
+and you will want to install castxml and pygccxml as per the instructions for python bindings (or
+through the bake build tool as described in the tutorial). The 'castxml' and 'pygccxml' packages
+provided by Ubuntu 18.04 and earlier are not recommended; a source build (coordinated via bake)
+is recommended. If you plan to work with bindings or rescan them for any ns-3 C++ changes you
+might make, please read the chapter in the manual on this topic.
+
+Caveats and troubleshooting
+***************************
+
+When building documentation, if you get an error such as
+``convert ... not authorized source-temp/figures/lena-dual-stripe.eps``, see
+`this post <https://cromwell-intl.com/open-source/pdf-not-authorized.html>`_ about editing
+ImageMagick's security policy configuration.  In brief, you will want to make this kind of
+change to ImageMagick security policy::
+
+   --- ImageMagick-6/policy.xml.bak 2020-04-28 21:10:08.564613444 -0700
+   +++ ImageMagick-6/policy.xml 2020-04-28 21:10:29.413438798 -0700
+   @@ -87,10 +87,10 @@
+      <policy domain="path" rights="none" pattern="@*"/>
+   -  <policy domain="coder" rights="none" pattern="PS" />
+   +  <policy domain="coder" rights="read|write" pattern="PS" />
+      <policy domain="coder" rights="none" pattern="PS2" />
+      <policy domain="coder" rights="none" pattern="PS3" />
+      <policy domain="coder" rights="none" pattern="EPS" />
+   -  <policy domain="coder" rights="none" pattern="PDF" />
+   +  <policy domain="coder" rights="read|write" pattern="PDF" />
+      <policy domain="coder" rights="none" pattern="XPS" />
+    </policymap>
+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/macos.rst ns-3.38/doc/installation/source/macos.rst
--- ns-3.37/doc/installation/source/macos.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/macos.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,124 @@
+.. include:: replace.txt
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+.. _macOS:
+
+macOS
+-----
+
+This chapter describes installation steps specific to Apple macOS.  macOS installation of |ns3|
+requires either the installation of the full
+`Xcode IDE <https://developer.apple.com/xcode/features/>`_ or a more minimal install of
+`Xcode Command Line Tools <https://www.freecodecamp.org/news/install-xcode-command-line-tools/>`_).
+
+The full Xcode IDE requires 40 GB of disk space.  If you are just interested in getting |ns3|
+to run, the full Xcode is not necessary; we recommend Command Line Tools instead.
+
+In addition to Command Line Tools, some |ns3| extensions require third-party libraries;
+we recommend either `Homebrew <https://brew.sh/>`_ or `MacPorts <https://www.macports.org/>`_.
+If you prefer, you can probably avoid installing Command Line Tools and install the compiler
+of your choice and any other tools you may need using Homebrew or MacPorts.
+
+In general, documentation on the web suggests to use either, but not both, Homebrew or MacPorts
+on a particular system.  It has been noted that Homebrew tends to install the GUI version of certain
+applications without easily supporting the command-line equivalent, such as for the
+`dia <https://gitlab.gnome.org/GNOME/dia/>`_ application; see ns-3
+`MR 1247 <https://gitlab.com/nsnam/ns-3-dev/-/merge_requests/1247>`_ for discussion about this.
+
+Finally, regarding Python, some |ns3| maintainers prefer to use a
+`virtualenv <https://docs.python.org/3/library/venv.html>`_ to guard against incompatibilities
+that might arise from the native macOS Python and versions that may be installed by Homebrew
+or `Anaconda <https://docs.anaconda.com/anaconda/install/mac-os/>`_.  Some |ns3| users never
+use Python bindings or visualizer, but if your |ns3| workflow requires more heavy use
+of Python, please keep the possibility of a virtualenv in mind if you run into Python difficulties.
+For a short guide on virtual environments, please see
+`this link <https://www.dataquest.io/blog/a-complete-guide-to-python-virtual-environments/>`_.
+
+Requirements
+************
+
+macOS uses the Clang/LLVM compiler toolchain.  It is possible to install gcc/g++ from
+Homebrew and MacPorts, but macOS will not provide it due to licensing issues.  |ns3|
+works on recent versions of both ``clang++`` and ``g++``, so for macOS, there is no need
+to install ``g++``.
+
+  +--------------------+---------------------------------------------------------------------+
+  | **ns-3 Version**   | **Homebrew packages**         | **MacPorts packages**               |
+  +====================+==================+==================================================+
+  | 3.36 and later     | ``cmake ninja``               | ``cmake ninja``                     |
+  +--------------------+---------------------------------------------------------------------+
+  | 3.35 and earlier   | None                          | None                                |
+  +--------------------+---------------------------------------------------------------------+
+
+Recommended
+***********
+
+  +-----------------------------+------------------------------------------------------------+
+  | **Feature**                 | **Homebrew packages**  | **MacPorts packages**             |
+  +=============================+============================================================+
+  | Compiler cache optimization | ``ccache``             | ``ccache``                        |
+  +-----------------------------+------------------------------------------------------------+
+  | Code linting                | ``clang-format llvm``  |  clang-format included with       |
+  |                             |                        | ``clang``, need to select         |
+  |                             |                        | ``clang-XX llvm-XX`` versions     |
+  +-----------------------------+------------------------------------------------------------+
+  | Debugging                   | None                   | ``gdb ddd`` (ddd requires gdb)    |
+  +-----------------------------+------------------------------------------------------------+
+
+.. note::
+  The ``llvm`` Homebrew package provides ``clang-tidy``, but please note that the binary is
+  placed at ``/opt/homebrew/opt/llvm/bin/clang-tidy`` so you may need to add this path to your
+  ``$PATH`` variable.
+
+.. note::
+  For debugging, ``lldb`` is the default debugger for llvm.  Memory checkers such as
+  Memory Graph exist for macOS, but the ns-3 team doesn't have experience with it as a
+  substitution for ``valgrind`` (which is reported to not work on M1 Macs).
+
+Optional
+********
+
+Please see below subsections for Python-related package requirements.
+
+For MacPorts packages we show the most recent package version available as of early 2023.
+
+  +-----------------------------+----------------------------------+--------------------------+
+  | **Feature**                 | **Homebrew packages**            | **MacPort packages**     |
+  +=============================+==================================+==========================+
+  | Reading pcap traces         | ``wireshark``                    | ``wireshark4``           |
+  +-----------------------------+----------------------------------+--------------------------+
+  | Database support            | ``sqlite``                       | ``sqlite3``              |
+  +-----------------------------+----------------------------------+--------------------------+
+  | NetAnim animator            | ``qt@5``                         | ``qt513``                |
+  +-----------------------------+----------------------------------+--------------------------+
+  | MPI-based distributed       |                                  | ``openmpi``              |
+  | simulation                  | ``open-mpi``                     |                          |
+  +-----------------------------+----------------------------------+--------------------------+
+  | Building Doxygen            | ``doxygen graphviz imagemagick`` | ``doxygen graphviz``     |
+  |                             |                                  | ``ImageMagick``          |
+  +-----------------------------+----------------------------------+--------------------------+
+  | Sphinx documentation        | ``sphinx-doc texlive``           | ``texlive``              |
+  |                             |                                  | ``pyXX-sphinx``, with    |
+  |                             |                                  | `XX`` the Python version |
+  +-----------------------------+----------------------------------+--------------------------+
+  | Eigen3                      | ``eigen``                        | ``eigen3``               |
+  +-----------------------------+----------------------------------+--------------------------+
+  | GNU Scientific Library      | ``gsl``                          | ``gsl``                  |
+  +-----------------------------+----------------------------------+--------------------------+
+  | XML config store            | ``libxml2``                      | ``libxml2``              |
+  +-----------------------------+----------------------------------+--------------------------+
+  | GTK-based config store      | ``gtk+3``                        | ``gtk3`` or ``gtk4``     |
+  +-----------------------------+----------------------------------+--------------------------+
+  | Emulation with virtual      |                                  |                          |
+  | machines                    | Not available for macOS          | Not available for macOS  |
+  +-----------------------------+----------------------------------+--------------------------+
+  | Support for openflow        | ``boost``                        | ``boost``                |
+  +-----------------------------+----------------------------------+--------------------------+
+
+Caveats and troubleshooting
+***************************
+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/overview.rst ns-3.38/doc/installation/source/overview.rst
--- ns-3.37/doc/installation/source/overview.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/overview.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,86 @@
+.. include:: replace.txt
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+Overview
+--------
+
+This guide documents the different ways that users can download, build, and install |ns3| from source code.  All of these actions (download, build, install) have variations
+or options, and can be customized or extended by users; this document attempts to inform readers
+about different possibilities.
+
+Please note a few important details:
+
+* |ns3| is supported for Linux, macOS, and Windows systems.  Linux is the primary system
+  supported, and (almost) all |ns3| features are supported on Linux.  However, most features can
+  also be used on macOS and Windows.  Windows is probably the least used and least supported
+  system.
+* |ns3| has minimal prerequisites for its most basic installation; namely, a **C++** compiler,
+  **Python3** support, the **CMake** build system, and at least one of **make**, **ninja**, or
+  **Xcode** build systems.  However, some users will want to install optional packages
+  to make use of the many optional extensions.
+* |ns3| installation requirements vary from release to release, and also as underlying operating
+  systems evolve.  This document is version controlled, so if you are using the *ns-3.X* release,
+  try to read the *ns-3.X* version of this document.  Even with this guidance, you may encounter
+  issues if you are trying to use an old version of |ns3| on a much newer system.  For instance,
+  |ns3| versions until around 2020 relied on Python2, which is now end-of-life and not installed
+  by default on many Linux distributions.  Compilers also become more pedantic and issue more
+  warnings as they evolve.  Solutions to some of these forward compatibility problems exist and
+  are discussed herein or might be found in the ns-3-users mailing list archives.
+
+If you find any stale or inaccurate information in this document, please report it to maintainers,
+on our `Zulip chat <https://ns-3.zulipchat.com>`_, in the GitLab.com
+`Issue tracker <https://gitlab.com/nsnam/ns-3-dev/-/issues/>`_, (or better yet, a patch to fix
+in the `Merge Request tracker <https://gitlab.com/nsnam/ns-3-dev/-/merge_requests>`_), or on our
+developers mailing list.
+
+We also will accept patches to this document to provide installation guidance for the
+FreeBSD operating system and other operating systems being used in practice.
+
+Software organization
+*********************
+
+|ns3| is a set of C++ libraries (usually compiled as shared libraries) that can be used by C++
+or Python programs to construct simulation scenarios and execute simulations.  Users can also
+write programs that link other C++ shared libraries (or import other Python modules).  Users can
+choose to use a subset of the available libraries; only the ``core`` library is strictly required.
+
+|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).
+
+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
+OpenSSL, a package manager is typically used (e.g., ``apt install openssl libssl-dev``), which leads
+to shared libraries being installed under ``/usr/lib/``, development headers being installed
+under ``/usr/include/``, etc.  Programs that wish to use these libraries must link the system
+libraries and include the development headers.  |ns3| is capabile of being installed in exactly
+the same way, and some downstream package maintainers have packaged |ns3| for some systems
+such as Ubuntu.  However, as of this writing, the |ns3| project has not prioritized or
+standardized such package distribution, favoring instead to recommend a *source download* without
+a system-level install.  This is mainly because most |ns3| users prefer to slightly or extensively
+edit or extend the |ns3| libraries, or to build them in specific ways (for debugging, or optimized
+for large-scale simulation campaign).  Our build system provides an ``install`` command that can
+be used to install libraries and headers to system locations (usually requiring administrator
+privileges), but usually the libraries are just built and used from within the |ns3| ``build``
+directory.
+
+Document organization
+*********************
+
+The rest of this document is organized as follows:
+
+* :ref:`Quick start` (for any operating system)
+* :ref:`General list` of requirements, recommendations, and optional prerequisites
+* :ref:`Linux` installation
+* :ref:`macOS` installation
+* :ref:`Windows` installation
+* :ref:`Bake` installation
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/quick-start.rst ns-3.38/doc/installation/source/quick-start.rst
--- ns-3.37/doc/installation/source/quick-start.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/quick-start.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,179 @@
+.. include:: replace.txt
+.. highlight:: bash
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+.. _Quick start:
+
+Quick Start
+-----------
+
+This chapter summarizes the |ns3| installation process for C++ users interested in trying a
+generic install of the main simulator.  Python bindings installation is not covered.
+
+Some of this chapter is redundant with the
+|ns3| `tutorial <https://www.nsnam.org/docs/tutorial/html/>`_, which also covers similar steps.
+
+The steps are:
+
+1. Download a source archive, or make a git clone, of |ns3| to a location on your file
+   system (usually somewhere under your home directory).
+
+2. Use a C++ compiler to compile the software into a set of shared libraries, executable example
+   programs, and tests
+
+|ns3| uses the CMake build system to manage the C++ compilation, and CMake itself calls on
+a lower-level build system such as ``make`` to perform the actual compilation.
+
+Prerequisites
+*************
+
+Make sure that your system has these prerequisites.  Download can be via either ``git`` or via
+source archive download (via a web browser, ``wget``, or ``curl``).
+
+  +--------------------+--------------------------------------+------------------------------+
+  | **Purpose**        | **Tool**                             | **Minimum Version**          |
+  +====================+==================+===================+==============================+
+  | **Download**       | ``git`` (for Git download)           | No minimum version           |
+  +                    +                                      +                              +
+  |                    | or ``tar`` and ``bunzip2``           | No minimum version           |
+  |                    | (for Web download)                   |                              |
+  +--------------------+--------------------------------------+------------------------------+
+  | **Compiler**       | ``g++``                              | >= 8                         |
+  +                    +                                      +                              +
+  |                    | or ``clang++``                       | >= 10                        |
+  +--------------------+--------------------------------------+------------------------------+
+  | **Configuration**  | ``python3``                          | >= 3.5                       |
+  +--------------------+--------------------------------------+------------------------------+
+  | **Build system**   | ``cmake``,                           | >= 3.10                      |
+  +                    +                                      +                              +
+  |                    | and at least one of:                 | No minimum version           |
+  |                    | ``make``, ``ninja``, or ``Xcode``    |                              |
+  +--------------------+--------------------------------------+------------------------------+
+
+.. note::
+
+  If you are using an older version of ns-3, other tools may be needed (such as
+  ``python2`` instead of ``python3`` and ``Waf`` instead of ``cmake``).  Check the file
+  ``RELEASE_NOTES`` in the top-level directory for requirements for older releases.
+
+From the command line, you can check the version of each of the above tools with version
+requirements as follows:
+
+  +--------------------------------------+------------------------------------+
+  | **Tool**                             | **Version check command**          |
+  +======================================+====================================+
+  | ``g++``                              | ``$ g++ --version``                |
+  +--------------------------------------+------------------------------------+
+  | ``clang++``                          | ``$ clang++ --version``            |
+  +--------------------------------------+------------------------------------+
+  | ``python3``                          | ``$ python3 -V``                   |
+  +--------------------------------------+------------------------------------+
+  | ``cmake``                            | ``$ cmake --version``              |
+  +--------------------------------------+------------------------------------+
+
+Download
+********
+
+There are two main options:
+
+1. Download a release tarball.  This will unpack to a directory such as ``ns-allinone-3.38``
+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
+
+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::
+
+  $ git clone https://gitlab.com/nsnam/ns-3-dev.git
+  $ cd ns-3-dev
+
+Note that if you select option 1), your directory name will contain the release number.  If
+you clone |ns3|, your directory will be named ``ns-3-dev``.  By default, Git will check out
+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
+
+In this quick-start, we are omitting download and build instructions for optional |ns3| modules,
+the ``NetAnim`` animator, Python bindings, and ``NetSimulyzer``.  The
+`ns-3 Tutorial <https://www.nsnam.org/docs/tutorial/html/getting-started.html>`_ has some
+instructions on optional components, or else the documentation associated with the extension
+should be consulted.
+
+Moreover, in this guide we will assume that you are using ns-3.36 or later. Earlier
+versions had different configuration, build, and run command and options.
+
+Building and testing ns-3
+*************************
+
+Once you have obtained the source either by downloading a release or by cloning a Git repository,
+the next step is to configure the build using the *CMake* build system.  The below commands
+make use of a Python wrapper around CMake, called ``ns3``, that simplifies the command-line
+syntax, resembling *Waf* syntax.  There are several options to control the build, but enabling
+the example programs and the tests, for a default build profile (with asserts enabled and
+and support for |ns3| logging) is what is usually done at first::
+
+  $ ./ns3 configure --enable-examples --enable-tests
+
+Depending on how fast your CPU is, the configuration command can take anywhere from a few
+seconds to a minute.
+
+Then, use the ``ns3`` program to build the |ns3| module libraries and executables::
+
+  $ ./ns3 build
+
+Build times vary based on the number of CPU cores, the speed of the CPU and memory, and the mode
+of the build (whether debug mode, which is faster, or the default or optimized modes, which are
+slower).  Additional configuration (not covered here) can be used to limit the scope of the
+build, and the ``ccache``, if installed, can speed things up.  In general, plan on the build
+taking a few minutes on faster workstations.
+
+You should see some output such as below, if successful::
+
+  'build' finished successfully (44.159s)
+
+  Modules built:
+  antenna                   aodv                      applications
+  bridge                    buildings                 config-store
+  core                      csma                      csma-layout
+  dsdv                      dsr                       energy
+  fd-net-device             flow-monitor              internet
+  internet-apps             lr-wpan                   lte
+  mesh                      mobility                  mpi
+  netanim (no Python)       network                   nix-vector-routing
+  olsr                      point-to-point            point-to-point-layout
+  propagation               sixlowpan                 spectrum
+  stats                     tap-bridge                test (no Python)
+  topology-read             traffic-control           uan
+  virtual-net-device        visualizer                wave
+  wifi                      wimax
+
+  Modules not built (see ns-3 tutorial for explanation):
+  brite                     click                     openflow
+
+
+Once complete, you can run the unit tests to check your build::
+
+  $ ./test.py
+
+This command should run several hundred unit tests.  If they pass, you have made a successful
+initial build of |ns3|.  Read further in this manual for instructions about building
+optional components, or else consult the |ns3| Tutorial or other documentation to get started
+with the base |ns3|.
+
+If you prefer to code with an code editor, consult the documentation in the |ns3| Manual
+on `Working with CMake <https://www.nsnam.org/docs/manual/html/working-with-cmake.html>`_,
+since CMake enables |ns3| integration with a variety of code editors, including:
+
+* JetBrains's CLion
+* Microsoft Visual Studio and Visual Studio Code
+* Apple's XCode
+* CodeBlocks
+* Eclipse CDT4
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/replace.txt ns-3.38/doc/installation/source/replace.txt
--- ns-3.37/doc/installation/source/replace.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/replace.txt	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,5 @@
+.. |ns3| replace:: *ns-3*
+
+.. |ns2| replace:: *ns-2*
+
+.. |check| replace:: :math:`\checkmark`
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/_static/custom.css ns-3.38/doc/installation/source/_static/custom.css
--- ns-3.37/doc/installation/source/_static/custom.css	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/_static/custom.css	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,4 @@
+table.align-left {
+    margin-left: auto;
+    margin-right: auto;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/system.rst ns-3.38/doc/installation/source/system.rst
--- ns-3.37/doc/installation/source/system.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/system.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,232 @@
+.. include:: replace.txt
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+.. _General list:
+
+System Prerequisites
+--------------------
+
+This chapter describes the various required, recommended, and optional system prerequisites
+for installing and using |ns3|.  Optional prerequisites depend on whether an optional feature of
+|ns3| is desired by the user.  The chapter is written to be generally applicable to all operating
+systems, and subsequent chapters describe the specific packages for different operating systems.
+
+The list of requirements depends on which version of ns-3 you are trying to build, and on which
+extensions you need.
+
+.. note::
+   **"Do I need to install all of these packages?"**  Some users want to install everything so
+   that their configuration output shows that every feature is enabled.  However, there is
+   no real need to install prerequisites related to features you are not yet using; you can always
+   come back later and install more prerequisites as needed.  The build system should warn
+   you if you are missing a prerequisite.
+
+In the following, we have classified the prerequisites as either being required, recommended
+for all users, or optional depending on use cases.
+
+.. note::
+   **"Is there a maintained list of all prerequisites?"**  We use GitLab.com's continuous
+   integration system for testing; the configuration YAML files for these jobs can be found
+   in the directory ``utils/tests/``.  So, for instance, if you want to look at what packages
+   the CI is installing for Alpine Linux, look at ``utils/tests/gitlab-ci-alpine.yml``.  The
+   default CI (Arch Linux) ``pacman`` commands are in ``utils/tests/gitlab-ci.yml``.
+
+Requirements
+************
+
+Minimal requirements for release 3.36 and later
+===============================================
+
+A C++ compiler (``g++`` or ``clang++``), Python 3, the `CMake <https://cmake.org>`_ build
+system, and a separate C++ building tool such as ``make``, ``ninja-build``, or ``Xcode`` are
+the minimal requirements for compiling the software.
+
+The ``tar`` and ``bunzip2`` utilities are needed to unpack source file archives.
+If you want to instead use `Git <https://git-scm.com/>`_ to fetch code, rather than downloading
+a source archive, then ``git`` is required instead.
+
+Minimal requirements for release 3.30-3.35
+==========================================
+
+If you are not using Python bindings, since the Waf build system is provided as part of |ns3|,
+there are only two build requirements (a C++ compiler, and Python3) for a minimal install of these
+older |ns3| releases.
+
+The ``tar`` and ``bunzip2`` utilities are needed to unpack source file archives.
+If you want to instead use `Git <https://git-scm.com/>`_ to fetch code, rather than downloading
+a source archive, then ``git`` is required instead.
+
+Minimal requirements for release 3.29 and earlier
+=================================================
+
+Similarly, only a C++ compiler and Python2 were strictly required for building the |ns3|
+releases 3.29 and earlier.
+
+The ``tar`` and ``bunzip2`` utilities are needed to unpack source file archives.
+If you want to instead use `Git <https://git-scm.com/>`_ to fetch code, rather than downloading
+a source archive, then ``git`` is required instead.
+
+Recommended
+***********
+
+The following are recommended for most users of |ns3|.
+
+compiler cache optimization (for ns-3.37 and later)
+===================================================
+
+`Ccache <https://ccache.dev>`_ is a compiler cache optimization that will speed up builds across
+multiple |ns3| directories, at the cost of up to an extra 5 GB of disk space used in the cache.
+
+Code linting
+============
+
+Since ns-3.37 release, `Clang-Format <https://clang.llvm.org/docs/ClangFormat.html>`_ and
+`Clang-Tidy <https://clang.llvm.org/extra/clang-tidy/>`_ are used to enforce the coding-style
+adopted by |ns3|.
+
+Users can invoke these tools directly from the command-line or through the
+(``utils/check-style-clang-format.py``) check program.
+Moreover, clang-tidy is integrated with CMake, enabling code scanning during the build phase.
+
+.. note::
+  clang-format-14 through clang-format-16 version is required.
+
+clang-format is strongly recommended to write code that follows the ns-3 code conventions, but
+might be skipped for simpler tasks (e.g., writing a simple simulation script for yourself).
+
+clang-tidy is recommended when writing a module, to both follow code conventions and to provide
+hints on possible bugs in code.
+
+Both are used in the CI system, and a merge request will likely fail if you did not use them.
+
+Debugging
+=========
+
+`GDB <https://www.sourceware.org/gdb/>`_ and `Valgrind <https://valgrind.org>`_ are useful
+for debugging and recommended if you are doing C++ development of new models or scenarios.
+Both of the above tools are available for Linux and BSD systems; for macOS,
+`LLDB <https://lldb.llvm.org>`_ is similar to GDB, but Valgrind doesn't appear to be available
+for M1 machines.
+
+Optional
+********
+
+The remaining prerequisites listed below are only needed for optional ns-3 components.
+
+.. note::
+  As of ns-3.30 release (August 2019), ns-3 uses Python 3 by default, but earlier
+  releases depend on Python 2 packages, and at least a Python 2 interpreter is recommended.
+  If installing the below prerequisites for an earlier release, one may in general substitute
+  'python' where 'python3' is found in the below (e.g., install 'python-dev' instead of
+  'python3-dev').
+
+To read pcap packet traces
+==========================
+
+Many |ns3| examples generate pcap files that can be viewed by pcap analyzers such as Tcpdump
+and `Wireshark <https://www.wireshark.org>`_.
+
+Database support
+================
+
+`SQLite <https://www.sqlite.org>`_ is recommended if you are using the statistics framework or
+if you are running LTE or NR simulations (which make use of SQLite databases):
+
+
+Python bindings (ns-3.37 and newer)
+===================================
+
+|ns3| Python support now uses `cppyy <https://cppyy.readthedocs.io/en/latest/>`_.
+
+Using Python bindings (release 3.30 to ns-3.36)
+===============================================
+
+This pertains to the use of existing Python bindings shipped with ns-3; for updating or
+generating new bindings, see further below.
+
+Python bindings used `pybindgen <https://github.com/gjcarneiro/pybindgen>`_ in the past, which
+can usually be found in the ``ns-3-allinone`` directory.  Python3 development packages, and
+setup tools, are typically needed.
+
+NetAnim animator
+================
+
+The `Qt <https://www.qt.io>`_ qt5 development tools are needed for NetAnim animator;
+qt4 will also work but we have migrated to qt5. qt6 compatibility is not tested.
+
+PyViz visualizer
+================
+
+The PyViz visualizer uses a variety of Python packages supporting GraphViz.
+In general, to enable Python support in ns-3, `cppyy <https://cppyy.readthedocs.io/en/latest/>`_ is required.
+
+MPI-based distributed simulation
+================================
+
+`Open MPI <https://www.open-mpi.org/>`_ support is needed if you intend to run large, parallel
+|ns3| simulations.
+
+Doxygen
+=======
+
+`Doxygen <https://www.doxygen.nl>`_ is only needed if you intend to write new Doxygen
+documentation for header files.
+
+Sphinx documentation
+====================
+
+The ns-3 manual (``doc/manual``), tutorial (``doc/tutorial``) and others are written in
+reStructuredText for Sphinx, and figures are typically in dia.  To build PDF versions,
+`texlive <https://www.tug.org/texlive/>`_ packages are needed.
+
+Eigen3 support
+==============
+`Eigen3 <https://gitlab.com/libeigen/eigen>`_ is used to support more efficient calculations
+when using the `3GPP propagation loss models <https://www.nsnam.org/docs//models/html/propagation.html#threegpppropagationlossmodel>`_
+in LTE and NR simulations.
+
+GNU Scientific Library (GSL)
+============================
+
+GNU Scientific Library (GSL) is is only used for more accurate 802.11b (legacy) WiFi error models (not needed for more modern OFDM-based Wi-Fi).
+
+XML-based version of the config store
+=====================================
+
+`Libxml2 <https://gitlab.gnome.org/GNOME/libxml2>`_ is needed for the XML-based Config Store feature.
+
+A GTK-based configuration system
+================================
+
+`GTK development libraries <https://www.gtk.org>`_ are also related to the (optional) config store,
+for graphical desktop support.
+
+Generating modified python bindings (ns-3.36 and earlier)
+=========================================================
+
+To modify the Python bindings found in release 3.36 and earlier (not needed for modern releases,
+or if you do not use Python, the `LLVM toolchain <https://llvm.org>`_ and
+`cxxfilt <https://pypi.org/project/cxxfilt/>`_ are needed.
+
+You will also need to install
+`castxml <https://github.com/CastXML/CastXML>`_ and
+`pygccxml <https://github.com/CastXML/pygccxml>`_ as per the instructions for Python bindings (or
+through the bake build tool as described in the |ns3| tutorial).
+If you plan to work with bindings or rescan them for any ns-3 C++ changes you
+might make, please read the chapter in the manual (corresponding to the release) on this topic.
+
+To experiment with virtual machines and ns-3
+============================================
+
+Linux systems can use `LXC <https://linuxcontainers.org>`_ and
+`TUN/TAP device drivers <https://docs.kernel.org/networking/tuntap.html>`_ for emulation support.
+
+Support for openflow module
+===========================
+
+`OpenFlow switch support <https://www.nsnam.org/docs/models/html/openflow-switch.html>`_ requires
+XML and `Boost <https://www.boost.org/>`_ development libraries.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/installation/source/windows.rst ns-3.38/doc/installation/source/windows.rst
--- ns-3.37/doc/installation/source/windows.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/installation/source/windows.rst	2023-03-16 20:54:23.500868000 -0700
@@ -0,0 +1,822 @@
+.. include:: replace.txt
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+.. _Windows:
+
+Windows
+-------
+
+This chapter describes installation steps specific to Microsoft Windows (version 10) and its
+derivatives (e.g. Home, Pro, Enterprise) using the Msys2/MinGW64 toolchain.
+
+There are two documented ways to use |ns3| on Windows:  the Windows Subsystem for Linux (WSL) or
+via the Msys2/MinGW64 toolchain.  Both options are listed below; users may choose to install
+either WSL or the Msys2/MinGW64 toolchain (installing both is not required).
+
+.. note::
+  |ns3| is not fully compatible with Visual Studio IDE / MSVC compiler; only Visual Studio Code
+editor, the Msys2/MinGW64 toolchain, and WSL, as explained below.
+
+.. _WSL2:
+
+Windows Subsystem for Linux
+***************************
+
+Windows Subsystem for Linux (WSL), particularly WSL2, can be used on Windows for |ns3|.  WSL2
+runs a real Linux kernel on Windows's Hyper-V hypervisor, providing 100% code compatibility with
+Linux and seamless integration with Windows.  VS Code has excellent support and integration with
+WSL, enabling Windows users to develop for ns-3 in a native environment. It is recommended to
+install the WSL extension in VS Code for this purpose.
+
+Users starting with WSL2 can follow the Linux installation instructions to fill out other
+package prerequisites.  Note that |ns3| emulation features using WSL2 are not tested/supported.
+
+For more information:
+
+* `WSL <https://learn.microsoft.com/en-us/windows/wsl/>`_
+* `Developing in WSL with VS Code <https://code.visualstudio.com/docs/remote/wsl>`_
+* `WSL tutorial <https://code.visualstudio.com/docs/remote/wsl-tutorial>`_
+
+.. _Windows 10 package prerequisites:
+
+Windows 10 package prerequisites
+********************************
+
+The following instructions are relevant to the ns-3.37 release and Windows 10.
+
+Installation of the Msys2 environment
++++++++++++++++++++++++++++++++++++++
+
+.. _Msys2: https://www.msys2.org/
+
+The `Msys2`_ includes ports of Unix tools for Windows built with multiple toolchains,
+including: MinGW32, MinGW64, Clang64, UCRT.
+
+|ns3| has been tested with the  MinGW64 (GCC) toolchain.  MinGW32 is 32-bit, which |ns3| does
+not support.  The project's Windows maintainer has tested Clang64 unsuccessfully, and has
+not tested the UCRT toolchain (which may work).
+
+The `Msys2`_ installer can be found on their site.
+Msys2 will be installed by default in the ``C:\msys64`` directory.
+
+The next required step is adding the binaries directories from the MinGW64 toolchain and generic Msys2 tools
+to the PATH environment variable. This can be accomplished via the GUI (search for system environment variable),
+or via the following command (assuming it was installed to the default directory):
+
+.. sourcecode:: console
+
+    C:\> setx PATH "C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%;" /m
+
+Note: if the MinGW64 binary directory doesn't precede the Windows/System32 directory (already in ``%PATH%``),
+the documentation build will fail since Windows has a conflicting ``convert`` command (FAT-to-NTFS). Similarly,
+if the Msys64 binary directory doesn't precede the Windows/System2 directory, running the ``bash`` command will
+result in Windows trying to run the Windows Subsystem for Linux (WSL) bash shell.
+
+Accessing the MinGW64 shell
++++++++++++++++++++++++++++
+
+After installing Msys2 and adding the binary directories to the ``PATH``, we can access the Unix-like MinGW64 shell
+and use the Pacman package manager.
+
+The Pacman package manager is similar to the one used by Arch Linux, and can be accessed via one of the Msys2 shells.
+In this case, we will be using the MinGW64 shell. We can take this opportunity to update the package cache and packages.
+
+.. sourcecode:: console
+
+    C:\ns-3-dev\> set MSYSTEM MINGW64
+    C:\ns-3-dev\> bash
+    /c/ns-3-dev/ MINGW64$ pacman -Syu
+
+Pacman will request you to close the shell and re-open it to proceed after the upgrade.
+
+
+Minimal requirements for C++ (release)
+++++++++++++++++++++++++++++++++++++++
+
+This is the minimal set of packages needed to run ns-3 C++ programs from a released tarball.
+
+.. sourcecode:: console
+
+    /c/ns-3-dev/ MINGW64$ pacman -S \
+    mingw-w64-x86_64-toolchain \
+    mingw-w64-x86_64-cmake \
+    mingw-w64-x86_64-ninja \
+    mingw-w64-x86_64-grep \
+    mingw-w64-x86_64-sed \
+    mingw-w64-x86_64-python
+
+
+Netanim animator
+++++++++++++++++
+
+Qt5 development tools are needed for Netanim animator. Qt4 will also work but we have migrated to qt5.
+
+.. sourcecode:: console
+
+    /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-qt5 git
+
+
+Support for MPI-based distributed emulation
++++++++++++++++++++++++++++++++++++++++++++
+
+The MPI setup requires two parts.
+
+The first part is the Microsoft MPI SDK required to build the MPI applications,
+which is distributed via Msys2.
+
+.. sourcecode:: console
+
+    /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-msmpi
+
+.. _Microsoft MPI: https://www.microsoft.com/en-us/download/details.aspx?id=100593
+
+The second part is the `Microsoft MPI`_ executors (mpiexec, mpirun) package,
+which is distributed as an installable (``msmpisetup.exe``).
+
+After installing it, the path containing the executors also need to be included to ``PATH``
+environment variable of the Windows and/or the MinGW64 shell, depending on whether
+you want to run MPI programs in either shell or both of them.
+
+.. sourcecode:: console
+
+    C:\ns-3-dev\> setx PATH "%PATH%;C:\Program Files\Microsoft MPI\Bin" /m
+    C:\ns-3-dev\> set MSYSTEM MINGW64
+    C:\ns-3-dev\> bash
+    /c/ns-3-dev/ MINGW64$ echo "export PATH=$PATH:/c/Program\ Files/Microsoft\ MPI/Bin" >> ~/.bashrc
+
+Debugging
++++++++++
+
+GDB is installed along with the mingw-w64-x86_64-toolchain package.
+
+Doxygen and related inline documentation
+++++++++++++++++++++++++++++++++++++++++
+
+To build the Doxygen-based documentation, we need doxygen and a Latex toolchain.
+Getting Doxygen from Msys2 is straightforward.
+
+.. sourcecode:: console
+
+  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-imagemagick mingw-w64-x86_64-doxygen mingw-w64-x86_64-graphviz
+
+For unknown reasons, the texlive packages in Msys2 are broken,
+so we recommend manually installing the official Texlive.
+
+.. _Texlive for Windows: https://tug.org/texlive/windows.html
+
+Start by installing `Texlive for Windows`_. It can be installed either via the network installer or
+directly from the ISO with all optional packages.
+
+To install it with the network installer, start by creating a texlive folder and downloading the
+texlive configuration profile below. You can change the installation directory (starting with
+``C:/texlive/2022``).
+
+.. sourcecode:: raw
+
+    selected_scheme scheme-custom
+    TEXDIR C:/texlive/2022
+    TEXMFCONFIG ~/.texlive2022/texmf-config
+    TEXMFHOME ~/texmf
+    TEXMFLOCAL C:/texlive/texmf-local
+    TEXMFSYSCONFIG C:/texlive/2022/texmf-config
+    TEXMFSYSVAR C:/texlive/2022/texmf-var
+    TEXMFVAR ~/.texlive2022/texmf-var
+    binary_win32 1
+    collection-basic 1
+    collection-bibtexextra 1
+    collection-binextra 1
+    collection-context 1
+    collection-fontsrecommended 1
+    collection-fontutils 1
+    collection-games 1
+    collection-humanities 1
+    collection-langenglish 1
+    collection-latex 1
+    collection-latexextra 1
+    collection-latexrecommended 1
+    collection-luatex 1
+    collection-mathscience 1
+    collection-metapost 1
+    collection-music 1
+    collection-pictures 1
+    collection-plaingeneric 1
+    collection-pstricks 1
+    collection-publishers 1
+    collection-texworks 1
+    collection-wintools 1
+    collection-xetex 1
+    instopt_adjustpath 1
+    instopt_adjustrepo 1
+    instopt_letter 0
+    instopt_portable 0
+    instopt_write18_restricted 1
+    tlpdbopt_autobackup 1
+    tlpdbopt_backupdir tlpkg/backups
+    tlpdbopt_create_formats 1
+    tlpdbopt_desktop_integration 1
+    tlpdbopt_file_assocs 1
+    tlpdbopt_generate_updmap 0
+    tlpdbopt_install_docfiles 0
+    tlpdbopt_install_srcfiles 0
+    tlpdbopt_post_code 1
+    tlpdbopt_sys_bin /usr/local/bin
+    tlpdbopt_sys_info /usr/local/share/info
+    tlpdbopt_sys_man /usr/local/share/man
+    tlpdbopt_w32_multi_user 0
+
+
+Then, download Texlive's web installer and unpack it to the texlive directory,
+and run the installer.
+
+.. sourcecode:: console
+
+    ~$ wget https://linorg.usp.br/CTAN/systems/texlive/tlnet/install-tl.zip
+    ~$ python3 -c "import shutil; shutil.unpack_archive('install-tl.zip', './')"
+    ~$ cd install-tl-*
+    ~/install-tl-20220923$ .\install-tl-windows.bat --no-gui --lang en -profile ..\texlive.profile
+
+After the installation finishes, add the installation directory to the PATH environment variable.
+
+.. sourcecode:: console
+
+    setx PATH "%PATH%;C:\texlive\2022\bin\win32" /m
+    C:\ns-3-dev\> set MSYSTEM MINGW64
+    C:\ns-3-dev\> bash
+    /c/ns-3-dev/ MINGW64$ echo "export PATH=$PATH:/c/texlive/2022/bin/win32" >> ~/.bashrc
+
+The ns-3 manual, models and tutorial
+++++++++++++++++++++++++++++++++++++
+
+These documents are written in reStructuredText for Sphinx (doc/tutorial, doc/manual, doc/models).
+The figures are typically written in dia.
+
+The documents can be generated into multiple formats, one of them being pdf,
+which requires the same Latex setup for doxygen.
+
+Sphinx can be installed via Msys2's package manager:
+
+.. sourcecode:: console
+
+  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-python-sphinx
+
+.. _Dia: https://sourceforge.net/projects/dia-installer/
+
+`Dia`_ on the other hand needs to be downloaded and installed manually.
+After installing it, or unzipping the package (example below), add Dia/bin to the PATH.
+
+.. sourcecode:: console
+
+    C:\ns-3-dev\> setx PATH "%PATH%;C:\dia_0.97.2_win32\bin" /m
+    C:\ns-3-dev\> set MSYSTEM MINGW64
+    C:\ns-3-dev\> bash
+    /c/ns-3-dev/ MINGW64$ echo "export PATH=$PATH:/c/dia_0.97.2_win32/bin" >> ~/.bashrc
+
+GNU Scientific Library (GSL)
+++++++++++++++++++++++++++++
+
+GSL is used to provide more accurate WiFi error models and can be installed with:
+
+.. sourcecode:: console
+
+  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-gsl
+
+Database support for statistics framework
++++++++++++++++++++++++++++++++++++++++++
+
+SQLite3 is installed along with the mingw-w64-x86_64-toolchain package.
+
+
+Xml-based version of the config store
++++++++++++++++++++++++++++++++++++++
+
+Requires libxml2 >= version 2.7, which can be installed with the following.
+
+.. sourcecode:: console
+
+  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-libxml2
+
+Support for openflow module
++++++++++++++++++++++++++++
+
+Requires some boost libraries that can be installed with the following.
+
+.. sourcecode:: console
+
+  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-boost
+
+Windows 10 Docker container
+***************************
+
+Docker containers are not as useful for Windows, since only Windows hosts can use them,
+however we add directions on how to use the Windows container and how to update the Docker
+image for reference.
+
+First, gather all dependencies previously mentioned to cover all supported features.
+Install them to a base directory for the container (e.g. ``C:\tools``).
+
+Save the following Dockerfile to the base directory.
+
+.. sourcecode:: docker
+
+    # It is really unfortunate we need a 16 GB base image just to get the installers working, but such is life
+    FROM mcr.microsoft.com/windows:20H2
+
+    # Copy the current host directory to the container
+    COPY .\\ C:/tools
+    WORKDIR C:\\tools
+
+    # Create temporary dir
+    RUN mkdir C:\\tools\\temp
+
+    # Export environment variables
+    RUN setx PATH "C:\\tools\\msys64\\mingw64\\bin;C:\\tools\\msys64\\usr\\bin;%PATH%" /m
+    RUN setx PATH "%PATH%;C:\\Program Files\\Microsoft MPI\\bin;C:\\tools\\dia\bin;C:\tools\texlive\2022\bin\win32" /m
+    RUN setx MSYSTEM "MINGW64" /m
+
+    # Install Msys2
+    RUN .\\msys2-x86_64-20220503.exe in --confirm-command --accept-messages --root C:\\tools\\msys64
+
+    # Update base packages
+    RUN C:\\tools\\msys64\\usr\\bin\\pacman -Syyuu --noconfirm
+
+    # Install base packages
+    RUN bash -c "echo export PATH=$PATH:/c/Program\ Files/Microsoft\ MPI/Bin >> /c/tools/msys64/home/$USER/.bashrc" && \
+        bash -c "echo export PATH=$PATH:/c/tools/dia/bin >> /c/tools/msys64/home/$USER/.bashrc" && \
+        bash -c "echo export PATH=$PATH:/c/tools/texlive/2022/bin/win32 >> /c/tools/msys64/home/$USER/.bashrc" && \
+        bash -c "pacman -S mingw-w64-x86_64-toolchain \
+                       mingw-w64-x86_64-cmake \
+                       mingw-w64-x86_64-ninja \
+                       mingw-w64-x86_64-grep \
+                       mingw-w64-x86_64-sed \
+                       mingw-w64-x86_64-qt5 \
+                       git \
+                       mingw-w64-x86_64-msmpi \
+                       mingw-w64-x86_64-uncrustify \
+                       mingw-w64-x86_64-imagemagick \
+                       mingw-w64-x86_64-doxygen \
+                       mingw-w64-x86_64-graphviz \
+                       mingw-w64-x86_64-python-sphinx \
+                       mingw-w64-x86_64-gsl \
+                       mingw-w64-x86_64-libxml2 \
+                       mingw-w64-x86_64-boost \
+                       --noconfirm"
+
+    # Install Microsoft MPI
+    RUN .\\msmpisetup.exe -unattend -force -full -verbose
+
+    # Install TexLive
+    RUN .\\install-tl-20220526\\install-tl-windows.bat --no-gui --lang en -profile .\\texlive.profile
+
+    # Move working directory to temp and start cmd
+    WORKDIR C:\\tools\\temp
+    ENTRYPOINT ["cmd"]
+
+Now you should be able to run ``docker build -t username/image .``.
+
+After building the container image, you should be able to use it:
+
+.. sourcecode:: console
+
+    $ docker run -it username/image
+    C:\tools\temp$ git clone https://gitlab.com/nsnam/ns-3-dev
+    C:\tools\temp$ cd ns-3-dev
+    C:\tools\temp\ns-3-dev$ python ns3 configure --enable-tests --enable-examples
+    C:\tools\temp\ns-3-dev$ python ns3 build
+    C:\tools\temp\ns-3-dev$ python test.py
+
+If testing succeeds, the container image can then be pushed to the Docker Hub using
+``docker push username/image``.
+
+
+Windows 10 Vagrant
+******************
+
+.. _VirtualBox: https://www.virtualbox.org/
+.. _Vagrant: https://www.vagrantup.com/
+.. _virtual machine: https://www.redhat.com/en/topics/virtualization/what-is-a-virtual-machine
+.. _boxes available: https://app.vagrantup.com/boxes/search
+
+As an alternative to manually setting up all dependencies required by ns-3,
+one can use a pre-packaged `virtual machine`_. There are many ways to do that,
+but for automation, the most used certainly is `Vagrant`_.
+
+Vagrant supports multiple virtual machine providers, is available in all platforms and is
+fairly straightforward to use and configure.
+
+There are many `boxes available`_ offering guests operating systems such as BSD, Mac, Linux and Windows.
+
+Using the pre-packaged Vagrant box
+++++++++++++++++++++++++++++++++++
+
+The provider for the ns-3 Vagrant box is `VirtualBox`_.
+
+The reference Windows virtual machine can be downloaded via the following Vagrant command
+
+.. sourcecode:: console
+
+    ~/mingw64_test $ vagrant init gabrielcarvfer/ns3_win10_mingw64
+
+After that, a Vagrantfile will be created in the current directory (in this case, mingw64_test).
+
+The file can be modified to adjust the number of processors and memory available to the virtual machine (VM).
+
+.. sourcecode:: console
+
+    ~/mingw64_test $ cat Vagrantfile
+    # -*- mode: ruby -*-
+    # vi: set ft=ruby :
+
+    # All Vagrant configuration is done below. The "2" in Vagrant.configure
+    # configures the configuration version (we support older styles for
+    # backwards compatibility). Please don't change it unless you know what
+    # you're doing.
+    Vagrant.configure("2") do |config|
+    # The most common configuration options are documented and commented below.
+    # For a complete reference, please see the online documentation at
+    # https://docs.vagrantup.com.
+
+    # Every Vagrant development environment requires a box. You can search for
+    # boxes at https://vagrantcloud.com/search.
+    config.vm.box = "gabrielcarvfer/ns3_win10_mingw64"
+
+    # Disable automatic box update checking. If you disable this, then
+    # boxes will only be checked for updates when the user runs
+    # `vagrant box outdated`. This is not recommended.
+    # config.vm.box_check_update = false
+
+    # Create a forwarded port mapping which allows access to a specific port
+    # within the machine from a port on the host machine. In the example below,
+    # accessing "localhost:8080" will access port 80 on the guest machine.
+    # NOTE: This will enable public access to the opened port
+    # config.vm.network "forwarded_port", guest: 80, host: 8080
+
+    # Create a forwarded port mapping which allows access to a specific port
+    # within the machine from a port on the host machine and only allow access
+    # via 127.0.0.1 to disable public access
+    # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
+
+    # Create a private network, which allows host-only access to the machine
+    # using a specific IP.
+    # config.vm.network "private_network", ip: "192.168.33.10"
+
+    # Create a public network, which generally matched to bridged network.
+    # Bridged networks make the machine appear as another physical device on
+    # your network.
+    # config.vm.network "public_network"
+
+    # Share an additional folder to the guest VM. The first argument is
+    # the path on the host to the actual folder. The second argument is
+    # the path on the guest to mount the folder. And the optional third
+    # argument is a set of non-required options.
+    # config.vm.synced_folder "../data", "/vagrant_data"
+
+    # Provider-specific configuration so you can fine-tune various
+    # backing providers for Vagrant. These expose provider-specific options.
+    # Example for VirtualBox:
+    #
+    # config.vm.provider "virtualbox" do |vb|
+    #   # Display the VirtualBox GUI when booting the machine
+    #   vb.gui = true
+    #
+    #   # Customize the amount of memory on the VM:
+    #   vb.memory = "1024"
+    # end
+    #
+    # View the documentation for the provider you are using for more
+    # information on available options.
+
+    # Enable provisioning with a shell script. Additional provisioners such as
+    # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
+    # documentation for more information about their specific syntax and use.
+    # config.vm.provision "shell", inline: <<-SHELL
+    #   apt-get update
+    #   apt-get install -y apache2
+    # SHELL
+    end
+
+We can uncomment the virtualbox provider block and change vCPUs and RAM.
+It is recommended never to match the number of vCPUs to the number of thread of the machine,
+or the host operating system can become unresponsive.
+For compilation workloads, it is recommended to allocate 1-2 GB of RAM per vCPU.
+
+.. _user Vagrantfile:
+
+.. sourcecode:: console
+
+    ~/mingw64_test/ $ cat Vagrantfile
+    # -*- mode: ruby -*-
+    # vi: set ft=ruby :
+    Vagrant.configure("2") do |config|
+    config.vm.box = "gabrielcarvfer/ns3_win10_mingw64"
+      config.vm.provider "virtualbox" do |vb|
+        vb.cpus = "8"
+        vb.memory = "8096" # 8GB of RAM
+      end
+    end
+
+After changing the settings, we can start the VM and login via ssh. The default password is "vagrant".
+
+.. sourcecode:: console
+
+    ~/mingw64_test/ $ vagrant up
+    ~/mingw64_test/ $ vagrant ssh
+    C:\Users\vagrant>
+
+
+We are now logged into the machine and ready to work. If you prefer to update the tools, get into the
+MinGW64 shell and run pacman.
+
+.. sourcecode:: console
+
+    C:\Users\vagrant\> set MSYSTEM MINGW64
+    C:\Users\vagrant\> bash
+    /c/Users/vagrant/ MINGW64$ pacman -Syu
+    /c/Users/vagrant/ MINGW64$ exit
+    C:\Users\vagrant\>
+
+At this point, we can clone ns-3 locally:
+
+.. sourcecode:: console
+
+    C:\Users\vagrant> git clone `https://gitlab.com/nsnam/ns-3-dev`
+    C:\Users\vagrant> cd ns-3-dev
+    C:\Users\vagrant\ns-3-dev> python3 ns3 configure --enable-tests --enable-examples --enable-mpi
+    C:\Users\vagrant\ns-3-dev> python3 test.py
+
+We can also access the ~/mingw64_test/ from the host machine, where the Vagrantfile resides, by accessing
+the synchronized folder C:\vagrant.
+If the Vagrantfile is in the host ns-3-dev directory, we can continue working on it.
+
+.. sourcecode:: console
+
+    C:\Users\vagrant> cd C:\vagrant
+    C:\vagrant\> python3 ns3 configure --enable-tests --enable-examples --enable-mpi
+    C:\vagrant\> python3 test.py
+
+If all the PATH variables were set for the MinGW64 shell, we can also use it instead of the
+default CMD shell.
+
+.. sourcecode:: console
+
+    C:\vagrant\> set MSYSTEM=MINGW64
+    C:\vagrant\> bash
+    /c/vagrant/ MINGW64$ ./ns3 clean
+    /c/vagrant/ MINGW64$ ./ns3 configure --enable-tests --enable-examples --enable-mpi
+    /c/vagrant/ MINGW64$ ./test.py
+
+To stop the Vagrant machine, we should close the SSH session then halt.
+
+.. sourcecode:: console
+
+    /c/vagrant/ MINGW64$ exit
+    C:\vagrant\> exit
+    ~/mingw64_test/ vagrant halt
+
+To destroy the machine (e.g. to restore the default settings), use the following.
+
+.. sourcecode:: console
+
+    vagrant destroy
+
+Packaging a new Vagrant box
++++++++++++++++++++++++++++
+
+**BEWARE: DO NOT CHANGE THE SETTINGS MENTIONED ON A REAL MACHINE**
+
+**THE SETTINGS ARE MEANT FOR A DISPOSABLE VIRTUAL MACHINE**
+
+.. _Windows 10 ISO: https://www.microsoft.com/pt-br/software-download/windows10ISO
+
+Start by downloading the `Windows 10 ISO`_.
+
+Then install `VirtualBox`_.
+
+Configure a VirtualBox VM and use the Windows 10 ISO file as the install source.
+
+During the installation, create a local user named "vagrant" and set its password to "vagrant".
+
+Check for any Windows updates and install them.
+
+The following commands assume administrative permissions and a PowerShell shell.
+
+Install the VirtualBox guest extensions
+=======================================
+
+On the VirtualBox GUI, click on ``Devices->Insert Guest Additions CD Image...``
+to download the VirtualBox guest extensions ISO and mount it as a CD drive on the guest VM.
+
+Run the installer to enable USB-passthrough, folder syncing and others.
+
+After installing, unmount the drive by removing it from the VM. Click on ``Settings->Storage``,
+select the guest drive and remove it clicking the button with an red ``x``.
+
+Install the OpenSSH server
+==========================
+
+Open PowerShell and run the following to install OpenSSH server,
+then set it to start automatically and open the firewall ports.
+
+.. sourcecode:: console
+
+    Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
+    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
+    Start-Service sshd
+    Set-Service -Name sshd -StartupType 'Automatic'
+    if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
+        Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
+        New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
+    } else {
+        Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
+    }
+
+Enable essential services and disable unnecessary ones
+======================================================
+
+Ensure the following services are set to **automatic** from the Services panel(services.msc):
+
+    - Base Filtering Engine
+    - Remote Procedure Call (RPC)
+    - DCOM Server Process Launcher
+    - RPC Endpoint Mapper
+    - Windows Firewall
+
+Ensure the following services are set to **disabled** from the Services panel(services.msc):
+
+    - Windows Update
+    - Windows Update Remediation
+    - Windows Search
+
+The same can be accomplished via the command-line with the following commands:
+
+.. sourcecode:: console
+
+    Set-Service -Name BFE -StartupType 'Automatic'
+    Set-Service -Name RpcSs -StartupType 'Automatic'
+    Set-Service -Name DcomLaunch -StartupType 'Automatic'
+    Set-Service -Name RpcEptMapper -StartupType 'Automatic'
+    Set-Service -Name mpssvc -StartupType 'Automatic'
+    Set-Service -Name wuauserv -StartupType 'Disabled'
+    Set-Service -Name WaaSMedicSvc -StartupType 'Disabled'
+    Set-Service -Name WSearch -StartupType 'Disabled'
+
+Install the packages you need
+=============================
+
+In this step we install all the software required by ns-3, as listed in the Section `Windows 10 package prerequisites`_.
+
+Disable Windows Defender
+========================
+
+After installing everything, it should be safe to disable the Windows security.
+
+Enter in the Windows Security settings and disable "anti-tamper protection".
+It rollbacks changes to security settings periodically.
+
+Enter in the Group Policy Editor (gpedit.msc) and disable:
+
+    - Realtime protection
+    - Behavior monitoring
+    - Scanning of archives, removable drives, network files, scripts
+    - Windows defender
+
+
+The same can be accomplished with the following command-line commands.
+
+.. sourcecode:: console
+
+    Set-MpPreference -DisableArchiveScanning 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableBehaviorMonitoring 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableIntrusionPreventionSystem 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableIOAVProtection 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableRemovableDriveScanning 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableBlockAtFirstSeen 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableScanningMappedNetworkDrivesForFullScan 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableScanningNetworkFiles 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableScriptScanning 1 -ErrorAction SilentlyContinue
+    Set-MpPreference -DisableRealtimeMonitoring 1 -ErrorAction SilentlyContinue
+    Set-Service -Name WdNisSvc -StartupType 'Disabled'
+    Set-Service -Name WinDefend -StartupType 'Disabled'
+    Set-Service -Name Sense -StartupType 'Disabled'
+    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Real-Time Protection" -Name SpyNetReporting -Value 0
+    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Real-Time Protection" -Name SubmitSamplesConsent -Value 0
+    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name TamperProtection -Value 4
+    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender" -Name DisableAntiSpyware -Value 1
+    Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" -Name DisableAntiSpyware -Value 1
+
+Note: the previous commands were an excerpt from the complete script in:
+https://github.com/jeremybeaume/tools/blob/master/disable-defender.ps1
+
+Turn off UAC notifications
+==========================
+
+The UAC notifications are the popups where you can give your OK to elevation to administrative privileges.
+It can be disabled via User Account Control Settings, or via the following commands.
+
+.. sourcecode:: console
+
+    reg ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f
+
+
+Change the strong password security Policy
+==========================================
+
+Open the Local Security Policy management window. Under Security Settings/Account Policy/Password Policy,
+disable the option saying "Password must meet complexity requirements".
+
+
+Testing
+=======
+
+After you reach this point, reboot your machine then log back in.
+
+Test if all your packages are working as expected.
+
+In the case of ns-3, try to enable all supported features, run the test.py and test-ns3.py suites.
+
+If everything works, then try to log in via SSH.
+
+If everything works, shut down the machine and prepare for packaging.
+
+The network interface configured should be a NAT. Other interfaces won't work correctly.
+
+Default Vagrantfile
+===================
+
+Vagrant can package VirtualBox VMs into Vagrant boxes without much more work.
+However, it still needs one more file to do that: the default Vagrantfile.
+
+This file will be used by Vagrant to configure the VM later on and how to connect to it.
+
+.. sourcecode:: ruby
+
+    # -*- mode: ruby -*-
+    # vi: set ft=ruby :
+
+    Vagrant.configure("2") do |config|
+    config.vm.box = "BOX_FILE.box" # name of the box
+    config.vm.communicator = "winssh" # indicate that we are talking to a windows box via ssh
+    config.vm.guest = :windows # indicate that the guest is a windows machine
+    config.vm.network :forwarded_port, guest: 3389, host: 3389, id: "rdp", auto_correct: true
+    config.ssh.password = "vagrant" # give the default password, so that it stops trying to use a .ssh key-pair
+    config.ssh.insert_key = false # let the user use a written password
+    config.ssh.keys_only = false
+    config.winssh.shell = "cmd" # select the default shell (could be cmd or powershell)
+      config.vm.provider :virtualbox do |v, override|
+          #v.gui = true # do not show the VirtualBox GUI if unset or set to false
+          v.customize ["modifyvm", :id, "--memory", 8096] # the default settings for the VM are 8GB of RAM
+          v.customize ["modifyvm", :id, "--cpus", 8] # the default settings for the VM are 8 vCPUs
+          v.customize ["modifyvm", :id, "--vram", 128] # 128 MB or vGPU RAM
+          v.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
+          v.customize ["setextradata", "global", "GUI/SuppressMessages", "all" ]
+      end
+    end
+
+This Vagrantfile will be baked into the Vagrant box, and can be modified by the `user Vagrantfile`_.
+After writing the Vagrantfile, we can call the following command.
+
+.. sourcecode:: console
+
+    vagrant package --vagrantfile Vagrantfile --base VIRTUALBOX_VM_NAME --output BOX_FILE.box
+
+It will take an awful long time depending on your drive.
+
+After it finishes, we can add the box to test it.
+
+.. sourcecode:: console
+
+    vagrant box add BOX_NAME BOX_FILE
+    vagrant up BOX_NAME
+    vagrant ssh
+
+If it can connect to the box, you are ready to upload it to the Vagrant servers.
+
+Publishing the Vagrant box
+==========================
+
+Create an account in https://app.vagrantup.com/ or log in with yours.
+In the dashboard, you can create a new box named BOX_NAME or select an existing one to update.
+
+After you select your box, click to add a provider. Pick Virtualbox.
+Calculate the MD5 hash of your BOX_FILE.box and fill the field then click to proceed.
+
+Upload the box.
+
+Now you should be able to download your box from the Vagrant servers via the the following command.
+
+.. sourcecode:: console
+
+    vagrant init yourUserName/BOX_NAME
+    vagrant up
+    vagrant ssh
+
+More information on Windows packaging to Vagrant boxes is available here:
+
+    - https://www.vagrantup.com/docs/boxes/base
+    - https://www.vagrantup.com/docs/vagrantfile/machine_settings
+    - https://www.vagrantup.com/docs/vagrantfile/ssh_settings
+    - https://www.vagrantup.com/docs/vagrantfile/winssh_settings
+    - https://github.com/pghalliday/windows-vagrant-boxes
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/main.h ns-3.38/doc/main.h
--- ns-3.37/doc/main.h	2022-11-01 17:37:24.375394800 -0700
+++ ns-3.38/doc/main.h	2023-03-16 20:54:23.500868000 -0700
@@ -2,6 +2,7 @@
  * \file
  * Main page of the Doxygen-generated documentation.
  */
+
 /**
  * \mainpage ns-3 Documentation
  *
@@ -70,6 +71,7 @@
  *     - wimax
  *
  */
+
 /**
  * \namespace ns3
  * \brief Every class exported by the ns3 library is enclosed in the
@@ -107,3 +109,12 @@
 #define NS3_LOG_ENABLE
 
 /**@}*/
+
+/**
+ * \page EnvironVar All Environment Variables
+ *
+ * All environment variables used by ns-3 are documented by module.
+ *
+ * \section environcore Core Environment Variables
+ * See \ref core-environ
+ */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/Makefile ns-3.38/doc/manual/Makefile
--- ns-3.37/doc/manual/Makefile	2022-11-01 17:37:24.375394800 -0700
+++ ns-3.38/doc/manual/Makefile	2023-03-16 20:54:23.500868000 -0700
@@ -26,7 +26,7 @@
 	source/hash-functions.rst \
 	source/helpers.rst \
 	source/how-to-write-tests.rst \
-	source/logging.rst \
+	source/logging-asserts.rst \
 	source/new-models.rst \
 	source/new-modules.rst \
 	source/object-model.rst \
@@ -45,7 +45,6 @@
 	source/tracing.rst \
 	source/troubleshoot.rst \
 	source/utilities.rst \
-	source/windows.rst \
 	source/working-with-cmake.rst \
 	source/working-with-git.rst \
 	source/working-with-gitlab-ci-local.rst \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/attributes.rst ns-3.38/doc/manual/source/attributes.rst
--- ns-3.37/doc/manual/source/attributes.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/attributes.rst	2023-03-16 20:54:23.532868400 -0700
@@ -32,7 +32,7 @@
 +=====================================+====================================+
 | Default Attribute values set when   | Affect all instances of the class. |
 | Attributes are defined in           |                                    |
-| :cpp:func:`GetTypeId ()`.           |                                    |
+| :cpp:func:`GetTypeId()`.            |                                    |
 +-------------------------------------+------------------------------------+
 | :cpp:class:`CommandLine`            | Affect all future instances.       |
 | :cpp:func:`Config::SetDefault()`    |                                    |
@@ -44,8 +44,8 @@
 | Helper methods with (string/        | Affects all instances created by   |
 | AttributeValue) parameter pairs     | the helper.                        |
 +-------------------------------------+------------------------------------+
-| :cpp:func:`MyClass::SetX ()`        | Alters this particular instance.   |
-| :cpp:func:`Object::SetAttribute ()` | Generally this is the only form    |
+| :cpp:func:`MyClass::SetX()`         | Alters this particular instance.   |
+| :cpp:func:`Object::SetAttribute()`  | Generally this is the only form    |
 | :cpp:func:`Config::Set()`           | which can be scheduled to alter    |
 |                                     | an instance once the simulation    |
 |                                     | is running.                        |
@@ -99,7 +99,7 @@
 For most basic usage (syntax), treat a smart pointer like a regular pointer::
 
   Ptr<WifiNetDevice> nd = ...;
-  nd->CallSomeFunction ();
+  nd->CallSomeFunction();
   // etc.
 
 So how do you get a smart pointer to an object, as in the first line
@@ -111,24 +111,24 @@
 As we discussed above in :ref:`Memory-management-and-class-Ptr`, at the
 lowest-level API, objects of type :cpp:class:`Object` are not instantiated
 using ``operator new`` as usual but instead by a templated function called
-:cpp:func:`CreateObject ()`.
+:cpp:func:`CreateObject()`.
 
 A typical way to create such an object is as follows::
 
-  Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice> ();
+  Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice>();
 
 You can think of this as being functionally equivalent to::
 
-  WifiNetDevice* nd = new WifiNetDevice ();
+  WifiNetDevice* nd = new WifiNetDevice();
 
 Objects that derive from :cpp:class:`Object` must be allocated on the heap
-using :cpp:func:`CreateObject ()`. Those deriving from :cpp:class:`ObjectBase`,
+using :cpp:func:`CreateObject()`. Those deriving from :cpp:class:`ObjectBase`,
 such as |ns3| helper functions and packet headers and trailers,
 can be allocated on the stack.
 
-In some scripts, you may not see a lot of :cpp:func:`CreateObject ()` calls
+In some scripts, you may not see a lot of :cpp:func:`CreateObject()` calls
 in the code; this is because there are some helper objects in effect
-that are doing the :cpp:func:`CreateObject ()` calls for you.
+that are doing the :cpp:func:`CreateObject()` calls for you.
 
 TypeId
 ++++++
@@ -150,39 +150,39 @@
 example: class :cpp:class:`Node`.
 
 The public header file ``node.h`` has a declaration that includes
-a static :cpp:func:`GetTypeId ()` function call::
+a static :cpp:func:`GetTypeId()` function call::
 
     class Node : public Object
     {
     public:
-      static TypeId GetTypeId (void);
+      static TypeId GetTypeId();
       ...
 
 This is defined in the ``node.cc`` file as follows::
 
     TypeId
-    Node::GetTypeId (void)
+    Node::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::Node")
-        .SetParent<Object> ()
-        .SetGroupName ("Network")
-        .AddConstructor<Node> ()
-        .AddAttribute ("DeviceList",
-                       "The list of devices associated to this Node.",
-                       ObjectVectorValue (),
-                       MakeObjectVectorAccessor (&Node::m_devices),
-                       MakeObjectVectorChecker<NetDevice> ())
-        .AddAttribute ("ApplicationList",
-                       "The list of applications associated to this Node.",
-                       ObjectVectorValue (),
-                       MakeObjectVectorAccessor (&Node::m_applications),
-                       MakeObjectVectorChecker<Application> ())
-        .AddAttribute ("Id",
-                       "The id (unique integer) of this Node.",
-                       TypeId::ATTR_GET, // allow only getting it.
-                       UintegerValue (0),
-                       MakeUintegerAccessor (&Node::m_id),
-                       MakeUintegerChecker<uint32_t> ())
+      static TypeId tid = TypeId("ns3::Node")
+        .SetParent<Object>()
+        .SetGroupName("Network")
+        .AddConstructor<Node>()
+        .AddAttribute("DeviceList",
+                      "The list of devices associated to this Node.",
+                      ObjectVectorValue(),
+                      MakeObjectVectorAccessor(&Node::m_devices),
+                      MakeObjectVectorChecker<NetDevice>())
+        .AddAttribute("ApplicationList",
+                      "The list of applications associated to this Node.",
+                      ObjectVectorValue(),
+                      MakeObjectVectorAccessor(&Node::m_applications),
+                      MakeObjectVectorChecker<Application>())
+        .AddAttribute("Id",
+                      "The id(unique integer) of this Node.",
+                      TypeId::ATTR_GET, // allow only getting it.
+                      UintegerValue(0),
+                      MakeUintegerAccessor(&Node::m_id),
+                      MakeUintegerChecker<uint32_t>())
         ;
       return tid;
     }
@@ -192,38 +192,38 @@
 includes a simple kind of RTTI in order to support ``dynamic_cast`` and
 ``typeid`` operators.
 
-The :cpp:func:`SetParent<Object> ()` call in the definition above is used in
+The :cpp:func:`SetParent<Object>()` call in the definition above is used in
 conjunction with our object aggregation mechanisms to allow safe up- and
-down-casting in inheritance trees during :cpp:func:`GetObject ()`.
+down-casting in inheritance trees during :cpp:func:`GetObject()`.
 It also enables subclasses to inherit the Attributes of their parent class.
 
-The :cpp:func:`AddConstructor<Node> ()` call is used in conjunction
+The :cpp:func:`AddConstructor<Node>()` call is used in conjunction
 with our abstract object factory mechanisms to allow us to construct
 C++ objects without forcing a user to know the concrete class of
 the object she is building.
 
-The three calls to :cpp:func:`AddAttribute ()` associate a given string
+The three calls to :cpp:func:`AddAttribute()` associate a given string
 with a strongly typed value in the class. Notice that you must provide
 a help string which may be displayed, for example, *via* command line
 processors. Each :cpp:class:`Attribute` is associated with mechanisms
 for accessing the underlying member variable in the object (for example,
-:cpp:func:`MakeUintegerAccessor ()` tells the generic :cpp:class:`Attribute`
+:cpp:func:`MakeUintegerAccessor()` tells the generic :cpp:class:`Attribute`
 code how to get to the node ID above). There are also "Checker" methods which
 are used to validate values against range limitations, such as maximum
 and minimum allowed values.
 
 When users want to create Nodes, they will usually call some form of
-:cpp:func:`CreateObject ()`,::
+:cpp:func:`CreateObject()`,::
 
-    Ptr<Node> n = CreateObject<Node> ();
+    Ptr<Node> n = CreateObject<Node>();
 
 or more abstractly, using an object factory, you can create a
 :cpp:class:`Node` object without even knowing the concrete C++ type::
 
     ObjectFactory factory;
     const std::string typeId = "ns3::Node'';
-    factory.SetTypeId (typeId);
-    Ptr<Object> node = factory.Create <Object> ();
+    factory.SetTypeId(typeId);
+    Ptr<Object> node = factory.Create <Object>();
 
 Both of these methods result in fully initialized attributes being available
 in the resulting :cpp:class:`Object` instances.
@@ -359,7 +359,7 @@
 
     class QueueBase : public Object {
     public:
-      static TypeId GetTypeId (void);
+      static TypeId GetTypeId();
       ...
 
     private:
@@ -408,34 +408,34 @@
   to that default.
 * Set or get the value on an already instantiated queue.
 
-The above things typically require providing ``Set ()`` and ``Get ()``
+The above things typically require providing ``Set()`` and ``Get()``
 functions, and some type of global default value.
 
 In the |ns3| attribute system, these value definitions and accessor function
 registrations are moved into the :cpp:class:`TypeId` class; *e.g*.::
 
-    NS_OBJECT_ENSURE_REGISTERED (QueueBase);
+    NS_OBJECT_ENSURE_REGISTERED(QueueBase);
 
     TypeId
-    QueueBase::GetTypeId (void)
+    QueueBase::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::DropTailQueue")
-        .SetParent<Queue> ()
-        .SetGroupName ("Network")
+      static TypeId tid = TypeId("ns3::DropTailQueue")
+        .SetParent<Queue>()
+        .SetGroupName("Network")
         ...
-        .AddAttribute ("MaxSize",
-                       "The max queue size",
-                       QueueSizeValue (QueueSize ("100p")),
-                       MakeQueueSizeAccessor (&QueueBase::SetMaxSize,
-                                              &QueueBase::GetMaxSize),
-                       MakeQueueSizeChecker ())
+        .AddAttribute("MaxSize",
+                      "The max queue size",
+                      QueueSizeValue(QueueSize("100p")),
+                      MakeQueueSizeAccessor(&QueueBase::SetMaxSize,
+                                            &QueueBase::GetMaxSize),
+                      MakeQueueSizeChecker())
         ...
         ;
 
       return tid;
     }
 
-The :cpp:func:`AddAttribute ()` method is performing a number of things for the
+The :cpp:func:`AddAttribute()` method is performing a number of things for the
 :cpp:member:`m_maxSize` value:
 
 * Binding the (usually private) member variable :cpp:member:`m_maxSize`
@@ -452,7 +452,7 @@
 these values.
 
 Note that initialization of the attribute relies on the macro
-``NS_OBJECT_ENSURE_REGISTERED (QueueBase)`` being called; if you leave this
+``NS_OBJECT_ENSURE_REGISTERED(QueueBase)`` being called; if you leave this
 out of your new class implementation, your attributes will not be initialized
 correctly.
 
@@ -486,13 +486,13 @@
     //
 
     int
-    main (int argc, char *argv[])
+    main(int argc, char *argv[])
     {
 
       // Queues in ns-3 are objects that hold items (other objects) in
       // a queue structure.  The C++ implementation uses templates to
       // allow queues to hold various types of items, but the most
-      // common is a pointer to a packet (Ptr<Packet>).
+      // common is a pointer to a packet(Ptr<Packet>).
       //
       // The maximum queue size can either be enforced in bytes ('b') or
       // packets ('p').  A special type called the ns3::QueueSize can
@@ -505,12 +505,12 @@
       //
       // Here, we set it to 80 packets.  We could use one of two value types:
       // a string-based value or a QueueSizeValue value
-      Config::SetDefault ("ns3::QueueBase::MaxSize", StringValue ("80p"));
+      Config::SetDefault("ns3::QueueBase::MaxSize", StringValue("80p"));
       // The below function call is redundant
-      Config::SetDefault ("ns3::QueueBase::MaxSize", QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, 80)));
+      Config::SetDefault("ns3::QueueBase::MaxSize", QueueSizeValue(QueueSize(QueueSizeUnit::PACKETS, 80)));
 
 The main thing to notice in the above are the two equivalent calls to
-:cpp:func:`Config::SetDefault ()`.  This is how we set the default value
+:cpp:func:`Config::SetDefault()`.  This is how we set the default value
 for all subsequently instantiated :cpp:class:`DropTailQueue`\s.  We illustrate
 that two types of ``Value`` classes, a :cpp:class:`StringValue` and
 a :cpp:class:`QueueSizeValue` class, can be used to assign the value
@@ -532,21 +532,21 @@
       // For example, via "--ns3::QueueBase::MaxSize=80p"
       CommandLine cmd;
       // This provides yet another way to set the value from the command line:
-      cmd.AddValue ("maxSize", "ns3::QueueBase::MaxSize");
-      cmd.Parse (argc, argv);
+      cmd.AddValue("maxSize", "ns3::QueueBase::MaxSize");
+      cmd.Parse(argc, argv);
 
 Now, we will create a few objects using the low-level API.  Our
 newly created queues will not have :cpp:member:`m_maxSize` initialized to
-0 packets, as defined in the :cpp:func:`QueueBase::GetTypeId ()`
+0 packets, as defined in the :cpp:func:`QueueBase::GetTypeId()`
 function, but to 80 packets, because of what we did above with
 default values.::
 
-    Ptr<Node> n0 = CreateObject<Node> ();
+    Ptr<Node> n0 = CreateObject<Node>();
 
-    Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
-    n0->AddDevice (net0);
+    Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice>();
+    n0->AddDevice(net0);
 
-    Ptr<Queue<Packet> > q = CreateObject<DropTailQueue<Packet> > ();
+    Ptr<Queue<Packet>> q = CreateObject<DropTailQueue<Packet>> ();
     net0->AddQueue(q);
 
 At this point, we have created a single :cpp:class:`Node` (``n0``)
@@ -569,23 +569,23 @@
 
     Ptr<GridPositionAllocator> p =
       CreateObjectWithAttributes<GridPositionAllocator>
-        ("MinX", DoubleValue (-100.0),
-         "MinY", DoubleValue (-100.0),
-         "DeltaX", DoubleValue (5.0),
-         "DeltaY", DoubleValue (20.0),
-         "GridWidth", UintegerValue (20),
-         "LayoutType", StringValue ("RowFirst"));
+       ("MinX", DoubleValue(-100.0),
+        "MinY", DoubleValue(-100.0),
+        "DeltaX", DoubleValue(5.0),
+        "DeltaY", DoubleValue(20.0),
+        "GridWidth", UintegerValue(20),
+        "LayoutType", StringValue("RowFirst"));
 
 or from the higher-level helper APIs, such as::
 
     mobility.SetPositionAllocator
-        ("ns3::GridPositionAllocator",
-         "MinX", DoubleValue (-100.0),
-         "MinY", DoubleValue (-100.0),
-         "DeltaX", DoubleValue (5.0),
-         "DeltaY", DoubleValue (20.0),
-         "GridWidth", UintegerValue (20),
-         "LayoutType", StringValue ("RowFirst"));
+       ("ns3::GridPositionAllocator",
+        "MinX", DoubleValue(-100.0),
+        "MinY", DoubleValue(-100.0),
+        "DeltaX", DoubleValue(5.0),
+        "DeltaY", DoubleValue(20.0),
+        "GridWidth", UintegerValue(20),
+        "LayoutType", StringValue("RowFirst"));
 
 We don't illustrate it here, but you can also configure an
 :cpp:class:`ObjectFactory` with new values for specific attributes.
@@ -596,9 +596,9 @@
 To review, there are several ways to set values for attributes for
 class instances *to be created in the future:*
 
-* :cpp:func:`Config::SetDefault ()`
-* :cpp:func:`CommandLine::AddValue ()`
-* :cpp:func:`CreateObjectWithAttributes<> ()`
+* :cpp:func:`Config::SetDefault()`
+* :cpp:func:`CommandLine::AddValue()`
+* :cpp:func:`CreateObjectWithAttributes<>()`
 * Various helper APIs
 
 But what if you've already created an instance, and you want
@@ -624,35 +624,35 @@
 ``"TxQueue"``::
 
     PointerValue ptr;
-    net0->GetAttribute ("TxQueue", ptr);
-    Ptr<Queue<Packet> > txQueue = ptr.Get<Queue<Packet> > ();
+    net0->GetAttribute("TxQueue", ptr);
+    Ptr<Queue<Packet>> txQueue = ptr.Get<Queue<Packet>>();
 
-Using the :cpp:func:`GetObject ()` function, we can perform a safe downcast
+Using the :cpp:func:`GetObject()` function, we can perform a safe downcast
 to a :cpp:class:`DropTailQueue`.  The `NS_ASSERT` checks that the pointer is
 valid.
 
 ::
 
-    Ptr<DropTailQueue<Packet> > dtq = txQueue->GetObject <DropTailQueue<Packet> > ();
-    NS_ASSERT (dtq != 0);
+    Ptr<DropTailQueue<Packet>> dtq = txQueue->GetObject<DropTailQueue<Packet>>();
+    NS_ASSERT (dtq);
 
 Next, we can get the value of an attribute on this queue.  We have introduced
 wrapper ``Value`` classes for the underlying data types, similar
 to Java wrappers around these types, since the attribute system stores values
 serialized to strings, and not disparate types.  Here, the attribute value
-is assigned to a :cpp:class:`QueueSizeValue`, and the :cpp:func:`Get ()`
 method on this value produces the (unwrapped) ``QueueSize``.  That is,
+is assigned to a :cpp:class:`QueueSizeValue`, and the :cpp:func:`Get()`
 the variable `limit` is written into by the GetAttribute method.::
 
     QueueSizeValue limit;
-    dtq->GetAttribute ("MaxSize", limit);
-    NS_LOG_INFO ("1.  dtq limit: " << limit.Get ());
+    dtq->GetAttribute("MaxSize", limit);
+    NS_LOG_INFO("1.  dtq limit: " << limit.Get());
 
 Note that the above downcast is not really needed; we could have gotten
 the attribute value directly from ``txQueue``::
 
-    txQueue->GetAttribute ("MaxSize", limit);
-    NS_LOG_INFO ("2.  txQueue limit: " << limit.Get ());
+    txQueue->GetAttribute("MaxSize", limit);
+    NS_LOG_INFO("2.  txQueue limit: " << limit.Get());
 
 Now, let's set it to another value (60 packets).  Let's also make
 use of the StringValue shorthand notation to set the size by
@@ -661,9 +661,9 @@
 
 ::
 
-    txQueue->SetAttribute ("MaxSize", StringValue ("60p"));
-    txQueue->GetAttribute ("MaxSize", limit);
-    NS_LOG_INFO ("3.  txQueue limit changed: " << limit.Get ());
+    txQueue->SetAttribute("MaxSize", StringValue("60p"));
+    txQueue->GetAttribute("MaxSize", limit);
+    NS_LOG_INFO("3.  txQueue limit changed: " << limit.Get());
 
 Config Namespace Path
 =====================
@@ -675,11 +675,11 @@
 
 ::
 
-    Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxSize",
-                 StringValue ("25p"));
-    txQueue->GetAttribute ("MaxSize", limit);
-    NS_LOG_INFO ("4.  txQueue limit changed through namespace: "
-                 << limit.Get ());
+    Config::Set("/NodeList/0/DeviceList/0/TxQueue/MaxSize",
+                StringValue("25p"));
+    txQueue->GetAttribute("MaxSize", limit);
+    NS_LOG_INFO("4.  txQueue limit changed through namespace: "
+                << limit.Get());
 
 The configuration path often has the form of
 ``".../<container name>/<index>/.../<attribute>/<attribute>"``
@@ -691,14 +691,14 @@
 attribute of the ``"TxQueue"`` of the chosen :cpp:class:`NetDevice`.
 
 We could have also used wildcards to set this value for all nodes and all net
-devices (which in this simple example has the same effect as the previous
-:cpp:func:`Config::Set ()`)::
+devices(which in this simple example has the same effect as the previous
+:cpp:func:`Config::Set()`)::
 
-    Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxSize",
-                 StringValue ("15p"));
-    txQueue->GetAttribute ("MaxSize", limit);
-    NS_LOG_INFO ("5.  txQueue limit changed through wildcarded namespace: "
-                 << limit.Get ());
+    Config::Set("/NodeList/*/DeviceList/*/TxQueue/MaxSize",
+                StringValue("15p"));
+    txQueue->GetAttribute("MaxSize", limit);
+    NS_LOG_INFO("5.  txQueue limit changed through wildcarded namespace: "
+                << limit.Get());
 
 If you run this program from the command line, you should see the following
 output corresponding to the steps we took above:
@@ -724,12 +724,12 @@
 
 ::
 
-    Names::Add ("server", n0);
-    Names::Add ("server/eth0", net0);
+    Names::Add("server", n0);
+    Names::Add("server/eth0", net0);
 
     ...
 
-    Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
+    Config::Set("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue(25));
 
 Here we've added the path elements ``"server"`` and ``"eth0"`` under
 the ``"/Names/"`` namespace, then used the resulting configuration path
@@ -755,8 +755,8 @@
 :cpp:class:`AttributeValue` is not really practical.
 So in the above, users have a choice of using strings or values::
 
-    p->Set ("cwnd", StringValue ("100")); // string-based setter
-    p->Set ("cwnd", IntegerValue (100)); // integer-based setter
+    p->Set("cwnd", StringValue("100")); // string-based setter
+    p->Set("cwnd", IntegerValue(100)); // integer-based setter
 
 The system provides some macros that help users declare and define
 new AttributeValue subclasses for new types that they want to introduce into
@@ -792,18 +792,18 @@
 variables is executed after an object is constructed. But what if you need the
 values assigned before the constructor body executes, because you need them in
 the logic of the constructor? There is a way to do this, used for example in the
-class :cpp:class:`ConfigStore`: call :cpp:func:`ObjectBase::ConstructSelf ()` as
+class :cpp:class:`ConfigStore`: call :cpp:func:`ObjectBase::ConstructSelf()` as
 follows::
 
-    ConfigStore::ConfigStore ()
+    ConfigStore::ConfigStore()
     {
-      ObjectBase::ConstructSelf (AttributeConstructionList ());
+      ObjectBase::ConstructSelf(AttributeConstructionList());
       // continue on with constructor.
     }
 
 Beware that the object and all its derived classes must also implement
-a :cpp:func:`GetInstanceTypeId ()` method. Otherwise
-the :cpp:func:`ObjectBase::ConstructSelf ()` will not be able to read
+a :cpp:func:`GetInstanceTypeId()` method. Otherwise
+the :cpp:func:`ObjectBase::ConstructSelf()` will not be able to read
 the attributes.
 
 Adding Attributes
@@ -834,11 +834,11 @@
 the user could declare the following addition in the runtime metadata system (to
 the :cpp:func:`GetTypeId` definition for :cpp:class:`TcpSocket`)::
 
-    .AddAttribute ("Congestion window",
-                   "Tcp congestion window (bytes)",
-                   UintegerValue (1),
-                   MakeUintegerAccessor (&TcpSocket::m_cWnd),
-                   MakeUintegerChecker<uint16_t> ())
+    .AddAttribute("Congestion window",
+                  "Tcp congestion window(bytes)",
+                  UintegerValue(1),
+                  MakeUintegerAccessor(&TcpSocket::m_cWnd),
+                  MakeUintegerChecker<uint16_t>())
 
 Now, the user with a pointer to a :cpp:class:`TcpSocket` instance
 can perform operations such as
@@ -863,7 +863,7 @@
     class MyMobility : public MobilityModel
     {
 
-This requires we declare the :cpp:func:`GetTypeId ()` function.
+This requires we declare the :cpp:func:`GetTypeId()` function.
 This is a one-line public function declaration::
 
     public:
@@ -871,31 +871,31 @@
        *  Register this type.
        *  \return The object TypeId.
        */
-      static TypeId GetTypeId (void);
+      static TypeId GetTypeId();
 
 We've already introduced what a :cpp:class:`TypeId` definition will look like
 in the ``my-mobility.cc`` implementation file::
 
-    NS_OBJECT_ENSURE_REGISTERED (MyMobility);
+    NS_OBJECT_ENSURE_REGISTERED(MyMobility);
 
     TypeId
-    MyMobility::GetTypeId (void)
+    MyMobility::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::MyMobility")
-        .SetParent<MobilityModel> ()
-        .SetGroupName ("Mobility")
-        .AddConstructor<MyMobility> ()
-        .AddAttribute ("Bounds",
-                       "Bounds of the area to cruise.",
-                       RectangleValue (Rectangle (0.0, 0.0, 100.0, 100.0)),
-                       MakeRectangleAccessor (&MyMobility::m_bounds),
-                       MakeRectangleChecker ())
-        .AddAttribute ("Time",
-                       "Change current direction and speed after moving for this delay.",
-                       TimeValue (Seconds (1.0)),
-                       MakeTimeAccessor (&MyMobility::m_modeTime),
-                       MakeTimeChecker ())
-        // etc (more parameters).
+      static TypeId tid = TypeId("ns3::MyMobility")
+        .SetParent<MobilityModel>()
+        .SetGroupName("Mobility")
+        .AddConstructor<MyMobility>()
+        .AddAttribute("Bounds",
+                      "Bounds of the area to cruise.",
+                      RectangleValue(Rectangle(0.0, 0.0, 100.0, 100.0)),
+                      MakeRectangleAccessor(&MyMobility::m_bounds),
+                      MakeRectangleChecker())
+        .AddAttribute("Time",
+                      "Change current direction and speed after moving for this delay.",
+                      // etc (more parameters).
+                      TimeValue(Seconds(1.0)),
+                      MakeTimeAccessor(&MyMobility::m_modeTime),
+                      MakeTimeChecker())
         ;
       return tid;
     }
@@ -903,14 +903,14 @@
 If we don't want to subclass from an existing class, in the header file
 we just inherit from :cpp:class:`ns3::Object`, and in the object file
 we set the parent class to :cpp:class:`ns3::Object` with
-``.SetParent<Object> ()``.
+``.SetParent<Object>()``.
 
 Typical mistakes here involve:
 
-* Not calling ``NS_OBJECT_ENSURE_REGISTERED ()``
-* Not calling the :cpp:func:`SetParent ()` method,
+* Not calling ``NS_OBJECT_ENSURE_REGISTERED()``
+* Not calling the :cpp:func:`SetParent()` method,
   or calling it with the wrong type.
-* Not calling the :cpp:func:`AddConstructor ()` method,
+* Not calling the :cpp:func:`AddConstructor()` method,
   or calling it with the wrong type.
 * Introducing a typographical error in the name of the :cpp:class:`TypeId`
   in its constructor.
@@ -950,27 +950,27 @@
 One macro call and two operators, must be added below the class declaration in
 order to turn a Rectangle into a value usable by the ``Attribute`` system::
 
-    std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
-    std::istream &operator >> (std::istream &is, Rectangle &rectangle);
+    std::ostream &operator <<(std::ostream &os, const Rectangle &rectangle);
+    std::istream &operator >>(std::istream &is, Rectangle &rectangle);
 
-    ATTRIBUTE_HELPER_HEADER (Rectangle);
+    ATTRIBUTE_HELPER_HEADER(Rectangle);
 
 Implementation File
 ~~~~~~~~~~~~~~~~~~~
 
 In the class definition (``.cc`` file), the code looks like this::
 
-    ATTRIBUTE_HELPER_CPP (Rectangle);
+    ATTRIBUTE_HELPER_CPP(Rectangle);
 
     std::ostream &
-    operator << (std::ostream &os, const Rectangle &rectangle)
+    operator <<(std::ostream &os, const Rectangle &rectangle)
     {
       os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|"
          << rectangle.yMax;
       return os;
     }
     std::istream &
-    operator >> (std::istream &is, Rectangle &rectangle)
+    operator >>(std::istream &is, Rectangle &rectangle)
      {
       char c1, c2, c3;
       is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3
@@ -979,13 +979,13 @@
           c2 != '|' ||
           c3 != '|')
         {
-          is.setstate (std::ios_base::failbit);
+          is.setstate(std::ios_base::failbit);
         }
       return is;
     }
 
 These stream operators simply convert from a string representation of the
-Rectangle (``"xMin|xMax|yMin|yMax"``) to the underlying Rectangle.  The modeler
+Rectangle(``"xMin|xMax|yMin|yMax"``) to the underlying Rectangle.  The modeler
 must specify these operators and the string syntactical representation of an
 instance of the new class.
 
@@ -1014,36 +1014,36 @@
     class ConfigExample : public Object
     {
     public:
-      static TypeId GetTypeId (void) {
-        static TypeId tid = TypeId ("ns3::A")
-          .SetParent<Object> ()
-          .AddAttribute ("TestInt16", "help text",
-                         IntegerValue (-2),
-                         MakeIntegerAccessor (&A::m_int16),
-                         MakeIntegerChecker<int16_t> ())
+      static TypeId GetTypeId() {
+        static TypeId tid = TypeId("ns3::A")
+          .SetParent<Object>()
+          .AddAttribute("TestInt16", "help text",
+                        IntegerValue(-2),
+                        MakeIntegerAccessor(&A::m_int16),
+                        MakeIntegerChecker<int16_t>())
           ;
           return tid;
         }
       int16_t m_int16;
     };
 
-    NS_OBJECT_ENSURE_REGISTERED (ConfigExample);
+    NS_OBJECT_ENSURE_REGISTERED(ConfigExample);
 
 Next, we use the Config subsystem to override the defaults in a couple of
 ways::
 
-      Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));
+      Config::SetDefault("ns3::ConfigExample::TestInt16", IntegerValue(-5));
 
-      Ptr<ConfigExample> a_obj = CreateObject<ConfigExample> ();
-      NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
-                           "Cannot set ConfigExample's integer attribute via Config::SetDefault");
+      Ptr<ConfigExample> a_obj = CreateObject<ConfigExample>();
+      NS_ABORT_MSG_UNLESS(a_obj->m_int16 == -5,
+                          "Cannot set ConfigExample's integer attribute via Config::SetDefault");
 
-      Ptr<ConfigExample> a2_obj = CreateObject<ConfigExample> ();
-      a2_obj->SetAttribute ("TestInt16", IntegerValue (-3));
+      Ptr<ConfigExample> a2_obj = CreateObject<ConfigExample>();
+      a2_obj->SetAttribute("TestInt16", IntegerValue(-3));
       IntegerValue iv;
-      a2_obj->GetAttribute ("TestInt16", iv);
-      NS_ABORT_MSG_UNLESS (iv.Get () == -3,
-                           "Cannot set ConfigExample's integer attribute via SetAttribute");
+      a2_obj->GetAttribute("TestInt16", iv);
+      NS_ABORT_MSG_UNLESS(iv.Get() == -3,
+                          "Cannot set ConfigExample's integer attribute via SetAttribute");
 
 The next statement is necessary to make sure that (one of) the objects
 created is rooted in the configuration namespace as an object instance.
@@ -1052,14 +1052,14 @@
 but here, since we are working at the core level, we need to create a
 new root namespace object::
 
-      Config::RegisterRootNamespaceObject (a2_obj);
+      Config::RegisterRootNamespaceObject(a2_obj);
 
 Writing
 +++++++
 
 Next, we want to output the configuration store.  The examples show how
 to do it in two formats, XML and raw text.  In practice, one should perform
-this step just before calling :cpp:func:`Simulator::Run ()` to save the
+this step just before calling :cpp:func:`Simulator::Run()` to save the
 final configuration just before running the simulation.
 
 There are three Attributes that govern the behavior of the ConfigStore:
@@ -1072,27 +1072,26 @@
 
 The example shows::
 
-      Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
-      Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
-      Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
+      Config::SetDefault("ns3::ConfigStore::Filename", StringValue("output-attributes.xml"));
+      Config::SetDefault("ns3::ConfigStore::FileFormat", StringValue("Xml"));
+      Config::SetDefault("ns3::ConfigStore::Mode", StringValue("Save"));
       ConfigStore outputConfig;
-      outputConfig.ConfigureDefaults ();
-      outputConfig.ConfigureAttributes ();
+      outputConfig.ConfigureDefaults();
+      outputConfig.ConfigureAttributes();
 
       // Output config store to txt format
-      Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
-      Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
-      Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
+      Config::SetDefault("ns3::ConfigStore::Filename", StringValue("output-attributes.txt"));
+      Config::SetDefault("ns3::ConfigStore::FileFormat", StringValue("RawText"));
+      Config::SetDefault("ns3::ConfigStore::Mode", StringValue("Save"));
       ConfigStore outputConfig2;
-      outputConfig2.ConfigureDefaults ();
-      outputConfig2.ConfigureAttributes ();
+      outputConfig2.ConfigureDefaults();
+      outputConfig2.ConfigureAttributes();
 
-      Simulator::Run ();
+      Simulator::Run();
 
-      Simulator::Destroy ();
+      Simulator::Destroy();
 
 Note the placement of these statements just prior to the
-:cpp:func:`Simulator::Run ()` statement.  This output logs all of the
 values in place just prior to starting the simulation (*i.e*. after
 all of the configuration has taken place).
 
@@ -1184,11 +1183,11 @@
 
 ::
 
-      Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
-      Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
-      Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
+      Config::SetDefault("ns3::ConfigStore::Filename", StringValue("input-defaults.xml"));
+      Config::SetDefault("ns3::ConfigStore::Mode", StringValue("Load"));
+      Config::SetDefault("ns3::ConfigStore::FileFormat", StringValue("Xml"));
       ConfigStore inputConfig;
-      inputConfig.ConfigureDefaults ();
+      inputConfig.ConfigureDefaults();
 
 Next, note that loading of input configuration data is limited to Attribute
 default (*i.e*. not instance) values, and global values.  Attribute instance
@@ -1219,31 +1218,31 @@
 
     #include "ns3/config-store-module.h"
     ...
-    int main (...)
+    int main(...)
     {
 
-      Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
-      Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
-      Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
+      Config::SetDefault("ns3::ConfigStore::Filename", StringValue("input-defaults.xml"));
+      Config::SetDefault("ns3::ConfigStore::Mode", StringValue("Load"));
+      Config::SetDefault("ns3::ConfigStore::FileFormat", StringValue("Xml"));
       ConfigStore inputConfig;
-      inputConfig.ConfigureDefaults ();
+      inputConfig.ConfigureDefaults();
 
       //
-      // Allow the user to override any of the defaults and the above Bind () at
+      // Allow the user to override any of the defaults and the above Bind() at
       // run-time, viacommand-line arguments
       //
       CommandLine cmd;
-      cmd.Parse (argc, argv);
+      cmd.Parse(argc, argv);
 
       // setup topology
       ...
 
-      // Invoke just before entering Simulator::Run ()
-      Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
-      Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
+      // Invoke just before entering Simulator::Run()
+      Config::SetDefault("ns3::ConfigStore::Filename", StringValue("output-attributes.xml"));
+      Config::SetDefault("ns3::ConfigStore::Mode", StringValue("Save"));
       ConfigStore outputConfig;
-      outputConfig.ConfigureAttributes ();
-      Simulator::Run ();
+      outputConfig.ConfigureAttributes();
+      Simulator::Run();
     }
 
 ConfigStore use cases (pre- and post-simulation)
@@ -1262,13 +1261,13 @@
 Hence, ConfigStore will not "report" their attributes if invoked earlier in the code.
 
 A typical workflow might involve running the simulation, calling ConfigStore
-at the end of the simulation (after ``Simulator::Run ()`` and before ``Simulator::Destroy ()``)
+at the end of the simulation (after ``Simulator::Run()`` and before ``Simulator::Destroy()``)
 This will show all the attributes in the Objects, both those with default values, and those
 with values changed during the simulation execution.
 
 To change these values, you'll need to either change the default (class-wide) attribute values
 (in this case call ConfigStore before the Object creation), or  specific object attribute
-(in this case call ConfigStore after the Object creation, typically just before ``Simulator::Run ()``.
+(in this case call ConfigStore after the Object creation, typically just before ``Simulator::Run()``.
 
 
 ConfigStore GUI
@@ -1278,7 +1277,7 @@
 GUI to access and change variables.
 
 Some screenshots are presented here. They are the result of using GtkConfig on
-``src/lte/examples/lena-dual-stripe.cc`` after ``Simulator::Run ()``.
+``src/lte/examples/lena-dual-stripe.cc`` after ``Simulator::Run()``.
 
 .. _GtkConfig:
 
@@ -1327,17 +1326,15 @@
 Usage is almost the same as the non-GTK-based version, but there
 are no :cpp:class:`ConfigStore` attributes involved::
 
-  // Invoke just before entering Simulator::Run ()
+  // Invoke just before entering Simulator::Run()
   GtkConfigStore config;
-  config.ConfigureDefaults ();
-  config.ConfigureAttributes ();
+  config.ConfigureDefaults();
+  config.ConfigureAttributes();
 
 Now, when you run the script, a GUI should pop up, allowing you to open menus of
 attributes on different nodes/objects, and then launch the simulation execution
 when you are done.
 
 Note that "launch the simulation" means to proceed with the simulation script.
-If GtkConfigStore has been called after ``Simulator::Run ()`` the simulation will
+If GtkConfigStore has been called after ``Simulator::Run()`` the simulation will
 not be started again - it will just end.
-
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/callbacks.rst ns-3.38/doc/manual/source/callbacks.rst
--- ns-3.37/doc/manual/source/callbacks.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/callbacks.rst	2023-03-16 20:54:23.532868400 -0700
@@ -19,15 +19,15 @@
 
   class A {
   public:
-    void ReceiveInput ( // parameters );
+    void ReceiveInput( /* parameters */ );
     ...
   }
 
-  (in another source file:)
+and in another source file::
 
   class B {
   public:
-    void DoSomething (void);
+    void DoSomething();
     ...
 
   private:
@@ -38,7 +38,7 @@
   B::DoSomething()
   {
     // Tell a_instance that something happened
-    a_instance->ReceiveInput ( // parameters);
+    a_instance->ReceiveInput( /* parameters */ );
     ...
   }
 
@@ -58,7 +58,9 @@
 has been a source of problems in previous simulators, when researchers want to
 extend or modify the system to do different things (as they are apt to do in
 research). Consider, for example, a user who wants to add an IPsec security
-protocol sublayer between TCP and IP::
+protocol sublayer between TCP and IP:
+
+.. code-block::text
 
   ------------                   -----------
   |   TCP    |                   |  TCP    |
@@ -104,7 +106,7 @@
 the value 0. If you want to initialize this pointer to something meaningful, you
 have to have a function with a matching signature. In this case::
 
-  int MyFunction (int arg) {}
+  int MyFunction(int arg) {}
 
 If you have this target, you can initialize the variable to point to your
 function like::
@@ -114,14 +116,14 @@
 You can then call MyFunction indirectly using the more suggestive form of the
 call::
 
-  int result = (*pfi) (1234);
+  int result = (*pfi)(1234);
 
 This is suggestive since it looks like you are dereferencing the function
 pointer just like you would dereference any pointer. Typically, however, people
 take advantage of the fact that the compiler knows what is going on and will
 just use a shorter form::
 
-  int result = pfi (1234);
+  int result = pfi(1234);
 
 Notice that the function pointer obeys value semantics, so you can pass it
 around like any other value. Typically, when you use an asynchronous interface
@@ -136,7 +138,7 @@
 The declaration of the variable providing the indirection looks only slightly
 different::
 
-  int (MyClass::*pmi) (int arg) = 0;
+  int (MyClass::*pmi)(int arg) = 0;
 
 This declares a variable named ``pmi`` just as the previous example declared a
 variable named ``pfi``. Since the will be to call a method of an instance of a
@@ -144,7 +146,7 @@
 
   class MyClass {
   public:
-    int MyMethod (int arg);
+    int MyMethod(int arg);
   };
 
 Given this class declaration, one would then initialize that variable like
@@ -158,18 +160,18 @@
 simplistic example of this is just calling a method indirectly (think virtual
 function)::
 
-  int (MyClass::*pmi) (int arg) = 0;  // Declare a PMI
+  int (MyClass::*pmi)(int arg) = 0;   // Declare a PMI
   pmi = &MyClass::MyMethod;           // Point at the implementation code
 
   MyClass myClass;                    // Need an instance of the class
-  (myClass.*pmi) (1234);              // Call the method with an object ptr
+  (myClass.*pmi)(1234);              // Call the method with an object ptr
 
 Just like in the C example, you can use this in an asynchronous call to another
 module which will *call back* using a method and an object pointer. The
 straightforward extension one might consider is to pass a pointer to the object
 and the PMI variable. The module would just do::
 
-  (*objectPtr.*pmi) (1234);
+  (*objectPtr.*pmi)(1234);
 
 to execute the callback on the desired object.
 
@@ -186,12 +188,12 @@
 
 A functor has two parts, a specific part and a generic part, related through
 inheritance. The calling code (the code that executes the callback) will execute
-a generic overloaded ``operator ()`` of a generic functor to cause the callback
+a generic overloaded ``operator()`` of a generic functor to cause the callback
 to be called. The called code (the code that wants to be called back) will have
-to provide a specialized implementation of the ``operator ()`` that performs the
+to provide a specialized implementation of the ``operator()`` that performs the
 class-specific work that caused the close-coupling problem above.
 
-With the specific functor and its overloaded ``operator ()`` created, the called
+With the specific functor and its overloaded ``operator()`` created, the called
 code then gives the specialized code to the module that will execute the
 callback (the calling code).
 
@@ -210,7 +212,7 @@
   class Functor
   {
   public:
-    virtual int operator() (T arg) = 0;
+    virtual int operator()(T arg) = 0;
   };
 
 The caller defines a specific part of the functor that really is just there to
@@ -226,7 +228,7 @@
       m_pmi = _pmi;
     }
 
-    virtual int operator() (ARG arg)
+    virtual int operator()(ARG arg)
     {
       (*m_p.*m_pmi)(arg);
     }
@@ -240,8 +242,8 @@
   class A
   {
   public:
-  A (int a0) : a (a0) {}
-  int Hello (int b0)
+  A(int a0) : a(a0) {}
+  int Hello(int b0)
   {
     std::cout << "Hello from A, a = " << a << " b0 = " << b0 << std::endl;
   }
@@ -269,19 +271,19 @@
 To use this, one could then declare some model code that takes a generic functor
 as a parameter::
 
-  void LibraryFunction (Functor functor);
+  void LibraryFunction(Functor functor);
 
 The code that will talk to the model would build a specific functor and pass it to ``LibraryFunction``::
 
   MyClass myClass;
-  SpecificFunctor<MyClass, int> functor (&myclass, MyClass::MyMethod);
+  SpecificFunctor<MyClass, int> functor(&myclass, MyClass::MyMethod);
 
 When ``LibraryFunction`` is done, it executes the callback using the
 ``operator()`` on the generic functor it was passed, and in this particular
 case, provides the integer argument::
 
   void
-  LibraryFunction (Functor functor)
+  LibraryFunction(Functor functor)
   {
     // Execute the library function
     functor(1234);
@@ -319,7 +321,7 @@
 Consider a function::
 
   static double
-  CbOne (double a, double b)
+  CbOne(double a, double b)
   {
     std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
     return a;
@@ -327,7 +329,7 @@
 
 Consider also the following main program snippet::
 
-  int main (int argc, char *argv[])
+  int main(int argc, char *argv[])
   {
     // return type: double
     // first arg type: double
@@ -365,7 +367,7 @@
 callback-- this is important.  We can pass in any such properly-typed function
 to this callback.  Let's look at this more closely::
 
-  static   double CbOne (double a, double b) {}
+  static   double CbOne(double a, double b) {}
              ^             ^         ^
              |             |         |
              |             |         |
@@ -378,21 +380,21 @@
 Now, let's bind our callback "one" to the function that matches its signature::
 
   // build callback instance which points to cbOne function
-  one = MakeCallback (&CbOne);
+  one = MakeCallback(&CbOne);
 
 This call to ``MakeCallback`` is, in essence, creating one of the specialized
 functors mentioned above.  The variable declared using the ``Callback``
 template function is going to be playing the part of the generic functor.  The
-assignment ``one = MakeCallback (&CbOne)`` is the cast that converts the
+assignment ``one = MakeCallback(&CbOne)`` is the cast that converts the
 specialized functor known to the callee to a generic functor known to the caller.
 
 Then, later in the program, if the callback is needed, it can be used as follows::
 
-  NS_ASSERT (!one.IsNull ());
+  NS_ASSERT(!one.IsNull());
 
   // invoke cbOne function through callback instance
   double retOne;
-  retOne = one (10.0, 20.0);
+  retOne = one(10.0, 20.0);
 
 The check for ``IsNull()`` ensures that the callback is not null -- that there
 is a function to call behind this callback.  Then, ``one()`` executes the
@@ -410,13 +412,13 @@
 
   class MyCb {
   public:
-    int CbTwo (double a) {
+    int CbTwo(double a) {
         std::cout << "invoke cbTwo a=" << a << std::endl;
         return -5;
     }
   };
 
-  int main ()
+  int main()
   {
     ...
     // return type: int
@@ -424,7 +426,7 @@
     Callback<int, double> two;
     MyCb cb;
     // build callback instance which points to MyCb::cbTwo
-    two = MakeCallback (&MyCb::CbTwo, &cb);
+    two = MakeCallback(&MyCb::CbTwo, &cb);
     ...
   }
 
@@ -432,7 +434,7 @@
 Recall from the background section above that ``Operator()`` will use the pointer to
 member syntax when it executes on an object::
 
-      virtual int operator() (ARG arg)
+      virtual int operator()(ARG arg)
       {
         (*m_p.*m_pmi)(arg);
       }
@@ -440,11 +442,11 @@
 And so we needed to provide the two variables (``m_p`` and ``m_pmi``) when
 we made the specific functor.  The line::
 
-    two = MakeCallback (&MyCb::CbTwo, &cb);
+    two = MakeCallback(&MyCb::CbTwo, &cb);
 
-does precisely that.  In this case, when ``two ()`` is invoked::
+does precisely that.  In this case, when ``two()`` is invoked::
 
-  int result = two (1.0);
+  int result = two(1.0);
 
 will result in a call to the ``CbTwo`` member function (method) on the object
 pointed to by ``&cb``.
@@ -457,8 +459,8 @@
 callback, which is preferable to simply passing "0" as an argument;
 it is the ``MakeNullCallback<>`` construct::
 
-  two = MakeNullCallback<int, double> ();
-  NS_ASSERT (two.IsNull ());
+  two = MakeNullCallback<int, double>();
+  NS_ASSERT(two.IsNull());
 
 Invoking a null callback is just like invoking a null function pointer: it will
 crash at runtime.
@@ -484,14 +486,14 @@
 calls an object that actually writes the packet to disk in the pcap file
 format.  The signature of one of these functions will be::
 
-  static void DefaultSink (Ptr<PcapFileWrapper> file, Ptr<const Packet> p);
+  static void DefaultSink(Ptr<PcapFileWrapper> file, Ptr<const Packet> p);
 
 The static keyword means this is a static function which does not need a
 ``this`` pointer, so it will be using C-style callbacks.  We don't want the
 calling code to have to know about anything but the Packet.  What we want in
 the calling code is just a call that looks like::
 
-  m_promiscSnifferTrace (m_currentPkt);
+  m_promiscSnifferTrace(m_currentPkt);
 
 What we want to do is to *bind* the ``Ptr<PcapFileWriter> file`` to the
 specific callback implementation when it is created and arrange for the
@@ -501,7 +503,7 @@
 takes the same parameters as the ``MakeCallback`` template function but also
 takes the parameters to be bound.  In the case of the example above::
 
-    MakeBoundCallback (&DefaultSink, file);
+    MakeBoundCallback(&DefaultSink, file);
 
 will create a specific callback implementation that knows to add in the extra
 bound arguments.  Conceptually, it extends the specific functor described above
@@ -518,7 +520,7 @@
         m_boundArg = boundArg;
       }
 
-      virtual int operator() (ARG arg)
+      virtual int operator()(ARG arg)
       {
         (*m_p.*m_pmi)(m_boundArg, arg);
       }
@@ -532,7 +534,7 @@
 in the functor / callback object itself.  When the ``operator()`` is invoked with
 the single parameter, as in::
 
-  m_promiscSnifferTrace (m_currentPkt);
+  m_promiscSnifferTrace(m_currentPkt);
 
 the implementation of ``operator()`` adds the bound parameter into the actual
 function call::
@@ -542,20 +544,20 @@
 It's possible to bind two or three arguments as well.  Say we have a function with
 signature::
 
-  static void NotifyEvent (Ptr<A> a, Ptr<B> b, MyEventType e);
+  static void NotifyEvent(Ptr<A> a, Ptr<B> b, MyEventType e);
 
 One can create bound callback binding first two arguments like::
 
-  MakeBoundCallback (&NotifyEvent, a1, b1);
+  MakeBoundCallback(&NotifyEvent, a1, b1);
 
 assuming `a1` and `b1` are objects of type `A` and `B` respectively.  Similarly for
 three arguments one would have function with a signature::
 
-  static void NotifyEvent (Ptr<A> a, Ptr<B> b, MyEventType e);
+  static void NotifyEvent(Ptr<A> a, Ptr<B> b, MyEventType e);
 
 Binding three arguments in done with::
 
-  MakeBoundCallback (&NotifyEvent, a1, b1, c1);
+  MakeBoundCallback(&NotifyEvent, a1, b1, c1);
 
 again assuming `a1`, `b1` and `c1` are objects of type `A`, `B` and `C` respectively.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/conf.py ns-3.38/doc/manual/source/conf.py
--- ns-3.37/doc/manual/source/conf.py	2022-11-01 17:39:10.947703100 -0700
+++ ns-3.38/doc/manual/source/conf.py	2023-03-16 20:54:51.628957000 -0700
@@ -73,9 +73,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.37'
+version = u'ns-3.38'
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.37'
+release = u'ns-3.38'
 
 # 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.37/doc/manual/source/develop.rst ns-3.38/doc/manual/source/develop.rst
--- ns-3.37/doc/manual/source/develop.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/develop.rst	2023-03-16 20:54:23.532868400 -0700
@@ -10,12 +10,10 @@
 
    working-with-git
    working-with-cmake
-   logging
+   logging-asserts
    tests
    new-models
    new-modules
    documentation
    profiling
    working-with-gitlab-ci-local
-   windows
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/documentation.rst ns-3.38/doc/manual/source/documentation.rst
--- ns-3.37/doc/manual/source/documentation.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/documentation.rst	2023-03-16 20:54:23.532868400 -0700
@@ -213,7 +213,7 @@
   |   The ``Frobnitz`` is accessed by::  | The ``Frobnitz`` is accessed by::  |
   |                                      |                                    |
   |     Foo::Frobnitz frob;              |   Foo::Frobnitz frob;              |
-  |     frob.Set (...);                  |   frob.Set (...);                  |
+  |     frob.Set(...);                   |   frob.Set(...);                   |
   +--------------------------------------+------------------------------------+
 
   To use a specific syntax highlighter, for example, ``bash`` shell commands:
@@ -315,7 +315,7 @@
    *  Understanding this material shouldn't be necessary to using
    *  the class or method.
    */
-   void ExampleFunction (const int foo, double & bar, const bool baz);
+   void ExampleFunction(const int foo, double & bar, const bool baz);
 
 In this style the Doxygen comment block begins with two \`*' characters:
 ``/**``, and precedes the item being documented.
@@ -324,7 +324,7 @@
 is appropriate::
 
   /** Destructor implementation. */
-  void DoDispose ();
+  void DoDispose();
 
   int m_count;  //!< Count of ...
 
@@ -355,11 +355,8 @@
   #. In the sub class mark inherited functions with an ordinary comment::
 
        // Inherited methods
-       virtual void FooBar (void);
-       virtual int BarFoo (double baz);
-
-     Note that the signatures have to match exactly, so include the formal
-     argument ``(void)``
+       virtual void FooBar();
+       virtual int BarFoo(double baz);
 
      This doesn't work for static functions; see ``GetTypeId``, below, for an
      example.
@@ -599,7 +596,7 @@
      * \tparam U \deduced The argument type.
      * \param [in] a The argument.
      */
-    template <typename T, typename U> T Function (U a);
+    template <typename T, typename U> T Function(U a);
 
   * Use ``\tparam U \deduced`` because the type ``U`` can be deduced at
     the site where the template is invoked.  Basically deduction can only
@@ -607,7 +604,7 @@
 
   * Use ``\tparam T \explicit`` because the type ``T`` can't be deduced;
     it must be given explicitly at the invocation site, as in
-    ``Create<MyObject> (...)``
+    ``Create<MyObject>(...)``
 
 * ``\internal`` should be used only to set off a discussion of implementation
   details, not to mark ``private`` functions (they are already marked,
@@ -624,16 +621,16 @@
 
 * Default constructor/destructor::
 
-    MyClass ();   //!< Default constructor
-    ~MyClass ();  //!< Destructor
+    MyClass();   //!< Default constructor
+    ~MyClass();  //!< Destructor
 
 * Dummy destructor and DoDispose::
 
     /** Dummy destructor, see DoDispose. */
-    ~MyClass ();
+    ~MyClass();
 
     /** Destructor implementation */
-    virtual void DoDispose ();
+    virtual void DoDispose();
 
 * GetTypeId::
 
@@ -641,7 +638,4 @@
      *  Register this type.
      *  \return The object TypeId.
      */
-    static TypeId GetTypeId (void);
-
-
-
+    static TypeId GetTypeId();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/events.rst ns-3.38/doc/manual/source/events.rst
--- ns-3.37/doc/manual/source/events.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/events.rst	2023-03-16 20:54:23.532868400 -0700
@@ -62,7 +62,7 @@
 
 ::
 
-   void handler (int arg0, int arg1)
+   void handler(int arg0, int arg1)
    {
      std::cout << "handler called with argument arg0=" << arg0 << " and
         arg1=" << arg1 << std::endl;
@@ -115,13 +115,13 @@
 
 ::
 
-  Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);
+  Simulator::Schedule(Time const &time, MEM mem_ptr, OBJ obj);
 
 vs.
 
 ::
 
-  Simulator::ScheduleWithContext (uint32_t context, Time const &time, MEM mem_ptr, OBJ obj);
+  Simulator::ScheduleWithContext(uint32_t context, Time const &time, MEM mem_ptr, OBJ obj);
 
 Readers who invest time and effort in developing or using a non-trivial
 simulation model will know the value of the |ns3| logging framework to
@@ -212,8 +212,8 @@
 You can choose which simulator engine to use by setting a global variable,
 for example::
 
-  GlobalValue::Bind ("SimulatorImplementationType",
-                     StringValue ("ns3::DistributedSimulatorImpl"));
+  GlobalValue::Bind("SimulatorImplementationType",
+                    StringValue("ns3::DistributedSimulatorImpl"));
 
 or by using a command line argument::
 
@@ -261,7 +261,7 @@
 
 A single adapter can be added on top of the `DefaultSimulatorImpl` by the same
 two methods above: binding the `"SimulatorImplementationType"` global value or
-using the command line argument.  To chain multipe adapters a different
+using the command line argument.  To chain multiple adapters a different
 approach must be used; see the `SimulatorAdapter::AddAdapter()`
 API documentation.
 
@@ -306,8 +306,8 @@
 future events.  The scheduler can be set with a global variable,
 similar to choosing the `SimulatorImpl`::
 
-  GlobalValue::Bind ("SchedulerType",
-                     StringValue ("ns3::DistributedSimulatorImpl"));
+  GlobalValue::Bind("SchedulerType",
+                    StringValue("ns3::DistributedSimulatorImpl"));
 
 The scheduler can be changed at any time via `Simulator::SetScheduler()`.
 The default scheduler is `MapScheduler` which uses a `std::map<>` to
@@ -335,7 +335,7 @@
 +=======================+=====================================+=============+==============+==========+==============+
 | CalendarScheduler     | `<std::list> []`                    | Constant    | Constant     | 24 bytes | 16 bytes     |
 +-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-| HeapScheduler         | Heap on `std::vector`               | Logarithmic | Logaritmic   | 24 bytes | 0            |
+| HeapScheduler         | Heap on `std::vector`               | Logarithmic | Logarithmic  | 24 bytes | 0            |
 +-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
 | ListScheduler         | `std::list`                         | Linear      | Constant     | 24 bytes | 16 bytes     |
 +-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
@@ -343,6 +343,3 @@
 +-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
 | PriorityQueueSchduler | `std::priority_queue<,std::vector>` | Logarithimc | Logarithims  | 24 bytes | 0            |
 +-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-
-
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/gnuplot.rst ns-3.38/doc/manual/source/gnuplot.rst
--- ns-3.37/doc/manual/source/gnuplot.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/gnuplot.rst	2023-03-16 20:54:23.532868400 -0700
@@ -9,7 +9,7 @@
 #. Create a gnuplot control file using |ns3|'s Gnuplot class.
 #. Create a gnuplot data file using values generated by |ns3|.
 
-This section is about method 1, i.e. it is about how to make a plot using |ns3|'s Gnuplot class.  If you are interested in method 2, see the "A Real Example" subsection under the "Tracing" section in the |ns3| `Tutorial <http://www.nsnam.org/tutorials.html>`_.
+This section is about method 1, i.e. it is about how to make a plot using |ns3|'s Gnuplot class.  If you are interested in method 2, see the "A Real Example" subsection under the "Tracing" section in the |ns3| `Tutorial <https://www.nsnam.org/docs/tutorial/html/index.html>`_.
 
 Creating Plots Using the Gnuplot Class
 **************************************
@@ -88,24 +88,24 @@
   std::string dataTitle               = "2-D Data";
 
   // Instantiate the plot and set its title.
-  Gnuplot plot (graphicsFileName);
-  plot.SetTitle (plotTitle);
+  Gnuplot plot(graphicsFileName);
+  plot.SetTitle(plotTitle);
 
   // Make the graphics file, which the plot file will create when it
   // is used with Gnuplot, be a PNG file.
-  plot.SetTerminal ("png");
+  plot.SetTerminal("png");
 
   // Set the labels for each axis.
-  plot.SetLegend ("X Values", "Y Values");
+  plot.SetLegend("X Values", "Y Values");
 
   // Set the range for the x axis.
-  plot.AppendExtra ("set xrange [-6:+6]");
+  plot.AppendExtra("set xrange [-6:+6]");
 
   // Instantiate the dataset, set its title, and make the points be
   // plotted along with connecting lines.
   Gnuplot2dDataset dataset;
-  dataset.SetTitle (dataTitle);
-  dataset.SetStyle (Gnuplot2dDataset::LINES_POINTS);
+  dataset.SetTitle(dataTitle);
+  dataset.SetStyle(Gnuplot2dDataset::LINES_POINTS);
 
   double x;
   double y;
@@ -121,20 +121,20 @@
       y = x * x;
 
       // Add this point.
-      dataset.Add (x, y);
+      dataset.Add(x, y);
     }
 
   // Add the dataset to the plot.
-  plot.AddDataset (dataset);
+  plot.AddDataset(dataset);
 
   // Open the plot file.
-  std::ofstream plotFile (plotFileName.c_str());
+  std::ofstream plotFile(plotFileName.c_str());
 
   // Write the plot file.
-  plot.GenerateOutput (plotFile);
+  plot.GenerateOutput(plotFile);
 
   // Close the plot file.
-  plotFile.close ();
+  plotFile.close();
 
 An Example 2-Dimensional Plot with Error Bars
 *********************************************
@@ -154,27 +154,27 @@
   std::string dataTitle               = "2-D Data With Error Bars";
 
   // Instantiate the plot and set its title.
-  Gnuplot plot (graphicsFileName);
-  plot.SetTitle (plotTitle);
+  Gnuplot plot(graphicsFileName);
+  plot.SetTitle(plotTitle);
 
   // Make the graphics file, which the plot file will create when it
   // is used with Gnuplot, be a PNG file.
-  plot.SetTerminal ("png");
+  plot.SetTerminal("png");
 
   // Set the labels for each axis.
-  plot.SetLegend ("X Values", "Y Values");
+  plot.SetLegend("X Values", "Y Values");
 
   // Set the range for the x axis.
-  plot.AppendExtra ("set xrange [-6:+6]");
+  plot.AppendExtra("set xrange [-6:+6]");
 
   // Instantiate the dataset, set its title, and make the points be
   // plotted with no connecting lines.
   Gnuplot2dDataset dataset;
-  dataset.SetTitle (dataTitle);
-  dataset.SetStyle (Gnuplot2dDataset::POINTS);
+  dataset.SetTitle(dataTitle);
+  dataset.SetStyle(Gnuplot2dDataset::POINTS);
 
   // Make the dataset have error bars in both the x and y directions.
-  dataset.SetErrorBars (Gnuplot2dDataset::XY);
+  dataset.SetErrorBars(Gnuplot2dDataset::XY);
 
   double x;
   double xErrorDelta;
@@ -199,20 +199,20 @@
 
       // Add this point with uncertainties in both the x and y
       // direction.
-      dataset.Add (x, y, xErrorDelta, yErrorDelta);
+      dataset.Add(x, y, xErrorDelta, yErrorDelta);
     }
 
   // Add the dataset to the plot.
-  plot.AddDataset (dataset);
+  plot.AddDataset(dataset);
 
   // Open the plot file.
-  std::ofstream plotFile (plotFileName.c_str());
+  std::ofstream plotFile(plotFileName.c_str());
 
   // Write the plot file.
-  plot.GenerateOutput (plotFile);
+  plot.GenerateOutput(plotFile);
 
   // Close the plot file.
-  plotFile.close ();
+  plotFile.close();
 
 An Example 3-Dimensional Plot
 *****************************
@@ -232,34 +232,34 @@
   std::string dataTitle               = "3-D Data";
 
   // Instantiate the plot and set its title.
-  Gnuplot plot (graphicsFileName);
-  plot.SetTitle (plotTitle);
+  Gnuplot plot(graphicsFileName);
+  plot.SetTitle(plotTitle);
 
   // Make the graphics file, which the plot file will create when it
   // is used with Gnuplot, be a PNG file.
-  plot.SetTerminal ("png");
+  plot.SetTerminal("png");
 
   // Rotate the plot 30 degrees around the x axis and then rotate the
   // plot 120 degrees around the new z axis.
-  plot.AppendExtra ("set view 30, 120, 1.0, 1.0");
+  plot.AppendExtra("set view 30, 120, 1.0, 1.0");
 
   // Make the zero for the z-axis be in the x-axis and y-axis plane.
-  plot.AppendExtra ("set ticslevel 0");
+  plot.AppendExtra("set ticslevel 0");
 
   // Set the labels for each axis.
-  plot.AppendExtra ("set xlabel 'X Values'");
-  plot.AppendExtra ("set ylabel 'Y Values'");
-  plot.AppendExtra ("set zlabel 'Z Values'");
+  plot.AppendExtra("set xlabel 'X Values'");
+  plot.AppendExtra("set ylabel 'Y Values'");
+  plot.AppendExtra("set zlabel 'Z Values'");
 
   // Set the ranges for the x and y axis.
-  plot.AppendExtra ("set xrange [-5:+5]");
-  plot.AppendExtra ("set yrange [-5:+5]");
+  plot.AppendExtra("set xrange [-5:+5]");
+  plot.AppendExtra("set yrange [-5:+5]");
 
   // Instantiate the dataset, set its title, and make the points be
   // connected by lines.
   Gnuplot3dDataset dataset;
-  dataset.SetTitle (dataTitle);
-  dataset.SetStyle ("with lines");
+  dataset.SetTitle(dataTitle);
+  dataset.SetStyle("with lines");
 
   double x;
   double y;
@@ -278,22 +278,22 @@
           z = x * x * y * y;
 
           // Add this point.
-          dataset.Add (x, y, z);
+          dataset.Add(x, y, z);
         }
 
       // The blank line is necessary at the end of each x value's data
       // points for the 3-D surface grid to work.
-      dataset.AddEmptyLine ();
+      dataset.AddEmptyLine();
     }
 
   // Add the dataset to the plot.
-  plot.AddDataset (dataset);
+  plot.AddDataset(dataset);
 
   // Open the plot file.
-  std::ofstream plotFile (plotFileName.c_str());
+  std::ofstream plotFile(plotFileName.c_str());
 
   // Write the plot file.
-  plot.GenerateOutput (plotFile);
+  plot.GenerateOutput(plotFile);
 
   // Close the plot file.
-  plotFile.close ();
+  plotFile.close();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/hash-functions.rst ns-3.38/doc/manual/source/hash-functions.rst
--- ns-3.37/doc/manual/source/hash-functions.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/hash-functions.rst	2023-03-16 20:54:23.532868400 -0700
@@ -99,7 +99,7 @@
  * ``include`` the declaration in ``hash.h`` (at the point where
    ``hash-murmur3.h`` is included.
  * In your own code, instantiate a ``Hasher`` object via the constructor
-   ``Hasher (Ptr<Hash::Function::Foo> ())``
+   ``Hasher(Ptr<Hash::Function::Foo>())``
 
 
 If your hash function is a single function, e.g. ``hashf``, you don't
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/how-to-write-tests.rst ns-3.38/doc/manual/source/how-to-write-tests.rst
--- ns-3.37/doc/manual/source/how-to-write-tests.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/how-to-write-tests.rst	2023-03-16 20:54:23.532868400 -0700
@@ -60,8 +60,8 @@
 
 ::
 
-  RouterTestSuite::RouterTestSuite ()
-    : TestSuite ("router", UNIT)
+  RouterTestSuite::RouterTestSuite()
+    : TestSuite("router", UNIT)
 
 Try this command:
 
@@ -154,8 +154,8 @@
 ::
 
    #include "ns3/example-as-test.h"
-   static ns3::ExampleAsTestSuite g_modExampleOne ("mymodule-example-mod-example-one", "mod-example", NS_TEST_SOURCEDIR, "--arg-one");
-   static ns3::ExampleAsTestSuite g_modExampleTwo ("mymodule-example-mod-example-two", "mod-example", NS_TEST_SOURCEDIR, "--arg-two");
+   static ns3::ExampleAsTestSuite g_modExampleOne("mymodule-example-mod-example-one", "mod-example", NS_TEST_SOURCEDIR, "--arg-one");
+   static ns3::ExampleAsTestSuite g_modExampleTwo("mymodule-example-mod-example-two", "mod-example", NS_TEST_SOURCEDIR, "--arg-two");
 
 The arguments to the constructor are the name of the test suite, the
 example to run, the directory that contains the "good" reference file
@@ -240,11 +240,11 @@
      ...
    };
    void
-   BasicMultiLinkElementTest::DoRun (void)
+   BasicMultiLinkElementTest::DoRun()
    {
-     MultiLinkElement mle (WIFI_MAC_MGT_BEACON);
+     MultiLinkElement mle(WIFI_MAC_MGT_BEACON);
      // Fill in the Multi-Link Element
-     TestHeaderSerialization (mle, WIFI_MAC_MGT_BEACON);
+     TestHeaderSerialization(mle, WIFI_MAC_MGT_BEACON);
    }
 
 Examples of this approach are found, e.g., in ``src/wifi/test/wifi-eht-info-elems-test.cc``
@@ -266,4 +266,3 @@
 
 Presenting your output test data
 ********************************
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/index.rst ns-3.38/doc/manual/source/index.rst
--- ns-3.37/doc/manual/source/index.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/index.rst	2023-03-16 20:54:23.532868400 -0700
@@ -3,15 +3,24 @@
 ns-3 Manual
 ===========
 
-This is the *ns-3 Manual*. Primary documentation for the ns-3 project is
-available in five forms:
+This is the *ns-3 Manual*. Primary documentation for the ns-3 project is organized as
+follows:
 
-* `ns-3 Doxygen <https://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
-* Tutorial, Manual *(this document)*, and Model Library for the `latest release <https://www.nsnam.org/documentation/latest/>`_ and `development tree <https://www.nsnam.org/documentation/development-tree/>`_
-* `ns-3 wiki <https://www.nsnam.org/wiki>`_
+* Several guides that are version controlled for each release (the
+  `latest release <https://www.nsnam.org/documentation/latest/>`_) and
+  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+
+  * Tutorial
+  * Installation Guide
+  * Manual *(this document)*
+  * Model Library
+  * Contributing Guide
+* `ns-3 Doxygen <https://www.nsnam.org/docs/doxygen/index.html>`_: Documentation of the public APIs of
+  the simulator
+* `ns-3 wiki <https://www.nsnam.org/wiki/Main_Page>`_
 
 This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <https://www.sphinx-doc.org/>`_ and is maintained in the
-``doc/manual`` directory of ns-3's source code.
+``doc/manual`` directory of ns-3's source code.  Source file column width is 100 columns.
 
 .. toctree::
    :maxdepth: 2
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/logging-asserts.rst ns-3.38/doc/manual/source/logging-asserts.rst
--- ns-3.37/doc/manual/source/logging-asserts.rst	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/doc/manual/source/logging-asserts.rst	2023-03-16 20:54:23.532868400 -0700
@@ -0,0 +1,586 @@
+.. include:: replace.txt
+.. highlight:: cpp
+
+.. heading hierarchy:
+   ------------- Chapter
+   ************* Section (#.#)
+   ============= Subsection (#.#.#)
+   ############# Paragraph (no number)
+
+Logging
+-------
+
+The |ns3| logging facility can be used to monitor or debug the progress
+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|.
+
+The project makes no guarantee about whether logging output will remain
+the same over time.  Users are cautioned against building simulation output
+frameworks on top of logging code, as the output and the way the output
+is enabled may change over time.
+
+Overview
+********
+
+|ns3| logging statements are typically used to log various program
+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()``::
+
+  if (address == iaddr.GetBroadcast())
+    {
+      NS_LOG_LOGIC("For me (interface broadcast address)");
+      return true;
+     }
+
+If logging has been enabled for the ``Ipv4L3Protocol`` component at a severity
+of ``LOGIC`` 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.
+
+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
+effect of the options discussed below.
+
+Enabling Output
+***************
+
+There are two ways that users typically control log output.  The
+first is by setting the ``NS_LOG`` environment variable; e.g.:
+
+.. sourcecode:: bash
+
+   $ NS_LOG="*" ./ns3 run first
+
+will run the ``first`` tutorial program with all logging output.  (The
+specifics of the ``NS_LOG`` format will be discussed below.)
+
+This can be made more granular by selecting individual components:
+
+.. sourcecode:: bash
+
+   $ NS_LOG="Ipv4L3Protocol" ./ns3 run first
+
+The output can be further tailored with prefix options.
+
+The second way to enable logging is to use explicit statements in your
+program, such as in the ``first`` tutorial program::
+
+   int
+   main(int argc, char *argv[])
+   {
+     LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
+     LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);
+     ...
+
+(The meaning of ``LOG_LEVEL_INFO``, and other possible values,
+will be discussed below.)
+
+``NS_LOG`` Syntax
+*****************
+
+The ``NS_LOG`` environment variable contains a list of log components
+and options.  Log components are separated by \`:' characters:
+
+.. sourcecode:: bash
+
+   $ NS_LOG="<log-component>:<log-component>..."
+
+Options for each log component are given as flags after
+each log component:
+
+.. sourcecode:: bash
+
+   $ NS_LOG="<log-component>=<option>|<option>...:<log-component>..."
+
+Options control the severity and level for that component,
+and whether optional information should be included, such as the
+simulation time, simulation node, function name, and the symbolic severity.
+
+Log Components
+==============
+
+Generally a log component refers to a single source code ``.cc`` file,
+and encompasses the entire file.
+
+Some helpers have special methods to enable the logging of all components
+in a module, spanning different compilation units, but logically grouped
+together, such as the |ns3| wifi code::
+
+   WifiHelper wifiHelper;
+   wifiHelper.EnableLogComponents();
+
+The ``NS_LOG`` log component wildcard \`*' will enable all components.
+
+To see what log components are defined, any of these will work:
+
+.. sourcecode:: bash
+
+   $ NS_LOG="print-list" ./ns3 run ...
+
+   $ NS_LOG="foo"  # a token not matching any log-component
+
+The first form will print the name and enabled flags for all log components
+which are linked in; try it with ``scratch-simulator``.
+The second form prints all registered log components,
+then exit with an error.
+
+
+Severity and Level Options
+==========================
+
+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.
+
+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_FUNCTION``  Function tracing
+``LOG_LOGIC``     Control flow tracing within functions
+================  =====================================
+
+Typically one wants to see messages at a given severity class *and higher*.
+This is done by defining inclusive logging "levels":
+
+======================  ===========================================
+Level                   Meaning
+======================  ===========================================
+``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_ALL``       All severity classes.
+``LOG_ALL``             Synonym for ``LOG_LEVEL_ALL``
+======================  ===========================================
+
+The severity class and level options can be given in the ``NS_LOG``
+environment variable by these tokens:
+
+============  =================
+Class         Level
+============  =================
+``error``     ``level_error``
+``warn``      ``level_warn``
+``debug``     ``level_debug``
+``info``      ``level_info``
+``function``  ``level_function``
+``logic``     ``level_logic``
+..            | ``level_all``
+              | ``all``
+              | ``*``
+============  =================
+
+Using a severity class token enables log messages at that severity only.
+For example, ``NS_LOG="*=warn"`` won't output messages with severity ``error``.
+``NS_LOG="*=level_debug"`` will output messages at severity levels
+``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``.
+
+The ``NS_LOG`` severity level wildcard \`*' and ``all``
+are synonyms for ``level_all``.
+
+For log components merely mentioned in ``NS_LOG``
+
+.. sourcecode:: bash
+
+   $ NS_LOG="<log-component>:..."
+
+the default severity is ``LOG_LEVEL_ALL``.
+
+
+Prefix Options
+==============
+
+A number of prefixes can help identify
+where and when a message originated, and at what severity.
+
+The available prefix options (as ``enum`` constants) are
+
+======================  ===========================================
+Prefix Symbol           Meaning
+======================  ===========================================
+``LOG_PREFIX_FUNC``     Prefix the name of the calling function.
+``LOG_PREFIX_TIME``     Prefix the simulation time.
+``LOG_PREFIX_NODE``     Prefix the node id.
+``LOG_PREFIX_LEVEL``    Prefix the severity level.
+``LOG_PREFIX_ALL``      Enable all prefixes.
+======================  ===========================================
+
+The prefix options are described briefly below.
+
+The options can be given in the ``NS_LOG``
+environment variable by these tokens:
+
+================  =========
+Token             Alternate
+================  =========
+``prefix_func``   ``func``
+``prefix_time``   ``time``
+``prefix_node``   ``node``
+``prefix_level``  ``level``
+``prefix_all``    | ``all``
+                  | ``*``
+================  =========
+
+For log components merely mentioned in ``NS_LOG``
+
+.. sourcecode:: bash
+
+   $ NS_LOG="<log-component>:..."
+
+the default prefix options are ``LOG_PREFIX_ALL``.
+
+Severity Prefix
+###############
+
+The severity class of a message can be included with the options
+``prefix_level`` or ``level``.  For example, this value of ``NS_LOG``
+enables logging for all log components (\`*') and all severity
+classes (``=all``), and prefixes the message with the severity
+class (``|prefix_level``).
+
+.. sourcecode:: bash
+
+   $ NS_LOG="*=all|prefix_level" ./ns3 run scratch-simulator
+   Scratch Simulator
+   [ERROR] error message
+   [WARN] warn message
+   [DEBUG] debug message
+   [INFO] info message
+   [FUNCT] function message
+   [LOGIC] logic message
+
+Time Prefix
+###########
+
+The simulation time can be included with the options
+``prefix_time`` or ``time``.  This prints the simulation time in seconds.
+
+Node Prefix
+###########
+
+The simulation node id can be included with the options
+``prefix_node`` or ``node``.
+
+Function Prefix
+###############
+
+The name of the calling function can be included with the options
+``prefix_func`` or ``func``.
+
+
+``NS_LOG`` Wildcards
+====================
+
+The log component wildcard \`*' will enable all components.  To
+enable all components at a specific severity level
+use ``*=<severity>``.
+
+The severity level option wildcard \`*' is a synonym for ``all``.
+This must occur before any \`|' characters separating options.
+To enable all severity classes, use ``<log-component>=*``,
+or ``<log-component>=*|<options>``.
+
+The option wildcard \`*' or token ``all`` enables all prefix options,
+but must occur *after* a \`|' character.  To enable a specific
+severity class or level, and all prefixes, use
+``<log-component>=<severity>|*``.
+
+The combined option wildcard ``**`` enables all severities and all prefixes;
+for example, ``<log-component>=**``.
+
+The uber-wildcard ``***`` enables all severities and all prefixes
+for all log components.  These are all equivalent:
+
+.. sourcecode:: bash
+
+   $ NS_LOG="***" ...      $ NS_LOG="*=all|*" ...        $ NS_LOG="*=*|all" ...
+   $ NS_LOG="*=**" ...     $ NS_LOG="*=level_all|*" ...  $ NS_LOG="*=*|prefix_all" ...
+   $ NS_LOG="*=*|*" ...
+
+Be advised:  even the trivial ``scratch-simulator`` produces over
+46K lines of output with ``NS_LOG="***"``!
+
+
+How to add logging to your code
+*******************************
+
+Adding logging to your code is very simple:
+
+1. Invoke the ``NS_LOG_COMPONENT_DEFINE(...);`` macro
+   inside of ``namespace ns3``.
+
+  Create a unique string identifier (usually based on the name of the file
+  and/or class defined within the file) and register it with a macro call
+  such as follows:
+
+  ::
+
+    namespace ns3 {
+
+    NS_LOG_COMPONENT_DEFINE("Ipv4L3Protocol");
+    ...
+
+  This registers ``Ipv4L3Protocol`` as a log component.
+
+  (The macro was carefully written to permit inclusion either within or
+  outside of namespace ``ns3``, and usage will vary across the codebase, but
+  the original intent was to register this *outside* of namespace ``ns3``
+  at file global scope.)
+
+2. Add logging statements (macro calls) to your functions and function bodies.
+
+In case you want to add logging statements to the methods of your template class
+(which are defined in an header file):
+
+1. Invoke the ``NS_LOG_TEMPLATE_DECLARE;`` macro in the private section of
+   your class declaration. For instance:
+
+  ::
+
+    template <typename Item>
+    class Queue : public QueueBase
+    {
+    ...
+    private:
+      std::list<Ptr<Item>> m_packets;           //!< the items in the queue
+      NS_LOG_TEMPLATE_DECLARE;                  //!< the log component
+    };
+
+  This requires you to perform these steps for all the subclasses of your class.
+
+2. Invoke the ``NS_LOG_TEMPLATE_DEFINE(...);`` macro in the constructor of
+   your class by providing the name of a log component registered by calling
+   the ``NS_LOG_COMPONENT_DEFINE(...);`` macro in some module. For instance:
+
+  ::
+
+    template <typename Item>
+    Queue<Item>::Queue()
+      : NS_LOG_TEMPLATE_DEFINE("Queue")
+    {
+    }
+
+3. Add logging statements (macro calls) to the methods of your class.
+
+In case you want to add logging statements to a static member template
+(which is defined in an header file):
+
+1. Invoke the ``NS_LOG_STATIC_TEMPLATE_DEFINE (...);`` macro in your static
+   method by providing the name of a log component registered by calling
+   the ``NS_LOG_COMPONENT_DEFINE(...);`` macro in some module. For instance:
+
+  ::
+
+    template <typename Item>
+    void
+    NetDeviceQueue::PacketEnqueued(Ptr<Queue<Item>> queue,
+                                   Ptr<NetDeviceQueueInterface> ndqi,
+                                   uint8_t txq, Ptr<const Item> item)
+    {
+
+      NS_LOG_STATIC_TEMPLATE_DEFINE("NetDeviceQueueInterface");
+    ...
+
+2. Add logging statements (macro calls) to your static method.
+
+Logging Macros
+==============
+
+  The logging macros and associated severity levels are
+
+  ================  ==========================
+  Severity Class    Macro
+  ================  ==========================
+  ``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(...);``
+  ================  ==========================
+
+  The macros function as output streamers, so anything you can send to
+  ``std::cout``, joined by ``<<`` operators, is allowed::
+
+    void MyClass::Check(int value, char * item)
+    {
+      NS_LOG_FUNCTION(this << arg << item);
+      if (arg > 10)
+        {
+          NS_LOG_ERROR("encountered bad value " << value <<
+                       " while checking " << name << "!");
+        }
+      ...
+    }
+
+  Note that ``NS_LOG_FUNCTION`` automatically inserts a \`\ :literal:`,\ `'
+  (comma-space) separator between each of its arguments.
+  This simplifies logging of function arguments;
+  just concatenate them with ``<<`` as in the example above.
+
+Unconditional Logging
+=====================
+
+As a convenience, the ``NS_LOG_UNCOND(...);`` macro will always log its
+arguments, even if the associated log-component is not enabled at any
+severity.  This macro does not use any of the prefix options.  Recall
+that logging is only enabled in ``debug``, ``default`` and ``relwithdebinfo``
+builds, so this macro will only produce output in the same builds.
+
+Guidelines
+==========
+
+* Start every class method with ``NS_LOG_FUNCTION(this << args...);``
+  This enables easy function call tracing.
+
+  * Except:  don't log operators or explicit copy constructors,
+    since these will cause infinite recursion and stack overflow.
+
+  * For methods without arguments use the same form:
+    ``NS_LOG_FUNCTION(this);``
+
+  * For static functions:
+
+    * With arguments use ``NS_LOG_FUNCTION(...);`` as normal.
+    * 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.
+  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.
+
+* 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));``.
+  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'.
+
+Controlling timestamp precision
+*******************************
+
+Timestamps are printed out in units of seconds.  When used with the default
+|ns3| time resolution of nanoseconds, the default timestamp precision is 9
+digits, with fixed format, to allow for 9 digits to be consistently printed
+to the right of the decimal point.  Example:
+
+::
+
+  +0.000123456s RandomVariableStream:SetAntithetic(0x805040, 0)
+
+When the |ns3| simulation uses higher time resolution such as picoseconds
+or femtoseconds, the precision is expanded accordingly; e.g. for picosecond:
+
+::
+
+  +0.000123456789s RandomVariableStream:SetAntithetic(0x805040, 0)
+
+When the |ns3| simulation uses a time resolution lower than microseconds,
+the default C++ precision is used.
+
+An example program at ``src/core/examples/sample-log-time-format.cc``
+demonstrates how to change the timestamp formatting.
+
+The maximum useful precision is 20 decimal digits, since Time is signed 64
+bits.
+
+
+Asserts
+*******
+
+The |ns3| assert facility can be used to validate that invariant conditions
+are met during execution. If the condition is not met an error message is given
+and the program stops, printing the location of the failed assert.
+
+The assert implementation is enabled in ``debug`` and ``default``
+builds, but disabled in all other build profiles to improve execution speed.
+
+How to add asserts to your code
+===============================
+
+There is only one macro one should use::
+
+  NS_ASSERT_MSG(condition, message);
+
+The ``condition`` should be the invariant you want to test, as a
+boolean expression.  The ``message`` should explain what the
+condition means and/or the possible source of the error.
+
+There is a variant available without a message, ``NS_ASSERT(condition)``,
+but we recommend using the message variant in ns-3 library code,
+as a well-crafted message can help users figure out how to fix the underlying
+issue with their script.
+
+In either case if the condition evaluates to ``false`` the assert will print
+an error message to ``std::cerr`` containing the following
+information:
+
+* Error message: "NS_ASSERT failed, "
+* The ``condition`` expression: "cond="``condition``"
+* The ``message``: "msg="``message``"
+* The simulation time and node, as would be printed by logging.
+  These are printed independent of the flags or prefix set on any
+  logging component.
+* The file and line containing the assert: "file=``file``, line=``line``
+
+Here is an example which doesn't assert:
+
+.. sourcecode:: bash
+
+   $ ./ns3 run assert-example
+   [0/2] Re-checking globbed directories...
+   ninja: no work to do.
+   NS_ASSERT_MSG example
+     if an argument is given this example will assert.
+
+and here is an example which does:
+
+.. sourcecode:: bash
+
+   $ ./ns3 run assert-example -- foo
+   [0/2] Re-checking globbed directories...
+   ninja: no work to do.
+   NS_ASSERT_MSG example
+     if an argument is given this example will assert.
+   NS_ASSERT failed, cond="argc == 1", msg="An argument was given, so we assert", file=/Users/barnes26/Code/netsim/ns3/repos/ns-3-dev/src/core/examples/assert-example.cc, line=44
+   NS_FATAL, terminating
+   terminate called without an active exception
+   Command 'build/debug/src/core/examples/ns3-dev-assert-example-debug foo' died with <Signals.SIGABRT: 6>.
+
+You can try the example program `assert-example.cc` in `src/core/example`
+with or without arguments to see the action of ``NS_ASSERT_MSG``.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/logging.rst ns-3.38/doc/manual/source/logging.rst
--- ns-3.37/doc/manual/source/logging.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/logging.rst	1969-12-31 16:00:00.000000000 -0800
@@ -1,516 +0,0 @@
-.. include:: replace.txt
-.. highlight:: cpp
-
-.. heading hierarchy:
-   ------------- Chapter
-   ************* Section (#.#)
-   ============= Subsection (#.#.#)
-   ############# Paragraph (no number)
-
-Logging
--------
-
-The |ns3| logging facility can be used to monitor or debug the progress
-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|.
-
-The project makes no guarantee about whether logging output will remain
-the same over time.  Users are cautioned against building simulation output
-frameworks on top of logging code, as the output and the way the output
-is enabled may change over time.
-
-Overview
-********
-
-|ns3| logging statements are typically used to log various program
-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()``::
-
-  if (address == iaddr.GetBroadcast ())
-    {
-      NS_LOG_LOGIC ("For me (interface broadcast address)");
-      return true;
-     }
-
-If logging has been enabled for the ``Ipv4L3Protocol`` component at a severity
-of ``LOGIC`` or above (see below about log severity), the statement
-will be printed out; otherwise, it will be suppressed.
-
-Enabling Output
-===============
-
-There are two ways that users typically control log output.  The
-first is by setting the ``NS_LOG`` environment variable; e.g.:
-
-.. sourcecode:: bash
-
-   $ NS_LOG="*" ./ns3 run first
-
-will run the ``first`` tutorial program with all logging output.  (The
-specifics of the ``NS_LOG`` format will be discussed below.)
-
-This can be made more granular by selecting individual components:
-
-.. sourcecode:: bash
-
-   $ NS_LOG="Ipv4L3Protocol" ./ns3 run first
-
-The output can be further tailored with prefix options.
-
-The second way to enable logging is to use explicit statements in your
-program, such as in the ``first`` tutorial program::
-
-   int
-   main (int argc, char *argv[])
-   {
-     LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
-     LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
-     ...
-
-(The meaning of ``LOG_LEVEL_INFO``, and other possible values,
-will be discussed below.)
-
-``NS_LOG`` Syntax
-=================
-
-The ``NS_LOG`` environment variable contains a list of log components
-and options.  Log components are separated by \`:' characters:
-
-.. sourcecode:: bash
-
-   $ NS_LOG="<log-component>:<log-component>..."
-
-Options for each log component are given as flags after
-each log component:
-
-.. sourcecode:: bash
-
-   $ NS_LOG="<log-component>=<option>|<option>...:<log-component>..."
-
-Options control the severity and level for that component,
-and whether optional information should be included, such as the
-simulation time, simulation node, function name, and the symbolic severity.
-
-Log Components
-==============
-
-Generally a log component refers to a single source code ``.cc`` file,
-and encompasses the entire file.
-
-Some helpers have special methods to enable the logging of all components
-in a module, spanning different compilation units, but logically grouped
-together, such as the |ns3| wifi code::
-
-   WifiHelper wifiHelper;
-   wifiHelper.EnableLogComponents ();
-
-The ``NS_LOG`` log component wildcard \`*' will enable all components.
-
-To see what log components are defined, any of these will work:
-
-.. sourcecode:: bash
-
-   $ NS_LOG="print-list" ./ns3 run ...
-
-   $ NS_LOG="foo"  # a token not matching any log-component
-
-The first form will print the name and enabled flags for all log components
-which are linked in; try it with ``scratch-simulator``.
-The second form prints all registered log components,
-then exit with an error.
-
-
-Severity and Level Options
-==========================
-
-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.
-
-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_FUNCTION``  Function tracing
-``LOG_LOGIC``     Control flow tracing within functions
-================  =====================================
-
-Typically one wants to see messages at a given severity class *and higher*.
-This is done by defining inclusive logging "levels":
-
-======================  ===========================================
-Level                   Meaning
-======================  ===========================================
-``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_ALL``       All severity classes.
-``LOG_ALL``             Synonym for ``LOG_LEVEL_ALL``
-======================  ===========================================
-
-The severity class and level options can be given in the ``NS_LOG``
-environment variable by these tokens:
-
-============  =================
-Class         Level
-============  =================
-``error``     ``level_error``
-``warn``      ``level_warn``
-``debug``     ``level_debug``
-``info``      ``level_info``
-``function``  ``level_function``
-``logic``     ``level_logic``
-..            | ``level_all``
-              | ``all``
-              | ``*``
-============  =================
-
-Using a severity class token enables log messages at that severity only.
-For example, ``NS_LOG="*=warn"`` won't output messages with severity ``error``.
-``NS_LOG="*=level_debug"`` will output messages at severity levels
-``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``.
-
-The ``NS_LOG`` severity level wildcard \`*' and ``all``
-are synonyms for ``level_all``.
-
-For log components merely mentioned in ``NS_LOG``
-
-.. sourcecode:: bash
-
-   $ NS_LOG="<log-component>:..."
-
-the default severity is ``LOG_LEVEL_ALL``.
-
-
-Prefix Options
-==============
-
-A number of prefixes can help identify
-where and when a message originated, and at what severity.
-
-The available prefix options (as ``enum`` constants) are
-
-======================  ===========================================
-Prefix Symbol           Meaning
-======================  ===========================================
-``LOG_PREFIX_FUNC``     Prefix the name of the calling function.
-``LOG_PREFIX_TIME``     Prefix the simulation time.
-``LOG_PREFIX_NODE``     Prefix the node id.
-``LOG_PREFIX_LEVEL``    Prefix the severity level.
-``LOG_PREFIX_ALL``      Enable all prefixes.
-======================  ===========================================
-
-The prefix options are described briefly below.
-
-The options can be given in the ``NS_LOG``
-environment variable by these tokens:
-
-================  =========
-Token             Alternate
-================  =========
-``prefix_func``   ``func``
-``prefix_time``   ``time``
-``prefix_node``   ``node``
-``prefix_level``  ``level``
-``prefix_all``    | ``all``
-                  | ``*``
-================  =========
-
-For log components merely mentioned in ``NS_LOG``
-
-.. sourcecode:: bash
-
-   $ NS_LOG="<log-component>:..."
-
-the default prefix options are ``LOG_PREFIX_ALL``.
-
-Severity Prefix
-###############
-
-The severity class of a message can be included with the options
-``prefix_level`` or ``level``.  For example, this value of ``NS_LOG``
-enables logging for all log components (\`*') and all severity
-classes (``=all``), and prefixes the message with the severity
-class (``|prefix_level``).
-
-.. sourcecode:: bash
-
-   $ NS_LOG="*=all|prefix_level" ./ns3 run scratch-simulator
-   Scratch Simulator
-   [ERROR] error message
-   [WARN] warn message
-   [DEBUG] debug message
-   [INFO] info message
-   [FUNCT] function message
-   [LOGIC] logic message
-
-Time Prefix
-###########
-
-The simulation time can be included with the options
-``prefix_time`` or ``time``.  This prints the simulation time in seconds.
-
-Node Prefix
-###########
-
-The simulation node id can be included with the options
-``prefix_node`` or ``node``.
-
-Function Prefix
-###############
-
-The name of the calling function can be included with the options
-``prefix_func`` or ``func``.
-
-
-``NS_LOG`` Wildcards
-####################
-
-The log component wildcard \`*' will enable all components.  To
-enable all components at a specific severity level
-use ``*=<severity>``.
-
-The severity level option wildcard \`*' is a synonym for ``all``.
-This must occur before any \`|' characters separating options.
-To enable all severity classes, use ``<log-component>=*``,
-or ``<log-component>=*|<options>``.
-
-The option wildcard \`*' or token ``all`` enables all prefix options,
-but must occur *after* a \`|' character.  To enable a specific
-severity class or level, and all prefixes, use
-``<log-component>=<severity>|*``.
-
-The combined option wildcard ``**`` enables all severities and all prefixes;
-for example, ``<log-component>=**``.
-
-The uber-wildcard ``***`` enables all severities and all prefixes
-for all log components.  These are all equivalent:
-
-.. sourcecode:: bash
-
-   $ NS_LOG="***" ...      $ NS_LOG="*=all|*" ...        $ NS_LOG="*=*|all" ...
-   $ NS_LOG="*=**" ...     $ NS_LOG="*=level_all|*" ...  $ NS_LOG="*=*|prefix_all" ...
-   $ NS_LOG="*=*|*" ...
-
-Be advised:  even the trivial ``scratch-simulator`` produces over
-46K lines of output with ``NS_LOG="***"``!
-
-
-How to add logging to your code
-*******************************
-
-Adding logging to your code is very simple:
-
-1. Invoke the ``NS_LOG_COMPONENT_DEFINE (...);`` macro
-   inside of ``namespace ns3``.
-
-  Create a unique string identifier (usually based on the name of the file
-  and/or class defined within the file) and register it with a macro call
-  such as follows:
-
-  ::
-
-    namespace ns3 {
-
-    NS_LOG_COMPONENT_DEFINE ("Ipv4L3Protocol");
-    ...
-
-  This registers ``Ipv4L3Protocol`` as a log component.
-
-  (The macro was carefully written to permit inclusion either within or
-  outside of namespace ``ns3``, and usage will vary across the codebase, but
-  the original intent was to register this *outside* of namespace ``ns3``
-  at file global scope.)
-
-2. Add logging statements (macro calls) to your functions and function bodies.
-
-In case you want to add logging statements to the methods of your template class
-(which are defined in an header file):
-
-1. Invoke the ``NS_LOG_TEMPLATE_DECLARE;`` macro in the private section of
-   your class declaration. For instance:
-
-  ::
-
-    template <typename Item>
-    class Queue : public QueueBase
-    {
-    ...
-    private:
-      std::list<Ptr<Item> > m_packets;          //!< the items in the queue
-      NS_LOG_TEMPLATE_DECLARE;                  //!< the log component
-    };
-
-  This requires you to perform these steps for all the subclasses of your class.
-
-2. Invoke the ``NS_LOG_TEMPLATE_DEFINE (...);`` macro in the constructor of
-   your class by providing the name of a log component registered by calling
-   the ``NS_LOG_COMPONENT_DEFINE (...);`` macro in some module. For instance:
-
-  ::
-
-    template <typename Item>
-    Queue<Item>::Queue ()
-      : NS_LOG_TEMPLATE_DEFINE ("Queue")
-    {
-    }
-
-3. Add logging statements (macro calls) to the methods of your class.
-
-In case you want to add logging statements to a static member template
-(which is defined in an header file):
-
-1. Invoke the ``NS_LOG_STATIC_TEMPLATE_DEFINE (...);`` macro in your static
-   method by providing the name of a log component registered by calling
-   the ``NS_LOG_COMPONENT_DEFINE (...);`` macro in some module. For instance:
-
-  ::
-
-    template <typename Item>
-    void
-    NetDeviceQueue::PacketEnqueued (Ptr<Queue<Item> > queue,
-                                    Ptr<NetDeviceQueueInterface> ndqi,
-                                    uint8_t txq, Ptr<const Item> item)
-    {
-
-      NS_LOG_STATIC_TEMPLATE_DEFINE ("NetDeviceQueueInterface");
-    ...
-
-2. Add logging statements (macro calls) to your static method.
-
-Controlling timestamp precision
-*******************************
-
-Timestamps are printed out in units of seconds.  When used with the default
-|ns3| time resolution of nanoseconds, the default timestamp precision is 9
-digits, with fixed format, to allow for 9 digits to be consistently printed
-to the right of the decimal point.  Example:
-
-::
-
-  +0.000123456s RandomVariableStream:SetAntithetic(0x805040, 0)
-
-When the |ns3| simulation uses higher time resolution such as picoseconds
-or femtoseconds, the precision is expanded accordingly; e.g. for picosecond:
-
-::
-
-  +0.000123456789s RandomVariableStream:SetAntithetic(0x805040, 0)
-
-When the |ns3| simulation uses a time resolution lower than microseconds,
-the default C++ precision is used.
-
-An example program at ``src\core\examples\sample-log-time-format.cc``
-demonstrates how to change the timestamp formatting.
-
-The maximum useful precision is 20 decimal digits, since Time is signed 64
-bits.
-
-Logging Macros
-==============
-
-  The logging macros and associated severity levels are
-
-  ================  ==========================
-  Severity Class    Macro
-  ================  ==========================
-  ``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 (...);``
-  ================  ==========================
-
-  The macros function as output streamers, so anything you can send to
-  ``std::cout``, joined by ``<<`` operators, is allowed::
-
-    void MyClass::Check (int value, char * item)
-    {
-      NS_LOG_FUNCTION (this << arg << item);
-      if (arg > 10)
-        {
-          NS_LOG_ERROR ("encountered bad value " << value <<
-                        " while checking " << name << "!");
-        }
-      ...
-    }
-
-  Note that ``NS_LOG_FUNCTION`` automatically inserts a \`\ :literal:`,\ `'
-  (comma-space) separator between each of its arguments.
-  This simplifies logging of function arguments;
-  just concatenate them with ``<<`` as in the example above.
-
-Unconditional Logging
-=====================
-
-As a convenience, the ``NS_LOG_UNCOND (...);`` macro will always log its
-arguments, even if the associated log-component is not enabled at any
-severity.  This macro does not use any of the prefix options.  Note that
-logging is only enabled in debug builds; this macro won't produce
-output in optimized builds.
-
-
-Guidelines
-==========
-
-* Start every class method with ``NS_LOG_FUNCTION (this << args...);``
-  This enables easy function call tracing.
-
-  * Except:  don't log operators or explicit copy constructors,
-    since these will cause infinite recursion and stack overflow.
-
-  * For methods without arguments use the same form:
-    ``NS_LOG_FUNCTION (this);``
-
-  * For static functions:
-
-    * With arguments use ``NS_LOG_FUNCTION (...);`` as normal.
-    * 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.
-  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.
-
-* 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));``.
-  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.37/doc/manual/source/new-models.rst ns-3.38/doc/manual/source/new-models.rst
--- ns-3.37/doc/manual/source/new-models.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/new-models.rst	2023-03-16 20:54:23.532868400 -0700
@@ -62,7 +62,7 @@
       * \returns true if the Packet is to be considered as errored/corrupted
       * \param pkt Packet to apply error model to
       */
-      bool IsCorrupt (Ptr<Packet> pkt);
+      bool IsCorrupt(Ptr<Packet> pkt);
     };
 
 Note that we do not pass a const pointer, thereby allowing the function to
@@ -109,16 +109,16 @@
     class ErrorModel
     {
     public:
-      ErrorModel ();
-      virtual ~ErrorModel ();
-      bool IsCorrupt (Ptr<Packet> pkt);
-      void Reset (void);
-      void Enable (void);
-      void Disable (void);
-      bool IsEnabled (void) const;
+      ErrorModel();
+      virtual ~ErrorModel();
+      bool IsCorrupt(Ptr<Packet> pkt);
+      void Reset();
+      void Enable();
+      void Disable();
+      bool IsEnabled() const;
     private:
-      virtual bool DoCorrupt (Ptr<Packet> pkt) = 0;
-      virtual void DoReset (void) = 0;
+      virtual bool DoCorrupt(Ptr<Packet> pkt) = 0;
+      virtual void DoReset() = 0;
     };
 
     enum ErrorUnit
@@ -133,16 +133,16 @@
     class RateErrorModel : public ErrorModel
     {
     public:
-      RateErrorModel ();
-      virtual ~RateErrorModel ();
-      enum ErrorUnit GetUnit (void) const;
-      void SetUnit (enum ErrorUnit error_unit);
-      double GetRate (void) const;
-      void SetRate (double rate);
-      void SetRandomVariable (const RandomVariable &ranvar);
+      RateErrorModel();
+      virtual ~RateErrorModel();
+      enum ErrorUnit GetUnit() const;
+      void SetUnit(enum ErrorUnit error_unit);
+      double GetRate() const;
+      void SetRate(double rate);
+      void SetRandomVariable(const RandomVariable &ranvar);
     private:
-      virtual bool DoCorrupt (Ptr<Packet> pkt);
-      virtual void DoReset (void);
+      virtual bool DoCorrupt(Ptr<Packet> pkt);
+      virtual void DoReset();
     };
 
 
@@ -294,19 +294,19 @@
     class ErrorModel : public Object
     {
     public:
-      static TypeId GetTypeId (void);
+      static TypeId GetTypeId();
 
-      ErrorModel ();
-      virtual ~ErrorModel ();
+      ErrorModel();
+      virtual ~ErrorModel();
     };
 
     class RateErrorModel : public ErrorModel
     {
     public:
-      static TypeId GetTypeId (void);
+      static TypeId GetTypeId();
 
-      RateErrorModel ();
-      virtual ~RateErrorModel ();
+      RateErrorModel();
+      virtual ~RateErrorModel();
     };
     #endif
 
@@ -319,7 +319,7 @@
 "ns3/object.h"``". Note also that this goes outside the namespace declaration.
 
 Second, each class must implement a static public member function called
-``GetTypeId (void)``.
+``GetTypeId()``.
 
 Third, it is a good idea to implement constructors and destructors rather than
 to let the compiler generate them, and to make the destructor virtual. In C++,
@@ -335,52 +335,52 @@
 
     namespace ns3 {
 
-    NS_OBJECT_ENSURE_REGISTERED (ErrorModel);
+    NS_OBJECT_ENSURE_REGISTERED(ErrorModel);
 
-    TypeId ErrorModel::GetTypeId (void)
+    TypeId ErrorModel::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::ErrorModel")
-        .SetParent<Object> ()
-        .SetGroupName ("Network")
+      static TypeId tid = TypeId("ns3::ErrorModel")
+        .SetParent<Object>()
+        .SetGroupName("Network")
         ;
       return tid;
     }
 
-    ErrorModel::ErrorModel ()
+    ErrorModel::ErrorModel()
     {
     }
 
-    ErrorModel::~ErrorModel ()
+    ErrorModel::~ErrorModel()
     {
     }
 
-    NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
+    NS_OBJECT_ENSURE_REGISTERED(RateErrorModel);
 
-    TypeId RateErrorModel::GetTypeId (void)
+    TypeId RateErrorModel::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::RateErrorModel")
-        .SetParent<ErrorModel> ()
-        .SetGroupName ("Network")
-        .AddConstructor<RateErrorModel> ()
+      static TypeId tid = TypeId("ns3::RateErrorModel")
+        .SetParent<ErrorModel>()
+        .SetGroupName("Network")
+        .AddConstructor<RateErrorModel>()
         ;
       return tid;
     }
 
-    RateErrorModel::RateErrorModel ()
+    RateErrorModel::RateErrorModel()
     {
     }
 
-    RateErrorModel::~RateErrorModel ()
+    RateErrorModel::~RateErrorModel()
     {
     }
 
-What is the ``GetTypeId (void)`` function? This function does a few things.  It
+What is the ``GetTypeId()`` function? This function does a few things.  It
 registers a unique string into the TypeId system. It establishes  the hierarchy
 of objects in the attribute system (via ``SetParent``). It also declares that
 certain objects can be created via the object creation framework
 (``AddConstructor``).
 
-The macro ``NS_OBJECT_ENSURE_REGISTERED (classname)`` is needed also once for
+The macro ``NS_OBJECT_ENSURE_REGISTERED(classname)`` is needed also once for
 every class that defines a new GetTypeId method, and it does the actual
 registration of the class into the system.  The :ref:`Object-model` chapter
 discusses this in more detail.
@@ -434,35 +434,35 @@
 ::
 
     void
-    PointToPointNetDevice::SetReceiveErrorModel (Ptr<ErrorModel> em)
+    PointToPointNetDevice::SetReceiveErrorModel(Ptr<ErrorModel> em)
     {
-      NS_LOG_FUNCTION (this << em);
+      NS_LOG_FUNCTION(this << em);
       m_receiveErrorModel = em;
     }
 
-       .AddAttribute ("ReceiveErrorModel",
+       .AddAttribute("ReceiveErrorModel",
                        "The receiver error model used to simulate packet loss",
-                       PointerValue (),
-                       MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
-                       MakePointerChecker<ErrorModel> ())
+                       PointerValue(),
+                       MakePointerAccessor(&PointToPointNetDevice::m_receiveErrorModel),
+                       MakePointerChecker<ErrorModel>())
 
 Plumb Into the System
 +++++++++++++++++++++
 
 ::
 
-    void PointToPointNetDevice::Receive (Ptr<Packet> packet)
+    void PointToPointNetDevice::Receive(Ptr<Packet> packet)
     {
-      NS_LOG_FUNCTION (this << packet);
+      NS_LOG_FUNCTION(this << packet);
       uint16_t protocol = 0;
 
-      if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (packet) )
+      if(m_receiveErrorModel && m_receiveErrorModel->IsCorrupt(packet) )
         {
     //
     // If we have an error model and it indicates that it is time to lose a
     // corrupted packet, don't forward this packet up, let it go.
     //
-          m_dropTrace (packet);
+          m_dropTrace(packet);
         }
       else
         {
@@ -470,12 +470,12 @@
     // Hit the receive trace hook, strip off the point-to-point protocol header
     // and forward this packet up the protocol stack.
     //
-          m_rxTrace (packet);
+          m_rxTrace(packet);
           ProcessHeader(packet, protocol);
-          m_rxCallback (this, packet, protocol, GetRemote ());
-          if (!m_promiscCallback.IsNull ())
-            {           m_promiscCallback (this, packet, protocol, GetRemote (),
-                          GetAddress (), NetDevice::PACKET_HOST);
+          m_rxCallback(this, packet, protocol, GetRemote());
+          if(!m_promiscCallback.IsNull())
+            {           m_promiscCallback(this, packet, protocol, GetRemote(),
+                          GetAddress(), NetDevice::PACKET_HOST);
             }
         }
     }
@@ -492,13 +492,13 @@
       // We can obtain a handle to the NetDevice via the channel and node
       // pointers
       Ptr<PointToPointNetDevice> nd3 = PointToPointTopology::GetNetDevice
-        (n3, channel2);
-      Ptr<ErrorModel> em = Create<ErrorModel> ();
-      nd3->SetReceiveErrorModel (em);
+       (n3, channel2);
+      Ptr<ErrorModel> em = Create<ErrorModel>();
+      nd3->SetReceiveErrorModel(em);
 
 
     bool
-    ErrorModel::DoCorrupt (Packet& p)
+    ErrorModel::DoCorrupt(Packet& p)
     {
       NS_LOG_FUNCTION;
       NS_LOG_UNCOND("Corrupt!");
@@ -514,7 +514,7 @@
 **************
 
 The trivial base class ErrorModel does not do anything interesting, but it
-provides a useful base class interface (Corrupt () and Reset ()), forwarded to
+provides a useful base class interface (``Corrupt()`` and ``Reset()``), forwarded to
 virtual functions that can be subclassed. Let's next consider what we call a
 BasicErrorModel which is based on the |ns2| ErrorModel class (in
 ``ns-2/queue/errmodel.{cc,h}``).
@@ -542,24 +542,24 @@
     class BasicErrorModel : public ErrorModel
     {
     public:
-      static TypeId GetTypeId (void);
+      static TypeId GetTypeId();
       ...
     private:
       // Implement base class pure virtual functions
-      virtual bool DoCorrupt (Ptr<Packet> p);
-      virtual bool DoReset (void);
+      virtual bool DoCorrupt(Ptr<Packet> p);
+      virtual bool DoReset();
       ...
     }
 
 and configure the subclass GetTypeId function by setting a unique TypeId string
 and setting the Parent to ErrorModel::
 
-    TypeId RateErrorModel::GetTypeId (void)
+    TypeId RateErrorModel::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::RateErrorModel")
-        .SetParent<ErrorModel> ()
-        .SetGroupName ("Network")
-        .AddConstructor<RateErrorModel> ()
+      static TypeId tid = TypeId("ns3::RateErrorModel")
+        .SetParent<ErrorModel>()
+        .SetGroupName("Network")
+        .AddConstructor<RateErrorModel>()
       ...
 
 Build Core Functions and Unit Tests
@@ -570,4 +570,3 @@
 
 Writing Unit Tests
 ++++++++++++++++++
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/new-modules.rst ns-3.38/doc/manual/source/new-modules.rst
--- ns-3.37/doc/manual/source/new-modules.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/new-modules.rst	2023-03-16 20:54:23.536868300 -0700
@@ -161,10 +161,10 @@
 which declares a new unit test named ``new-module``,
 with a single test case consisting of the class ``NewModuleTestCase1``::
 
-  NewModuleTestSuite::NewModuleTestSuite ()
-    : TestSuite ("new-module", UNIT)
+  NewModuleTestSuite::NewModuleTestSuite()
+    : TestSuite("new-module", UNIT)
   {
-    AddTestCase (new NewModuleTestCase1);
+    AddTestCase(new NewModuleTestCase1);
   }
 
 Step 3 - Declare Source Files
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/object-model.rst ns-3.38/doc/manual/source/object-model.rst
--- ns-3.37/doc/manual/source/object-model.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/object-model.rst	2023-03-16 20:54:23.536868300 -0700
@@ -32,10 +32,10 @@
     class Address
     {
     public:
-      Address ();
-      Address (uint8_t type, const uint8_t *buffer, uint8_t len);
-      Address (const Address & address);
-      Address &operator = (const Address &address);
+      Address();
+      Address(uint8_t type, const uint8_t *buffer, uint8_t len);
+      Address(const Address & address);
+      Address &operator=(const Address &address);
       ...
     private:
       uint8_t m_type;
@@ -132,7 +132,7 @@
 
 For objects deriving from class :cpp:class:`Object`::
 
-    Ptr<WifiNetDevice> device = CreateObject<WifiNetDevice> ();
+    Ptr<WifiNetDevice> device = CreateObject<WifiNetDevice>();
 
 Please do not create such objects using ``operator new``; create them using
 :cpp:func:`CreateObject()` instead.
@@ -141,7 +141,7 @@
 that support usage of the smart pointer class, a templated helper function is
 available and recommended to be used::
 
-    Ptr<B> b = Create<B> ();
+    Ptr<B> b = Create<B>();
 
 This is simply a wrapper around operator new that correctly handles the
 reference counting system.
@@ -190,12 +190,12 @@
     static void
     AddIpv4Stack(Ptr<Node> node)
     {
-      Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();
-      ipv4->SetNode (node);
-      node->AggregateObject (ipv4);
-      Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> ();
-      ipv4Impl->SetIpv4 (ipv4);
-      node->AggregateObject (ipv4Impl);
+      Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol>();
+      ipv4->SetNode(node);
+      node->AggregateObject(ipv4);
+      Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl>();
+      ipv4Impl->SetIpv4(ipv4);
+      node->AggregateObject(ipv4Impl);
     }
 
 Note that the Ipv4 protocols are created using :cpp:func:`CreateObject()`.
@@ -220,7 +220,7 @@
 that has an interface to the IP forwarding configuration. It performs the
 following::
 
-    Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4> ();
+    Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4>();
 
 If the node in fact does not have an Ipv4 object aggregated to it, then the
 method will return null. Therefore, it is good practice to check the return
@@ -248,9 +248,9 @@
 Class :cpp:class:`ObjectFactory` can be used to instantiate objects and to
 configure the attributes on those objects::
 
-    void SetTypeId (TypeId tid);
-    void Set (std::string name, const AttributeValue &value);
-    Ptr<T> Create (void) const;
+    void SetTypeId(TypeId tid);
+    void Set(std::string name, const AttributeValue &value);
+    Ptr<T> Create() const;
 
 The first method allows one to use the |ns3| TypeId system to specify the type
 of objects created. The second allows one to set attributes on the objects to be
@@ -260,15 +260,15 @@
 
     ObjectFactory factory;
     // Make this factory create objects of type FriisPropagationLossModel
-    factory.SetTypeId ("ns3::FriisPropagationLossModel")
+    factory.SetTypeId("ns3::FriisPropagationLossModel")
     // Make this factory object change a default value of an attribute, for
     // subsequently created objects
-    factory.Set ("SystemLoss", DoubleValue (2.0));
+    factory.Set("SystemLoss", DoubleValue(2.0));
     // Create one such object
-    Ptr<Object> object = factory.Create ();
-    factory.Set ("SystemLoss", DoubleValue (3.0));
+    Ptr<Object> object = factory.Create();
+    factory.Set("SystemLoss", DoubleValue(3.0));
     // Create another object with a different SystemLoss
-    Ptr<Object> object = factory.Create ();
+    Ptr<Object> object = factory.Create();
 
 Downcasting
 ***********
@@ -285,9 +285,9 @@
 
     template <typename T1, typename T2>
     Ptr<T1>
-    DynamicCast (Ptr<T2> const&p)
+    DynamicCast(Ptr<T2> const&p)
     {
-      return Ptr<T1> (dynamic_cast<T1 *> (PeekPointer (p)));
+      return Ptr<T1>(dynamic_cast<T1 *>(PeekPointer(p)));
     }
 
 DynamicCast works when the programmer has a base type pointer and is testing
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/organization.rst ns-3.38/doc/manual/source/organization.rst
--- ns-3.37/doc/manual/source/organization.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/organization.rst	2023-03-16 20:54:23.536868300 -0700
@@ -56,6 +56,6 @@
 applications, and the following chapter describes additional support for
 simulation, such as animators and statistics.
 
-The project maintains a separate manual devoted to testing and validation
-of |ns3| code (see the `ns-3 Testing and Validation manual
-<http://www.nsnam.org/tutorials.html>`_).
+The project maintains a manual section devoted to testing and validation
+of |ns3| code (see the `tests section in the ns-3 manual
+<https://www.nsnam.org/docs/manual/html/tests.html>`_).
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/profiling.rst ns-3.38/doc/manual/source/profiling.rst
--- ns-3.37/doc/manual/source/profiling.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/profiling.rst	2023-03-16 20:54:23.536868300 -0700
@@ -466,7 +466,7 @@
 
 .. _Perf : https://perf.wiki.kernel.org/index.php/Tutorial
 .. _Hotspot : https://github.com/KDAB/hotspot
-.. _AMD uProf : https://developer.amd.com/amd-uprof/
+.. _AMD uProf : https://www.amd.com/en/developer/uprof.html
 .. _Intel VTune : https://www.intel.com/content/www/us/en/develop/documentation/get-started-with-vtune/top.html
 .. _Windows Performance Toolkit : https://docs.microsoft.com/en-us/windows-hardware/test/wpt/
 .. _Sysprof : https://wiki.gnome.org/Apps/Sysprof
@@ -1003,30 +1003,30 @@
 
 .. sourcecode:: console
 
-  Time variable                                  usr           sys          wall               GGC
-  phase setup                        :   0.00 (  0%)   0.00 (  0%)   0.01 (  1%)    1478 kB (  2%)
-  phase parsing                      :   0.31 ( 46%)   0.17 ( 85%)   0.48 ( 55%)   55432 kB ( 71%)
-  phase lang. deferred               :   0.03 (  4%)   0.00 (  0%)   0.03 (  3%)    4287 kB (  5%)
-  phase opt and generate             :   0.32 ( 48%)   0.03 ( 15%)   0.35 ( 40%)   16635 kB ( 21%)
-  phase last asm                     :   0.01 (  1%)   0.00 (  0%)   0.01 (  1%)     769 kB (  1%)
-  ------------------------------------------------------------------------------------------------
-  |name lookup                       :   0.05 (  7%)   0.02 ( 10%)   0.04 (  5%)    2468 kB (  3%)
-  |overload resolution               :   0.05 (  7%)   0.00 (  0%)   0.05 (  6%)    4217 kB (  5%)
-  dump files                         :   0.00 (  0%)   0.00 (  0%)   0.01 (  1%)       0 kB (  0%)
-  callgraph construction             :   0.01 (  1%)   0.00 (  0%)   0.01 (  1%)    2170 kB (  3%)
+  Time variable                      usr           sys          wall               GGC
+  phase setup            :   0.00 (  0%)   0.00 (  0%)   0.01 (  1%)    1478 kB (  2%)
+  phase parsing          :   0.31 ( 46%)   0.17 ( 85%)   0.48 ( 55%)   55432 kB ( 71%)
+  phase lang. deferred   :   0.03 (  4%)   0.00 (  0%)   0.03 (  3%)    4287 kB (  5%)
+  phase opt and generate :   0.32 ( 48%)   0.03 ( 15%)   0.35 ( 40%)   16635 kB ( 21%)
+  phase last asm         :   0.01 (  1%)   0.00 (  0%)   0.01 (  1%)     769 kB (  1%)
+  ------------------------------------------------------------------------------------
+  |name lookup           :   0.05 (  7%)   0.02 ( 10%)   0.04 (  5%)    2468 kB (  3%)
+  |overload resolution   :   0.05 (  7%)   0.00 (  0%)   0.05 (  6%)    4217 kB (  5%)
+  dump files             :   0.00 (  0%)   0.00 (  0%)   0.01 (  1%)       0 kB (  0%)
+  callgraph construction :   0.01 (  1%)   0.00 (  0%)   0.01 (  1%)    2170 kB (  3%)
   ...
-  preprocessing                      :   0.05 (  7%)   0.06 ( 30%)   0.10 ( 11%)    1751 kB (  2%)
-  parser (global)                    :   0.06 (  9%)   0.03 ( 15%)   0.07 (  8%)   16303 kB ( 21%)
-  parser struct body                 :   0.06 (  9%)   0.04 ( 20%)   0.08 (  9%)   12525 kB ( 16%)
-  parser enumerator list             :   0.01 (  1%)   0.00 (  0%)   0.00 (  0%)     112 kB (  0%)
-  parser function body               :   0.02 (  3%)   0.02 ( 10%)   0.02 (  2%)    3039 kB (  4%)
-  parser inl. func. body             :   0.03 (  4%)   0.00 (  0%)   0.01 (  1%)    2024 kB (  3%)
-  parser inl. meth. body             :   0.02 (  3%)   0.01 (  5%)   0.06 (  7%)    5792 kB (  7%)
-  template instantiation             :   0.09 ( 13%)   0.01 (  5%)   0.13 ( 15%)   12274 kB ( 16%)
+  preprocessing          :   0.05 (  7%)   0.06 ( 30%)   0.10 ( 11%)    1751 kB (  2%)
+  parser (global)        :   0.06 (  9%)   0.03 ( 15%)   0.07 (  8%)   16303 kB ( 21%)
+  parser struct body     :   0.06 (  9%)   0.04 ( 20%)   0.08 (  9%)   12525 kB ( 16%)
+  parser enumerator list :   0.01 (  1%)   0.00 (  0%)   0.00 (  0%)     112 kB (  0%)
+  parser function body   :   0.02 (  3%)   0.02 ( 10%)   0.02 (  2%)    3039 kB (  4%)
+  parser inl. func. body :   0.03 (  4%)   0.00 (  0%)   0.01 (  1%)    2024 kB (  3%)
+  parser inl. meth. body :   0.02 (  3%)   0.01 (  5%)   0.06 (  7%)    5792 kB (  7%)
+  template instantiation :   0.09 ( 13%)   0.01 (  5%)   0.13 ( 15%)   12274 kB ( 16%)
   ...
-  symout                             :   0.01 (  1%)   0.00 (  0%)   0.02 (  2%)    8114 kB ( 10%)
+  symout                 :   0.01 (  1%)   0.00 (  0%)   0.02 (  2%)    8114 kB ( 10%)
   ...
-  TOTAL                              :   0.67          0.20          0.88          78612 kB
+  TOTAL                  :   0.67          0.20          0.88          78612 kB
 
 In the table above, the first few lines show the five main compilations steps: ``setup``,
 ``parsing``, ``lang. deferred`` (C++ specific transformations),
@@ -1041,6 +1041,8 @@
 
 It is recommended to use the Clang alternative.
 
+.. _Clang :
+
 Clang
 +++++
 
@@ -1164,6 +1166,37 @@
 however, they can increase ccache misses, reducing the time of the first
 compilation at the cost of increasing recompilation times.
 
+.. _Perfetto UI : https://ui.perfetto.dev/
+.. _NinjaTracing : https://github.com/nico/ninjatracing
+
+NinjaTracing
+++++++++++++
+
+If the Ninja generator is being used (``./ns3 configure -G Ninja``), its build log
+can be used to identify targets slowing down the build process. The `NinjaTracing`_
+utility is used to convert the log format into a tracing Json file.
+
+The following steps show how it can be used:
+
+.. sourcecode:: console
+
+  ~/ns-3-dev$ ./ns3 configure --enable-ninja-tracing
+  ~/ns-3-dev$ ./ns3 build
+  ~/ns-3-dev$ ./ns3 build ninjaTrace
+
+The output ``ninja_performance_trace.json`` should be located in the ``~/ns-3-dev`` directory.
+You can then visualize the results using the ``about:tracing`` panel available in
+Chromium-based browser or with a compatible trace viewer such as `Perfetto UI`_.
+
+It can also be used in conjunction with the `Clang`_ time-trace feature for more granular
+information from within the compiler and linker.
+
+.. sourcecode:: console
+
+  ~/ns-3-dev$ CXX=clang++ ./ns3 configure --enable-ninja-tracing -- -DNS3_CLANG_TIMETRACE=ON
+  ~/ns-3-dev$ ./ns3 build
+  ~/ns-3-dev$ ./ns3 build ninjaTrace
+
 
 CMake Profiler
 **************
@@ -1182,9 +1215,6 @@
 
   ~/ns-3-dev$ ./ns3 configure --trace-performance
 
-
-.. _Perfetto UI: https://ui.perfetto.dev/
-
 A ``cmake_performance_trace.log`` file will be generated in the ns-3-dev directory.
 The tracing results can be visualized using the ``about:tracing`` panel available
 in Chromium-based browsers or a compatible trace viewer such as `Perfetto UI`_.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/python.rst ns-3.38/doc/manual/source/python.rst
--- ns-3.37/doc/manual/source/python.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/python.rst	2023-03-16 20:54:23.536868300 -0700
@@ -83,7 +83,7 @@
   serverApps.Start(ns.core.Seconds(1.0))
   serverApps.Stop(ns.core.Seconds(10.0))
 
-  address = ns.addressFromIpv4Address(interfaces.GetAddress(1))
+  address = interfaces.GetAddress(1).ConvertTo()
   echoClient = ns.applications.UdpEchoClientHelper(address, 9)
   echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
   echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0)))
@@ -324,20 +324,11 @@
 A different operator used by |ns3| is `operator Address()`, used to
 convert different types of Addresses into the generic type Address.
 This is not supported by Cppyy and requires explicit conversion.
-Some helpers have been added to handle the common cases.
 
 .. sourcecode:: python
 
-    # Define ns.cppyy.gbl.addressFromIpv4Address and others
-    cppyy.cppdef("""using namespace ns3;
-                    Address addressFromIpv4Address(Ipv4Address ip){ return Address(ip); };
-                    Address addressFromInetSocketAddress(InetSocketAddress addr){ return Address(addr); };
-                    Address addressFromPacketSocketAddress(PacketSocketAddress addr){ return Address(addr); };
-                    """)
-    # Expose addressFromIpv4Address as a member of the ns3 namespace (equivalent to ns)
-    setattr(cppyy.gbl.ns3, "addressFromIpv4Address", cppyy.gbl.addressFromIpv4Address)
-    setattr(cppyy.gbl.ns3, "addressFromInetSocketAddress", cppyy.gbl.addressFromInetSocketAddress)
-    setattr(cppyy.gbl.ns3, "addressFromPacketSocketAddress", cppyy.gbl.addressFromPacketSocketAddress)
+    # Explicitly convert the InetSocketAddress to Address using InetSocketAddress.ConvertTo()
+    sink.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80).ConvertTo())
 
 Most of the missing APIs can be wrapped, given enough time, patience, and expertise, and will likely be wrapped if bug reports are submitted.
 However, don't file a bug report saying "bindings are incomplete", because the project does not have maintainers to maintain every API.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/random-variables.rst ns-3.38/doc/manual/source/random-variables.rst
--- ns-3.37/doc/manual/source/random-variables.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/random-variables.rst	2023-03-16 20:54:23.536868300 -0700
@@ -117,20 +117,20 @@
 
 ::
 
-  Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable> ();
+  Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
 
 then you can access values by calling methods on the object such as:
 
 ::
 
-  myRandomNo = x->GetInteger ();
+  myRandomNo = x->GetInteger();
 
 
 If you try to instead do something like this:
 
 ::
 
-  myRandomNo = UniformRandomVariable().GetInteger ();
+  myRandomNo = UniformRandomVariable().GetInteger();
 
 your program will encounter a segmentation fault, because the implementation
 relies on some attribute construction that occurs only when `CreateObject`
@@ -160,11 +160,11 @@
 run number behavior.  This seeding and substream state setting must be called
 before any random variables are created; e.g::
 
-  RngSeedManager::SetSeed (3);  // Changes seed from default of 1 to 3
-  RngSeedManager::SetRun (7);   // Changes run number from default of 1 to 7
+  RngSeedManager::SetSeed(3);  // Changes seed from default of 1 to 3
+  RngSeedManager::SetRun(7);   // Changes run number from default of 1 to 7
   // Now, create random variables
-  Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable> ();
-  Ptr<ExponentialRandomVariable> y = CreateObject<ExponentialRandomVarlable> ();
+  Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
+  Ptr<ExponentialRandomVariable> y = CreateObject<ExponentialRandomVarlable>();
   ...
 
 Which is better, setting a new seed or advancing the substream state?  There is
@@ -227,13 +227,13 @@
    * \brief Returns a random double from the underlying distribution
    * \return A floating point random value
    */
-  double GetValue (void) const;
+  double GetValue() const;
 
   /**
    * \brief Returns a random integer from the underlying distribution
    * \return  Integer cast of ::GetValue()
    */
-  uint32_t GetInteger (void) const;
+  uint32_t GetInteger() const;
 
 We have already described the seeding configuration above. Different
 RandomVariable subclasses may have additional API.
@@ -273,17 +273,17 @@
 An example is in the propagation models for WifiNetDevice::
 
     TypeId
-    RandomPropagationDelayModel::GetTypeId (void)
+    RandomPropagationDelayModel::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
-        .SetParent<PropagationDelayModel> ()
-        .SetGroupName ("Propagation")
-        .AddConstructor<RandomPropagationDelayModel> ()
-        .AddAttribute ("Variable",
-                       "The random variable which generates random delays (s).",
-                       StringValue ("ns3::UniformRandomVariable"),
-                       MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
-                       MakePointerChecker<RandomVariableStream> ())
+      static TypeId tid = TypeId("ns3::RandomPropagationDelayModel")
+        .SetParent<PropagationDelayModel>()
+        .SetGroupName("Propagation")
+        .AddConstructor<RandomPropagationDelayModel>()
+        .AddAttribute("Variable",
+                      "The random variable which generates random delays (s).",
+                      StringValue("ns3::UniformRandomVariable"),
+                      MakePointerAccessor(&RandomPropagationDelayModel::m_variable),
+                      MakePointerChecker<RandomVariableStream>())
         ;
       return tid;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/realtime.rst ns-3.38/doc/manual/source/realtime.rst
--- ns-3.37/doc/manual/source/realtime.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/realtime.rst	2023-03-16 20:54:23.536868300 -0700
@@ -58,8 +58,8 @@
 perspective.  Users just need to set the attribute
 ``SimulatorImplementationType`` to the Realtime simulator, such as follows: ::
 
-  GlobalValue::Bind ("SimulatorImplementationType",
-    StringValue ("ns3::RealtimeSimulatorImpl"));
+  GlobalValue::Bind("SimulatorImplementationType",
+                    StringValue("ns3::RealtimeSimulatorImpl"));
 
 There is a script in ``examples/realtime/realtime-udp-echo.cc`` that
 has an example of how to configure the realtime behavior.  Try:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/test-framework.rst ns-3.38/doc/manual/source/test-framework.rst
--- ns-3.37/doc/manual/source/test-framework.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/test-framework.rst	2023-03-16 20:54:23.536868300 -0700
@@ -495,7 +495,7 @@
 ``ns3tcp-loss-NewReno0-response-vectors.pcap`` is a file consisting of a number of TCP
 headers that are used as the expected responses of the |ns3| TCP under test.
 
-Note that Unit Tests are often preferrable to System Tests, as they are more
+Note that Unit Tests are often preferable to System Tests, as they are more
 independent from small changes in the modules that are not the goal of the test.
 
 Examples
@@ -627,7 +627,7 @@
 
   $ ./ns3 run "test-runner --suite=pcap-file"
 
-|ns3| logging is available when you run it this way, such as:
+|ns3| logging is available when you run it this way, such as::
 
   $ NS_LOG="Packet" ./ns3 run "test-runner --suite=pcap-file"
 
@@ -707,7 +707,7 @@
   (gdb) r --suite=
   Starting program: <..>/build/utils/ns3-dev-test-runner-debug --suite=wifi-interference
   [Thread debugging using libthread_db enabled]
-  assert failed. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
+  assert failed. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size() && uid != 0"
   ...
 
 Here is another example of how to use valgrind to debug a memory problem
@@ -757,13 +757,13 @@
   class MySuite : public TestSuite
   {
   public:
-    MyTestSuite ();
+    MyTestSuite();
   };
 
-  MyTestSuite::MyTestSuite ()
-    : TestSuite ("my-test-suite-name", UNIT)
+  MyTestSuite::MyTestSuite()
+    : TestSuite("my-test-suite-name", UNIT)
   {
-    AddTestCase (new MyTestCase, TestCase::QUICK);
+    AddTestCase(new MyTestCase, TestCase::QUICK);
   }
 
   static MyTestSuite myTestSuite;
@@ -794,20 +794,20 @@
 
   class MyTestCase : public TestCase
   {
-    MyTestCase ();
-    virtual void DoSetup (void);
-    virtual void DoRun (void);
+    MyTestCase();
+    virtual void DoSetup();
+    virtual void DoRun();
   };
 
-  MyTestCase::MyTestCase ()
-    : TestCase ("Check some bit of functionality")
+  MyTestCase::MyTestCase()
+    : TestCase("Check some bit of functionality")
   {
   }
 
   void
-  MyTestCase::DoRun (void)
+  MyTestCase::DoRun()
   {
-    NS_TEST_ASSERT_MSG_EQ (true, true, "Some failure message");
+    NS_TEST_ASSERT_MSG_EQ(true, true, "Some failure message");
   }
 
 Utilities
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/tracing.rst ns-3.38/doc/manual/source/tracing.rst
--- ns-3.37/doc/manual/source/tracing.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/tracing.rst	2023-03-16 20:54:23.536868300 -0700
@@ -24,7 +24,7 @@
 
   #include <iostream>
   ...
-  int main ()
+  int main()
   {
     ...
     std::cout << "The value of x is " << x << std::endl;
@@ -139,19 +139,19 @@
   class MyObject : public Object
   {
   public:
-    static TypeId GetTypeId (void)
+    static TypeId GetTypeId()
     {
-      static TypeId tid = TypeId ("MyObject")
-        .SetParent (Object::GetTypeId ())
-        .AddConstructor<MyObject> ()
-        .AddTraceSource ("MyInteger",
-                         "An integer value to trace.",
-                         MakeTraceSourceAccessor (&MyObject::m_myInt))
+      static TypeId tid = TypeId("MyObject")
+        .SetParent(Object::GetTypeId())
+        .AddConstructor<MyObject>()
+        .AddTraceSource("MyInteger",
+                        "An integer value to trace.",
+                        MakeTraceSourceAccessor(&MyObject::m_myInt))
         ;
       return tid;
     }
 
-    MyObject () {}
+    MyObject() {}
     TracedValue<uint32_t> m_myInt;
   };
 
@@ -166,7 +166,7 @@
 callback process.::
 
   void
-  IntTrace (Int oldValue, Int newValue)
+  IntTrace(Int oldValue, Int newValue)
   {
     std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
   }
@@ -176,11 +176,11 @@
 ``TracedValue`` is executed.::
 
   int
-  main (int argc, char *argv[])
+  main(int argc, char *argv[])
   {
-    Ptr<MyObject> myObject = CreateObject<MyObject> ();
+    Ptr<MyObject> myObject = CreateObject<MyObject>();
 
-    myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
+    myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace));
 
     myObject->m_myInt = 1234;
   }
@@ -228,13 +228,13 @@
 For example, one might find something that looks like the following in the
 system (taken from ``examples/tcp-large-transfer.cc``)::
 
-  void CwndTracer (uint32_t oldval, uint32_t newval) {}
+  void CwndTracer(uint32_t oldval, uint32_t newval) {}
 
   ...
 
-  Config::ConnectWithoutContext (
-    "/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
-    MakeCallback (&CwndTracer));
+  Config::ConnectWithoutContext(
+      "/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
+      MakeCallback(&CwndTracer));
 
 This should look very familiar. It is the same thing as the previous example,
 except that a static member function of class ``Config`` is being called instead
@@ -246,11 +246,11 @@
 ``Object`` that has the "CongestionWindow" ``Attribute`` handy (call it
 ``theObject``), you could write this just like the previous example::
 
-  void CwndTracer (uint32_t oldval, uint32_t newval) {}
+  void CwndTracer(uint32_t oldval, uint32_t newval) {}
 
   ...
 
-  theObject->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
+  theObject->TraceConnectWithoutContext("CongestionWindow", MakeCallback(&CwndTracer));
 
 It turns out that the code for ``Config::ConnectWithoutContext`` does exactly
 that. This function takes a path that represents a chain of ``Object`` pointers
@@ -284,7 +284,7 @@
 an attribute called "CongestionWindow" which is a ``TracedValue<uint32_t>``.
 The ``Config::ConnectWithoutContext`` now does a,::
 
-  object->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
+  object->TraceConnectWithoutContext("CongestionWindow", MakeCallback(&CwndTracer));
 
 using the object pointer from "SocketList/0" which makes the connection between
 the trace source defined in the socket to the callback -- ``CwndTracer``.
@@ -323,10 +323,10 @@
 
 Perhaps you will recall seeing some of these variations::
 
-  pointToPoint.EnablePcapAll ("second");
-  pointToPoint.EnablePcap ("second", p2pNodes.Get (0)->GetId (), 0);
-  csma.EnablePcap ("third", csmaDevices.Get (0), true);
-  pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));
+  pointToPoint.EnablePcapAll("second");
+  pointToPoint.EnablePcap("second", p2pNodes.Get(0)->GetId(), 0);
+  csma.EnablePcap("third", csmaDevices.Get(0), true);
+  pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));
 
 What may not be obvious, though, is that there is a consistent model for all of
 the trace-related methods found in the system. We will now take a little time
@@ -381,14 +381,14 @@
 functionality for using pcap tracing in an |ns3| device. Every device must
 implement a single virtual method inherited from this class.::
 
-  virtual void EnablePcapInternal (std::string prefix, Ptr<NetDevice> nd, bool promiscuous) = 0;
+  virtual void EnablePcapInternal(std::string prefix, Ptr<NetDevice> nd, bool promiscuous) = 0;
 
 The signature of this method reflects the device-centric view of the situation
 at this level. All of the public methods inherited from class
 ``PcapUserHelperForDevice`` reduce to calling this single device-dependent
 implementation method. For example, the lowest level pcap method,::
 
-  void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
 
 will call the device implementation of ``EnablePcapInternal`` directly. All
 other public pcap tracing methods build on this implementation to provide
@@ -402,17 +402,17 @@
 
 ::
 
-  void EnablePcap (std::string prefix, Ptr<NetDevice> nd,
-                   bool promiscuous = false, bool explicitFilename = false);
-  void EnablePcap (std::string prefix, std::string ndName,
-                   bool promiscuous = false, bool explicitFilename = false);
-  void EnablePcap (std::string prefix, NetDeviceContainer d,
-                   bool promiscuous = false);
-  void EnablePcap (std::string prefix, NodeContainer n,
-                   bool promiscuous = false);
-  void EnablePcap (std::string prefix, uint32_t nodeid, uint32_t deviceid,
-                   bool promiscuous = false);
-  void EnablePcapAll (std::string prefix, bool promiscuous = false);
+  void EnablePcap(std::string prefix, Ptr<NetDevice> nd,
+                  bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, std::string ndName,
+                  bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, NetDeviceContainer d,
+                  bool promiscuous = false);
+  void EnablePcap(std::string prefix, NodeContainer n,
+                  bool promiscuous = false);
+  void EnablePcap(std::string prefix, uint32_t nodeid, uint32_t deviceid,
+                  bool promiscuous = false);
+  void EnablePcapAll(std::string prefix, bool promiscuous = false);
 
 In each of the methods shown above, there is a default parameter called
 ``promiscuous`` that defaults to false. This parameter indicates that the trace
@@ -422,7 +422,7 @@
 
   Ptr<NetDevice> nd;
   ...
-  helper.EnablePcap ("prefix", nd, true);
+  helper.EnablePcap("prefix", nd, true);
 
 will enable promiscuous mode captures on the ``NetDevice`` specified by ``nd``.
 
@@ -438,7 +438,7 @@
 
   Ptr<NetDevice> nd;
   ...
-  helper.EnablePcap ("prefix", nd);
+  helper.EnablePcap("prefix", nd);
 
 You can enable pcap tracing on a particular node/net-device pair by providing a
 ``std::string`` representing an object name service string to an ``EnablePcap``
@@ -446,10 +446,10 @@
 ``<Node>`` is implicit since the named net device must belong to exactly one
 ``Node``.  For example,::
 
-  Names::Add ("server" ...);
-  Names::Add ("server/eth0" ...);
+  Names::Add("server" ...);
+  Names::Add("server/eth0" ...);
   ...
-  helper.EnablePcap ("prefix", "server/ath0");
+  helper.EnablePcap("prefix", "server/ath0");
 
 You can enable pcap tracing on a collection of node/net-device pairs by
 providing a ``NetDeviceContainer``. For each ``NetDevice`` in the container the
@@ -460,7 +460,7 @@
 
   NetDeviceContainer d = ...;
   ...
-  helper.EnablePcap ("prefix", d);
+  helper.EnablePcap("prefix", d);
 
 You can enable pcap tracing on a collection of node/net-device pairs by
 providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` its
@@ -471,18 +471,18 @@
 
   NodeContainer n;
   ...
-  helper.EnablePcap ("prefix", n);
+  helper.EnablePcap("prefix", n);
 
 You can enable pcap tracing on the basis of node ID and device ID as well as
 with explicit ``Ptr``. Each ``Node`` in the system has an integer node ID and
 each device connected to a node has an integer device ID.::
 
-  helper.EnablePcap ("prefix", 21, 1);
+  helper.EnablePcap("prefix", 21, 1);
 
 Finally, you can enable pcap tracing for all devices in the system, with the
 same type as that managed by the device helper.::
 
-  helper.EnablePcapAll ("prefix");
+  helper.EnablePcapAll("prefix");
 
 Pcap Tracing Device Helper Filename Selection
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -506,8 +506,8 @@
 
 Finally, two of the methods shown above,::
 
-  void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
-  void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
 
 have a default parameter called ``explicitFilename``. When set to true, this
 parameter disables the automatic filename completion mechanism and allows you to
@@ -520,7 +520,7 @@
 
   Ptr<NetDevice> nd;
   ...
-  helper.EnablePcap ("my-pcap-file.pcap", nd, true, true);
+  helper.EnablePcap("my-pcap-file.pcap", nd, true, true);
 
 The first ``true`` parameter enables promiscuous mode traces and the second
 tells the helper to interpret the ``prefix`` parameter as a complete filename.
@@ -537,7 +537,7 @@
 must implement a single virtual method inherited from the ASCII trace
 ``mixin``.::
 
-  virtual void EnableAsciiInternal (Ptr<OutputStreamWrapper> stream, std::string prefix, Ptr<NetDevice> nd) = 0;
+  virtual void EnableAsciiInternal(Ptr<OutputStreamWrapper> stream, std::string prefix, Ptr<NetDevice> nd) = 0;
 
 The signature of this method reflects the device-centric view of the situation
 at this level; and also the fact that the helper may be writing to a shared
@@ -546,8 +546,8 @@
 dependent implementation method. For example, the lowest level ASCII trace
 methods,::
 
-  void EnableAscii (std::string prefix, Ptr<NetDevice> nd);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
+  void EnableAscii(std::string prefix, Ptr<NetDevice> nd);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
 
 will call the device implementation of ``EnableAsciiInternal`` directly,
 providing either a valid prefix or stream.  All other public ASCII tracing
@@ -562,23 +562,23 @@
 
 ::
 
-  void EnableAscii (std::string prefix, Ptr<NetDevice> nd);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
+  void EnableAscii(std::string prefix, Ptr<NetDevice> nd);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
 
-  void EnableAscii (std::string prefix, std::string ndName);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, std::string ndName);
+  void EnableAscii(std::string prefix, std::string ndName);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, std::string ndName);
 
-  void EnableAscii (std::string prefix, NetDeviceContainer d);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, NetDeviceContainer d);
+  void EnableAscii(std::string prefix, NetDeviceContainer d);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, NetDeviceContainer d);
 
-  void EnableAscii (std::string prefix, NodeContainer n);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, NodeContainer n);
+  void EnableAscii(std::string prefix, NodeContainer n);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, NodeContainer n);
 
-  void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t deviceid);
+  void EnableAscii(std::string prefix, uint32_t nodeid, uint32_t deviceid);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t deviceid);
 
-  void EnableAsciiAll (std::string prefix);
-  void EnableAsciiAll (Ptr<OutputStreamWrapper> stream);
+  void EnableAsciiAll(std::string prefix);
+  void EnableAsciiAll(Ptr<OutputStreamWrapper> stream);
 
 You are encouraged to peruse the Doxygen for class ``TraceHelperForDevice`` to
 find the details of these methods; but to summarize ...
@@ -598,7 +598,7 @@
 
   Ptr<NetDevice> nd;
   ...
-  helper.EnableAscii ("prefix", nd);
+  helper.EnableAscii("prefix", nd);
 
 In this case, no trace contexts are written to the ASCII trace file since they
 would be redundant. The system will pick the file name to be created using the
@@ -612,10 +612,10 @@
   Ptr<NetDevice> nd1;
   Ptr<NetDevice> nd2;
   ...
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
   ...
-  helper.EnableAscii (stream, nd1);
-  helper.EnableAscii (stream, nd2);
+  helper.EnableAscii(stream, nd1);
+  helper.EnableAscii(stream, nd2);
 
 In this case, trace contexts are written to the ASCII trace file since they
 are required to disambiguate traces from the two devices.  Note that since the
@@ -628,28 +628,28 @@
 string.  Again, the ``<Node>`` is implicit since the named net device must
 belong to exactly one ``Node``.  For example,::
 
-  Names::Add ("client" ...);
-  Names::Add ("client/eth0" ...);
-  Names::Add ("server" ...);
-  Names::Add ("server/eth0" ...);
+  Names::Add("client" ...);
+  Names::Add("client/eth0" ...);
+  Names::Add("server" ...);
+  Names::Add("server/eth0" ...);
   ...
-  helper.EnableAscii ("prefix", "client/eth0");
-  helper.EnableAscii ("prefix", "server/eth0");
+  helper.EnableAscii("prefix", "client/eth0");
+  helper.EnableAscii("prefix", "server/eth0");
 
 This would result in two files named ``prefix-client-eth0.tr`` and
 ``prefix-server-eth0.tr`` with traces for each device in the respective trace
 file. Since all of the EnableAscii functions are overloaded to take a stream
 wrapper, you can use that form as well::
 
-  Names::Add ("client" ...);
-  Names::Add ("client/eth0" ...);
-  Names::Add ("server" ...);
-  Names::Add ("server/eth0" ...);
+  Names::Add("client" ...);
+  Names::Add("client/eth0" ...);
+  Names::Add("server" ...);
+  Names::Add("server/eth0" ...);
   ...
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
   ...
-  helper.EnableAscii (stream, "client/eth0");
-  helper.EnableAscii (stream, "server/eth0");
+  helper.EnableAscii(stream, "client/eth0");
+  helper.EnableAscii(stream, "server/eth0");
 
 This would result in a single trace file called ``trace-file-name.tr`` that
 contains all of the trace events for both devices. The events would be
@@ -663,7 +663,7 @@
 
   NetDeviceContainer d = ...;
   ...
-  helper.EnableAscii ("prefix", d);
+  helper.EnableAscii("prefix", d);
 
 This would result in a number of ASCII trace files being created, each of which
 follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the
@@ -671,9 +671,9 @@
 
   NetDeviceContainer d = ...;
   ...
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
   ...
-  helper.EnableAscii (stream, d);
+  helper.EnableAscii(stream, d);
 
 You can enable ascii tracing on a collection of node/net-device pairs by
 providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` its
@@ -684,7 +684,7 @@
 
   NodeContainer n;
   ...
-  helper.EnableAscii ("prefix", n);
+  helper.EnableAscii("prefix", n);
 
 This would result in a number of ASCII trace files being created, each of which
 follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the
@@ -694,14 +694,14 @@
 with explicit ``Ptr``. Each ``Node`` in the system has an integer node ID and
 each device connected to a node has an integer device ID.::
 
-  helper.EnableAscii ("prefix", 21, 1);
+  helper.EnableAscii("prefix", 21, 1);
 
 Of course, the traces can be combined into a single file as shown above.
 
 Finally, you can enable pcap tracing for all devices in the system, with the
 same type as that managed by the device helper.::
 
-  helper.EnableAsciiAll ("prefix");
+  helper.EnableAsciiAll("prefix");
 
 This would result in a number of ASCII trace files being created, one for
 every device in the system of the type managed by the helper. All of these
@@ -751,14 +751,14 @@
 are required to disambiguate class ``Ipv4`` from ``Ipv6`` which are both derived
 from class ``Object``, and methods that share the same signature.::
 
-  virtual void EnablePcapIpv4Internal (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface) = 0;
+  virtual void EnablePcapIpv4Internal(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface) = 0;
 
 The signature of this method reflects the protocol and interface-centric view of
 the situation at this level. All of the public methods inherited from class
 ``PcapHelperForIpv4`` reduce to calling this single device-dependent
 implementation method.  For example, the lowest level pcap method,::
 
-  void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnablePcapIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
 
 will call the device implementation of ``EnablePcapIpv4Internal`` directly.  All
 other public pcap tracing methods build on this implementation to provide
@@ -777,12 +777,12 @@
 
 Note that just like in the device version, there are six methods::
 
-  void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
-  void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface);
-  void EnablePcapIpv4 (std::string prefix, Ipv4InterfaceContainer c);
-  void EnablePcapIpv4 (std::string prefix, NodeContainer n);
-  void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, uint32_t interface);
-  void EnablePcapIpv4All (std::string prefix);
+  void EnablePcapIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnablePcapIpv4(std::string prefix, std::string ipv4Name, uint32_t interface);
+  void EnablePcapIpv4(std::string prefix, Ipv4InterfaceContainer c);
+  void EnablePcapIpv4(std::string prefix, NodeContainer n);
+  void EnablePcapIpv4(std::string prefix, uint32_t nodeid, uint32_t interface);
+  void EnablePcapIpv4All(std::string prefix);
 
 You are encouraged to peruse the Doxygen for class ``PcapHelperForIpv4`` to find
 the details of these methods; but to summarize ...
@@ -790,17 +790,17 @@
 You can enable pcap tracing on a particular protocol/interface pair by providing
 a ``Ptr<Ipv4>`` and ``interface`` to an ``EnablePcap`` method.  For example,::
 
-  Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
+  Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
   ...
-  helper.EnablePcapIpv4 ("prefix", ipv4, 0);
+  helper.EnablePcapIpv4("prefix", ipv4, 0);
 
 You can enable pcap tracing on a particular node/net-device pair by providing a
 ``std::string`` representing an object name service string to an ``EnablePcap``
 method.  The ``Ptr<Ipv4>`` is looked up from the name string.  For example,::
 
-  Names::Add ("serverIPv4" ...);
+  Names::Add("serverIPv4" ...);
   ...
-  helper.EnablePcapIpv4 ("prefix", "serverIpv4", 1);
+  helper.EnablePcapIpv4("prefix", "serverIpv4", 1);
 
 You can enable pcap tracing on a collection of protocol/interface pairs by
 providing an ``Ipv4InterfaceContainer``. For each ``Ipv4`` / interface pair in
@@ -810,13 +810,13 @@
 
   NodeContainer nodes;
   ...
-  NetDeviceContainer devices = deviceHelper.Install (nodes);
+  NetDeviceContainer devices = deviceHelper.Install(nodes);
   ...
   Ipv4AddressHelper ipv4;
-  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
-  Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
+  ipv4.SetBase("10.1.1.0", "255.255.255.0");
+  Ipv4InterfaceContainer interfaces = ipv4.Assign(devices);
   ...
-  helper.EnablePcapIpv4 ("prefix", interfaces);
+  helper.EnablePcapIpv4("prefix", interfaces);
 
 You can enable pcap tracing on a collection of protocol/interface pairs by
 providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` the
@@ -825,19 +825,19 @@
 
   NodeContainer n;
   ...
-  helper.EnablePcapIpv4 ("prefix", n);
+  helper.EnablePcapIpv4("prefix", n);
 
 You can enable pcap tracing on the basis of node ID and interface as well. In
 this case, the node-id is translated to a ``Ptr<Node>`` and the appropriate
 protocol is looked up in the node. The resulting protocol and interface are used
 to specify the resulting trace source.::
 
-  helper.EnablePcapIpv4 ("prefix", 21, 1);
+  helper.EnablePcapIpv4("prefix", 21, 1);
 
 Finally, you can enable pcap tracing for all interfaces in the system, with
 associated protocol being the same type as that managed by the device helper.::
 
-  helper.EnablePcapIpv4All ("prefix");
+  helper.EnablePcapIpv4All("prefix");
 
 Pcap Tracing Protocol Helper Filename Selection
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -884,8 +884,8 @@
 using ASCII tracing to a protocol helper. Each protocol that enables these
 methods must implement a single virtual method inherited from this class.::
 
-  virtual void EnableAsciiIpv4Internal (Ptr<OutputStreamWrapper> stream, std::string prefix,
-                                        Ptr<Ipv4> ipv4, uint32_t interface) = 0;
+  virtual void EnableAsciiIpv4Internal(Ptr<OutputStreamWrapper> stream, std::string prefix,
+                                       Ptr<Ipv4> ipv4, uint32_t interface) = 0;
 
 The signature of this method reflects the protocol- and interface-centric view
 of the situation at this level; and also the fact that the helper may be writing
@@ -894,8 +894,8 @@
 dependent implementation method. For example, the lowest level ascii trace
 methods,::
 
-  void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
 
 will call the device implementation of ``EnableAsciiIpv4Internal`` directly,
 providing either the prefix or the stream. All other public ascii tracing
@@ -910,23 +910,23 @@
 
 ::
 
-  void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
 
-  void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, std::string ipv4Name, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, std::string ipv4Name, uint32_t interface);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, std::string ipv4Name, uint32_t interface);
 
-  void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ipv4InterfaceContainer c);
+  void EnableAsciiIpv4(std::string prefix, Ipv4InterfaceContainer c);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, Ipv4InterfaceContainer c);
 
-  void EnableAsciiIpv4 (std::string prefix, NodeContainer n);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, NodeContainer n);
+  void EnableAsciiIpv4(std::string prefix, NodeContainer n);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, NodeContainer n);
 
-  void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, uint32_t nodeid, uint32_t deviceid);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t interface);
 
-  void EnableAsciiIpv4All (std::string prefix);
-  void EnableAsciiIpv4All (Ptr<OutputStreamWrapper> stream);
+  void EnableAsciiIpv4All(std::string prefix);
+  void EnableAsciiIpv4All(Ptr<OutputStreamWrapper> stream);
 
 You are encouraged to peruse the Doxygen for class ``PcapAndAsciiHelperForIpv4``
 to find the details of these methods; but to summarize ...
@@ -945,7 +945,7 @@
 
   Ptr<Ipv4> ipv4;
   ...
-  helper.EnableAsciiIpv4 ("prefix", ipv4, 1);
+  helper.EnableAsciiIpv4("prefix", ipv4, 1);
 
 In this case, no trace contexts are written to the ASCII trace file since they
 would be redundant. The system will pick the file name to be created using the
@@ -957,13 +957,13 @@
 refer to a single file. We have already something similar to this in the "cwnd"
 example above::
 
-  Ptr<Ipv4> protocol1 = node1->GetObject<Ipv4> ();
-  Ptr<Ipv4> protocol2 = node2->GetObject<Ipv4> ();
+  Ptr<Ipv4> protocol1 = node1->GetObject<Ipv4>();
+  Ptr<Ipv4> protocol2 = node2->GetObject<Ipv4>();
   ...
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
   ...
-  helper.EnableAsciiIpv4 (stream, protocol1, 1);
-  helper.EnableAsciiIpv4 (stream, protocol2, 1);
+  helper.EnableAsciiIpv4(stream, protocol1, 1);
+  helper.EnableAsciiIpv4(stream, protocol2, 1);
 
 In this case, trace contexts are written to the ASCII trace file since they are
 required to disambiguate traces from the two interfaces. Note that since the
@@ -976,24 +976,24 @@
 the resulting filenames is implicit since there is a one-to-one correspondence
 between protocol instances and nodes, For example,::
 
-  Names::Add ("node1Ipv4" ...);
-  Names::Add ("node2Ipv4" ...);
+  Names::Add("node1Ipv4" ...);
+  Names::Add("node2Ipv4" ...);
   ...
-  helper.EnableAsciiIpv4 ("prefix", "node1Ipv4", 1);
-  helper.EnableAsciiIpv4 ("prefix", "node2Ipv4", 1);
+  helper.EnableAsciiIpv4("prefix", "node1Ipv4", 1);
+  helper.EnableAsciiIpv4("prefix", "node2Ipv4", 1);
 
 This would result in two files named "prefix-nnode1Ipv4-i1.tr" and
 "prefix-nnode2Ipv4-i1.tr" with traces for each interface in the respective
 trace file. Since all of the EnableAscii functions are overloaded to take a
 stream wrapper, you can use that form as well::
 
-  Names::Add ("node1Ipv4" ...);
-  Names::Add ("node2Ipv4" ...);
+  Names::Add("node1Ipv4" ...);
+  Names::Add("node2Ipv4" ...);
   ...
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
   ...
-  helper.EnableAsciiIpv4 (stream, "node1Ipv4", 1);
-  helper.EnableAsciiIpv4 (stream, "node2Ipv4", 1);
+  helper.EnableAsciiIpv4(stream, "node1Ipv4", 1);
+  helper.EnableAsciiIpv4(stream, "node2Ipv4", 1);
 
 This would result in a single trace file called "trace-file-name.tr" that
 contains all of the trace events for both interfaces. The events would be
@@ -1007,14 +1007,14 @@
 
   NodeContainer nodes;
   ...
-  NetDeviceContainer devices = deviceHelper.Install (nodes);
+  NetDeviceContainer devices = deviceHelper.Install(nodes);
   ...
   Ipv4AddressHelper ipv4;
-  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
-  Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
+  ipv4.SetBase("10.1.1.0", "255.255.255.0");
+  Ipv4InterfaceContainer interfaces = ipv4.Assign(devices);
   ...
   ...
-  helper.EnableAsciiIpv4 ("prefix", interfaces);
+  helper.EnableAsciiIpv4("prefix", interfaces);
 
 This would result in a number of ASCII trace files being created, each of which
 follows the <prefix>-n<node id>-i<interface>.tr convention. Combining all of the
@@ -1022,15 +1022,15 @@
 
   NodeContainer nodes;
   ...
-  NetDeviceContainer devices = deviceHelper.Install (nodes);
+  NetDeviceContainer devices = deviceHelper.Install(nodes);
   ...
   Ipv4AddressHelper ipv4;
-  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
-  Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
+  ipv4.SetBase("10.1.1.0", "255.255.255.0");
+  Ipv4InterfaceContainer interfaces = ipv4.Assign(devices);
   ...
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
   ...
-  helper.EnableAsciiIpv4 (stream, interfaces);
+  helper.EnableAsciiIpv4(stream, interfaces);
 
 You can enable ASCII tracing on a collection of protocol/interface pairs by
 providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` the
@@ -1039,7 +1039,7 @@
 
   NodeContainer n;
   ...
-  helper.EnableAsciiIpv4 ("prefix", n);
+  helper.EnableAsciiIpv4("prefix", n);
 
 This would result in a number of ASCII trace files being created, each of which
 follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the
@@ -1050,14 +1050,14 @@
 protocol is looked up in the node.  The resulting protocol and interface are
 used to specify the resulting trace source.::
 
-  helper.EnableAsciiIpv4 ("prefix", 21, 1);
+  helper.EnableAsciiIpv4("prefix", 21, 1);
 
 Of course, the traces can be combined into a single file as shown above.
 
 Finally, you can enable ASCII tracing for all interfaces in the system, with
 associated protocol being the same type as that managed by the device helper.::
 
-  helper.EnableAsciiIpv4All ("prefix");
+  helper.EnableAsciiIpv4All("prefix");
 
 This would result in a number of ASCII trace files being created, one for
 every interface in the system related to a protocol of the type managed by the
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/troubleshoot.rst ns-3.38/doc/manual/source/troubleshoot.rst
--- ns-3.37/doc/manual/source/troubleshoot.rst	2022-11-01 17:37:24.407395000 -0700
+++ ns-3.38/doc/manual/source/troubleshoot.rst	2023-03-16 20:54:23.536868300 -0700
@@ -28,7 +28,7 @@
     Compilation finished successfully
     Command ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] exited with code -11
 
-The error message says that the program terminated unsuccessfully, but it is
+The error message says that the program terminated unsuccessfuly, but it is
 not clear from this information what might be wrong. To examine more
 closely, try running it under the `gdb debugger
 <https://access.redhat.com/documentation/en-us/red_hat_developer_toolset/9/html/user_guide/chap-gdb>`_:
@@ -55,7 +55,7 @@
     Program received signal SIGSEGV, Segmentation fault.
     0x0804aa12 in main (argc=1, argv=0xbfdfefa4)
         at ../examples/tcp-point-to-point.cc:136
-    136       Ptr<Socket> localSocket = socketFactory->CreateSocket ();
+    136       Ptr<Socket> localSocket = socketFactory->CreateSocket();
     (gdb) p localSocket
     $1 = {m_ptr = 0x3c5d65}
     (gdb) p socketFactory
@@ -73,9 +73,9 @@
 
 .. sourcecode:: cpp
 
-  Ptr<SocketFactory> socketFactory = n2->GetObject<SocketFactory> (Tcp::iid);
-  Ptr<Socket> localSocket = socketFactory->CreateSocket ();
-  localSocket->Bind ();
+  Ptr<SocketFactory> socketFactory = n2->GetObject<SocketFactory>(Tcp::iid);
+  Ptr<Socket> localSocket = socketFactory->CreateSocket();
+  localSocket->Bind();
 
 The culprit here is that the return value of GetObject is not being checked and
 may be null.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/windows.rst ns-3.38/doc/manual/source/windows.rst
--- ns-3.37/doc/manual/source/windows.rst	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/manual/source/windows.rst	1969-12-31 16:00:00.000000000 -0800
@@ -1,798 +0,0 @@
-.. include:: replace.txt
-.. highlight:: console
-
-.. Section Separators:
-   ----
-   ****
-   ++++
-   ====
-   ~~~~
-
-.. _Windows 10:
-
-Windows 10
-----------
-
-This chapter describes installation steps specific to Windows 10 and its
-derivatives (e.g. Home, Pro, Enterprise) using the Msys2/MinGW64 toolchain.
-
-.. _Windows 10 package prerequisites:
-
-Windows 10 package prerequisites
-********************************
-
-The following instructions are relevant to the ns-3.37 release and Windows 10.
-
-Installation of the Msys2 environment
-+++++++++++++++++++++++++++++++++++++
-
-.. _Msys2: https://www.msys2.org/
-
-The `Msys2`_ includes ports of Unix tools for Windows built with multiple toolchains,
-including: MinGW32, MinGW64, Clang64, UCRT.
-
-The MinGW64 (GCC) toolchain is the one ns-3 was tested.
-
-The `Msys2`_ installer can be found on their site.
-Msys2 will be installed by default in the ``C:\msys64`` directory.
-
-The next required step is adding the binaries directories from the MinGW64 toolchain and generic Msys2 tools
-to the PATH environment variable. This can be accomplished via the GUI (search for system environment variable),
-or via the following command (assuming it was installed to the default directory):
-
-.. sourcecode:: console
-
-    C:\> setx PATH "C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%;" /m
-
-Note: if the MinGW64 binary directory doesn't precede the Windows/System32 directory (already in ``%PATH%``),
-the documentation build will fail since Windows has a conflicting ``convert`` command (FAT-to-NTFS). Similarly,
-the Msys64 binary directory doesn't precede the Windows/System2 directory, running the ``bash`` command will
-result in Windows trying to run the Windows Subsystem for Linux (WSL) bash shell.
-
-Accessing the MinGW64 shell
-+++++++++++++++++++++++++++
-
-After installing Msys2 and adding the binary directories to the ``PATH``, we can access the Unix-like MinGW64 shell
-and use the Pacman package manager.
-
-The Pacman package manager is similar to the one used by Arch Linux, and can be accessed via one of the Msys2 shells.
-In this case, we will be using the MinGW64 shell. We can take this opportunity to update the package cache and packages.
-
-.. sourcecode:: console
-
-    C:\ns-3-dev\> set MSYSTEM MINGW64
-    C:\ns-3-dev\> bash
-    /c/ns-3-dev/ MINGW64$ pacman -Syu
-
-Pacman will request you to close the shell and re-open it to proceed after the upgrade.
-
-
-Minimal requirements for C++ (release)
-++++++++++++++++++++++++++++++++++++++
-
-This is the minimal set of packages needed to run ns-3 C++ programs from a released tarball.
-
-.. sourcecode:: console
-
-    /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake \
-    mingw-w64-x86_64-ninja mingw-w64-x86_64-grep mingw-w64-x86_64-sed mingw-w64-x86_64-python
-
-
-Netanim animator
-++++++++++++++++
-
-Qt5 development tools are needed for Netanim animator. Qt4 will also work but we have migrated to qt5.
-
-.. sourcecode:: console
-
-    /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-qt5 git
-
-
-Support for MPI-based distributed emulation
-+++++++++++++++++++++++++++++++++++++++++++
-
-The MPI setup requires two parts.
-
-The first part is the Microsoft MPI SDK required to build the MPI applications,
-which is distributed via Msys2.
-
-.. sourcecode:: console
-
-    /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-msmpi
-
-.. _Microsoft MPI: https://www.microsoft.com/en-us/download/details.aspx?id=100593
-
-The second part is the `Microsoft MPI`_ executors (mpiexec, mpirun) package,
-which is distributed as an installable (``msmpisetup.exe``).
-
-After installing it, the path containing the executors also need to be included to ``PATH``
-environment variable of the Windows and/or the MinGW64 shell, depending on whether
-you want to run MPI programs in either shell or both of them.
-
-.. sourcecode:: console
-
-    C:\ns-3-dev\> setx PATH "%PATH%;C:\Program Files\Microsoft MPI\Bin" /m
-    C:\ns-3-dev\> set MSYSTEM MINGW64
-    C:\ns-3-dev\> bash
-    /c/ns-3-dev/ MINGW64$ echo "export PATH=$PATH:/c/Program\ Files/Microsoft\ MPI/Bin" >> ~/.bashrc
-
-Debugging
-+++++++++
-
-GDB is installed along with the mingw-w64-x86_64-toolchain package.
-
-Support for utils/check-style.py code style check program
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-.. sourcecode:: console
-
-    /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-uncrustify
-
-
-Doxygen and related inline documentation
-++++++++++++++++++++++++++++++++++++++++
-
-To build the Doxygen-based documentation, we need doxygen and a Latex toolchain.
-Getting Doxygen from Msys2 is straightforward.
-
-.. sourcecode:: console
-
-  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-imagemagick mingw-w64-x86_64-doxygen mingw-w64-x86_64-graphviz
-
-For unknown reasons, the texlive packages in Msys2 are broken,
-so we recommend manually installing the official Texlive.
-
-.. _Texlive for Windows: https://tug.org/texlive/windows.html
-
-Start by installing `Texlive for Windows`_. It can be installed either via the network installer or
-directly from the ISO with all optional packages.
-
-To install it with the network installer, start by creating a texlive folder and downloading the
-texlive configuration profile below. You can change the installation directory (starting with
-``C:/texlive/2022``).
-
-.. sourcecode:: raw
-
-    selected_scheme scheme-custom
-    TEXDIR C:/texlive/2022
-    TEXMFCONFIG ~/.texlive2022/texmf-config
-    TEXMFHOME ~/texmf
-    TEXMFLOCAL C:/texlive/texmf-local
-    TEXMFSYSCONFIG C:/texlive/2022/texmf-config
-    TEXMFSYSVAR C:/texlive/2022/texmf-var
-    TEXMFVAR ~/.texlive2022/texmf-var
-    binary_win32 1
-    collection-basic 1
-    collection-bibtexextra 1
-    collection-binextra 1
-    collection-context 1
-    collection-fontsrecommended 1
-    collection-fontutils 1
-    collection-games 1
-    collection-humanities 1
-    collection-langenglish 1
-    collection-latex 1
-    collection-latexextra 1
-    collection-latexrecommended 1
-    collection-luatex 1
-    collection-mathscience 1
-    collection-metapost 1
-    collection-music 1
-    collection-pictures 1
-    collection-plaingeneric 1
-    collection-pstricks 1
-    collection-publishers 1
-    collection-texworks 1
-    collection-wintools 1
-    collection-xetex 1
-    instopt_adjustpath 1
-    instopt_adjustrepo 1
-    instopt_letter 0
-    instopt_portable 0
-    instopt_write18_restricted 1
-    tlpdbopt_autobackup 1
-    tlpdbopt_backupdir tlpkg/backups
-    tlpdbopt_create_formats 1
-    tlpdbopt_desktop_integration 1
-    tlpdbopt_file_assocs 1
-    tlpdbopt_generate_updmap 0
-    tlpdbopt_install_docfiles 0
-    tlpdbopt_install_srcfiles 0
-    tlpdbopt_post_code 1
-    tlpdbopt_sys_bin /usr/local/bin
-    tlpdbopt_sys_info /usr/local/share/info
-    tlpdbopt_sys_man /usr/local/share/man
-    tlpdbopt_w32_multi_user 0
-
-
-Then, download Texlive's web installer and unpack it to the texlive directory,
-and run the installer.
-
-.. sourcecode:: console
-
-    ~$ wget https://linorg.usp.br/CTAN/systems/texlive/tlnet/install-tl.zip
-    ~$ python3 -c "import shutil; shutil.unpack_archive('install-tl.zip', './')"
-    ~$ cd install-tl-*
-    ~/install-tl-20220923$ .\install-tl-windows.bat --no-gui --lang en -profile ..\texlive.profile
-
-After the installation finishes, add the installation directory to the PATH environment variable.
-
-.. sourcecode:: console
-
-    setx PATH "%PATH%;C:\texlive\2022\bin\win32" /m
-    C:\ns-3-dev\> set MSYSTEM MINGW64
-    C:\ns-3-dev\> bash
-    /c/ns-3-dev/ MINGW64$ echo "export PATH=$PATH:/c/texlive/2022/bin/win32" >> ~/.bashrc
-
-The ns-3 manual, models and tutorial
-++++++++++++++++++++++++++++++++++++
-
-These documents are written in reStructuredText for Sphinx (doc/tutorial, doc/manual, doc/models).
-The figures are typically written in dia.
-
-The documents can be generated into multiple formats, one of them being pdf,
-which requires the same Latex setup for doxygen.
-
-Sphinx can be installed via Msys2's package manager:
-
-.. sourcecode:: console
-
-  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-python-sphinx
-
-.. _Dia: https://sourceforge.net/projects/dia-installer/
-
-`Dia`_ on the other hand needs to be downloaded and installed manually.
-After installing it, or unzipping the package (example below), add Dia/bin to the PATH.
-
-.. sourcecode:: console
-
-    C:\ns-3-dev\> setx PATH "%PATH%;C:\dia_0.97.2_win32\bin" /m
-    C:\ns-3-dev\> set MSYSTEM MINGW64
-    C:\ns-3-dev\> bash
-    /c/ns-3-dev/ MINGW64$ echo "export PATH=$PATH:/c/dia_0.97.2_win32/bin" >> ~/.bashrc
-
-GNU Scientific Library (GSL)
-++++++++++++++++++++++++++++
-
-GSL is used to provide more accurate WiFi error models and can be installed with:
-
-.. sourcecode:: console
-
-  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-gsl
-
-Database support for statistics framework
-+++++++++++++++++++++++++++++++++++++++++
-
-SQLite3 is installed along with the mingw-w64-x86_64-toolchain package.
-
-
-Xml-based version of the config store
-+++++++++++++++++++++++++++++++++++++
-
-Requires libxml2 >= version 2.7, which can be installed with the following.
-
-.. sourcecode:: console
-
-  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-libxml2
-
-Support for openflow module
-+++++++++++++++++++++++++++
-
-Requires some boost libraries that can be installed with the following.
-
-.. sourcecode:: console
-
-  /c/ns-3-dev/ MINGW64$ pacman -S mingw-w64-x86_64-boost
-
-Windows 10 Docker container
-***************************
-
-Docker containers are not as useful for Windows, since only Windows hosts can use them,
-however we add directions on how to use the Windows container and how to update the Docker
-image for reference.
-
-First, gather all dependencies previously mentioned to cover all supported features.
-Install them to a base directory for the container (e.g. ``C:\tools``).
-
-Save the following Dockerfile to the base directory.
-
-.. sourcecode:: docker
-
-    # It is really unfortunate we need a 16 GB base image just to get the installers working, but such is life
-    FROM mcr.microsoft.com/windows:20H2
-
-    # Copy the current host directory to the container
-    COPY .\\ C:/tools
-    WORKDIR C:\\tools
-
-    # Create temporary dir
-    RUN mkdir C:\\tools\\temp
-
-    # Export environment variables
-    RUN setx PATH "C:\\tools\\msys64\\mingw64\\bin;C:\\tools\\msys64\\usr\\bin;%PATH%" /m
-    RUN setx PATH "%PATH%;C:\\Program Files\\Microsoft MPI\\bin;C:\\tools\\dia\bin;C:\tools\texlive\2022\bin\win32" /m
-    RUN setx MSYSTEM "MINGW64" /m
-
-    # Install Msys2
-    RUN .\\msys2-x86_64-20220503.exe in --confirm-command --accept-messages --root C:\\tools\\msys64
-
-    # Update base packages
-    RUN C:\\tools\\msys64\\usr\\bin\\pacman -Syyuu --noconfirm
-
-    # Install base packages
-    RUN bash -c "echo export PATH=$PATH:/c/Program\ Files/Microsoft\ MPI/Bin >> /c/tools/msys64/home/$USER/.bashrc" && \
-        bash -c "echo export PATH=$PATH:/c/tools/dia/bin >> /c/tools/msys64/home/$USER/.bashrc" && \
-        bash -c "echo export PATH=$PATH:/c/tools/texlive/2022/bin/win32 >> /c/tools/msys64/home/$USER/.bashrc" && \
-        bash -c "pacman -S mingw-w64-x86_64-toolchain \
-                       mingw-w64-x86_64-cmake \
-                       mingw-w64-x86_64-ninja \
-                       mingw-w64-x86_64-grep \
-                       mingw-w64-x86_64-sed \
-                       mingw-w64-x86_64-qt5 \
-                       git \
-                       mingw-w64-x86_64-msmpi \
-                       mingw-w64-x86_64-uncrustify \
-                       mingw-w64-x86_64-imagemagick \
-                       mingw-w64-x86_64-doxygen \
-                       mingw-w64-x86_64-graphviz \
-                       mingw-w64-x86_64-python-sphinx \
-                       mingw-w64-x86_64-gsl \
-                       mingw-w64-x86_64-libxml2 \
-                       mingw-w64-x86_64-boost \
-                       --noconfirm"
-
-    # Install Microsoft MPI
-    RUN .\\msmpisetup.exe -unattend -force -full -verbose
-
-    # Install TexLive
-    RUN .\\install-tl-20220526\\install-tl-windows.bat --no-gui --lang en -profile .\\texlive.profile
-
-    # Move working directory to temp and start cmd
-    WORKDIR C:\\tools\\temp
-    ENTRYPOINT ["cmd"]
-
-Now you should be able to run ``docker build -t username/image .``.
-
-After building the container image, you should be able to use it:
-
-.. sourcecode:: console
-
-    $ docker run -it username/image
-    C:\tools\temp$ git clone https://gitlab.com/nsnam/ns-3-dev
-    C:\tools\temp$ cd ns-3-dev
-    C:\tools\temp\ns-3-dev$ python ns3 configure --enable-tests --enable-examples
-    C:\tools\temp\ns-3-dev$ python ns3 build
-    C:\tools\temp\ns-3-dev$ python test.py
-
-If testing succeeds, the container image can then be pushed to the Docker Hub using
-``docker push username/image``.
-
-
-Windows 10 Vagrant
-******************
-
-.. _VirtualBox: https://www.virtualbox.org/
-.. _Vagrant: https://www.vagrantup.com/
-.. _virtual machine: https://www.redhat.com/en/topics/virtualization/what-is-a-virtual-machine
-.. _boxes available: https://app.vagrantup.com/boxes/search
-
-As an alternative to manually setting up all dependencies required by ns-3,
-one can use a pre-packaged `virtual machine`_. There are many ways to do that,
-but for automation, the most used certainly is `Vagrant`_.
-
-Vagrant supports multiple virtual machine providers, is available in all platforms and is
-fairly straightforward to use and configure.
-
-There are many `boxes available`_ offering guests operating systems such as BSD, Mac, Linux and Windows.
-
-Using the pre-packaged Vagrant box
-++++++++++++++++++++++++++++++++++
-
-The provider for the ns-3 Vagrant box is `VirtualBox`_.
-
-The reference Windows virtual machine can be downloaded via the following Vagrant command
-
-.. sourcecode:: console
-
-    ~/mingw64_test $ vagrant init gabrielcarvfer/ns3_win10_mingw64
-
-After that, a Vagrantfile will be created in the current directory (in this case, mingw64_test).
-
-The file can be modified to adjust the number of processors and memory available to the virtual machine (VM).
-
-.. sourcecode:: console
-
-    ~/mingw64_test $ cat Vagrantfile
-    # -*- mode: ruby -*-
-    # vi: set ft=ruby :
-
-    # All Vagrant configuration is done below. The "2" in Vagrant.configure
-    # configures the configuration version (we support older styles for
-    # backwards compatibility). Please don't change it unless you know what
-    # you're doing.
-    Vagrant.configure("2") do |config|
-    # The most common configuration options are documented and commented below.
-    # For a complete reference, please see the online documentation at
-    # https://docs.vagrantup.com.
-
-    # Every Vagrant development environment requires a box. You can search for
-    # boxes at https://vagrantcloud.com/search.
-    config.vm.box = "gabrielcarvfer/ns3_win10_mingw64"
-
-    # Disable automatic box update checking. If you disable this, then
-    # boxes will only be checked for updates when the user runs
-    # `vagrant box outdated`. This is not recommended.
-    # config.vm.box_check_update = false
-
-    # Create a forwarded port mapping which allows access to a specific port
-    # within the machine from a port on the host machine. In the example below,
-    # accessing "localhost:8080" will access port 80 on the guest machine.
-    # NOTE: This will enable public access to the opened port
-    # config.vm.network "forwarded_port", guest: 80, host: 8080
-
-    # Create a forwarded port mapping which allows access to a specific port
-    # within the machine from a port on the host machine and only allow access
-    # via 127.0.0.1 to disable public access
-    # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
-
-    # Create a private network, which allows host-only access to the machine
-    # using a specific IP.
-    # config.vm.network "private_network", ip: "192.168.33.10"
-
-    # Create a public network, which generally matched to bridged network.
-    # Bridged networks make the machine appear as another physical device on
-    # your network.
-    # config.vm.network "public_network"
-
-    # Share an additional folder to the guest VM. The first argument is
-    # the path on the host to the actual folder. The second argument is
-    # the path on the guest to mount the folder. And the optional third
-    # argument is a set of non-required options.
-    # config.vm.synced_folder "../data", "/vagrant_data"
-
-    # Provider-specific configuration so you can fine-tune various
-    # backing providers for Vagrant. These expose provider-specific options.
-    # Example for VirtualBox:
-    #
-    # config.vm.provider "virtualbox" do |vb|
-    #   # Display the VirtualBox GUI when booting the machine
-    #   vb.gui = true
-    #
-    #   # Customize the amount of memory on the VM:
-    #   vb.memory = "1024"
-    # end
-    #
-    # View the documentation for the provider you are using for more
-    # information on available options.
-
-    # Enable provisioning with a shell script. Additional provisioners such as
-    # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
-    # documentation for more information about their specific syntax and use.
-    # config.vm.provision "shell", inline: <<-SHELL
-    #   apt-get update
-    #   apt-get install -y apache2
-    # SHELL
-    end
-
-We can uncomment the virtualbox provider block and change vCPUs and RAM.
-It is recommended never to match the number of vCPUs to the number of thread of the machine,
-or the host operating system can become unresponsive.
-For compilation workloads, it is recommended to allocate 1-2 GB of RAM per vCPU.
-
-.. _user Vagrantfile:
-
-.. sourcecode:: console
-
-    ~/mingw64_test/ $ cat Vagrantfile
-    # -*- mode: ruby -*-
-    # vi: set ft=ruby :
-    Vagrant.configure("2") do |config|
-    config.vm.box = "gabrielcarvfer/ns3_win10_mingw64"
-      config.vm.provider "virtualbox" do |vb|
-        vb.cpus = "8"
-        vb.memory = "8096" # 8GB of RAM
-      end
-    end
-
-After changing the settings, we can start the VM and login via ssh. The default password is "vagrant".
-
-.. sourcecode:: console
-
-    ~/mingw64_test/ $ vagrant up
-    ~/mingw64_test/ $ vagrant ssh
-    C:\Users\vagrant>
-
-
-We are now logged into the machine and ready to work. If you prefer to update the tools, get into the
-MinGW64 shell and run pacman.
-
-.. sourcecode:: console
-
-    C:\Users\vagrant\> set MSYSTEM MINGW64
-    C:\Users\vagrant\> bash
-    /c/Users/vagrant/ MINGW64$ pacman -Syu
-    /c/Users/vagrant/ MINGW64$ exit
-    C:\Users\vagrant\>
-
-At this point, we can clone ns-3 locally:
-
-.. sourcecode:: console
-
-    C:\Users\vagrant> git clone `https://gitlab.com/nsnam/ns-3-dev`
-    C:\Users\vagrant> cd ns-3-dev
-    C:\Users\vagrant\ns-3-dev> python3 ns3 configure --enable-tests --enable-examples --enable-mpi
-    C:\Users\vagrant\ns-3-dev> python3 test.py
-
-We can also access the ~/mingw64_test/ from the host machine, where the Vagrantfile resides, by accessing
-the synchronized folder C:\vagrant.
-If the Vagrantfile is in the host ns-3-dev directory, we can continue working on it.
-
-.. sourcecode:: console
-
-    C:\Users\vagrant> cd C:\vagrant
-    C:\vagrant\> python3 ns3 configure --enable-tests --enable-examples --enable-mpi
-    C:\vagrant\> python3 test.py
-
-If all the PATH variables were set for the MinGW64 shell, we can also use it instead of the
-default CMD shell.
-
-.. sourcecode:: console
-
-    C:\vagrant\> set MSYSTEM=MINGW64
-    C:\vagrant\> bash
-    /c/vagrant/ MINGW64$ ./ns3 clean
-    /c/vagrant/ MINGW64$ ./ns3 configure --enable-tests --enable-examples --enable-mpi
-    /c/vagrant/ MINGW64$ ./test.py
-
-To stop the Vagrant machine, we should close the SSH session then halt.
-
-.. sourcecode:: console
-
-    /c/vagrant/ MINGW64$ exit
-    C:\vagrant\> exit
-    ~/mingw64_test/ vagrant halt
-
-To destroy the machine (e.g. to restore the default settings), use the following.
-
-.. sourcecode:: console
-
-    vagrant destroy
-
-Packaging a new Vagrant box
-+++++++++++++++++++++++++++
-
-**BEWARE: DO NOT CHANGE THE SETTINGS MENTIONED ON A REAL MACHINE**
-
-**THE SETTINGS ARE MEANT FOR A DISPOSABLE VIRTUAL MACHINE**
-
-.. _Windows 10 ISO: https://www.microsoft.com/pt-br/software-download/windows10ISO
-
-Start by downloading the `Windows 10 ISO`_.
-
-Then install `VirtualBox`_.
-
-Configure a VirtualBox VM and use the Windows 10 ISO file as the install source.
-
-During the installation, create a local user named "vagrant" and set its password to "vagrant".
-
-Check for any Windows updates and install them.
-
-The following commands assume administrative permissions and a PowerShell shell.
-
-Install the VirtualBox guest extensions
-=======================================
-
-On the VirtualBox GUI, click on ``Devices->Insert Guest Additions CD Image...``
-to download the VirtualBox guest extensions ISO and mount it as a CD drive on the guest VM.
-
-Run the installer to enable USB-passthrough, folder syncing and others.
-
-After installing, unmount the drive by removing it from the VM. Click on ``Settings->Storage``,
-select the guest drive and remove it clicking the button with an red ``x``.
-
-Install the OpenSSH server
-==========================
-
-Open PowerShell and run the following to install OpenSSH server,
-then set it to start automatically and open the firewall ports.
-
-.. sourcecode:: console
-
-    Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
-    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
-    Start-Service sshd
-    Set-Service -Name sshd -StartupType 'Automatic'
-    if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
-        Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
-        New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
-    } else {
-        Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
-    }
-
-Enable essential services and disable unnecessary ones
-======================================================
-
-Ensure the following services are set to **automatic** from the Services panel(services.msc):
-
-    - Base Filtering Engine
-    - Remote Procedure Call (RPC)
-    - DCOM Server Process Launcher
-    - RPC Endpoint Mapper
-    - Windows Firewall
-
-Ensure the following services are set to **disabled** from the Services panel(services.msc):
-
-    - Windows Update
-    - Windows Update Remediation
-    - Windows Search
-
-The same can be accomplished via the command-line with the following commands:
-
-.. sourcecode:: console
-
-    Set-Service -Name BFE -StartupType 'Automatic'
-    Set-Service -Name RpcSs -StartupType 'Automatic'
-    Set-Service -Name DcomLaunch -StartupType 'Automatic'
-    Set-Service -Name RpcEptMapper -StartupType 'Automatic'
-    Set-Service -Name mpssvc -StartupType 'Automatic'
-    Set-Service -Name wuauserv -StartupType 'Disabled'
-    Set-Service -Name WaaSMedicSvc -StartupType 'Disabled'
-    Set-Service -Name WSearch -StartupType 'Disabled'
-
-Install the packages you need
-=============================
-
-In this step we install all the software required by ns-3, as listed in the Section `Windows 10 package prerequisites`_.
-
-Disable Windows Defender
-========================
-
-After installing everything, it should be safe to disable the Windows security.
-
-Enter in the Windows Security settings and disable "anti-tamper protection".
-It rollbacks changes to security settings periodically.
-
-Enter in the Group Policy Editor (gpedit.msc) and disable:
-
-    - Realtime protection
-    - Behavior monitoring
-    - Scanning of archives, removable drives, network files, scripts
-    - Windows defender
-
-
-The same can be accomplished with the following command-line commands.
-
-.. sourcecode:: console
-
-    Set-MpPreference -DisableArchiveScanning 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableBehaviorMonitoring 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableIntrusionPreventionSystem 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableIOAVProtection 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableRemovableDriveScanning 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableBlockAtFirstSeen 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableScanningMappedNetworkDrivesForFullScan 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableScanningNetworkFiles 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableScriptScanning 1 -ErrorAction SilentlyContinue
-    Set-MpPreference -DisableRealtimeMonitoring 1 -ErrorAction SilentlyContinue
-    Set-Service -Name WdNisSvc -StartupType 'Disabled'
-    Set-Service -Name WinDefend -StartupType 'Disabled'
-    Set-Service -Name Sense -StartupType 'Disabled'
-    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Real-Time Protection" -Name SpyNetReporting -Value 0
-    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Real-Time Protection" -Name SubmitSamplesConsent -Value 0
-    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name TamperProtection -Value 4
-    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender" -Name DisableAntiSpyware -Value 1
-    Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" -Name DisableAntiSpyware -Value 1
-
-Note: the previous commands were an excerpt from the complete script in:
-https://github.com/jeremybeaume/tools/blob/master/disable-defender.ps1
-
-Turn off UAC notifications
-==========================
-
-The UAC notifications are the popups where you can give your OK to elevation to administrative privileges.
-It can be disabled via User Account Control Settings, or via the following commands.
-
-.. sourcecode:: console
-
-    reg ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f
-
-
-Change the strong password security Policy
-==========================================
-
-Open the Local Security Policy management window. Under Security Settings/Account Policy/Password Policy,
-disable the option saying "Password must meet complexity requirements".
-
-
-Testing
-=======
-
-After you reach this point, reboot your machine then log back in.
-
-Test if all your packages are working as expected.
-
-In the case of ns-3, try to enable all supported features, run the test.py and test-ns3.py suites.
-
-If everything works, then try to log in via SSH.
-
-If everything works, shut down the machine and prepare for packaging.
-
-The network interface configured should be a NAT. Other interfaces won't work correctly.
-
-Default Vagrantfile
-===================
-
-Vagrant can package VirtualBox VMs into Vagrant boxes without much more work.
-However, it still needs one more file to do that: the default Vagrantfile.
-
-This file will be used by Vagrant to configure the VM later on and how to connect to it.
-
-.. sourcecode:: ruby
-
-    # -*- mode: ruby -*-
-    # vi: set ft=ruby :
-
-    Vagrant.configure("2") do |config|
-    config.vm.box = "BOX_FILE.box" # name of the box
-    config.vm.communicator = "winssh" # indicate that we are talking to a windows box via ssh
-    config.vm.guest = :windows # indicate that the guest is a windows machine
-    config.vm.network :forwarded_port, guest: 3389, host: 3389, id: "rdp", auto_correct: true
-    config.ssh.password = "vagrant" # give the default password, so that it stops trying to use a .ssh key-pair
-    config.ssh.insert_key = false # let the user use a written password
-    config.ssh.keys_only = false
-    config.winssh.shell = "cmd" # select the default shell (could be cmd or powershell)
-      config.vm.provider :virtualbox do |v, override|
-          #v.gui = true # do not show the VirtualBox GUI if unset or set to false
-          v.customize ["modifyvm", :id, "--memory", 8096] # the default settings for the VM are 8GB of RAM
-          v.customize ["modifyvm", :id, "--cpus", 8] # the default settings for the VM are 8 vCPUs
-          v.customize ["modifyvm", :id, "--vram", 128] # 128 MB or vGPU RAM
-          v.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
-          v.customize ["setextradata", "global", "GUI/SuppressMessages", "all" ]
-      end
-    end
-
-This Vagrantfile will be baked into the Vagrant box, and can be modified by the `user Vagrantfile`_.
-After writing the Vagrantfile, we can call the following command.
-
-.. sourcecode:: console
-
-    vagrant package --vagrantfile Vagrantfile --base VIRTUALBOX_VM_NAME --output BOX_FILE.box
-
-It will take an awful long time depending on your drive.
-
-After it finishes, we can add the box to test it.
-
-.. sourcecode:: console
-
-    vagrant box add BOX_NAME BOX_FILE
-    vagrant up BOX_NAME
-    vagrant ssh
-
-If it can connect to the box, you are ready to upload it to the Vagrant servers.
-
-Publishing the Vagrant box
-==========================
-
-Create an account in https://app.vagrantup.com/ or log in with yours.
-In the dashboard, you can create a new box named BOX_NAME or select an existing one to update.
-
-After you select your box, click to add a provider. Pick Virtualbox.
-Calculate the MD5 hash of your BOX_FILE.box and fill the field then click to proceed.
-
-Upload the box.
-
-Now you should be able to download your box from the Vagrant servers via the the following command.
-
-.. sourcecode:: console
-
-    vagrant init yourUserName/BOX_NAME
-    vagrant up
-    vagrant ssh
-
-More information on Windows packaging to Vagrant boxes is available here:
-
-    - https://www.vagrantup.com/docs/boxes/base
-    - https://www.vagrantup.com/docs/vagrantfile/machine_settings
-    - https://www.vagrantup.com/docs/vagrantfile/ssh_settings
-    - https://www.vagrantup.com/docs/vagrantfile/winssh_settings
-    - https://github.com/pghalliday/windows-vagrant-boxes
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/working-with-cmake.rst ns-3.38/doc/manual/source/working-with-cmake.rst
--- ns-3.37/doc/manual/source/working-with-cmake.rst	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/manual/source/working-with-cmake.rst	2023-03-16 20:54:23.536868300 -0700
@@ -13,7 +13,7 @@
 Working with CMake
 ------------------
 
-The ns-3 project used Waf build system in the past, but it has moved to
+The |ns3| project used Waf build system in the past, but it has moved to
 CMake for the ns-3.36 release.
 
 CMake is very verbose and commands can be very long for basic operations.
@@ -30,7 +30,7 @@
 .. _CMake generated : https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html
 .. _generator options : https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html
 
-It is the recommended way to work on ns-3, except if you are using an
+It is the recommended way to work on |ns3|, except if you are using an
 IDE that supports projects that can be generated with CMake or CMake projects.
 
 Here is a non-exhaustive list of IDEs that can be used:
@@ -62,8 +62,8 @@
 There are two ways to configure the project: the easiest way
 is using the ``ns3`` script and the other way directly with CMake.
 
-Configuring the project with ns3
-++++++++++++++++++++++++++++++++
+Configuring the project with ``ns3``
+++++++++++++++++++++++++++++++++++++
 
 Navigate to the ns-3-dev directory, then run ``./ns3 configure --help`` to
 print the configuration options:
@@ -95,7 +95,7 @@
 
 Note: the command output was trimmed to the most used options.
 
-To configure ns-3 in release mode, while enabling examples and tests,
+To configure |ns3| in release mode, while enabling examples and tests,
 run ``./ns3 configure -d release --enable-examples --enable-tests``.
 To check what underlying commands dare being executed, add the
 ``--dry-run`` option:
@@ -123,7 +123,7 @@
   ...
   -- Processing src/wifi
   -- Processing src/wimax
-  -- ---- Summary of optional NS-3 features:
+  -- ---- Summary of optional ns-3 features:
   Build profile                 : release
   Build directory               : /mnt/dev/tools/source/ns-3-dev/build
   ...
@@ -162,23 +162,48 @@
 At the end, notice we print the same commands from ``--dry-run``. This is done
 to familiarize Waf users with CMake and how the options names changed.
 
-The mapping of the ns3 build profiles into the CMake build types is the following:
+The mapping of the ``ns3`` build profiles into the CMake build types is the following:
+
++--------------------------------------------------------------------------------------------------------------------+
+| Equivalent build profiles                                                                                          |
++-------------------------+--------------------------------------------------------+---------------------------------+
+| ``ns3 --build-profile`` | CMake                                                  | Equivalent GCC compiler flags   |
+|                         +------------------------+-------------------------------+---------------------------------+
+|                         | CMAKE_BUILD_TYPE       | Additional flags              |                                 |
++=========================+========================+===============================+=================================+
+| debug                   | debug                  |                               | -g                              |
++-------------------------+------------------------+-------------------------------+---------------------------------+
+| default                 | default                |                               | -O2 -g                          |
++-------------------------+------------------------+-------------------------------+---------------------------------+
+| release                 | release                |                               | -O3                             |
++-------------------------+------------------------+-------------------------------+---------------------------------+
+| optimized               | release                | -DNS3_NATIVE_OPTIMIZATIONS=ON | -O3 -march=native -mtune=native |
++-------------------------+------------------------+-------------------------------+---------------------------------+
+| minsizerel              | minsizerel             |                               | -Os                             |
++-------------------------+------------------------+-------------------------------+---------------------------------+
+
+In addition to setting compiler flags each build type also controls whether certain features are enabled or not:
+
++-------------------------+-----------------+-------------+----------------------------+
+| ``ns3 --build-profile`` |  ``NS3_ASSERT`` | ``NS3_LOG`` | ``NS3_WARNINGS_AS_ERRORS`` |
++=========================+=================+=============+============================+
+| debug                   |   ON            |   ON        |   ON                       |
++-------------------------+-----------------+-------------+----------------------------+
+| default                 |   ON            |   ON        |   OFF                      |
++-------------------------+-----------------+-------------+----------------------------+
+| release                 |   OFF           |   OFF       |   OFF                      |
++-------------------------+-----------------+-------------+----------------------------+
+| optimized               |   OFF           |   OFF       |   OFF                      |
++-------------------------+-----------------+-------------+----------------------------+
+| minsizerel              |   OFF           |   OFF       |   OFF                      |
++-------------------------+-----------------+-------------+----------------------------+
+
+``NS3_ASSERT`` and ``NS_LOG`` control whether the assert or logging macros
+are functional or compiled out.
+``NS3_WARNINGS_AS_ERRORS`` controls whether compiler warnings are treated
+as errors and stop the build, or whether they are only warnings and
+allow the build to continue.
 
-+---------------------------+------------------------------------------------------------------------------------------+
-| Equivalent build profiles                                                                                            |
-+---------------------------+--------------------------------------------------------+---------------------------------+
-| ns3                       | CMake                                                  | Equivalent GCC compiler flags   |
-|                           +------------------------+-------------------------------+---------------------------------+
-|                           | CMAKE_BUILD_TYPE       | Additional flags              |                                 |
-+===========================+========================+===============================+=================================+
-| debug                     | debug                  |                               | -g                              |
-+---------------------------+------------------------+-------------------------------+---------------------------------+
-| default                   | default|relwithdebinfo |                               | -O2 -g                          |
-+---------------------------+------------------------+-------------------------------+---------------------------------+
-| release                   | release                |                               | -O3                             |
-+---------------------------+------------------------+-------------------------------+---------------------------------+
-| optimized                 | release                | -DNS3_NATIVE_OPTIMIZATIONS=ON | -O3 -march=native -mtune=native |
-+---------------------------+------------------------+-------------------------------+---------------------------------+
 
 Configuring the project with CMake
 ++++++++++++++++++++++++++++++++++
@@ -210,10 +235,12 @@
 +==================+===================+
 | DEBUG            | -g                |
 +------------------+-------------------+
-| RELEASE          | -O3 -DNDEBUG      |
+| DEFAULT          | -O2 -g -DNDEBUG   |
 +------------------+-------------------+
 | RELWITHDEBINFO   | -O2 -g -DNDEBUG   |
 +------------------+-------------------+
+| RELEASE          | -O3 -DNDEBUG      |
++------------------+-------------------+
 | MINSIZEREL       | -Os -DNDEBUG      |
 +------------------+-------------------+
 
@@ -314,7 +341,7 @@
 Previous settings stored in the CMakeCache.txt will be preserved, while new modules will be
 scanned and targets will be added.
 
-The cache can also be refreshed with the ns3 wrapper script:
+The cache can also be refreshed with the ``ns3`` wrapper script:
 
 .. sourcecode:: console
 
@@ -330,10 +357,10 @@
 The last way is omitted, since each underlying build system
 has its own unique command-line syntax.
 
-Building the project with ns3
-+++++++++++++++++++++++++++++
+Building the project with n``s3``
++++++++++++++++++++++++++++++++++
 
-The ns3 wrapper script makes life easier for command line users, accepting module names without
+The ``ns3`` wrapper script makes life easier for command line users, accepting module names without
 the ``lib`` prefix and scratch files without the ``scratch_`` prefix. The following command can
 be used to build the entire project:
 
@@ -486,8 +513,8 @@
 If your module depends on external libraries, check the section
 `Linking third-party libraries`_.
 
-Python bindings will be picked up if there is a subdirectory bindings
-and NS3_PYTHON_BINDINGS is enabled.
+Python bindings are generated at runtime for all built modules
+if NS3_PYTHON_BINDINGS is enabled.
 
 Next, we need to port the examples wscript. Repeat the copy, rename and open
 steps. We should have something like the following:
@@ -517,11 +544,54 @@
       ${libinternet-apps}
   )
 
+Migrating definitions, compilation and linking options
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+If your Waf modules had additional definitions, compilation or linking flags,
+you also need to translate them to CMake. The easiest way to accomplish that
+is using the CMake counterparts *BEFORE* defining your target.
+
+If you, for example, had the following:
+
+.. sourcecode:: python
+
+   conf.env.append_value("CXXFLAGS", ["-fopenmp", "-I/usr/local/include/e2sim"])
+   conf.env.append_value("CXXDEFINES", ["LAPACK", "LUSOLVER=LAPACK"])
+   conf.env.append_value("LINKFLAGS", ["-llapack", "-L/usr/local/src/GoToBLAS2", "-lblas", "-Lsrc/common", "-lthyme"])
+   conf.env.append_value("LIB", ["e2sim"])
+
+You would need to replace it with the following counterparts:
+
+.. sourcecode:: cmake
+
+   # The settings below will impact all future target declarations
+   # in the current subdirectory and its subdirectories
+   #
+   # a.k.a. the module, its examples and tests will have the definitions,
+   # compilation options and will be linked to the specified libraries
+   add_compile_options(-fopenmp) # CXXFLAGS counterpart
+   include_directories(/usr/local/include/e2sim) # CXXFLAGS -I counterpart
+   add_definitions(-DLAPACK -DLUSOLVER=LAPACK) # CXXDEFINES counterpart
+   link_directories(/usr/local/src/GoToBLAS2 src/common) # LINKFLAGS -L counterpart
+   link_libraries(lapack blas thyme e2sim) # LINKFLAGS -l or LIB counterpart
+
+   # Target definition after changing settings
+   build_lib_example(
+       NAME hypothetical-module
+       SOURCE_FILES hypothetical-module-source.cc
+       LIBRARIES_TO_LINK
+         # depends on wifi, internet, aodv and internet-apps modules
+         ${libwifi}
+         ${libinternet}
+         ${libaodv}
+         ${libinternet-apps}
+         # and lapack, blas, thyme, e2sim external libraries
+     )
 
 Running programs
 ****************
 
-Running programs with the ns3 wrapper script is pretty simple. To run the
+Running programs with the ``ns3`` wrapper script is pretty simple. To run the
 scratch program produced by ``scratch/scratch-simulator.cc``, you need the following:
 
 .. sourcecode:: console
@@ -531,7 +601,7 @@
 Notice the ``--no-build`` indicates that the program should only be executed, and not built
 before execution.
 
-To familiarize users with CMake, ns3 can also print the underlying CMake
+To familiarize users with CMake, ``ns3`` can also print the underlying CMake
 and command line commands used by adding the ``--dry-run`` flag.
 Removing the ``--no-build`` flag and adding ``--dry-run`` to the same example,
 produces the following:
@@ -558,11 +628,11 @@
 ``PATH`` on Windows). This is not necessary in platforms that support `RPATH`_.
 
 Notice that when the scratch-simulator program is called on the last line, it has
-a ns3-version prefix and could also have a build type suffix.
-This is valid for all libraries and executables, but omitted in ns-3 for simplicity.
+a ``ns3-<version>`` prefix and could also have a build type suffix.
+This is valid for all libraries and executables, but omitted in ``ns3`` for simplicity.
 
 Debugging can be done with GDB. Again, we have the two ways to run the program.
-Using the ns-3 wrapper:
+Using the ``ns3`` wrapper:
 
 .. sourcecode:: console
 
@@ -592,7 +662,7 @@
 * Module name changes
 
 The following sections will detail some of these cases assuming a hypothetical module defined below.
-Notice that the build_lib is the fundamental piece of every ns-3 module, while user-settable
+Notice that the build_lib is the fundamental piece of every |ns3| module, while user-settable
 options and external libraries checking are optional.
 
 .. sourcecode:: cmake
@@ -651,14 +721,14 @@
     )
 
 
-Linking ns-3 modules
-++++++++++++++++++++
+Linking |ns3| modules
++++++++++++++++++++++
 
-Adding a dependency to another ns-3 module just requires adding ``${lib${modulename}}``
-to the ``LIBRARIES_TO_LINK`` list, where modulename contains the value of the ns-3
+Adding a dependency to another |ns3| module just requires adding ``${lib${modulename}}``
+to the ``LIBRARIES_TO_LINK`` list, where modulename contains the value of the |ns3|
 module which will be depended upon.
 
-Note: All ns-3 module libraries are prefixed with ``lib``,
+Note: All |ns3| module libraries are prefixed with ``lib``,
 as CMake requires unique global target names.
 
 .. sourcecode:: cmake
@@ -1150,7 +1220,7 @@
 We assume the old CMake style is the one being used, which means we need to include the include
 directories provided by the ``Find${Package}.cmake module``, usually exported as a variable
 ``${Package}_INCLUDE_DIRS``, and get a list of libraries for that module so that they can be
-added to the list of libraries to link of the ns-3 modules. Libraries are usually exported as
+added to the list of libraries to link of the |ns3| modules. Libraries are usually exported as
 the variable ``${Package}_LIBRARIES``.
 
 As an example for the above, we use the Boost library
@@ -1214,7 +1284,7 @@
 
 
 If ``Find${Package}.cmake`` does not exist in your module path, CMake will warn you that is the case.
-If ``${Package_FOUND}`` is set to False, other variables such as the ones related to libraries and
+If ``${Package_FOUND}`` is set to ``False``, other variables such as the ones related to libraries and
 include directories might not be set, and can result in CMake failures to configure if used.
 
 In case the ``Find${Package}.cmake`` you need is not distributed by the upstream CMake project,
@@ -1238,7 +1308,7 @@
   list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/new-modules")
 
 
-One of the custom Find files currently shipped by ns-3 is the ``FindGTK3.cmake`` file.
+One of the custom Find files currently shipped by |ns3| is the ``FindGTK3.cmake`` file.
 GTK3 requires Harfbuzz, which has its own ``FindHarfBuzz.cmake`` file. Both of them
 are in the ``build-support/3rd-party`` directory.
 
@@ -1535,10 +1605,10 @@
 To set custom compiler defines for that specific executable, defines can be passed
 to the ``DEFINITIONS`` argument.
 
-Add the ``STANDALONE`` option to prevent linking the ns-3 static library
+Add the ``STANDALONE`` option to prevent linking the |ns3| static library
 (``NS3_STATIC``) and single shared library (``NS3_MONOLIB``) to the executable.
 This may be necessary in case the executable redefine symbols which are part
-of the ns-3 library. This is the case for the fd-net-device creators and the tap-creator,
+of the |ns3| library. This is the case for the fd-net-device creators and the tap-creator,
 which include the source file ``encode-decode.cc``, which is also part of fd-net-device module
 and tap-bridge module, respectively.
 
@@ -1709,7 +1779,7 @@
 The first block declares the arguments received by the macro (in CMake, the
 only difference is that a function has its own scope). Notice that there are
 different types of arguments. Options that can only be set to ON/OFF.
-Options are OFF by default, and are set to ON if the option name is added to
+Options are ``OFF`` by default, and are set to ``ON`` if the option name is added to
 the arguments list (e.g. ``build_lib(... IGNORE_PCH)``).
 
 Note: You can find more information about ``IGNORE_PCH`` at the `PCH side-effects`_ section.
@@ -1813,7 +1883,7 @@
   endfunction()
 
 In the next code block, we create an alias to ``libmodule``, ``ns3::libmodule``,
-which can later be used when importing ns-3 with CMake's ``find_package(ns3)``.
+which can later be used when importing |ns3| with CMake's ``find_package(ns3)``.
 
 Then, we associate configured headers (``config-store-config``, ``core-config.h`` and
 ``version-defines.h``) to the core module.
@@ -1850,8 +1920,8 @@
 In the next code block, we make the library a dependency to the ClangAnalyzer's time trace report,
 which measures which step of compilation took most time and which files were responsible for that.
 
-Then, the ns-3 libraries are separated from non-ns-3 libraries, that can be propagated or not
-for libraries/executables linked to the current ns-3 module being processed.
+Then, the |ns3| libraries are separated from non-|ns3| libraries, that can be propagated or not
+for libraries/executables linked to the current |ns3| module being processed.
 
 The default is propagating these third-party libraries and their include directories, but this
 can be turned off by setting ``NS3_REEXPORT_THIRD_PARTY_LIBRARIES=OFF``
@@ -1921,7 +1991,7 @@
 After the lists of libraries to link that should be exported (``PUBLIC``) and
 not exported (``PRIVATE``) are built, we can link them with ``target_link_libraries``.
 
-Next, we set the output name of the module library to n3version-modulename(optional build suffix).
+Next, we set the output name of the module library to ``n3version-modulename`` (+ optional build suffix).
 
 .. sourcecode:: cmake
 
@@ -1939,8 +2009,8 @@
     # ...
   endfunction()
 
-Next we export include directories, to let library consumers importing ns-3 via CMake
-use them just by linking to one of the ns-3 modules.
+Next we export include directories, to let library consumers importing |ns3| via CMake
+use them just by linking to one of the |ns3| modules.
 
 .. sourcecode:: cmake
 
@@ -1960,7 +2030,7 @@
   endfunction()
 
 We append the list of third-party/external libraries for each processed module,
-and append a list of object libraries that can be later used for the static ns-3 build.
+and append a list of object libraries that can be later used for the static |ns3| build.
 
 .. sourcecode:: cmake
 
@@ -1982,7 +2052,7 @@
   endfunction()
 
 The following block creates the ``${BLIB_LIBNAME}-module.h`` header for user scripts,
-and copies header files from src/module and contrib/module to the include/ns3 directory.
+and copies header files from ``src/module`` and ``contrib/module`` to the ``include/ns3`` directory.
 
 .. sourcecode:: cmake
 
@@ -2083,8 +2153,8 @@
     # ...
   endfunction()
 
-In the next code block we add the library to the ns3ExportTargets, later used for installation.
-We also print an additional message the folder just finished being processed if NS3_VERBOSE is set to ON.
+In the next code block we add the library to the ``ns3ExportTargets``, later used for installation.
+We also print an additional message the folder just finished being processed if ``NS3_VERBOSE`` is set to ``ON``.
 
 .. sourcecode:: cmake
 
@@ -2112,7 +2182,7 @@
 The second most important macro from a module author perspective is the ``build_lib_example``, which
 builds the examples for their module. As with ``build_lib`` we explain what it does block-by-block.
 
-In the first block, arguments are parsed and we check wether the current module is in the contrib
+In the first block, arguments are parsed and we check whether the current module is in the contrib
 or the src folder.
 
 .. sourcecode:: cmake
@@ -2130,15 +2200,15 @@
     # ...
   endfunction()
 
-Then we check if the ns-3 modules required by the example are enabled to be built.
+Then we check if the |ns3| modules required by the example are enabled to be built.
 If the list ``missing_dependencies`` is empty, we create the example. Otherwise, we skip it.
 The example can be linked to the current module (``${lib${BLIB_EXAMPLE_LIBNAME}}``) and
 other libraries to link (``${BLIB_EXAMPLE_LIBRARIES_TO_LINK}``) and optionally to the visualizer
 module (``${optional_visualizer_lib}``).
 If the visualizer module is not enabled, ``optional_visualizer_lib`` is empty.
 
-The example can also be linked to a single ns-3 shared library (``lib-ns3-monolib``) or
-a single ns-3 static library (``lib-ns3-static``), if either ``NS3_MONOLIB=ON`` or ``NS3_STATIC=ON``.
+The example can also be linked to a single |ns3| shared library (``lib-ns3-monolib``) or
+a single |ns3| static library (``lib-ns3-static``), if either ``NS3_MONOLIB=ON`` or ``NS3_STATIC=ON``.
 Note that both of these options are handled by the ``build_exec`` macro.
 
 .. sourcecode:: cmake
@@ -2180,7 +2250,7 @@
 The `build_exec`_ macro will also set resulting folder where the example will end up
 after built (e.g. build/src/module/examples). It does that by forwarding the
 ``EXECUTABLE_DIRECTORY_PATH`` to the macro ``set_runtime_outputdirectory``, which also
-adds the proper ns-3 version prefix and build type suffix to the executable.
+adds the proper |ns3| version prefix and build type suffix to the executable.
 
 As with the module libraries, we can also reuse precompiled headers here to speed up
 the parsing step of compilation.
@@ -2513,8 +2583,8 @@
 
 The ``-fno-semantic-interposition`` flag `disables semantic interposition`_, which can
 reduce overhead of function calls in shared libraries built with ``-fPIC``.
-This is the default behaviour for Clang. The inlined ns-3 calls will not be
-correctly interposed by the ``LD_PRELOAD`` trick, which is not know to be used by ns-3 users.
+This is the default behaviour for Clang. The inlined |ns3| calls will not be
+correctly interposed by the ``LD_PRELOAD`` trick, which is not know to be used by |ns3| users.
 To re-enable semantic interposition, comment out the line and reconfigure the project.
 
 Note: the most common use of the ``LD_PRELOAD`` trick is to use custom memory allocators,
@@ -2570,7 +2640,7 @@
 .. _PCHs: https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html
 
 
-There are a few ways of speeding up the build of ns-3 and its modules.
+There are a few ways of speeding up the build of |ns3| and its modules.
 Partially rebuilding only changed modules is one of the ways, and
 this is already handled by the build system.
 
@@ -2770,7 +2840,7 @@
 .. sourcecode:: cpp
 
 
-   #include "cmake_pch.hxx" // PCH includes ns3/log.h before defining NS_LOG_APPEND_CONTEXT below
+   #include "cmake_pch.hxx" // PCH includes ns3/log.h before defining ``NS_LOG_APPEND_CONTEXT`` below
    ...
    #define NS_LOG_APPEND_CONTEXT                                   \
    if (m_ipv4) { std::clog << "[node " << m_ipv4->GetObject<Node> ()->GetId () << "] "; }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/manual/source/working-with-git.rst ns-3.38/doc/manual/source/working-with-git.rst
--- ns-3.37/doc/manual/source/working-with-git.rst	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/manual/source/working-with-git.rst	2023-03-16 20:54:23.536868300 -0700
@@ -10,10 +10,10 @@
 
 .. _Working with git:
 
-Working with git as a user
+Working with Git as a user
 --------------------------
 
-The ns-3 project used Mercurial in the past as its source code control system, but it has moved to Git in December 2018. Git is a VCS like Mercurial, Subversion or CVS, and it is used to maintain many open-source (and closed-source) projects. While git and mercurial have a lot of common properties, if you are new to git you should read first an introduction to it. The most up-to-date guide is the Git Book, at https://git-scm.com/book/en/v2/Getting-Started-Git-Basics.
+The ns-3 project used Mercurial in the past as its source code control system, but it has moved to Git in December 2018. Git is a VCS like Mercurial, Subversion or CVS, and it is used to maintain many open-source (and closed-source) projects. While Git and mercurial have a lot of common properties, if you are new to Git you should read first an introduction to it. The most up-to-date guide is the Git Book, at https://git-scm.com/book/en/v2/Getting-Started-Git-Basics.
 
 The ns-3 project is officially hosted on GitLab.com at https://gitlab.com/nsnam/.  For convenience and historical reasons, ns-3-dev mirrors are currently posted on Bitbucket.com and GitHub.com, and kept in sync with the official repository periodically via cron jobs.  We recommend that users who have been working from one of these mirrors repoint their remotes so that they pull origin or upstream from GitLab.com (see below explanation about how to configure remotes).
 
@@ -22,10 +22,10 @@
 ns-3's Git workflow in a nutshell
 *********************************
 
-Experienced git users will not necessarily need instruction on how to set up personal repositories (below).  However, they should be aware of the project's workflow:
+Experienced Git users will not necessarily need instruction on how to set up personal repositories (below).  However, they should be aware of the project's workflow:
 
 * The main repository's ``master`` branch is the main development branch.  The project maintains only this one branch and strives to maintain a mostly linear history on it.
-* Releases are made by creating a branch from the ``master`` branch and tagging the branch with the release number when ready, and then merging the release branch back to the ``master`` branch.  Releases can be identified by a git tag, and a modified ``VERSION`` file in the branch.  However, the modified ``VERSION`` file is not merged back to ``master``.
+* Releases are made by creating a branch from the ``master`` branch and tagging the branch with the release number when ready, and then merging the release branch back to the ``master`` branch.  Releases can be identified by a Git tag, and a modified ``VERSION`` file in the branch.  However, the modified ``VERSION`` file is not merged back to ``master``.
 
   * If a hotfix release must be made to update a past release, a new hotfix support branch will be created by branching from the tip of the last relevant release.  Changesets from ``master`` branch (such as bug fixes) may be cherry-picked to the hotfix branch.  The hotfix release is tagged with the hotfix version number, and merged back to the ``master`` branch.
 
@@ -64,7 +64,7 @@
 
 Note that you may only do this once; if you try to fork again, Gitlab will take you to the page of the original fork. So, if you are planning to maintain two or more separate forks (for example, one for your private work, another for maintenance, etc.), you are doing a mistake. Instead, you should add these forks as a remote of your existing directory (see below for adding remotes). Usually, it is a good thing to add the maintainer's repository as remotes, because it can happen that "bleeding edge" features will appear there before landing in ns-3-dev.
 
-For more information on forking with Gilab, there is plenty of visual documentation (https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html). To work with your forked repository, you have two ways: one is a clean clone while the other is meant to re-use an existing ns-3 git repository.
+For more information on forking with Gilab, there is plenty of visual documentation (https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html). To work with your forked repository, you have two ways: one is a clean clone while the other is meant to re-use an existing ns-3 Git repository.
 
 Clone your forked repository on your machine
 ============================================
@@ -81,16 +81,16 @@
 Naming conventions
 ==================
 
-Git is able to fetch and push changes to several repositories, each of them is called ``remote``. With time, you probably will have many remotes, each one with many branches. To avoid confusion, it is recommended to give meaningful names to the remotes; in the following, we will use ``origin`` to indicate the ns-3-dev repository in your personal namespace (your forked version, server-side) and ``nsnam`` to indicate the ns-3-dev repository in the nsnam namespace, server-side.
+Git is able to fetch and push changes to several repositories, each of them is called ``remote``. With time, you probably will have many remotes, each one with many branches. To avoid confusion, it is recommended to give meaningful names to the remotes. Following the Git terminology, we will use ``origin`` to indicate the ns-3-dev repository in your personal namespace (your forked version, server-side) and ``upstream`` to indicate the ns-3-dev repository in the nsnam namespace, server-side.
 
 Add the official ns-3 repository as remote upstream
 ***************************************************
 
-You could have already used git in the past, and therefore already having a ns-3 git repository somewhere. Or, instead, you could have it cloned for the first time in the step above. In both cases, when you fork/clone a repository, your history is no more bound to the repository itself. At this point, it is your duty to sync your fork with the original repository. The first remote repository we have encountered is ``origin``; we must add the official ns-3 repo as another remote repository::
+You could have already used Git in the past, and therefore already having a ns-3 Git repository somewhere. Or, instead, you could have it cloned for the first time in the step above. In both cases, when you fork/clone a repository, your history is no more bound to the repository itself. At this point, it is your duty to sync your fork with the original repository. The first remote repository we have encountered is ``origin``; we must add the official ns-3 repo as another remote repository::
 
-   $ git remote add nsnam https://gitlab.com/nsnam/ns-3-dev
+   $ git remote add upstream https://gitlab.com/nsnam/ns-3-dev
 
-With the command above, we added a remote repository, named nsnam, which links to the official ns-3 repo. To show your remote repositories::
+With the command above, we added a remote repository, named upstream, which links to the official ns-3 repo. To show your remote repositories::
 
    $ git remote show
 
@@ -98,12 +98,12 @@
 
    $ git remote show origin
 
-Many options are available; please refer to the git manual for more.
+Many options are available; please refer to the Git manual for more.
 
 Add your forked repository as remote
 ************************************
 
-If you were a user of the old github mirror, you probably have an existing git repository installed somewhere. In your case, it is not necessary to clone your fork and to port all your work in the new directory; you can add the fork as new remote::
+If you were a user of the old github mirror, you probably have an existing Git repository installed somewhere. In your case, it is not necessary to clone your fork and to port all your work in the new directory; you can add the fork as new remote::
 
    $ git remote rename origin old-origin
    $ git remote add origin https://gitlab.com/your-user-name/ns-3-dev
@@ -117,10 +117,10 @@
 We assume, from now to the end of this document, that you will not make commits on top of the master branch. It should be kept clean from *any* personal modifications: all the works must be done in branches. Therefore, to move the current HEAD of the master branch to the latest commit in ns-3-dev, you should do::
 
    $ git checkout master
-   $ git fetch nsnam
-   $ git pull nsnam master
+   $ git fetch upstream
+   $ git pull upstream master
 
-If you tried a pull which resulted in a conflict and you would like to start over, you can recover with git reset (but this never happens if you do not commit over master).
+If you tried a pull which resulted in a conflict and you would like to start over, you can recover with Git reset (but this never happens if you do not commit over master).
 
 Start a new branch to do some work
 **********************************
@@ -133,7 +133,7 @@
 
    * master
      remotes/origin/master
-     remotes/nsnam/master
+     remotes/upstream/master
 
 The branch master is your local master branch; remotes/origin/master point at the master branch on your repository located in the Gitlab server, while remotes/nsnam/master points to the official master branch.
 
@@ -150,14 +150,14 @@
     * master
      [name_of_your_new_branch]
      remotes/origin/master
-     remotes/nsnam/master
+     remotes/upstream/master
 
 Edit and commit the modifications
 *********************************
 
-After you edit some file, you should commit the difference. As a policy, git users love small and incremental patches. So, commit early, and commit often: you could rewrite your history later.
+After you edit some file, you should commit the difference. As a policy, Git users love small and incremental patches. So, commit early, and commit often: you could rewrite your history later.
 
-Suppose we edited ``src/internet/model/tcp-socket-base.cc``. With git status, we can see the repository status::
+Suppose we edited ``src/internet/model/tcp-socket-base.cc``. With Git status, we can see the repository status::
 
    $ git status
       On branch tcp-next
@@ -220,7 +220,7 @@
 Rebase your branch on top of master
 ***********************************
 
-Meanwhile you were busy with your branch, the upstream master could have changed. To rebase your work with the now new master, first of all sync your master branch (pulling the nsnam/master branch into your local master branch) as explained before; then
+Meanwhile you were busy with your branch, the upstream master could have changed. To rebase your work with the now new master, first of all sync your master branch (pulling the upstream/master branch into your local master branch) as explained before; then
 
 ::
 
@@ -241,7 +241,7 @@
 
 The ``git push`` command can be used every time you need to push something from your computer to a remote repository, except when you propose changes to the main ns-3-dev repository: your changes must pass a review stage.
 
-Please note that for older git version, the push command looks like::
+Please note that for older Git version, the push command looks like::
 
     $ git push -u origin [name_of_your_new_branch]
 
@@ -252,12 +252,15 @@
 After you push your branch to origin, you can follow the instructions here https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html
 to create a merge request.
 
-GitLab CI (Continous Integration)
+It is strongly suggested to rebase your branch on top of upstream/master (or master, if you kept it synced) before submitting your work.
+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 (Continous Integration) feature. Shortly put, after every push the code is built and tests are run in one of the GitLab servers.
+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.
 
-Merge requests are expected to pass the CI, as is to not generate errors or warings during compilation, to have all the tests pasing, and to not generate warnings on the documentation.
+Merge requests are expected to pass the CI, as is to not generate errors or warnings during compilation, to have all the tests passing, and to not generate warnings on the documentation.
 Hence, the CI is very important for the workflow. However, sometimes running the Ci is superfluous, for example:
 
 - You are in the middle of some work (and perhaps you know that there are errors),
@@ -270,13 +273,19 @@
 
     $ git push -o ci.skip
 
+GitLab CI optimization
+++++++++++++++++++++++
+
+The GitLab Ci jobs are optimized to take advantage of caches (this is done automatically).
 
-Porting patches from mercurial repositories to git
+In order to take full advantage of the caches, it is suggested to rebase your branches on top of upstream/master (or your own 'master' branch if you keep it synced with the latest commits from upstream/master).
+
+Porting patches from mercurial repositories to Git
 **************************************************
 
 *Placeholder section; please improve it.*
 
-Working with git as a maintainer
+Working with Git as a maintainer
 --------------------------------
 
 As a maintainer, you are a person who has write access to the main nsnam repository. You could push your own work (without passing from code review) or push someone else's work. Let's investigate the two cases.
@@ -287,11 +296,11 @@
 Since you have been added to the Developer list on Gitlab (if not, please open an issue) you can use the git + ssh address when adding nsnam as remote. Once you have done that, you can do your modifications to a local branch, then update the master to point to the latest changes of the nsnam repo, and then::
 
     $ git checkout master
-    $ git pull nsnam master
+    $ git pull upstream master
     $ git merge [your_branch_name]
-    $ git push nsnam master
+    $ git push upstream master
 
-Please note that if you want to keep track of your branch, you can use as command ``git merge --no-ff [your_branch_name]``. It is always recommended to rebase your branch before merging, to have a clean history. That is not a requirement, though: git perfectly handles a master with parallel merged branches.
+Please note that if you want to keep track of your branch, you can use as command ``git merge --no-ff [your_branch_name]``. It is always recommended to rebase your branch before merging, to have a clean history. That is not a requirement, though: Git perfectly handles a master with parallel merged branches.
 
 Review and merge someone else's work
 ************************************
@@ -306,11 +315,11 @@
 linear history on a single ``master`` branch.  Releases are branches from
 this ``master`` branch but the branches themselves are not long-lived;
 the release branches are merged back to ``master`` in a special way.  However,
-the release branches can be checked out by using the git tag facility;
+the release branches can be checked out by using the Git tag facility;
 a named release such as 'ns-3.30' can be checked out on a branch by specifying
 the release name 'ns-3.30' (or 'ns-3.30.1' etc.).
 
-A compact way to represent a git history is the following command:
+A compact way to represent a Git history is the following command:
 
 ::
 
@@ -359,17 +368,19 @@
   $ cat VERSION
   3.34
 
-We next change the file conf.py in the tutorial, manual, and models directories
+We next change the file conf.py in the contributing, tutorial, manual, and models directories
 to change the strings 'ns-3-dev' to ns-3.34.
 
-When you are done, the 'git status' command should show:
+When you are done, the 'git diff --stat' command should show:
 
 ::
 
-  VERSION                     | 2 +-
-  doc/manual/source/conf.py   | 4 ++--
-  doc/models/source/conf.py   | 4 ++--
-  doc/tutorial/source/conf.py | 4 ++--
+  VERSION                         | 2 +-
+  doc/contributing/source/conf.py | 4 ++--
+  doc/manual/source/conf.py       | 4 ++--
+  doc/models/source/conf.py       | 4 ++--
+  doc/tutorial/source/conf.py     | 4 ++--
+  5 files changed, 9 insertions(+), 9 deletions(-)
 
 Make a commit of these files:
 
@@ -377,19 +388,25 @@
 
   $ git commit -a -m"Update VERSION and documentation tags for ns-3.34 release"
 
-Next, make the following change to RELEASE_NOTES and commit it:
+Next, make the following change to RELEASE_NOTES.md and commit it:
 
 ::
 
-   Availability
-   ------------
-  -This release is not yet available.
+  -Release 3-dev
+  --------------
+  +Release 3.34
+  +------------
+  +
+  +### Availability
+  +
   +This release is available from:
-  +https://www.nsnam.org/release/ns-allinone-3.34.tar.bz2
+  +<https://www.nsnam.org/release/ns-allinone-3.34.tar.bz2>
+
+Commit this change:
 
-  $ git commit -m"Update availability in RELEASE_NOTES" RELEASE_NOTES
+  $ git commit -m"Update availability in RELEASE_NOTES.md" RELEASE_NOTES.md
 
-Finally, add a git annotated tag:
+Finally, add a Git annotated tag:
 
 ::
 
@@ -427,7 +444,7 @@
   $ git commit -m"Merge ns-3.34-release branch"
   $ git branch -d ns-3.34-release
 
-The git history now looks like this::
+The Git history now looks like this::
 
   $ git log --graph --decorate --oneline --all
   *   fd075f6 (HEAD -> master) Merge ns-3.34-release branch
@@ -541,7 +558,7 @@
   $ git commit
   $ git branch -d ns-3.34.1-release
 
-The new log should show something like the below, with parallel git
+The new log should show something like the below, with parallel Git
 history paths until the merge back again::
 
   $ git log --graph --decorate --oneline --all
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/models/Makefile ns-3.38/doc/models/Makefile
--- ns-3.37/doc/models/Makefile	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/models/Makefile	2023-03-16 20:54:23.536868300 -0700
@@ -343,6 +343,9 @@
 	$(SRC)/spectrum/doc/spectrum-tv-rand-geo-points.eps \
 	$(SRC)/spectrum/doc/spectrum-tv-8vsb.png \
 	$(SRC)/spectrum/doc/spectrum-tv-cofdm.png \
+	$(SRC)/spectrum/doc/two-ray-spectrum-loss-model-3gpp-radiation-pattern.png \
+	$(SRC)/spectrum/doc/two-ray-spectrum-loss-model-iso-radiation-pattern.png \
+	$(SRC)/spectrum/doc/three-gpp-gain-reference-gain-vs-fc.png \
 	$(SRC)/traffic-control/doc/classful-queue-disc.dia \
 	$(SRC)/traffic-control/doc/multi-queue-aware-queue-disc.dia \
 	$(SRC)/traffic-control/doc/figures/collision_prob.jpeg \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/models/source/conf.py ns-3.38/doc/models/source/conf.py
--- ns-3.37/doc/models/source/conf.py	2022-11-01 17:39:10.947703100 -0700
+++ ns-3.38/doc/models/source/conf.py	2023-03-16 20:54:51.628957000 -0700
@@ -73,9 +73,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.37'
+version = u'ns-3.38'
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.37'
+release = u'ns-3.38'
 
 # 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.37/doc/models/source/index.rst ns-3.38/doc/models/source/index.rst
--- ns-3.37/doc/models/source/index.rst	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/models/source/index.rst	2023-03-16 20:54:23.536868300 -0700
@@ -3,15 +3,25 @@
 ns-3 Model Library
 ==================
 
-This is the *ns-3 Model Library* documentation. Primary documentation for the ns-3 project is
-available in five forms:
+This is the *ns-3 Model Library* documentation. Primary documentation for the ns-3 project is organized as
+follows:
 
-* `ns-3 Doxygen <https://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
-* Tutorial, Manual, and Model Library *(this document)* for the `latest release <https://www.nsnam.org/documentation/latest/>`_ and `development tree <https://www.nsnam.org/documentation/development-tree/>`_
-* `ns-3 wiki <https://www.nsnam.org/wiki>`_
+* Several guides that are version controlled for each release (the
+  `latest release <https://www.nsnam.org/documentation/latest/>`_) and
+  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+
+  * Tutorial
+  * Installation Guide
+  * Manual
+  * Model Library *(this document)*
+  * Contributing Guide
+* `ns-3 Doxygen <https://www.nsnam.org/docs/doxygen/index.html>`_: Documentation of the public APIs of
+  the simulator
+* `ns-3 wiki <https://www.nsnam.org/wiki/Main_Page>`_
 
 This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <https://www.sphinx-doc.org/>`_ and is maintained in the
-``doc/models`` directory of ns-3's source code.
+``doc/models`` directory of ns-3's source code (and much of the source content is also pulled
+from the ``doc/`` directory of each module.  Source file column width is 100 columns.
 
 .. toctree::
    :maxdepth: 1
@@ -32,6 +42,7 @@
    energy
    flow-monitor
    internet-models
+   internet-apps
    lr-wpan
    lte
    mesh
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/models/source/internet-models.rst ns-3.38/doc/models/source/internet-models.rst
--- ns-3.37/doc/models/source/internet-models.rst	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/models/source/internet-models.rst	2023-03-16 20:54:23.536868300 -0700
@@ -1,5 +1,5 @@
-Internet Models (IP, TCP, Routing, UDP, Internet Applications)
----------------------------------------------------------------------
+Internet Models (IP, TCP, Routing, UDP)
+---------------------------------------
 
 .. toctree::
 
@@ -9,4 +9,3 @@
    routing-overview
    tcp
    udp
-   internet-apps
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/modules ns-3.38/doc/modules
--- ns-3.37/doc/modules	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/modules	2023-03-16 20:54:23.536868300 -0700
@@ -1,17 +1,14 @@
 /**
  * @file
  * @ingroup utils
- * @brief Definition of support modules Constants, Utils, Example programs and
+ * @brief Definition of support modules Utils, Example programs and
  * Test suites.
  */
 /**
  * @anchor modules_anchor
  *
- * @defgroup constants Constants
- * @brief Constants you can change
- *
  * @defgroup utils Utils
- * @brief The utils directory is for various programs and scripts related 
+ * @brief The utils directory is for various programs and scripts related
  * to code coverage, test suites, style checking, and benchmarking.
  *
  * @defgroup examples Example programs
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/ns3_html_theme/ns3_doxy_header.html ns-3.38/doc/ns3_html_theme/ns3_doxy_header.html
--- ns-3.37/doc/ns3_html_theme/ns3_doxy_header.html	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/ns3_html_theme/ns3_doxy_header.html	2023-03-16 20:54:23.536868300 -0700
@@ -33,7 +33,7 @@
           <tr style="height: 56px;">
             <td id="projectlogo">
               <a id="ns3_home1"
-		 href="http://www.nsnam.org/">
+		 href="https://www.nsnam.org/">
 		<img alt="ns-3 Logo"
                      src="$relpath$$projectlogo"/>
               </a>
@@ -48,7 +48,7 @@
                 <ul >
                   <li style="background-image:none">
                     <a id="ns3_home2"
-                         href="http://www.nsnam.org/"
+                         href="https://www.nsnam.org/"
                          >&nbsp;&nbsp;Home</a>
                   </li>
                   <li><span
@@ -71,6 +71,9 @@
                           onmouseover="mcancelclosetime()"
                           onmouseout="mclosetime()">
                         <a id="ns3_man"
+                           href="/docs/installation/html/index.html"
+                           >Installation</a><br/>
+                        <a id="ns3_man"
                            href="/docs/manual/html/index.html"
                            >Manual</a><br/>
                         <a id="ns3_mod"
@@ -80,7 +83,7 @@
                            href="/docs/contributing/html/index.html"
                            >Contributing</a><br/>
                         <a id="ns3_wiki"
-                           href="http://www.nsnam.org/wiki"
+                           href="https://www.nsnam.org/wiki"
                            >Wiki</a><br/>
                       </div>
                   </li>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/ns3_html_theme/static/ns3_stylesheet.css ns-3.38/doc/ns3_html_theme/static/ns3_stylesheet.css
--- ns-3.37/doc/ns3_html_theme/static/ns3_stylesheet.css	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/ns3_html_theme/static/ns3_stylesheet.css	2023-03-16 20:54:23.536868300 -0700
@@ -4,12 +4,23 @@
 table,
 div,
 p,
-dl    {
+dl {
   color: black;
   font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
   font-size: 12px;
 }
 
+/* Dark mode is not supported in Doxygen versions earlier than 1.9.6
+@media (prefers-color-scheme: dark) {
+  body,
+  table,
+  div,
+  p,
+  dl    {
+    color: ivory;
+  }
+} */
+
 a {
   color: #91A501;
   font-weight: bold;
@@ -41,7 +52,7 @@
 
 /* Sphinx nav links bar (relbar) */
 div.related {
-  background-image:url('tab_b.png')
+  background-image: url('tab_b.png')
 }
 
 div.related h3 {
@@ -81,13 +92,13 @@
 /* Title bar elements */
 
 #titlearea {
-  background-image:url('bar-top.png');
-  background-repeat:repeat;
+  background-image: url('bar-top.png');
+  background-repeat: repeat;
   border-bottom: 1px solid #5B5B5B;
   color: white;
   position: relative;
   /* Doxygen tab bar ("Main Page", "Related Pages"..) is at z: 9999 */
-  z-index:10000;
+  z-index: 10000;
 }
 
 #projectlogo {
@@ -100,28 +111,28 @@
 
 #projecttext {
   align: left;
-  font-color:white;
+  font-color: white;
   padding-left: 2em;
   width: 250px;
 }
 
 #projectbrief {
   color: white;
-  font: 120% Tahoma, Arial,sans-serif;
+  font: 120% Tahoma, Arial, sans-serif;
   margin: 0px;
   padding: 0px;
 }
 
 #projectnumber {
   color: white;
-  font: 100% Tahoma, Arial,sans-serif;
+  font: 100% Tahoma, Arial, sans-serif;
   margin: 0px;
   padding: 0px;
 }
 
 #projectsection {
   color: white;
-  font: 24pt  Aldo, Tahoma, Arial,sans-serif;
+  font: 24pt Aldo, Tahoma, Arial, sans-serif;
   margin-right: 10px;
   margin: 10px;
   text-align: right;
@@ -137,26 +148,26 @@
 
 #ns3-menu .menu {
   background-image: url('menu-bgr-400.png');
-/*   background-origin: padding-box; */
-/*   background-position: -10px 0; */
+  /*   background-origin: padding-box; */
+  /*   background-position: -10px 0; */
   background-repeat: no-repeat;
   background-size: 100% 39px;
   display: table-cell;
-  float:left;
+  float: left;
   height: 40px;
   margin: 0px 0px 0px 0px;
-  padding:0px 0px 0px 0px;
+  padding: 0px 0px 0px 0px;
   vertical-align: middle;
 }
 
 
 #ns3-menu .menu ul {
-/*   float:left; */
+  /*   float:left; */
   height: 40;
-  list-style:none;
-  margin:0px 0px 0px -2px;
-/*   overflow:hidden; */
-  padding:0px 0px 0px 0px;
+  list-style: none;
+  margin: 0px 0px 0px -2px;
+  /*   overflow:hidden; */
+  padding: 0px 0px 0px 0px;
   z-index: 30;
 }
 
@@ -165,35 +176,38 @@
   background-origin: padding-box;
   background-position: 0 11px;
   background-repeat: no-repeat;
-  color:#ffffff;
-  float:left;
+  color: #ffffff;
+  float: left;
   font-family: Aldo, Tahoma, Arial, sans-serif;
   font-size: 14px;
-/*   height:40px; */
-  margin:0px 0px 0px 0px;
-/*   overflow:hidden; */
-  padding:11px 13px 0px 12px;
-  text-transform:uppercase;
+  /*   height:40px; */
+  margin: 0px 0px 0px 0px;
+  /*   overflow:hidden; */
+  padding: 11px 13px 0px 12px;
+  text-transform: uppercase;
 }
 
 #ns3-menu .menu ul li a {
-  color:#ffffff;
+  color: #ffffff;
   /* cursor: pointer; */
-  display:block;
-  float:left;
-  font-weight: normal;  /* default anchors are bold */
-  text-decoration:none; /* default anchors are underlined */
+  display: block;
+  float: left;
+  font-weight: normal;
+  /* default anchors are bold */
+  text-decoration: none;
+  /* default anchors are underlined */
 }
 
 #ns3-menu .menu ul li a:hover {
-  color:#cadc48;
-  text-decoration:none; /* don't add underline on hover */
+  color: #cadc48;
+  text-decoration: none;
+  /* don't add underline on hover */
 }
 
 #ns3-menu .menu div {
   background: #94A901;
   background-size: 100%;
-  color:#ffffff;
+  color: #ffffff;
   position: absolute;
   visibility: hidden;
 }
@@ -212,6 +226,7 @@
 }
 
 #ns3-menu .menu div a:hover {
-  color:#cadc48;
-  text-decoration:none; /* don't add underline on hover */
+  color: #cadc48;
+  text-decoration: none;
+  /* don't add underline on hover */
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/release_steps.txt ns-3.38/doc/release_steps.txt
--- ns-3.37/doc/release_steps.txt	2022-11-01 17:37:24.411394800 -0700
+++ ns-3.38/doc/release_steps.txt	2023-03-16 20:54:23.536868300 -0700
@@ -124,16 +124,16 @@
 Make sure that the version.cache file included in the source archive looks
 something like the below example:
 
-CLOSEST_TAG = '"ns-3.36.1"'
-VERSION_COMMIT_HASH = '"g7004d1a66"'
+CLOSEST_TAG = '"ns-3.37"'
+VERSION_COMMIT_HASH = '"g4407a9528"'
 VERSION_DIRTY_FLAG = '0'
 VERSION_MAJOR = '3'
-VERSION_MINOR = '36'
-VERSION_PATCH = '1'
+VERSION_MINOR = '37'
+VERSION_PATCH = '0'
 VERSION_RELEASE_CANDIDATE = '""'
-VERSION_TAG = '"ns-3.36.1"'
+VERSION_TAG = '"ns-3.37"'
 VERSION_TAG_DISTANCE = '0'
-VERSION_BUILD_PROFILE = 'relwithdebinfo
+VERSION_BUILD_PROFILE = 'default'
 
 4.  Test this tarball on at least one system
    - check that ns-3-allinone build.py works
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/building-topologies.rst ns-3.38/doc/tutorial/source/building-topologies.rst
--- ns-3.37/doc/tutorial/source/building-topologies.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/building-topologies.rst	2023-03-16 20:54:23.564868500 -0700
@@ -76,7 +76,7 @@
 
   using namespace ns3;
 
-  NS_LOG_COMPONENT_DEFINE ("SecondScriptExample");
+  NS_LOG_COMPONENT_DEFINE("SecondScriptExample");
 
 The main program begins with a slightly different twist.  We use a verbose
 flag to determine whether or not the ``UdpEchoClientApplication`` and
@@ -99,10 +99,10 @@
   uint32_t nCsma = 3;
 
   CommandLine cmd;
-  cmd.AddValue ("nCsma", "Number of \"extra\" CSMA nodes/devices", nCsma);
-  cmd.AddValue ("verbose", "Tell echo applications to log if true", verbose);
+  cmd.AddValue("nCsma", "Number of \"extra\" CSMA nodes/devices", nCsma);
+  cmd.AddValue("verbose", "Tell echo applications to log if true", verbose);
 
-  cmd.Parse (argc, argv);
+  cmd.Parse(argc, argv);
 
   if (verbose)
     {
@@ -119,7 +119,7 @@
 ::
 
   NodeContainer p2pNodes;
-  p2pNodes.Create (2);
+  p2pNodes.Create(2);
 
 Next, we declare another ``NodeContainer`` to hold the nodes that will be
 part of the bus (CSMA) network.  First, we just instantiate the container
@@ -128,8 +128,8 @@
 ::
 
   NodeContainer csmaNodes;
-  csmaNodes.Add (p2pNodes.Get (1));
-  csmaNodes.Create (nCsma);
+  csmaNodes.Add(p2pNodes.Get(1));
+  csmaNodes.Create(nCsma);
 
 The next line of code ``Gets`` the first node (as in having an index of one)
 from the point-to-point node container and adds it to the container of nodes
@@ -148,11 +148,11 @@
 ::
 
   PointToPointHelper pointToPoint;
-  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-  pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+  pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+  pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
 
   NetDeviceContainer p2pDevices;
-  p2pDevices = pointToPoint.Install (p2pNodes);
+  p2pDevices = pointToPoint.Install(p2pNodes);
 
 We then instantiate a ``NetDeviceContainer`` to keep track of the
 point-to-point net devices and we ``Install`` devices on the
@@ -173,11 +173,11 @@
 ::
 
   CsmaHelper csma;
-  csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps"));
-  csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560)));
+  csma.SetChannelAttribute("DataRate", StringValue("100Mbps"));
+  csma.SetChannelAttribute("Delay", TimeValue(NanoSeconds(6560)));
 
   NetDeviceContainer csmaDevices;
-  csmaDevices = csma.Install (csmaNodes);
+  csmaDevices = csma.Install(csmaNodes);
 
 Just as we created a ``NetDeviceContainer`` to hold the devices created by
 the ``PointToPointHelper`` we create a ``NetDeviceContainer`` to hold
@@ -192,8 +192,8 @@
 ::
 
   InternetStackHelper stack;
-  stack.Install (p2pNodes.Get (0));
-  stack.Install (csmaNodes);
+  stack.Install(p2pNodes.Get(0));
+  stack.Install(csmaNodes);
 
 Recall that we took one of the nodes from the ``p2pNodes`` container and
 added it to the ``csmaNodes`` container.  Thus we only need to install
@@ -208,9 +208,9 @@
 ::
 
   Ipv4AddressHelper address;
-  address.SetBase ("10.1.1.0", "255.255.255.0");
+  address.SetBase("10.1.1.0", "255.255.255.0");
   Ipv4InterfaceContainer p2pInterfaces;
-  p2pInterfaces = address.Assign (p2pDevices);
+  p2pInterfaces = address.Assign(p2pDevices);
 
 Recall that we save the created interfaces in a container to make it easy to
 pull out addressing information later for use in setting up the applications.
@@ -224,9 +224,9 @@
 
 ::
 
-  address.SetBase ("10.1.2.0", "255.255.255.0");
+  address.SetBase("10.1.2.0", "255.255.255.0");
   Ipv4InterfaceContainer csmaInterfaces;
-  csmaInterfaces = address.Assign (csmaDevices);
+  csmaInterfaces = address.Assign(csmaDevices);
 
 Now we have a topology built, but we need applications.  This section is
 going to be fundamentally similar to the applications section of
@@ -242,11 +242,11 @@
 
 ::
 
-  UdpEchoServerHelper echoServer (9);
+  UdpEchoServerHelper echoServer(9);
 
-  ApplicationContainer serverApps = echoServer.Install (csmaNodes.Get (nCsma));
-  serverApps.Start (Seconds (1.0));
-  serverApps.Stop (Seconds (10.0));
+  ApplicationContainer serverApps = echoServer.Install(csmaNodes.Get(nCsma));
+  serverApps.Start(Seconds(1.0));
+  serverApps.Stop(Seconds(10.0));
 
 Recall that the ``csmaNodes NodeContainer`` contains one of the
 nodes created for the point-to-point network and ``nCsma`` "extra" nodes.
@@ -267,14 +267,14 @@
 
 ::
 
-  UdpEchoClientHelper echoClient (csmaInterfaces.GetAddress (nCsma), 9);
-  echoClient.SetAttribute ("MaxPackets", UintegerValue (1));
-  echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
-  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));
-
-  ApplicationContainer clientApps = echoClient.Install (p2pNodes.Get (0));
-  clientApps.Start (Seconds (2.0));
-  clientApps.Stop (Seconds (10.0));
+  UdpEchoClientHelper echoClient(csmaInterfaces.GetAddress(nCsma), 9);
+  echoClient.SetAttribute("MaxPackets", UintegerValue(1));
+  echoClient.SetAttribute("Interval", TimeValue(Seconds(1.0)));
+  echoClient.SetAttribute("PacketSize", UintegerValue(1024));
+
+  ApplicationContainer clientApps = echoClient.Install(p2pNodes.Get(0));
+  clientApps.Start(Seconds(2.0));
+  clientApps.Stop(Seconds(10.0));
 
 Since we have actually built an internetwork here, we need some form of
 internetwork routing.  |ns3| provides what we call global routing to
@@ -292,7 +292,7 @@
 
 ::
 
-  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
+  Ipv4GlobalRoutingHelper::PopulateRoutingTables();
 
 Next we enable pcap tracing.  The first line of code to enable pcap tracing
 in the point-to-point helper should be familiar to you by now.  The second
@@ -301,8 +301,8 @@
 
 ::
 
-  pointToPoint.EnablePcapAll ("second");
-  csma.EnablePcap ("second", csmaDevices.Get (1), true);
+  pointToPoint.EnablePcapAll("second");
+  csma.EnablePcap("second", csmaDevices.Get(1), true);
 
 The CSMA network is a multi-point-to-point network.  This means that there
 can (and are in this case) multiple endpoints on a shared medium.  Each of
@@ -329,8 +329,8 @@
 
 ::
 
-    Simulator::Run ();
-    Simulator::Destroy ();
+    Simulator::Run();
+    Simulator::Destroy();
     return 0;
   }
 
@@ -564,9 +564,9 @@
 
 ::
 
-  pointToPoint.EnablePcap ("second", p2pNodes.Get (0)->GetId (), 0);
-  csma.EnablePcap ("second", csmaNodes.Get (nCsma)->GetId (), 0, false);
-  csma.EnablePcap ("second", csmaNodes.Get (nCsma-1)->GetId (), 0, false);
+  pointToPoint.EnablePcap("second", p2pNodes.Get(0)->GetId(), 0);
+  csma.EnablePcap("second", csmaNodes.Get(nCsma)->GetId(), 0, false);
+  csma.EnablePcap("second", csmaNodes.Get(nCsma-1)->GetId(), 0, false);
 
 We know that we want to create a pcap file with the base name "second" and
 we also know that the device of interest in both cases is going to be zero,
@@ -610,14 +610,14 @@
 
 .. sourcecode:: bash
 
-   csma.EnablePcap ("second", csmaDevices.Get (1), true);
+   csma.EnablePcap("second", csmaDevices.Get(1), true);
 
 Change the index to the quantity ``nCsma``, corresponding to the last
 node in the topology-- the node that contains the echo server:
 
 .. sourcecode:: bash
 
-   csma.EnablePcap ("second", csmaDevices.Get (nCsma), true);
+   csma.EnablePcap("second", csmaDevices.Get(nCsma), true);
 
 If you build the new script and run the simulation setting ``nCsma`` to 100,
 
@@ -657,7 +657,7 @@
 
 .. sourcecode:: bash
 
-   csma.EnablePcap ("second", csmaDevices.Get (nCsma - 1), false);
+   csma.EnablePcap("second", csmaDevices.Get(nCsma - 1), false);
 
 Now build and run as before:
 
@@ -872,7 +872,7 @@
 
   using namespace ns3;
 
-  NS_LOG_COMPONENT_DEFINE ("ThirdScriptExample");
+  NS_LOG_COMPONENT_DEFINE("ThirdScriptExample");
 
 The main program begins just like ``second.cc`` by adding some command line
 parameters for enabling or disabling logging components and for changing the
@@ -885,11 +885,11 @@
   uint32_t nWifi = 3;
 
   CommandLine cmd;
-  cmd.AddValue ("nCsma", "Number of \"extra\" CSMA nodes/devices", nCsma);
-  cmd.AddValue ("nWifi", "Number of wifi STA devices", nWifi);
-  cmd.AddValue ("verbose", "Tell echo applications to log if true", verbose);
+  cmd.AddValue("nCsma", "Number of \"extra\" CSMA nodes/devices", nCsma);
+  cmd.AddValue("nWifi", "Number of wifi STA devices", nWifi);
+  cmd.AddValue("verbose", "Tell echo applications to log if true", verbose);
 
-  cmd.Parse (argc,argv);
+  cmd.Parse(argc,argv);
 
   if (verbose)
     {
@@ -903,7 +903,7 @@
 ::
 
   NodeContainer p2pNodes;
-  p2pNodes.Create (2);
+  p2pNodes.Create(2);
 
 Next, we see an old friend.  We instantiate a ``PointToPointHelper`` and
 set the associated default ``Attributes`` so that we create a five megabit
@@ -914,11 +914,11 @@
 ::
 
   PointToPointHelper pointToPoint;
-  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-  pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+  pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+  pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
 
   NetDeviceContainer p2pDevices;
-  p2pDevices = pointToPoint.Install (p2pNodes);
+  p2pDevices = pointToPoint.Install(p2pNodes);
 
 Next, we declare another ``NodeContainer`` to hold the nodes that will be
 part of the bus (CSMA) network.
@@ -926,8 +926,8 @@
 ::
 
   NodeContainer csmaNodes;
-  csmaNodes.Add (p2pNodes.Get (1));
-  csmaNodes.Create (nCsma);
+  csmaNodes.Add(p2pNodes.Get(1));
+  csmaNodes.Create(nCsma);
 
 The next line of code ``Gets`` the first node (as in having an index of one)
 from the point-to-point node container and adds it to the container of nodes
@@ -943,11 +943,11 @@
 ::
 
   CsmaHelper csma;
-  csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps"));
-  csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560)));
+  csma.SetChannelAttribute("DataRate", StringValue("100Mbps"));
+  csma.SetChannelAttribute("Delay", TimeValue(NanoSeconds(6560)));
 
   NetDeviceContainer csmaDevices;
-  csmaDevices = csma.Install (csmaNodes);
+  csmaDevices = csma.Install(csmaNodes);
 
 Next, we are going to create the nodes that will be part of the Wi-Fi network.
 We are going to create a number of "station" nodes as specified by the
@@ -957,8 +957,8 @@
 ::
 
   NodeContainer wifiStaNodes;
-  wifiStaNodes.Create (nWifi);
-  NodeContainer wifiApNode = p2pNodes.Get (0);
+  wifiStaNodes.Create(nWifi);
+  NodeContainer wifiApNode = p2pNodes.Get(0);
 
 The next bit of code constructs the wifi devices and the interconnection
 channel between these wifi nodes. First, we configure the PHY and channel
@@ -966,11 +966,11 @@
 
 ::
 
-  YansWifiChannelHelper channel = YansWifiChannelHelper::Default ();
-  YansWifiPhyHelper phy = YansWifiPhyHelper::Default ();
+  YansWifiChannelHelper channel = YansWifiChannelHelper::Default();
+  YansWifiPhyHelper phy = YansWifiPhyHelper::Default();
 
 For simplicity, this code uses the default PHY layer configuration and
-channel models which are documented in the API doxygen documentation for
+channel models which are documented in the API Doxygen documentation for
 the ``YansWifiChannelHelper::Default`` and ``YansWifiPhyHelper::Default``
 methods. Once these objects are created, we create a channel object
 and associate it to our PHY layer object manager to make sure
@@ -980,7 +980,7 @@
 
 ::
 
-  phy.SetChannel (channel.Create ());
+  phy.SetChannel(channel.Create());
 
 Once the PHY helper is configured, we can focus on the MAC layer. The
 WifiMacHelper object is used to set MAC parameters.
@@ -992,7 +992,7 @@
 ::
 
   WifiMacHelper mac;
-  Ssid ssid = Ssid ("ns-3-ssid");
+  Ssid ssid = Ssid("ns-3-ssid");
 
 WifiHelper will, by default, configure
 the standard in use to be 802.11ax (known commercially as Wi-Fi 6) and configure
@@ -1013,9 +1013,9 @@
 ::
 
   NetDeviceContainer staDevices
-  mac.SetType ("ns3::StaWifiMac",
-    "Ssid", SsidValue (ssid),
-    "ActiveProbing", BooleanValue (false));
+  mac.SetType("ns3::StaWifiMac",
+              "Ssid", SsidValue(ssid),
+              "ActiveProbing", BooleanValue(false));
 
 In the above code, the specific kind of MAC layer that
 will be created by the helper is specified by the TypeId value
@@ -1035,7 +1035,7 @@
 ::
 
   NetDeviceContainer staDevices;
-  staDevices = wifi.Install (phy, mac, wifiStaNodes);
+  staDevices = wifi.Install(phy, mac, wifiStaNodes);
 
 We have configured Wi-Fi for all of our STA nodes, and now we need to
 configure the AP (access point) node.  We begin this process by changing
@@ -1044,8 +1044,8 @@
 
 ::
 
-  mac.SetType ("ns3::ApWifiMac",
-               "Ssid", SsidValue (ssid));
+  mac.SetType("ns3::ApWifiMac",
+              "Ssid", SsidValue(ssid));
 
 In this case, the ``WifiMacHelper`` is going to create MAC
 layers of the "ns3::ApWifiMac", the latter specifying that a MAC
@@ -1057,7 +1057,7 @@
 ::
 
   NetDeviceContainer apDevices;
-  apDevices = wifi.Install (phy, mac, wifiApNode);
+  apDevices = wifi.Install(phy, mac, wifiApNode);
 
 Now, we are going to add mobility models.  We want the STA nodes to be mobile,
 wandering around inside a bounding box, and we want to make the AP node
@@ -1069,13 +1069,13 @@
 
   MobilityHelper mobility;
 
-  mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
-    "MinX", DoubleValue (0.0),
-    "MinY", DoubleValue (0.0),
-    "DeltaX", DoubleValue (5.0),
-    "DeltaY", DoubleValue (10.0),
-    "GridWidth", UintegerValue (3),
-    "LayoutType", StringValue ("RowFirst"));
+  mobility.SetPositionAllocator("ns3::GridPositionAllocator",
+                                "MinX", DoubleValue(0.0),
+                                "MinY", DoubleValue(0.0),
+                                "DeltaX", DoubleValue(5.0),
+                                "DeltaY", DoubleValue(10.0),
+                                "GridWidth", UintegerValue(3),
+                                "LayoutType", StringValue("RowFirst"));
 
 This code tells the mobility helper to use a two-dimensional grid to initially
 place the STA nodes.  Feel free to explore the Doxygen for class
@@ -1088,15 +1088,15 @@
 
 ::
 
-  mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel",
-    "Bounds", RectangleValue (Rectangle (-50, 50, -50, 50)));
+  mobility.SetMobilityModel("ns3::RandomWalk2dMobilityModel",
+                            "Bounds", RectangleValue(Rectangle(-50, 50, -50, 50)));
 
 We now tell the ``MobilityHelper`` to install the mobility models on the
 STA nodes.
 
 ::
 
-  mobility.Install (wifiStaNodes);
+  mobility.Install(wifiStaNodes);
 
 We want the access point to remain in a fixed position during the simulation.
 We accomplish this by setting the mobility model for this node to be the
@@ -1104,8 +1104,8 @@
 
 ::
 
-  mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
-  mobility.Install (wifiApNode);
+  mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+  mobility.Install(wifiApNode);
 
 We now have our nodes, devices and channels created, and mobility models
 chosen for the Wi-Fi nodes, but we have no protocol stacks present.  Just as
@@ -1115,9 +1115,9 @@
 ::
 
   InternetStackHelper stack;
-  stack.Install (csmaNodes);
-  stack.Install (wifiApNode);
-  stack.Install (wifiStaNodes);
+  stack.Install(csmaNodes);
+  stack.Install(wifiApNode);
+  stack.Install(wifiStaNodes);
 
 Just as in the ``second.cc`` example script, we are going to use the
 ``Ipv4AddressHelper`` to assign IP addresses to our device interfaces.
@@ -1130,50 +1130,50 @@
 
   Ipv4AddressHelper address;
 
-  address.SetBase ("10.1.1.0", "255.255.255.0");
+  address.SetBase("10.1.1.0", "255.255.255.0");
   Ipv4InterfaceContainer p2pInterfaces;
-  p2pInterfaces = address.Assign (p2pDevices);
+  p2pInterfaces = address.Assign(p2pDevices);
 
-  address.SetBase ("10.1.2.0", "255.255.255.0");
+  address.SetBase("10.1.2.0", "255.255.255.0");
   Ipv4InterfaceContainer csmaInterfaces;
-  csmaInterfaces = address.Assign (csmaDevices);
+  csmaInterfaces = address.Assign(csmaDevices);
 
-  address.SetBase ("10.1.3.0", "255.255.255.0");
-  address.Assign (staDevices);
-  address.Assign (apDevices);
+  address.SetBase("10.1.3.0", "255.255.255.0");
+  address.Assign(staDevices);
+  address.Assign(apDevices);
 
 We put the echo server on the "rightmost" node in the illustration at the
 start of the file.  We have done this before.
 
 ::
 
-  UdpEchoServerHelper echoServer (9);
+  UdpEchoServerHelper echoServer(9);
 
-  ApplicationContainer serverApps = echoServer.Install (csmaNodes.Get (nCsma));
-  serverApps.Start (Seconds (1.0));
-  serverApps.Stop (Seconds (10.0));
+  ApplicationContainer serverApps = echoServer.Install(csmaNodes.Get(nCsma));
+  serverApps.Start(Seconds(1.0));
+  serverApps.Stop(Seconds(10.0));
 
 And we put the echo client on the last STA node we created, pointing it to
 the server on the CSMA network.  We have also seen similar operations before.
 
 ::
 
-  UdpEchoClientHelper echoClient (csmaInterfaces.GetAddress (nCsma), 9);
-  echoClient.SetAttribute ("MaxPackets", UintegerValue (1));
-  echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
-  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));
+  UdpEchoClientHelper echoClient(csmaInterfaces.GetAddress(nCsma), 9);
+  echoClient.SetAttribute("MaxPackets", UintegerValue(1));
+  echoClient.SetAttribute("Interval", TimeValue(Seconds(1.0)));
+  echoClient.SetAttribute("PacketSize", UintegerValue(1024));
 
   ApplicationContainer clientApps =
-    echoClient.Install (wifiStaNodes.Get (nWifi - 1));
-  clientApps.Start (Seconds (2.0));
-  clientApps.Stop (Seconds (10.0));
+      echoClient.Install(wifiStaNodes.Get(nWifi - 1));
+  clientApps.Start(Seconds(2.0));
+  clientApps.Stop(Seconds(10.0));
 
 Since we have built an internetwork here, we need to enable internetwork routing
 just as we did in the ``second.cc`` example script.
 
 ::
 
-  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
+  Ipv4GlobalRoutingHelper::PopulateRoutingTables();
 
 One thing that can surprise some users is the fact that the simulation we just
 created will never "naturally" stop.  This is because we asked the wireless
@@ -1186,15 +1186,15 @@
 
 ::
 
-  Simulator::Stop (Seconds (10.0));
+  Simulator::Stop(Seconds(10.0));
 
 We create just enough tracing to cover all three networks:
 
 ::
 
-  pointToPoint.EnablePcapAll ("third");
-  phy.EnablePcap ("third", apDevices.Get (0));
-  csma.EnablePcap ("third", csmaDevices.Get (0), true);
+  pointToPoint.EnablePcapAll("third");
+  phy.EnablePcap("third", apDevices.Get(0));
+  csma.EnablePcap("third", csmaDevices.Get(0), true);
 
 These three lines of code will start pcap tracing on both of the point-to-point
 nodes that serves as our backbone, will start a promiscuous (monitor) mode
@@ -1206,8 +1206,8 @@
 
 ::
 
-    Simulator::Run ();
-    Simulator::Destroy ();
+    Simulator::Run();
+    Simulator::Destroy();
     return 0;
   }
 
@@ -1357,11 +1357,11 @@
 ::
 
   void
-  CourseChange (std::string context, Ptr<const MobilityModel> model)
+  CourseChange(std::string context, Ptr<const MobilityModel> model)
   {
-    Vector position = model->GetPosition ();
-    NS_LOG_UNCOND (context <<
-      " x = " << position.x << ", y = " << position.y);
+    Vector position = model->GetPosition();
+    NS_LOG_UNCOND(context <<
+                " x = " << position.x << ", y = " << position.y);
   }
 
 This code just pulls the position information from the mobility model and
@@ -1374,11 +1374,10 @@
 ::
 
   std::ostringstream oss;
-  oss <<
-    "/NodeList/" << wifiStaNodes.Get (nWifi - 1)->GetId () <<
-    "/$ns3::MobilityModel/CourseChange";
+  oss << "/NodeList/" << wifiStaNodes.Get(nWifi - 1)->GetId()
+      << "/$ns3::MobilityModel/CourseChange";
 
-  Config::Connect (oss.str (), MakeCallback (&CourseChange));
+  Config::Connect(oss.str(), MakeCallback(&CourseChange));
 
 What we do here is to create a string containing the tracing namespace path
 of the event to which we want to connect.  First, we have to figure out which
@@ -1525,7 +1524,7 @@
   packets) for QoS stations
 * SimpleNetDevice: The default configuration is to install a DropTail queue of default
   size (100 packets)
-* LTENetDevice: Queueing occurs at the RLC layer (RLC UM default buffer is 10 * 1024 bytes, RLC AM does not have a buffer limit).
+* LteNetDevice: Queueing occurs at the RLC layer (RLC UM default buffer is 10 * 1024 bytes, RLC AM does not have a buffer limit).
 * UanNetDevice: There is a default 10 packet queue at the MAC layer
 
 
@@ -1535,29 +1534,29 @@
 * The type of queue used by a NetDevice can be usually modified through the device helper::
 
     NodeContainer nodes;
-    nodes.Create (2);
+    nodes.Create(2);
 
     PointToPointHelper p2p;
-    p2p.SetQueue ("ns3::DropTailQueue", "MaxSize", StringValue ("50p"));
+    p2p.SetQueue("ns3::DropTailQueue", "MaxSize", StringValue("50p"));
 
-    NetDeviceContainer devices = p2p.Install (nodes);
+    NetDeviceContainer devices = p2p.Install(nodes);
 
 * The type of queue disc installed on a NetDevice can be modified through the
   traffic control helper::
 
     InternetStackHelper stack;
-    stack.Install (nodes);
+    stack.Install(nodes);
 
     TrafficControlHelper tch;
-    tch.SetRootQueueDisc ("ns3::CoDelQueueDisc", "MaxSize", StringValue ("1000p"));
-    tch.Install (devices);
+    tch.SetRootQueueDisc("ns3::CoDelQueueDisc", "MaxSize", StringValue("1000p"));
+    tch.Install(devices);
 
 * BQL can be enabled on a device that supports it through the traffic control helper::
 
     InternetStackHelper stack;
-    stack.Install (nodes);
+    stack.Install(nodes);
 
     TrafficControlHelper tch;
-    tch.SetRootQueueDisc ("ns3::CoDelQueueDisc", "MaxSize", StringValue ("1000p"));
-    tch.SetQueueLimits ("ns3::DynamicQueueLimits", "HoldTime", StringValue ("4ms"));
-    tch.Install (devices);
+    tch.SetRootQueueDisc("ns3::CoDelQueueDisc", "MaxSize", StringValue("1000p"));
+    tch.SetQueueLimits("ns3::DynamicQueueLimits", "HoldTime", StringValue("4ms"));
+    tch.Install(devices);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/conceptual-overview.rst ns-3.38/doc/tutorial/source/conceptual-overview.rst
--- ns-3.37/doc/tutorial/source/conceptual-overview.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/conceptual-overview.rst	2023-03-16 20:54:23.564868500 -0700
@@ -125,7 +125,7 @@
 Just as an Ethernet NIC is designed to work with an Ethernet network, the
 ``CsmaNetDevice`` is designed to work with a ``CsmaChannel``; the
 ``PointToPointNetDevice`` is designed to work with a
-``PointToPointChannel`` and a ``WifiNetNevice`` is designed to work with
+``PointToPointChannel`` and a ``WifiNetDevice`` is designed to work with
 a ``WifiChannel``.
 
 Topology Helpers
@@ -292,7 +292,7 @@
 
 ::
 
-  NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");
+  NS_LOG_COMPONENT_DEFINE("FirstScriptExample");
 
 We will use this statement as a convenient place to talk about our Doxygen
 documentation system.  If you look at the project web site,
@@ -334,7 +334,7 @@
 ::
 
   int
-  main (int argc, char *argv[])
+  main(int argc, char *argv[])
   {
 
 This is just the declaration of the main function of your program (script).
@@ -347,7 +347,7 @@
 
 ::
 
-    Time::SetResolution (Time::NS);
+    Time::SetResolution(Time::NS);
 
 The resolution is the smallest time value that can be represented (as well as
 the smallest representable difference between two time values).
@@ -386,7 +386,7 @@
 ::
 
     NodeContainer nodes;
-    nodes.Create (2);
+    nodes.Create(2);
 
 Let's find the documentation for the ``NodeContainer`` class before we
 continue.  Another way to get into the documentation for a given class is via
@@ -433,8 +433,8 @@
 ::
 
     PointToPointHelper pointToPoint;
-    pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-    pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+    pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+    pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
 
 The first line,
 
@@ -447,7 +447,7 @@
 
 ::
 
-    pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
+    pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
 
 tells the ``PointToPointHelper`` object to use the value "5Mbps"
 (five megabits per second) as the "DataRate" when it creates a
@@ -468,7 +468,7 @@
 
 ::
 
-    pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+    pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
 
 tells the ``PointToPointHelper`` to use the value "2ms" (two milliseconds)
 as the value of the propagation delay of every point to point channel it
@@ -490,7 +490,7 @@
 ::
 
     NetDeviceContainer devices;
-    devices = pointToPoint.Install (nodes);
+    devices = pointToPoint.Install(nodes);
 
 will finish configuring the devices and channel.  The first line declares the
 device container mentioned above and the second does the heavy lifting.  The
@@ -504,7 +504,7 @@
 helper are used to initialize the corresponding ``Attributes`` in the
 created objects.
 
-After executing the ``pointToPoint.Install (nodes)`` call we will have
+After executing the ``pointToPoint.Install(nodes)`` call we will have
 two nodes, each with an installed point-to-point net device and a single
 point-to-point channel between them.  Both devices will be configured to
 transmit data at five megabits per second over the channel which has a two
@@ -518,7 +518,7 @@
 ::
 
     InternetStackHelper stack;
-    stack.Install (nodes);
+    stack.Install(nodes);
 
 The ``InternetStackHelper`` is a topology helper that is to internet stacks
 what the ``PointToPointHelper`` is to point-to-point net devices.  The
@@ -539,7 +539,7 @@
 ::
 
     Ipv4AddressHelper address;
-    address.SetBase ("10.1.1.0", "255.255.255.0");
+    address.SetBase("10.1.1.0", "255.255.255.0");
 
 declare an address helper object and tell it that it should begin allocating IP
 addresses from the network 10.1.1.0 using the mask 255.255.255.0 to define
@@ -554,7 +554,7 @@
 
 ::
 
-    Ipv4InterfaceContainer interfaces = address.Assign (devices);
+    Ipv4InterfaceContainer interfaces = address.Assign(devices);
 
 performs the actual address assignment.  In |ns3| we make the
 association between an IP address and a device using an ``Ipv4Interface``
@@ -584,11 +584,11 @@
 
 ::
 
-    UdpEchoServerHelper echoServer (9);
+    UdpEchoServerHelper echoServer(9);
 
-    ApplicationContainer serverApps = echoServer.Install (nodes.Get (1));
-    serverApps.Start (Seconds (1.0));
-    serverApps.Stop (Seconds (10.0));
+    ApplicationContainer serverApps = echoServer.Install(nodes.Get(1));
+    serverApps.Start(Seconds(1.0));
+    serverApps.Stop(Seconds(10.0));
 
 The first line of code in the above snippet declares the
 ``UdpEchoServerHelper``.  As usual, this isn't the application itself, it
@@ -605,10 +605,10 @@
 has an ``Install`` method.  It is the execution of this method that actually
 causes the underlying echo server application to be instantiated and attached
 to a node.  Interestingly, the ``Install`` method takes a
-``NodeContainter`` as a parameter just as the other ``Install`` methods
+``NodeContainer`` as a parameter just as the other ``Install`` methods
 we have seen.  This is actually what is passed to the method even though it
 doesn't look so in this case.  There is a C++ *implicit conversion* at
-work here that takes the result of ``nodes.Get (1)`` (which returns a smart
+work here that takes the result of ``nodes.Get(1)`` (which returns a smart
 pointer to a node object --- ``Ptr<Node>``) and uses that in a constructor
 for an unnamed ``NodeContainer`` that is then passed to ``Install``.
 If you are ever at a loss to find a particular method signature in C++ code
@@ -633,8 +633,8 @@
 
 ::
 
-    serverApps.Start (Seconds (1.0));
-    serverApps.Stop (Seconds (10.0));
+    serverApps.Start(Seconds(1.0));
+    serverApps.Stop(Seconds(10.0));
 
 will cause the echo server application to ``Start`` (enable itself) at one
 second into the simulation and to ``Stop`` (disable itself) at ten seconds
@@ -651,14 +651,14 @@
 
 ::
 
-    UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9);
-    echoClient.SetAttribute ("MaxPackets", UintegerValue (1));
-    echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
-    echoClient.SetAttribute ("PacketSize", UintegerValue (1024));
-
-    ApplicationContainer clientApps = echoClient.Install (nodes.Get (0));
-    clientApps.Start (Seconds (2.0));
-    clientApps.Stop (Seconds (10.0));
+    UdpEchoClientHelper echoClient(interfaces.GetAddress(1), 9);
+    echoClient.SetAttribute("MaxPackets", UintegerValue(1));
+    echoClient.SetAttribute("Interval", TimeValue(Seconds(1.0)));
+    echoClient.SetAttribute("PacketSize", UintegerValue(1024));
+
+    ApplicationContainer clientApps = echoClient.Install(nodes.Get(0));
+    clientApps.Start(Seconds(2.0));
+    clientApps.Stop(Seconds(10.0));
 
 For the echo client, however, we need to set five different ``Attributes``.
 The first two ``Attributes`` are set during construction of the
@@ -695,17 +695,17 @@
 
 ::
 
-    Simulator::Run ();
+    Simulator::Run();
 
 When we previously called the methods,
 
 ::
 
-    serverApps.Start (Seconds (1.0));
-    serverApps.Stop (Seconds (10.0));
+    serverApps.Start(Seconds(1.0));
+    serverApps.Stop(Seconds(10.0));
     ...
-    clientApps.Start (Seconds (2.0));
-    clientApps.Stop (Seconds (10.0));
+    clientApps.Start(Seconds(2.0));
+    clientApps.Stop(Seconds(10.0));
 
 we actually scheduled events in the simulator at 1.0 seconds, 2.0 seconds and
 two events at 10.0 seconds.  When ``Simulator::Run`` is called, the system
@@ -741,7 +741,7 @@
 
 ::
 
-    Simulator::Destroy ();
+    Simulator::Destroy();
     return 0;
   }
 
@@ -764,7 +764,7 @@
 The simulation will stop automatically when no further events are in the
 event queue, or when a special Stop event is found. The Stop event is
 created through the
-``Simulator::Stop (stopTime);`` function.
+``Simulator::Stop(stopTime);`` function.
 
 There is a typical case where ``Simulator::Stop`` is absolutely necessary
 to stop the simulation: when there is a self-sustaining event.
@@ -791,9 +791,9 @@
 
 ::
 
-  +  Simulator::Stop (Seconds (11.0));
-     Simulator::Run ();
-     Simulator::Destroy ();
+  +  Simulator::Stop(Seconds(11.0));
+     Simulator::Run();
+     Simulator::Destroy();
      return 0;
    }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/conf.py ns-3.38/doc/tutorial/source/conf.py
--- ns-3.37/doc/tutorial/source/conf.py	2022-11-01 17:39:10.947703100 -0700
+++ ns-3.38/doc/tutorial/source/conf.py	2023-03-16 20:54:51.628957000 -0700
@@ -71,9 +71,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.37'
+version = u'ns-3.38'
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.37'
+release = u'ns-3.38'
 
 # 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.37/doc/tutorial/source/data-collection.rst ns-3.38/doc/tutorial/source/data-collection.rst
--- ns-3.37/doc/tutorial/source/data-collection.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/data-collection.rst	2023-03-16 20:54:23.564868500 -0700
@@ -50,8 +50,8 @@
 ::
 
   CommandLine cmd;
-  cmd.AddValue ("useIpv6", "Use Ipv6", useV6);
-  cmd.Parse (argc, argv);
+  cmd.AddValue("useIpv6", "Use Ipv6", useV6);
+  cmd.Parse(argc, argv);
 
 If the user specifies ``useIpv6``, option, the program will be run
 using IPv6 instead of IPv4.  The ``help`` option, available on all |ns3|
@@ -124,40 +124,40 @@
   +   // Configure the plot.  The first argument is the file name prefix
   +   // for the output files generated.  The second, third, and fourth
   +   // arguments are, respectively, the plot title, x-axis, and y-axis labels
-  +   plotHelper.ConfigurePlot ("seventh-packet-byte-count",
-  +                             "Packet Byte Count vs. Time",
-  +                             "Time (Seconds)",
-  +                             "Packet Byte Count");
+  +   plotHelper.ConfigurePlot("seventh-packet-byte-count",
+  +                            "Packet Byte Count vs. Time",
+  +                            "Time(Seconds)",
+  +                            "Packet Byte Count");
   +
   +   // Specify the probe type, trace source path (in configuration namespace), and
   +   // probe output trace source ("OutputBytes") to plot.  The fourth argument
   +   // specifies the name of the data series label on the plot.  The last
   +   // argument formats the plot by specifying where the key should be placed.
-  +   plotHelper.PlotProbe (probeType,
-  +                         tracePath,
-  +                         "OutputBytes",
-  +                         "Packet Byte Count",
-  +                         GnuplotAggregator::KEY_BELOW);
+  +   plotHelper.PlotProbe(probeType,
+  +                        tracePath,
+  +                        "OutputBytes",
+  +                        "Packet Byte Count",
+  +                        GnuplotAggregator::KEY_BELOW);
   +
   +   // Use FileHelper to write out the packet byte count over time
   +   FileHelper fileHelper;
   +
   +   // Configure the file to be written, and the formatting of output data.
-  +   fileHelper.ConfigureFile ("seventh-packet-byte-count",
-  +                             FileAggregator::FORMATTED);
+  +   fileHelper.ConfigureFile("seventh-packet-byte-count",
+  +                            FileAggregator::FORMATTED);
   +
   +   // Set the labels for this formatted output file.
-  +   fileHelper.Set2dFormat ("Time (Seconds) = %.3e\tPacket Byte Count = %.0f");
+  +   fileHelper.Set2dFormat("Time (Seconds) = %.3e\tPacket Byte Count = %.0f");
   +
   +   // Specify the probe type, probe path (in configuration namespace), and
   +   // probe output trace source ("OutputBytes") to write.
-  +   fileHelper.WriteProbe (probeType,
-  +                          tracePath,
-  +                          "OutputBytes");
-  +
-      Simulator::Stop (Seconds (20));
-      Simulator::Run ();
-      Simulator::Destroy ();
+  +   fileHelper.WriteProbe(probeType,
+  +                         tracePath,
+  +                         "OutputBytes");
+  +
+      Simulator::Stop(Seconds(20));
+      Simulator::Run();
+      Simulator::Destroy();
 
 
 The careful reader will have noticed, when testing the IPv6 command
@@ -243,10 +243,10 @@
   +  // Configure the plot.  The first argument is the file name prefix
   +  // for the output files generated.  The second, third, and fourth
   +  // arguments are, respectively, the plot title, x-axis, and y-axis labels
-  +  plotHelper.ConfigurePlot ("seventh-packet-byte-count",
-  +                            "Packet Byte Count vs. Time",
-  +                            "Time (Seconds)",
-  +                            "Packet Byte Count");
+  +  plotHelper.ConfigurePlot("seventh-packet-byte-count",
+  +                           "Packet Byte Count vs. Time",
+  +                           "Time (Seconds)",
+  +                           "Packet Byte Count");
 
 
 To this point, an empty plot has been configured.  The filename prefix
@@ -272,11 +272,11 @@
   +  // probe output trace source ("OutputBytes") to plot.  The fourth argument
   +  // specifies the name of the data series label on the plot.  The last
   +  // argument formats the plot by specifying where the key should be placed.
-  +  plotHelper.PlotProbe (probeType,
-  +                        tracePath,
-  +                        "OutputBytes",
-  +                        "Packet Byte Count",
-  +                        GnuplotAggregator::KEY_BELOW);
+  +  plotHelper.PlotProbe(probeType,
+  +                       tracePath,
+  +                       "OutputBytes",
+  +                       "Packet Byte Count",
+  +                       GnuplotAggregator::KEY_BELOW);
 
 The first two arguments are the name of the probe type and the trace source path.
 These two are probably the hardest to determine when you try to use
@@ -287,8 +287,8 @@
 
 ::
 
-    .AddTraceSource ("Tx", "Send IPv6 packet to outgoing interface.",
-                     MakeTraceSourceAccessor (&Ipv6L3Protocol::m_txTrace))
+    .AddTraceSource("Tx", "Send IPv6 packet to outgoing interface.",
+                    MakeTraceSourceAccessor(&Ipv6L3Protocol::m_txTrace))
 
 This says that ``Tx`` is a name for variable ``m_txTrace``, which has
 a declaration of:
@@ -317,18 +317,18 @@
 ::
 
   TypeId
-  Ipv6PacketProbe::GetTypeId ()
+  Ipv6PacketProbe::GetTypeId()
   {
-    static TypeId tid = TypeId ("ns3::Ipv6PacketProbe")
-      .SetParent<Probe> ()
-      .SetGroupName ("Stats")
-      .AddConstructor<Ipv6PacketProbe> ()
-      .AddTraceSource ( "Output",
-                        "The packet plus its IPv6 object and interface that serve as the output for this probe",
-                        MakeTraceSourceAccessor (&Ipv6PacketProbe::m_output))
-      .AddTraceSource ( "OutputBytes",
-                        "The number of bytes in the packet",
-                        MakeTraceSourceAccessor (&Ipv6PacketProbe::m_outputBytes))
+    static TypeId tid = TypeId("ns3::Ipv6PacketProbe")
+      .SetParent<Probe>()
+      .SetGroupName("Stats")
+      .AddConstructor<Ipv6PacketProbe>()
+      .AddTraceSource("Output",
+                      "The packet plus its IPv6 object and interface that serve as the output for this probe",
+                      MakeTraceSourceAccessor(&Ipv6PacketProbe::m_output))
+      .AddTraceSource("OutputBytes",
+                      "The number of bytes in the packet",
+                      MakeTraceSourceAccessor(&Ipv6PacketProbe::m_outputBytes))
     ;
     return tid;
   }
@@ -407,8 +407,8 @@
   +   FileHelper fileHelper;
   +
   +   // Configure the file to be written, and the formatting of output data.
-  +   fileHelper.ConfigureFile ("seventh-packet-byte-count",
-  +                             FileAggregator::FORMATTED);
+  +   fileHelper.ConfigureFile("seventh-packet-byte-count",
+  +                            FileAggregator::FORMATTED);
 
 The file helper file prefix is the first argument, and a format specifier
 is next.
@@ -420,7 +420,7 @@
 
   +
   +   // Set the labels for this formatted output file.
-  +   fileHelper.Set2dFormat ("Time (Seconds) = %.3e\tPacket Byte Count = %.0f");
+  +   fileHelper.Set2dFormat("Time (Seconds) = %.3e\tPacket Byte Count = %.0f");
 
 Finally, the trace source of interest must be hooked.  Again, the probeType and
 tracePath variables in this example are used, and the probe's output
@@ -431,9 +431,9 @@
   +
   +   // Specify the probe type, trace source path (in configuration namespace), and
   +   // probe output trace source ("OutputBytes") to write.
-  +   fileHelper.WriteProbe (probeType,
-  +                          tracePath,
-  +                          "OutputBytes");
+  +   fileHelper.WriteProbe(probeType,
+  +                         tracePath,
+  +                         "OutputBytes");
   +
 
 The wildcard fields in this trace source specifier match two trace sources.
@@ -448,4 +448,3 @@
 above may be replicated within the scope of support of the existing
 probes and trace sources.  More capabilities including statistics
 processing will be added in future releases.
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/getting-started.rst ns-3.38/doc/tutorial/source/getting-started.rst
--- ns-3.37/doc/tutorial/source/getting-started.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/getting-started.rst	2023-03-16 20:54:23.564868500 -0700
@@ -70,13 +70,13 @@
 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.37), the following tools
+As of the most recent |ns3| release (ns-3.38), the following tools
 are needed to get started with |ns3|:
 
 ============  ===========================================================
 Prerequisite  Package/version
 ============  ===========================================================
-C++ compiler  ``clang++`` or ``g++`` (g++ version 8 or greater)
+C++ compiler  ``clang++`` or ``g++`` (g++ version 9 or greater)
 Python        ``python3`` version >=3.6
 CMake         ``cmake`` version >=3.10
 Build system  ``make``, ``ninja``, ``xcodebuild`` (XCode)
@@ -124,21 +124,21 @@
   $ cd
   $ mkdir workspace
   $ cd workspace
-  $ wget https://www.nsnam.org/release/ns-allinone-3.37.tar.bz2
-  $ tar xjf ns-allinone-3.37.tar.bz2
+  $ wget https://www.nsnam.org/release/ns-allinone-3.38.tar.bz2
+  $ tar xjf ns-allinone-3.38.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.37``, you should see a number of files and directories
+``ns-allinone-3.38``, you should see a number of files and directories
 
 .. sourcecode:: text
 
-  $ cd ns-allinone-3.37
+  $ cd ns-allinone-3.38
   $ ls
-  bake  build.py  constants.py  netanim-3.108 ns-3.37  README.md  util.py
+  bake  build.py  constants.py  netanim-3.109 ns-3.38  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 +188,7 @@
 
 .. sourcecode:: console
 
-  $ python3 download.py -n ns-3.37
+  $ python3 download.py -n ns-3.38
 
 After this step, the additional repositories of |ns3|, bake, pybindgen,
 and netanim will be downloaded to the ``ns-3-allinone`` directory.
@@ -199,7 +199,7 @@
 The above two techniques (source archive, or ns-3-allinone repository
 via Git) are useful to get the most basic installation of |ns3| with a
 few addons (pybindgen for generating Python bindings, and netanim
-for network animiations).  The third repository provided by default in
+for network animations).  The third repository provided by default in
 ns-3-allinone is called ``bake``.
 
 Bake is a tool for coordinated software building from multiple repositories,
@@ -257,9 +257,9 @@
 
 There are a few configuration targets available:
 
-1.  ``ns-3.37``:  the code corresponding to the release
+1.  ``ns-3.38``:  the code corresponding to the release
 2.  ``ns-3-dev``:  a similar module but using the development code tree
-3.  ``ns-allinone-3.37``:  the module that includes other optional features
+3.  ``ns-allinone-3.38``:  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 +276,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.37``.
+this tutorial example with ``ns-3.38``.
 
 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 +305,7 @@
 
 .. sourcecode:: console
 
-  $ ./bake.py configure -e ns-allinone-3.37
+  $ ./bake.py configure -e ns-allinone-3.38
 
 Next, we'll ask bake to check whether we have enough tools to download
 various components.  Type:
@@ -351,11 +351,11 @@
   >> Searching for system dependency qt - OK
   >> Searching for system dependency g++ - OK
   >> Searching for system dependency cmake - OK
-  >> Downloading netanim-3.108 - OK
-  >> Downloading click-ns-3.37 - OK
+  >> Downloading netanim-3.109 - OK
+  >> Downloading click-ns-3.38 - OK
   >> Downloading BRITE - OK
   >> Downloading openflow-dev - OK
-  >> Downloading ns-3.37 (target directory:ns-3.37) - OK
+  >> Downloading ns-3.38 (target directory:ns-3.38) - OK
 
 The above suggests that three sources have been downloaded.  Check the
 ``source`` directory now and type ``ls``; one should see:
@@ -364,7 +364,7 @@
 
   $ cd source
   $ ls
-  BRITE  click-ns-3.37  netanim-3.108  ns-3.37  openflow-dev
+  BRITE  click-ns-3.37  netanim-3.109  ns-3.38  openflow-dev
 
 You are now ready to build the |ns3| distribution.
 
@@ -394,7 +394,7 @@
 
 If you downloaded
 using a tarball you should have a directory called something like
-``ns-allinone-3.37`` under your ``~/workspace`` directory.
+``ns-allinone-3.38`` under your ``~/workspace`` directory.
 Type the following:
 
 .. sourcecode:: console
@@ -426,8 +426,8 @@
 
 .. sourcecode:: text
 
-  >> Building netanim-3.108 - OK
-  >> Building ns-3.37 - OK
+  >> Building netanim-3.109 - OK
+  >> Building ns-3.38 - OK
 
 There may be failures to build all components, but the build will proceed
 anyway if the component is optional.
@@ -735,7 +735,7 @@
 
 For instance, ns-3.28 was released prior to Fedora 28, which included
 a new major version of gcc (gcc-8).  Building ns-3.28 or older releases
-on Fedora 28, when Gtk2+ is installed, will result in an error such as::
+on Fedora 28, when GTK+2 is installed, will result in an error such as::
 
   /usr/include/gtk-2.0/gtk/gtkfilechooserbutton.h:59:8: error: unnecessary parentheses in declaration of ‘__gtk_reserved1’ [-Werror=parentheses]
    void (*__gtk_reserved1);
@@ -822,9 +822,9 @@
 
 .. sourcecode:: cpp
 
-  NS_BUILD_DEBUG (std::cout << "Part of an output line..." << std::flush; timer.Start ());
-  DoLongInvolvedComputation ();
-  NS_BUILD_DEBUG (timer.Stop (); std::cout << "Done: " << timer << std::endl;)
+  NS_BUILD_DEBUG(std::cout << "Part of an output line..." << std::flush; timer.Start());
+  DoLongInvolvedComputation();
+  NS_BUILD_DEBUG(timer.Stop(); std::cout << "Done: " << timer << std::endl;)
 
 By default ns3 puts the build artifacts in the ``build`` directory.
 You can specify a different output directory with the ``--out``
@@ -933,6 +933,44 @@
 it useful if their use case involves working with programs outside
 of the |ns3| directory.
 
+Clean
+=====
+
+Cleaning refers to the removal of artifacts (e.g. files) generated or edited
+by the build process.  There are different levels of cleaning possible:
+
++----------+-------------------+--------------------------------------------------------------------------------+
+| Scope    | Command           | Description                                                                    |
++==========+===================+================================================================================+
+| clean    | `./ns3 clean`     | Remove artifacts generated by the CMake configuration and the build            |
++----------+-------------------+--------------------------------------------------------------------------------+
+| distclean| `./ns3 distclean` | Remove artifacts from the configuration, build, documentation, test and Python |
++----------+-------------------+--------------------------------------------------------------------------------+
+| ccache   | `ccache -C`       | Remove all compiled artifacts from the ccache                                  |
++----------+-------------------+--------------------------------------------------------------------------------+
+
+`clean` can be used if the focus is on reconfiguring the way that ns-3 is
+presently being compiled.  `distclean` can be used if the focus is
+on restoring the ns-3 directory to an original state.
+
+The ccache lies outside of the ns-3 directory (typically in a hidden
+directory at `~/.cache/ccache`) and is shared across projects.
+Users should be aware that cleaning the ccache will cause cache misses
+on other build directories outside of the current working directory.
+Cleaning this cache periodically may be helpful to reclaim disk space.
+Cleaning the ccache is completely separate from cleaning any files
+within the ns-3 directory.
+
+Because clean operations involve removing files, the option conservatively
+refuses to remove files if one of the deleted files or directories lies
+outside of the current working directory.  Users may wish to precede the
+actual clean with a `--dry-run`, when in doubt about what the clean
+command will do, because a dry run will print the warning if one exists.
+For example::
+
+  ./ns3 clean --dry-run
+  ./ns3 clean
+
 One ns3
 =======
 
@@ -1043,11 +1081,11 @@
 since the examples won't be built by the test-runner target.
 
 On Windows, the Msys2/MinGW64/bin directory path must be on the PATH environment variable,
-otherwise the dll's required by the C++ runtime will not be found, resulting in crashes
+otherwise the DLL's required by the C++ runtime will not be found, resulting in crashes
 without any explicit reasoning.
 
 Note: The ns-3 script adds only the ns-3 lib directory path to the PATH,
-ensuring the ns-3 dlls will be found by running programs. If you are using CMake directly or
+ensuring the ns-3 DLLs will be found by running programs. If you are using CMake directly or
 an IDE, make sure to also include the path to ns-3-dev/build/lib in the PATH variable.
 
 .. _setx : https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/setx/
@@ -1626,4 +1664,3 @@
   then
       echo "$gitDiff" >> version.txt
   fi
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/index.rst ns-3.38/doc/tutorial/source/index.rst
--- ns-3.37/doc/tutorial/source/index.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/index.rst	2023-03-16 20:54:23.564868500 -0700
@@ -1,17 +1,26 @@
 .. only:: html or latex
 
 ns-3 Tutorial
-===========================
+=============
 
-This is the *ns-3 Tutorial*. Primary documentation for the ns-3 project is
-available in five forms:
+This is the *ns-3 Tutorial*. Primary documentation for the ns-3 project is organized as
+follows:
 
-* `ns-3 Doxygen <https://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
-* Tutorial *(this document)*, Manual, and Model Library for the `latest release <https://www.nsnam.org/documentation/latest/>`_ and `development tree <https://www.nsnam.org/documentation/development-tree/>`_
-* `ns-3 wiki <https://www.nsnam.org/wiki>`_
+* Several guides that are version controlled for each release (the
+  `latest release <https://www.nsnam.org/documentation/latest/>`_) and
+  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+
+  * Tutorial *(this document)*
+  * Installation Guide
+  * Manual
+  * Model Library
+  * Contributing Guide
+* `ns-3 Doxygen <https://www.nsnam.org/docs/doxygen/index.html>`_: Documentation of the public APIs of
+  the simulator
+* `ns-3 wiki <https://www.nsnam.org/wiki/Main_Page>`_
 
 This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <https://www.sphinx-doc.org/>`_ and is maintained in the
-``doc/tutorial`` directory of ns-3's source code.
+``doc/tutorial`` directory of ns-3's source code.  Source file column width is 100 columns.
 
 .. toctree::
    :maxdepth: 2
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/quick-start.rst ns-3.38/doc/tutorial/source/quick-start.rst
--- ns-3.37/doc/tutorial/source/quick-start.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/quick-start.rst	2023-03-16 20:54:23.564868500 -0700
@@ -64,13 +64,13 @@
 
    ::
 
-    $ tar xjf ns-allinone-3.37.tar.bz2
+    $ tar xjf ns-allinone-3.38.tar.bz2
 
 3) Change into the |ns3| directory directly; e.g.
 
    ::
 
-    $ cd ns-allinone-3.37/ns-3.37
+    $ cd ns-allinone-3.38/ns-3.38
 
 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.37 as of this
+If instead you want to try the most recent release (version 3.38 as of this
 writing), you can checkout a branch corresponding to that git tag:
 
 ::
 
-  $ git checkout -b ns-3.37-branch ns-3.37
+  $ git checkout -b ns-3.38-branch ns-3.38
 
 Building and testing ns-3
 *************************
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/tracing.rst ns-3.38/doc/tutorial/source/tracing.rst
--- ns-3.37/doc/tutorial/source/tracing.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/tracing.rst	2023-03-16 20:54:23.564868500 -0700
@@ -3,7 +3,7 @@
 .. role:: raw-role(raw)
    :format: html latex
 
-.. Mimic doxygen formatting for parameter names
+.. Mimic Doxygen formatting for parameter names
 
 .. raw:: html
 
@@ -71,7 +71,7 @@
   #include <iostream>
   ...
   void
-  SomeFunction (void)
+  SomeFunction()
   {
     uint32_t x = SOME_INTERESTING_VALUE;
     ...
@@ -107,16 +107,16 @@
 Let's pick a random example.  If you wanted to add more logging to the
 |ns3| TCP socket (``tcp-socket-base.cc``) you could just add a new
 message down in the implementation.  Notice that in
-``TcpSocketBase::ProcessEstablished ()`` there is no log message for the
+``TcpSocketBase::ProcessEstablished()`` there is no log message for the
 reception of a SYN+ACK in ESTABLISHED state.
 You could simply add one, changing the code.  Here is the original::
 
   /* Received a packet upon ESTABLISHED state. This function is mimicking the
       role of tcp_rcv_established() in tcp_input.c in Linux kernel. */
   void
-  TcpSocketBase::ProcessEstablished (Ptr<Packet> packet, const TcpHeader& tcpHeader)
+  TcpSocketBase::ProcessEstablished(Ptr<Packet> packet, const TcpHeader& tcpHeader)
   {
-    NS_LOG_FUNCTION (this << tcpHeader);
+    NS_LOG_FUNCTION(this << tcpHeader);
     ...
 
     else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK))
@@ -130,13 +130,13 @@
   /* Received a packet upon ESTABLISHED state. This function is mimicking the
       role of tcp_rcv_established() in tcp_input.c in Linux kernel. */
   void
-  TcpSocketBase::ProcessEstablished (Ptr<Packet> packet, const TcpHeader& tcpHeader)
+  TcpSocketBase::ProcessEstablished(Ptr<Packet> packet, const TcpHeader& tcpHeader)
   {
-    NS_LOG_FUNCTION (this << tcpHeader);
+    NS_LOG_FUNCTION(this << tcpHeader);
     ...
     else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK))
       { // No action for received SYN+ACK, it is probably a duplicated packet
-        NS_LOG_LOGIC ("TcpSocketBase " << this << " ignoring SYN+ACK");
+        NS_LOG_LOGIC("TcpSocketBase " << this << " ignoring SYN+ACK");
       }
     ...
 
@@ -261,7 +261,7 @@
 function with a matching signature.  In this case, you could provide a
 function that looks like::
 
-  int MyFunction (int arg) {}
+  int MyFunction(int arg) {}
 
 If you have this target, you can initialize the variable to point to
 your function::
@@ -271,14 +271,14 @@
 You can then call MyFunction indirectly using the more suggestive form
 of the call::
 
-  int result = (*pfi) (1234);
+  int result = (*pfi)(1234);
 
 This is suggestive since it looks like you are dereferencing the
 function pointer just like you would dereference any pointer.
 Typically, however, people take advantage of the fact that the
 compiler knows what is going on and will just use a shorter form::
 
-  int result = pfi (1234);
+  int result = pfi(1234);
 
 This looks like you are calling a function named ``pfi``, but the
 compiler is smart enough to know to call through the variable ``pfi``
@@ -363,21 +363,21 @@
   class MyObject : public Object
   {
   public:
-    static TypeId GetTypeId (void)
+    static TypeId GetTypeId()
     {
-      static TypeId tid = TypeId ("MyObject")
-        .SetParent (Object::GetTypeId ())
-        .SetGroupName ("MyGroup")
-        .AddConstructor<MyObject> ()
-        .AddTraceSource ("MyInteger",
-                         "An integer value to trace.",
-                         MakeTraceSourceAccessor (&MyObject::m_myInt),
-                         "ns3::TracedValueCallback::Int32")
+      static TypeId tid = TypeId("MyObject")
+        .SetParent(Object::GetTypeId())
+        .SetGroupName("MyGroup")
+        .AddConstructor<MyObject>()
+        .AddTraceSource("MyInteger",
+                        "An integer value to trace.",
+                        MakeTraceSourceAccessor(&MyObject::m_myInt),
+                        "ns3::TracedValueCallback::Int32")
         ;
       return tid;
     }
 
-    MyObject () {}
+    MyObject() {}
     TracedValue<int32_t> m_myInt;
   };
 
@@ -405,7 +405,7 @@
 
 ::
 
-  void (* traceSink)(int32_t oldValue, int32_t newValue);
+  void (*traceSink)(int32_t oldValue, int32_t newValue);
 
 All trace sinks hooking this trace source must have this signature.
 We'll discuss below how you can determine the required callback
@@ -414,7 +414,7 @@
 Sure enough, continuing through ``fourth.cc`` we see::
 
   void
-  IntTrace (int32_t oldValue, int32_t newValue)
+  IntTrace(int32_t oldValue, int32_t newValue)
   {
     std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
   }
@@ -427,10 +427,10 @@
 code to connect the source to the sink, which happens in ``main``::
 
   int
-  main (int argc, char *argv[])
+  main(int argc, char *argv[])
   {
-    Ptr<MyObject> myObject = CreateObject<MyObject> ();
-    myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
+    Ptr<MyObject> myObject = CreateObject<MyObject>();
+    myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace));
 
     myObject->m_myInt = 1234;
   }
@@ -519,10 +519,10 @@
 more clear to you what this function is doing::
 
   void
-  CourseChange (std::string context, Ptr<const MobilityModel> model)
+  CourseChange(std::string context, Ptr<const MobilityModel> model)
   {
-    Vector position = model->GetPosition ();
-    NS_LOG_UNCOND (context <<
+    Vector position = model->GetPosition();
+    NS_LOG_UNCOND(context <<
       " x = " << position.x << ", y = " << position.y);
   }
 
@@ -533,10 +533,10 @@
 
   std::ostringstream oss;
   oss << "/NodeList/"
-      << wifiStaNodes.Get (nWifi - 1)->GetId ()
+      << wifiStaNodes.Get(nWifi - 1)->GetId()
       << "/$ns3::MobilityModel/CourseChange";
 
-  Config::Connect (oss.str (), MakeCallback (&CourseChange));
+  Config::Connect(oss.str(), MakeCallback(&CourseChange));
 
 Let's try and make some sense of what is sometimes considered
 relatively mysterious code.  For the purposes of discussion, assume
@@ -558,16 +558,16 @@
 could have used this ``Ptr<Node>`` to call a Connect method
 directly::
 
-  Ptr<Object> theObject = wifiStaNodes.Get (nWifi - 1);
-  theObject->TraceConnectWithoutContext ("CourseChange", MakeCallback (&CourseChange));
+  Ptr<Object> theObject = wifiStaNodes.Get(nWifi - 1);
+  theObject->TraceConnectWithoutContext("CourseChange", MakeCallback(&CourseChange));
 
 In the ``third.cc`` example, we actually wanted an additional "context"
 to be delivered along with the Callback parameters (which will be
 explained below) so we could actually use the following equivalent
 code::
 
-  Ptr<Object> theObject = wifiStaNodes.Get (nWifi - 1);
-  theObject->TraceConnect ("CourseChange", MakeCallback (&CourseChange));
+  Ptr<Object> theObject = wifiStaNodes.Get(nWifi - 1);
+  theObject->TraceConnect("CourseChange", MakeCallback(&CourseChange));
 
 It turns out that the internal code for
 ``Config::ConnectWithoutContext`` and ``Config::Connect`` actually
@@ -596,7 +596,7 @@
 As described in the Object Model section of the |ns3| Manual, we make
 widespread use of object aggregation.  This allows us to form an
 association between different Objects without building a complicated
-inheritance tree or predeciding what objects will be part of a
+inheritance tree or pre-deciding what objects will be part of a
 Node.  Each Object in an Aggregation can be reached from the other
 Objects.
 
@@ -613,7 +613,7 @@
 are familiar with ``GetObject``, we have asked the system to do the
 following::
 
-  Ptr<MobilityModel> mobilityModel = node->GetObject<MobilityModel> ()
+  Ptr<MobilityModel> mobilityModel = node->GetObject<MobilityModel>()
 
 We are now at the last Object in the path, so we turn our attention to
 the Attributes of that Object.  The ``MobilityModel`` class defines an
@@ -623,10 +623,10 @@
 
 ::
 
-  .AddTraceSource ("CourseChange",
-                   "The value of the position and/or velocity vector changed",
-                   MakeTraceSourceAccessor (&MobilityModel::m_courseChangeTrace),
-                   "ns3::MobilityModel::CourseChangeCallback")
+  .AddTraceSource("CourseChange",
+                  "The value of the position and/or velocity vector changed",
+                  MakeTraceSourceAccessor(&MobilityModel::m_courseChangeTrace),
+                  "ns3::MobilityModel::CourseChangeCallback")
 
 which should look very familiar at this point.
 
@@ -635,7 +635,7 @@
 
 ::
 
-  TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+  TracedCallback<Ptr<const MobilityModel>> m_courseChangeTrace;
 
 The type declaration ``TracedCallback`` identifies
 ``m_courseChangeTrace`` as a special list of Callbacks that can be
@@ -650,7 +650,7 @@
 ``NotifyCourseChange()``::
 
   void
-  MobilityModel::NotifyCourseChange (void) const
+  MobilityModel::NotifyCourseChange() const
   {
     m_courseChangeTrace(this);
   }
@@ -843,8 +843,8 @@
 this case, ``src/mobility/examples/main-random-topology.cc`` has
 something just waiting for you to use::
 
-  Config::Connect ("/NodeList/*/$ns3::MobilityModel/CourseChange",
-    MakeCallback (&CourseChange));
+  Config::Connect("/NodeList/*/$ns3::MobilityModel/CourseChange",
+                  MakeCallback(&CourseChange));
 
 We'll return to this example in a moment.
 
@@ -889,7 +889,7 @@
 same file::
 
   static void
-  CourseChange (std::string context, Ptr<const MobilityModel> model)
+  CourseChange(std::string context, Ptr<const MobilityModel> model)
   {
     ...
   }
@@ -915,7 +915,7 @@
 the declaration.  Recall that for our current example, this is in
 ``mobility-model.h``, where we have previously found::
 
-  TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+  TracedCallback<Ptr<const MobilityModel>> m_courseChangeTrace;
 
 There is a one-to-one correspondence between the template parameter
 list in the declaration and the formal arguments of the callback
@@ -925,7 +925,7 @@
 example::
 
   void
-  CourseChange (Ptr<const MobilityModel> model)
+  CourseChange(Ptr<const MobilityModel> model)
   {
     ...
   }
@@ -936,7 +936,7 @@
 arguments::
 
   void
-  CourseChange (std::string context, Ptr<const MobilityModel> model)
+  CourseChange(std::string context, Ptr<const MobilityModel> model)
   {
     ...
   }
@@ -946,7 +946,7 @@
 come up with::
 
   static void
-  CourseChange (std::string path, Ptr<const MobilityModel> model)
+  CourseChange(std::string path, Ptr<const MobilityModel> model)
   {
     ...
   }
@@ -971,7 +971,7 @@
 source.  Recall that this is in ``mobility-model.h``, where we have
 previously found::
 
-  TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+  TracedCallback<Ptr<const MobilityModel>> m_courseChangeTrace;
 
 This declaration is for a template.  The template parameter is inside
 the angle-brackets, so we are really interested in finding out what
@@ -995,19 +995,19 @@
 
 ::
 
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::TracedCallback ()
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext (c ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Connect (const CallbackB ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::TracedCallback()
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext(c ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Connect(const CallbackB ...
   TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::DisconnectWithoutContext ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Disconnect (const Callba ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (void) const ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1) const ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
-  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Disconnect(const Callba ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()() const ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()(T1 a1) const ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()(T1 a1, T2 a2 ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()(T1 a1, T2 a2 ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()(T1 a1, T2 a2 ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()(T1 a1, T2 a2 ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()(T1 a1, T2 a2 ...
+  TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator()(T1 a1, T2 a2 ...
 
 It turns out that all of this comes from the header file
 ``traced-callback.h`` which sounds very promising.  You can then take
@@ -1052,7 +1052,7 @@
 possible type parameters with default values.  Go back and compare
 this with the declaration you are trying to understand::
 
-  TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace;
+  TracedCallback<Ptr<const MobilityModel>> m_courseChangeTrace;
 
 The ``typename T1`` in the templated class declaration corresponds to
 the ``Ptr<const MobilityModel>`` in the declaration above.  All of the
@@ -1071,8 +1071,8 @@
   TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext ...
   {
     Callback<void,T1,T2,T3,T4,T5,T6,T7,T8> cb;
-    cb.Assign (callback);
-    m_callbackList.push_back (cb);
+    cb.Assign(callback);
+    m_callbackList.push_back(cb);
   }
 
 You are now in the belly of the beast.  When the template is
@@ -1084,9 +1084,9 @@
   void
   TracedCallback<Ptr<const MobilityModel>::ConnectWithoutContext ... cb
   {
-    Callback<void, Ptr<const MobilityModel> > cb;
-    cb.Assign (callback);
-    m_callbackList.push_back (cb);
+    Callback<void, Ptr<const MobilityModel>> cb;
+    cb.Assign(callback);
+    m_callbackList.push_back(cb);
   }
 
 You can now see the implementation of everything we've been talking
@@ -1115,7 +1115,7 @@
 
 ::
 
-    Callback<void, Ptr<const MobilityModel> > cb;
+    Callback<void, Ptr<const MobilityModel>> cb;
 
 declaration means.  Now we are in a position to understand that the
 first (non-optional) template argument, ``void``, represents the
@@ -1130,7 +1130,7 @@
 ::
 
   void
-  CourseChangeCallback (Ptr<const MobilityModel> model)
+  CourseChangeCallback(Ptr<const MobilityModel> model)
   {
     ...
   }
@@ -1141,7 +1141,7 @@
 ``Connect`` function will provide the context for you.  You'll need::
 
   void
-  CourseChangeCallback (std::string context, Ptr<const MobilityModel> model)
+  CourseChangeCallback(std::string context, Ptr<const MobilityModel> model)
   {
     ...
   }
@@ -1151,7 +1151,7 @@
 come up with::
 
   static void
-  CourseChangeCallback (std::string path, Ptr<const MobilityModel> model)
+  CourseChangeCallback(std::string path, Ptr<const MobilityModel> model)
   {
     ...
   }
@@ -1316,15 +1316,15 @@
 
 ::
 
-  ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow",
-    MakeCallback (&Ns3TcpCwndTestCase1::CwndChange, this));
+  ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow",
+      MakeCallback(&Ns3TcpCwndTestCase1::CwndChange, this));
 
 This should look very familiar to you.  We mentioned above that if we
 had a pointer to the ``TcpSocketBase``, we could ``TraceConnect`` to the
 "CongestionWindow" trace source.  That's exactly what we have here; so
 it turns out that this line of code does exactly what we want.  Let's
 go ahead and extract the code we need from this function
-(``Ns3TcpCwndTestCase1::DoRun (void)``).  If you look at this
+(``Ns3TcpCwndTestCase1::DoRun()``).  If you look at this
 function, you will find that it looks just like an |ns3| script.  It
 turns out that is exactly what it is.  It is a script run by the test
 framework, so we can just pull it out and wrap it in ``main`` instead
@@ -1379,7 +1379,7 @@
    give the object to the system to use during simulation time.
 
 We took the second approach in the ``fifth.cc`` example.  This
-decision required us to create the ``MyApp`` ``Application``, the
+decision required us to create the ``TutorialApp`` ``Application``, the
 entire purpose of which is to take a ``Socket`` as a parameter.
 
 Walkthrough: ``fifth.cc``
@@ -1406,18 +1406,21 @@
    * Foundation, Include., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    */
 
-  #include <fstream>
+  #include "tutorial-app.h"
+
+  #include "ns3/applications-module.h"
   #include "ns3/core-module.h"
-  #include "ns3/network-module.h"
   #include "ns3/internet-module.h"
+  #include "ns3/network-module.h"
   #include "ns3/point-to-point-module.h"
-  #include "ns3/applications-module.h"
+
+  #include <fstream>
 
   using namespace ns3;
 
-  NS_LOG_COMPONENT_DEFINE ("FifthScriptExample");
+  NS_LOG_COMPONENT_DEFINE("FifthScriptExample");
 
-This has all been covered, so we won't rehash it.  The next lines of
+The next lines of
 source are the network illustration and a comment addressing the
 problem described above with ``Socket``.
 
@@ -1460,44 +1463,67 @@
 
 This should also be self-explanatory.
 
-The next part is the declaration of the ``MyApp`` ``Application`` that
-we put together to allow the ``Socket`` to be created at configuration
-time.
+Previous versions of |ns3| declared a custom application called ``MyApp``
+for use in this program.  Current versions of |ns3| have moved this to
+a separate header file (``tutorial-app.h'') and implementation file
+(``tutorial-app.cc'').  This simple application allows the ``Socket''
+to be created at configuration time.
 
 ::
 
-  class MyApp : public Application
+  /**
+   * Tutorial - a simple Application sending packets.
+   */
+  class TutorialApp : public Application
   {
-  public:
-
-    MyApp ();
-    virtual ~MyApp();
-
-    void Setup (Ptr<Socket> socket, Address address, uint32_t packetSize,
-      uint32_t nPackets, DataRate dataRate);
-
-  private:
-    virtual void StartApplication (void);
-    virtual void StopApplication (void);
-
-    void ScheduleTx (void);
-    void SendPacket (void);
-
-    Ptr<Socket>     m_socket;
-    Address         m_peer;
-    uint32_t        m_packetSize;
-    uint32_t        m_nPackets;
-    DataRate        m_dataRate;
-    EventId         m_sendEvent;
-    bool            m_running;
-    uint32_t        m_packetsSent;
+    public:
+      TutorialApp();
+      ~TutorialApp() override;
+
+      /**
+       * Register this type.
+       * \return The TypeId.
+       */
+      static TypeId GetTypeId();
+
+      /**
+       * Setup the socket.
+       * \param socket The socket.
+       * \param address The destination address.
+       * \param packetSize The packet size to transmit.
+       * \param nPackets The number of packets to transmit.
+       * \param dataRate the data rate to use.
+       */
+      void Setup(Ptr<Socket> socket,
+                 Address address,
+                 uint32_t packetSize,
+                 uint32_t nPackets,
+                 DataRate dataRate);
+
+    private:
+      void StartApplication() override;
+      void StopApplication() override;
+
+      /// Schedule a new transmission.
+      void ScheduleTx();
+      /// Send a packet.
+      void SendPacket();
+
+      Ptr<Socket> m_socket;   //!< The transmission socket.
+      Address m_peer;         //!< The destination address.
+      uint32_t m_packetSize;  //!< The packet size.
+      uint32_t m_nPackets;    //!< The number of packets to send.
+      DataRate m_dataRate;    //!< The data rate to use.
+      EventId m_sendEvent;    //!< Send event.
+      bool m_running;         //!< True if the application is running.
+      uint32_t m_packetsSent; //!< The number of packets sent.
   };
 
 You can see that this class inherits from the |ns3| ``Application``
 class.  Take a look at ``src/network/model/application.h`` if you are
-interested in what is inherited.  The ``MyApp`` class is obligated to
+interested in what is inherited.  The ``TutorialApp`` class is obligated to
 override the ``StartApplication`` and ``StopApplication`` methods.
-These methods are automatically called when ``MyApp`` is required to
+These methods are automatically called when ``TutorialApp`` is required to
 start and stop sending data during the simulation.
 
 Starting/Stopping Applications
@@ -1516,8 +1542,8 @@
 (hopefully) familiar lines of an |ns3| script::
 
   ApplicationContainer apps = ...
-  apps.Start (Seconds (1.0));
-  apps.Stop (Seconds (10.0));
+  apps.Start(Seconds(1.0));
+  apps.Stop(Seconds(10.0));
 
 The application container code (see
 ``src/network/helper/application-container.h`` if you are interested)
@@ -1525,25 +1551,25 @@
 
 ::
 
-  app->SetStartTime (startTime);
+  app->SetStartTime(startTime);
 
 as a result of the ``apps.Start`` call and
 
 ::
 
-  app->SetStopTime (stopTime);
+  app->SetStopTime(stopTime);
 
 as a result of the ``apps.Stop`` call.
 
 The ultimate result of these calls is that we want to have the
 simulator automatically make calls into our ``Applications`` to tell
-them when to start and stop.  In the case of ``MyApp``, it inherits
+them when to start and stop.  In the case of ``TutorialApp``, it inherits
 from class ``Application`` and overrides ``StartApplication``, and
 ``StopApplication``.  These are the functions that will be called by
-the simulator at the appropriate time.  In the case of ``MyApp`` you
-will find that ``MyApp::StartApplication`` does the initial ``Bind``,
+the simulator at the appropriate time.  In the case of ``TutorialApp`` you
+will find that ``TutorialApp::StartApplication`` does the initial ``Bind``,
 and ``Connect`` on the socket, and then starts data flowing by calling
-``MyApp::SendPacket``.  ``MyApp::StopApplication`` stops generating
+``TutorialApp::SendPacket``.  ``TutorialApp::StopApplication`` stops generating
 packets by cancelling any pending send events then closes the socket.
 
 One of the nice things about |ns3| is that you can completely ignore
@@ -1566,12 +1592,12 @@
 Take a look at ``src/network/model/node-list.cc`` and search for
 ``NodeList::Add``.  The public static implementation calls into a
 private implementation called ``NodeListPriv::Add``.  This is a
-relatively common idom in |ns3|.  So, take a look at
+relatively common idiom in |ns3|.  So, take a look at
 ``NodeListPriv::Add``.  There you will find,
 
 ::
 
-  Simulator::ScheduleWithContext (index, TimeStep (0), &Node::Initialize, node);
+  Simulator::ScheduleWithContext(index, TimeStep(0), &Node::Initialize, node);
 
 This tells you that whenever a Node is created in a simulation, as
 a side-effect, a call to that node's ``Initialize`` method is
@@ -1616,14 +1642,15 @@
 look at ``src/network/model/application.cc`` and you will find::
 
   void
-  Application::DoInitialize (void)
+  Application::DoInitialize()
   {
-    m_startEvent = Simulator::Schedule (m_startTime, &Application::StartApplication, this);
-    if (m_stopTime != TimeStep (0))
+    NS_LOG_FUNCTION(this);
+    m_startEvent = Simulator::Schedule(m_startTime, &Application::StartApplication, this);
+    if (m_stopTime != TimeStep(0))
       {
-        m_stopEvent = Simulator::Schedule (m_stopTime, &Application::StopApplication, this);
+        m_stopEvent = Simulator::Schedule(m_stopTime, &Application::StopApplication, this);
       }
-    Object::DoInitialize ();
+    Object::DoInitialize();
   }
 
 Here, we finally come to the end of the trail.  If you have kept it
@@ -1652,27 +1679,27 @@
 This has been another fairly long journey, but it only has to be made
 once, and you now understand another very deep piece of |ns3|.
 
-The MyApp Application
-~~~~~~~~~~~~~~~~~~~~~
+The TutorialApp Application
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The ``MyApp`` ``Application`` needs a constructor and a destructor, of
+The ``TutorialApp`` ``Application`` needs a constructor and a destructor, of
 course::
 
-  MyApp::MyApp ()
-    : m_socket (0),
-      m_peer (),
-      m_packetSize (0),
-      m_nPackets (0),
-      m_dataRate (0),
-      m_sendEvent (),
-      m_running (false),
-      m_packetsSent (0)
+  TutorialApp::TutorialApp()
+      : m_socket(nullptr),
+        m_peer(),
+        m_packetSize(0),
+        m_nPackets(0),
+        m_dataRate(0),
+        m_sendEvent(),
+        m_running(false),
+        m_packetsSent(0)
   {
   }
 
-  MyApp::~MyApp()
+  TutorialApp::~TutorialApp()
   {
-    m_socket = 0;
+      m_socket = nullptr;
   }
 
 The existence of the next bit of code is the whole reason why we wrote
@@ -1681,14 +1708,17 @@
 ::
 
   void
-  MyApp::Setup (Ptr<Socket> socket, Address address, uint32_t packetSize,
-                       uint32_t nPackets, DataRate dataRate)
-  {
-    m_socket = socket;
-    m_peer = address;
-    m_packetSize = packetSize;
-    m_nPackets = nPackets;
-    m_dataRate = dataRate;
+  TutorialApp::Setup(Ptr<Socket> socket,
+                     Address address,
+                     uint32_t packetSize,
+                     uint32_t nPackets,
+                     DataRate dataRate)
+  {
+      m_socket = socket;
+      m_peer = address;
+      m_packetSize = packetSize;
+      m_nPackets = nPackets;
+      m_dataRate = dataRate;
   }
 
 This code should be pretty self-explanatory.  We are just initializing
@@ -1702,13 +1732,13 @@
 ::
 
   void
-  MyApp::StartApplication (void)
+  TutorialApp::StartApplication()
   {
-    m_running = true;
-    m_packetsSent = 0;
-    m_socket->Bind ();
-    m_socket->Connect (m_peer);
-    SendPacket ();
+      m_running = true;
+      m_packetsSent = 0;
+      m_socket->Bind();
+      m_socket->Connect(m_peer);
+      SendPacket();
   }
 
 The above code is the overridden implementation
@@ -1731,18 +1761,18 @@
 ::
 
   void
-  MyApp::StopApplication (void)
+  TutorialApp::StopApplication()
   {
-    m_running = false;
+      m_running = false;
 
-    if (m_sendEvent.IsRunning ())
+      if (m_sendEvent.IsRunning())
       {
-        Simulator::Cancel (m_sendEvent);
+          Simulator::Cancel(m_sendEvent);
       }
 
-    if (m_socket)
+      if (m_socket)
       {
-        m_socket->Close ();
+          m_socket->Close();
       }
   }
 
@@ -1765,14 +1795,14 @@
 ::
 
   void
-  MyApp::SendPacket (void)
+  TutorialApp::SendPacket()
   {
-    Ptr<Packet> packet = Create<Packet> (m_packetSize);
-    m_socket->Send (packet);
+      Ptr<Packet> packet = Create<Packet>(m_packetSize);
+      m_socket->Send(packet);
 
-    if (++m_packetsSent < m_nPackets)
+      if (++m_packetsSent < m_nPackets)
       {
-        ScheduleTx ();
+          ScheduleTx();
       }
   }
 
@@ -1788,12 +1818,12 @@
 ::
 
   void
-  MyApp::ScheduleTx (void)
+  TutorialApp::ScheduleTx()
   {
-    if (m_running)
+      if (m_running)
       {
-        Time tNext (Seconds (m_packetSize * 8 / static_cast<double> (m_dataRate.GetBitRate ())));
-        m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this);
+          Time tNext(Seconds(m_packetSize * 8 / static_cast<double>(m_dataRate.GetBitRate())));
+          m_sendEvent = Simulator::Schedule(tNext, &TutorialApp::SendPacket, this);
       }
   }
 
@@ -1817,9 +1847,9 @@
 code implements the corresponding trace sink::
 
   static void
-  CwndChange (uint32_t oldCwnd, uint32_t newCwnd)
+  CwndChange(uint32_t oldCwnd, uint32_t newCwnd)
   {
-    NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd);
+      NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "\t" << newCwnd);
   }
 
 This should be very familiar to you now, so we won't dwell on the
@@ -1836,9 +1866,9 @@
 ::
 
   static void
-  RxDrop (Ptr<const Packet> p)
+  RxDrop(Ptr<const Packet> p)
   {
-    NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
+      NS_LOG_UNCOND("RxDrop at " << Simulator::Now().GetSeconds());
   }
 
 This trace sink will be connected to the "PhyRxDrop" trace source of
@@ -1850,7 +1880,7 @@
 ``PointToPointNetDevice::m_phyRxDropTrace``.  If you then look in
 ``src/point-to-point/model/point-to-point-net-device.h`` for this
 member variable, you will find that it is declared as a
-``TracedCallback<Ptr<const Packet> >``.  This should tell you that the
+``TracedCallback<Ptr<const Packet>>``.  This should tell you that the
 callback target should be a function that returns void and takes a
 single parameter which is a ``Ptr<const Packet>`` (assuming we use
 ``ConnectWithoutContext``) -- just what we have above.
@@ -1858,20 +1888,45 @@
 Main Program
 ~~~~~~~~~~~~
 
-The following code should be very familiar to you by now::
+The main function starts off by configuring the TCP type to use a legacy
+``NewReno`` congestion control variant, with what is called the ``classic''
+TCP loss recovery mechanism.  When this tutorial program was originally
+written, these were the default TCP configurations, but over time,
+|ns3| TCP has evolved to use the current Linux TCP defaults of ``Cubic``
+and ``Prr'' loss recovery.  The first statements also configure the
+command-line argument processing.
+
+::
 
   int
-  main (int argc, char *argv[])
+  main(int argc, char* argv[])
   {
-    NodeContainer nodes;
-    nodes.Create (2);
+      CommandLine cmd(__FILE__);
+      cmd.Parse(argc, argv);
+
+      // In the following three lines, TCP NewReno is used as the congestion
+      // control algorithm, the initial congestion window of a TCP connection is
+      // set to 1 packet, and the classic fast recovery algorithm is used. Note
+      // that this configuration is used only to demonstrate how TCP parameters
+      // can be configured in ns-3. Otherwise, it is recommended to use the default
+      // settings of TCP in ns-3.
+      Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue("ns3::TcpNewReno"));
+      Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(1));
+      Config::SetDefault("ns3::TcpL4Protocol::RecoveryType",
+                         TypeIdValue(TypeId::LookupByName("ns3::TcpClassicRecovery")));
+
+
+The following code should be very familiar to you by now::
 
-    PointToPointHelper pointToPoint;
-    pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-    pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+      NodeContainer nodes;
+      nodes.Create(2);
 
-    NetDeviceContainer devices;
-    devices = pointToPoint.Install (nodes);
+      PointToPointHelper pointToPoint;
+      pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+      pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
+
+      NetDeviceContainer devices;
+      devices = pointToPoint.Install(nodes);
 
 This creates two nodes with a point-to-point channel between them,
 just as shown in the illustration at the start of the file.
@@ -1890,9 +1945,9 @@
 
 ::
 
-  Ptr<RateErrorModel> em = CreateObject<RateErrorModel> ();
-  em->SetAttribute ("ErrorRate", DoubleValue (0.00001));
-  devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em));
+    Ptr<RateErrorModel> em = CreateObject<RateErrorModel>();
+    em->SetAttribute("ErrorRate", DoubleValue(0.00001));
+    devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em));
 
 The above code instantiates a ``RateErrorModel`` Object, and we set
 the "ErrorRate" ``Attribute`` to the desired value.  We then set the
@@ -1902,12 +1957,12 @@
 
 ::
 
-  InternetStackHelper stack;
-  stack.Install (nodes);
+    InternetStackHelper stack;
+    stack.Install(nodes);
 
-  Ipv4AddressHelper address;
-  address.SetBase ("10.1.1.0", "255.255.255.252");
-  Ipv4InterfaceContainer interfaces = address.Assign (devices);
+    Ipv4AddressHelper address;
+    address.SetBase("10.1.1.0", "255.255.255.252");
+    Ipv4InterfaceContainer interfaces = address.Assign(devices);
 
 The above code should be familiar.  It installs internet stacks on our
 two nodes and creates interfaces and assigns IP addresses for the
@@ -1919,20 +1974,20 @@
 
 ::
 
-  uint16_t sinkPort = 8080;
-  Address sinkAddress (InetSocketAddress(interfaces.GetAddress (1), sinkPort));
-  PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory",
-    InetSocketAddress (Ipv4Address::GetAny (), sinkPort));
-  ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1));
-  sinkApps.Start (Seconds (0.));
-  sinkApps.Stop (Seconds (20.));
+    uint16_t sinkPort = 8080;
+    Address sinkAddress(InetSocketAddress(interfaces.GetAddress(1), sinkPort));
+    PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory",
+                                      InetSocketAddress(Ipv4Address::GetAny(), sinkPort));
+    ApplicationContainer sinkApps = packetSinkHelper.Install(nodes.Get(1));
+    sinkApps.Start(Seconds(0.));
+    sinkApps.Stop(Seconds(20.));
 
 This should all be familiar, with the exception of,
 
 ::
 
-  PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory",
-    InetSocketAddress (Ipv4Address::GetAny (), sinkPort));
+    PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory",
+                                      InetSocketAddress(Ipv4Address::GetAny(), sinkPort));
 
 This code instantiates a ``PacketSinkHelper`` and tells it to create
 sockets using the class ``ns3::TcpSocketFactory``.  This class
@@ -1952,10 +2007,8 @@
 
 ::
 
-  Ptr<Socket> ns3TcpSocket = Socket::CreateSocket (nodes.Get (0),
-    TcpSocketFactory::GetTypeId ());
-  ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow",
-    MakeCallback (&CwndChange));
+    Ptr<Socket> ns3TcpSocket = Socket::CreateSocket(nodes.Get(0), TcpSocketFactory::GetTypeId());
+    ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow", MakeCallback(&CwndChange));
 
 The first statement calls the static member function
 ``Socket::CreateSocket`` and provides a Node and an explicit
@@ -1975,19 +2028,19 @@
 ``Application`` so we are going to have to create and install it
 "manually".  This is actually quite easy::
 
-  Ptr<MyApp> app = CreateObject<MyApp> ();
-  app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps"));
-  nodes.Get (0)->AddApplication (app);
-  app->Start (Seconds (1.));
-  app->Stop (Seconds (20.));
+    Ptr<TutorialApp> app = CreateObject<TutorialApp>();
+    app->Setup(ns3TcpSocket, sinkAddress, 1040, 1000, DataRate("1Mbps"));
+    nodes.Get(0)->AddApplication(app);
+    app->Start(Seconds(1.));
+    app->Stop(Seconds(20.));
 
-The first line creates an ``Object`` of type ``MyApp`` -- our
+The first line creates an ``Object`` of type ``TutorialApp`` -- our
 ``Application``.  The second line tells the ``Application`` what
 ``Socket`` to use, what address to connect to, how much data to send
 at each send event, how many send events to generate and the rate at
 which to produce data from those events.
 
-Next, we manually add the ``MyApp Application`` to the source Node and
+Next, we manually add the ``TutorialApp Application`` to the source Node and
 explicitly call the ``Start`` and ``Stop`` methods on the
 ``Application`` to tell it when to start and stop doing its thing.
 
@@ -1996,7 +2049,7 @@
 
 ::
 
-  devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop));
+    devices.Get(1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback(&RxDrop));
 
 It should now be obvious that we are getting a reference to the
 receiving ``Node NetDevice`` from its container and connecting the
@@ -2008,9 +2061,9 @@
 
 ::
 
-    Simulator::Stop (Seconds(20));
-    Simulator::Run ();
-    Simulator::Destroy ();
+    Simulator::Stop(Seconds(20));
+    Simulator::Run();
+    Simulator::Destroy();
 
     return 0;
   }
@@ -2029,7 +2082,7 @@
 ++++++++++++++++++++
 
 Since we have provided the file ``fifth.cc`` for you, if you have
-built your distribution (in debug mode since it uses ``NS_LOG`` -- recall
+built your distribution (in debug or default mode since it uses ``NS_LOG`` -- recall
 that optimized builds optimize out ``NS_LOG``) it will be waiting for you
 to run.
 
@@ -2061,7 +2114,7 @@
 Now edit up "cwnd.dat" in your favorite editor and remove the ns3
 build status and drop lines, leaving only the traced data (you could
 also comment out the ``TraceConnectWithoutContext("PhyRxDrop",
-MakeCallback (&RxDrop));`` in the script to get rid of the drop prints
+MakeCallback(&RxDrop));`` in the script to get rid of the drop prints
 just as easily.
 
 You can now run gnuplot (if you have it installed) and tell it to
@@ -2119,16 +2172,16 @@
 ::
 
   static void
-  CwndChange (Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
+  CwndChange(Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
   {
-    NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd);
-    *stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
+    NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "\t" << newCwnd);
+    *stream->GetStream() << Simulator::Now().GetSeconds() << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
   }
 
   static void
-  RxDrop (Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
+  RxDrop(Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
   {
-    NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
+    NS_LOG_UNCOND("RxDrop at " << Simulator::Now().GetSeconds());
     file->Write(Simulator::Now(), p);
   }
 
@@ -2144,12 +2197,11 @@
 that obey value semantics.  Further notice that we have added the
 following line in the ``CwndChange`` trace sink implementation::
 
-  *stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
+  *stream->GetStream() << Simulator::Now().GetSeconds() << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
 
-This would be very familiar code if you replaced ``*stream->GetStream
-()`` with ``std::cout``, as in::
+This would be very familiar code if you replaced ``*stream->GetStream()`` with ``std::cout``, as in::
 
-  std::cout << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
+  std::cout << Simulator::Now().GetSeconds() << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
 
 This illustrates that the ``Ptr<OutputStreamWrapper>`` is really just
 carrying around a ``std::ofstream`` for you, and you can use it here
@@ -2168,14 +2220,14 @@
 to do just that::
 
   AsciiTraceHelper asciiTraceHelper;
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd");
-  ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream));
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("sixth.cwnd");
+  ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow", MakeBoundCallback(&CwndChange, stream));
 
   ...
 
   PcapHelper pcapHelper;
-  Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", std::ios::out, PcapHelper::DLT_PPP);
-  devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file));
+  Ptr<PcapFileWrapper> file = pcapHelper.CreateFile("sixth.pcap", std::ios::out, PcapHelper::DLT_PPP);
+  devices.Get(1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback(&RxDrop, file));
 
 In the first section of the code snippet above, we are creating the
 ASCII trace file, creating an object responsible for managing it and
@@ -2208,7 +2260,7 @@
 
 ::
 
-  Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap",
+  Ptr<PcapFileWrapper> file = pcapHelper.CreateFile("sixth.pcap",
   "w", PcapHelper::DLT_PPP);
 
 creates a PCAP file named "sixth.pcap" with file mode "w".  This means
@@ -2302,7 +2354,7 @@
 into your plot program.  There are no extraneous prints in the file,
 no parsing or editing is required.
 
-Since "sixth.pcap" is a PCAP file, you can fiew it with ``tcpdump``.
+Since "sixth.pcap" is a PCAP file, you can view it with ``tcpdump``.
 
 .. sourcecode:: bash
 
@@ -2325,32 +2377,32 @@
 and we did this in only 18 lines of code::
 
   static void
-  CwndChange (Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
+  CwndChange(Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
   {
-    NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd);
-    *stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
+    NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "\t" << newCwnd);
+    *stream->GetStream() << Simulator::Now().GetSeconds() << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
   }
 
   ...
 
   AsciiTraceHelper asciiTraceHelper;
-  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd");
-  ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream));
+  Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("sixth.cwnd");
+  ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow", MakeBoundCallback(&CwndChange, stream));
 
   ...
 
   static void
-  RxDrop (Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
+  RxDrop(Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
   {
-    NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
+    NS_LOG_UNCOND("RxDrop at " << Simulator::Now().GetSeconds());
     file->Write(Simulator::Now(), p);
   }
 
   ...
 
   PcapHelper pcapHelper;
-  Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", "w", PcapHelper::DLT_PPP);
-  devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file));
+  Ptr<PcapFileWrapper> file = pcapHelper.CreateFile("sixth.pcap", "w", PcapHelper::DLT_PPP);
+  devices.Get(1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback(&RxDrop, file));
 
 Trace Helpers
 *************
@@ -2365,10 +2417,10 @@
 
 ::
 
-  pointToPoint.EnablePcapAll ("second");
-  pointToPoint.EnablePcap ("second", p2pNodes.Get (0)->GetId (), 0);
-  csma.EnablePcap ("third", csmaDevices.Get (0), true);
-  pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));
+  pointToPoint.EnablePcapAll("second");
+  pointToPoint.EnablePcap("second", p2pNodes.Get(0)->GetId(), 0);
+  csma.EnablePcap("third", csmaDevices.Get(0), true);
+  pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));
 
 What may not be obvious, though, is that there is a consistent model
 for all of the trace-related methods found in the system.  We will now
@@ -2433,7 +2485,7 @@
 
 ::
 
-  virtual void EnablePcapInternal (std::string prefix, Ptr<NetDevice> nd, bool promiscuous, bool explicitFilename) = 0;
+  virtual void EnablePcapInternal(std::string prefix, Ptr<NetDevice> nd, bool promiscuous, bool explicitFilename) = 0;
 
 The signature of this method reflects the device-centric view of the
 situation at this level.  All of the public methods inherited from
@@ -2443,7 +2495,7 @@
 
 ::
 
-  void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
 
 will call the device implementation of ``EnablePcapInternal``
 directly.  All other public PCAP tracing methods build on this
@@ -2458,12 +2510,12 @@
 
 ::
 
-  void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
-  void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
-  void EnablePcap (std::string prefix, NetDeviceContainer d, bool promiscuous = false);
-  void EnablePcap (std::string prefix, NodeContainer n, bool promiscuous = false);
-  void EnablePcap (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool promiscuous = false);
-  void EnablePcapAll (std::string prefix, bool promiscuous = false);
+  void EnablePcap(std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, NetDeviceContainer d, bool promiscuous = false);
+  void EnablePcap(std::string prefix, NodeContainer n, bool promiscuous = false);
+  void EnablePcap(std::string prefix, uint32_t nodeid, uint32_t deviceid, bool promiscuous = false);
+  void EnablePcapAll(std::string prefix, bool promiscuous = false);
 
 In each of the methods shown above, there is a default parameter
 called ``promiscuous`` that defaults to ``false``.  This parameter
@@ -2476,7 +2528,7 @@
 
   Ptr<NetDevice> nd;
   ...
-  helper.EnablePcap ("prefix", nd, true);
+  helper.EnablePcap("prefix", nd, true);
 
 will enable promiscuous mode captures on the ``NetDevice`` specified
 by ``nd``.
@@ -2497,7 +2549,7 @@
 
     Ptr<NetDevice> nd;
     ...
-    helper.EnablePcap ("prefix", nd);
+    helper.EnablePcap("prefix", nd);
 
 * You can enable PCAP tracing on a particular node/net-device pair by
   providing a ``std::string`` representing an object name service string
@@ -2507,10 +2559,10 @@
 
   ::
 
-    Names::Add ("server" ...);
-    Names::Add ("server/eth0" ...);
+    Names::Add("server" ...);
+    Names::Add("server/eth0" ...);
     ...
-    helper.EnablePcap ("prefix", "server/ath0");
+    helper.EnablePcap("prefix", "server/ath0");
 
 * You can enable PCAP tracing on a collection of node/net-device pairs
   by providing a ``NetDeviceContainer``.  For each ``NetDevice`` in the
@@ -2523,7 +2575,7 @@
 
     NetDeviceContainer d = ...;
     ...
-    helper.EnablePcap ("prefix", d);
+    helper.EnablePcap("prefix", d);
 
 * You can enable PCAP tracing on a collection of node/net-device pairs
   by providing a ``NodeContainer``.  For each Node in the
@@ -2536,7 +2588,7 @@
 
     NodeContainer n;
     ...
-    helper.EnablePcap ("prefix", n);
+    helper.EnablePcap("prefix", n);
 
 * You can enable PCAP tracing on the basis of Node ID and device ID as
   well as with explicit ``Ptr``.  Each Node in the system has an
@@ -2545,14 +2597,14 @@
 
   ::
 
-    helper.EnablePcap ("prefix", 21, 1);
+    helper.EnablePcap("prefix", 21, 1);
 
 * Finally, you can enable PCAP tracing for all devices in the system,
   with the same type as that managed by the device helper.
 
   ::
 
-    helper.EnablePcapAll ("prefix");
+    helper.EnablePcapAll("prefix");
 
 Filenames
 #########
@@ -2580,8 +2632,8 @@
 
 ::
 
-  void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
-  void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
+  void EnablePcap(std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
 
 have a default parameter called ``explicitFilename``.  When set to
 true, this parameter disables the automatic filename completion
@@ -2595,7 +2647,7 @@
 
   Ptr<NetDevice> nd;
   ...
-  helper.EnablePcap ("my-pcap-file.pcap", nd, true, true);
+  helper.EnablePcap("my-pcap-file.pcap", nd, true, true);
 
 The first ``true`` parameter enables promiscuous mode traces and the
 second tells the helper to interpret the ``prefix`` parameter as a
@@ -2616,10 +2668,10 @@
 
 ::
 
-  virtual void EnableAsciiInternal (Ptr<OutputStreamWrapper> stream,
-                                    std::string prefix,
-                                    Ptr<NetDevice> nd,
-                                    bool explicitFilename) = 0;
+  virtual void EnableAsciiInternal(Ptr<OutputStreamWrapper> stream,
+                                   std::string prefix,
+                                   Ptr<NetDevice> nd,
+                                   bool explicitFilename) = 0;
 
 
 The signature of this method reflects the device-centric view of the
@@ -2632,8 +2684,8 @@
 
 ::
 
-  void EnableAscii (std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
+  void EnableAscii(std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
 
 
 will call the device implementation of ``EnableAsciiInternal``
@@ -2650,23 +2702,23 @@
 
 ::
 
-  void EnableAscii (std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
+  void EnableAscii(std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
 
-  void EnableAscii (std::string prefix, std::string ndName, bool explicitFilename = false);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, std::string ndName);
+  void EnableAscii(std::string prefix, std::string ndName, bool explicitFilename = false);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, std::string ndName);
 
-  void EnableAscii (std::string prefix, NetDeviceContainer d);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, NetDeviceContainer d);
+  void EnableAscii(std::string prefix, NetDeviceContainer d);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, NetDeviceContainer d);
 
-  void EnableAscii (std::string prefix, NodeContainer n);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, NodeContainer n);
+  void EnableAscii(std::string prefix, NodeContainer n);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, NodeContainer n);
 
-  void EnableAsciiAll (std::string prefix);
-  void EnableAsciiAll (Ptr<OutputStreamWrapper> stream);
+  void EnableAsciiAll(std::string prefix);
+  void EnableAsciiAll(Ptr<OutputStreamWrapper> stream);
 
-  void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
-  void EnableAscii (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t deviceid);
+  void EnableAscii(std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
+  void EnableAscii(Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t deviceid);
 
 You are encouraged to peruse the API Documentation for class
 ``AsciiTraceHelperForDevice`` to find the details of these methods;
@@ -2690,7 +2742,7 @@
 
   Ptr<NetDevice> nd;
   ...
-  helper.EnableAscii ("prefix", nd);
+  helper.EnableAscii("prefix", nd);
 
 * The first four methods also include a default parameter called
   ``explicitFilename`` that operate similar to equivalent parameters
@@ -2710,10 +2762,10 @@
     Ptr<NetDevice> nd1;
     Ptr<NetDevice> nd2;
     ...
-    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
     ...
-    helper.EnableAscii (stream, nd1);
-    helper.EnableAscii (stream, nd2);
+    helper.EnableAscii(stream, nd1);
+    helper.EnableAscii(stream, nd2);
 
 
   In this case, trace contexts *are* written to the ASCII trace file
@@ -2730,13 +2782,13 @@
 
   ::
 
-    Names::Add ("client" ...);
-    Names::Add ("client/eth0" ...);
-    Names::Add ("server" ...);
-    Names::Add ("server/eth0" ...);
+    Names::Add("client" ...);
+    Names::Add("client/eth0" ...);
+    Names::Add("server" ...);
+    Names::Add("server/eth0" ...);
     ...
-    helper.EnableAscii ("prefix", "client/eth0");
-    helper.EnableAscii ("prefix", "server/eth0");
+    helper.EnableAscii("prefix", "client/eth0");
+    helper.EnableAscii("prefix", "server/eth0");
 
   This would result in two files named ``prefix-client-eth0.tr`` and
   ``prefix-server-eth0.tr`` with traces for each device in the
@@ -2744,15 +2796,15 @@
   are overloaded to take a stream wrapper, you can use that form as
   well::
 
-    Names::Add ("client" ...);
-    Names::Add ("client/eth0" ...);
-    Names::Add ("server" ...);
-    Names::Add ("server/eth0" ...);
+    Names::Add("client" ...);
+    Names::Add("client/eth0" ...);
+    Names::Add("server" ...);
+    Names::Add("server/eth0" ...);
     ...
-    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
     ...
-    helper.EnableAscii (stream, "client/eth0");
-    helper.EnableAscii (stream, "server/eth0");
+    helper.EnableAscii(stream, "client/eth0");
+    helper.EnableAscii(stream, "server/eth0");
 
   This would result in a single trace file called
   ``trace-file-name.tr`` that contains all of the trace events for
@@ -2770,7 +2822,7 @@
 
     NetDeviceContainer d = ...;
     ...
-    helper.EnableAscii ("prefix", d);
+    helper.EnableAscii("prefix", d);
 
   This would result in a number of ASCII trace files being created,
   each of which follows the ``<prefix>-<node id>-<device id>.tr``
@@ -2781,9 +2833,9 @@
 
     NetDeviceContainer d = ...;
     ...
-    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
     ...
-    helper.EnableAscii (stream, d);
+    helper.EnableAscii(stream, d);
 
 * You can enable ASCII tracing on a collection of (node, net-device)
   pairs by providing a ``NodeContainer``.  For each Node in the
@@ -2796,7 +2848,7 @@
 
     NodeContainer n;
     ...
-    helper.EnableAscii ("prefix", n);
+    helper.EnableAscii("prefix", n);
 
   This would result in a number of ASCII trace files being created,
   each of which follows the ``<prefix>-<node id>-<device id>.tr``
@@ -2810,7 +2862,7 @@
 
   ::
 
-    helper.EnableAscii ("prefix", 21, 1);
+    helper.EnableAscii("prefix", 21, 1);
 
   Of course, the traces can be combined into a single file as shown
   above.
@@ -2820,7 +2872,7 @@
 
   ::
 
-    helper.EnableAsciiAll ("prefix");
+    helper.EnableAsciiAll("prefix");
 
   This would result in a number of ASCII trace files being created,
   one for every device in the system of the type managed by the
@@ -2886,10 +2938,10 @@
 
 ::
 
-  virtual void EnablePcapIpv4Internal (std::string prefix,
-                                       Ptr<Ipv4> ipv4,
-                                       uint32_t interface,
-                                       bool explicitFilename) = 0;
+  virtual void EnablePcapIpv4Internal(std::string prefix,
+                                      Ptr<Ipv4> ipv4,
+                                      uint32_t interface,
+                                      bool explicitFilename) = 0;
 
 The signature of this method reflects the protocol and
 interface-centric view of the situation at this level.  All of the
@@ -2899,7 +2951,7 @@
 
 ::
 
-  void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
+  void EnablePcapIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
 
 
 will call the device implementation of ``EnablePcapIpv4Internal``
@@ -2920,12 +2972,12 @@
 
 Note that just like in the device version, there are six methods::
 
-  void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
-  void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
-  void EnablePcapIpv4 (std::string prefix, Ipv4InterfaceContainer c);
-  void EnablePcapIpv4 (std::string prefix, NodeContainer n);
-  void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, uint32_t interface, bool explicitFilename);
-  void EnablePcapIpv4All (std::string prefix);
+  void EnablePcapIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
+  void EnablePcapIpv4(std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
+  void EnablePcapIpv4(std::string prefix, Ipv4InterfaceContainer c);
+  void EnablePcapIpv4(std::string prefix, NodeContainer n);
+  void EnablePcapIpv4(std::string prefix, uint32_t nodeid, uint32_t interface, bool explicitFilename);
+  void EnablePcapIpv4All(std::string prefix);
 
 You are encouraged to peruse the API Documentation for class
 ``PcapHelperForIpv4`` to find the details of these methods; but to
@@ -2937,9 +2989,9 @@
 
   ::
 
-    Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
+    Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
     ...
-    helper.EnablePcapIpv4 ("prefix", ipv4, 0);
+    helper.EnablePcapIpv4("prefix", ipv4, 0);
 
 * You can enable PCAP tracing on a particular node/net-device pair by
   providing a ``std::string`` representing an object name service string
@@ -2948,9 +3000,9 @@
 
   ::
 
-    Names::Add ("serverIPv4" ...);
+    Names::Add("serverIPv4" ...);
     ...
-    helper.EnablePcapIpv4 ("prefix", "serverIpv4", 1);
+    helper.EnablePcapIpv4("prefix", "serverIpv4", 1);
 
 * You can enable PCAP tracing on a collection of protocol/interface
   pairs by providing an ``Ipv4InterfaceContainer``.  For each ``Ipv4`` /
@@ -2963,13 +3015,13 @@
 
     NodeContainer nodes;
     ...
-    NetDeviceContainer devices = deviceHelper.Install (nodes);
+    NetDeviceContainer devices = deviceHelper.Install(nodes);
     ...
     Ipv4AddressHelper ipv4;
-    ipv4.SetBase ("10.1.1.0", "255.255.255.0");
-    Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
+    ipv4.SetBase("10.1.1.0", "255.255.255.0");
+    Ipv4InterfaceContainer interfaces = ipv4.Assign(devices);
     ...
-    helper.EnablePcapIpv4 ("prefix", interfaces);
+    helper.EnablePcapIpv4("prefix", interfaces);
 
 * You can enable PCAP tracing on a collection of protocol/interface
   pairs by providing a ``NodeContainer``.  For each Node in the
@@ -2981,7 +3033,7 @@
 
     NodeContainer n;
     ...
-    helper.EnablePcapIpv4 ("prefix", n);
+    helper.EnablePcapIpv4("prefix", n);
 
 * You can enable PCAP tracing on the basis of Node ID and interface as
   well.  In this case, the node-id is translated to a ``Ptr<Node>`` and
@@ -2990,7 +3042,7 @@
 
   ::
 
-    helper.EnablePcapIpv4 ("prefix", 21, 1);
+    helper.EnablePcapIpv4("prefix", 21, 1);
 
 * Finally, you can enable PCAP tracing for all interfaces in the
   system, with associated protocol being the same type as that managed
@@ -2998,7 +3050,7 @@
 
   ::
 
-    helper.EnablePcapIpv4All ("prefix");
+    helper.EnablePcapIpv4All("prefix");
 
 Filenames
 #########
@@ -3058,11 +3110,11 @@
 
 ::
 
-  virtual void EnableAsciiIpv4Internal (Ptr<OutputStreamWrapper> stream,
-                                        std::string prefix,
-                                        Ptr<Ipv4> ipv4,
-                                        uint32_t interface,
-                                        bool explicitFilename) = 0;
+  virtual void EnableAsciiIpv4Internal(Ptr<OutputStreamWrapper> stream,
+                                       std::string prefix,
+                                       Ptr<Ipv4> ipv4,
+                                       uint32_t interface,
+                                       bool explicitFilename) = 0;
 
 The signature of this method reflects the protocol- and
 interface-centric view of the situation at this level; and also the
@@ -3074,8 +3126,8 @@
 
 ::
 
-  void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
 
 
 will call the device implementation of ``EnableAsciiIpv4Internal``
@@ -3092,23 +3144,23 @@
 
 ::
 
-  void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
 
-  void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, std::string ipv4Name, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, std::string ipv4Name, uint32_t interface);
 
-  void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ipv4InterfaceContainer c);
+  void EnableAsciiIpv4(std::string prefix, Ipv4InterfaceContainer c);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, Ipv4InterfaceContainer c);
 
-  void EnableAsciiIpv4 (std::string prefix, NodeContainer n);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, NodeContainer n);
+  void EnableAsciiIpv4(std::string prefix, NodeContainer n);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, NodeContainer n);
 
-  void EnableAsciiIpv4All (std::string prefix);
-  void EnableAsciiIpv4All (Ptr<OutputStreamWrapper> stream);
+  void EnableAsciiIpv4All(std::string prefix);
+  void EnableAsciiIpv4All(Ptr<OutputStreamWrapper> stream);
 
-  void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
-  void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t interface);
+  void EnableAsciiIpv4(std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
+  void EnableAsciiIpv4(Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t interface);
 
 You are encouraged to peruse the API Documentation for class
 ``PcapAndAsciiHelperForIpv4`` to find the details of these methods;
@@ -3132,7 +3184,7 @@
 
     Ptr<Ipv4> ipv4;
     ...
-    helper.EnableAsciiIpv4 ("prefix", ipv4, 1);
+    helper.EnableAsciiIpv4("prefix", ipv4, 1);
 
   In this case, no trace contexts are written to the ASCII trace file
   since they would be redundant.  The system will pick the file name to
@@ -3144,13 +3196,13 @@
   using an object to refer to a single file.  We have already something
   similar to this in the "cwnd" example above::
 
-    Ptr<Ipv4> protocol1 = node1->GetObject<Ipv4> ();
-    Ptr<Ipv4> protocol2 = node2->GetObject<Ipv4> ();
+    Ptr<Ipv4> protocol1 = node1->GetObject<Ipv4>();
+    Ptr<Ipv4> protocol2 = node2->GetObject<Ipv4>();
     ...
-    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
     ...
-    helper.EnableAsciiIpv4 (stream, protocol1, 1);
-    helper.EnableAsciiIpv4 (stream, protocol2, 1);
+    helper.EnableAsciiIpv4(stream, protocol1, 1);
+    helper.EnableAsciiIpv4(stream, protocol2, 1);
 
   In this case, trace contexts are written to the ASCII trace file since
   they are required to disambiguate traces from the two interfaces.
@@ -3166,24 +3218,24 @@
 
   ::
 
-    Names::Add ("node1Ipv4" ...);
-    Names::Add ("node2Ipv4" ...);
+    Names::Add("node1Ipv4" ...);
+    Names::Add("node2Ipv4" ...);
     ...
-    helper.EnableAsciiIpv4 ("prefix", "node1Ipv4", 1);
-    helper.EnableAsciiIpv4 ("prefix", "node2Ipv4", 1);
+    helper.EnableAsciiIpv4("prefix", "node1Ipv4", 1);
+    helper.EnableAsciiIpv4("prefix", "node2Ipv4", 1);
 
   This would result in two files named "prefix-nnode1Ipv4-i1.tr" and
   "prefix-nnode2Ipv4-i1.tr" with traces for each interface in the
   respective trace file.  Since all of the EnableAscii functions are
   overloaded to take a stream wrapper, you can use that form as well::
 
-    Names::Add ("node1Ipv4" ...);
-    Names::Add ("node2Ipv4" ...);
+    Names::Add("node1Ipv4" ...);
+    Names::Add("node2Ipv4" ...);
     ...
-    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
     ...
-    helper.EnableAsciiIpv4 (stream, "node1Ipv4", 1);
-    helper.EnableAsciiIpv4 (stream, "node2Ipv4", 1);
+    helper.EnableAsciiIpv4(stream, "node1Ipv4", 1);
+    helper.EnableAsciiIpv4(stream, "node2Ipv4", 1);
 
   This would result in a single trace file called "trace-file-name.tr"
   that contains all of the trace events for both interfaces.  The events
@@ -3200,14 +3252,14 @@
 
     NodeContainer nodes;
     ...
-    NetDeviceContainer devices = deviceHelper.Install (nodes);
+    NetDeviceContainer devices = deviceHelper.Install(nodes);
     ...
     Ipv4AddressHelper ipv4;
-    ipv4.SetBase ("10.1.1.0", "255.255.255.0");
-    Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
+    ipv4.SetBase("10.1.1.0", "255.255.255.0");
+    Ipv4InterfaceContainer interfaces = ipv4.Assign(devices);
     ...
     ...
-    helper.EnableAsciiIpv4 ("prefix", interfaces);
+    helper.EnableAsciiIpv4("prefix", interfaces);
 
   This would result in a number of ASCII trace files being created, each
   of which follows the <prefix>-n<node id>-i<interface>.tr convention.
@@ -3216,15 +3268,15 @@
 
     NodeContainer nodes;
     ...
-    NetDeviceContainer devices = deviceHelper.Install (nodes);
+    NetDeviceContainer devices = deviceHelper.Install(nodes);
     ...
     Ipv4AddressHelper ipv4;
-    ipv4.SetBase ("10.1.1.0", "255.255.255.0");
-    Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
+    ipv4.SetBase("10.1.1.0", "255.255.255.0");
+    Ipv4InterfaceContainer interfaces = ipv4.Assign(devices);
     ...
-    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
+    Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("trace-file-name.tr");
     ...
-    helper.EnableAsciiIpv4 (stream, interfaces);
+    helper.EnableAsciiIpv4(stream, interfaces);
 
 * You can enable ASCII tracing on a collection of protocol/interface
   pairs by providing a ``NodeContainer``.  For each Node in the
@@ -3236,7 +3288,7 @@
 
     NodeContainer n;
     ...
-    helper.EnableAsciiIpv4 ("prefix", n);
+    helper.EnableAsciiIpv4("prefix", n);
 
   This would result in a number of ASCII trace files being created,
   each of which follows the <prefix>-<node id>-<device id>.tr
@@ -3250,7 +3302,7 @@
 
   ::
 
-    helper.EnableAsciiIpv4 ("prefix", 21, 1);
+    helper.EnableAsciiIpv4("prefix", 21, 1);
 
   Of course, the traces can be combined into a single file as shown
   above.
@@ -3261,7 +3313,7 @@
 
   ::
 
-    helper.EnableAsciiIpv4All ("prefix");
+    helper.EnableAsciiIpv4All("prefix");
 
   This would result in a number of ASCII trace files being created, one
   for every interface in the system related to a protocol of the type
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/doc/tutorial/source/tweaking.rst ns-3.38/doc/tutorial/source/tweaking.rst
--- ns-3.37/doc/tutorial/source/tweaking.rst	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/doc/tutorial/source/tweaking.rst	2023-03-16 20:54:23.564868500 -0700
@@ -350,7 +350,7 @@
 
 ::
 
-  NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");
+  NS_LOG_COMPONENT_DEFINE("FirstScriptExample");
 
 You now know that you can enable all of the logging for this component by
 setting the ``NS_LOG`` environment variable to the various levels.  Let's
@@ -363,14 +363,14 @@
 
 ::
 
-  NS_LOG_INFO ("Creating Topology");
+  NS_LOG_INFO("Creating Topology");
 
 right before the lines,
 
 ::
 
   NodeContainer nodes;
-  nodes.Create (2);
+  nodes.Create(2);
 
 Now build the script using ns3 and clear the ``NS_LOG`` variable to turn
 off the torrent of logging we previously enabled:
@@ -426,12 +426,12 @@
 ::
 
   int
-  main (int argc, char *argv[])
+  main(int argc, char *argv[])
   {
     ...
 
     CommandLine cmd;
-    cmd.Parse (argc, argv);
+    cmd.Parse(argc, argv);
 
     ...
   }
@@ -470,8 +470,8 @@
 ::
 
     PointToPointHelper pointToPoint;
-    pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-    pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+    pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+    pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
 
 and mentioned that ``DataRate`` was actually an ``Attribute`` of the
 ``PointToPointNetDevice``.  Let's use the command line argument parser
@@ -507,12 +507,12 @@
   ...
 
   NodeContainer nodes;
-  nodes.Create (2);
+  nodes.Create(2);
 
   PointToPointHelper pointToPoint;
 
   NetDeviceContainer devices;
-  devices = pointToPoint.Install (nodes);
+  devices = pointToPoint.Install(nodes);
 
   ...
 
@@ -680,13 +680,13 @@
 ::
 
   int
-  main (int argc, char *argv[])
+  main(int argc, char *argv[])
   {
     uint32_t nPackets = 1;
 
     CommandLine cmd;
     cmd.AddValue("nPackets", "Number of packets to echo", nPackets);
-    cmd.Parse (argc, argv);
+    cmd.Parse(argc, argv);
 
     ...
 
@@ -696,7 +696,7 @@
 
 ::
 
-  echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));
+  echoClient.SetAttribute("MaxPackets", UintegerValue(nPackets));
 
 Now if you run the script and provide the ``--PrintHelp`` argument, you
 should see your new ``User Argument`` listed in the help display.
@@ -778,7 +778,7 @@
 
   #include <iostream>
   ...
-  int main ()
+  int main()
   {
     ...
     std::cout << "The value of x is " << x << std::endl;
@@ -838,12 +838,12 @@
 
 Let's just jump right in and add some ASCII tracing output to our
 ``scratch/myfirst.cc`` script.  Right before the call to
-``Simulator::Run ()``, add the following lines of code:
+``Simulator::Run()``, add the following lines of code:
 
 ::
 
   AsciiTraceHelper ascii;
-  pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));
+  pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));
 
 Like in many other |ns3| idioms, this code uses a  helper object to
 help create ASCII traces.  The second line contains two nested method calls.
@@ -998,7 +998,7 @@
 
 ::
 
-  pointToPoint.EnablePcapAll ("myfirst");
+  pointToPoint.EnablePcapAll("myfirst");
 
 Go ahead and insert this line of code after the ASCII tracing code we just
 added to ``scratch/myfirst.cc``.  Notice that we only passed the string
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/energy/energy-model-example.cc ns-3.38/examples/energy/energy-model-example.cc
--- ns-3.37/examples/energy/energy-model-example.cc	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/examples/energy/energy-model-example.cc	2023-03-16 20:54:23.568868400 -0700
@@ -39,7 +39,7 @@
  * Print a received packet
  *
  * \param from sender address
- * \return a sting with the details of the packet: dst {IP, port}, time.
+ * \return a string with the details of the packet: dst {IP, port}, time.
  */
 static inline std::string
 PrintReceivedPacket(Address& from)
@@ -186,7 +186,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents();
+        WifiHelper::EnableLogComponents();
     }
     wifi.SetStandard(WIFI_STANDARD_80211b);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/energy/energy-model-with-harvesting-example.cc ns-3.38/examples/energy/energy-model-with-harvesting-example.cc
--- ns-3.37/examples/energy/energy-model-with-harvesting-example.cc	2022-11-01 17:37:24.439395000 -0700
+++ ns-3.38/examples/energy/energy-model-with-harvesting-example.cc	2023-03-16 20:54:23.568868400 -0700
@@ -68,7 +68,7 @@
  * Print a received packet
  *
  * \param from sender address
- * \return a sting with the details of the packet: dst {IP, port}, time.
+ * \return a string with the details of the packet: dst {IP, port}, time.
  */
 static inline std::string
 PrintReceivedPacket(Address& from)
@@ -234,7 +234,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents();
+        WifiHelper::EnableLogComponents();
     }
     wifi.SetStandard(WIFI_STANDARD_80211b);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/error-model/simple-error-model.cc ns-3.38/examples/error-model/simple-error-model.cc
--- ns-3.37/examples/error-model/simple-error-model.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/error-model/simple-error-model.cc	2023-03-16 20:54:23.568868400 -0700
@@ -158,7 +158,7 @@
 
     // Now, let's use the ListErrorModel and explicitly force a loss
     // of the packets with pkt-uids = 11 and 17 on node 2, device 0
-    std::list<uint32_t> sampleList;
+    std::list<uint64_t> sampleList;
     sampleList.push_back(11);
     sampleList.push_back(17);
     // This time, we'll explicitly create the error model we want
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/CMakeLists.txt ns-3.38/examples/ipv6/CMakeLists.txt
--- ns-3.37/examples/ipv6/CMakeLists.txt	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/CMakeLists.txt	2023-03-16 20:54:23.568868400 -0700
@@ -2,18 +2,18 @@
   NAME fragmentation-ipv6
   SOURCE_FILES fragmentation-ipv6.cc
   LIBRARIES_TO_LINK
+    ${libapplications}
     ${libcsma}
     ${libinternet}
-    ${libinternet-apps}
 )
 
 build_example(
   NAME fragmentation-ipv6-two-MTU
   SOURCE_FILES fragmentation-ipv6-two-MTU.cc
   LIBRARIES_TO_LINK
+    ${libapplications}
     ${libcsma}
     ${libinternet}
-    ${libinternet-apps}
 )
 
 build_example(
@@ -35,8 +35,8 @@
 )
 
 build_example(
-  NAME ping6
-  SOURCE_FILES ping6.cc
+  NAME ping6-example
+  SOURCE_FILES ping6-example.cc
   LIBRARIES_TO_LINK
     ${libcsma}
     ${libinternet}
@@ -44,8 +44,8 @@
 )
 
 build_example(
-  NAME radvd
-  SOURCE_FILES radvd.cc
+  NAME radvd-one-prefix
+  SOURCE_FILES radvd-one-prefix.cc
   LIBRARIES_TO_LINK
     ${libcsma}
     ${libinternet}
@@ -74,17 +74,17 @@
   LIBRARIES_TO_LINK
     ${liblr-wpan}
     ${libinternet}
+    ${libinternet-apps}
     ${libsixlowpan}
     ${libmobility}
-    ${libinternet-apps}
 )
 
 build_example(
   NAME fragmentation-ipv6-PMTU
   SOURCE_FILES fragmentation-ipv6-PMTU.cc
   LIBRARIES_TO_LINK
+    ${libapplications}
     ${libcsma}
     ${libinternet}
-    ${libinternet-apps}
     ${libpoint-to-point}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/fragmentation-ipv6.cc ns-3.38/examples/ipv6/fragmentation-ipv6.cc
--- ns-3.37/examples/ipv6/fragmentation-ipv6.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/fragmentation-ipv6.cc	2023-03-16 20:54:23.568868400 -0700
@@ -27,6 +27,7 @@
 // //
 // // - Tracing of queues and packet receptions to file "fragmentation-ipv6.tr"
 
+#include "ns3/applications-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
 #include "ns3/internet-apps-module.h"
@@ -55,8 +56,10 @@
         LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_ALL);
+        LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_ALL);
     }
+    LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
 
     NS_LOG_INFO("Create nodes.");
     Ptr<Node> n0 = CreateObject<Node>();
@@ -89,25 +92,24 @@
     i2.SetForwarding(0, true);
     i2.SetDefaultRouteInAllNodes(0);
 
-    Ipv6StaticRoutingHelper routingHelper;
     Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
-    routingHelper.PrintRoutingTableAt(Seconds(0), n0, routingStream);
+    Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(0), n0, routingStream);
 
-    /* Create a Ping6 application to send ICMPv6 echo request from n0 to n1 via r */
+    /* Create a UdpEchoClient and UdpEchoServer application to send packets from n0 to n1 via r */
     uint32_t packetSize = 4096;
     uint32_t maxPacketCount = 5;
-    Time interPacketInterval = Seconds(1.0);
-    Ping6Helper ping6;
 
-    ping6.SetLocal(i1.GetAddress(0, 1));
-    ping6.SetRemote(i2.GetAddress(1, 1));
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(net1.Get(0));
-    apps.Start(Seconds(2.0));
-    apps.Stop(Seconds(20.0));
+    UdpEchoClientHelper echoClient(i2.GetAddress(1, 1), 42);
+    echoClient.SetAttribute("PacketSize", UintegerValue(packetSize));
+    echoClient.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
+    ApplicationContainer clientApps = echoClient.Install(net1.Get(0));
+    clientApps.Start(Seconds(2.0));
+    clientApps.Stop(Seconds(20.0));
+
+    UdpEchoServerHelper echoServer(42);
+    ApplicationContainer serverApps = echoServer.Install(net2.Get(1));
+    serverApps.Start(Seconds(0.0));
+    serverApps.Stop(Seconds(30.0));
 
     AsciiTraceHelper ascii;
     csma.EnableAsciiAll(ascii.CreateFileStream("fragmentation-ipv6.tr"));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/fragmentation-ipv6-PMTU.cc ns-3.38/examples/ipv6/fragmentation-ipv6-PMTU.cc
--- ns-3.37/examples/ipv6/fragmentation-ipv6-PMTU.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/fragmentation-ipv6-PMTU.cc	2023-03-16 20:54:23.568868400 -0700
@@ -30,6 +30,7 @@
 // //
 // // - Tracing of queues and packet receptions to file "fragmentation-ipv6-PMTU.tr"
 
+#include "ns3/applications-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
 #include "ns3/internet-apps-module.h"
@@ -60,8 +61,10 @@
         LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_ALL);
+        LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_ALL);
     }
+    LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
 
     NS_LOG_INFO("Create nodes.");
     Ptr<Node> n0 = CreateObject<Node>();
@@ -117,35 +120,33 @@
     i3.SetForwarding(0, true);
     i3.SetDefaultRouteInAllNodes(0);
 
-    Ipv6StaticRoutingHelper routingHelper;
     Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
-    routingHelper.PrintRoutingTableAt(Seconds(0), r1, routingStream);
+    Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(0), r1, routingStream);
 
-    /* Create a Ping6 application to send ICMPv6 echo request from r to n2 */
-    uint32_t packetSize = 1600; // Packet should fragment as intermediate link MTU is 1500
-    uint32_t maxPacketCount = 5;
-    Time interPacketInterval = Seconds(1.0);
-    Ping6Helper ping6;
+    // Create an UDP Echo server on n2
+    UdpEchoServerHelper echoServer(42);
+    ApplicationContainer serverApps = echoServer.Install(n2);
+    serverApps.Start(Seconds(0.0));
+    serverApps.Stop(Seconds(30.0));
 
-    ping6.SetLocal(i2.GetAddress(1, 1));
-    ping6.SetRemote(i3.GetAddress(1, 1));
+    uint32_t maxPacketCount = 5;
 
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(n1);
-    apps.Start(Seconds(2.0));
-    apps.Stop(Seconds(10.0));
-
-    /* Create a Ping6 application to send ICMPv6 echo request from n0 to n2 */
-    packetSize = 4000;
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-
-    ping6.SetLocal(i1.GetAddress(0, 1));
-    ping6.SetRemote(i3.GetAddress(1, 1));
-    apps = ping6.Install(n0);
-    apps.Start(Seconds(11.0));
-    apps.Stop(Seconds(20.0));
+    // Create an UDP Echo client on n1 to send UDP packets to n2 via r1
+    uint32_t packetSizeN1 = 1600; // Packet should fragment as intermediate link MTU is 1500
+    UdpEchoClientHelper echoClient(i3.GetAddress(1, 1), 42);
+    echoClient.SetAttribute("PacketSize", UintegerValue(packetSizeN1));
+    echoClient.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
+    ApplicationContainer clientAppsN1 = echoClient.Install(n1);
+    clientAppsN1.Start(Seconds(2.0));
+    clientAppsN1.Stop(Seconds(10.0));
+
+    // Create an UDP Echo client on n0 to send UDP packets to n2 via r0 and r1
+    uint32_t packetSizeN2 = 4000; // Packet should fragment as intermediate link MTU is 1500
+    echoClient.SetAttribute("PacketSize", UintegerValue(packetSizeN2));
+    echoClient.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
+    ApplicationContainer clientAppsN2 = echoClient.Install(n1);
+    clientAppsN2.Start(Seconds(11.0));
+    clientAppsN2.Stop(Seconds(20.0));
 
     AsciiTraceHelper ascii;
     csma.EnableAsciiAll(ascii.CreateFileStream("fragmentation-ipv6-PMTU.tr"));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/fragmentation-ipv6-two-MTU.cc ns-3.38/examples/ipv6/fragmentation-ipv6-two-MTU.cc
--- ns-3.37/examples/ipv6/fragmentation-ipv6-two-MTU.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/fragmentation-ipv6-two-MTU.cc	2023-03-16 20:54:23.568868400 -0700
@@ -29,6 +29,7 @@
 // //
 // // - Tracing of queues and packet receptions to file "fragmentation-ipv6-two-mtu.tr"
 
+#include "ns3/applications-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
 #include "ns3/internet-apps-module.h"
@@ -57,8 +58,10 @@
         LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_ALL);
+        LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_ALL);
     }
+    LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
 
     NS_LOG_INFO("Create nodes.");
     Ptr<Node> n0 = CreateObject<Node>();
@@ -92,25 +95,24 @@
     i2.SetForwarding(0, true);
     i2.SetDefaultRouteInAllNodes(0);
 
-    Ipv6StaticRoutingHelper routingHelper;
     Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
-    routingHelper.PrintRoutingTableAt(Seconds(0), n0, routingStream);
+    Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(0), n0, routingStream);
 
-    /* Create a Ping6 application to send ICMPv6 echo request from n0 to n1 via r */
+    /* Create a UdpEchoClient and UdpEchoServer application to send packets from n0 to n1 via r */
     uint32_t packetSize = 4096;
     uint32_t maxPacketCount = 5;
-    Time interPacketInterval = Seconds(1.0);
-    Ping6Helper ping6;
 
-    ping6.SetLocal(i1.GetAddress(0, 1));
-    ping6.SetRemote(i2.GetAddress(1, 1));
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(net1.Get(0));
-    apps.Start(Seconds(2.0));
-    apps.Stop(Seconds(20.0));
+    UdpEchoClientHelper echoClient(i2.GetAddress(1, 1), 42);
+    echoClient.SetAttribute("PacketSize", UintegerValue(packetSize));
+    echoClient.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
+    ApplicationContainer clientApps = echoClient.Install(net1.Get(0));
+    clientApps.Start(Seconds(2.0));
+    clientApps.Stop(Seconds(20.0));
+
+    UdpEchoServerHelper echoServer(42);
+    ApplicationContainer serverApps = echoServer.Install(net2.Get(1));
+    serverApps.Start(Seconds(0.0));
+    serverApps.Stop(Seconds(30.0));
 
     AsciiTraceHelper ascii;
     csma.EnableAsciiAll(ascii.CreateFileStream("fragmentation-ipv6-two-mtu.tr"));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/icmpv6-redirect.cc ns-3.38/examples/ipv6/icmpv6-redirect.cc
--- ns-3.37/examples/ipv6/icmpv6-redirect.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/icmpv6-redirect.cc	2023-03-16 20:54:23.568868400 -0700
@@ -114,21 +114,17 @@
                             iic1.GetInterfaceIndex(1));
 
     Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
-    routingHelper.PrintRoutingTableAt(Seconds(0.0), r1, routingStream);
-    routingHelper.PrintRoutingTableAt(Seconds(3.0), sta1, routingStream);
+    Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(0.0), r1, routingStream);
+    Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(3.0), sta1, routingStream);
 
     NS_LOG_INFO("Create Applications.");
     uint32_t packetSize = 1024;
     uint32_t maxPacketCount = 5;
-    Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
+    PingHelper ping(iic2.GetAddress(1, 1));
 
-    ping6.SetLocal(iic1.GetAddress(0, 1));
-    ping6.SetRemote(iic2.GetAddress(1, 1));
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(sta1);
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(sta1);
     apps.Start(Seconds(2.0));
     apps.Stop(Seconds(10.0));
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/loose-routing-ipv6.cc ns-3.38/examples/ipv6/loose-routing-ipv6.cc
--- ns-3.37/examples/ipv6/loose-routing-ipv6.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/loose-routing-ipv6.cc	2023-03-16 20:54:23.568868400 -0700
@@ -60,7 +60,7 @@
 
     if (verbose)
     {
-        LogComponentEnable("Ipv6ExtensionLooseRouting", LOG_LEVEL_ALL);
+        // LogComponentEnable("Ipv6ExtensionLooseRouting", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6Extension", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6L3Protocol", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
@@ -69,6 +69,8 @@
         LogComponentEnable("NdiscCache", LOG_LEVEL_ALL);
     }
 
+    // LogComponentEnable("Ping", LOG_LEVEL_INFO);
+
     NS_LOG_INFO("Create nodes.");
     Ptr<Node> h0 = CreateObject<Node>();
     Ptr<Node> h1 = CreateObject<Node>();
@@ -164,16 +166,16 @@
     routersAddress.push_back(i6.GetAddress(1, 1));
     routersAddress.push_back(i2.GetAddress(0, 1));
 
-    Ping6Helper client;
-    /* remote address is first routers in RH0 => source routing */
-    client.SetRemote(i1.GetAddress(1, 1));
-    client.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
+    // remote address is first routers in RH0 => source routing
+    PingHelper client(i1.GetAddress(1, 1));
+    client.SetAttribute("Count", UintegerValue(maxPacketCount));
     client.SetAttribute("Interval", TimeValue(interPacketInterval));
-    client.SetAttribute("PacketSize", UintegerValue(packetSize));
-    client.SetRoutersAddress(routersAddress);
+    client.SetAttribute("Size", UintegerValue(packetSize));
     ApplicationContainer apps = client.Install(h0);
+    DynamicCast<Ping>(apps.Get(0))->SetRouters(routersAddress);
+
     apps.Start(Seconds(1.0));
-    apps.Stop(Seconds(10.0));
+    apps.Stop(Seconds(20.0));
 
     AsciiTraceHelper ascii;
     csma.EnableAsciiAll(ascii.CreateFileStream("loose-routing-ipv6.tr"));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/ping6.cc ns-3.38/examples/ipv6/ping6.cc
--- ns-3.37/examples/ipv6/ping6.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/ping6.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2008-2009 Strasbourg University
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
- */
-
-// Network topology
-//
-//       n0    n1
-//       |     |
-//       =================
-//              LAN
-//
-// - ICMPv6 echo request flows from n0 to n1 and back with ICMPv6 echo reply
-// - DropTail queues
-// - Tracing of queues and packet receptions to file "ping6.tr"
-
-#include "ns3/core-module.h"
-#include "ns3/csma-module.h"
-#include "ns3/internet-apps-module.h"
-#include "ns3/internet-module.h"
-
-#include <fstream>
-
-using namespace ns3;
-
-NS_LOG_COMPONENT_DEFINE("Ping6Example");
-
-int
-main(int argc, char** argv)
-{
-    bool verbose = false;
-
-    CommandLine cmd(__FILE__);
-    cmd.AddValue("verbose", "turn on log components", verbose);
-    cmd.Parse(argc, argv);
-
-    if (verbose)
-    {
-        LogComponentEnable("Ping6Example", LOG_LEVEL_INFO);
-        LogComponentEnable("Ipv6EndPointDemux", LOG_LEVEL_ALL);
-        LogComponentEnable("Ipv6L3Protocol", LOG_LEVEL_ALL);
-        LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
-        LogComponentEnable("Ipv6ListRouting", LOG_LEVEL_ALL);
-        LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
-        LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
-        LogComponentEnable("NdiscCache", LOG_LEVEL_ALL);
-    }
-
-    NS_LOG_INFO("Create nodes.");
-    NodeContainer n;
-    n.Create(4);
-
-    /* Install IPv4/IPv6 stack */
-    InternetStackHelper internetv6;
-    internetv6.SetIpv4StackInstall(false);
-    internetv6.Install(n);
-
-    NS_LOG_INFO("Create channels.");
-    CsmaHelper csma;
-    csma.SetChannelAttribute("DataRate", DataRateValue(5000000));
-    csma.SetChannelAttribute("Delay", TimeValue(MilliSeconds(2)));
-    NetDeviceContainer d = csma.Install(n);
-
-    Ipv6AddressHelper ipv6;
-    NS_LOG_INFO("Assign IPv6 Addresses.");
-    Ipv6InterfaceContainer i = ipv6.Assign(d);
-
-    NS_LOG_INFO("Create Applications.");
-
-    /* Create a Ping6 application to send ICMPv6 echo request from node zero to
-     * all-nodes (ff02::1).
-     */
-    uint32_t packetSize = 1024;
-    uint32_t maxPacketCount = 5;
-    Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
-
-    /*
-    ping6.SetLocal (i.GetAddress (0, 1));
-    ping6.SetRemote (i.GetAddress (1, 1));
-    */
-    ping6.SetIfIndex(i.GetInterfaceIndex(0));
-    ping6.SetRemote(Ipv6Address::GetAllNodesMulticast());
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(n.Get(0));
-    apps.Start(Seconds(2.0));
-    apps.Stop(Seconds(10.0));
-
-    AsciiTraceHelper ascii;
-    csma.EnableAsciiAll(ascii.CreateFileStream("ping6.tr"));
-    csma.EnablePcapAll(std::string("ping6"), true);
-
-    NS_LOG_INFO("Run Simulation.");
-    Simulator::Run();
-    Simulator::Destroy();
-    NS_LOG_INFO("Done.");
-
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/ping6-example.cc ns-3.38/examples/ipv6/ping6-example.cc
--- ns-3.37/examples/ipv6/ping6-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/examples/ipv6/ping6-example.cc	2023-03-16 20:54:23.568868400 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2008-2009 Strasbourg University
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
+ */
+
+// Network topology
+//
+//       n0    n1
+//       |     |
+//       =================
+//              LAN
+//
+// - ICMPv6 echo request flows from n0 to n1 and back with ICMPv6 echo reply
+// - DropTail queues
+// - Tracing of queues and packet receptions to file "ping6.tr"
+
+#include "ns3/core-module.h"
+#include "ns3/csma-module.h"
+#include "ns3/internet-apps-module.h"
+#include "ns3/internet-module.h"
+
+#include <fstream>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("Ping6Example");
+
+int
+main(int argc, char** argv)
+{
+    bool verbose = false;
+    bool allNodes = false;
+
+    CommandLine cmd(__FILE__);
+    cmd.AddValue("verbose", "turn on log components", verbose);
+    cmd.AddValue("allNodes", "Ping all the nodes (true) or just one neighbor (false)", allNodes);
+    cmd.Parse(argc, argv);
+
+    if (verbose)
+    {
+        LogComponentEnable("Ping6Example", LOG_LEVEL_INFO);
+        LogComponentEnable("Ipv6EndPointDemux", LOG_LEVEL_ALL);
+        LogComponentEnable("Ipv6L3Protocol", LOG_LEVEL_ALL);
+        LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
+        LogComponentEnable("Ipv6ListRouting", LOG_LEVEL_ALL);
+        LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
+        LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
+        LogComponentEnable("NdiscCache", LOG_LEVEL_ALL);
+    }
+
+    NS_LOG_INFO("Create nodes.");
+    NodeContainer n;
+    n.Create(4);
+
+    /* Install IPv4/IPv6 stack */
+    InternetStackHelper internetv6;
+    internetv6.SetIpv4StackInstall(false);
+    internetv6.Install(n);
+
+    NS_LOG_INFO("Create channels.");
+    CsmaHelper csma;
+    csma.SetChannelAttribute("DataRate", DataRateValue(5000000));
+    csma.SetChannelAttribute("Delay", TimeValue(MilliSeconds(2)));
+    NetDeviceContainer d = csma.Install(n);
+
+    Ipv6AddressHelper ipv6;
+    NS_LOG_INFO("Assign IPv6 Addresses.");
+    Ipv6InterfaceContainer i = ipv6.Assign(d);
+
+    NS_LOG_INFO("Create Applications.");
+
+    // Create a Ping application to send ICMPv6 echo request from node zero
+    uint32_t packetSize = 1024;
+    uint32_t maxPacketCount = 5;
+
+    Ipv6Address destination = allNodes ? Ipv6Address::GetAllNodesMulticast() : i.GetAddress(1, 0);
+    PingHelper ping(destination);
+
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ping.SetAttribute("InterfaceAddress", AddressValue(i.GetAddress(0, 0)));
+
+    ApplicationContainer apps = ping.Install(n.Get(0));
+    apps.Start(Seconds(2.0));
+    apps.Stop(Seconds(10.0));
+
+    AsciiTraceHelper ascii;
+    csma.EnableAsciiAll(ascii.CreateFileStream("ping6.tr"));
+    csma.EnablePcapAll(std::string("ping6"), true);
+
+    NS_LOG_INFO("Run Simulation.");
+    Simulator::Run();
+    Simulator::Destroy();
+    NS_LOG_INFO("Done.");
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/radvd.cc ns-3.38/examples/ipv6/radvd.cc
--- ns-3.37/examples/ipv6/radvd.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/radvd.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,160 +0,0 @@
-/*
- * Copyright (c) 2009 Strasbourg University
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: David Gross <gdavid.devel@gmail.com>
- *         Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
- */
-
-// Network topology
-// //
-// //             n0   R    n1
-// //             |    _    |
-// //             ====|_|====
-// //                router
-// // - R sends RA to n0's subnet (2001:1::/64);
-// // - R sends RA to n1's subnet (2001:2::/64);
-// // - n0 ping6 n1.
-// //
-// // - Tracing of queues and packet receptions to file "radvd.tr"
-
-#include "ns3/radvd.h"
-
-#include "ns3/core-module.h"
-#include "ns3/csma-module.h"
-#include "ns3/internet-apps-module.h"
-#include "ns3/internet-module.h"
-#include "ns3/radvd-interface.h"
-#include "ns3/radvd-prefix.h"
-
-#include <fstream>
-
-using namespace ns3;
-
-NS_LOG_COMPONENT_DEFINE("RadvdExample");
-
-int
-main(int argc, char** argv)
-{
-    bool verbose = false;
-
-    CommandLine cmd(__FILE__);
-    cmd.AddValue("verbose", "turn on log components", verbose);
-    cmd.Parse(argc, argv);
-
-    if (verbose)
-    {
-        LogComponentEnable("Ipv6L3Protocol", LOG_LEVEL_ALL);
-        LogComponentEnable("Ipv6RawSocketImpl", LOG_LEVEL_ALL);
-        LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
-        LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
-        LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
-        LogComponentEnable("RadvdApplication", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
-    }
-
-    NS_LOG_INFO("Create nodes.");
-    Ptr<Node> n0 = CreateObject<Node>();
-    Ptr<Node> r = CreateObject<Node>();
-    Ptr<Node> n1 = CreateObject<Node>();
-
-    NodeContainer net1(n0, r);
-    NodeContainer net2(r, n1);
-    NodeContainer all(n0, r, n1);
-
-    NS_LOG_INFO("Create IPv6 Internet Stack");
-    InternetStackHelper internetv6;
-    internetv6.Install(all);
-
-    NS_LOG_INFO("Create channels.");
-    CsmaHelper csma;
-    csma.SetChannelAttribute("DataRate", DataRateValue(5000000));
-    csma.SetChannelAttribute("Delay", TimeValue(MilliSeconds(2)));
-    NetDeviceContainer d1 = csma.Install(net1); /* n0 - R */
-    NetDeviceContainer d2 = csma.Install(net2); /* R - n1 */
-
-    NS_LOG_INFO("Create networks and assign IPv6 Addresses.");
-    Ipv6AddressHelper ipv6;
-
-    /* first subnet */
-    ipv6.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64));
-    NetDeviceContainer tmp;
-    tmp.Add(d1.Get(0));                                           /* n0 */
-    Ipv6InterfaceContainer iic1 = ipv6.AssignWithoutAddress(tmp); /* n0 interface */
-
-    NetDeviceContainer tmp2;
-    tmp2.Add(d1.Get(1)); /* R */
-    Ipv6InterfaceContainer iicr1 =
-        ipv6.Assign(tmp2); /* R interface to the first subnet is just statically assigned */
-    iicr1.SetForwarding(0, true);
-    iic1.Add(iicr1);
-
-    /* second subnet R - n1 */
-    ipv6.SetBase(Ipv6Address("2001:2::"), Ipv6Prefix(64));
-    NetDeviceContainer tmp3;
-    tmp3.Add(d2.Get(0));                              /* R */
-    Ipv6InterfaceContainer iicr2 = ipv6.Assign(tmp3); /* R interface */
-    iicr2.SetForwarding(0, true);
-
-    NetDeviceContainer tmp4;
-    tmp4.Add(d2.Get(1)); /* n1 */
-    Ipv6InterfaceContainer iic2 = ipv6.AssignWithoutAddress(tmp4);
-    iic2.Add(iicr2);
-
-    /* radvd configuration */
-    RadvdHelper radvdHelper;
-
-    /* R interface (n0 - R) */
-    /* n0 will receive unsolicited (periodic) RA */
-    radvdHelper.AddAnnouncedPrefix(iic1.GetInterfaceIndex(1), Ipv6Address("2001:1::0"), 64);
-
-    /* R interface (R - n1) */
-    /* n1 will have to use RS, as RA are not sent automatically */
-    radvdHelper.AddAnnouncedPrefix(iic2.GetInterfaceIndex(1), Ipv6Address("2001:2::0"), 64);
-    radvdHelper.GetRadvdInterface(iic2.GetInterfaceIndex(1))->SetSendAdvert(false);
-
-    ApplicationContainer radvdApps = radvdHelper.Install(r);
-    radvdApps.Start(Seconds(1.0));
-    radvdApps.Stop(Seconds(10.0));
-
-    /* Create a Ping6 application to send ICMPv6 echo request from n0 to n1 via R */
-    uint32_t packetSize = 1024;
-    uint32_t maxPacketCount = 5;
-    Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
-
-    /* ping6.SetLocal (iic1.GetAddress (0, 1)); */
-    ping6.SetRemote(
-        Ipv6Address("2001:2::200:ff:fe00:4")); /* should be n1 address after autoconfiguration */
-    ping6.SetIfIndex(iic1.GetInterfaceIndex(0));
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(net1.Get(0));
-    apps.Start(Seconds(2.0));
-    apps.Stop(Seconds(7.0));
-
-    AsciiTraceHelper ascii;
-    csma.EnableAsciiAll(ascii.CreateFileStream("radvd.tr"));
-    csma.EnablePcapAll(std::string("radvd"), true);
-
-    NS_LOG_INFO("Run Simulation.");
-    Simulator::Run();
-    Simulator::Destroy();
-    NS_LOG_INFO("Done.");
-
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/radvd-one-prefix.cc ns-3.38/examples/ipv6/radvd-one-prefix.cc
--- ns-3.37/examples/ipv6/radvd-one-prefix.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/examples/ipv6/radvd-one-prefix.cc	2023-03-16 20:54:23.568868400 -0700
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009 Strasbourg University
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: David Gross <gdavid.devel@gmail.com>
+ *         Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
+ */
+
+// Network topology
+// //
+// //             n0   R    n1
+// //             |    _    |
+// //             ====|_|====
+// //                router
+// // - R sends RA to n0's subnet (2001:1::/64);
+// // - R sends RA to n1's subnet (2001:2::/64);
+// // - n0 ping n1.
+// //
+// // - Tracing of queues and packet receptions to file "radvd.tr"
+
+#include "ns3/core-module.h"
+#include "ns3/csma-module.h"
+#include "ns3/internet-apps-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/radvd-interface.h"
+#include "ns3/radvd-prefix.h"
+#include "ns3/radvd.h"
+
+#include <fstream>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("RadvdExample");
+
+int
+main(int argc, char** argv)
+{
+    bool verbose = false;
+
+    CommandLine cmd(__FILE__);
+    cmd.AddValue("verbose", "turn on log components", verbose);
+    cmd.Parse(argc, argv);
+
+    if (verbose)
+    {
+        LogComponentEnable("Ipv6L3Protocol", LOG_LEVEL_ALL);
+        LogComponentEnable("Ipv6RawSocketImpl", LOG_LEVEL_ALL);
+        LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
+        LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
+        LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
+        LogComponentEnable("RadvdApplication", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
+    }
+
+    NS_LOG_INFO("Create nodes.");
+    Ptr<Node> n0 = CreateObject<Node>();
+    Ptr<Node> r = CreateObject<Node>();
+    Ptr<Node> n1 = CreateObject<Node>();
+
+    NodeContainer net1(n0, r);
+    NodeContainer net2(r, n1);
+    NodeContainer all(n0, r, n1);
+
+    NS_LOG_INFO("Create IPv6 Internet Stack");
+    InternetStackHelper internetv6;
+    internetv6.Install(all);
+
+    NS_LOG_INFO("Create channels.");
+    CsmaHelper csma;
+    csma.SetChannelAttribute("DataRate", DataRateValue(5000000));
+    csma.SetChannelAttribute("Delay", TimeValue(MilliSeconds(2)));
+    NetDeviceContainer d1 = csma.Install(net1); /* n0 - R */
+    NetDeviceContainer d2 = csma.Install(net2); /* R - n1 */
+
+    NS_LOG_INFO("Create networks and assign IPv6 Addresses.");
+    Ipv6AddressHelper ipv6;
+
+    /* first subnet */
+    ipv6.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64));
+    NetDeviceContainer tmp;
+    tmp.Add(d1.Get(0));                                           /* n0 */
+    Ipv6InterfaceContainer iic1 = ipv6.AssignWithoutAddress(tmp); /* n0 interface */
+
+    NetDeviceContainer tmp2;
+    tmp2.Add(d1.Get(1)); /* R */
+    Ipv6InterfaceContainer iicr1 =
+        ipv6.Assign(tmp2); /* R interface to the first subnet is just statically assigned */
+    iicr1.SetForwarding(0, true);
+    iic1.Add(iicr1);
+
+    /* second subnet R - n1 */
+    ipv6.SetBase(Ipv6Address("2001:2::"), Ipv6Prefix(64));
+    NetDeviceContainer tmp3;
+    tmp3.Add(d2.Get(0));                              /* R */
+    Ipv6InterfaceContainer iicr2 = ipv6.Assign(tmp3); /* R interface */
+    iicr2.SetForwarding(0, true);
+
+    NetDeviceContainer tmp4;
+    tmp4.Add(d2.Get(1)); /* n1 */
+    Ipv6InterfaceContainer iic2 = ipv6.AssignWithoutAddress(tmp4);
+    iic2.Add(iicr2);
+
+    /* radvd configuration */
+    RadvdHelper radvdHelper;
+
+    /* R interface (n0 - R) */
+    /* n0 will receive unsolicited (periodic) RA */
+    radvdHelper.AddAnnouncedPrefix(iic1.GetInterfaceIndex(1), Ipv6Address("2001:1::0"), 64);
+
+    /* R interface (R - n1) */
+    /* n1 will have to use RS, as RA are not sent automatically */
+    radvdHelper.AddAnnouncedPrefix(iic2.GetInterfaceIndex(1), Ipv6Address("2001:2::0"), 64);
+    radvdHelper.GetRadvdInterface(iic2.GetInterfaceIndex(1))->SetSendAdvert(false);
+
+    ApplicationContainer radvdApps = radvdHelper.Install(r);
+    radvdApps.Start(Seconds(1.0));
+    radvdApps.Stop(Seconds(10.0));
+
+    /* Create a Ping application to send ICMPv6 echo request from n0 to n1 via R */
+    uint32_t packetSize = 1024;
+    uint32_t maxPacketCount = 5;
+    PingHelper ping(
+        Ipv6Address("2001:2::200:ff:fe00:4")); /* should be n1 address after autoconfiguration */
+
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(net1.Get(0));
+    apps.Start(Seconds(2.0));
+    apps.Stop(Seconds(7.0));
+
+    AsciiTraceHelper ascii;
+    csma.EnableAsciiAll(ascii.CreateFileStream("radvd.tr"));
+    csma.EnablePcapAll(std::string("radvd"), true);
+
+    NS_LOG_INFO("Run Simulation.");
+    Simulator::Run();
+    Simulator::Destroy();
+    NS_LOG_INFO("Done.");
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/radvd-two-prefix.cc ns-3.38/examples/ipv6/radvd-two-prefix.cc
--- ns-3.37/examples/ipv6/radvd-two-prefix.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/radvd-two-prefix.cc	2023-03-16 20:54:23.568868400 -0700
@@ -27,7 +27,7 @@
 // // - R sends RA to n0's subnet (2001:1::/64 and 2001:ABCD::/64);
 // // - R interface to n0 has two addresses with following prefixes 2001:1::/64 and 2001:ABCD::/64;
 // // - R sends RA to n1's subnet (2001:2::/64);
-// // - n0 ping6 n1.
+// // - n0 ping n1.
 // //
 // // - Tracing of queues and packet receptions to file "radvd-two-prefix.tr"
 
@@ -98,7 +98,7 @@
         LogComponentEnable("Ipv6StaticRouting", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
         LogComponentEnable("RadvdApplication", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
     }
 
     NS_LOG_INFO("Create nodes.");
@@ -190,28 +190,22 @@
     radvdApps.Start(Seconds(1.0));
     radvdApps.Stop(Seconds(2.0));
 
-    /* Create a Ping6 application to send ICMPv6 echo request from n0 to n1 via R */
+    /* Create a Ping application to send ICMPv6 echo request from n0 to n1 via R */
     uint32_t packetSize = 1024;
     uint32_t maxPacketCount = 8;
-    Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
-
-    /* ping6.SetLocal (iic1.GetAddress (0, 1)); */
-    ping6.SetRemote(
+    PingHelper ping(
         Ipv6Address("2001:2::200:ff:fe00:4")); /* should be n1 address after autoconfiguration */
-    ping6.SetIfIndex(iic1.GetInterfaceIndex(0));
+    // ping.SetIfIndex(iic1.GetInterfaceIndex(0));
 
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(net1.Get(0));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(net1.Get(0));
     apps.Start(Seconds(2.0));
-    apps.Stop(Seconds(9.0));
+    apps.Stop(Seconds(5.0));
 
-    Ipv6StaticRoutingHelper routingHelper;
     Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
-    routingHelper.PrintRoutingTableAt(Seconds(2.0), n0, routingStream);
-    routingHelper.PrintRoutingTableAt(Seconds(10.0), n0, routingStream);
+    Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(2.0), n0, routingStream);
+    Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(10.0), n0, routingStream);
 
     IpAddressHelper ipAddressHelper;
     /* RA should be received, two prefixes + routes + default route should be present */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/ipv6/wsn-ping6.cc ns-3.38/examples/ipv6/wsn-ping6.cc
--- ns-3.37/examples/ipv6/wsn-ping6.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/ipv6/wsn-ping6.cc	2023-03-16 20:54:23.568868400 -0700
@@ -61,7 +61,7 @@
         LogComponentEnable("Ipv6ListRouting", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
         LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
         LogComponentEnable("NdiscCache", LOG_LEVEL_ALL);
         LogComponentEnable("SixLowPanNetDevice", LOG_LEVEL_ALL);
     }
@@ -88,7 +88,7 @@
     // Add and install the LrWpanNetDevice for each node
     // lrWpanHelper.EnableLogComponents();
     NetDeviceContainer devContainer = lrWpanHelper.Install(nodes);
-    lrWpanHelper.AssociateToPan(devContainer, 10);
+    lrWpanHelper.CreateAssociatedPan(devContainer, 10);
 
     std::cout << "Created " << devContainer.GetN() << " devices" << std::endl;
     std::cout << "There are " << nodes.GetN() << " nodes" << std::endl;
@@ -111,22 +111,18 @@
 
     NS_LOG_INFO("Create Applications.");
 
-    /* Create a Ping6 application to send ICMPv6 echo request from node zero to
+    /* Create a Ping application to send ICMPv6 echo request from node zero to
      * all-nodes (ff02::1).
      */
-    uint32_t packetSize = 10;
+    uint32_t packetSize = 16;
     uint32_t maxPacketCount = 5;
     Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
+    PingHelper ping(i.GetAddress(1, 1));
 
-    ping6.SetLocal(i.GetAddress(0, 1));
-    ping6.SetRemote(i.GetAddress(1, 1));
-    // ping6.SetRemote (Ipv6Address::GetAllNodesMulticast ());
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(nodes.Get(0));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Interval", TimeValue(interPacketInterval));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(nodes.Get(0));
     apps.Start(Seconds(2.0));
     apps.Stop(Seconds(10.0));
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/matrix-topology/matrix-topology.cc ns-3.38/examples/matrix-topology/matrix-topology.cc
--- ns-3.37/examples/matrix-topology/matrix-topology.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/matrix-topology/matrix-topology.cc	2023-03-16 20:54:23.568868400 -0700
@@ -117,7 +117,7 @@
     std::vector<std::vector<double>> coord_array;
     coord_array = readCordinatesFile(node_coordinates_file_name);
 
-    // Optionally display node co-ordinates file
+    // Optionally display node coordinates file
     // printCoordinateArray (node_coordinates_file_name.c_str (),coord_array);
 
     int n_nodes = coord_array.size();
@@ -312,7 +312,7 @@
     {
         std::string line;
         getline(adj_mat_file, line);
-        if (line == "")
+        if (line.empty())
         {
             NS_LOG_WARN("WARNING: Ignoring blank row in the array: " << i);
             break;
@@ -377,7 +377,7 @@
         std::string line;
         getline(node_coordinates_file, line);
 
-        if (line == "")
+        if (line.empty())
         {
             NS_LOG_WARN("WARNING: Ignoring blank row: " << m);
             break;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/realtime/realtime-udp-echo.py ns-3.38/examples/realtime/realtime-udp-echo.py
--- ns-3.37/examples/realtime/realtime-udp-echo.py	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/realtime/realtime-udp-echo.py	2023-03-16 20:54:23.568868400 -0700
@@ -85,7 +85,7 @@
   packetSize = 1024
   maxPacketCount = 500
   interPacketInterval = ns.core.Seconds(0.01)
-  client = ns.applications.UdpEchoClientHelper(ns.addressFromIpv4Address(i.GetAddress (1)), port)
+  client = ns.applications.UdpEchoClientHelper(i.GetAddress(1).ConvertTo(), port)
   client.SetAttribute("MaxPackets", ns.core.UintegerValue(maxPacketCount))
   client.SetAttribute("Interval", ns.core.TimeValue(interPacketInterval))
   client.SetAttribute("PacketSize", ns.core.UintegerValue(packetSize))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/routing/dynamic-global-routing.cc ns-3.38/examples/routing/dynamic-global-routing.cc
--- ns-3.37/examples/routing/dynamic-global-routing.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/routing/dynamic-global-routing.cc	2023-03-16 20:54:23.568868400 -0700
@@ -210,10 +210,9 @@
     Simulator::Schedule(Seconds(14), &Ipv4::SetUp, ipv41, ipv4ifIndex1);
 
     // Trace routing tables
-    Ipv4GlobalRoutingHelper g;
     Ptr<OutputStreamWrapper> routingStream =
         Create<OutputStreamWrapper>("dynamic-global-routing.routes", std::ios::out);
-    g.PrintRoutingTableAllAt(Seconds(12), routingStream);
+    Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(12), routingStream);
 
     NS_LOG_INFO("Run Simulation.");
     Simulator::Run();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/routing/global-routing-multi-switch-plus-router.cc ns-3.38/examples/routing/global-routing-multi-switch-plus-router.cc
--- ns-3.37/examples/routing/global-routing-multi-switch-plus-router.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/routing/global-routing-multi-switch-plus-router.cc	2023-03-16 20:54:23.568868400 -0700
@@ -332,7 +332,7 @@
     // Parse the pcapLocations string into pcapLocationVec
     // ----------------------------------------------------------------------
     std::vector<std::string> pcapLocationVec;
-    if (pcapLocations != "")
+    if (!pcapLocations.empty())
     {
         std::stringstream sStream(pcapLocations);
 
@@ -710,8 +710,7 @@
         Create<OutputStreamWrapper>("global-routing-multi-switch-plus-router.routes",
                                     std::ios::out);
 
-    Ipv4GlobalRoutingHelper g;
-    g.PrintRoutingTableAllAt(Seconds(0.1), routingStream);
+    Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(0.1), routingStream);
 
     // ======================================================================
     // Configure PCAP traces
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/routing/manet-routing-compare.cc ns-3.38/examples/routing/manet-routing-compare.cc
--- ns-3.37/examples/routing/manet-routing-compare.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/routing/manet-routing-compare.cc	2023-03-16 20:54:23.568868400 -0700
@@ -102,7 +102,7 @@
     // static void SetMACParam (ns3::NetDeviceContainer & devices,
     //                                  int slotDistance);
     /**
-     * Handles the command-line parmeters.
+     * Handles the command-line parameters.
      * \param argc The argument count.
      * \param argv The argument vector.
      * \return the CSV filename.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/routing/ripng-simple-network.cc ns-3.38/examples/routing/ripng-simple-network.cc
--- ns-3.37/examples/routing/ripng-simple-network.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/routing/ripng-simple-network.cc	2023-03-16 20:54:23.568868400 -0700
@@ -96,12 +96,7 @@
         LogComponentEnable("Ipv6Interface", LOG_LEVEL_ALL);
         LogComponentEnable("Icmpv6L4Protocol", LOG_LEVEL_ALL);
         LogComponentEnable("NdiscCache", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
-    }
-
-    if (showPings)
-    {
-        LogComponentEnable("Ping6Application", LOG_LEVEL_INFO);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
     }
 
     if (SplitHorizon == "NoSplitHorizon")
@@ -223,38 +218,36 @@
 
     if (printRoutingTables)
     {
-        RipNgHelper routingHelper;
-
         Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
 
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), a, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), b, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), c, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), d, routingStream);
-
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), a, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), b, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), c, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), d, routingStream);
-
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), a, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), b, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), c, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), d, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(30.0), a, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(30.0), b, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(30.0), c, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(30.0), d, routingStream);
+
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(60.0), a, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(60.0), b, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(60.0), c, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(60.0), d, routingStream);
+
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(90.0), a, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(90.0), b, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(90.0), c, routingStream);
+        Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(90.0), d, routingStream);
     }
 
     NS_LOG_INFO("Create Applications.");
     uint32_t packetSize = 1024;
-    uint32_t maxPacketCount = 100;
     Time interPacketInterval = Seconds(1.0);
-    Ping6Helper ping6;
+    PingHelper ping(iic7.GetAddress(1, 1));
 
-    ping6.SetLocal(iic1.GetAddress(0, 1));
-    ping6.SetRemote(iic7.GetAddress(1, 1));
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(src);
+    ping.SetAttribute("Interval", TimeValue(interPacketInterval));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    if (showPings)
+    {
+        ping.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE));
+    }
+    ApplicationContainer apps = ping.Install(src);
     apps.Start(Seconds(1.0));
     apps.Stop(Seconds(110.0));
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/routing/rip-simple-network.cc ns-3.38/examples/routing/rip-simple-network.cc
--- ns-3.37/examples/routing/rip-simple-network.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/routing/rip-simple-network.cc	2023-03-16 20:54:23.568868400 -0700
@@ -97,7 +97,7 @@
         LogComponentEnable("Icmpv4L4Protocol", LOG_LEVEL_ALL);
         LogComponentEnable("Ipv4L3Protocol", LOG_LEVEL_ALL);
         LogComponentEnable("ArpCache", LOG_LEVEL_ALL);
-        LogComponentEnable("V4Ping", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
     }
 
     if (SplitHorizon == "NoSplitHorizon")
@@ -213,36 +213,34 @@
 
     if (printRoutingTables)
     {
-        RipHelper routingHelper;
-
         Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
 
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), a, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), b, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), c, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(30.0), d, routingStream);
-
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), a, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), b, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), c, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(60.0), d, routingStream);
-
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), a, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), b, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), c, routingStream);
-        routingHelper.PrintRoutingTableAt(Seconds(90.0), d, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(30.0), a, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(30.0), b, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(30.0), c, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(30.0), d, routingStream);
+
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(60.0), a, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(60.0), b, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(60.0), c, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(60.0), d, routingStream);
+
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(90.0), a, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(90.0), b, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(90.0), c, routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAt(Seconds(90.0), d, routingStream);
     }
 
     NS_LOG_INFO("Create Applications.");
     uint32_t packetSize = 1024;
     Time interPacketInterval = Seconds(1.0);
-    V4PingHelper ping("10.0.6.2");
+    PingHelper ping(Ipv4Address("10.0.6.2"));
 
     ping.SetAttribute("Interval", TimeValue(interPacketInterval));
     ping.SetAttribute("Size", UintegerValue(packetSize));
     if (showPings)
     {
-        ping.SetAttribute("Verbose", BooleanValue(true));
+        ping.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE));
     }
     ApplicationContainer apps = ping.Install(src);
     apps.Start(Seconds(1.0));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/routing/simple-routing-ping6.cc ns-3.38/examples/routing/simple-routing-ping6.cc
--- ns-3.37/examples/routing/simple-routing-ping6.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/routing/simple-routing-ping6.cc	2023-03-16 20:54:23.568868400 -0700
@@ -98,7 +98,7 @@
   LogComponentEnable ("Icmpv6L4Protocol", LOG_LEVEL_ALL);
   LogComponentEnable ("Ipv6StaticRouting", LOG_LEVEL_ALL);
   LogComponentEnable ("Ipv6Interface", LOG_LEVEL_ALL);
-  LogComponentEnable ("Ping6Application", LOG_LEVEL_ALL);
+  LogComponentEnable ("Ping", LOG_LEVEL_ALL);
 #endif
 
     CommandLine cmd(__FILE__);
@@ -139,19 +139,14 @@
 
     stackHelper.PrintRoutingTable(n0);
 
-    /* Create a Ping6 application to send ICMPv6 echo request from n0 to n1 via r */
+    /* Create a Ping application to send ICMPv6 echo request from n0 to n1 via r */
     uint32_t packetSize = 1024;
     uint32_t maxPacketCount = 5;
-    Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
+    PingHelper ping(i2.GetAddress(1, 1));
 
-    ping6.SetLocal(i1.GetAddress(0, 1));
-    ping6.SetRemote(i2.GetAddress(1, 1));
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(net1.Get(0));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(net1.Get(0));
     apps.Start(Seconds(2.0));
     apps.Stop(Seconds(20.0));
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/routing/simple-routing-ping6.py ns-3.38/examples/routing/simple-routing-ping6.py
--- ns-3.37/examples/routing/simple-routing-ping6.py	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/routing/simple-routing-ping6.py	2023-03-16 20:54:23.568868400 -0700
@@ -74,16 +74,17 @@
     packetSize = 1024
     maxPacketCount = 5
     interPacketInterval = ns.Seconds(1.)
-    ping6 = ns.Ping6Helper()
+    # ping = ns.PingHelper(i2.GetAddress(1, 1).ConvertTo())
+    ping = ns.PingHelper(i2.GetAddress(1, 1).ConvertTo())
 
-    ping6.SetLocal(i1.GetAddress(0, 1))
-    ping6.SetRemote(i2.GetAddress(1, 1))
+    # ping6.SetLocal(i1.GetAddress(0, 1))
+    # ping6.SetRemote(i2.GetAddress(1, 1))
 
-    ping6.SetAttribute("MaxPackets", ns.UintegerValue(maxPacketCount))
-    ping6.SetAttribute("Interval", ns.TimeValue(interPacketInterval))
-    ping6.SetAttribute("PacketSize", ns.UintegerValue(packetSize))
+    ping.SetAttribute("Count", ns.UintegerValue(maxPacketCount))
+    ping.SetAttribute("Interval", ns.TimeValue(interPacketInterval))
+    ping.SetAttribute("Size", ns.UintegerValue(packetSize))
 
-    apps = ping6.Install(ns.NodeContainer(net1.Get(0)))
+    apps = ping.Install(ns.NodeContainer(net1.Get(0)))
     apps.Start(ns.Seconds(2.0))
     apps.Stop(ns.Seconds(20.0))
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/stats/wifi-example-apps.cc ns-3.38/examples/stats/wifi-example-apps.cc
--- ns-3.37/examples/stats/wifi-example-apps.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/stats/wifi-example-apps.cc	2023-03-16 20:54:23.568868400 -0700
@@ -37,6 +37,10 @@
 
 NS_LOG_COMPONENT_DEFINE("WiFiDistanceApps");
 
+// ==============================================
+// SENDER
+// ==============================================
+
 TypeId
 Sender::GetTypeId()
 {
@@ -46,7 +50,7 @@
                             .AddAttribute("PacketSize",
                                           "The size of packets transmitted.",
                                           UintegerValue(64),
-                                          MakeUintegerAccessor(&Sender::m_pktSize),
+                                          MakeUintegerAccessor(&Sender::m_packetSize),
                                           MakeUintegerChecker<uint32_t>(1))
                             .AddAttribute("Destination",
                                           "Target host address.",
@@ -61,7 +65,7 @@
                             .AddAttribute("NumPackets",
                                           "Total number of packets to send.",
                                           UintegerValue(30),
-                                          MakeUintegerAccessor(&Sender::m_numPkts),
+                                          MakeUintegerAccessor(&Sender::m_nPackets),
                                           MakeUintegerChecker<uint32_t>(1))
                             .AddAttribute("Interval",
                                           "Delay between transmissions.",
@@ -79,7 +83,6 @@
 {
     NS_LOG_FUNCTION_NOARGS();
     m_interval = CreateObject<ConstantRandomVariable>();
-    m_socket = nullptr;
 }
 
 Sender::~Sender()
@@ -114,8 +117,6 @@
 
     Simulator::Cancel(m_sendEvent);
     m_sendEvent = Simulator::ScheduleNow(&Sender::SendPacket, this);
-
-    // end Sender::StartApplication
 }
 
 void
@@ -123,16 +124,15 @@
 {
     NS_LOG_FUNCTION_NOARGS();
     Simulator::Cancel(m_sendEvent);
-    // end Sender::StopApplication
 }
 
 void
 Sender::SendPacket()
 {
-    // NS_LOG_FUNCTION_NOARGS ();
+    // NS_LOG_FUNCTION_NOARGS();
     NS_LOG_INFO("Sending packet at " << Simulator::Now() << " to " << m_destAddr);
 
-    Ptr<Packet> packet = Create<Packet>(m_pktSize);
+    Ptr<Packet> packet = Create<Packet>(m_packetSize);
 
     TimestampTag timestamp;
     timestamp.SetTimestamp(Simulator::Now());
@@ -145,18 +145,17 @@
     // Report the event to the trace.
     m_txTrace(packet);
 
-    if (++m_count < m_numPkts)
+    if (++m_count < m_nPackets)
     {
         m_sendEvent =
             Simulator::Schedule(Seconds(m_interval->GetValue()), &Sender::SendPacket, this);
     }
-
-    // end Sender::SendPacket
 }
 
-//----------------------------------------------------------------------
-//-- Receiver
-//------------------------------------------------------
+// ==============================================
+// RECEIVER
+// ==============================================
+
 TypeId
 Receiver::GetTypeId()
 {
@@ -172,11 +171,8 @@
 }
 
 Receiver::Receiver()
-    : m_calc(nullptr),
-      m_delay(nullptr)
 {
     NS_LOG_FUNCTION_NOARGS();
-    m_socket = nullptr;
 }
 
 Receiver::~Receiver()
@@ -209,8 +205,6 @@
     }
 
     m_socket->SetRecvCallback(MakeCallback(&Receiver::Receive, this));
-
-    // end Receiver::StartApplication
 }
 
 void
@@ -222,22 +216,18 @@
     {
         m_socket->SetRecvCallback(MakeNullCallback<void, Ptr<Socket>>());
     }
-
-    // end Receiver::StopApplication
 }
 
 void
 Receiver::SetCounter(Ptr<CounterCalculator<>> calc)
 {
     m_calc = calc;
-    // end Receiver::SetCounter
 }
 
 void
 Receiver::SetDelayTracker(Ptr<TimeMinMaxAvgTotalCalculator> delay)
 {
     m_delay = delay;
-    // end Receiver::SetDelayTracker
 }
 
 void
@@ -272,71 +262,5 @@
         {
             m_calc->Update();
         }
-
-        // end receiving packets
     }
-
-    // end Receiver::Receive
-}
-
-//----------------------------------------------------------------------
-//-- TimestampTag
-//------------------------------------------------------
-TypeId
-TimestampTag::GetTypeId()
-{
-    static TypeId tid = TypeId("TimestampTag")
-                            .SetParent<Tag>()
-                            .AddConstructor<TimestampTag>()
-                            .AddAttribute("Timestamp",
-                                          "Some momentous point in time!",
-                                          EmptyAttributeValue(),
-                                          MakeTimeAccessor(&TimestampTag::GetTimestamp),
-                                          MakeTimeChecker());
-    return tid;
-}
-
-TypeId
-TimestampTag::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-uint32_t
-TimestampTag::GetSerializedSize() const
-{
-    return 8;
-}
-
-void
-TimestampTag::Serialize(TagBuffer i) const
-{
-    int64_t t = m_timestamp.GetNanoSeconds();
-    i.Write((const uint8_t*)&t, 8);
-}
-
-void
-TimestampTag::Deserialize(TagBuffer i)
-{
-    int64_t t;
-    i.Read((uint8_t*)&t, 8);
-    m_timestamp = NanoSeconds(t);
-}
-
-void
-TimestampTag::SetTimestamp(Time time)
-{
-    m_timestamp = time;
-}
-
-Time
-TimestampTag::GetTimestamp() const
-{
-    return m_timestamp;
-}
-
-void
-TimestampTag::Print(std::ostream& os) const
-{
-    os << "t=" << m_timestamp;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/stats/wifi-example-apps.h ns-3.38/examples/stats/wifi-example-apps.h
--- ns-3.37/examples/stats/wifi-example-apps.h	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/stats/wifi-example-apps.h	2023-03-16 20:54:23.568868400 -0700
@@ -31,6 +31,10 @@
 
 using namespace ns3;
 
+// ==============================================
+// SENDER
+// ==============================================
+
 /**
  * Sender application.
  */
@@ -42,6 +46,7 @@
      * \return The object TypeId.
      */
     static TypeId GetTypeId();
+
     Sender();
     ~Sender() override;
 
@@ -57,23 +62,24 @@
      */
     void SendPacket();
 
-    uint32_t m_pktSize;                     //!< The packet size.
-    Ipv4Address m_destAddr;                 //!< Destination address.
-    uint32_t m_destPort;                    //!< Destination port.
-    Ptr<ConstantRandomVariable> m_interval; //!< Rng for sending packets.
-    uint32_t m_numPkts;                     //!< Number of packets to send.
+    Ipv4Address m_destAddr;                 //!< Destination address
+    uint32_t m_destPort{0};                 //!< Destination port
+    uint32_t m_packetSize{0};               //!< The packet size
+    Ptr<ConstantRandomVariable> m_interval; //!< Rng for sending packets
+    uint32_t m_nPackets{0};                 //!< Number of packets to send
+    uint32_t m_count{0};                    //!< Number of packets sent
 
-    Ptr<Socket> m_socket; //!< Sending socket.
-    EventId m_sendEvent;  //!< Send packet event.
+    Ptr<Socket> m_socket; //!< Sending socket
+    EventId m_sendEvent;  //!< Send packet event
 
-    /// Tx TracedCallback.
+    /// Tx TracedCallback
     TracedCallback<Ptr<const Packet>> m_txTrace;
-
-    uint32_t m_count; //!< Number of packets sent.
-
-    // end class Sender
 };
 
+// ==============================================
+// RECEIVER
+// ==============================================
+
 /**
  * Receiver application.
  */
@@ -85,6 +91,7 @@
      * \return The object TypeId.
      */
     static TypeId GetTypeId();
+
     Receiver();
     ~Receiver() override;
 
@@ -113,51 +120,9 @@
      */
     void Receive(Ptr<Socket> socket);
 
-    Ptr<Socket> m_socket; //!< Receiving socket.
-    uint32_t m_port;      //!< Listening port.
-
-    Ptr<CounterCalculator<>> m_calc;           //!< Counter of the number of received packets.
-    Ptr<TimeMinMaxAvgTotalCalculator> m_delay; //!< Delay calculator.
-
-    // end class Receiver
-};
-
-/**
- * Timestamp tag - it carries when the packet has been sent.
- *
- * It would have been more realistic to include this info in
- * a header. Here we show how to avoid the extra overhead in
- * a simulation.
- */
-class TimestampTag : public Tag
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return The object TypeId.
-     */
-    static TypeId GetTypeId();
-    TypeId GetInstanceTypeId() const override;
-
-    uint32_t GetSerializedSize() const override;
-    void Serialize(TagBuffer i) const override;
-    void Deserialize(TagBuffer i) override;
-
-    /**
-     * Set the timestamp.
-     * \param time The timestamp.
-     */
-    void SetTimestamp(Time time);
-    /**
-     * Get the timestamp.
-     * \return the timestamp.
-     */
-    Time GetTimestamp() const;
-
-    void Print(std::ostream& os) const override;
-
-  private:
-    Time m_timestamp; //!< Timestamp.
+    Ptr<Socket> m_socket; //!< Receiving socket
+    uint32_t m_port{0};   //!< Listening port
 
-    // end class TimestampTag
+    Ptr<CounterCalculator<>> m_calc;           //!< Counter of the number of received packets
+    Ptr<TimeMinMaxAvgTotalCalculator> m_delay; //!< Delay calculator
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/stats/wifi-example-sim.cc ns-3.38/examples/stats/wifi-example-sim.cc
--- ns-3.37/examples/stats/wifi-example-sim.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/stats/wifi-example-sim.cc	2023-03-16 20:54:23.568868400 -0700
@@ -36,7 +36,7 @@
 #include "ns3/mobility-module.h"
 #include "ns3/network-module.h"
 #include "ns3/stats-module.h"
-#include "ns3/yans-wifi-helper.h"
+#include "ns3/wifi-module.h"
 
 #include <ctime>
 #include <sstream>
@@ -49,20 +49,16 @@
  * Function called when a packet is transmitted.
  *
  * \param datac The counter of the number of transmitted packets.
- * \param path The callback context
- * \param packet The transmsiotted packet.
+ * \param path The callback context.
+ * \param packet The transmitted packet.
  */
 void
 TxCallback(Ptr<CounterCalculator<uint32_t>> datac, std::string path, Ptr<const Packet> packet)
 {
     NS_LOG_INFO("Sent frame counted in " << datac->GetKey());
     datac->Update();
-    // end TxCallback
 }
 
-//----------------------------------------------------------------------
-//-- main
-//----------------------------------------------
 int
 main(int argc, char* argv[])
 {
@@ -109,7 +105,7 @@
         input = sstr.str();
     }
 
-    //------------------------------------------------------------
+    //--------------------------------------------
     //-- Create nodes and network stacks
     //--------------------------------------------
     NS_LOG_INFO("Creating nodes.");
@@ -131,7 +127,7 @@
     ipAddrs.SetBase("192.168.0.0", "255.255.255.0");
     ipAddrs.Assign(nodeDevices);
 
-    //------------------------------------------------------------
+    //--------------------------------------------
     //-- Setup physical layout
     //--------------------------------------------
     NS_LOG_INFO("Installing static mobility; distance " << distance << " .");
@@ -142,7 +138,7 @@
     mobility.SetPositionAllocator(positionAlloc);
     mobility.Install(nodes);
 
-    //------------------------------------------------------------
+    //--------------------------------------------
     //-- Create a custom traffic source and sink
     //--------------------------------------------
     NS_LOG_INFO("Create traffic source & sink.");
@@ -159,7 +155,7 @@
     Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination",
                 Ipv4AddressValue("192.168.0.2"));
 
-    //------------------------------------------------------------
+    //--------------------------------------------
     //-- Setup stats and data collection
     //--------------------------------------------
 
@@ -212,23 +208,24 @@
     receiver->SetCounter(appRx);
     data.AddDataCalculator(appRx);
 
-    /**
-     * Just to show this is here...
-     Ptr<MinMaxAvgTotalCalculator<uint32_t> > test =
-     CreateObject<MinMaxAvgTotalCalculator<uint32_t> >();
-     test->SetKey("test-dc");
-     data.AddDataCalculator(test);
-
-     test->Update(4);
-     test->Update(8);
-     test->Update(24);
-     test->Update(12);
-    **/
+    // Just to show this is here...
+
+    /*
+    Ptr<MinMaxAvgTotalCalculator<uint32_t>> test =
+        CreateObject<MinMaxAvgTotalCalculator<uint32_t>>();
+    test->SetKey("test-dc");
+    data.AddDataCalculator(test);
+
+    test->Update(4);
+    test->Update(8);
+    test->Update(24);
+    test->Update(12);
+    */
 
     // This DataCalculator connects directly to the transmit trace
     // provided by our Sender Application.  It records some basic
     // statistics about the sizes of the packets received (min, max,
-    // avg, total # bytes), although in this scenaro they're fixed.
+    // avg, total # bytes), although in this scenario they're fixed.
     Ptr<PacketSizeMinMaxAvgTotalCalculator> appTxPkts =
         CreateObject<PacketSizeMinMaxAvgTotalCalculator>();
     appTxPkts->SetKey("tx-pkt-size");
@@ -247,13 +244,13 @@
     receiver->SetDelayTracker(delayStat);
     data.AddDataCalculator(delayStat);
 
-    //------------------------------------------------------------
+    //--------------------------------------------
     //-- Run the simulation
     //--------------------------------------------
     NS_LOG_INFO("Run Simulation.");
     Simulator::Run();
 
-    //------------------------------------------------------------
+    //--------------------------------------------
     //-- Generate statistics output.
     //--------------------------------------------
 
@@ -286,6 +283,5 @@
     // Free any memory here at the end of this example.
     Simulator::Destroy();
 
-    // end main
     return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tcp/tcp-linux-reno.cc ns-3.38/examples/tcp/tcp-linux-reno.cc
--- ns-3.37/examples/tcp/tcp-linux-reno.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/tcp/tcp-linux-reno.cc	2023-03-16 20:54:23.568868400 -0700
@@ -152,21 +152,11 @@
     // Set recovery algorithm and TCP variant
     Config::SetDefault("ns3::TcpL4Protocol::RecoveryType",
                        TypeIdValue(TypeId::LookupByName(recovery)));
-    if (tcpTypeId == "ns3::TcpWestwoodPlus")
-    {
-        // TcpWestwoodPlus is not an actual TypeId name; we need TcpWestwood here
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpWestwood::GetTypeId()));
-        // the default protocol type in ns3::TcpWestwood is WESTWOOD
-        Config::SetDefault("ns3::TcpWestwood::ProtocolType", EnumValue(TcpWestwood::WESTWOODPLUS));
-    }
-    else
-    {
-        TypeId tcpTid;
-        NS_ABORT_MSG_UNLESS(TypeId::LookupByNameFailSafe(tcpTypeId, &tcpTid),
-                            "TypeId " << tcpTypeId << " not found");
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType",
-                           TypeIdValue(TypeId::LookupByName(tcpTypeId)));
-    }
+    TypeId tcpTid;
+    NS_ABORT_MSG_UNLESS(TypeId::LookupByNameFailSafe(tcpTypeId, &tcpTid),
+                        "TypeId " << tcpTypeId << " not found");
+    Config::SetDefault("ns3::TcpL4Protocol::SocketType",
+                       TypeIdValue(TypeId::LookupByName(tcpTypeId)));
 
     // Create nodes
     NodeContainer leftNodes;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tcp/tcp-star-server.cc ns-3.38/examples/tcp/tcp-star-server.cc
--- ns-3.37/examples/tcp/tcp-star-server.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/tcp/tcp-star-server.cc	2023-03-16 20:54:23.568868400 -0700
@@ -36,7 +36,7 @@
 //       ./ns3 run "tcp-star-server --ns3::OnOffApplication::DataRate=10000"
 //       ./ns3 run "tcp-star-server --ns3::OnOffApplication::PacketSize=500"
 // See the ns-3 tutorial for more info on the command line:
-// http://www.nsnam.org/tutorials.html
+// https://www.nsnam.org/docs/tutorial/html/index.html
 
 #include "ns3/applications-module.h"
 #include "ns3/core-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tcp/tcp-validation.cc ns-3.38/examples/tcp/tcp-validation.cc
--- ns-3.37/examples/tcp/tcp-validation.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/tcp/tcp-validation.cc	2023-03-16 20:54:23.568868400 -0700
@@ -34,7 +34,7 @@
 
 // This program is designed to observe long-running TCP congestion control
 // behavior over a configurable bottleneck link.  The program is also
-// instrumented to check progam data against validated results, when
+// instrumented to check program data against validated results, when
 // the validation option is enabled.
 //
 // ---> downstream (primary data transfer from servers to clients)
@@ -181,7 +181,7 @@
 {
     // TCP segment size is configured below to be 1448 bytes
     // so that we can report cwnd in units of segments
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << static_cast<double>(newCwnd) / 1448
                   << std::endl;
@@ -225,7 +225,7 @@
 void
 TraceFirstDctcp(std::ofstream* ofStream, uint32_t bytesMarked, uint32_t bytesAcked, double alpha)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << alpha << std::endl;
     }
@@ -277,7 +277,7 @@
 void
 TraceFirstRtt(std::ofstream* ofStream, Time oldRtt, Time newRtt)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << newRtt.GetSeconds() * 1000
                   << std::endl;
@@ -296,7 +296,7 @@
 {
     // TCP segment size is configured below to be 1448 bytes
     // so that we can report cwnd in units of segments
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << static_cast<double>(newCwnd) / 1448
                   << std::endl;
@@ -313,7 +313,7 @@
 void
 TraceSecondRtt(std::ofstream* ofStream, Time oldRtt, Time newRtt)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << newRtt.GetSeconds() * 1000
                   << std::endl;
@@ -331,7 +331,7 @@
 void
 TraceSecondDctcp(std::ofstream* ofStream, uint32_t bytesMarked, uint32_t bytesAcked, double alpha)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << alpha << std::endl;
     }
@@ -344,9 +344,9 @@
  * \param rtt RTT value.
  */
 void
-TracePingRtt(std::ofstream* ofStream, Time rtt)
+TracePingRtt(std::ofstream* ofStream, uint16_t, Time rtt)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << rtt.GetSeconds() * 1000 << std::endl;
     }
@@ -385,7 +385,7 @@
 void
 TraceQueueDrop(std::ofstream* ofStream, Ptr<const QueueDiscItem> item)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << std::hex << item->Hash() << std::endl;
     }
@@ -402,7 +402,7 @@
 void
 TraceQueueMark(std::ofstream* ofStream, Ptr<const QueueDiscItem> item, const char* reason)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << std::hex << item->Hash() << std::endl;
     }
@@ -421,7 +421,7 @@
 TraceQueueLength(std::ofstream* ofStream, DataRate queueLinkRate, uint32_t oldVal, uint32_t newVal)
 {
     // output in units of ms
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << std::fixed
                   << static_cast<double>(newVal * 8) / (queueLinkRate.GetBitRate() / 1000)
@@ -438,7 +438,7 @@
 void
 TraceMarksFrequency(std::ofstream* ofStream, Time marksSamplingInterval)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << g_marksObserved << std::endl;
     }
@@ -459,7 +459,7 @@
 TraceFirstThroughput(std::ofstream* ofStream, Time throughputInterval)
 {
     double throughput = g_firstBytesReceived * 8 / throughputInterval.GetSeconds() / 1e6;
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " " << throughput << std::endl;
     }
@@ -508,7 +508,7 @@
 void
 TraceSecondThroughput(std::ofstream* ofStream, Time throughputInterval)
 {
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         *ofStream << Simulator::Now().GetSeconds() << " "
                   << g_secondBytesReceived * 8 / throughputInterval.GetSeconds() / 1e6 << std::endl;
@@ -681,7 +681,7 @@
     cmd.Parse(argc, argv);
 
     // If validation is selected, perform some configuration checks
-    if (g_validate != "")
+    if (!g_validate.empty())
     {
         NS_ABORT_MSG_UNLESS(g_validate == "dctcp-10ms" || g_validate == "dctcp-80ms" ||
                                 g_validate == "cubic-50ms-no-ecn" || g_validate == "cubic-50ms-ecn",
@@ -689,7 +689,7 @@
         if (g_validate == "dctcp-10ms" || g_validate == "dctcp-80ms")
         {
             NS_ABORT_MSG_UNLESS(firstTcpType == "dctcp", "Incorrect TCP");
-            NS_ABORT_MSG_UNLESS(secondTcpType == "", "Incorrect TCP");
+            NS_ABORT_MSG_UNLESS(secondTcpType.empty(), "Incorrect TCP");
             NS_ABORT_MSG_UNLESS(linkRate == DataRate("50Mbps"), "Incorrect data rate");
             NS_ABORT_MSG_UNLESS(queueUseEcn == true, "Incorrect ECN configuration");
             NS_ABORT_MSG_UNLESS(stopTime >= Seconds(15), "Incorrect stopTime");
@@ -705,7 +705,7 @@
         else if (g_validate == "cubic-50ms-no-ecn" || g_validate == "cubic-50ms-ecn")
         {
             NS_ABORT_MSG_UNLESS(firstTcpType == "cubic", "Incorrect TCP");
-            NS_ABORT_MSG_UNLESS(secondTcpType == "", "Incorrect TCP");
+            NS_ABORT_MSG_UNLESS(secondTcpType.empty(), "Incorrect TCP");
             NS_ABORT_MSG_UNLESS(baseRtt == MilliSeconds(50), "Incorrect RTT");
             NS_ABORT_MSG_UNLESS(linkRate == DataRate("50Mbps"), "Incorrect data rate");
             NS_ABORT_MSG_UNLESS(stopTime >= Seconds(20), "Incorrect stopTime");
@@ -771,7 +771,7 @@
         enableSecondTcp = true;
         secondTcpTypeId = TcpDctcp::GetTypeId();
     }
-    else if (secondTcpType == "")
+    else if (secondTcpType.empty())
     {
         enableSecondTcp = false;
         NS_LOG_DEBUG("No second TCP selected");
@@ -841,7 +841,7 @@
     std::ofstream queueMarkOfStream;
     std::ofstream queueMarksFrequencyOfStream;
     std::ofstream queueLengthOfStream;
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         pingOfStream.open(pingTraceFile, std::ofstream::out);
         firstTcpRttOfStream.open(firstTcpRttTraceFile, std::ofstream::out);
@@ -975,12 +975,13 @@
     ////////////////////////////////////////////////////////////
     // application setup                                      //
     ////////////////////////////////////////////////////////////
-    V4PingHelper pingHelper("192.168.1.2");
+    PingHelper pingHelper(Ipv4Address("192.168.1.2"));
     pingHelper.SetAttribute("Interval", TimeValue(pingInterval));
     pingHelper.SetAttribute("Size", UintegerValue(pingSize));
+    pingHelper.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::SILENT));
     ApplicationContainer pingContainer = pingHelper.Install(pingServer);
-    Ptr<V4Ping> v4Ping = pingContainer.Get(0)->GetObject<V4Ping>();
-    v4Ping->TraceConnectWithoutContext("Rtt", MakeBoundCallback(&TracePingRtt, &pingOfStream));
+    Ptr<Ping> ping = pingContainer.Get(0)->GetObject<Ping>();
+    ping->TraceConnectWithoutContext("Rtt", MakeBoundCallback(&TracePingRtt, &pingOfStream));
     pingContainer.Start(Seconds(1));
     pingContainer.Stop(stopTime - Seconds(1));
 
@@ -1088,7 +1089,7 @@
     Simulator::Run();
     Simulator::Destroy();
 
-    if (g_validate == "")
+    if (g_validate.empty())
     {
         pingOfStream.close();
         firstTcpCwndOfStream.close();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tcp/tcp-variants-comparison.cc ns-3.38/examples/tcp/tcp-variants-comparison.cc
--- ns-3.37/examples/tcp/tcp-variants-comparison.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/tcp/tcp-variants-comparison.cc	2023-03-16 20:54:23.568868400 -0700
@@ -59,14 +59,14 @@
 static std::map<uint32_t, bool> firstSshThr;                    //!< First SlowStart threshold.
 static std::map<uint32_t, bool> firstRtt;                       //!< First RTT.
 static std::map<uint32_t, bool> firstRto;                       //!< First RTO.
-static std::map<uint32_t, Ptr<OutputStreamWrapper>> cWndStream; //!< Congstion window outut stream.
+static std::map<uint32_t, Ptr<OutputStreamWrapper>> cWndStream; //!< Congstion window output stream.
 static std::map<uint32_t, Ptr<OutputStreamWrapper>>
-    ssThreshStream; //!< SlowStart threshold outut stream.
-static std::map<uint32_t, Ptr<OutputStreamWrapper>> rttStream;      //!< RTT outut stream.
-static std::map<uint32_t, Ptr<OutputStreamWrapper>> rtoStream;      //!< RTO outut stream.
-static std::map<uint32_t, Ptr<OutputStreamWrapper>> nextTxStream;   //!< Next TX outut stream.
-static std::map<uint32_t, Ptr<OutputStreamWrapper>> nextRxStream;   //!< Next RX outut stream.
-static std::map<uint32_t, Ptr<OutputStreamWrapper>> inFlightStream; //!< In flight outut stream.
+    ssThreshStream; //!< SlowStart threshold output stream.
+static std::map<uint32_t, Ptr<OutputStreamWrapper>> rttStream;      //!< RTT output stream.
+static std::map<uint32_t, Ptr<OutputStreamWrapper>> rtoStream;      //!< RTO output stream.
+static std::map<uint32_t, Ptr<OutputStreamWrapper>> nextTxStream;   //!< Next TX output stream.
+static std::map<uint32_t, Ptr<OutputStreamWrapper>> nextRxStream;   //!< Next RX output stream.
+static std::map<uint32_t, Ptr<OutputStreamWrapper>> inFlightStream; //!< In flight output stream.
 static std::map<uint32_t, uint32_t> cWndValue;                      //!< congestion window value.
 static std::map<uint32_t, uint32_t> ssThreshValue;                  //!< SlowStart threshold value.
 
@@ -342,7 +342,7 @@
 int
 main(int argc, char* argv[])
 {
-    std::string transport_prot = "TcpWestwood";
+    std::string transport_prot = "TcpWestwoodPlus";
     double error_p = 0.0;
     std::string bandwidth = "2Mbps";
     std::string delay = "0.01ms";
@@ -365,7 +365,7 @@
     cmd.AddValue("transport_prot",
                  "Transport protocol to use: TcpNewReno, TcpLinuxReno, "
                  "TcpHybla, TcpHighSpeed, TcpHtcp, TcpVegas, TcpScalable, TcpVeno, "
-                 "TcpBic, TcpYeah, TcpIllinois, TcpWestwood, TcpWestwoodPlus, TcpLedbat, "
+                 "TcpBic, TcpYeah, TcpIllinois, TcpWestwoodPlus, TcpLedbat, "
                  "TcpLp, TcpDctcp, TcpCubic, TcpBbr",
                  transport_prot);
     cmd.AddValue("error_p", "Packet error rate", error_p);
@@ -423,21 +423,11 @@
     Config::SetDefault("ns3::TcpL4Protocol::RecoveryType",
                        TypeIdValue(TypeId::LookupByName(recovery)));
     // Select TCP variant
-    if (transport_prot == "ns3::TcpWestwoodPlus")
-    {
-        // TcpWestwoodPlus is not an actual TypeId name; we need TcpWestwood here
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpWestwood::GetTypeId()));
-        // the default protocol type in ns3::TcpWestwood is WESTWOOD
-        Config::SetDefault("ns3::TcpWestwood::ProtocolType", EnumValue(TcpWestwood::WESTWOODPLUS));
-    }
-    else
-    {
-        TypeId tcpTid;
-        NS_ABORT_MSG_UNLESS(TypeId::LookupByNameFailSafe(transport_prot, &tcpTid),
-                            "TypeId " << transport_prot << " not found");
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType",
-                           TypeIdValue(TypeId::LookupByName(transport_prot)));
-    }
+    TypeId tcpTid;
+    NS_ABORT_MSG_UNLESS(TypeId::LookupByNameFailSafe(transport_prot, &tcpTid),
+                        "TypeId " << transport_prot << " not found");
+    Config::SetDefault("ns3::TcpL4Protocol::SocketType",
+                       TypeIdValue(TypeId::LookupByName(transport_prot)));
 
     // Create gateways, sources, and sinks
     NodeContainer gateways;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/traffic-control/queue-discs-benchmark.cc ns-3.38/examples/traffic-control/queue-discs-benchmark.cc
--- ns-3.37/examples/traffic-control/queue-discs-benchmark.cc	2022-11-01 17:37:24.443395000 -0700
+++ ns-3.38/examples/traffic-control/queue-discs-benchmark.cc	2023-03-16 20:54:23.568868400 -0700
@@ -38,13 +38,13 @@
 //
 // The output will consist of a number of ping Rtt such as:
 //
-//    /NodeList/0/ApplicationList/2/$ns3::V4Ping/Rtt=111 ms
-//    /NodeList/0/ApplicationList/2/$ns3::V4Ping/Rtt=111 ms
-//    /NodeList/0/ApplicationList/2/$ns3::V4Ping/Rtt=110 ms
-//    /NodeList/0/ApplicationList/2/$ns3::V4Ping/Rtt=111 ms
-//    /NodeList/0/ApplicationList/2/$ns3::V4Ping/Rtt=111 ms
-//    /NodeList/0/ApplicationList/2/$ns3::V4Ping/Rtt=112 ms
-//    /NodeList/0/ApplicationList/2/$ns3::V4Ping/Rtt=111 ms
+//    /NodeList/0/ApplicationList/2/$ns3::Ping/Rtt=111 ms
+//    /NodeList/0/ApplicationList/2/$ns3::Ping/Rtt=111 ms
+//    /NodeList/0/ApplicationList/2/$ns3::Ping/Rtt=110 ms
+//    /NodeList/0/ApplicationList/2/$ns3::Ping/Rtt=111 ms
+//    /NodeList/0/ApplicationList/2/$ns3::Ping/Rtt=111 ms
+//    /NodeList/0/ApplicationList/2/$ns3::Ping/Rtt=112 ms
+//    /NodeList/0/ApplicationList/2/$ns3::Ping/Rtt=111 ms
 //
 // The files output will consist of a trace file with bytes in queue and of a trace file for limits
 // (when BQL is enabled) both for bottleneck NetDevice on n2, two files with upload and download
@@ -67,9 +67,9 @@
 NS_LOG_COMPONENT_DEFINE("BenchmarkQueueDiscs");
 
 /**
- * Print the queue limitis.
+ * Print the queue limits.
  *
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param oldVal Old value.
  * \param newVal New value.
  */
@@ -82,7 +82,7 @@
 /**
  * Print the bytes in the queue.
  *
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param oldVal Old value.
  * \param newVal New value.
  */
@@ -96,7 +96,7 @@
  * Sample and print the queue goodput.
  *
  * \param app The Tx app.
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param period The sampling period.
  */
 static void
@@ -116,7 +116,7 @@
  * \param rtt The RTT.
  */
 static void
-PingRtt(std::string context, Time rtt)
+PingRtt(std::string context, uint16_t, Time rtt)
 {
     std::cout << context << "=" << rtt.GetMilliSeconds() << " ms" << std::endl;
 }
@@ -326,10 +326,11 @@
     sourceApps.Add(onOffHelperDown.Install(n3));
 
     // Configure and install ping
-    V4PingHelper ping = V4PingHelper(n3Interface.GetAddress(0));
+    PingHelper ping(n3Interface.GetAddress(0));
+    ping.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::QUIET));
     ping.Install(n1);
 
-    Config::Connect("/NodeList/*/ApplicationList/*/$ns3::V4Ping/Rtt", MakeCallback(&PingRtt));
+    Config::Connect("/NodeList/*/ApplicationList/*/$ns3::Ping/Rtt", MakeCallback(&PingRtt));
 
     uploadApp.Start(Seconds(0));
     uploadApp.Stop(Seconds(stopTime));
@@ -359,6 +360,8 @@
     FlowMonitorHelper flowHelper;
     flowMonitor = flowHelper.InstallAll();
 
+    accessLink.EnablePcapAll("queue");
+
     Simulator::Stop(Seconds(stopTime));
     Simulator::Run();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tutorial/first.py ns-3.38/examples/tutorial/first.py
--- ns-3.37/examples/tutorial/first.py	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/tutorial/first.py	2023-03-16 20:54:23.568868400 -0700
@@ -49,7 +49,7 @@
 serverApps.Start(ns.core.Seconds(1.0))
 serverApps.Stop(ns.core.Seconds(10.0))
 
-address = ns.addressFromIpv4Address(interfaces.GetAddress(1))
+address = interfaces.GetAddress(1).ConvertTo()
 echoClient = ns.applications.UdpEchoClientHelper(address, 9)
 echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
 echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0)))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tutorial/second.py ns-3.38/examples/tutorial/second.py
--- ns-3.37/examples/tutorial/second.py	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/tutorial/second.py	2023-03-16 20:54:23.568868400 -0700
@@ -76,7 +76,7 @@
 serverApps.Start(ns.core.Seconds(1.0))
 serverApps.Stop(ns.core.Seconds(10.0))
 
-echoClient = ns.applications.UdpEchoClientHelper(ns.addressFromIpv4Address(csmaInterfaces.GetAddress(nCsma.value)), 9)
+echoClient = ns.applications.UdpEchoClientHelper(csmaInterfaces.GetAddress(nCsma.value).ConvertTo(), 9)
 echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
 echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
 echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tutorial/seventh.cc ns-3.38/examples/tutorial/seventh.cc
--- ns-3.37/examples/tutorial/seventh.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/tutorial/seventh.cc	2023-03-16 20:54:23.568868400 -0700
@@ -69,7 +69,7 @@
 /**
  * Congestion window change callback
  *
- * \param stream The ouput stream file.
+ * \param stream The output stream file.
  * \param oldCwnd Old congestion window.
  * \param newCwnd New congestion window.
  */
@@ -84,7 +84,7 @@
 /**
  * Rx drop callback
  *
- * \param file The ouput PCAP file.
+ * \param file The output PCAP file.
  * \param p The dropped packet.
  */
 static void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tutorial/sixth.cc ns-3.38/examples/tutorial/sixth.cc
--- ns-3.37/examples/tutorial/sixth.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/tutorial/sixth.cc	2023-03-16 20:54:23.568868400 -0700
@@ -65,7 +65,7 @@
 /**
  * Congestion window change callback
  *
- * \param stream The ouput stream file.
+ * \param stream The output stream file.
  * \param oldCwnd Old congestion window.
  * \param newCwnd New congestion window.
  */
@@ -80,7 +80,7 @@
 /**
  * Rx drop callback
  *
- * \param file The ouput PCAP file.
+ * \param file The output PCAP file.
  * \param p The dropped packet.
  */
 static void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tutorial/third.py ns-3.38/examples/tutorial/third.py
--- ns-3.37/examples/tutorial/third.py	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/tutorial/third.py	2023-03-16 20:54:23.572868300 -0700
@@ -126,7 +126,7 @@
 serverApps.Start(ns.core.Seconds(1.0))
 serverApps.Stop(ns.core.Seconds(10.0))
 
-echoClient = ns.applications.UdpEchoClientHelper(ns.addressFromIpv4Address(csmaInterfaces.GetAddress(nCsma.value)), 9)
+echoClient = ns.applications.UdpEchoClientHelper(csmaInterfaces.GetAddress(nCsma.value).ConvertTo(), 9)
 echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
 echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
 echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/tutorial/tutorial-app.h ns-3.38/examples/tutorial/tutorial-app.h
--- ns-3.37/examples/tutorial/tutorial-app.h	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/tutorial/tutorial-app.h	2023-03-16 20:54:23.572868300 -0700
@@ -46,7 +46,7 @@
      * \param address The destination address.
      * \param packetSize The packet size to transmit.
      * \param nPackets The number of packets to transmit.
-     * \param dataRate the datarate to use.
+     * \param dataRate the data rate to use.
      */
     void Setup(Ptr<Socket> socket,
                Address address,
@@ -63,14 +63,14 @@
     /// Send a packet.
     void SendPacket();
 
-    Ptr<Socket> m_socket;   //!< The tranmission socket.
+    Ptr<Socket> m_socket;   //!< The transmission socket.
     Address m_peer;         //!< The destination address.
     uint32_t m_packetSize;  //!< The packet size.
-    uint32_t m_nPackets;    //!< The number of pacts to send.
-    DataRate m_dataRate;    //!< The datarate to use.
+    uint32_t m_nPackets;    //!< The number of packets to send.
+    DataRate m_dataRate;    //!< The data rate to use.
     EventId m_sendEvent;    //!< Send event.
     bool m_running;         //!< True if the application is running.
-    uint32_t m_packetsSent; //!< The number of pacts sent.
+    uint32_t m_packetsSent; //!< The number of packets sent.
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/CMakeLists.txt ns-3.38/examples/wireless/CMakeLists.txt
--- ns-3.37/examples/wireless/CMakeLists.txt	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/CMakeLists.txt	2023-03-16 20:54:23.572868300 -0700
@@ -291,3 +291,10 @@
   SOURCE_FILES wifi-ofdm-eht-validation.cc
   LIBRARIES_TO_LINK ${libwifi}
 )
+
+build_example(
+  NAME wifi-eht-network
+  SOURCE_FILES wifi-eht-network.cc
+  LIBRARIES_TO_LINK ${libwifi}
+                    ${libapplications}
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/examples-to-run.py ns-3.38/examples/wireless/examples-to-run.py
--- ns-3.37/examples/wireless/examples-to-run.py	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/examples-to-run.py	2023-03-16 20:54:23.572868300 -0700
@@ -39,11 +39,11 @@
     ("wifi-error-models-comparison", "True", "True"),
     ("wifi-80211n-mimo --simulationTime=0.1 --step=10", "True", "True"),
     ("wifi-ht-network --simulationTime=0.2 --frequency=5 --useRts=0 --minExpectedThroughput=5 --maxExpectedThroughput=135", "True", "True"),
-    ("wifi-ht-network --simulationTime=0.2 --frequency=5 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=131", "True", "True"),
+    ("wifi-ht-network --simulationTime=0.2 --frequency=5 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=132", "True", "True"),
     ("wifi-ht-network --simulationTime=0.2 --frequency=2.4 --useRts=0 --minExpectedThroughput=5 --maxExpectedThroughput=132", "True", "True"),
     ("wifi-ht-network --simulationTime=0.2 --frequency=2.4 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=129", "True", "True"),
     ("wifi-vht-network --simulationTime=0.2 --useRts=0  --minExpectedThroughput=5 --maxExpectedThroughput=583", "True", "True"),
-    ("wifi-vht-network --simulationTime=0.2 --useRts=1  --minExpectedThroughput=5 --maxExpectedThroughput=547", "True", "True"),
+    ("wifi-vht-network --simulationTime=0.2 --useRts=1  --minExpectedThroughput=5 --maxExpectedThroughput=557", "True", "True"),
     ("wifi-he-network --simulationTime=0.25 --frequency=5 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=844", "True", "True"),
     ("wifi-he-network --simulationTime=0.3 --frequency=5 --useRts=0 --useExtendedBlockAck=1 --minExpectedThroughput=6 --maxExpectedThroughput=1033", "True", "True"),
     ("wifi-he-network --simulationTime=0.3 --frequency=5 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=745", "True", "True"),
@@ -53,6 +53,15 @@
     ("wifi-he-network --simulationTime=0.3 --frequency=2.4 --udp=0 --downlink=1 --useRts=1 --nStations=5 --dlAckType=MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --minExpectedThroughput=27 --maxExpectedThroughput=50", "True", "True"),
     ("wifi-he-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=0 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=6 --muSchedAccessReqInterval=50ms --minExpectedThroughput=31 --maxExpectedThroughput=290", "True", "True"),
     ("wifi-he-network --simulationTime=0.3 --udp=1 --downlink=0 --useRts=1 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --muSchedAccessReqInterval=50ms --minExpectedThroughput=46 --maxExpectedThroughput=327", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=550", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=0 --useExtendedBlockAck=1 --frequency2=6 --minExpectedThroughput=12 --maxExpectedThroughput=550", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=547", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=0 --useExtendedBlockAck=1 --frequency2=5 --minExpectedThroughput=12 --maxExpectedThroughput=500", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=212", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.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.25 --frequency=2.4 --udp=0 --downlink=1 --useRts=0 --nStations=5 --dlAckType=MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --frequency2=5 --useExtendedBlockAck=1 --minExpectedThroughput=40 --maxExpectedThroughput=100", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=1 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=6 --muSchedAccessReqInterval=50ms --frequency2=2.4 --minExpectedThroughput=50 --maxExpectedThroughput=140", "True", "True"),
+    ("wifi-eht-network --simulationTime=0.2 --udp=1 --downlink=0 --useRts=0 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --muSchedAccessReqInterval=50ms --frequency2=6 --minExpectedThroughput=70 --maxExpectedThroughput=715", "True", "True"),
     ("wifi-simple-ht-hidden-stations --simulationTime=1 --enableRts=0 --nMpdus=32 --minExpectedThroughput=59 --maxExpectedThroughput=60", "True", "True"),
     ("wifi-simple-ht-hidden-stations --simulationTime=1 --enableRts=1 --nMpdus=32 --minExpectedThroughput=57 --maxExpectedThroughput=58", "True", "True"),
     ("wifi-mixed-network --simulationTime=1", "True", "True"),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/mixed-wired-wireless.py ns-3.38/examples/wireless/mixed-wired-wireless.py
--- ns-3.37/examples/wireless/mixed-wired-wireless.py	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/mixed-wired-wireless.py	2023-03-16 20:54:23.572868300 -0700
@@ -320,15 +320,14 @@
     # Let's fetch the IP address of the last node, which is on Ipv4Interface 1
     remoteAddr = ns.cppyy.gbl.getIpv4AddressFromNode(appSink)
     socketAddr = ns.network.InetSocketAddress(remoteAddr, port)
-    genericAddress = ns.addressFromInetSocketAddress(socketAddr)
-    onoff = ns.applications.OnOffHelper("ns3::UdpSocketFactory", genericAddress)
+    onoff = ns.applications.OnOffHelper("ns3::UdpSocketFactory", socketAddr.ConvertTo())
     apps = onoff.Install(ns.network.NodeContainer(appSource))
     apps.Start(ns.core.Seconds(3))
     apps.Stop(ns.core.Seconds(stopTime.value - 1))
 
     #  Create a packet sink to receive these packets
     sink = ns.applications.PacketSinkHelper("ns3::UdpSocketFactory",
-                                ns.addressFromInetSocketAddress(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), port)))
+                                ns.network.InetSocketAddress(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), port)).ConvertTo())
     sinkContainer = ns.network.NodeContainer(appSink)
     apps = sink.Install(sinkContainer)
     apps.Start(ns.core.Seconds(3))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-80211n-mimo.cc ns-3.38/examples/wireless/wifi-80211n-mimo.cc
--- ns-3.37/examples/wireless/wifi-80211n-mimo.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-80211n-mimo.cc	2023-03-16 20:54:23.572868300 -0700
@@ -62,42 +62,12 @@
 {
     std::ofstream file("80211n-mimo-throughput.plt");
 
-    // clang-format off
-  std::vector <std::string> modes = {
-    "HtMcs0",
-    "HtMcs1",
-    "HtMcs2",
-    "HtMcs3",
-    "HtMcs4",
-    "HtMcs5",
-    "HtMcs6",
-    "HtMcs7",
-    "HtMcs8",
-    "HtMcs9",
-    "HtMcs10",
-    "HtMcs11",
-    "HtMcs12",
-    "HtMcs13",
-    "HtMcs14",
-    "HtMcs15",
-    "HtMcs16",
-    "HtMcs17",
-    "HtMcs18",
-    "HtMcs19",
-    "HtMcs20",
-    "HtMcs21",
-    "HtMcs22",
-    "HtMcs23",
-    "HtMcs24",
-    "HtMcs25",
-    "HtMcs26",
-    "HtMcs27",
-    "HtMcs28",
-    "HtMcs29",
-    "HtMcs30",
-    "HtMcs31",
-  };
-    // clang-format on
+    std::vector<std::string> modes = {
+        "HtMcs0",  "HtMcs1",  "HtMcs2",  "HtMcs3",  "HtMcs4",  "HtMcs5",  "HtMcs6",  "HtMcs7",
+        "HtMcs8",  "HtMcs9",  "HtMcs10", "HtMcs11", "HtMcs12", "HtMcs13", "HtMcs14", "HtMcs15",
+        "HtMcs16", "HtMcs17", "HtMcs18", "HtMcs19", "HtMcs20", "HtMcs21", "HtMcs22", "HtMcs23",
+        "HtMcs24", "HtMcs25", "HtMcs26", "HtMcs27", "HtMcs28", "HtMcs29", "HtMcs30", "HtMcs31",
+    };
 
     bool udp = true;
     double simulationTime = 5; // seconds
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-adhoc.cc ns-3.38/examples/wireless/wifi-adhoc.cc
--- ns-3.37/examples/wireless/wifi-adhoc.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-adhoc.cc	2023-03-16 20:54:23.572868300 -0700
@@ -67,7 +67,7 @@
   private:
     /**
      * Receive a packet.
-     * \param socket The recieving socket.
+     * \param socket The receiving socket.
      */
     void ReceivePacket(Ptr<Socket> socket);
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-ap.py ns-3.38/examples/wireless/wifi-ap.py
--- ns-3.37/examples/wireless/wifi-ap.py	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-ap.py	2023-03-16 20:54:23.572868300 -0700
@@ -128,8 +128,7 @@
     socket.SetPhysicalAddress(staDevs.Get(1).GetAddress())
     socket.SetProtocol(1)
 
-    genericAddress = ns.addressFromPacketSocketAddress(socket)
-    onoff = ns.applications.OnOffHelper("ns3::PacketSocketFactory", genericAddress)
+    onoff = ns.applications.OnOffHelper("ns3::PacketSocketFactory", socket.ConvertTo())
     onoff.SetConstantRate (ns.network.DataRate ("500kb/s"))
 
     apps = onoff.Install(ns.network.NodeContainer(stas.Get(0)))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-clear-channel-cmu.cc ns-3.38/examples/wireless/wifi-clear-channel-cmu.cc
--- ns-3.37/examples/wireless/wifi-clear-channel-cmu.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-clear-channel-cmu.cc	2023-03-16 20:54:23.572868300 -0700
@@ -64,7 +64,7 @@
   private:
     /**
      * Receive a packet.
-     * \param socket The recieving socket.
+     * \param socket The receiving socket.
      */
     void ReceivePacket(Ptr<Socket> socket);
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-eht-network.cc ns-3.38/examples/wireless/wifi-eht-network.cc
--- ns-3.37/examples/wireless/wifi-eht-network.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/examples/wireless/wifi-eht-network.cc	2023-03-16 20:54:23.572868300 -0700
@@ -0,0 +1,579 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2022
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Sebastien Deronne <sebastien.deronne@gmail.com>
+ */
+
+#include "ns3/boolean.h"
+#include "ns3/command-line.h"
+#include "ns3/config.h"
+#include "ns3/double.h"
+#include "ns3/eht-phy.h"
+#include "ns3/enum.h"
+#include "ns3/internet-stack-helper.h"
+#include "ns3/ipv4-address-helper.h"
+#include "ns3/ipv4-global-routing-helper.h"
+#include "ns3/log.h"
+#include "ns3/mobility-helper.h"
+#include "ns3/multi-model-spectrum-channel.h"
+#include "ns3/on-off-helper.h"
+#include "ns3/packet-sink-helper.h"
+#include "ns3/packet-sink.h"
+#include "ns3/rng-seed-manager.h"
+#include "ns3/spectrum-wifi-helper.h"
+#include "ns3/ssid.h"
+#include "ns3/string.h"
+#include "ns3/udp-client-server-helper.h"
+#include "ns3/uinteger.h"
+#include "ns3/wifi-acknowledgment.h"
+#include "ns3/yans-wifi-channel.h"
+#include "ns3/yans-wifi-helper.h"
+
+#include <array>
+#include <functional>
+#include <numeric>
+
+// This is a simple example in order to show how to configure an IEEE 802.11be Wi-Fi network.
+//
+// It outputs the UDP or TCP goodput for every EHT MCS value, which depends on the MCS value (0 to
+// 13), the channel width (20, 40, 80 or 160 MHz) and the guard interval (800ns, 1600ns or 3200ns).
+// The PHY bitrate is constant over all the simulation run. The user can also specify the distance
+// between the access point and the station: the larger the distance the smaller the goodput.
+//
+// The simulation assumes a configurable number of stations in an infrastructure network:
+//
+//  STA     AP
+//    *     *
+//    |     |
+//   n1     n2
+//
+// Packets in this simulation belong to BestEffort Access Class (AC_BE).
+// By selecting an acknowledgment sequence for DL MU PPDUs, it is possible to aggregate a
+// Round Robin scheduler to the AP, so that DL MU PPDUs are sent by the AP via DL OFDMA.
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("eht-wifi-network");
+
+/**
+ * \param udp true if UDP is used, false if TCP is used
+ * \param serverApp a container of server applications
+ * \param payloadSize the size in bytes of the packets
+ * \return the bytes received by each server application
+ */
+std::vector<uint64_t>
+GetRxBytes(bool udp, const ApplicationContainer& serverApp, uint32_t payloadSize)
+{
+    std::vector<uint64_t> rxBytes(serverApp.GetN(), 0);
+    if (udp)
+    {
+        for (uint32_t i = 0; i < serverApp.GetN(); i++)
+        {
+            rxBytes[i] = payloadSize * DynamicCast<UdpServer>(serverApp.Get(i))->GetReceived();
+        }
+    }
+    else
+    {
+        for (uint32_t i = 0; i < serverApp.GetN(); i++)
+        {
+            rxBytes[i] = DynamicCast<PacketSink>(serverApp.Get(i))->GetTotalRx();
+        }
+    }
+    return rxBytes;
+};
+
+/**
+ * Print average throughput over an intermediate time interval.
+ * \param rxBytes a vector of the amount of bytes received by each server application
+ * \param udp true if UDP is used, false if TCP is used
+ * \param serverApp a container of server applications
+ * \param payloadSize the size in bytes of the packets
+ * \param tputInterval the duration of an intermediate time interval
+ * \param simulationTime the simulation time in seconds
+ */
+void
+PrintIntermediateTput(std::vector<uint64_t>& rxBytes,
+                      bool udp,
+                      const ApplicationContainer& serverApp,
+                      uint32_t payloadSize,
+                      Time tputInterval,
+                      double simulationTime)
+{
+    auto newRxBytes = GetRxBytes(udp, serverApp, payloadSize);
+    Time now = Simulator::Now();
+
+    std::cout << "[" << (now - tputInterval).As(Time::S) << " - " << now.As(Time::S)
+              << "] Per-STA Throughput (Mbit/s):";
+
+    for (std::size_t i = 0; i < newRxBytes.size(); i++)
+    {
+        std::cout << "\t\t(" << i << ") "
+                  << (newRxBytes[i] - rxBytes[i]) * 8. / tputInterval.GetMicroSeconds(); // Mbit/s
+    }
+    std::cout << std::endl;
+
+    rxBytes.swap(newRxBytes);
+
+    if (now < Seconds(simulationTime) - NanoSeconds(1))
+    {
+        Simulator::Schedule(Min(tputInterval, Seconds(simulationTime) - now - NanoSeconds(1)),
+                            &PrintIntermediateTput,
+                            rxBytes,
+                            udp,
+                            serverApp,
+                            payloadSize,
+                            tputInterval,
+                            simulationTime);
+    }
+}
+
+int
+main(int argc, char* argv[])
+{
+    bool udp{true};
+    bool downlink{true};
+    bool useRts{false};
+    bool useExtendedBlockAck{false};
+    double simulationTime{10}; // seconds
+    double distance{1.0};      // meters
+    double frequency{5};       // whether the first link operates in the 2.4, 5 or 6 GHz
+    double frequency2{0}; // whether the second link operates in the 2.4, 5 or 6 GHz (0 means no
+                          // second link exists)
+    double frequency3{
+        0}; // whether the third link operates in the 2.4, 5 or 6 GHz (0 means no third link exists)
+    std::size_t nStations{1};
+    std::string dlAckSeqType{"NO-OFDMA"};
+    bool enableUlOfdma{false};
+    bool enableBsrp{false};
+    int mcs{-1}; // -1 indicates an unset value
+    uint32_t payloadSize =
+        700; // must fit in the max TX duration when transmitting at MCS 0 over an RU of 26 tones
+    Time tputInterval{0}; // interval for detailed throughput measurement
+    double minExpectedThroughput{0};
+    double maxExpectedThroughput{0};
+    Time accessReqInterval{0};
+
+    CommandLine cmd(__FILE__);
+    cmd.AddValue(
+        "frequency",
+        "Whether the first link operates in the 2.4, 5 or 6 GHz band (other values gets rejected)",
+        frequency);
+    cmd.AddValue(
+        "frequency2",
+        "Whether the second link operates in the 2.4, 5 or 6 GHz band (0 means the device has one "
+        "link, otherwise the band must be different than first link and third link)",
+        frequency2);
+    cmd.AddValue(
+        "frequency3",
+        "Whether the third link operates in the 2.4, 5 or 6 GHz band (0 means the device has up to "
+        "two links, otherwise the band must be different than first link and second link)",
+        frequency3);
+    cmd.AddValue("distance",
+                 "Distance in meters between the station and the access point",
+                 distance);
+    cmd.AddValue("simulationTime", "Simulation time in seconds", simulationTime);
+    cmd.AddValue("udp", "UDP if set to 1, TCP otherwise", udp);
+    cmd.AddValue("downlink",
+                 "Generate downlink flows if set to 1, uplink flows otherwise",
+                 downlink);
+    cmd.AddValue("useRts", "Enable/disable RTS/CTS", useRts);
+    cmd.AddValue("useExtendedBlockAck", "Enable/disable use of extended BACK", useExtendedBlockAck);
+    cmd.AddValue("nStations", "Number of non-AP HE stations", nStations);
+    cmd.AddValue("dlAckType",
+                 "Ack sequence type for DL OFDMA (NO-OFDMA, ACK-SU-FORMAT, MU-BAR, AGGR-MU-BAR)",
+                 dlAckSeqType);
+    cmd.AddValue("enableUlOfdma",
+                 "Enable UL OFDMA (useful if DL OFDMA is enabled and TCP is used)",
+                 enableUlOfdma);
+    cmd.AddValue("enableBsrp",
+                 "Enable BSRP (useful if DL and UL OFDMA are enabled and TCP is used)",
+                 enableBsrp);
+    cmd.AddValue(
+        "muSchedAccessReqInterval",
+        "Duration of the interval between two requests for channel access made by the MU scheduler",
+        accessReqInterval);
+    cmd.AddValue("mcs", "if set, limit testing to a specific MCS (0-11)", mcs);
+    cmd.AddValue("payloadSize", "The application payload size in bytes", payloadSize);
+    cmd.AddValue("tputInterval", "duration of intervals for throughput measurement", tputInterval);
+    cmd.AddValue("minExpectedThroughput",
+                 "if set, simulation fails if the lowest throughput is below this value",
+                 minExpectedThroughput);
+    cmd.AddValue("maxExpectedThroughput",
+                 "if set, simulation fails if the highest throughput is above this value",
+                 maxExpectedThroughput);
+    cmd.Parse(argc, argv);
+
+    if (useRts)
+    {
+        Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("0"));
+        Config::SetDefault("ns3::WifiDefaultProtectionManager::EnableMuRts", BooleanValue(true));
+    }
+
+    if (dlAckSeqType == "ACK-SU-FORMAT")
+    {
+        Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
+                           EnumValue(WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE));
+    }
+    else if (dlAckSeqType == "MU-BAR")
+    {
+        Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
+                           EnumValue(WifiAcknowledgment::DL_MU_TF_MU_BAR));
+    }
+    else if (dlAckSeqType == "AGGR-MU-BAR")
+    {
+        Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
+                           EnumValue(WifiAcknowledgment::DL_MU_AGGREGATE_TF));
+    }
+    else if (dlAckSeqType != "NO-OFDMA")
+    {
+        NS_ABORT_MSG("Invalid DL ack sequence type (must be NO-OFDMA, ACK-SU-FORMAT, MU-BAR or "
+                     "AGGR-MU-BAR)");
+    }
+
+    double prevThroughput[12];
+    for (uint32_t l = 0; l < 12; l++)
+    {
+        prevThroughput[l] = 0;
+    }
+    std::cout << "MCS value"
+              << "\t\t"
+              << "Channel width"
+              << "\t\t"
+              << "GI"
+              << "\t\t\t"
+              << "Throughput" << '\n';
+    int minMcs = 0;
+    int maxMcs = 13;
+    if (mcs >= 0 && mcs <= 13)
+    {
+        minMcs = mcs;
+        maxMcs = mcs;
+    }
+    for (int mcs = minMcs; mcs <= maxMcs; mcs++)
+    {
+        uint8_t index = 0;
+        double previous = 0;
+        uint16_t maxChannelWidth =
+            (frequency != 2.4 && frequency2 != 2.4 && frequency3 != 2.4) ? 160 : 40;
+        for (int channelWidth = 20; channelWidth <= maxChannelWidth;) // MHz
+        {
+            for (int gi = 3200; gi >= 800;) // Nanoseconds
+            {
+                if (!udp)
+                {
+                    Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(payloadSize));
+                }
+
+                NodeContainer wifiStaNodes;
+                wifiStaNodes.Create(nStations);
+                NodeContainer wifiApNode;
+                wifiApNode.Create(1);
+
+                NetDeviceContainer apDevice;
+                NetDeviceContainer staDevices;
+                WifiMacHelper mac;
+                WifiHelper wifi;
+
+                wifi.SetStandard(WIFI_STANDARD_80211be);
+                std::array<std::string, 3> channelStr;
+                uint8_t nLinks = 0;
+                std::string dataModeStr = "EhtMcs" + std::to_string(mcs);
+                std::string ctrlRateStr;
+                uint64_t nonHtRefRateMbps = EhtPhy::GetNonHtReferenceRate(mcs) / 1e6;
+
+                if (frequency2 == frequency || frequency3 == frequency ||
+                    (frequency3 != 0 && frequency3 == frequency2))
+                {
+                    std::cout << "Frequency values must be unique!" << std::endl;
+                    return 0;
+                }
+
+                for (auto freq : {frequency, frequency2, frequency3})
+                {
+                    if (nLinks > 0 && freq == 0)
+                    {
+                        break;
+                    }
+                    channelStr[nLinks] = "{0, " + std::to_string(channelWidth) + ", ";
+                    if (freq == 6)
+                    {
+                        channelStr[nLinks] += "BAND_6GHZ, 0}";
+                        Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
+                                           DoubleValue(48));
+                        wifi.SetRemoteStationManager(nLinks,
+                                                     "ns3::ConstantRateWifiManager",
+                                                     "DataMode",
+                                                     StringValue(dataModeStr),
+                                                     "ControlMode",
+                                                     StringValue(dataModeStr));
+                    }
+                    else if (freq == 5)
+                    {
+                        channelStr[nLinks] += "BAND_5GHZ, 0}";
+                        ctrlRateStr = "OfdmRate" + std::to_string(nonHtRefRateMbps) + "Mbps";
+                        wifi.SetRemoteStationManager(nLinks,
+                                                     "ns3::ConstantRateWifiManager",
+                                                     "DataMode",
+                                                     StringValue(dataModeStr),
+                                                     "ControlMode",
+                                                     StringValue(ctrlRateStr));
+                    }
+                    else if (freq == 2.4)
+                    {
+                        channelStr[nLinks] += "BAND_2_4GHZ, 0}";
+                        Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
+                                           DoubleValue(40));
+                        ctrlRateStr = "ErpOfdmRate" + std::to_string(nonHtRefRateMbps) + "Mbps";
+                        wifi.SetRemoteStationManager(nLinks,
+                                                     "ns3::ConstantRateWifiManager",
+                                                     "DataMode",
+                                                     StringValue(dataModeStr),
+                                                     "ControlMode",
+                                                     StringValue(ctrlRateStr));
+                    }
+                    else
+                    {
+                        std::cout << "Wrong frequency value!" << std::endl;
+                        return 0;
+                    }
+                    nLinks++;
+                }
+
+                Ssid ssid = Ssid("ns3-80211be");
+
+                /*
+                 * SingleModelSpectrumChannel cannot be used with 802.11be because two
+                 * spectrum models are required: one with 78.125 kHz bands for HE PPDUs
+                 * and one with 312.5 kHz bands for, e.g., non-HT PPDUs (for more details,
+                 * see issue #408 (CLOSED))
+                 */
+                Ptr<MultiModelSpectrumChannel> spectrumChannel =
+                    CreateObject<MultiModelSpectrumChannel>();
+
+                Ptr<LogDistancePropagationLossModel> lossModel =
+                    CreateObject<LogDistancePropagationLossModel>();
+                spectrumChannel->AddPropagationLossModel(lossModel);
+
+                SpectrumWifiPhyHelper phy(nLinks);
+                phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
+                phy.SetChannel(spectrumChannel);
+
+                mac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid));
+                for (uint8_t linkId = 0; linkId < nLinks; linkId++)
+                {
+                    phy.Set(linkId, "ChannelSettings", StringValue(channelStr[linkId]));
+                }
+                staDevices = wifi.Install(phy, mac, wifiStaNodes);
+
+                if (dlAckSeqType != "NO-OFDMA")
+                {
+                    mac.SetMultiUserScheduler("ns3::RrMultiUserScheduler",
+                                              "EnableUlOfdma",
+                                              BooleanValue(enableUlOfdma),
+                                              "EnableBsrp",
+                                              BooleanValue(enableBsrp),
+                                              "AccessReqInterval",
+                                              TimeValue(accessReqInterval));
+                }
+                mac.SetType("ns3::ApWifiMac",
+                            "EnableBeaconJitter",
+                            BooleanValue(false),
+                            "Ssid",
+                            SsidValue(ssid));
+                apDevice = wifi.Install(phy, mac, wifiApNode);
+
+                RngSeedManager::SetSeed(1);
+                RngSeedManager::SetRun(1);
+                int64_t streamNumber = 100;
+                streamNumber += wifi.AssignStreams(apDevice, streamNumber);
+                streamNumber += wifi.AssignStreams(staDevices, streamNumber);
+
+                // Set guard interval and MPDU buffer size
+                Config::Set(
+                    "/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HeConfiguration/GuardInterval",
+                    TimeValue(NanoSeconds(gi)));
+                Config::Set(
+                    "/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HeConfiguration/MpduBufferSize",
+                    UintegerValue(useExtendedBlockAck ? 256 : 64));
+
+                // mobility.
+                MobilityHelper mobility;
+                Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
+
+                positionAlloc->Add(Vector(0.0, 0.0, 0.0));
+                positionAlloc->Add(Vector(distance, 0.0, 0.0));
+                mobility.SetPositionAllocator(positionAlloc);
+
+                mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+
+                mobility.Install(wifiApNode);
+                mobility.Install(wifiStaNodes);
+
+                /* Internet stack*/
+                InternetStackHelper stack;
+                stack.Install(wifiApNode);
+                stack.Install(wifiStaNodes);
+
+                Ipv4AddressHelper address;
+                address.SetBase("192.168.1.0", "255.255.255.0");
+                Ipv4InterfaceContainer staNodeInterfaces;
+                Ipv4InterfaceContainer apNodeInterface;
+
+                staNodeInterfaces = address.Assign(staDevices);
+                apNodeInterface = address.Assign(apDevice);
+
+                /* Setting applications */
+                ApplicationContainer serverApp;
+                auto serverNodes = downlink ? std::ref(wifiStaNodes) : std::ref(wifiApNode);
+                Ipv4InterfaceContainer serverInterfaces;
+                NodeContainer clientNodes;
+                for (std::size_t i = 0; i < nStations; i++)
+                {
+                    serverInterfaces.Add(downlink ? staNodeInterfaces.Get(i)
+                                                  : apNodeInterface.Get(0));
+                    clientNodes.Add(downlink ? wifiApNode.Get(0) : wifiStaNodes.Get(i));
+                }
+
+                if (udp)
+                {
+                    // UDP flow
+                    uint16_t port = 9;
+                    UdpServerHelper server(port);
+                    serverApp = server.Install(serverNodes.get());
+                    serverApp.Start(Seconds(0.0));
+                    serverApp.Stop(Seconds(simulationTime + 1));
+
+                    for (std::size_t i = 0; i < nStations; i++)
+                    {
+                        UdpClientHelper client(serverInterfaces.GetAddress(i), port);
+                        client.SetAttribute("MaxPackets", UintegerValue(4294967295U));
+                        client.SetAttribute("Interval", TimeValue(Time("0.00001"))); // packets/s
+                        client.SetAttribute("PacketSize", UintegerValue(payloadSize));
+                        ApplicationContainer clientApp = client.Install(clientNodes.Get(i));
+                        clientApp.Start(Seconds(1.0));
+                        clientApp.Stop(Seconds(simulationTime + 1));
+                    }
+                }
+                else
+                {
+                    // TCP flow
+                    uint16_t port = 50000;
+                    Address localAddress(InetSocketAddress(Ipv4Address::GetAny(), port));
+                    PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory", localAddress);
+                    serverApp = packetSinkHelper.Install(serverNodes.get());
+                    serverApp.Start(Seconds(0.0));
+                    serverApp.Stop(Seconds(simulationTime + 1));
+
+                    for (std::size_t i = 0; i < nStations; i++)
+                    {
+                        OnOffHelper onoff("ns3::TcpSocketFactory", Ipv4Address::GetAny());
+                        onoff.SetAttribute("OnTime",
+                                           StringValue("ns3::ConstantRandomVariable[Constant=1]"));
+                        onoff.SetAttribute("OffTime",
+                                           StringValue("ns3::ConstantRandomVariable[Constant=0]"));
+                        onoff.SetAttribute("PacketSize", UintegerValue(payloadSize));
+                        onoff.SetAttribute("DataRate", DataRateValue(1000000000)); // bit/s
+                        AddressValue remoteAddress(
+                            InetSocketAddress(serverInterfaces.GetAddress(i), port));
+                        onoff.SetAttribute("Remote", remoteAddress);
+                        ApplicationContainer clientApp = onoff.Install(clientNodes.Get(i));
+                        clientApp.Start(Seconds(1.0));
+                        clientApp.Stop(Seconds(simulationTime + 1));
+                    }
+                }
+
+                // cumulative number of bytes received by each server application
+                std::vector<uint64_t> cumulRxBytes(nStations, 0);
+
+                if (tputInterval.IsStrictlyPositive())
+                {
+                    Simulator::Schedule(Seconds(1) + tputInterval,
+                                        &PrintIntermediateTput,
+                                        cumulRxBytes,
+                                        udp,
+                                        serverApp,
+                                        payloadSize,
+                                        tputInterval,
+                                        simulationTime + 1);
+                }
+
+                Simulator::Schedule(Seconds(0), &Ipv4GlobalRoutingHelper::PopulateRoutingTables);
+
+                Simulator::Stop(Seconds(simulationTime + 1));
+                Simulator::Run();
+
+                // When multiple stations are used, there are chances that association requests
+                // collide and hence the throughput may be lower than expected. Therefore, we relax
+                // the check that the throughput cannot decrease by introducing a scaling factor (or
+                // tolerance)
+                double tolerance = 0.10;
+                cumulRxBytes = GetRxBytes(udp, serverApp, payloadSize);
+                uint64_t rxBytes = std::accumulate(cumulRxBytes.cbegin(), cumulRxBytes.cend(), 0);
+                double throughput = (rxBytes * 8) / (simulationTime * 1000000.0); // Mbit/s
+
+                Simulator::Destroy();
+
+                std::cout << mcs << "\t\t\t" << channelWidth << " MHz\t\t\t" << gi << " ns\t\t\t"
+                          << throughput << " Mbit/s" << std::endl;
+
+                // test first element
+                if (mcs == 0 && channelWidth == 20 && gi == 3200)
+                {
+                    if (throughput * (1 + tolerance) < minExpectedThroughput)
+                    {
+                        NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
+                        exit(1);
+                    }
+                }
+                // test last element
+                if (mcs == 11 && channelWidth == 160 && gi == 800)
+                {
+                    if (maxExpectedThroughput > 0 &&
+                        throughput > maxExpectedThroughput * (1 + tolerance))
+                    {
+                        NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
+                        exit(1);
+                    }
+                }
+                // test previous throughput is smaller (for the same mcs)
+                if (throughput * (1 + tolerance) > previous)
+                {
+                    previous = throughput;
+                }
+                else if (throughput > 0)
+                {
+                    NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
+                    exit(1);
+                }
+                // test previous throughput is smaller (for the same channel width and GI)
+                if (throughput * (1 + tolerance) > prevThroughput[index])
+                {
+                    prevThroughput[index] = throughput;
+                }
+                else if (throughput > 0)
+                {
+                    NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
+                    exit(1);
+                }
+                index++;
+                gi /= 2;
+            }
+            channelWidth *= 2;
+        }
+    }
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-he-network.cc ns-3.38/examples/wireless/wifi-he-network.cc
--- ns-3.37/examples/wireless/wifi-he-network.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-he-network.cc	2023-03-16 20:54:23.572868300 -0700
@@ -22,6 +22,7 @@
 #include "ns3/config.h"
 #include "ns3/double.h"
 #include "ns3/enum.h"
+#include "ns3/he-phy.h"
 #include "ns3/internet-stack-helper.h"
 #include "ns3/ipv4-address-helper.h"
 #include "ns3/ipv4-global-routing-helper.h"
@@ -132,6 +133,7 @@
     if (useRts)
     {
         Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("0"));
+        Config::SetDefault("ns3::WifiDefaultProtectionManager::EnableMuRts", BooleanValue(true));
     }
 
     if (dlAckSeqType == "ACK-SU-FORMAT")
@@ -208,10 +210,16 @@
                 WifiMacHelper mac;
                 WifiHelper wifi;
                 std::string channelStr("{0, " + std::to_string(channelWidth) + ", ");
+                StringValue ctrlRate;
+                auto nonHtRefRateMbps = HePhy::GetNonHtReferenceRate(mcs) / 1e6;
+
+                std::ostringstream ossDataMode;
+                ossDataMode << "HeMcs" << mcs;
 
                 if (frequency == 6)
                 {
                     wifi.SetStandard(WIFI_STANDARD_80211ax);
+                    ctrlRate = StringValue(ossDataMode.str());
                     channelStr += "BAND_6GHZ, 0}";
                     Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
                                        DoubleValue(48));
@@ -219,11 +227,17 @@
                 else if (frequency == 5)
                 {
                     wifi.SetStandard(WIFI_STANDARD_80211ax);
+                    std::ostringstream ossControlMode;
+                    ossControlMode << "OfdmRate" << nonHtRefRateMbps << "Mbps";
+                    ctrlRate = StringValue(ossControlMode.str());
                     channelStr += "BAND_5GHZ, 0}";
                 }
                 else if (frequency == 2.4)
                 {
                     wifi.SetStandard(WIFI_STANDARD_80211ax);
+                    std::ostringstream ossControlMode;
+                    ossControlMode << "ErpOfdmRate" << nonHtRefRateMbps << "Mbps";
+                    ctrlRate = StringValue(ossControlMode.str());
                     channelStr += "BAND_2_4GHZ, 0}";
                     Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
                                        DoubleValue(40));
@@ -234,13 +248,11 @@
                     return 0;
                 }
 
-                std::ostringstream oss;
-                oss << "HeMcs" << mcs;
                 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
                                              "DataMode",
-                                             StringValue(oss.str()),
+                                             StringValue(ossDataMode.str()),
                                              "ControlMode",
-                                             StringValue(oss.str()));
+                                             ctrlRate);
                 // Set guard interval and MPDU buffer size
                 wifi.ConfigHeOptions("GuardInterval",
                                      TimeValue(NanoSeconds(gi)),
@@ -259,6 +271,11 @@
                      */
                     Ptr<MultiModelSpectrumChannel> spectrumChannel =
                         CreateObject<MultiModelSpectrumChannel>();
+
+                    Ptr<LogDistancePropagationLossModel> lossModel =
+                        CreateObject<LogDistancePropagationLossModel>();
+                    spectrumChannel->AddPropagationLossModel(lossModel);
+
                     SpectrumWifiPhyHelper phy;
                     phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
                     phy.SetChannel(spectrumChannel);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-hidden-terminal.cc ns-3.38/examples/wireless/wifi-hidden-terminal.cc
--- ns-3.37/examples/wireless/wifi-hidden-terminal.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-hidden-terminal.cc	2023-03-16 20:54:23.572868300 -0700
@@ -49,7 +49,7 @@
 /**
  * Run single 10 seconds experiment
  *
- * \param enableCtsRts if true, enable RTS/CTS for packets larget than 100 bytes.
+ * \param enableCtsRts if true, enable RTS/CTS for packets larger than 100 bytes.
  * \param wifiManager WiFi manager to use.
  */
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-ht-network.cc ns-3.38/examples/wireless/wifi-ht-network.cc
--- ns-3.37/examples/wireless/wifi-ht-network.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-ht-network.cc	2023-03-16 20:54:23.572868300 -0700
@@ -23,6 +23,7 @@
 #include "ns3/config.h"
 #include "ns3/double.h"
 #include "ns3/enum.h"
+#include "ns3/ht-phy.h"
 #include "ns3/internet-stack-helper.h"
 #include "ns3/ipv4-address-helper.h"
 #include "ns3/ipv4-global-routing-helper.h"
@@ -144,14 +145,17 @@
 
                 WifiMacHelper mac;
                 WifiHelper wifi;
+                std::ostringstream ossControlMode;
 
                 if (frequency == 5.0)
                 {
+                    ossControlMode << "OfdmRate";
                     wifi.SetStandard(WIFI_STANDARD_80211n);
                 }
                 else if (frequency == 2.4)
                 {
                     wifi.SetStandard(WIFI_STANDARD_80211n);
+                    ossControlMode << "ErpOfdmRate";
                     Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
                                        DoubleValue(40.046));
                 }
@@ -161,13 +165,16 @@
                     return 0;
                 }
 
-                std::ostringstream oss;
-                oss << "HtMcs" << mcs;
+                auto nonHtRefRateMbps = HtPhy::GetNonHtReferenceRate(mcs) / 1e6;
+                ossControlMode << nonHtRefRateMbps << "Mbps";
+
+                std::ostringstream ossDataMode;
+                ossDataMode << "HtMcs" << mcs;
                 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
                                              "DataMode",
-                                             StringValue(oss.str()),
+                                             StringValue(ossDataMode.str()),
                                              "ControlMode",
-                                             StringValue(oss.str()));
+                                             StringValue(ossControlMode.str()));
                 // Set guard interval
                 wifi.ConfigHtOptions("ShortGuardIntervalSupported", BooleanValue(sgi));
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-multirate.cc ns-3.38/examples/wireless/wifi-multirate.cc
--- ns-3.37/examples/wireless/wifi-multirate.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-multirate.cc	2023-03-16 20:54:23.572868300 -0700
@@ -114,7 +114,7 @@
      *
      * \return true if routing is enabled.
      */
-    bool IsRouting()
+    bool IsRouting() const
     {
         return (m_enableRouting == 1) ? 1 : 0;
     }
@@ -124,7 +124,7 @@
      *
      * \return true if mobility is enabled.
      */
-    bool IsMobility()
+    bool IsMobility() const
     {
         return (m_enableMobility == 1) ? 1 : 0;
     }
@@ -134,7 +134,7 @@
      *
      * \return the scenario number.
      */
-    uint32_t GetScenario()
+    uint32_t GetScenario() const
     {
         return m_scenario;
     }
@@ -144,7 +144,7 @@
      *
      * \return the RTS Threshold.
      */
-    std::string GetRtsThreshold()
+    std::string GetRtsThreshold() const
     {
         return m_rtsThreshold;
     }
@@ -154,7 +154,7 @@
      *
      * \return the Output File Name.
      */
-    std::string GetOutputFileName()
+    std::string GetOutputFileName() const
     {
         return m_outputFileName;
     }
@@ -164,7 +164,7 @@
      *
      * \return the Rate Manager.
      */
-    std::string GetRateManager()
+    std::string GetRateManager() const
     {
         return m_rateManager;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-ofdm-eht-validation.cc ns-3.38/examples/wireless/wifi-ofdm-eht-validation.cc
--- ns-3.37/examples/wireless/wifi-ofdm-eht-validation.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-ofdm-eht-validation.cc	2023-03-16 20:54:23.572868300 -0700
@@ -124,25 +124,30 @@
     std::stringstream plotExtra;
     plotExtra << "set xrange [-5:55]\n\
 set yrange [0:1]\n";
-    uint8_t lineNumber = 1;
-    const std::string colors[14] = {"green",
-                                    "blue",
-                                    "red",
-                                    "black",
-                                    "orange",
-                                    "purple",
-                                    "yellow",
-                                    "pink",
-                                    "grey",
-                                    "magenta",
-                                    "brown",
-                                    "turquoise",
-                                    "olive",
-                                    "beige"};
-    for (uint32_t i = 0; i < modes.size(); i++)
+
+    const std::vector<std::string> colors{
+        "green",
+        "blue",
+        "red",
+        "black",
+        "orange",
+        "purple",
+        "yellow",
+        "pink",
+        "grey",
+        "magenta",
+        "brown",
+        "turquoise",
+        "olive",
+        "beige",
+    };
+
+    NS_ASSERT_MSG(colors.size() == modes.size(), "Colors and modes vectors have different sizes");
+
+    for (std::size_t i = 0; i < modes.size(); i++)
     {
-        plotExtra << "set style line " << +lineNumber++ << " linewidth 5 linecolor rgb \""
-                  << colors[i] << "\" \n";
+        plotExtra << "set style line " << (i + 1) << " linewidth 5 linecolor rgb \"" << colors[i]
+                  << "\" \n";
     }
     plotExtra << "set style increment user";
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-power-adaptation-interference.cc ns-3.38/examples/wireless/wifi-power-adaptation-interference.cc
--- ns-3.37/examples/wireless/wifi-power-adaptation-interference.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-power-adaptation-interference.cc	2023-03-16 20:54:23.572868300 -0700
@@ -189,7 +189,7 @@
      *
      * \return the busy time.
      */
-    double GetBusyTime();
+    double GetBusyTime() const;
 
   private:
     /// Time, DataRate pair vector.
@@ -416,7 +416,7 @@
 }
 
 double
-NodeStatistics::GetBusyTime()
+NodeStatistics::GetBusyTime() const
 {
     return m_totalBusyTime + m_totalRxTime;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-simple-adhoc.cc ns-3.38/examples/wireless/wifi-simple-adhoc.cc
--- ns-3.37/examples/wireless/wifi-simple-adhoc.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-simple-adhoc.cc	2023-03-16 20:54:23.572868300 -0700
@@ -137,7 +137,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents(); // Turn on all Wifi logging
+        WifiHelper::EnableLogComponents(); // Turn on all Wifi logging
     }
     wifi.SetStandard(WIFI_STANDARD_80211b);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-simple-adhoc-grid.cc ns-3.38/examples/wireless/wifi-simple-adhoc-grid.cc
--- ns-3.37/examples/wireless/wifi-simple-adhoc-grid.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-simple-adhoc-grid.cc	2023-03-16 20:54:23.572868300 -0700
@@ -31,7 +31,7 @@
 // n0   n1   n2   n3   n4
 //
 // the layout is affected by the parameters given to GridPositionAllocator;
-// by default, GridWidth is 5 and numNodes is 25..
+// by default, GridWidth is 5 (nodes per row) and numNodes is 25..
 //
 // There are a number of command-line options available to control
 // the default behavior.  The list of available command-line options
@@ -43,12 +43,26 @@
 //
 // For instance, for this configuration, the physical layer will
 // stop successfully receiving packets when distance increases beyond
-// the default of 500m.
-// To see this effect, try running:
+// the default of 100m.  The cutoff is around 116m; below that value, the
+// received signal strength falls below the (default) RSSI limit of -82 dBm
+// used by Wi-Fi's threshold preamble detection running.
 //
-// ./ns3 run "wifi-simple-adhoc-grid --distance=500"
-// ./ns3 run "wifi-simple-adhoc-grid --distance=1000"
-// ./ns3 run "wifi-simple-adhoc-grid --distance=1500"
+// To see this effect, try running at a larger distance, and no packet
+// reception will be reported:
+//
+// ./ns3 run "wifi-simple-adhoc-grid --distance=200"
+//
+// The default path through the topology will follow the following node
+// numbers:  24->23->18->13->12->11->10->5->0
+//
+// To see this, the following Bash commands will list the UDP packet
+// transmissions hop-by-hop, if tracing is enabled:
+//
+// ./ns3 run "wifi-simple-adhoc-grid --tracing=1"
+// grep ^t wifi-simple-adhoc-grid.tr  | grep Udp | grep -v olsr | less
+//
+// By changing the distance to a smaller value, more nodes can be reached
+// by each transmission, and the number of forwarding hops will decrease.
 //
 // The source node and sink node can be changed like this:
 //
@@ -67,6 +81,9 @@
 //
 // tcpdump -r wifi-simple-adhoc-grid-0-0.pcap -nn -tt
 //
+// or you can examine the text-based trace wifi-simple-adhoc-grid.tr with
+// an editor.
+//
 
 #include "ns3/command-line.h"
 #include "ns3/config.h"
@@ -133,7 +150,7 @@
 main(int argc, char* argv[])
 {
     std::string phyMode("DsssRate1Mbps");
-    double distance = 500;      // m
+    double distance = 100;      // m
     uint32_t packetSize = 1000; // bytes
     uint32_t numPackets = 1;
     uint32_t numNodes = 25; // by default, 5x5
@@ -168,7 +185,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents(); // Turn on all Wifi logging
+        WifiHelper::EnableLogComponents(); // Turn on all Wifi logging
     }
 
     YansWifiPhyHelper wifiPhy;
@@ -246,10 +263,10 @@
         // Trace routing tables
         Ptr<OutputStreamWrapper> routingStream =
             Create<OutputStreamWrapper>("wifi-simple-adhoc-grid.routes", std::ios::out);
-        olsr.PrintRoutingTableAllEvery(Seconds(2), routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAllEvery(Seconds(2), routingStream);
         Ptr<OutputStreamWrapper> neighborStream =
             Create<OutputStreamWrapper>("wifi-simple-adhoc-grid.neighbors", std::ios::out);
-        olsr.PrintNeighborCacheAllEvery(Seconds(2), neighborStream);
+        Ipv4RoutingHelper::PrintNeighborCacheAllEvery(Seconds(2), neighborStream);
 
         // To do-- enable an IP-level trace that shows forwarding events only
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-simple-infra.cc ns-3.38/examples/wireless/wifi-simple-infra.cc
--- ns-3.37/examples/wireless/wifi-simple-infra.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-simple-infra.cc	2023-03-16 20:54:23.572868300 -0700
@@ -168,7 +168,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents(); // Turn on all Wifi logging
+        WifiHelper::EnableLogComponents(); // Turn on all Wifi logging
     }
     wifi.SetStandard(WIFI_STANDARD_80211b);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-simple-interference.cc ns-3.38/examples/wireless/wifi-simple-interference.cc
--- ns-3.37/examples/wireless/wifi-simple-interference.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-simple-interference.cc	2023-03-16 20:54:23.572868300 -0700
@@ -198,7 +198,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents(); // Turn on all Wifi logging
+        WifiHelper::EnableLogComponents(); // Turn on all Wifi logging
     }
     wifi.SetStandard(WIFI_STANDARD_80211b);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-sleep.cc ns-3.38/examples/wireless/wifi-sleep.cc
--- ns-3.37/examples/wireless/wifi-sleep.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-sleep.cc	2023-03-16 20:54:23.572868300 -0700
@@ -147,7 +147,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents(); // Turn on all Wifi logging
+        WifiHelper::EnableLogComponents(); // Turn on all Wifi logging
     }
     wifi.SetStandard(WIFI_STANDARD_80211b);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-spatial-reuse.cc ns-3.38/examples/wireless/wifi-spatial-reuse.cc
--- ns-3.37/examples/wireless/wifi-spatial-reuse.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-spatial-reuse.cc	2023-03-16 20:54:23.572868300 -0700
@@ -32,7 +32,7 @@
 //  STA1 and AP1 are in one BSS (with color set to 1), while STA2 and AP2 are in
 //  another BSS (with color set to 2). The distances are configurable (d1 through d3).
 //
-//  STA1 is continously transmitting data to AP1, while STA2 is continuously sending data to AP2.
+//  STA1 is continuously transmitting data to AP1, while STA2 is continuously sending data to AP2.
 //  Each STA has configurable traffic loads (inter packet interval and packet size).
 //  It is also possible to configure TX power per node as well as their CCA-ED tresholds.
 //  OBSS_PD spatial reuse feature can be enabled (default) or disabled, and the OBSS_PD
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-spectrum-per-example.cc ns-3.38/examples/wireless/wifi-spectrum-per-example.cc
--- ns-3.37/examples/wireless/wifi-spectrum-per-example.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-spectrum-per-example.cc	2023-03-16 20:54:23.572868300 -0700
@@ -102,7 +102,7 @@
  * Monitor sniffer Rx trace
  *
  * \param packet The sensed packet.
- * \param channelFreqMhz The channel frequancy [MHz].
+ * \param channelFreqMhz The channel frequency [MHz].
  * \param txVector The Tx vector.
  * \param aMpdu The aMPDU.
  * \param signalNoise The signal and noise dBm.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-spectrum-per-interference.cc ns-3.38/examples/wireless/wifi-spectrum-per-interference.cc
--- ns-3.37/examples/wireless/wifi-spectrum-per-interference.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-spectrum-per-interference.cc	2023-03-16 20:54:23.572868300 -0700
@@ -108,7 +108,7 @@
  * Monitor sniffer Rx trace
  *
  * \param packet The sensed packet.
- * \param channelFreqMhz The channel frequancy [MHz].
+ * \param channelFreqMhz The channel frequency [MHz].
  * \param txVector The Tx vector.
  * \param aMpdu The aMPDU.
  * \param signalNoise The signal and noise dBm.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-tcp.cc ns-3.38/examples/wireless/wifi-tcp.cc
--- ns-3.37/examples/wireless/wifi-tcp.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-tcp.cc	2023-03-16 20:54:23.572868300 -0700
@@ -44,7 +44,7 @@
 #include "ns3/packet-sink.h"
 #include "ns3/ssid.h"
 #include "ns3/string.h"
-#include "ns3/tcp-westwood.h"
+#include "ns3/tcp-westwood-plus.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
 
@@ -56,13 +56,13 @@
 uint64_t lastTotalRx = 0; //!< The value of the last total received bytes
 
 /**
- * Calulate the throughput
+ * Calculate the throughput
  */
 void
 CalculateThroughput()
 {
     Time now = Simulator::Now(); /* Return the simulator's virtual time. */
-    double cur = (sink->GetTotalRx() - lastTotalRx) * (double)8 /
+    double cur = (sink->GetTotalRx() - lastTotalRx) * 8.0 /
                  1e5; /* Convert Application RX Packets to MBits. */
     std::cout << now.GetSeconds() << "s: \t" << cur << " Mbit/s" << std::endl;
     lastTotalRx = sink->GetTotalRx();
@@ -95,21 +95,11 @@
 
     tcpVariant = std::string("ns3::") + tcpVariant;
     // Select TCP variant
-    if (tcpVariant == "ns3::TcpWestwoodPlus")
-    {
-        // TcpWestwoodPlus is not an actual TypeId name; we need TcpWestwood here
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpWestwood::GetTypeId()));
-        // the default protocol type in ns3::TcpWestwood is WESTWOOD
-        Config::SetDefault("ns3::TcpWestwood::ProtocolType", EnumValue(TcpWestwood::WESTWOODPLUS));
-    }
-    else
-    {
-        TypeId tcpTid;
-        NS_ABORT_MSG_UNLESS(TypeId::LookupByNameFailSafe(tcpVariant, &tcpTid),
-                            "TypeId " << tcpVariant << " not found");
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType",
-                           TypeIdValue(TypeId::LookupByName(tcpVariant)));
-    }
+    TypeId tcpTid;
+    NS_ABORT_MSG_UNLESS(TypeId::LookupByNameFailSafe(tcpVariant, &tcpTid),
+                        "TypeId " << tcpVariant << " not found");
+    Config::SetDefault("ns3::TcpL4Protocol::SocketType",
+                       TypeIdValue(TypeId::LookupByName(tcpVariant)));
 
     /* Configure TCP Options */
     Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(payloadSize));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/examples/wireless/wifi-vht-network.cc ns-3.38/examples/wireless/wifi-vht-network.cc
--- ns-3.37/examples/wireless/wifi-vht-network.cc	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/examples/wireless/wifi-vht-network.cc	2023-03-16 20:54:23.572868300 -0700
@@ -33,6 +33,7 @@
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
 #include "ns3/uinteger.h"
+#include "ns3/vht-phy.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
 
@@ -148,13 +149,18 @@
                 wifi.SetStandard(WIFI_STANDARD_80211ac);
                 WifiMacHelper mac;
 
-                std::ostringstream oss;
-                oss << "VhtMcs" << mcs;
+                std::ostringstream ossControlMode;
+                auto nonHtRefRateMbps = VhtPhy::GetNonHtReferenceRate(mcs) / 1e6;
+                ossControlMode << "OfdmRate" << nonHtRefRateMbps << "Mbps";
+
+                std::ostringstream ossDataMode;
+                ossDataMode << "VhtMcs" << mcs;
                 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
                                              "DataMode",
-                                             StringValue(oss.str()),
+                                             StringValue(ossDataMode.str()),
                                              "ControlMode",
-                                             StringValue(oss.str()));
+                                             StringValue(ossControlMode.str()));
+
                 // Set guard interval
                 wifi.ConfigHtOptions("ShortGuardIntervalSupported", BooleanValue(sgi));
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/ns3 ns-3.38/ns3
--- ns-3.37/ns3	2022-11-01 17:37:24.447394800 -0700
+++ ns-3.38/ns3	2023-03-16 20:54:23.572868300 -0700
@@ -81,9 +81,16 @@
 def parse_args(argv):
     parser = argparse.ArgumentParser(description="ns-3 wrapper for the CMake build system", add_help=False)
 
+    sub_parser = parser.add_subparsers()
+
     parser.add_argument('-h', '--help',
                         help="Print a summary of available commands",
-                        action="store_true", default=None, dest="help")
+                        action="store_true", default=None, dest="main_help")
+    parser_help = sub_parser.add_parser('help',
+                                        help='Print a summary of available commands')
+    parser_help.add_argument('help',
+                             help='Print a summary of available commands',
+                             action="store_true", default=False)
     # parser.add_argument('--docset',
     #               help=(
     #                   'Create Docset, without building. This requires the docsetutil tool from Xcode 9.2 or earlier.'
@@ -91,8 +98,6 @@
     #               action="store_true", default=None,
     #               dest="docset_build")
 
-    sub_parser = parser.add_subparsers()
-
     parser_build = sub_parser.add_parser('build',
                                          help=('Accepts a list of targets to build,'
                                                ' or builds the entire project if no target is given'),
@@ -135,6 +140,7 @@
         ("build-version", "embedding git changes as a build version during build"),
         ("clang-tidy", "clang-tidy static analysis"),
         ("dpdk", "the fd-net-device DPDK features"),
+        ("eigen", "Eigen3 library support"),
         ("examples", "the ns-3 examples"),
         ("gcov", "code coverage analysis"),
         ("gsl", "GNU Scientific Library (GSL) features"),
@@ -142,6 +148,7 @@
         ("logs", "the logs regardless of the compile mode"),
         ("monolib", "a single shared library with all ns-3 modules"),
         ("mpi", "the MPI support for distributed simulation"),
+        ("ninja-tracing", "the conversion of the Ninja generator log file into about://tracing format"),
         ("precompiled-headers", "precompiled headers"),
         ("python-bindings", "python bindings"),
         ("tests", "the ns-3 tests"),
@@ -206,6 +213,9 @@
     parser_clean = sub_parser.add_parser('clean', help='Removes files created by ns3')
     parser_clean.add_argument('clean', action="store_true", default=False)
 
+    parser_distclean = sub_parser.add_parser('distclean', help='Removes files created by ns3, tests and documentation')
+    parser_distclean.add_argument('distclean', action="store_true", default=False)
+
     parser_install = sub_parser.add_parser('install', help='Install ns-3')
     parser_install.add_argument('install', action="store_true", default=False)
 
@@ -285,7 +295,7 @@
                              action="store", type=str, nargs="?", default="all")
 
     add_argument_to_subparsers(
-        [parser, parser_build, parser_configure, parser_clean, parser_docs, parser_run, parser_show],
+        [parser, parser_build, parser_configure, parser_clean, parser_distclean, parser_docs, parser_run, parser_show],
         ["--dry-run"],
         help_msg="Do not execute the commands.",
         dest="dry_run")
@@ -325,6 +335,15 @@
         parser_run.print_help()
         exit(-1)
 
+    # Merge attributes
+    attributes_to_merge = ["dry_run", "help", "verbose", "quiet"]
+    filtered_attributes = list(
+        filter(lambda x: x if ("disable" not in x and "enable" not in x) else None, args.__dir__()))
+    for attribute in attributes_to_merge:
+        merging_attributes = list(
+            map(lambda x: args.__getattribute__(x) if attribute in x else None, filtered_attributes))
+        setattr(args, attribute, merging_attributes.count(True) > 0)
+
     if args.help:
         print(parser.description)
         print("")
@@ -346,15 +365,6 @@
         print(parser.format_help().replace(parser.description, "").replace(parser.format_usage(), ""))
         exit(0)
 
-    # Merge attributes
-    attributes_to_merge = ["dry_run", "verbose", "quiet"]
-    filtered_attributes = list(
-        filter(lambda x: x if ("disable" not in x and "enable" not in x) else None, args.__dir__()))
-    for attribute in attributes_to_merge:
-        merging_attributes = list(
-            map(lambda x: args.__getattribute__(x) if attribute in x else None, filtered_attributes))
-        setattr(args, attribute, merging_attributes.count(True) > 0)
-
     attributes_to_merge = ["jobs"]
     filtered_attributes = list(filter(lambda x: x if ("disable" not in x and "enable" not in x) else 0, args.__dir__()))
     for attribute in attributes_to_merge:
@@ -363,7 +373,7 @@
         setattr(args, attribute, min(merging_attributes))
 
     # If some positional options are not in args, set them to false.
-    for option in ["clean", "configure", "docs", "install", "run", "shell", "uninstall", "show"]:
+    for option in ["clean", "configure", "docs", "install", "run", "shell", "uninstall", "show", "distclean"]:
         if option not in args:
             setattr(args, option, False)
 
@@ -417,28 +427,83 @@
     print_buffer += "\n" + message
 
 
+def remove_dir(dir_to_remove, dry_run, directory_qualifier=""):
+    dir_to_remove = os.path.abspath(dir_to_remove)
+    if os.path.exists(dir_to_remove):
+        if ".." in os.path.relpath(dir_to_remove, ns3_path):
+            # In case the directory to remove isn't within
+            # the current ns-3 directory, print an error
+            # message for the dry-run case
+            # Or throw an exception in a normal run
+            error_message = (f"The {directory_qualifier} directory '{dir_to_remove}' "
+                             "is not within the current ns-3 directory. "
+                             "Deleting it can cause data loss.")
+            if dry_run:
+                print_and_buffer(error_message)
+                return
+            else:
+                raise Exception(error_message)
+
+        # Remove directories that are within the current ns-3 directory
+        print_and_buffer("rm -R %s" % os.path.relpath(dir_to_remove, ns3_path))
+        if not dry_run:
+            shutil.rmtree(dir_to_remove, ignore_errors=True)
+
+
+def remove_file(file_to_remove, dry_run):
+    if os.path.exists(file_to_remove):
+        print_and_buffer("rm -R %s" % os.path.relpath(file_to_remove, ns3_path))
+        if not dry_run:
+            os.remove(file_to_remove)
+
+
 def clean_cmake_artifacts(dry_run=False):
-    print_and_buffer("rm -R %s" % os.path.relpath(out_dir, ns3_path))
-    if not dry_run:
-        if out_dir == ns3_path:
-            raise Exception("The output directory and the ns-3 directory are the same. "
-                            "Deleting it can cause data loss.")
-        shutil.rmtree(out_dir, ignore_errors=True)
+    remove_dir(out_dir, dry_run, "output")
 
     cmake_cache_files = glob.glob("%s/**/CMakeCache.txt" % ns3_path, recursive=True)
     for cmake_cache_file in cmake_cache_files:
         dirname = os.path.dirname(cmake_cache_file)
-        print_and_buffer("rm -R %s" % os.path.relpath(dirname, ns3_path))
-        if not dry_run:
-            if dirname == ns3_path:
-                raise Exception("The CMake cache directory and the ns-3 directory are the same. "
-                                "Deleting it can cause data loss.")
-            shutil.rmtree(dirname, ignore_errors=True)
+        remove_dir(dirname, dry_run, "CMake cache")
 
-    if os.path.exists(lock_file):
-        print_and_buffer("rm %s" % os.path.relpath(lock_file, ns3_path))
-        if not dry_run:
-            os.remove(lock_file)
+    dirs_to_remove = [os.path.join(ns3_path, "testpy-output"),
+                      os.path.join(ns3_path, "__pycache__")
+                      ]
+    for dir_to_remove in dirs_to_remove:
+        remove_dir(dir_to_remove, dry_run)
+
+    remove_file(lock_file, dry_run)
+
+
+def clean_docs_and_tests_artifacts(dry_run=False):
+    docs_dir = os.path.join(ns3_path, 'doc')
+
+    file_artifacts = ["doxygen.log",
+                      "doxygen.warnings.log",
+                      "introspected-command-line.h",
+                      "introspected-doxygen.h",
+                      "ns3-object.txt"]
+    docs_files = [os.path.join(docs_dir, filename) for filename in file_artifacts]
+
+    docs_and_tests_dirs = [os.path.join(docs_dir, "html"),
+                           os.path.join(docs_dir, "html-warn"),
+                           ]
+    docs_and_tests_dirs.extend(glob.glob(f"{docs_dir}/**/build", recursive=True))
+    docs_and_tests_dirs.extend(glob.glob(f"{docs_dir}/**/source-temp", recursive=True))
+
+    for directory in docs_and_tests_dirs:
+        remove_dir(directory, dry_run)
+
+    for file in docs_files:
+        remove_file(file, dry_run)
+
+
+def clean_pip_packaging_artifacts(dry_run=False):
+    pip_dirs = [os.path.join(ns3_path, "dist"),
+                os.path.join(ns3_path, "nsnam.egg-info"),
+                os.path.join(ns3_path, "wheelhouse")
+                ]
+    for directory in pip_dirs:
+        remove_dir(directory, dry_run)
 
 
 def search_cmake_cache(build_profile):
@@ -575,6 +640,7 @@
                ("COVERAGE", "gcov"),
                ("DES_METRICS", "des_metrics"),
                ("DPDK", "dpdk"),
+               ("EIGEN", "eigen"),
                ("ENABLE_BUILD_VERSION", "build_version"),
                ("ENABLE_SUDO", "sudo"),
                ("EXAMPLES", "examples"),
@@ -583,6 +649,7 @@
                ("LOG", "logs"),
                ("MONOLIB", "monolib"),
                ("MPI", "mpi"),
+               ("NINJA_TRACING", "ninja_tracing"),
                ("PRECOMPILE_HEADERS", "precompiled_headers"),
                ("PYTHON_BINDINGS", "python_bindings"),
                ("SANITIZE", "sanitizers"),
@@ -595,12 +662,17 @@
     for (cmake_flag, option_name) in options:
         arg = on_off_condition(args, cmake_flag, option_name)
         if arg:
+            is_on = "=ON" in arg
+            reverse = arg.replace("=ON" if is_on else "=OFF",
+                                  "=OFF" if is_on else "=ON")
+            if reverse in cmake_args:
+                cmake_args.remove(reverse)
             cmake_args.append(arg)
 
     if args.lcov_zerocounters is not None:
         cmake_args.append("-DNS3_COVERAGE_ZERO_COUNTERS=%s" % on_off(args.lcov_zerocounters))
 
-    # Output, Brite, Click and Openflow directories
+    # Output, Brite, Click and Openflow dirs
     if args.output_directory is not None:
         cmake_args.append("-DNS3_OUTPUT_DIRECTORY=%s" % args.output_directory)
 
@@ -667,7 +739,8 @@
 
 
 def refresh_cmake(current_cmake_cache_folder, output):
-    ret = subprocess.run([check_program_installed("cmake"), ".."], cwd=current_cmake_cache_folder, stdout=output)
+    cmake, _ = cmake_check_version()
+    ret = subprocess.run([cmake, ".."], cwd=current_cmake_cache_folder, stdout=output)
     if ret.returncode != 0:
         exit(ret.returncode)
     update_scratches_list(current_cmake_cache_folder)
@@ -768,7 +841,8 @@
 
 def cmake_check_version():
     # Check CMake version
-    cmake = shutil.which("cmake")
+    cmake3 = shutil.which("cmake3")
+    cmake = cmake3 if cmake3 else shutil.which("cmake")
     if not cmake:
         print("Error:  CMake not found; please install version 3.10 or greater, or modify", path_variable)
         exit(1)
@@ -781,12 +855,12 @@
 
 
 def cmake_build(current_cmake_cache_folder, output, jobs, target=None, dry_run=False, build_verbose=False):
-    _, version = cmake_check_version()
+    cmake, version = cmake_check_version()
 
     # Older CMake versions don't accept the number of jobs directly
     jobs_part = ("-j %d" % jobs) if parse_version(version) >= parse_version("3.12.0") else ""
     target_part = (" --target %s" % target) if target else ""
-    cmake_build_command = "cmake --build . %s%s" % (jobs_part, target_part)
+    cmake_build_command = "%s --build . %s%s" % (cmake, jobs_part, target_part)
 
     print_and_buffer("cd %s; %s ; cd %s" % (os.path.relpath(current_cmake_cache_folder, ns3_path),
                                             cmake_build_command,
@@ -974,6 +1048,7 @@
                                   "sphinx",
                                   "manual",
                                   "models",
+                                  "ninjaTrace",
                                   "timeTraceReport",
                                   "tutorial",
                                   "contributing",
@@ -1098,8 +1173,8 @@
         if args.perf:
             debugging_software.extend([
                 check_program_installed("perf"),
-                "record", "--call-graph", "dwarf", "-a", "-e",
-                "cache-misses,branch-misses,cpu-cycles,stalled-cycles-frontend,stalled-cycles-backend,context-switches"
+                "record", "--call-graph", "dwarf", "-e",
+                "cache-misses,branch-misses,cpu-cycles,stalled-cycles-frontend,stalled-cycles-backend"
             ])
 
         # running with the visualizer?
@@ -1327,7 +1402,8 @@
     refuse_run_as_root()
 
     # Enable colorized output for CMake and GCC/Clang
-    os.environ["CLICOLOR"] = "1"
+    if os.getenv("CLICOLOR") is None:
+        os.environ["CLICOLOR"] = "1"
 
     # If pybindgen exists in the parent directory
     # (e.g. ns3-all-in-one), add it to the PYTHONPATH
@@ -1356,6 +1432,13 @@
         # We end things earlier when cleaning
         return
 
+    if args.distclean:
+        clean_cmake_artifacts(dry_run=args.dry_run)
+        clean_docs_and_tests_artifacts(dry_run=args.dry_run)
+        clean_pip_packaging_artifacts(dry_run=args.dry_run)
+        # We end things earlier when cleaning
+        return
+
     # Installation and uninstallation options become cmake targets
     if args.install:
         args.build = ['install']
@@ -1374,7 +1457,7 @@
         args.build = [args.docs] if args.docs != "all" else ["sphinx", "doxygen"]
         if "doxygen" in args.build and (not build_info["ENABLE_EXAMPLES"] or not build_info["ENABLE_TESTS"]):
             print('The "./ns3 docs doxygen" and "./ns3 docs all" commands,\n'
-                  'require examples and tests to generate introspected documentation.\n'
+                  'requires examples and tests to generate introspected documentation.\n'
                   'Try "./ns3 docs doxygen-no-build" or enable examples and tests.')
             exit(1)
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/README.md ns-3.38/README.md
--- ns-3.37/README.md	2022-11-01 17:37:24.363394700 -0700
+++ ns-3.38/README.md	2023-03-16 20:54:23.488868200 -0700
@@ -1,5 +1,11 @@
 # The Network Simulator, Version 3
 
+
+[![codecov](https://codecov.io/gh/nsnam/ns-3-dev-git/branch/master/graph/badge.svg)](https://codecov.io/gh/nsnam/ns-3-dev-git/branch/master/)
+[![Gitlab CI](https://gitlab.com/nsnam/ns-3-dev/badges/master/pipeline.svg)](https://gitlab.com/nsnam/ns-3-dev/-/pipelines)
+[![Github CI](https://github.com/nsnam/ns-3-dev-git/actions/workflows/per_commit.yml/badge.svg)](https://github.com/nsnam/ns-3-dev-git/actions)
+
+
 ## Table of Contents
 
 1) [An overview](#an-open-source-project)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/RELEASE_NOTES.md ns-3.38/RELEASE_NOTES.md
--- ns-3.37/RELEASE_NOTES.md	2022-11-01 17:39:10.947703100 -0700
+++ ns-3.38/RELEASE_NOTES.md	2023-03-16 20:54:51.628957000 -0700
@@ -13,6 +13,67 @@
 and references prefixed by '!' refer to a
 [GitLab.com merge request](https://gitlab.com/nsnam/ns-3-dev/-/merge_requests) number.
 
+Release 3.38
+------------
+
+### Availability
+
+This release is available from:
+<https://www.nsnam.org/release/ns-allinone-3.38.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
+
+Python API requires [Cppyy](https://cppyy.readthedocs.io/en/latest/installation.html).
+
+This release has discontinued support for g++-8 compilers.
+
+### New user-visible features
+
+- (core) !1236 - Added some macros to silence compiler warnings. The new macros are in **warnings.h**, and their use is not suggested unless for very specific cases.
+- (core) !1269 - Added new template classes `ValArray` and `MatrixArray` for efficient storage and operations on 1D, 2D, and 3D arrays.
+- (documentation) - Added an Installation Guide to replace the ns-3 wiki's installation page
+- (internet) !1229 - Ping now supports broadcast addresses.
+- (internet) !1186 - `TcpWestwood` model has been removed, and the class has been renamed `TcpWestwoodPlus`.
+- (internet-apps) - A new Ping model that works for both IPv4 and IPv6 has been added, to replace the address family specific v4Ping and Ping6.
+- (lr-wpan) !1006 - Added RX sensitivity configuration support.
+- (lr-wpan) !1268 - Added beacon payload via MLME-SET.request primitive.
+- (netanim) Added helper function to update the size of a node
+- (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"
+- (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
+
+### Bugs fixed
+
+- (core) !1236 - Deprecation warnings are silenced while calling `NS_OBJECT_ENSURE_REGISTERED`
+- (build) #808 - Handle profile setting changes in the first ns3 run
+- (build) #815 - Configure find_program to search for programs in PATH first, then AppBundles in MacOS
+- (lr-wpan) #636 - Ext address, short address and manual assoc adjustments
+- (internet) !1229 - Fixed a bug in `Icmpv4Header::HandleEcho` when replying to broadcast-type Echo requests, and two bugs in `Ipv4RawSocketImpl::SendTo` in handling sockets bound to a specific address and directed to a broadcast-type address.
+- (internet) - `NeighborCacheHelper::PopulateNeighborCache` is now robust against missing IPv4 or IPv6 stack in nodes.
+- (network) !1229 - Fixed a bug in `Ipv4Address::IsSubnetDirectedBroadcast`
+- (wifi) Fixed multiple issues about setting the TXOP holder
+- (wifi) #861 - Bianchi validation program was not compatible with 11ax
+- (wifi) Fixed 10 MHz offset in center frequencies for 6 GHz channels
+- (wifi) Fixed setting of Duration/ID field of a Multi-STA Block Ack
+- (wifi) Fixed handling of the HE TB TXVECTOR and TRIGVECTOR parameters
+- (wifi) Prevent extraction of in-flight MPDUs due to lifetime expired
+- (wifi) Fixed getting the primary 80 MHz channel number in the 6 GHz band
+
 Release 3.37
 ------------
 
@@ -324,7 +385,7 @@
 - (network) Improved support for bit fields in header serialization/deserialization.
 - (network) Added support for DLT_LORATAP DataLinkType to PCAP files
 - (network) More arithmetic operators are provided for DataRate objects
-- (nix-vector) Nix-Vector routing supports multiple interface addresse and can print out routing paths.
+- (nix-vector) Nix-Vector routing supports multiple interface addresses and can print out routing paths.
 - (olsr) Add support for printing OLSR headers
 - (sixlowpan) Added support for stateful (i.e., context-based) RFC6282 compression.
 - (tcp) TCP CUBIC is now the default TCP congestion control, replacing NewReno.
@@ -588,7 +649,7 @@
 - (lr-wpan) !326 - Inactive Periods Queue fix
 - (lte) #106 - Inconsistent imsi representation in LTE module
 - (lte) #196 - Remove LteSecondaryCellSelectionTestSuite from LTE module
-- (lte) #221 - Stop T310 upon receving handover command
+- (lte) #221 - Stop T310 upon receiving handover command
 - (network) #216 - Correct the return value for PacketSocket::SendTo()
 - (network) !239 - Refactor DelayJitterEstimation to more closely follow RFC 1889 and RFC 3550
 - (propagation) !269 - Fix Okumura-Hata propagation loss model for frequency > 1.5 Ghz and medium or small city
@@ -596,7 +657,7 @@
 - (wifi) - Fix frame capture when signals arrive at the exact same time and add additional tests to verify these cases
 - (wifi) - Fix MCS selection in ideal rate manager for cases with unbalanced MIMO settings and/or RX diversity
 - (wifi) - Fix SNR computations for MIMO
-- (wifi) - Fix ReportAmpduTxStatus called two times when BAR is explicitely sent upon missed BACK
+- (wifi) - Fix ReportAmpduTxStatus called two times when BAR is explicitly sent upon missed BACK
 - (wifi)  A zero value for the backoff timer might be discarded and a new value
   might be generated by an erroneous call to NotifyCollision().
 - (wifi) Bug 1909, Issue #41 - Implementation of ACK timeout
@@ -2040,8 +2101,8 @@
   same way as with Ipv4PacketInfoTag. See Doxygen for current limitations in
   using Ipv[4,6]PacketInfoTag to set IP properties.
 
-- Ipv[4,6]Interfaces not respecting the minimum MTU requirements (68 octects
-  for IPv4 and 1280 octects for IPv6) will be automatically set as Down.
+- Ipv[4,6]Interfaces not respecting the minimum MTU requirements (68 octets
+  for IPv4 and 1280 octets for IPv6) will be automatically set as Down.
 
 - IPv6 addresses and routing tables are printed in a more conventional way,
   closely matching the Linux "route -A inet6" command.
@@ -2322,7 +2383,7 @@
 - bug 1633 - Bake - should not report that it is downloading qt4 when it is already installed
 - bug 1635 - Small bug without Simulator::Destroy()
 - bug 1636 - Compilation error flagged as unmet dependency
-- bug 1637 - Bake calling apt-get for unpriviledged user
+- bug 1637 - Bake calling apt-get for unprivileged user
 - bug 1639 - bake.py support for linux mint
 - bug 1640 - bake needs to test for g++
 - bug 1641 - bake reports autotools dependency, but needs automake
@@ -3701,7 +3762,7 @@
   Support for several ICMP messages has been added to ns-3.  See
   src/internet-stack/icmpv4.h for details.
 - IPv6 Address Support
-  New clases to support IPv6 addresses has been added to the system.  This
+  New classes to support IPv6 addresses has been added to the system.  This
   is enabling technology for fuller IPv6 support scheduled for ns-3.4.
 - A flow-id tag has been added to the contributed code section
 - Star topologies can be created from the topology helper functions
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/scratch/nested-subdir/CMakeLists.txt ns-3.38/scratch/nested-subdir/CMakeLists.txt
--- ns-3.37/scratch/nested-subdir/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/scratch/nested-subdir/CMakeLists.txt	2023-03-16 20:54:23.572868300 -0700
@@ -0,0 +1,16 @@
+# Create a custom library
+add_library(
+  scratch-nested-subdir-lib
+  lib/scratch-nested-subdir-library-source.cc
+)
+
+# Link the custom library to the scratch executable
+build_exec(
+  EXECNAME scratch-nested-subdir-executable
+  SOURCE_FILES scratch-nested-subdir-executable.cc
+  LIBRARIES_TO_LINK scratch-nested-subdir-lib
+                    ${libcore}
+  # use "${ns3-libs}" "${ns3-contrib-libs}" in case you want to link to all
+  # enabled modules
+  EXECUTABLE_DIRECTORY_PATH ${CMAKE_OUTPUT_DIRECTORY}/scratch/nested-subdir
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/scratch/nested-subdir/lib/scratch-nested-subdir-library-header.h ns-3.38/scratch/nested-subdir/lib/scratch-nested-subdir-library-header.h
--- ns-3.37/scratch/nested-subdir/lib/scratch-nested-subdir-library-header.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/scratch/nested-subdir/lib/scratch-nested-subdir-library-header.h	2023-03-16 20:54:23.572868300 -0700
@@ -0,0 +1,32 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// This header does not provide much functionality but is meant to demonstrate how,
+// in a scratch subdirectory, one can create new programs that are implemented
+// in multiple files and headers.
+
+#include <string>
+
+namespace ns3
+{
+
+/**
+ * Get a message from the subdir.
+ *
+ * \return The message from the subdir
+ */
+std::string ScratchNestedSubdirGetMessage();
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/scratch/nested-subdir/lib/scratch-nested-subdir-library-source.cc ns-3.38/scratch/nested-subdir/lib/scratch-nested-subdir-library-source.cc
--- ns-3.37/scratch/nested-subdir/lib/scratch-nested-subdir-library-source.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/scratch/nested-subdir/lib/scratch-nested-subdir-library-source.cc	2023-03-16 20:54:23.572868300 -0700
@@ -0,0 +1,30 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// This file contains the implementation of the functions declared in the
+// corresponding header file.
+
+#include "scratch-nested-subdir-library-header.h"
+
+namespace ns3
+{
+
+std::string
+ScratchNestedSubdirGetMessage()
+{
+    return "Scratch Nested Subdir";
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/scratch/nested-subdir/scratch-nested-subdir-executable.cc ns-3.38/scratch/nested-subdir/scratch-nested-subdir-executable.cc
--- ns-3.37/scratch/nested-subdir/scratch-nested-subdir-executable.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/scratch/nested-subdir/scratch-nested-subdir-executable.cc	2023-03-16 20:54:23.572868300 -0700
@@ -0,0 +1,51 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// This example shows how to create new simulations that are implemented in
+// multiple files and headers. The structure of this simulation project
+// is as follows:
+//
+// scratch/
+// |  nested-subdir/
+// |  |  - scratch-nested-subdir-executable.cc        // Main simulation file
+// |  |  lib
+// |  |  |  - scratch-nested-subdir-library-header.h  // Additional header
+// |  |  |  - scratch-nested-subdir-library-source.cc // Additional header implementation
+//
+// This file contains the main() function, which calls an external function
+// defined in the "scratch-nested-subdir-library-header.h" header file and
+// implemented in "scratch-nested-subdir-library-source.cc".
+
+#include "lib/scratch-nested-subdir-library-header.h"
+
+#include "ns3/core-module.h"
+
+#include <string>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("ScratchNestedSubdir");
+
+int
+main(int argc, char* argv[])
+{
+    std::string message = ScratchNestedSubdirGetMessage();
+    NS_LOG_UNCOND(message);
+
+    Simulator::Run();
+    Simulator::Destroy();
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/model/angles.cc ns-3.38/src/antenna/model/angles.cc
--- ns-3.37/src/antenna/model/angles.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/model/angles.cc	2023-03-16 20:54:23.572868300 -0700
@@ -132,7 +132,7 @@
     double incl;
     std::string unit;
 
-    if (a.m_printDeg)
+    if (Angles::m_printDeg)
     {
         azim = RadiansToDegrees(a.m_azimuth);
         incl = RadiansToDegrees(a.m_inclination);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/model/angles.h ns-3.38/src/antenna/model/angles.h
--- ns-3.37/src/antenna/model/angles.h	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/model/angles.h	2023-03-16 20:54:23.572868300 -0700
@@ -120,7 +120,7 @@
     /**
      * This constructor allows to specify azimuth and inclination.
      * Inclination must be in [0, M_PI], while azimuth is
-     * automatically notmalized in [-M_PI, M_PI)
+     * automatically normalized in [-M_PI, M_PI)
      *
      * \param azimuth the azimuth angle in radians
      * \param inclination the inclination angle in radians
@@ -209,7 +209,7 @@
      *
      * Note: while an arbitrary value for the azimuth angle is valid
      * and can be wrapped in [-M_PI, M_PI), an inclination angle outside
-     * the [0, M_PI] range can be ambiguos and is thus not valid.
+     * the [0, M_PI] range can be ambiguous and is thus not valid.
      */
     void NormalizeAngles();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/model/phased-array-model.cc ns-3.38/src/antenna/model/phased-array-model.cc
--- ns-3.37/src/antenna/model/phased-array-model.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/model/phased-array-model.cc	2023-03-16 20:54:23.576868300 -0700
@@ -36,7 +36,7 @@
 std::ostream&
 operator<<(std::ostream& os, const PhasedArrayModel::ComplexVector& cv)
 {
-    size_t N = cv.size();
+    size_t N = cv.GetSize();
 
     // empty
     if (N == 0)
@@ -63,7 +63,6 @@
 
 PhasedArrayModel::~PhasedArrayModel()
 {
-    m_beamformingVector.clear();
 }
 
 TypeId
@@ -85,8 +84,8 @@
 PhasedArrayModel::SetBeamformingVector(const ComplexVector& beamformingVector)
 {
     NS_LOG_FUNCTION(this << beamformingVector);
-    NS_ASSERT_MSG(beamformingVector.size() == GetNumberOfElements(),
-                  beamformingVector.size() << " != " << GetNumberOfElements());
+    NS_ASSERT_MSG(beamformingVector.GetSize() == GetNumberOfElements(),
+                  beamformingVector.GetSize() << " != " << GetNumberOfElements());
     m_beamformingVector = beamformingVector;
     m_isBfVectorValid = true;
 }
@@ -101,30 +100,17 @@
     return m_beamformingVector;
 }
 
-double
-PhasedArrayModel::ComputeNorm(const ComplexVector& vector)
-{
-    double norm = 0;
-
-    for (uint64_t i = 0; i < vector.size(); i++)
-    {
-        norm += std::norm(vector[i]);
-    }
-
-    return std::sqrt(norm);
-}
-
 PhasedArrayModel::ComplexVector
 PhasedArrayModel::GetBeamformingVector(Angles a) const
 {
     NS_LOG_FUNCTION(this << a);
 
     ComplexVector beamformingVector = GetSteeringVector(a);
-    double norm = ComputeNorm(beamformingVector);
+    double normRes = norm(beamformingVector);
 
-    for (uint64_t i = 0; i < beamformingVector.size(); i++)
+    for (size_t i = 0; i < GetNumberOfElements(); i++)
     {
-        beamformingVector[i] = std::conj(beamformingVector[i]) / norm;
+        beamformingVector[i] = std::conj(beamformingVector[i]) / normRes;
     }
 
     return beamformingVector;
@@ -133,9 +119,8 @@
 PhasedArrayModel::ComplexVector
 PhasedArrayModel::GetSteeringVector(Angles a) const
 {
-    ComplexVector steeringVector;
-    steeringVector.resize(GetNumberOfElements());
-    for (uint64_t i = 0; i < GetNumberOfElements(); i++)
+    ComplexVector steeringVector(GetNumberOfElements());
+    for (size_t i = 0; i < GetNumberOfElements(); i++)
     {
         Vector loc = GetElementLocation(i);
         double phase = -2 * M_PI *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/model/phased-array-model.h ns-3.38/src/antenna/model/phased-array-model.h
--- ns-3.37/src/antenna/model/phased-array-model.h	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/model/phased-array-model.h	2023-03-16 20:54:23.576868300 -0700
@@ -20,6 +20,7 @@
 
 #include <ns3/angles.h>
 #include <ns3/antenna-model.h>
+#include <ns3/matrix-array.h>
 #include <ns3/object.h>
 
 #include <complex>
@@ -51,8 +52,24 @@
      */
     static TypeId GetTypeId();
 
-    typedef std::vector<std::complex<double>>
-        ComplexVector; //!< type definition for complex vectors
+    //!< type definition for complex vectors
+    using ComplexVector = ComplexMatrixArray; //!< the underlying Valarray
+
+    /**
+     * \brief Computes the Frobenius norm of the complex vector
+     *
+     * \param complexVector on which to calculate Frobenius norm
+     * \return the Frobenius norm of the complex vector
+     */
+    double norm(const ComplexVector& complexVector) const
+    {
+        double norm = 0;
+        for (size_t i = 0; i < complexVector.GetSize(); i++)
+        {
+            norm += std::norm(complexVector[i]);
+        }
+        return std::sqrt(norm);
+    }
 
     /**
      * Returns the horizontal and vertical components of the antenna element field
@@ -76,7 +93,7 @@
      * Returns the number of antenna elements
      * \return the number of antenna elements
      */
-    virtual uint64_t GetNumberOfElements() const = 0;
+    virtual size_t GetNumberOfElements() const = 0;
 
     /**
      * Sets the beamforming vector to be used
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/model/three-gpp-antenna-model.h ns-3.38/src/antenna/model/three-gpp-antenna-model.h
--- ns-3.37/src/antenna/model/three-gpp-antenna-model.h	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/model/three-gpp-antenna-model.h	2023-03-16 20:54:23.576868300 -0700
@@ -25,11 +25,9 @@
 {
 
 /**
- *
  * \brief  Antenna model based on a parabolic approximation of the main lobe radiation pattern.
  *
  * This class implements the parabolic model as described in 3GPP TR 38.901 v15.0.0
- *
  */
 class ThreeGppAntennaModel : public AntennaModel
 {
@@ -65,8 +63,8 @@
     double GetSlaV() const;
 
     /**
-     * Get the naximum attenuation of the antenna element.
-     * \return the naximum attenuation in dB
+     * Get the maximum attenuation of the antenna element.
+     * \return the maximum attenuation in dB
      */
     double GetMaxAttenuation() const;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/model/uniform-planar-array.cc ns-3.38/src/antenna/model/uniform-planar-array.cc
--- ns-3.37/src/antenna/model/uniform-planar-array.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/model/uniform-planar-array.cc	2023-03-16 20:54:23.576868300 -0700
@@ -249,7 +249,7 @@
     return loc;
 }
 
-uint64_t
+size_t
 UniformPlanarArray::GetNumberOfElements() const
 {
     return m_numRows * m_numColumns;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/model/uniform-planar-array.h ns-3.38/src/antenna/model/uniform-planar-array.h
--- ns-3.37/src/antenna/model/uniform-planar-array.h	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/model/uniform-planar-array.h	2023-03-16 20:54:23.576868300 -0700
@@ -81,7 +81,7 @@
      * Returns the number of antenna elements
      * \return the number of antenna elements
      */
-    uint64_t GetNumberOfElements() const override;
+    size_t GetNumberOfElements() const override;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/test/test-cosine-antenna.cc ns-3.38/src/antenna/test/test-cosine-antenna.cc
--- ns-3.37/src/antenna/test/test-cosine-antenna.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/test/test-cosine-antenna.cc	2023-03-16 20:54:23.576868300 -0700
@@ -66,7 +66,7 @@
      * \param b Horizontal and Vertical Beamwidth
      * \param o Orientation
      * \param g MaxGain
-     * \param expectedGainDb Expeted antenna gain
+     * \param expectedGainDb Expected antenna gain
      * \param cond Test condition
      */
     CosineAntennaModelTestCase(Angles a,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/test/test-degrees-radians.cc ns-3.38/src/antenna/test/test-degrees-radians.cc
--- ns-3.37/src/antenna/test/test-degrees-radians.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/test/test-degrees-radians.cc	2023-03-16 20:54:23.576868300 -0700
@@ -129,7 +129,7 @@
 /**
  * \ingroup tests
  *
- * \brief TestSuite: degree to radians (and viceversa) conversions
+ * \brief TestSuite: degree to radians (and vice-versa) conversions
  */
 class DegreesRadiansTestSuite : public TestSuite
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/test/test-isotropic-antenna.cc ns-3.38/src/antenna/test/test-isotropic-antenna.cc
--- ns-3.37/src/antenna/test/test-isotropic-antenna.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/test/test-isotropic-antenna.cc	2023-03-16 20:54:23.576868300 -0700
@@ -45,7 +45,7 @@
     /**
      * Constructor
      * \param a Antenna angle
-     * \param expectedGainDb Expeted antenna gain
+     * \param expectedGainDb Expected antenna gain
      */
     IsotropicAntennaModelTestCase(Angles a, double expectedGainDb);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/test/test-parabolic-antenna.cc ns-3.38/src/antenna/test/test-parabolic-antenna.cc
--- ns-3.37/src/antenna/test/test-parabolic-antenna.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/test/test-parabolic-antenna.cc	2023-03-16 20:54:23.576868300 -0700
@@ -66,7 +66,7 @@
      * \param b Beamwidth
      * \param o Orientation
      * \param g MaxGain
-     * \param expectedGainDb Expeted antenna gain
+     * \param expectedGainDb Expected antenna gain
      * \param cond Test condition
      */
     ParabolicAntennaModelTestCase(Angles a,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/antenna/test/test-uniform-planar-array.cc ns-3.38/src/antenna/test/test-uniform-planar-array.cc
--- ns-3.37/src/antenna/test/test-uniform-planar-array.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/antenna/test/test-uniform-planar-array.cc	2023-03-16 20:54:23.576868300 -0700
@@ -153,14 +153,16 @@
 {
     // compute gain
     PhasedArrayModel::ComplexVector sv = a->GetSteeringVector(m_direction);
-    NS_TEST_EXPECT_MSG_EQ(sv.size(), a->GetNumberOfElements(), "steering vector of wrong size");
+    NS_TEST_EXPECT_MSG_EQ(sv.GetSize(), a->GetNumberOfElements(), "steering vector of wrong size");
     PhasedArrayModel::ComplexVector bf = a->GetBeamformingVector(m_direction);
-    NS_TEST_EXPECT_MSG_EQ(bf.size(), a->GetNumberOfElements(), "beamforming vector of wrong size");
+    NS_TEST_EXPECT_MSG_EQ(bf.GetSize(),
+                          a->GetNumberOfElements(),
+                          "beamforming vector of wrong size");
     std::pair<double, double> fp = a->GetElementFieldPattern(m_direction);
 
     // scalar product dot (sv, bf)
     std::complex<double> prod{0};
-    for (size_t i = 0; i < sv.size(); i++)
+    for (size_t i = 0; i < sv.GetSize(); i++)
     {
         prod += sv[i] * bf[i];
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/examples/aodv.cc ns-3.38/src/aodv/examples/aodv.cc
--- ns-3.37/src/aodv/examples/aodv.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/examples/aodv.cc	2023-03-16 20:54:23.576868300 -0700
@@ -24,8 +24,8 @@
 #include "ns3/internet-module.h"
 #include "ns3/mobility-module.h"
 #include "ns3/network-module.h"
+#include "ns3/ping-helper.h"
 #include "ns3/point-to-point-module.h"
-#include "ns3/v4ping-helper.h"
 #include "ns3/yans-wifi-helper.h"
 
 #include <cmath>
@@ -46,8 +46,8 @@
  *
  * When 1/3 of simulation time has elapsed, one of the nodes is moved out of
  * range, thereby breaking the topology.  By default, this will result in
- * only 34 of 100 pings being received.  If the step size is reduced
- * to cover the gap, then all pings can be received.
+ * stopping ping replies reception after sequence number 33. If the step size is reduced
+ * to cover the gap, then also the following pings can be received.
  */
 class AodvExample
 {
@@ -234,15 +234,15 @@
     {
         Ptr<OutputStreamWrapper> routingStream =
             Create<OutputStreamWrapper>("aodv.routes", std::ios::out);
-        aodv.PrintRoutingTableAllAt(Seconds(8), routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(8), routingStream);
     }
 }
 
 void
 AodvExample::InstallApplications()
 {
-    V4PingHelper ping(interfaces.GetAddress(size - 1));
-    ping.SetAttribute("Verbose", BooleanValue(true));
+    PingHelper ping(interfaces.GetAddress(size - 1));
+    ping.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE));
 
     ApplicationContainer p = ping.Install(nodes.Get(0));
     p.Start(Seconds(0));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/model/aodv-id-cache.cc ns-3.38/src/aodv/model/aodv-id-cache.cc
--- ns-3.37/src/aodv/model/aodv-id-cache.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/model/aodv-id-cache.cc	2023-03-16 20:54:23.576868300 -0700
@@ -43,7 +43,7 @@
             return true;
         }
     }
-    struct UniqueId uniqueId = {addr, id, m_lifetime + Simulator::Now()};
+    UniqueId uniqueId = {addr, id, m_lifetime + Simulator::Now()};
     m_idCache.push_back(uniqueId);
     return false;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/model/aodv-id-cache.h ns-3.38/src/aodv/model/aodv-id-cache.h
--- ns-3.37/src/aodv/model/aodv-id-cache.h	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/model/aodv-id-cache.h	2023-03-16 20:54:23.576868300 -0700
@@ -79,7 +79,7 @@
 
     /**
      * Return lifetime for existing entries in cache
-     * \returns thhe lifetime
+     * \returns the lifetime
      */
     Time GetLifeTime() const
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/model/aodv-packet.h ns-3.38/src/aodv/model/aodv-packet.h
--- ns-3.37/src/aodv/model/aodv-packet.h	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/model/aodv-packet.h	2023-03-16 20:54:23.576868300 -0700
@@ -108,6 +108,7 @@
 /**
  * \brief Stream output operator
  * \param os output stream
+ * \param h the TypeHeader
  * \return updated stream
  */
 std::ostream& operator<<(std::ostream& os, const TypeHeader& h);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/model/aodv-routing-protocol.h ns-3.38/src/aodv/model/aodv-routing-protocol.h
--- ns-3.37/src/aodv/model/aodv-routing-protocol.h	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/model/aodv-routing-protocol.h	2023-03-16 20:54:23.576868300 -0700
@@ -260,7 +260,6 @@
                              ///< originated route discovery.
     bool m_enableHello;      ///< Indicates whether a hello messages enable
     bool m_enableBroadcast;  ///< Indicates whether a a broadcast data packets forwarding enable
-    //\}
 
     /// IP protocol
     Ptr<Ipv4> m_ipv4;
@@ -377,8 +376,10 @@
      */
     Ptr<Ipv4Route> LoopbackRoute(const Ipv4Header& header, Ptr<NetDevice> oif) const;
 
-    ///\name Receive control packets
-    //\{
+    /**
+     * \name Receive control packets
+     * @{
+     */
     /**
      * Receive and process control packet
      * \param socket input socket
@@ -410,10 +411,12 @@
      */
     /// Receive  from node with address src
     void RecvError(Ptr<Packet> p, Ipv4Address src);
-    //\}
+    /** @} */
 
-    ///\name Send
-    //\{
+    /**
+     * \name Send
+     * @{
+     */
     /** Forward packet from route request queue
      * \param dst destination address
      * \param route route to use
@@ -459,7 +462,7 @@
      * \param origin originating node IP address
      */
     void SendRerrWhenNoRouteToForward(Ipv4Address dst, uint32_t dstSeqNo, Ipv4Address origin);
-    /// @}
+    /** @} */
 
     /**
      * Send packet to destination socket
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/test/aodv-id-cache-test-suite.cc ns-3.38/src/aodv/test/aodv-id-cache-test-suite.cc
--- ns-3.37/src/aodv/test/aodv-id-cache-test-suite.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/test/aodv-id-cache-test-suite.cc	2023-03-16 20:54:23.576868300 -0700
@@ -33,13 +33,13 @@
 {
 
 /**
- * \ingroup aodv
  * \defgroup aodv-test AODV module tests
+ * \ingroup aodv
+ * \ingroup tests
  */
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for id cache
  */
@@ -111,7 +111,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Id Cache Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/test/aodv-regression.cc ns-3.38/src/aodv/test/aodv-regression.cc
--- ns-3.37/src/aodv/test/aodv-regression.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/test/aodv-regression.cc	2023-03-16 20:54:23.576868300 -0700
@@ -45,7 +45,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief AODV regression test suite
  */
@@ -69,7 +68,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Chain Regression Test
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/test/aodv-test-suite.cc ns-3.38/src/aodv/test/aodv-test-suite.cc
--- ns-3.37/src/aodv/test/aodv-test-suite.cc	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/aodv/test/aodv-test-suite.cc	2023-03-16 20:54:23.576868300 -0700
@@ -30,7 +30,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for neighbors
  */
@@ -136,7 +135,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Type header test case
  */
@@ -164,7 +162,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for RREQ
  */
@@ -224,7 +221,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for RREP
  */
@@ -282,7 +278,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for RREP-ACK
  */
@@ -307,7 +302,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for RERR
  */
@@ -342,7 +336,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for AODV routing table entry
  */
@@ -559,7 +552,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for AODV routing table entry
  */
@@ -653,7 +645,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief Unit test for AODV routing table
  */
@@ -741,7 +732,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief AODV test suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/aodv/test/loopback.cc ns-3.38/src/aodv/test/loopback.cc
--- ns-3.37/src/aodv/test/loopback.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/aodv/test/loopback.cc	2023-03-16 20:54:23.576868300 -0700
@@ -74,7 +74,7 @@
      * Echo data function
      * \param socket The socket to echo data
      */
-    void EchoData(Ptr<Socket> socket);
+    void EchoData(Ptr<Socket> socket) const;
 
   public:
     LoopbackTestCase();
@@ -98,7 +98,7 @@
 }
 
 void
-LoopbackTestCase::EchoData(Ptr<Socket> socket)
+LoopbackTestCase::EchoData(Ptr<Socket> socket) const
 {
     Address from;
     Ptr<Packet> receivedPacket = socket->RecvFrom(std::numeric_limits<uint32_t>::max(), 0, from);
@@ -191,7 +191,6 @@
 
 /**
  * \ingroup aodv-test
- * \ingroup tests
  *
  * \brief AODV Loopback test suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/applications/model/udp-client.cc ns-3.38/src/applications/model/udp-client.cc
--- ns-3.37/src/applications/model/udp-client.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/applications/model/udp-client.cc	2023-03-16 20:54:23.580868500 -0700
@@ -50,11 +50,12 @@
             .SetParent<Application>()
             .SetGroupName("Applications")
             .AddConstructor<UdpClient>()
-            .AddAttribute("MaxPackets",
-                          "The maximum number of packets the application will send",
-                          UintegerValue(100),
-                          MakeUintegerAccessor(&UdpClient::m_count),
-                          MakeUintegerChecker<uint32_t>())
+            .AddAttribute(
+                "MaxPackets",
+                "The maximum number of packets the application will send (zero means infinite)",
+                UintegerValue(100),
+                MakeUintegerAccessor(&UdpClient::m_count),
+                MakeUintegerChecker<uint32_t>())
             .AddAttribute("Interval",
                           "The time to wait between packets",
                           TimeValue(Seconds(1.0)),
@@ -223,7 +224,7 @@
     }
 #endif // NS3_LOG_ENABLE
 
-    if (m_sent < m_count)
+    if (m_sent < m_count || m_count == 0)
     {
         m_sendEvent = Simulator::Schedule(m_interval, &UdpClient::Send, this);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/applications/model/udp-echo-client.cc ns-3.38/src/applications/model/udp-echo-client.cc
--- ns-3.37/src/applications/model/udp-echo-client.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/applications/model/udp-echo-client.cc	2023-03-16 20:54:23.580868500 -0700
@@ -44,11 +44,12 @@
             .SetParent<Application>()
             .SetGroupName("Applications")
             .AddConstructor<UdpEchoClient>()
-            .AddAttribute("MaxPackets",
-                          "The maximum number of packets the application will send",
-                          UintegerValue(100),
-                          MakeUintegerAccessor(&UdpEchoClient::m_count),
-                          MakeUintegerChecker<uint32_t>())
+            .AddAttribute(
+                "MaxPackets",
+                "The maximum number of packets the application will send (zero means infinite)",
+                UintegerValue(100),
+                MakeUintegerAccessor(&UdpEchoClient::m_count),
+                MakeUintegerChecker<uint32_t>())
             .AddAttribute("Interval",
                           "The time to wait between packets",
                           TimeValue(Seconds(1.0)),
@@ -391,7 +392,7 @@
                        << Inet6SocketAddress::ConvertFrom(m_peerAddress).GetPort());
     }
 
-    if (m_sent < m_count)
+    if (m_sent < m_count || m_count == 0)
     {
         ScheduleTransmit(m_interval);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/applications/model/udp-trace-client.cc ns-3.38/src/applications/model/udp-trace-client.cc
--- ns-3.37/src/applications/model/udp-trace-client.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/applications/model/udp-trace-client.cc	2023-03-16 20:54:23.580868500 -0700
@@ -48,16 +48,18 @@
 /**
  * \brief Default trace to send
  */
-struct UdpTraceClient::TraceEntry UdpTraceClient::g_defaultEntries[] = {{0, 534, 'I'},
-                                                                        {40, 1542, 'P'},
-                                                                        {120, 134, 'B'},
-                                                                        {80, 390, 'B'},
-                                                                        {240, 765, 'P'},
-                                                                        {160, 407, 'B'},
-                                                                        {200, 504, 'B'},
-                                                                        {360, 903, 'P'},
-                                                                        {280, 421, 'B'},
-                                                                        {320, 587, 'B'}};
+struct UdpTraceClient::TraceEntry UdpTraceClient::g_defaultEntries[] = {
+    {0, 534, 'I'},
+    {40, 1542, 'P'},
+    {120, 134, 'B'},
+    {80, 390, 'B'},
+    {240, 765, 'P'},
+    {160, 407, 'B'},
+    {200, 504, 'B'},
+    {360, 903, 'P'},
+    {280, 421, 'B'},
+    {320, 587, 'B'},
+};
 
 TypeId
 UdpTraceClient::GetTypeId()
@@ -149,7 +151,7 @@
 UdpTraceClient::SetTraceFile(std::string traceFile)
 {
     NS_LOG_FUNCTION(this << traceFile);
-    if (traceFile == "")
+    if (traceFile.empty())
     {
         LoadDefaultTrace();
     }
@@ -229,9 +231,9 @@
 {
     NS_LOG_FUNCTION(this);
     uint32_t prevTime = 0;
-    for (uint32_t i = 0; i < (sizeof(g_defaultEntries) / sizeof(struct TraceEntry)); i++)
+    for (uint32_t i = 0; i < (sizeof(g_defaultEntries) / sizeof(TraceEntry)); i++)
     {
-        struct TraceEntry entry = g_defaultEntries[i];
+        TraceEntry entry = g_defaultEntries[i];
         if (entry.frameType == 'B')
         {
             entry.timeToSend = 0;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/applications/model/udp-trace-client.h ns-3.38/src/applications/model/udp-trace-client.h
--- ns-3.37/src/applications/model/udp-trace-client.h	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/applications/model/udp-trace-client.h	2023-03-16 20:54:23.580868500 -0700
@@ -165,9 +165,9 @@
     uint16_t m_peerPort;   //!< Remote peer port
     EventId m_sendEvent;   //!< Event to send the next packet
 
-    std::vector<struct TraceEntry> m_entries;    //!< Entries in the trace to send
-    uint32_t m_currentEntry;                     //!< Current entry index
-    static struct TraceEntry g_defaultEntries[]; //!< Default trace to send
+    std::vector<TraceEntry> m_entries;    //!< Entries in the trace to send
+    uint32_t m_currentEntry;              //!< Current entry index
+    static TraceEntry g_defaultEntries[]; //!< Default trace to send
     uint16_t m_maxPacketSize; //!< Maximum packet size to send (including the SeqTsHeader)
     bool m_traceLoop;         //!< Loop through the trace file
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/applications/test/bulk-send-application-test-suite.cc ns-3.38/src/applications/test/bulk-send-application-test-suite.cc
--- ns-3.37/src/applications/test/bulk-send-application-test-suite.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/applications/test/bulk-send-application-test-suite.cc	2023-03-16 20:54:23.580868500 -0700
@@ -173,8 +173,8 @@
     uint64_t m_received{0};      //!< number of bytes received
     uint64_t m_seqTxCounter{0};  //!< Counter for Sequences on Tx
     uint64_t m_seqRxCounter{0};  //!< Counter for Sequences on Rx
-    Time m_lastTxTs{Seconds(0)}; //!< Last recored timestamp on Tx
-    Time m_lastRxTs{Seconds(0)}; //!< Last recored timestamp on Rx
+    Time m_lastTxTs{Seconds(0)}; //!< Last recorded timestamp on Tx
+    Time m_lastRxTs{Seconds(0)}; //!< Last recorded timestamp on Rx
 };
 
 BulkSendSeqTsSizeTestCase::BulkSendSeqTsSizeTestCase()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/applications/test/udp-client-server-test.cc ns-3.38/src/applications/test/udp-client-server-test.cc
--- ns-3.37/src/applications/test/udp-client-server-test.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/applications/test/udp-client-server-test.cc	2023-03-16 20:54:23.580868500 -0700
@@ -330,13 +330,13 @@
 
     ApplicationContainer clientApps = echoClient.Install(nodes.Get(0));
 
-    uint8_t arry[64];
+    uint8_t array[64];
     uint8_t i;
     for (i = 0; i < 64; i++)
     {
-        arry[i] = i;
+        array[i] = i;
     }
-    echoClient.SetFill(clientApps.Get(0), &(arry[0]), (uint32_t)64, (uint32_t)64);
+    echoClient.SetFill(clientApps.Get(0), &(array[0]), (uint32_t)64, (uint32_t)64);
 
     clientApps.Start(Seconds(2.0));
     clientApps.Stop(Seconds(10.0));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/bridge/examples/csma-bridge.cc ns-3.38/src/bridge/examples/csma-bridge.cc
--- ns-3.37/src/bridge/examples/csma-bridge.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/bridge/examples/csma-bridge.cc	2023-03-16 20:54:23.580868500 -0700
@@ -167,4 +167,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/bridge/examples/csma-bridge-one-hop.cc ns-3.38/src/bridge/examples/csma-bridge-one-hop.cc
--- ns-3.37/src/bridge/examples/csma-bridge-one-hop.cc	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/bridge/examples/csma-bridge-one-hop.cc	2023-03-16 20:54:23.580868500 -0700
@@ -16,7 +16,7 @@
 // Network topology
 //
 //         bridge1         The node named bridge1 (node 5 in the nodelist)
-//   ------------------        has three CMSA net devices that are bridged
+//   ------------------        has three CSMA net devices that are bridged
 //   CSMA   CSMA   CSMA        together using a BridgeNetDevice.
 //     |      |      |
 //     |      |      |     The bridge node talks over three CSMA channels
@@ -34,7 +34,7 @@
 //     |      |      |
 //     |      |      |
 //   CSMA   CSMA   CSMA    The node named bridge2 (node 6 in the nodelist)
-//   ------------------        has three CMSA net devices that are bridged
+//   ------------------        has three CSMA net devices that are bridged
 //        bridge2              together using a BridgeNetDevice.
 //
 // Or, more abstractly, recognizing that bridge 1 and bridge 2 are nodes
@@ -238,4 +238,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/bridge/examples/csma-bridge.py ns-3.38/src/bridge/examples/csma-bridge.py
--- ns-3.37/src/bridge/examples/csma-bridge.py	2022-11-01 17:37:24.455395000 -0700
+++ ns-3.38/src/bridge/examples/csma-bridge.py	2023-03-16 20:54:23.580868500 -0700
@@ -96,8 +96,7 @@
     port = 9   # Discard port(RFC 863)
 
     inet_sock_address = ns.network.InetSocketAddress(ns.network.Ipv4Address("10.1.1.2"), port)
-    onoff = ns.applications.OnOffHelper("ns3::UdpSocketFactory",
-                            ns.network.Address(ns.addressFromInetSocketAddress(inet_sock_address)))
+    onoff = ns.applications.OnOffHelper("ns3::UdpSocketFactory", inet_sock_address.ConvertTo())
     onoff.SetConstantRate (ns.network.DataRate ("500kb/s"))
 
     app = onoff.Install(ns.network.NodeContainer(terminals.Get(0)))
@@ -107,8 +106,7 @@
 
     # Create an optional packet sink to receive these packets
     inet_address = ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), port)
-    sink = ns.applications.PacketSinkHelper("ns3::UdpSocketFactory",
-                                ns.network.Address(ns.addressFromInetSocketAddress(inet_address)))
+    sink = ns.applications.PacketSinkHelper("ns3::UdpSocketFactory", inet_address.ConvertTo())
     app = sink.Install(ns.network.NodeContainer(terminals.Get(1)))
     app.Start(ns.core.Seconds(0.0))
 
@@ -117,7 +115,7 @@
     #
     inet_address = ns.network.InetSocketAddress(ns.network.Ipv4Address("10.1.1.1"), port)
     onoff.SetAttribute("Remote",
-                       ns.network.AddressValue(ns.addressFromInetSocketAddress(inet_address)))
+                       ns.network.AddressValue(inet_address.ConvertTo()))
     app = onoff.Install(ns.network.NodeContainer(terminals.Get(3)))
     app.Start(ns.core.Seconds(1.1))
     app.Stop(ns.core.Seconds(10.0))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/CMakeLists.txt ns-3.38/src/brite/CMakeLists.txt
--- ns-3.37/src/brite/CMakeLists.txt	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/CMakeLists.txt	2023-03-16 20:54:23.580868500 -0700
@@ -18,8 +18,10 @@
 
 if((NOT
     brite_FOUND)
-   OR (NOT
-       ${brite_FOUND})
+   AND (NOT
+        ${brite_FOUND})
+   AND (NOT
+        ${NS3_FETCH_OPTIONAL_COMPONENTS})
 )
   message(
     ${HIGHLIGHTED_STATUS}
@@ -28,6 +30,12 @@
   return()
 endif()
 
+if(${NS3_FETCH_OPTIONAL_COMPONENTS})
+  set(brite_LIBRARIES
+      brite
+  )
+endif()
+
 # Only process module if include folder and library have been found
 include_directories(${brite_INCLUDE_DIRS})
 set(NS3_BRITE
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/brite-generic-example.cc ns-3.38/src/brite/examples/brite-generic-example.cc
--- ns-3.37/src/brite/examples/brite-generic-example.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/brite-generic-example.cc	2023-03-16 20:54:23.580868500 -0700
@@ -14,6 +14,8 @@
  *
  */
 
+#include "Brite.h"
+
 #include "ns3/applications-module.h"
 #include "ns3/brite-module.h"
 #include "ns3/core-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/brite-MPI-example.cc ns-3.38/src/brite/examples/brite-MPI-example.cc
--- ns-3.37/src/brite/examples/brite-MPI-example.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/brite-MPI-example.cc	2023-03-16 20:54:23.580868500 -0700
@@ -14,6 +14,8 @@
  *
  */
 
+#include "Brite.h"
+
 #include "ns3/applications-module.h"
 #include "ns3/brite-module.h"
 #include "ns3/core-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/ASBarabasi.conf ns-3.38/src/brite/examples/conf_files/ASBarabasi.conf
--- ns-3.37/src/brite/examples/conf_files/ASBarabasi.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/ASBarabasi.conf	2023-03-16 20:54:23.580868500 -0700
@@ -15,7 +15,7 @@
 EndModel
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/ASWaxman.conf ns-3.38/src/brite/examples/conf_files/ASWaxman.conf
--- ns-3.37/src/brite/examples/conf_files/ASWaxman.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/ASWaxman.conf	2023-03-16 20:54:23.580868500 -0700
@@ -17,7 +17,7 @@
 	BWMax = 1024.0
 EndModel
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTBarabasi10.conf ns-3.38/src/brite/examples/conf_files/RTBarabasi10.conf
--- ns-3.37/src/brite/examples/conf_files/RTBarabasi10.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTBarabasi10.conf	2023-03-16 20:54:23.580868500 -0700
@@ -16,7 +16,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTBarabasi20.conf ns-3.38/src/brite/examples/conf_files/RTBarabasi20.conf
--- ns-3.37/src/brite/examples/conf_files/RTBarabasi20.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTBarabasi20.conf	2023-03-16 20:54:23.580868500 -0700
@@ -16,7 +16,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 1		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTBarabasi5.conf ns-3.38/src/brite/examples/conf_files/RTBarabasi5.conf
--- ns-3.37/src/brite/examples/conf_files/RTBarabasi5.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTBarabasi5.conf	2023-03-16 20:54:23.580868500 -0700
@@ -16,7 +16,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTBarabasi.conf ns-3.38/src/brite/examples/conf_files/RTBarabasi.conf
--- ns-3.37/src/brite/examples/conf_files/RTBarabasi.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTBarabasi.conf	2023-03-16 20:54:23.580868500 -0700
@@ -16,7 +16,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTWaxman10.conf ns-3.38/src/brite/examples/conf_files/RTWaxman10.conf
--- ns-3.37/src/brite/examples/conf_files/RTWaxman10.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTWaxman10.conf	2023-03-16 20:54:23.580868500 -0700
@@ -19,7 +19,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTWaxman20.conf ns-3.38/src/brite/examples/conf_files/RTWaxman20.conf
--- ns-3.37/src/brite/examples/conf_files/RTWaxman20.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTWaxman20.conf	2023-03-16 20:54:23.580868500 -0700
@@ -19,7 +19,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTWaxman5.conf ns-3.38/src/brite/examples/conf_files/RTWaxman5.conf
--- ns-3.37/src/brite/examples/conf_files/RTWaxman5.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTWaxman5.conf	2023-03-16 20:54:23.580868500 -0700
@@ -19,7 +19,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/RTWaxman.conf ns-3.38/src/brite/examples/conf_files/RTWaxman.conf
--- ns-3.37/src/brite/examples/conf_files/RTWaxman.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/RTWaxman.conf	2023-03-16 20:54:23.580868500 -0700
@@ -19,7 +19,7 @@
 
 
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 1		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf ns-3.38/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf
--- ns-3.37/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf	2023-03-16 20:54:23.580868500 -0700
@@ -43,7 +43,7 @@
 	BWMax = -1.0
 EndModel
 
-BeginOutput 			#**Atleast one of these options should have value 1**
+BeginOutput 			#**At least one of these options should have value 1**
 	BRITE = 1		#0 = Do not save as BRITE, 1 = save as BRITE.
 	OTTER = 0		#0 = Do not visualize with Otter, 1 = Visualize
 EndOutput
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/helper/brite-topology-helper.cc ns-3.38/src/brite/helper/brite-topology-helper.cc
--- ns-3.37/src/brite/helper/brite-topology-helper.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/helper/brite-topology-helper.cc	2023-03-16 20:54:23.580868500 -0700
@@ -16,6 +16,8 @@
 
 #include "brite-topology-helper.h"
 
+#include "Brite.h"
+
 #include "ns3/abort.h"
 #include "ns3/data-rate.h"
 #include "ns3/ipv4-address-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/brite/helper/brite-topology-helper.h ns-3.38/src/brite/helper/brite-topology-helper.h
--- ns-3.37/src/brite/helper/brite-topology-helper.h	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/brite/helper/brite-topology-helper.h	2023-03-16 20:54:23.580868500 -0700
@@ -28,8 +28,12 @@
 #include <string>
 #include <vector>
 
-// located in BRITE source directory
-#include "Brite.h"
+// These are in #include <Brite.h>,
+// here we just need a forward declaration.
+namespace brite
+{
+struct Topology;
+};
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/doc/source/buildings-design.rst ns-3.38/src/buildings/doc/source/buildings-design.rst
--- ns-3.37/src/buildings/doc/source/buildings-design.rst	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/doc/source/buildings-design.rst	2023-03-16 20:54:23.580868500 -0700
@@ -151,8 +151,8 @@
 
 The model considers that the mean of the shadowing loss in dB is always 0. For the variance, the model considers three possible values of standard deviation, in detail:
 
- * outdoor (``m_shadowingSigmaOutdoor``, defaul value of 7 dB) :math:`\rightarrow X_\mathrm{O} \sim N(\mu_\mathrm{O}, \sigma_\mathrm{O}^2)`.
- * indoor (``m_shadowingSigmaIndoor``, defaul value of 10 dB) :math:`\rightarrow X_\mathrm{I} \sim N(\mu_\mathrm{I}, \sigma_\mathrm{I}^2)`.
+ * outdoor (``m_shadowingSigmaOutdoor``, default value of 7 dB) :math:`\rightarrow X_\mathrm{O} \sim N(\mu_\mathrm{O}, \sigma_\mathrm{O}^2)`.
+ * indoor (``m_shadowingSigmaIndoor``, default value of 10 dB) :math:`\rightarrow X_\mathrm{I} \sim N(\mu_\mathrm{I}, \sigma_\mathrm{I}^2)`.
  * external walls penetration (``m_shadowingSigmaExtWalls``, default value 5 dB) :math:`\rightarrow X_\mathrm{W} \sim N(\mu_\mathrm{W}, \sigma_\mathrm{W}^2)`
 
 The simulator generates a shadowing value per each active link according to nodes' position the first time the link is used for transmitting. In case of transmissions from outdoor nodes to indoor ones, and vice-versa, the standard deviation (:math:`\sigma_\mathrm{IO}`) has to be calculated as the square root of the sum of the quadratic values of the standard deviatio in case of outdoor nodes and the one for the external walls penetration. This is due to the fact that that the components producing the shadowing are independent of each other; therefore, the variance of a distribution resulting from the sum of two independent normal ones is the sum of the variances.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/doc/source/buildings-user.rst ns-3.38/src/buildings/doc/source/buildings-user.rst
--- ns-3.37/src/buildings/doc/source/buildings-user.rst	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/doc/source/buildings-user.rst	2023-03-16 20:54:23.580868500 -0700
@@ -30,13 +30,13 @@
     double y_max = 20.0;
     double z_min = 0.0;
     double z_max = 10.0;
-    Ptr<Building> b = CreateObject <Building> ();
-    b->SetBoundaries (Box (x_min, x_max, y_min, y_max, z_min, z_max));
-    b->SetBuildingType (Building::Residential);
-    b->SetExtWallsType (Building::ConcreteWithWindows);
-    b->SetNFloors (3);
-    b->SetNRoomsX (3);
-    b->SetNRoomsY (2);
+    Ptr<Building> b = CreateObject<Building>();
+    b->SetBoundaries(Box(x_min, x_max, y_min, y_max, z_min, z_max));
+    b->SetBuildingType(Building::Residential);
+    b->SetExtWallsType(Building::ConcreteWithWindows);
+    b->SetNFloors(3);
+    b->SetNRoomsX(3);
+    b->SetNRoomsY(2);
 
 This building has three floors and an internal 3 x 2  grid of rooms of equal size.
 
@@ -45,19 +45,19 @@
 rectangular grid. Here's an example of how to use it::
 
   Ptr<GridBuildingAllocator>  gridBuildingAllocator;
-  gridBuildingAllocator = CreateObject<GridBuildingAllocator> ();
-  gridBuildingAllocator->SetAttribute ("GridWidth", UintegerValue (3));
-  gridBuildingAllocator->SetAttribute ("LengthX", DoubleValue (7));
-  gridBuildingAllocator->SetAttribute ("LengthY", DoubleValue (13));
-  gridBuildingAllocator->SetAttribute ("DeltaX", DoubleValue (3));
-  gridBuildingAllocator->SetAttribute ("DeltaY", DoubleValue (3));
-  gridBuildingAllocator->SetAttribute ("Height", DoubleValue (6));
-  gridBuildingAllocator->SetBuildingAttribute ("NRoomsX", UintegerValue (2));
-  gridBuildingAllocator->SetBuildingAttribute ("NRoomsY", UintegerValue (4));
-  gridBuildingAllocator->SetBuildingAttribute ("NFloors", UintegerValue (2));
-  gridBuildingAllocator->SetAttribute ("MinX", DoubleValue (0));
-  gridBuildingAllocator->SetAttribute ("MinY", DoubleValue (0));
-  gridBuildingAllocator->Create (6);
+  gridBuildingAllocator = CreateObject<GridBuildingAllocator>();
+  gridBuildingAllocator->SetAttribute("GridWidth", UintegerValue(3));
+  gridBuildingAllocator->SetAttribute("LengthX", DoubleValue(7));
+  gridBuildingAllocator->SetAttribute("LengthY", DoubleValue(13));
+  gridBuildingAllocator->SetAttribute("DeltaX", DoubleValue(3));
+  gridBuildingAllocator->SetAttribute("DeltaY", DoubleValue(3));
+  gridBuildingAllocator->SetAttribute("Height", DoubleValue(6));
+  gridBuildingAllocator->SetBuildingAttribute("NRoomsX", UintegerValue(2));
+  gridBuildingAllocator->SetBuildingAttribute("NRoomsY", UintegerValue(4));
+  gridBuildingAllocator->SetBuildingAttribute("NFloors", UintegerValue(2));
+  gridBuildingAllocator->SetAttribute("MinX", DoubleValue(0));
+  gridBuildingAllocator->SetAttribute("MinY", DoubleValue(0));
+  gridBuildingAllocator->Create(6);
 
 
 This will create a 3x2 grid of 6 buildings, each 7 x 13 x 6 m with 2 x
@@ -74,10 +74,10 @@
 the information on their position w.r.t. the buildings. Here is an example::
 
     MobilityHelper mobility;
-    mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
-    ueNodes.Create (2);
-    mobility.Install (ueNodes);
-    BuildingsHelper::Install (ueNodes);
+    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+    ueNodes.Create(2);
+    mobility.Install(ueNodes);
+    BuildingsHelper::Install(ueNodes);
 
 It is to be noted that any mobility model can be used. However, the
 user is advised to make sure that the behavior of the mobility model
@@ -120,18 +120,18 @@
 simulation. The important additional step is to For example, you can
 place nodes manually like this::
 
-    Ptr<ConstantPositionMobilityModel> mm0 = enbNodes.Get (0)->GetObject<ConstantPositionMobilityModel> ();
-    Ptr<ConstantPositionMobilityModel> mm1 = enbNodes.Get (1)->GetObject<ConstantPositionMobilityModel> ();
-    mm0->SetPosition (Vector (5.0, 5.0, 1.5));
-    mm1->SetPosition (Vector (30.0, 40.0, 1.5));
+    Ptr<ConstantPositionMobilityModel> mm0 = enbNodes.Get(0)->GetObject<ConstantPositionMobilityModel>();
+    Ptr<ConstantPositionMobilityModel> mm1 = enbNodes.Get(1)->GetObject<ConstantPositionMobilityModel>();
+    mm0->SetPosition(Vector(5.0, 5.0, 1.5));
+    mm1->SetPosition(Vector(30.0, 40.0, 1.5));
 
     MobilityHelper mobility;
-    mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
-    ueNodes.Create (2);
-    mobility.Install (ueNodes);
-    BuildingsHelper::Install (ueNodes);
-    mm0->SetPosition (Vector (5.0, 5.0, 1.5));
-    mm1->SetPosition (Vector (30.0, 40.0, 1.5));
+    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+    ueNodes.Create(2);
+    mobility.Install(ueNodes);
+    BuildingsHelper::Install(ueNodes);
+    mm0->SetPosition(Vector(5.0, 5.0, 1.5));
+    mm1->SetPosition(Vector(30.0, 40.0, 1.5));
 
 Alternatively, you could use any existing PositionAllocator
 class. The coordinates of the node will determine whether it is placed
@@ -220,9 +220,9 @@
 
 The ``BuildingPropagationLossModel`` class has the following configurable parameters configurable with the attribute system:
 
-* ``Frequency``: reference frequency (default 2160 MHz), note that by setting the frequency the wavelength is set accordingly automatically and viceversa).
+* ``Frequency``: reference frequency (default 2160 MHz), note that by setting the frequency the wavelength is set accordingly automatically and vice-versa).
 * ``Lambda``: the wavelength (0.139 meters, considering the above frequency).
-* ``ShadowSigmaOutdoor``: the standard deviation of the shadowing for outdoor nodes (defaul 7.0).
+* ``ShadowSigmaOutdoor``: the standard deviation of the shadowing for outdoor nodes (default 7.0).
 * ``ShadowSigmaIndoor``: the standard deviation of the shadowing for indoor nodes (default 8.0).
 * ``ShadowSigmaExtWalls``: the standard deviation of the shadowing due to external walls penetration for outdoor to indoor communications (default 5.0).
 * ``RooftopLevel``: the level of the rooftop of the building in meters (default 20 meters).
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/examples/buildings-pathloss-profiler.cc ns-3.38/src/buildings/examples/buildings-pathloss-profiler.cc
--- ns-3.37/src/buildings/examples/buildings-pathloss-profiler.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/examples/buildings-pathloss-profiler.cc	2023-03-16 20:54:23.580868500 -0700
@@ -95,4 +95,6 @@
     }
 
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/examples/outdoor-group-mobility-example.cc ns-3.38/src/buildings/examples/outdoor-group-mobility-example.cc
--- ns-3.37/src/buildings/examples/outdoor-group-mobility-example.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/examples/outdoor-group-mobility-example.cc	2023-03-16 20:54:23.580868500 -0700
@@ -98,7 +98,7 @@
 /**
  * Print the buildings list in a format that can be used by Gnuplot to draw them.
  *
- * \param filename The ouput filename.
+ * \param filename The output filename.
  */
 void
 PrintGnuplottableBuildingListToFile(std::string filename)
@@ -279,4 +279,6 @@
     Simulator::Run();
     g_timeSeries.close();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/examples/outdoor-random-walk-example.cc ns-3.38/src/buildings/examples/outdoor-random-walk-example.cc
--- ns-3.37/src/buildings/examples/outdoor-random-walk-example.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/examples/outdoor-random-walk-example.cc	2023-03-16 20:54:23.580868500 -0700
@@ -31,7 +31,7 @@
 /**
  * Print the buildings list in a format that can be used by Gnuplot to draw them.
  *
- * \param filename The ouput filename.
+ * \param filename The output filename.
  */
 void
 PrintGnuplottableBuildingListToFile(std::string filename)
@@ -131,4 +131,6 @@
     Simulator::Stop(Seconds(1e4));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/helper/building-container.h ns-3.38/src/buildings/helper/building-container.h
--- ns-3.37/src/buildings/helper/building-container.h	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/helper/building-container.h	2023-03-16 20:54:23.580868500 -0700
@@ -137,7 +137,7 @@
      * Buildings can be retrieved from the container in two ways.  First,
      * directly by an index into the container, and second, using an iterator.
      * This method is used in the direct method and is used to retrieve the
-     * indexed Ptr<Appliation>.
+     * indexed Ptr<Application>.
      *
      * \code
      *   uint32_t nBuildings = container.GetN ();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/helper/building-position-allocator.h ns-3.38/src/buildings/helper/building-position-allocator.h
--- ns-3.37/src/buildings/helper/building-position-allocator.h	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/helper/building-position-allocator.h	2023-03-16 20:54:23.580868500 -0700
@@ -162,7 +162,7 @@
 
   private:
     /**
-     * Room informations
+     * Room information
      */
     struct RoomInfo
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/model/building-list.cc ns-3.38/src/buildings/model/building-list.cc
--- ns-3.37/src/buildings/model/building-list.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/model/building-list.cc	2023-03-16 20:54:23.580868500 -0700
@@ -55,13 +55,13 @@
      */
     uint32_t Add(Ptr<Building> building);
     /**
-     * Returns an interator to the start of the list.
+     * Returns an iterator to the start of the list.
      *
      * \returns iterator to the begin of the container.
      */
     BuildingList::Iterator Begin() const;
     /**
-     * Returns an interator to the end of the list.
+     * Returns an iterator to the end of the list.
      *
      * \returns iterator to the end of the container.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/model/buildings-channel-condition-model.cc ns-3.38/src/buildings/model/buildings-channel-condition-model.cc
--- ns-3.37/src/buildings/model/buildings-channel-condition-model.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/model/buildings-channel-condition-model.cc	2023-03-16 20:54:23.584868400 -0700
@@ -105,7 +105,7 @@
     {
         cond->SetO2iCondition(ChannelCondition::O2iConditionValue::O2I);
 
-        NS_LOG_DEBUG("a is indoor and b outdoor or viceversa");
+        NS_LOG_DEBUG("a is indoor and b outdoor or vice-versa");
         cond->SetLosCondition(ChannelCondition::LosConditionValue::NLOS);
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/model/hybrid-buildings-propagation-loss-model.cc ns-3.38/src/buildings/model/hybrid-buildings-propagation-loss-model.cc
--- ns-3.37/src/buildings/model/hybrid-buildings-propagation-loss-model.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/model/hybrid-buildings-propagation-loss-model.cc	2023-03-16 20:54:23.584868400 -0700
@@ -174,7 +174,7 @@
                 }
                 else
                 {
-                    // Over the rooftop tranmission -> Okumura Hata
+                    // Over the rooftop transmission -> Okumura Hata
                     loss = OkumuraHata(a, b);
                     NS_LOG_INFO(this << " O-O (>1000): above rooftop -> OH : " << loss);
                 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/model/random-walk-2d-outdoor-mobility-model.h ns-3.38/src/buildings/model/random-walk-2d-outdoor-mobility-model.h
--- ns-3.37/src/buildings/model/random-walk-2d-outdoor-mobility-model.h	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/model/random-walk-2d-outdoor-mobility-model.h	2023-03-16 20:54:23.584868400 -0700
@@ -124,7 +124,7 @@
 
     ConstantVelocityHelper m_helper;       //!< helper for this object
     EventId m_event;                       //!< stored event ID
-    enum Mode m_mode;                      //!< whether in time or distance mode
+    Mode m_mode;                           //!< whether in time or distance mode
     double m_modeDistance;                 //!< Change direction and speed after this distance
     Time m_modeTime;                       //!< Change current direction and speed after this delay
     Ptr<RandomVariableStream> m_speed;     //!< rv for picking speed
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/test/buildings-channel-condition-model-test.cc ns-3.38/src/buildings/test/buildings-channel-condition-model-test.cc
--- ns-3.37/src/buildings/test/buildings-channel-condition-model-test.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/test/buildings-channel-condition-model-test.cc	2023-03-16 20:54:23.584868400 -0700
@@ -59,14 +59,14 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         Vector m_positionA;                            //!< the position of the first node
         Vector m_positionB;                            //!< the position of the second node
         ChannelCondition::LosConditionValue m_losCond; //!< the correct channel condition
-    } TestVector;
+    };
 
-    TestVectors<TestVector> m_testVectors; //!< array containg all the test vectors
+    TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
 };
 
 BuildingsChannelConditionModelTestCase::BuildingsChannelConditionModelTestCase()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/test/buildings-pathloss-test.h ns-3.38/src/buildings/test/buildings-pathloss-test.h
--- ns-3.37/src/buildings/test/buildings-pathloss-test.h	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/test/buildings-pathloss-test.h	2023-03-16 20:54:23.584868400 -0700
@@ -55,7 +55,7 @@
      * \param freq Communication frequency
      * \param m1 First MobilityModel Index
      * \param m2 Second MobilityModel Index
-     * \param env Enviroment type
+     * \param env Environment type
      * \param city City size
      * \param refValue Theoretical loss
      * \param name Test name
@@ -81,7 +81,7 @@
     double m_freq;                  //!< Communication frequency
     uint16_t m_mobilityModelIndex1; //!< First MobilityModel Index
     uint16_t m_mobilityModelIndex2; //!< Second MobilityModel Index
-    EnvironmentType m_env;          //!< Enviroment type
+    EnvironmentType m_env;          //!< Environment type
     CitySize m_city;                //!< City size
     double m_lossRef;               //!< Theoretical loss
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc ns-3.38/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc
--- ns-3.37/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc	2023-03-16 20:54:23.584868400 -0700
@@ -68,15 +68,15 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         Vector m_positionA;                            //!< the position of the first node
         Vector m_positionB;                            //!< the position of the second node
         ChannelCondition::LosConditionValue m_losCond; //!< the correct channel condition
         TypeId m_typeId; //!< the type ID of the channel condition model to be used
-    } TestVector;
+    };
 
-    TestVectors<TestVector> m_testVectors; //!< array containg all the test vectors
+    TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
 };
 
 ThreeGppV2vBuildingsChCondModelTestCase::ThreeGppV2vBuildingsChCondModelTestCase()
@@ -221,13 +221,13 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         Vector m_positionA; //!< the position of the first node
         Vector m_positionB; //!< the position of the second node
         double m_pLos;      //!< LOS probability
         TypeId m_typeId;    //!< the type ID of the channel condition model to be used
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     Ptr<ThreeGppV2vUrbanChannelConditionModel> m_condModel; //!< the channel condition model
@@ -386,13 +386,13 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         Vector m_positionA; //!< the position of the first node
         Vector m_positionB; //!< the position of the second node
         double m_pLos;      //!< LOS probability
         TypeId m_typeId;    //!< the type ID of the channel condition model to be used
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     Ptr<ThreeGppV2vHighwayChannelConditionModel> m_condModel; //!< the channel condition model
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/click/CMakeLists.txt ns-3.38/src/click/CMakeLists.txt
--- ns-3.37/src/click/CMakeLists.txt	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/click/CMakeLists.txt	2023-03-16 20:54:23.584868400 -0700
@@ -23,6 +23,8 @@
     click_FOUND)
    AND (NOT
         ${click_FOUND})
+   AND (NOT
+        ${NS3_FETCH_OPTIONAL_COMPONENTS})
 )
   message(
     ${HIGHLIGHTED_STATUS}
@@ -31,6 +33,13 @@
   return()
 endif()
 
+if(${NS3_FETCH_OPTIONAL_COMPONENTS})
+  set(click_LIBRARIES
+      click
+      nsclick
+  )
+endif()
+
 include_directories(${click_INCLUDE_DIRS})
 set(NS3_CLICK
     "ON"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/click/doc/click.rst ns-3.38/src/click/doc/click.rst
--- ns-3.37/src/click/doc/click.rst	2022-11-01 17:37:24.459395000 -0700
+++ ns-3.38/src/click/doc/click.rst	2023-03-16 20:54:23.584868400 -0700
@@ -142,9 +142,9 @@
 .. sourcecode:: cpp
 
   ClickInternetStackHelper click;
-  click.SetClickFile (myNodeContainer, "nsclick-simple-lan.click");
-  click.SetRoutingTableElement (myNodeContainer, "u/rt");
-  click.Install (myNodeContainer);
+  click.SetClickFile(myNodeContainer, "nsclick-simple-lan.click");
+  click.SetRoutingTableElement(myNodeContainer, "u/rt");
+  click.Install(myNodeContainer);
 
 The example scripts inside ``src/click/examples/`` demonstrate the use of Click based nodes
 in different scenarios. The helper source can be found inside ``src/click/helper/click-internet-stack-helper.{h,cc}``
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/click/model/ipv4-click-routing.cc ns-3.38/src/click/model/ipv4-click-routing.cc
--- ns-3.37/src/click/model/ipv4-click-routing.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/click/model/ipv4-click-routing.cc	2023-03-16 20:54:23.584868400 -0700
@@ -29,6 +29,7 @@
 #include "ns3/random-variable-stream.h"
 #include "ns3/simulator.h"
 
+#include <click/simclick.h>
 #include <cstdarg>
 #include <cstdlib>
 #include <map>
@@ -817,7 +818,7 @@
         // Try to fill the buffer with up to size bytes.
         // If this is not enough space, write the required buffer size into
         // the size variable and return an error code.
-        // Otherwise return the bytes actually writte into the buffer in size.
+        // Otherwise return the bytes actually written into the buffer in size.
 
         // Append key/value pair, separated by \0.
         std::map<std::string, std::string> defines = clickInstance->GetDefines();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/click/model/ipv4-click-routing.h ns-3.38/src/click/model/ipv4-click-routing.h
--- ns-3.37/src/click/model/ipv4-click-routing.h	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/click/model/ipv4-click-routing.h	2023-03-16 20:54:23.584868400 -0700
@@ -26,19 +26,18 @@
 #include "ns3/packet.h"
 #include "ns3/test.h"
 
-#include <sys/time.h>
-#include <sys/types.h>
-
-#ifdef NS3_CLICK
-#include <click/simclick.h>
-#endif
-
 #include <map>
 #include <string>
+#include <sys/time.h>
+#include <sys/types.h>
 
 class ClickTrivialTest;
 class ClickIfidFromNameTest;
 class ClickIpMacAddressFromNameTest;
+// These are in #include <click/simclick.h>,
+// here we just need a forward declaration.
+struct simclick_node;
+typedef struct simclick_node simclick_node_t;
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/CMakeLists.txt ns-3.38/src/CMakeLists.txt
--- ns-3.37/src/CMakeLists.txt	2022-11-01 17:37:24.451395000 -0700
+++ ns-3.38/src/CMakeLists.txt	2023-03-16 20:54:23.572868300 -0700
@@ -87,3 +87,7 @@
     add_dependencies(timeTraceReport ${lib-ns3-monolib})
   endif()
 endif()
+
+if(${NS3_FETCH_OPTIONAL_COMPONENTS})
+  add_dependency_to_optional_modules_dependencies()
+endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/examples/config-store-save.cc ns-3.38/src/config-store/examples/config-store-save.cc
--- ns-3.37/src/config-store/examples/config-store-save.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/examples/config-store-save.cc	2023-03-16 20:54:23.584868400 -0700
@@ -113,4 +113,6 @@
     Simulator::Run();
 
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/model/attribute-iterator.cc ns-3.38/src/config-store/model/attribute-iterator.cc
--- ns-3.37/src/config-store/model/attribute-iterator.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/model/attribute-iterator.cc	2023-03-16 20:54:23.584868400 -0700
@@ -73,7 +73,7 @@
     {
         oss << "/" << m_currentPath[i];
     }
-    if (attr != "")
+    if (!attr.empty())
     {
         oss << "/" << attr;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/model/config-store.cc ns-3.38/src/config-store/model/config-store.cc
--- ns-3.37/src/config-store/model/config-store.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/model/config-store.cc	2023-03-16 20:54:23.584868400 -0700
@@ -154,14 +154,14 @@
 }
 
 void
-ConfigStore::SetMode(enum Mode mode)
+ConfigStore::SetMode(Mode mode)
 {
     NS_LOG_FUNCTION(this << mode);
     m_mode = mode;
 }
 
 void
-ConfigStore::SetFileFormat(enum FileFormat format)
+ConfigStore::SetFileFormat(FileFormat format)
 {
     NS_LOG_FUNCTION(this << format);
     m_fileFormat = format;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/model/config-store.h ns-3.38/src/config-store/model/config-store.h
--- ns-3.37/src/config-store/model/config-store.h	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/model/config-store.h	2023-03-16 20:54:23.584868400 -0700
@@ -96,12 +96,12 @@
      * Set the mode of operation
      * \param mode mode of operation
      */
-    void SetMode(enum Mode mode);
+    void SetMode(Mode mode);
     /**
      * Set the file format
      * \param format the file format
      */
-    void SetFileFormat(enum FileFormat format);
+    void SetFileFormat(FileFormat format);
     /**
      * Set the filename
      * \param filename the file name
@@ -123,11 +123,11 @@
     void ConfigureAttributes();
 
   private:
-    enum Mode m_mode;             ///< store mode
-    enum FileFormat m_fileFormat; ///< store format
-    bool m_saveDeprecated;        ///< save deprecated attributes
-    std::string m_filename;       ///< store file name
-    FileConfig* m_file;           ///< configuration file
+    Mode m_mode;             ///< store mode
+    FileFormat m_fileFormat; ///< store format
+    bool m_saveDeprecated;   ///< save deprecated attributes
+    std::string m_filename;  ///< store file name
+    FileConfig* m_file;      ///< configuration file
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/model/model-node-creator.h ns-3.38/src/config-store/model/model-node-creator.h
--- ns-3.37/src/config-store/model/model-node-creator.h	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/model/model-node-creator.h	2023-03-16 20:54:23.584868400 -0700
@@ -36,9 +36,7 @@
 struct ModelNode
 {
     /**
-     * \enum node type
      * \brief node type structure
-     *
      */
     enum
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/model/model-typeid-creator.h ns-3.38/src/config-store/model/model-typeid-creator.h
--- ns-3.37/src/config-store/model/model-typeid-creator.h	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/model/model-typeid-creator.h	2023-03-16 20:54:23.584868400 -0700
@@ -38,7 +38,6 @@
 struct ModelTypeid
 {
     /**
-     * \enum node type
      * \brief Whether the node represents an attribute or TypeId
      */
     enum
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/model/xml-config.cc ns-3.38/src/config-store/model/xml-config.cc
--- ns-3.37/src/config-store/model/xml-config.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/model/xml-config.cc	2023-03-16 20:54:23.584868400 -0700
@@ -29,6 +29,7 @@
 #include "ns3/string.h"
 
 #include <libxml/encoding.h>
+#include <libxml/xmlreader.h>
 #include <libxml/xmlwriter.h>
 
 namespace ns3
@@ -46,7 +47,7 @@
 XmlConfigSave::SetFilename(std::string filename)
 {
     NS_LOG_FUNCTION(filename);
-    if (filename == "")
+    if (filename.empty())
     {
         return;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/config-store/model/xml-config.h ns-3.38/src/config-store/model/xml-config.h
--- ns-3.37/src/config-store/model/xml-config.h	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/config-store/model/xml-config.h	2023-03-16 20:54:23.584868400 -0700
@@ -22,10 +22,13 @@
 
 #include "file-config.h"
 
-#include <libxml/xmlreader.h>
-#include <libxml/xmlwriter.h>
 #include <string>
 
+// These are in #include <libxml/xmlwriter.h>,
+// here we just need a forward declaration.
+typedef struct _xmlTextWriter xmlTextWriter;
+typedef xmlTextWriter* xmlTextWriterPtr;
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/CMakeLists.txt ns-3.38/src/core/CMakeLists.txt
--- ns-3.37/src/core/CMakeLists.txt	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/CMakeLists.txt	2023-03-16 20:54:23.584868400 -0700
@@ -163,12 +163,12 @@
     model/watchdog.cc
     model/synchronizer.cc
     model/make-event.cc
+    model/environment-variable.cc
     model/log.cc
     model/breakpoint.cc
     model/type-id.cc
     model/attribute-construction-list.cc
     model/object-base.cc
-    model/ref-count-base.cc
     model/object.cc
     model/test.cc
     model/random-variable-stream.cc
@@ -209,6 +209,7 @@
     model/trickle-timer.cc
     model/realtime-simulator-impl.cc
     model/wall-clock-synchronizer.cc
+    model/matrix-array.cc
 )
 
 # Define core lib headers
@@ -246,6 +247,7 @@
     model/fatal-error.h
     model/fatal-impl.h
     model/fd-reader.h
+    model/environment-variable.h
     model/global-value.h
     model/hash-fnv.h
     model/hash-function.h
@@ -278,7 +280,6 @@
     model/priority-queue-scheduler.h
     model/ptr.h
     model/random-variable-stream.h
-    model/ref-count-base.h
     model/rng-seed-manager.h
     model/rng-stream.h
     model/scheduler.h
@@ -309,9 +310,12 @@
     model/unused.h
     model/valgrind.h
     model/vector.h
+    model/warnings.h
     model/watchdog.h
     model/realtime-simulator-impl.h
     model/wall-clock-synchronizer.h
+    model/val-array.h
+    model/matrix-array.h
 )
 
 set(test_sources
@@ -323,6 +327,7 @@
     test/callback-test-suite.cc
     test/command-line-test-suite.cc
     test/config-test-suite.cc
+    test/environment-variable-test-suite.cc
     test/event-garbage-collector-test-suite.cc
     test/global-value-test-suite.cc
     test/hash-test-suite.cc
@@ -336,6 +341,7 @@
     test/ptr-test-suite.cc
     test/sample-test-suite.cc
     test/simulator-test-suite.cc
+    test/splitstring-test-suite.cc
     test/threaded-test-suite.cc
     test/time-test-suite.cc
     test/timer-test-suite.cc
@@ -345,6 +351,8 @@
     test/type-id-test-suite.cc
     test/type-traits-test-suite.cc
     test/watchdog-test-suite.cc
+    test/val-array-test-suite.cc
+    test/matrix-array-test-suite.cc
 )
 
 # Build core lib
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/doc/core.h ns-3.38/src/core/doc/core.h
--- ns-3.37/src/core/doc/core.h	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/doc/core.h	2023-03-16 20:54:23.584868400 -0700
@@ -81,3 +81,19 @@
  * \defgroup core-tests Core module tests
  * TestSuites for the Core module
  */
+
+/**
+ * \ingroup core
+ * \defgroup system System Services
+ *
+ * System-independent interfaces to operating system services:
+ * environment variables, files system, threading, wall clock time.
+ *
+ * Services provided:
+ *
+ *   - Environment variables
+ *   - File and directory paths.
+ *   - Thread primitives:  threads, conditional waits, mutex, critical sections.
+ *   - Asynchronous input from a file descriptor.
+ *   - Wall clock time.
+ */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/doc/environment-variables.h ns-3.38/src/core/doc/environment-variables.h
--- ns-3.37/src/core/doc/environment-variables.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/doc/environment-variables.h	2023-03-16 20:54:23.584868400 -0700
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2022 Lawrence Livermore National Laboratory
+ *
+ * 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: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+// This file supplies Doxygen documentation only.
+// It should *NOT* be included in the build.
+
+/**
+ * \ingroup system
+ * \defgroup core-environ Environment Variables
+ */
+
+/**
+ * \ingroup core-environ
+ * \brief Initialize an ns3::Attribute
+ *
+ * Sets a new default \pname{value} for an Attribute.
+ * This is invoked if the ns3::Object being constructed has an Attribute
+ * matching \pname{name}, and that Attribute didn't appear in the
+ * ns3::AttributeConstructionList argument to ns3::ObjectBase::ConstructSelf.
+ * The ns3::AttributeConstructionList is typically createad by an ns3::ObjectFactory
+ * and populated by ns3::ObjectFactory::Set.
+ *
+ * All objects with an Attribute matching \pname{name} will be matched,
+ * so name collisions could wreak havoc.
+ *
+ * <dl class="params">
+ *   <dt>%Parameters</dt>
+ *     <dd>
+ *       <table class="params">
+ *         <tr>
+ *           <td class="paramname">name</td>
+ *           <td>The name of the Attribute to set.</td>
+ *         </tr>
+ *         <tr>
+ *           <td class="paramname">value</td>
+ *           <td>The value to set the Attribute to.</td>
+ *         </tr>
+ *         <tr>
+ *           <td class="paramname">;</td>
+ *           <td>Multiple \pname{name}=\pname{value} pairs should be delimited by ';'</td>
+ *         </tr>
+ *       </table>
+ *     </dd>
+ * </dl>
+ *
+ * Referenced by ns3::ObjectBase::ConstructSelf.
+ */
+const char* NS_ATTRIBUTE_DEFAULT = "name=value[;...]";
+
+/**
+ * \ingroup core-environ
+ * \brief Write the ns3::CommandLine::Usage message, in Doxygen format,
+ * to the referenced location.
+ *
+ * Set the directory where ns3::CommandLine instances should write their Usage
+ * message, used when building documentation.
+ *
+ * This is used primarily by the documentation builds, which execute each
+ * registered example to gather their ns3::CommandLine::Usage information.
+ * This wouldn't normally be useful to users.
+ *
+ * <dl class="params">
+ *   <dt>%Parameters</dt>
+ *   <dd>
+ *     <table class="params">
+ *       <tr>
+ *         <td class="paramname">path</td>
+ *         <td> The directory where ns3::CommandLine should write its Usage message.</td>
+ *       </tr>
+ *     </table>
+ *   </dd>
+ * </dl>
+ *
+ * Referenced by ns3::CommandLine::PrintDoxygenUsage.
+ */
+const char* NS_COMMANDLINE_INTROSPECTION = "path";
+
+/**
+ * \ingroup core-environ
+ * \brief Initialize a ns3::GlobalValue.
+ *
+ * Initialize the ns3::GlobalValue \pname{name} from \pname{value}.
+ *
+ * This overrides the initial value set in the corresponding
+ * ns3::GlobalValue constructor.
+ *
+ * <dl class="params">
+ *   <dt>%Parameters</dt>
+ *   <dd>
+ *     <table class="params">
+ *       <tr>
+ *         <td class="paramname">name</td>
+ *         <td>The name of the ns3::GlobalValue to set.</td>
+ *       </tr>
+ *       <tr>
+ *         <td class="paramname">value</td>
+ *         <td>The value to set the ns3::GlobalValue to.</td>
+ *       </tr>
+ *       <tr>
+ *         <td class="paramname">;</td>
+ *         <td>Multiple \pname{name}=\pname{value} pairs should be delimited by ';'</td>
+ *       </tr>
+ *     </table>
+ *   </dd>
+ * </dl>
+ *
+ * Referenced by ns3::GlobalValue::InitializeFromEnv.
+ */
+const char* NS_GLOBAL_VALUE = "name=value[;...]";
+
+/**
+ * \ingroup core-environ
+ * \brief Control which logging components are enabled.
+ *
+ * Enable logging from specific \pname{component}s, with specified \pname{option}s.
+ * See the \ref logging module, or the Logging chapter
+ * in the manual for details on what symbols can be used for specifying
+ * log level and prefix options.
+ *
+ * <dl class="params">
+ *   <dt>%Parameters</dt>
+ *   <dd>
+ *     <table class="params">
+ *       <tr>
+ *         <td class="paramname">component</td>
+ *         <td>The logging component to enable.</td>
+ *       </tr>
+ *       <tr>
+ *         <td class="paramname">option</td>
+ *         <td>Log level and or prefix to enable. Multiple options should be delimited with '|'</td>
+ *       </tr>
+ *       <tr>
+ *         <td class="paramname">:</td>
+ *         <td>Multiple logging components can be enabled simultaneously, delimited by ':'</td>
+ *       </tr>
+ *     </table>
+ *   </dd>
+ * </dl>
+ *
+ * Referenced by ns3::PrintList, ns3::LogComponent::EnvVarCheck and
+ * ns3::CheckEnvironmentVariables(), all in \ref log.cc.
+ */
+const char* NS_LOG = "component=option[|option...][:...]";
+
+/**
+ * \ingroup core-environ
+ * \brief Where to make temporary directories.
+ *
+ * The absolute path where ns-3 should make temporary directories.
+ *
+ * See ns3::SystemPath::MakeTemporaryDirectoryName for details on how this environment
+ * variable is used to create a temporary directory, and how that directory
+ * will be named.
+ *
+ * <dl class="params">
+ *   <dt>%Parameters</dt>
+ *   <dd>
+ *     <table class="params">
+ *       <tr>
+ *         <td class="paramname">path</td>
+ *         <td>The absolute path in which to create the temporary directory.</td>
+ *       </tr>
+ *     </table>
+ *   </dd>
+ * </dl>
+ *
+ * Referenced by ns3::SystemPath::MakeTemporaryDirectoryName.
+ *
+ * @{
+ */
+const char* TMP = "path";
+const char* TEMP = "path";
+/**@}*/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/assert-example.cc ns-3.38/src/core/examples/assert-example.cc
--- ns-3.37/src/core/examples/assert-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/examples/assert-example.cc	2023-03-16 20:54:23.584868400 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023 Lawrence Livermore National Laboratory
+ *
+ * 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: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+/**
+ * \file
+ * \ingroup core-examples
+ * \ingroup assert
+ * Example program illustrating the use of NS_ASSERT_MSG()
+ */
+
+#include "ns3/core-module.h"
+
+#include <iostream>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("AssertExample");
+
+int
+main(int argc, char** argv)
+{
+    CommandLine cmd;
+    cmd.Parse(argc, argv);
+
+    std::cout << "NS_ASSERT_MSG example\n"
+              << "  if an argument is given this example will assert.\n";
+
+    NS_ASSERT_MSG(argc == 1, "An argument was given, so we assert");
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/CMakeLists.txt ns-3.38/src/core/examples/CMakeLists.txt
--- ns-3.37/src/core/examples/CMakeLists.txt	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/CMakeLists.txt	2023-03-16 20:54:23.584868400 -0700
@@ -1,4 +1,5 @@
 set(base_examples
+    assert-example
     command-line-example
     fatal-example
     hash-example
@@ -55,3 +56,10 @@
   LIBRARIES_TO_LINK ${libcore}
                     ${libflow-monitor}
 )
+
+build_lib_example(
+  NAME log-example
+  SOURCE_FILES log-example.cc
+  LIBRARIES_TO_LINK ${libcore}
+                    ${libnetwork}
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/command-line-example.cc ns-3.38/src/core/examples/command-line-example.cc
--- ns-3.37/src/core/examples/command-line-example.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/command-line-example.cc	2023-03-16 20:54:23.584868400 -0700
@@ -49,7 +49,7 @@
  * \returns \c true.
  */
 bool
-SetCbArg(std::string val)
+SetCbArg(const std::string& val)
 {
     g_cbArg = val;
     return true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/empirical-random-variable-example.cc ns-3.38/src/core/examples/empirical-random-variable-example.cc
--- ns-3.37/src/core/examples/empirical-random-variable-example.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/empirical-random-variable-example.cc	2023-03-16 20:54:23.584868400 -0700
@@ -82,13 +82,16 @@
  * \param value The value to print.
  * \param count The number of times that value has been sampled.
  * \param n The total number of random values sampled.
+ * \param sum The sum of the counts seen up to \p value, used to show
+ *            the CDF for \p value.
  */
 void
-PrintStatsLine(const double value, const long count, const long n)
+PrintStatsLine(const double value, const long count, const long n, const long sum)
 {
     std::cout << std::fixed << std::setprecision(3) << std::setw(10) << std::right << value
               << std::setw(10) << std::right << count << std::setw(10) << std::right
-              << count / static_cast<double>(n) * 100.0 << std::endl;
+              << count / static_cast<double>(n) * 100.0 << std::setw(10) << std::right
+              << sum / static_cast<double>(n) * 100.0 << std::endl;
 }
 
 /**
@@ -136,21 +139,21 @@
     long sum = 0;
     double weighted = 0;
     std::cout << std::endl;
-    std::cout << "     Value    Counts         %" << std::endl;
-    std::cout << "----------  --------  --------" << std::endl;
+    std::cout << "     Value    Counts         %     % CDF" << std::endl;
+    std::cout << "----------  --------  --------  --------" << std::endl;
     for (auto c : counts)
     {
         long count = c.second;
         double value = c.first;
         sum += count;
         weighted += value * count;
-        PrintStatsLine(value, count, n);
+        PrintStatsLine(value, count, n, sum);
     }
-    PrintSummary(sum, n, weighted, 8.75);
+    PrintSummary(sum, n, weighted, 0.8);
 
     std::cout << "Interpolating " << mode << std::endl;
     erv->SetInterpolate(true);
-    Histogram h(0.5);
+    Histogram h(0.1);
     for (long i = 0; i < n; ++i)
     {
         h.AddValue(erv->GetValue());
@@ -161,8 +164,8 @@
     sum = 0;
     weighted = 0;
     std::cout << std::endl;
-    std::cout << " Bin Start    Counts         %" << std::endl;
-    std::cout << "----------  --------  --------" << std::endl;
+    std::cout << " Bin Start    Counts         %     % CDF" << std::endl;
+    std::cout << "----------  --------  --------  --------" << std::endl;
     for (uint32_t i = 0; i < h.GetNBins(); ++i)
     {
         long count = h.GetBinCount(i);
@@ -170,9 +173,9 @@
         double value = start + h.GetBinWidth(i) / 2.;
         sum += count;
         weighted += count * value;
-        PrintStatsLine(start, count, n);
+        PrintStatsLine(start, count, n, sum);
     }
-    PrintSummary(sum, n, weighted, 6.25);
+    PrintSummary(sum, n, weighted, 0.760);
 }
 
 int
@@ -203,10 +206,15 @@
 
     // Create the ERV in sampling mode
     Ptr<EmpiricalRandomVariable> erv = CreateObject<EmpiricalRandomVariable>();
-    erv->SetInterpolate(false);
-    erv->CDF(0.0, 0.0);
-    erv->CDF(5.0, 0.25);
-    erv->CDF(10.0, 1.0);
+
+    //                          // Expectation for bin
+    erv->CDF(0.0, 0.0 / 15.0);  // 0
+    erv->CDF(0.2, 1.0 / 15.0);  // 0.2 1/15  =  2/150
+    erv->CDF(0.4, 3.0 / 15.0);  // 0.4 2/15  =  8/150
+    erv->CDF(0.6, 4.0 / 15.0);  // 0.6 1/15  =  6/150
+    erv->CDF(0.8, 7.0 / 15.0);  // 0.8 3/15  = 24/150
+    erv->CDF(1.0, 9.0 / 15.0);  // 1.0 2/15  = 20/150
+    erv->CDF(1.0, 15.0 / 15.0); // 1.0 6/15  = 60/150  <avg> = 120/150 = 0.8
 
     if (single)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/hash-example.cc ns-3.38/src/core/examples/hash-example.cc
--- ns-3.37/src/core/examples/hash-example.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/hash-example.cc	2023-03-16 20:54:23.584868400 -0700
@@ -137,7 +137,7 @@
      * \param [in] hash Hash function.
      * \param [in] bits Which hash length to use.
      */
-    Collider(const std::string name, Hasher hash, const enum Bits bits)
+    Collider(const std::string name, Hasher hash, const Bits bits)
         : m_name(name),
           m_hash(hash),
           m_bits(bits)
@@ -245,7 +245,7 @@
     }
 
     /** Hash function. */
-    enum Bits m_bits;
+    Bits m_bits;
 
     /** Hashed dictionary of first instance of each hash. */
     typedef std::map<uint64_t, std::string> hashdict_t;
@@ -291,7 +291,7 @@
      */
     void Add(const std::string phrase)
     {
-        if (phrase.size() == 0)
+        if (phrase.empty())
         {
             return;
         }
@@ -437,7 +437,7 @@
      * \param [in] file The word file to add.
      * \return \c true If the file is new to the list.
      */
-    bool Add(const std::string file)
+    bool Add(const std::string& file)
     {
         if (std::find(m_files.begin(), m_files.end(), file) == m_files.end())
         {
@@ -460,7 +460,7 @@
      */
     void ReadInto(Dictionary& dict)
     {
-        if (m_files.size() == 0)
+        if (m_files.empty())
         {
             Add(GetDefault());
         }
@@ -545,4 +545,5 @@
         dict.Time();
     } // if (timing)
 
+    return 0;
 } // main
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/length-example.cc ns-3.38/src/core/examples/length-example.cc
--- ns-3.37/src/core/examples/length-example.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/length-example.cc	2023-03-16 20:54:23.584868400 -0700
@@ -23,17 +23,21 @@
 #include <iostream>
 
 /**
- * \file
+ * \defgroup length-examples Demonstrates usage of the ns3::Length class
  * \ingroup core-examples
  * \ingroup length
+ */
+
+/**
+ * \file
+ * \ingroup length-examples
  * Demonstrates usage of the ns3::Length class
  */
 
 using namespace ns3;
 
 /**
- * \ingroup core-examples
- * \ingroup length
+ * \ingroup length-examples
  * \brief Demonstrates the use of ns3::Length constructors.
  */
 void
@@ -55,8 +59,7 @@
 }
 
 /**
- * \ingroup core-examples
- * \ingroup length
+ * \ingroup length-examples
  * \brief Demonstrates the use of ns3::Length conversions.
  */
 void
@@ -75,8 +78,7 @@
 }
 
 /**
- * \ingroup core-examples
- * \ingroup length
+ * \ingroup length-examples
  * \brief Demonstrates the use of ns3::Length arithmetic operators.
  */
 void
@@ -103,8 +105,7 @@
 }
 
 /**
- * \ingroup core-examples
- * \ingroup length
+ * \ingroup length-examples
  * \brief Demonstrates the use of ns3::Length equality operators.
  */
 void
@@ -141,8 +142,7 @@
 }
 
 /**
- * \ingroup core-examples
- * \ingroup length
+ * \ingroup length-examples
  * \brief Demonstrates the use of ns3::Length multiplications and divisions.
  */
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/log-example.cc ns-3.38/src/core/examples/log-example.cc
--- ns-3.37/src/core/examples/log-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/examples/log-example.cc	2023-03-16 20:54:23.588868400 -0700
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2023 Lawrence Livermore National Laboratory
+ *
+ * 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: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+/**
+ * \file
+ * \ingroup core-examples
+ * \ingroup logging
+ * Example program illustrating the various logging functions.
+ */
+
+/** File-local context string */
+#define NS_LOG_APPEND_CONTEXT                                                                      \
+    {                                                                                              \
+        std::clog << "(local context) ";                                                           \
+    }
+
+#include "ns3/core-module.h"
+#include "ns3/network-module.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("LogExample");
+
+/**
+ * Unnamed namespace for log-example.cc
+ */
+namespace
+{
+
+/** A free function with logging. */
+void
+FreeEvent()
+{
+    NS_LOG_FUNCTION_NOARGS();
+
+    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");
+}
+
+/** Simple object to aggregate to a node.
+ * This helps demonstrate the logging node prefix.
+ */
+
+class MyEventObject : public Object
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId()
+    {
+        static TypeId tid =
+            TypeId("MyEventObject").SetParent<Object>().AddConstructor<MyEventObject>();
+        return tid;
+    }
+
+    /** Constructor. */
+    MyEventObject()
+    {
+        NS_LOG_FUNCTION(this);
+    }
+
+    /** Destructor. */
+    ~MyEventObject() override
+    {
+        NS_LOG_FUNCTION(this);
+    }
+
+    /** Class member function with logging. */
+    void Event()
+    {
+        NS_LOG_FUNCTION(this);
+
+        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");
+    }
+
+}; // MyEventObject
+
+NS_OBJECT_ENSURE_REGISTERED(MyEventObject);
+
+} // Unnamed namespace
+
+int
+main(int argc, char** argv)
+{
+    CommandLine cmd;
+    cmd.Parse(argc, argv);
+
+    NS_LOG_UNCOND("Creating a Node");
+    auto node = CreateObject<Node>();
+
+    NS_LOG_UNCOND("Creating MyEventObject");
+    auto myObj = CreateObject<MyEventObject>();
+
+    NS_LOG_UNCOND("Aggregating MyEventObject to Node");
+    node->AggregateObject(myObj);
+
+    NS_LOG_UNCOND("Scheduling the MyEventObject::Event with node context");
+    Simulator::ScheduleWithContext(node->GetId(), Seconds(3), &MyEventObject::Event, &(*myObj));
+
+    NS_LOG_UNCOND("Scheduling FreeEvent");
+    Simulator::Schedule(Seconds(5), FreeEvent);
+
+    NS_LOG_UNCOND("Starting run...");
+    Simulator::Run();
+    NS_LOG_UNCOND("... run complete");
+    Simulator::Destroy();
+    NS_LOG_UNCOND("Goodbye");
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/main-random-variable-stream.cc ns-3.38/src/core/examples/main-random-variable-stream.cc
--- ns-3.37/src/core/examples/main-random-variable-stream.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/main-random-variable-stream.cc	2023-03-16 20:54:23.588868400 -0700
@@ -25,6 +25,8 @@
 #include "ns3/string.h"
 
 #include <cmath>
+#include <fstream>
+#include <iostream>
 #include <map>
 
 /**
@@ -116,15 +118,17 @@
     unsigned int probes = 1000000;
     double precision = 0.01;
 
-    GnuplotCollection gnuplots("main-random-variables.pdf");
+    const std::string plotFile{"main-random-variables"};
+    GnuplotCollection gnuplots(plotFile + ".pdf");
     gnuplots.SetTerminal("pdf enhanced");
 
     {
+        std::cout << "UniformRandomVariable......." << std::flush;
         Gnuplot plot;
         plot.SetTitle("UniformRandomVariable");
         plot.AppendExtra("set yrange [0:]");
 
-        Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
+        auto x = CreateObject<UniformRandomVariable>();
         x->SetAttribute("Min", DoubleValue(0.0));
         x->SetAttribute("Max", DoubleValue(1.0));
 
@@ -132,29 +136,31 @@
         plot.AddDataset(Gnuplot2dFunction("1.0", "0 <= x && x <= 1 ? 1.0 : 0"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
     {
+        std::cout << "ExponentialRandomVariable..." << std::flush;
         Gnuplot plot;
         plot.SetTitle("ExponentialRandomVariable");
-        plot.AppendExtra("set xrange [0:8]");
+        plot.AppendExtra("set xrange [0:4]");
         plot.AppendExtra("ExpDist(x,l) = 1/l * exp(-1/l * x)");
 
-        Ptr<ExponentialRandomVariable> x1 = CreateObject<ExponentialRandomVariable>();
+        auto x1 = CreateObject<ExponentialRandomVariable>();
         x1->SetAttribute("Mean", DoubleValue(0.5));
 
         plot.AddDataset(Histogram(x1, probes, precision, "ExponentialRandomVariable m=0.5"));
 
         plot.AddDataset(Gnuplot2dFunction("ExponentialDistribution mean 0.5", "ExpDist(x, 0.5)"));
 
-        Ptr<ExponentialRandomVariable> x2 = CreateObject<ExponentialRandomVariable>();
+        auto x2 = CreateObject<ExponentialRandomVariable>();
         x2->SetAttribute("Mean", DoubleValue(1.0));
 
         plot.AddDataset(Histogram(x2, probes, precision, "ExponentialRandomVariable m=1"));
 
         plot.AddDataset(Gnuplot2dFunction("ExponentialDistribution mean 1.0", "ExpDist(x, 1.0)"));
 
-        Ptr<ExponentialRandomVariable> x3 = CreateObject<ExponentialRandomVariable>();
+        auto x3 = CreateObject<ExponentialRandomVariable>();
         x3->SetAttribute("Mean", DoubleValue(1.5));
 
         plot.AddDataset(Histogram(x3, probes, precision, "ExponentialRandomVariable m=1.5"));
@@ -162,28 +168,30 @@
         plot.AddDataset(Gnuplot2dFunction("ExponentialDistribution mean 1.5", "ExpDist(x, 1.5)"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
     {
+        std::cout << "ParetoRandomVariable........" << std::flush;
         Gnuplot plot;
         plot.SetTitle("ParetoRandomVariable");
-        plot.AppendExtra("set xrange [0:2]");
+        plot.AppendExtra("set xrange [0:4]");
 
-        Ptr<ParetoRandomVariable> x1 = CreateObject<ParetoRandomVariable>();
+        auto x1 = CreateObject<ParetoRandomVariable>();
         x1->SetAttribute("Scale", DoubleValue(1.0));
         x1->SetAttribute("Shape", DoubleValue(1.5));
 
         plot.AddDataset(
             Histogram(x1, probes, precision, "ParetoRandomVariable scale=1.0 shape=1.5"));
 
-        Ptr<ParetoRandomVariable> x2 = CreateObject<ParetoRandomVariable>();
+        auto x2 = CreateObject<ParetoRandomVariable>();
         x2->SetAttribute("Scale", DoubleValue(1.0));
         x2->SetAttribute("Shape", DoubleValue(2.0));
 
         plot.AddDataset(
             Histogram(x2, probes, precision, "ParetoRandomVariable scale=1.0 shape=2.0"));
 
-        Ptr<ParetoRandomVariable> x3 = CreateObject<ParetoRandomVariable>();
+        auto x3 = CreateObject<ParetoRandomVariable>();
         x3->SetAttribute("Scale", DoubleValue(1.0));
         x3->SetAttribute("Shape", DoubleValue(2.5));
 
@@ -191,28 +199,30 @@
             Histogram(x3, probes, precision, "ParetoRandomVariable scale=1.0 shape=2.5"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
     {
+        std::cout << "WeibullRandomVariable......." << std::flush;
         Gnuplot plot;
         plot.SetTitle("WeibullRandomVariable");
-        plot.AppendExtra("set xrange [0:3]");
+        plot.AppendExtra("set xrange [0:4]");
 
-        Ptr<WeibullRandomVariable> x1 = CreateObject<WeibullRandomVariable>();
+        auto x1 = CreateObject<WeibullRandomVariable>();
         x1->SetAttribute("Scale", DoubleValue(1.0));
         x1->SetAttribute("Shape", DoubleValue(1.0));
 
         plot.AddDataset(
             Histogram(x1, probes, precision, "WeibullRandomVariable scale=1.0 shape=1.0"));
 
-        Ptr<WeibullRandomVariable> x2 = CreateObject<WeibullRandomVariable>();
+        auto x2 = CreateObject<WeibullRandomVariable>();
         x2->SetAttribute("Scale", DoubleValue(1.0));
         x2->SetAttribute("Shape", DoubleValue(2.0));
 
         plot.AddDataset(
             Histogram(x2, probes, precision, "WeibullRandomVariable scale=1.0 shape=2.0"));
 
-        Ptr<WeibullRandomVariable> x3 = CreateObject<WeibullRandomVariable>();
+        auto x3 = CreateObject<WeibullRandomVariable>();
         x3->SetAttribute("Scale", DoubleValue(1.0));
         x3->SetAttribute("Shape", DoubleValue(3.0));
 
@@ -220,16 +230,18 @@
             Histogram(x3, probes, precision, "WeibullRandomVariable scale=1.0 shape=3.0"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
     {
+        std::cout << "NormalRandomVariable........" << std::flush;
         Gnuplot plot;
         plot.SetTitle("NormalRandomVariable");
-        plot.AppendExtra("set xrange [-3:3]");
+        plot.AppendExtra("set xrange [-4:4]");
         plot.AppendExtra(
             "NormalDist(x,m,s) = 1 / (s * sqrt(2*pi)) * exp(-1.0 / 2.0 * ((x-m) / s)**2)");
 
-        Ptr<NormalRandomVariable> x1 = CreateObject<NormalRandomVariable>();
+        auto x1 = CreateObject<NormalRandomVariable>();
         x1->SetAttribute("Mean", DoubleValue(0.0));
         x1->SetAttribute("Variance", DoubleValue(1.0));
 
@@ -238,7 +250,7 @@
         plot.AddDataset(Gnuplot2dFunction("NormalDist {/Symbol m}=0.0 {/Symbol s}=1.0",
                                           "NormalDist(x,0.0,1.0)"));
 
-        Ptr<NormalRandomVariable> x2 = CreateObject<NormalRandomVariable>();
+        auto x2 = CreateObject<NormalRandomVariable>();
         x2->SetAttribute("Mean", DoubleValue(0.0));
         x2->SetAttribute("Variance", DoubleValue(2.0));
 
@@ -247,7 +259,7 @@
         plot.AddDataset(Gnuplot2dFunction("NormalDist {/Symbol m}=0.0 {/Symbol s}=sqrt(2.0)",
                                           "NormalDist(x,0.0,sqrt(2.0))"));
 
-        Ptr<NormalRandomVariable> x3 = CreateObject<NormalRandomVariable>();
+        auto x3 = CreateObject<NormalRandomVariable>();
         x3->SetAttribute("Mean", DoubleValue(0.0));
         x3->SetAttribute("Variance", DoubleValue(3.0));
 
@@ -257,55 +269,61 @@
                                           "NormalDist(x,0.0,sqrt(3.0))"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
-    /** \todo Turn this plot back on once its distribution has been finished. */
-    /*
     {
-      Gnuplot plot;
-      plot.SetTitle ("EmpiricalRandomVariable");
-      plot.AppendExtra ("set xrange [*:*]");
-
-      EmpiricalRandomVariable emp1;
-      emp1.CDF (0.0,  0.0 / 15.0);
-      emp1.CDF (0.2,  1.0 / 15.0);
-      emp1.CDF (0.4,  3.0 / 15.0);
-      emp1.CDF (0.6,  6.0 / 15.0);
-      emp1.CDF (0.8, 10.0 / 15.0);
-      emp1.CDF (1.0, 15.0 / 15.0);
+        std::cout << "EmpiricalVariable..........." << std::flush;
+        Gnuplot plot;
+        plot.SetTitle("EmpiricalRandomVariable");
+        plot.AppendExtra("set xrange [*:*]");
 
-      plot.AddDataset ( Histogram (emp1, probes, precision,
-                                    "EmpiricalRandomVariable (Stairs)") );
+        auto x = CreateObject<EmpiricalRandomVariable>();
+        x->CDF(0.0, 0.0 / 15.0);
+        x->CDF(0.2, 1.0 / 15.0);
+        x->CDF(0.4, 3.0 / 15.0);
+        x->CDF(0.6, 6.0 / 15.0);
+        x->CDF(0.8, 10.0 / 15.0);
+        x->CDF(1.0, 15.0 / 15.0);
+
+        plot.AddDataset(
+            Histogram(x, probes, precision, "EmpiricalRandomVariable (Sampling)", true));
+
+        x->SetInterpolate(true);
+        plot.AppendExtra("set y2range [0:*]");
+        auto d2 = Histogram(x, probes, precision, "EmpiricalRandomVariable (Interpolate)");
+        d2.SetExtra(" axis x1y2");
+        plot.AddDataset(d2);
 
-      gnuplots.AddPlot (plot);
+        gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
-    */
 
-    /** \todo Turn this plot back on once its distribution has been finished. */
-    /*
     {
-      Gnuplot plot;
-      plot.SetTitle ("DeterministicRandomVariable");
-      plot.AppendExtra ("set xrange [*:*]");
+        std::cout << "DeterministicVariable......." << std::flush;
+        Gnuplot plot;
+        plot.SetTitle("DeterministicRandomVariable");
+        plot.AppendExtra("set xrange [*:*]");
 
-      double values[] = { 0.0, 0.2, 0.2, 0.4, 0.2, 0.6, 0.8, 0.8, 1.0 };
-      DeterministicRandomVariable det1 (values, sizeof(values) / sizeof(values[0]));
+        auto x1 = CreateObject<DeterministicRandomVariable>();
+        double values[] = {0.0, 0.2, 0.2, 0.4, 0.2, 0.6, 0.8, 0.8, 1.0};
+        x1->SetValueArray(values, sizeof(values) / sizeof(values[0]));
 
-      plot.AddDataset ( Histogram (det1, probes, precision,
-                                    "DeterministicRandomVariable", true) );
+        plot.AddDataset(Histogram(x1, probes, precision, "DeterministicRandomVariable", true));
 
-      gnuplots.AddPlot (plot);
+        gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
-    */
 
     {
+        std::cout << "LogNormalRandomVariable....." << std::flush;
         Gnuplot plot;
         plot.SetTitle("LogNormalRandomVariable");
-        plot.AppendExtra("set xrange [0:3]");
+        plot.AppendExtra("set xrange [0:4]");
 
         plot.AppendExtra("LogNormalDist(x,m,s) = 1.0/x * NormalDist(log(x), m, s)");
 
-        Ptr<LogNormalRandomVariable> x1 = CreateObject<LogNormalRandomVariable>();
+        auto x1 = CreateObject<LogNormalRandomVariable>();
         x1->SetAttribute("Mu", DoubleValue(0.0));
         x1->SetAttribute("Sigma", DoubleValue(1.0));
 
@@ -314,13 +332,13 @@
         plot.AddDataset(
             Gnuplot2dFunction("LogNormalDist(x, 0.0, 1.0)", "LogNormalDist(x, 0.0, 1.0)"));
 
-        Ptr<LogNormalRandomVariable> x2 = CreateObject<LogNormalRandomVariable>();
+        auto x2 = CreateObject<LogNormalRandomVariable>();
         x2->SetAttribute("Mu", DoubleValue(0.0));
         x2->SetAttribute("Sigma", DoubleValue(0.5));
 
         plot.AddDataset(Histogram(x2, probes, precision, "LogNormalRandomVariable m=0.0 s=0.5"));
 
-        Ptr<LogNormalRandomVariable> x3 = CreateObject<LogNormalRandomVariable>();
+        auto x3 = CreateObject<LogNormalRandomVariable>();
         x3->SetAttribute("Mu", DoubleValue(0.0));
         x3->SetAttribute("Sigma", DoubleValue(0.25));
 
@@ -329,13 +347,13 @@
         plot.AddDataset(
             Gnuplot2dFunction("LogNormalDist(x, 0.0, 0.25)", "LogNormalDist(x, 0.0, 0.25)"));
 
-        Ptr<LogNormalRandomVariable> x4 = CreateObject<LogNormalRandomVariable>();
+        auto x4 = CreateObject<LogNormalRandomVariable>();
         x4->SetAttribute("Mu", DoubleValue(0.0));
         x4->SetAttribute("Sigma", DoubleValue(0.125));
 
         plot.AddDataset(Histogram(x4, probes, precision, "LogNormalRandomVariable m=0.0 s=0.125"));
 
-        Ptr<LogNormalRandomVariable> x5 = CreateObject<LogNormalRandomVariable>();
+        auto x5 = CreateObject<LogNormalRandomVariable>();
         x5->SetAttribute("Mu", DoubleValue(0.0));
         x5->SetAttribute("Sigma", DoubleValue(2.0));
 
@@ -344,21 +362,23 @@
         plot.AddDataset(
             Gnuplot2dFunction("LogNormalDist(x, 0.0, 2.0)", "LogNormalDist(x, 0.0, 2.0)"));
 
-        Ptr<LogNormalRandomVariable> x6 = CreateObject<LogNormalRandomVariable>();
+        auto x6 = CreateObject<LogNormalRandomVariable>();
         x6->SetAttribute("Mu", DoubleValue(0.0));
         x6->SetAttribute("Sigma", DoubleValue(2.5));
 
         plot.AddDataset(Histogram(x6, probes, precision, "LogNormalRandomVariable m=0.0 s=2.5"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
     {
+        std::cout << "TriangularRandomVariable...." << std::flush;
         Gnuplot plot;
         plot.SetTitle("TriangularRandomVariable");
         plot.AppendExtra("set xrange [*:*]");
 
-        Ptr<TriangularRandomVariable> x1 = CreateObject<TriangularRandomVariable>();
+        auto x1 = CreateObject<TriangularRandomVariable>();
         x1->SetAttribute("Min", DoubleValue(0.0));
         x1->SetAttribute("Max", DoubleValue(1.0));
         x1->SetAttribute("Mean", DoubleValue(0.5));
@@ -366,7 +386,7 @@
         plot.AddDataset(
             Histogram(x1, probes, precision, "TriangularRandomVariable [0.0 .. 1.0) m=0.5"));
 
-        Ptr<TriangularRandomVariable> x2 = CreateObject<TriangularRandomVariable>();
+        auto x2 = CreateObject<TriangularRandomVariable>();
         x2->SetAttribute("Min", DoubleValue(0.0));
         x2->SetAttribute("Max", DoubleValue(1.0));
         x2->SetAttribute("Mean", DoubleValue(0.4));
@@ -374,7 +394,7 @@
         plot.AddDataset(
             Histogram(x2, probes, precision, "TriangularRandomVariable [0.0 .. 1.0) m=0.4"));
 
-        Ptr<TriangularRandomVariable> x3 = CreateObject<TriangularRandomVariable>();
+        auto x3 = CreateObject<TriangularRandomVariable>();
         x3->SetAttribute("Min", DoubleValue(0.0));
         x3->SetAttribute("Max", DoubleValue(1.0));
         x3->SetAttribute("Mean", DoubleValue(0.65));
@@ -383,9 +403,11 @@
             Histogram(x3, probes, precision, "TriangularRandomVariable [0.0 .. 1.0) m=0.65"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
     {
+        std::cout << "GammaRandomVariable........." << std::flush;
         Gnuplot plot;
         plot.SetTitle("GammaRandomVariable");
         plot.AppendExtra("set xrange [0:10]");
@@ -396,7 +418,7 @@
             "set label 1 '{/Symbol g}(x,{/Symbol a},{/Symbol b}) = x^{/Symbol a-1} e^{-x {/Symbol "
             "b}^{-1}} ( {/Symbol b}^{/Symbol a} {/Symbol G}({/Symbol a}) )^{-1}' at 0.7, 0.9");
 
-        Ptr<GammaRandomVariable> x1 = CreateObject<GammaRandomVariable>();
+        auto x1 = CreateObject<GammaRandomVariable>();
         x1->SetAttribute("Alpha", DoubleValue(1.0));
         x1->SetAttribute("Beta", DoubleValue(1.0));
 
@@ -404,7 +426,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 1.0, 1.0)", "GammaDist(x, 1.0, 1.0)"));
 
-        Ptr<GammaRandomVariable> x2 = CreateObject<GammaRandomVariable>();
+        auto x2 = CreateObject<GammaRandomVariable>();
         x2->SetAttribute("Alpha", DoubleValue(1.5));
         x2->SetAttribute("Beta", DoubleValue(1.0));
 
@@ -412,7 +434,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 1.5, 1.0)", "GammaDist(x, 1.5, 1.0)"));
 
-        Ptr<GammaRandomVariable> x3 = CreateObject<GammaRandomVariable>();
+        auto x3 = CreateObject<GammaRandomVariable>();
         x3->SetAttribute("Alpha", DoubleValue(2.0));
         x3->SetAttribute("Beta", DoubleValue(1.0));
 
@@ -420,7 +442,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 2.0, 1.0)", "GammaDist(x, 2.0, 1.0)"));
 
-        Ptr<GammaRandomVariable> x4 = CreateObject<GammaRandomVariable>();
+        auto x4 = CreateObject<GammaRandomVariable>();
         x4->SetAttribute("Alpha", DoubleValue(4.0));
         x4->SetAttribute("Beta", DoubleValue(1.0));
 
@@ -428,7 +450,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 4.0, 1.0)", "GammaDist(x, 4.0, 1.0)"));
 
-        Ptr<GammaRandomVariable> x5 = CreateObject<GammaRandomVariable>();
+        auto x5 = CreateObject<GammaRandomVariable>();
         x5->SetAttribute("Alpha", DoubleValue(2.0));
         x5->SetAttribute("Beta", DoubleValue(2.0));
 
@@ -436,7 +458,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 2.0, 2.0)", "GammaDist(x, 2.0, 2.0)"));
 
-        Ptr<GammaRandomVariable> x6 = CreateObject<GammaRandomVariable>();
+        auto x6 = CreateObject<GammaRandomVariable>();
         x6->SetAttribute("Alpha", DoubleValue(2.5));
         x6->SetAttribute("Beta", DoubleValue(3.0));
 
@@ -444,7 +466,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 2.5, 3.0)", "GammaDist(x, 2.5, 3.0)"));
 
-        Ptr<GammaRandomVariable> x7 = CreateObject<GammaRandomVariable>();
+        auto x7 = CreateObject<GammaRandomVariable>();
         x7->SetAttribute("Alpha", DoubleValue(2.5));
         x7->SetAttribute("Beta", DoubleValue(4.5));
 
@@ -453,9 +475,11 @@
         plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 2.5, 4.5)", "GammaDist(x, 2.5, 4.5)"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
     {
+        std::cout << "ErlangRandomVariable........" << std::flush;
         Gnuplot plot;
         plot.SetTitle("ErlangRandomVariable");
         plot.AppendExtra("set xrange [0:10]");
@@ -464,7 +488,7 @@
         plot.AppendExtra("set label 1 'Erlang(x,k,{/Symbol l}) = x^{k-1} e^{-x {/Symbol l}^{-1}} ( "
                          "{/Symbol l}^k (k-1)! )^{-1}' at 0.7, 0.9");
 
-        Ptr<ErlangRandomVariable> x1 = CreateObject<ErlangRandomVariable>();
+        auto x1 = CreateObject<ErlangRandomVariable>();
         x1->SetAttribute("K", IntegerValue(1));
         x1->SetAttribute("Lambda", DoubleValue(1.0));
 
@@ -473,7 +497,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("Erlang(x, 1, 1.0)", "ErlangDist(x, 1, 1.0)"));
 
-        Ptr<ErlangRandomVariable> x2 = CreateObject<ErlangRandomVariable>();
+        auto x2 = CreateObject<ErlangRandomVariable>();
         x2->SetAttribute("K", IntegerValue(2));
         x2->SetAttribute("Lambda", DoubleValue(1.0));
 
@@ -482,7 +506,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("Erlang(x, 2, 1.0)", "ErlangDist(x, 2, 1.0)"));
 
-        Ptr<ErlangRandomVariable> x3 = CreateObject<ErlangRandomVariable>();
+        auto x3 = CreateObject<ErlangRandomVariable>();
         x3->SetAttribute("K", IntegerValue(3));
         x3->SetAttribute("Lambda", DoubleValue(1.0));
 
@@ -491,7 +515,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("Erlang(x, 3, 1.0)", "ErlangDist(x, 3, 1.0)"));
 
-        Ptr<ErlangRandomVariable> x4 = CreateObject<ErlangRandomVariable>();
+        auto x4 = CreateObject<ErlangRandomVariable>();
         x4->SetAttribute("K", IntegerValue(5));
         x4->SetAttribute("Lambda", DoubleValue(1.0));
 
@@ -500,7 +524,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("Erlang(x, 5, 1.0)", "ErlangDist(x, 5, 1.0)"));
 
-        Ptr<ErlangRandomVariable> x5 = CreateObject<ErlangRandomVariable>();
+        auto x5 = CreateObject<ErlangRandomVariable>();
         x5->SetAttribute("K", IntegerValue(2));
         x5->SetAttribute("Lambda", DoubleValue(2.0));
 
@@ -509,7 +533,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("Erlang(x, 2, 2.0)", "ErlangDist(x, 2, 2.0)"));
 
-        Ptr<ErlangRandomVariable> x6 = CreateObject<ErlangRandomVariable>();
+        auto x6 = CreateObject<ErlangRandomVariable>();
         x6->SetAttribute("K", IntegerValue(2));
         x6->SetAttribute("Lambda", DoubleValue(3.0));
 
@@ -518,7 +542,7 @@
 
         plot.AddDataset(Gnuplot2dFunction("Erlang(x, 2, 3.0)", "ErlangDist(x, 2, 3.0)"));
 
-        Ptr<ErlangRandomVariable> x7 = CreateObject<ErlangRandomVariable>();
+        auto x7 = CreateObject<ErlangRandomVariable>();
         x7->SetAttribute("K", IntegerValue(2));
         x7->SetAttribute("Lambda", DoubleValue(5.0));
 
@@ -528,9 +552,30 @@
         plot.AddDataset(Gnuplot2dFunction("Erlang(x, 2, 5.0)", "ErlangDist(x, 2, 5.0)"));
 
         gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
     }
 
-    gnuplots.GenerateOutput(std::cout);
+    {
+        std::string gnuFile = plotFile + ".plt";
+        std::cout << "Writing Gnuplot file: " << gnuFile << "..." << std::flush;
+        std::ofstream gnuStream(gnuFile);
+        gnuplots.GenerateOutput(gnuStream);
+        gnuStream.close();
+
+        std::cout << "done\nGenerating " << plotFile << ".pdf..." << std::flush;
+        std::string shellcmd = "gnuplot " + gnuFile;
+        int returnValue = std::system(shellcmd.c_str());
+        if (returnValue)
+        {
+            std::cout << std::endl
+                      << "Error: shell command failed with " << returnValue << "; no pdf generated."
+                      << std::endl;
+        }
+        else
+        {
+            std::cout << "done" << std::endl;
+        }
+    }
 
     return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/main-test-sync.cc ns-3.38/src/core/examples/main-test-sync.cc
--- ns-3.37/src/core/examples/main-test-sync.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/main-test-sync.cc	2023-03-16 20:54:23.588868400 -0700
@@ -158,4 +158,6 @@
     {
         test();
     }
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/sample-log-time-format.cc ns-3.38/src/core/examples/sample-log-time-format.cc
--- ns-3.37/src/core/examples/sample-log-time-format.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/sample-log-time-format.cc	2023-03-16 20:54:23.588868400 -0700
@@ -126,10 +126,12 @@
     LogComponentEnable("RandomVariableStream", LOG_LEVEL_ALL);
     LogComponentEnableAll(LOG_PREFIX_TIME);
 
-    std::map<std::string, Time::Unit> resolutionMap = {{"Time::US", Time::US},
-                                                       {"Time::NS", Time::NS},
-                                                       {"Time::PS", Time::PS},
-                                                       {"Time::FS", Time::FS}};
+    std::map<std::string, Time::Unit> resolutionMap = {
+        {"Time::US", Time::US},
+        {"Time::NS", Time::NS},
+        {"Time::PS", Time::PS},
+        {"Time::FS", Time::FS},
+    };
 
     CommandLine cmd(__FILE__);
     cmd.AddValue("replaceTimePrinter", "replace time printing function", replaceTimePrinter);
@@ -162,4 +164,6 @@
 
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/sample-random-variable.cc ns-3.38/src/core/examples/sample-random-variable.cc
--- ns-3.37/src/core/examples/sample-random-variable.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/sample-random-variable.cc	2023-03-16 20:54:23.588868400 -0700
@@ -68,4 +68,6 @@
     Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable>();
 
     std::cout << uv->GetValue() << std::endl;
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/sample-random-variable-stream.cc ns-3.38/src/core/examples/sample-random-variable-stream.cc
--- ns-3.37/src/core/examples/sample-random-variable-stream.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/sample-random-variable-stream.cc	2023-03-16 20:54:23.588868400 -0700
@@ -68,4 +68,6 @@
     Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable>();
 
     std::cout << uv->GetValue() << std::endl;
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/sample-simulator.cc ns-3.38/src/core/examples/sample-simulator.cc
--- ns-3.37/src/core/examples/sample-simulator.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/sample-simulator.cc	2023-03-16 20:54:23.588868400 -0700
@@ -124,4 +124,6 @@
     Simulator::Run();
 
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/sample-simulator.py ns-3.38/src/core/examples/sample-simulator.py
--- ns-3.37/src/core/examples/sample-simulator.py	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/sample-simulator.py	2023-03-16 20:54:23.588868400 -0700
@@ -47,7 +47,7 @@
     {
     public:
       /** Start model execution by scheduling a HandleEvent. */
-      void Start (void);
+      void Start ();
 
     private:
       /**
@@ -59,7 +59,7 @@
     };
 
     void
-    MyModel::Start (void)
+    MyModel::Start ()
     {
       Simulator::Schedule (Seconds (10.0),
                            &MyModel::HandleEvent,
@@ -92,7 +92,7 @@
         return MakeEvent(&RandomFunctionCpp, model);
     }
 
-    void CancelledFunctionCpp(void) {
+    void CancelledFunctionCpp() {
         CPyCppyy::Eval("CancelledEvent()");
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/examples/test-string-value-formatting.cc ns-3.38/src/core/examples/test-string-value-formatting.cc
--- ns-3.37/src/core/examples/test-string-value-formatting.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/examples/test-string-value-formatting.cc	2023-03-16 20:54:23.588868400 -0700
@@ -18,7 +18,7 @@
 #include "ns3/core-module.h"
 
 /**
- * \defgroup string-value-formatting
+ * \defgroup string-value-formatting StringValue parsing tests
  * Check that StringValue parses complex values correctly.
  */
 
@@ -203,4 +203,6 @@
     NS_ASSERT_MSG(val.Get() == 30, "Minimum not set to 30");
     uniformStream->GetAttribute("Max", val);
     NS_ASSERT_MSG(val.Get() == 60, "Maximum not set to 60");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/helper/csv-reader.cc ns-3.38/src/core/helper/csv-reader.cc
--- ns-3.37/src/core/helper/csv-reader.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/helper/csv-reader.cc	2023-03-16 20:54:23.588868400 -0700
@@ -329,7 +329,7 @@
 
         start_col = end_col;
     }
-    m_blankRow = (m_columns.size() == 1) && (m_columns[0] == "");
+    m_blankRow = (m_columns.size() == 1) && (m_columns[0].empty());
     NS_LOG_LOGIC("blank row: " << m_blankRow);
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/assert.h ns-3.38/src/core/model/assert.h
--- ns-3.37/src/core/model/assert.h	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/model/assert.h	2023-03-16 20:54:23.588868400 -0700
@@ -68,7 +68,7 @@
     {                                                                                              \
         if (!(condition))                                                                          \
         {                                                                                          \
-            std::cerr << "assert failed. cond=\"" << #condition << "\", ";                         \
+            std::cerr << "NS_ASSERT failed, cond=\"" << #condition << "\", ";                      \
             NS_FATAL_ERROR_NO_MSG();                                                               \
         }                                                                                          \
     } while (false)
@@ -88,13 +88,17 @@
     {                                                                                              \
         if (!(condition))                                                                          \
         {                                                                                          \
-            std::cerr << "assert failed. cond=\"" << #condition << "\", ";                         \
+            std::cerr << "NS_ASSERT failed, cond=\"" << #condition << "\", ";                      \
             NS_FATAL_ERROR(message);                                                               \
         }                                                                                          \
     } while (false)
 
 #else /* NS3_ASSERT_ENABLE */
 
+// NOTE: The no-op macros are not inserted into the final code.
+// However, the use of sizeof() allows the compiler to silently check if the condition is
+// syntactically valid.
+
 #define NS_ASSERT(condition)                                                                       \
     do                                                                                             \
     {                                                                                              \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/attribute-construction-list.cc ns-3.38/src/core/model/attribute-construction-list.cc
--- ns-3.37/src/core/model/attribute-construction-list.cc	2022-11-01 17:37:24.463395000 -0700
+++ ns-3.38/src/core/model/attribute-construction-list.cc	2023-03-16 20:54:23.588868400 -0700
@@ -54,7 +54,7 @@
         }
     }
     // store the new value.
-    struct Item attr;
+    Item attr;
     attr.checker = checker;
     attr.value = value;
     attr.name = name;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/attribute-container.h ns-3.38/src/core/model/attribute-container.h
--- ns-3.37/src/core/model/attribute-container.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/attribute-container.h	2023-03-16 20:54:23.588868400 -0700
@@ -228,7 +228,7 @@
 Ptr<const AttributeChecker> MakeAttributeContainerChecker(Ptr<const AttributeChecker> itemchecker);
 
 /**
- * Make unitialized AttributeContainerChecker using explicit types.
+ * Make uninitialized AttributeContainerChecker using explicit types.
  * @tparam A AttributeValue type in container.
  * @tparam C Container type returned by Get.
  * \return AttributeContainerChecker.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/attribute-helper.h ns-3.38/src/core/model/attribute-helper.h
--- ns-3.37/src/core/model/attribute-helper.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/attribute-helper.h	2023-03-16 20:54:23.588868400 -0700
@@ -111,7 +111,12 @@
 
         std::string GetValueTypeName() const override
         {
-            return m_type;
+            if (m_type.rfind("ns3::", 0) == 0)
+            {
+                // m_type already starts with "ns3::"
+                return m_type;
+            }
+            return "ns3::" + m_type;
         }
 
         bool HasUnderlyingTypeInformation() const override
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/calendar-scheduler.h ns-3.38/src/core/model/calendar-scheduler.h
--- ns-3.37/src/core/model/calendar-scheduler.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/calendar-scheduler.h	2023-03-16 20:54:23.588868400 -0700
@@ -55,7 +55,7 @@
  * `(ts / m_width) % m_nBuckets`.  This class automatically adjusts
  * the number of buckets to keep the average occupancy around 2.
  * Buckets themselves are implemented as a `std::list<>`, and events are
- * kept sorted withing the buckets.
+ * kept sorted within the buckets.
  *
  * \par Time Complexity
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/callback.h ns-3.38/src/core/model/callback.h
--- ns-3.37/src/core/model/callback.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/callback.h	2023-03-16 20:54:23.588868400 -0700
@@ -441,6 +441,9 @@
 template <typename R, typename... UArgs>
 class Callback : public CallbackBase
 {
+    template <typename ROther, typename... UArgsOther>
+    friend class Callback;
+
   public:
     Callback()
     {
@@ -464,18 +467,17 @@
      * \param [in] bargs The values of the bound arguments
      */
     template <typename... BArgs>
-    Callback(const CallbackBase& cb, BArgs... bargs)
+    Callback(const Callback<R, BArgs..., UArgs...>& cb, BArgs... bargs)
     {
-        auto cbDerived =
-            static_cast<const CallbackImpl<R, BArgs..., UArgs...>*>(PeekPointer(cb.GetImpl()));
-
-        std::function<R(BArgs..., UArgs...)> f(cbDerived->GetFunction());
+        auto f = cb.DoPeekImpl()->GetFunction();
 
-        CallbackComponentVector components(cbDerived->GetComponents());
+        CallbackComponentVector components(cb.DoPeekImpl()->GetComponents());
         components.insert(components.end(), {std::make_shared<CallbackComponent<BArgs>>(bargs)...});
 
         m_impl = Create<CallbackImpl<R, UArgs...>>(
-            [f, bargs...](UArgs... uargs) -> R { return f(bargs..., uargs...); },
+            [f, bargs...](auto&&... uargs) -> R {
+                return f(bargs..., std::forward<decltype(uargs)>(uargs)...);
+            },
             components);
     }
 
@@ -508,7 +510,9 @@
                                             std::make_shared<CallbackComponent<BArgs>>(bargs)...});
 
         m_impl = Create<CallbackImpl<R, UArgs...>>(
-            [f, bargs...](UArgs... uargs) -> R { return f(bargs..., uargs...); },
+            [f, bargs...](auto&&... uargs) -> R {
+                return f(bargs..., std::forward<decltype(uargs)>(uargs)...);
+            },
             components);
     }
 
@@ -525,11 +529,23 @@
      * The integer sequence is 0..N-1, where N is the number of arguments left unbound.
      */
     template <std::size_t... INDEX, typename... BoundArgs>
-    auto BindImpl(std::index_sequence<INDEX...> seq, BoundArgs... bargs)
+    auto BindImpl(std::index_sequence<INDEX...> seq, BoundArgs&&... bargs)
     {
-        return Callback<R, std::tuple_element_t<sizeof...(bargs) + INDEX, std::tuple<UArgs...>>...>(
-            *this,
-            bargs...);
+        Callback<R, std::tuple_element_t<sizeof...(bargs) + INDEX, std::tuple<UArgs...>>...> cb;
+
+        const auto f = DoPeekImpl()->GetFunction();
+
+        CallbackComponentVector components(DoPeekImpl()->GetComponents());
+        components.insert(components.end(),
+                          {std::make_shared<CallbackComponent<BoundArgs>>(bargs)...});
+
+        cb.m_impl = Create<std::remove_pointer_t<decltype(cb.DoPeekImpl())>>(
+            [f, bargs...](auto&&... uargs) mutable {
+                return f(bargs..., std::forward<decltype(uargs)>(uargs)...);
+            },
+            components);
+
+        return cb;
     }
 
   public:
@@ -541,7 +557,7 @@
      * \return The bound callback
      */
     template <typename... BoundArgs>
-    auto Bind(BoundArgs... bargs)
+    auto Bind(BoundArgs&&... bargs)
     {
         static_assert(sizeof...(UArgs) > 0);
         return BindImpl(std::make_index_sequence<sizeof...(UArgs) - sizeof...(BoundArgs)>{},
@@ -749,9 +765,9 @@
  */
 template <typename R, typename... Args, typename... BArgs>
 auto
-MakeBoundCallback(R (*fnPtr)(Args...), BArgs... bargs)
+MakeBoundCallback(R (*fnPtr)(Args...), BArgs&&... bargs)
 {
-    return Callback<R, Args...>(fnPtr).Bind(bargs...);
+    return Callback<R, Args...>(fnPtr).Bind(std::forward<BArgs>(bargs)...);
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/command-line.cc ns-3.38/src/core/model/command-line.cc
--- ns-3.37/src/core/model/command-line.cc	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/command-line.cc	2023-03-16 20:54:23.588868400 -0700
@@ -21,6 +21,7 @@
 
 #include "config.h"
 #include "des-metrics.h"
+#include "environment-variable.h"
 #include "global-value.h"
 #include "log.h"
 #include "string.h"
@@ -33,7 +34,7 @@
 
 #include <algorithm> // transform
 #include <cctype>    // tolower
-#include <cstdlib>   // exit, getenv
+#include <cstdlib>   // exit
 #include <cstring>   // strlen
 #include <iomanip>   // setw, boolalpha
 #include <set>
@@ -198,7 +199,7 @@
 
     m_nonOptionCount = 0;
 
-    if (args.size() > 0)
+    if (!args.empty())
     {
         args.erase(args.begin()); // discard the program name
 
@@ -385,8 +386,9 @@
 
     // Hack to show just the declared non-options
     Items nonOptions(m_nonOptions.begin(), m_nonOptions.begin() + m_NNonOptions);
-    os << m_shortName << (m_options.size() ? " [Program Options]" : "")
-       << (nonOptions.size() ? " [Program Arguments]" : "") << " [General Arguments]" << std::endl;
+    os << m_shortName << (!m_options.empty() ? " [Program Options]" : "")
+       << (!nonOptions.empty() ? " [Program Arguments]" : "") << " [General Arguments]"
+       << std::endl;
 
     if (!m_usage.empty())
     {
@@ -461,13 +463,13 @@
 {
     NS_LOG_FUNCTION(this);
 
-    const char* envVar = std::getenv("NS_COMMANDLINE_INTROSPECTION");
-    if (envVar == nullptr || std::strlen(envVar) == 0)
+    auto [found, path] = EnvironmentVariable::Get("NS_COMMANDLINE_INTROSPECTION");
+    if (!found)
     {
         return;
     }
 
-    if (m_shortName.size() == 0)
+    if (m_shortName.empty())
     {
         NS_FATAL_ERROR("No file name on example-to-run; forgot to use CommandLine var (__FILE__)?");
         return;
@@ -476,7 +478,7 @@
     // Hack to show just the declared non-options
     Items nonOptions(m_nonOptions.begin(), m_nonOptions.begin() + m_NNonOptions);
 
-    std::string outf = SystemPath::Append(std::string(envVar), m_shortName + ".command-line");
+    std::string outf = SystemPath::Append(path, m_shortName + ".command-line");
 
     NS_LOG_INFO("Writing CommandLine doxy to " << outf);
 
@@ -484,8 +486,8 @@
 
     os << "/**\n \\file " << m_shortName << ".cc\n"
        << "<h3>Usage</h3>\n"
-       << "<code>$ ./ns3 run \"" << m_shortName << (m_options.size() ? " [Program Options]" : "")
-       << (nonOptions.size() ? " [Program Arguments]" : "") << "\"</code>\n";
+       << "<code>$ ./ns3 run \"" << m_shortName << (!m_options.empty() ? " [Program Options]" : "")
+       << (!nonOptions.empty() ? " [Program Arguments]" : "") << "\"</code>\n";
 
     if (!m_usage.empty())
     {
@@ -691,7 +693,7 @@
 
     auto errorExit = [this, name, value]() {
         std::cerr << "Invalid command-line argument: --" << name;
-        if (value != "")
+        if (!value.empty())
         {
             std::cerr << "=" << value;
         }
@@ -723,7 +725,7 @@
 bool
 CommandLine::CallbackItem::HasDefault() const
 {
-    return m_default != "";
+    return !m_default.empty();
 }
 
 std::string
@@ -759,7 +761,7 @@
 void
 CommandLine::AddValue(const std::string& name,
                       const std::string& help,
-                      ns3::Callback<bool, std::string> callback,
+                      ns3::Callback<bool, const std::string&> callback,
                       const std::string& defaultValue /* = "" */)
 
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/command-line.h ns-3.38/src/core/model/command-line.h
--- ns-3.37/src/core/model/command-line.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/command-line.h	2023-03-16 20:54:23.588868400 -0700
@@ -322,7 +322,7 @@
      */
     void AddValue(const std::string& name,
                   const std::string& help,
-                  ns3::Callback<bool, std::string> callback,
+                  ns3::Callback<bool, const std::string&> callback,
                   const std::string& defaultValue = "");
 
     /**
@@ -533,7 +533,7 @@
         bool HasDefault() const override;
         std::string GetDefault() const override;
 
-        ns3::Callback<bool, std::string> m_callback; /**< The Callback */
+        ns3::Callback<bool, const std::string&> m_callback; /**< The Callback */
         std::string m_default; /**< The default value, as a string, if it exists. */
     };                         // class CallbackItem
 
@@ -777,7 +777,7 @@
 bool
 CommandLine::UserItem<T>::HasDefault() const
 {
-    return (m_default.size() > 0);
+    return !m_default.empty();
 }
 
 template <typename T>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/config.cc ns-3.38/src/core/model/config.cc
--- ns-3.37/src/core/model/config.cc	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/config.cc	2023-03-16 20:54:23.588868400 -0700
@@ -129,7 +129,7 @@
 {
     if (!ConnectFailSafe(name, cb))
     {
-        NS_FATAL_ERROR("Cound not connect callback to " << name);
+        NS_FATAL_ERROR("Could not connect callback to " << name);
     }
 }
 
@@ -605,7 +605,7 @@
 Resolver::DoArrayResolve(std::string path, const ObjectPtrContainerValue& container)
 {
     NS_LOG_FUNCTION(this << path << &container);
-    NS_ASSERT(path != "");
+    NS_ASSERT(!path.empty());
     NS_ASSERT((path.find('/')) == 0);
     std::string::size_type next = path.find('/', 1);
     if (next == std::string::npos)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/deprecated.h ns-3.38/src/core/model/deprecated.h
--- ns-3.37/src/core/model/deprecated.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/deprecated.h	2023-03-16 20:54:51.628957000 -0700
@@ -60,6 +60,15 @@
  *     void SomethingUseful () { ... }
  *     \endcode.
  *
+ * \note Sometimes it is necessary to silence a deprecation warning.
+ * Even though this is highly discouraged, if necessary it is possible to use:
+ * \code
+ *   NS_WARNING_PUSH_DEPRECATED;
+ *   // call to a function or class that has been deprecated.
+ *   NS_WARNING_POP;
+ * \endcode
+ * These macros are defined in warnings.h
+ *
  * \param msg Optional message to add to the compiler warning.
  *
  */
@@ -67,6 +76,13 @@
 
 /**
  * \ingroup core
+ * \def NS_DEPRECATED_3_38
+ * Tag for things deprecated in version ns-3.38.
+ */
+#define NS_DEPRECATED_3_38(msg) NS_DEPRECATED(msg)
+
+/**
+ * \ingroup core
  * \def NS_DEPRECATED_3_37
  * Tag for things deprecated in version ns-3.37.
  */
@@ -79,18 +95,4 @@
  */
 #define NS_DEPRECATED_3_36(msg) NS_DEPRECATED(msg)
 
-/**
- * \ingroup core
- * \def NS_DEPRECATED_3_35
- * Tag for things deprecated in version ns-3.35.
- */
-#define NS_DEPRECATED_3_35 NS_DEPRECATED("")
-
-/**
- * \ingroup core
- * \def NS_DEPRECATED_3_34
- * Tag for things deprecated in version ns-3.34.
- */
-#define NS_DEPRECATED_3_34 NS_DEPRECATED("")
-
 #endif /* NS3_DEPRECATED_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/des-metrics.cc ns-3.38/src/core/model/des-metrics.cc
--- ns-3.37/src/core/model/des-metrics.cc	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/des-metrics.cc	2023-03-16 20:54:23.588868400 -0700
@@ -50,17 +50,17 @@
     m_initialized = true;
 
     std::string model_name("desTraceFile");
-    if (args.size() > 0)
+    if (!args.empty())
     {
         std::string arg0 = args[0];
         model_name = SystemPath::Split(arg0).back();
     }
     std::string jsonFile = model_name + ".json";
-    if (outDir != "")
+    if (!outDir.empty())
     {
         DesMetrics::m_outputDir = outDir;
     }
-    if (DesMetrics::m_outputDir != "")
+    if (!DesMetrics::m_outputDir.empty())
     {
         jsonFile = SystemPath::Append(DesMetrics::m_outputDir, jsonFile);
     }
@@ -76,7 +76,7 @@
     m_os << " \"model_name\" : \"" << model_name << "\"," << std::endl;
     m_os << " \"capture_date\" : \"" << capture_date << "\"," << std::endl;
     m_os << " \"command_line_arguments\" : \"";
-    if (args.size() == 0)
+    if (args.empty())
     {
         for (std::size_t i = 0; i < args.size(); ++i)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/environment-variable.cc ns-3.38/src/core/model/environment-variable.cc
--- ns-3.37/src/core/model/environment-variable.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/model/environment-variable.cc	2023-03-16 20:54:23.588868400 -0700
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2022 Lawrence Livermore National Laboratory
+ *
+ * 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: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+#include "environment-variable.h"
+
+#include "ns3/string.h"
+
+#include <cstdlib>  // std::getenv
+#include <cstring>  // strlen
+#include <iostream> // clog
+#include <stdlib.h> // Global functions setenv, unsetenv
+
+/**
+ * \file
+ * \ingroup core-environ
+ * Class EnvironmentVariable implementation.
+ */
+
+#ifdef __WIN32__
+#include <cerrno>
+
+/**
+ * Windows implementation of the POSIX function `setenv()`
+ *
+ * \param [in] var_name The environment variable to set.
+ *             Must not be a null-pointer, and must not contain `=`.
+ * \param [in] new_value The new value to set \p var_name to.
+ *             Must not by a null pointer or empty.
+ * \param [in] change_flag Must be non-zero to actually change the environment.
+ * \returns 0 if successful, -1 if failed.
+ */
+int
+setenv(const char* var_name, const char* new_value, int change_flag)
+{
+    std::string variable{var_name};
+    std::string value{new_value};
+
+    // In case arguments are null pointers, return invalid error
+    // Windows does not accept empty environment variables
+    if (variable.empty() || value.empty())
+    {
+        errno = EINVAL;
+        return -1;
+    }
+
+    // Posix does not accept '=', so impose that here
+    if (variable.find('=') != std::string::npos)
+    {
+        errno = EINVAL;
+        return -1;
+    }
+
+    // Change flag equals to zero preserves a pre-existing value
+    if (change_flag == 0)
+    {
+        char* old_value = std::getenv(var_name);
+        if (old_value != nullptr)
+        {
+            return 0;
+        }
+    }
+
+    // Write new value for the environment variable
+    return _putenv_s(var_name, new_value);
+}
+
+/**
+ * Windows implementation of the POSIX function `unsetenv()`
+ * \param [in] var_name The environment variable to unset and remove from the environment.
+ * \returns 0 if successful, -1 if failed.
+ */
+int
+unsetenv(const char* var_name)
+{
+    return _putenv_s(var_name, "");
+}
+
+#endif // __WIN32__
+
+namespace ns3
+{
+
+/**
+ * \ingroup core-environ
+ *
+ * \def NS_LOCAL_LOG(msg)
+ * File-local logging macro for environment-variable.cc
+ * Our usual Logging doesn't work here because these functions
+ * get called during static initialization of Logging itself.
+ * \param msg The message stream to log
+ *
+ * \def NS_LOCAL_ASSERT(cond, msg)
+ * File-local assert macro for environment-variable.cc
+ * Our usual assert doesn't work here because these functions
+ * get called during static initialization of Logging itself.
+ * \param cond The condition which is asserted to be \c true
+ * \param msg The message stream to log
+ */
+#if 0
+#define NS_LOCAL_LOG(msg)                                                                          \
+    std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): " << msg << std::endl
+
+#define NS_LOCAL_ASSERT(cond, msg)                                                                 \
+    do                                                                                             \
+    {                                                                                              \
+        if (!(cond))                                                                               \
+        {                                                                                          \
+            NS_LOCAL_LOG("assert failed. cond=\"" << #cond << "\", " << msg);                      \
+        }                                                                                          \
+    } while (false)
+
+#else
+#define NS_LOCAL_LOG(msg)
+#define NS_LOCAL_ASSERT(cond, msg)
+#endif
+
+/* static */
+EnvironmentVariable::DictionaryList&
+EnvironmentVariable::Instance()
+{
+    static DictionaryList instance;
+    return instance;
+}
+
+/* static */
+void
+EnvironmentVariable::Clear()
+{
+    Instance().clear();
+}
+
+/* static */
+std::shared_ptr<EnvironmentVariable::Dictionary>
+EnvironmentVariable::GetDictionary(const std::string& envvar, const std::string& delim /* ";" */)
+{
+    NS_LOCAL_LOG(envvar << ", " << delim);
+    std::shared_ptr<Dictionary> dict;
+    auto loc = Instance().find(envvar);
+    if (loc != Instance().end())
+    {
+        NS_LOCAL_LOG("found envvar in cache");
+        dict = loc->second;
+    }
+    else
+    {
+        NS_LOCAL_LOG("envvar not in cache, checking environment");
+        dict = std::make_shared<Dictionary>(envvar, delim);
+        Instance().insert({envvar, dict});
+    }
+
+    return dict;
+}
+
+/* static */
+EnvironmentVariable::KeyFoundType
+EnvironmentVariable::Get(const std::string& envvar,
+                         const std::string& key /* "" */,
+                         const std::string& delim /* ";" */)
+{
+    auto dict = GetDictionary(envvar, delim);
+    return dict->Get(key);
+}
+
+/* static */
+bool
+EnvironmentVariable::Set(const std::string& variable, const std::string& value)
+{
+    int fail = setenv(variable.c_str(), value.c_str(), 1);
+    return !fail;
+}
+
+/* static */
+bool
+EnvironmentVariable::Unset(const std::string& variable)
+{
+    int fail = unsetenv(variable.c_str());
+    return !fail;
+}
+
+EnvironmentVariable::KeyFoundType
+EnvironmentVariable::Dictionary::Get(const std::string& key) const
+{
+    NS_LOCAL_LOG(key);
+
+    if (!m_exists)
+    {
+        return {false, ""};
+    }
+
+    if (key.empty())
+    {
+        // Empty key is request for entire value
+        return {true, m_variable};
+    }
+
+    auto loc = m_dict.find(key);
+    if (loc != m_dict.end())
+    {
+        NS_LOCAL_LOG("found key in dictionary");
+        NS_LOCAL_LOG("found: key '" << key << "', value: '" << loc->second << "'");
+        return {true, loc->second};
+    }
+
+    // key not found
+    return {false, ""};
+}
+
+EnvironmentVariable::Dictionary::Dictionary(const std::string& envvar,
+                                            const std::string& delim /* "=" */)
+{
+    NS_LOCAL_LOG(envvar << ", " << delim);
+
+    const char* envCstr = std::getenv(envvar.c_str());
+    // Returns null pointer if envvar doesn't exist
+    if (!envCstr)
+    {
+        m_exists = false;
+        return;
+    }
+
+    // So it exists
+    m_exists = true;
+    m_variable = envCstr;
+    NS_LOCAL_LOG("found envvar in environment with value '" << m_variable << "'");
+
+    // ...but might be empty
+    if (m_variable.empty())
+    {
+        return;
+    }
+
+    StringVector keyvals = SplitString(m_variable, delim);
+    NS_LOCAL_ASSERT(keyvals.empty(), "Unexpected empty keyvals from non-empty m_variable");
+    for (const auto& keyval : keyvals)
+    {
+        if (keyval.empty())
+        {
+            continue;
+        }
+
+        std::size_t equals = keyval.find_first_of('=');
+        std::string key{keyval, 0, equals};
+        std::string value;
+        if (equals < keyval.size() - 1)
+        {
+            value = keyval.substr(equals + 1, keyval.size());
+        }
+        NS_LOCAL_LOG("found key '" << key << "' with value '" << value << "'");
+        m_dict.insert({key, value});
+    }
+}
+
+EnvironmentVariable::Dictionary::KeyValueStore
+EnvironmentVariable::Dictionary::GetStore() const
+{
+    return m_dict;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/environment-variable.h ns-3.38/src/core/model/environment-variable.h
--- ns-3.37/src/core/model/environment-variable.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/model/environment-variable.h	2023-03-16 20:54:23.588868400 -0700
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2022 Lawrence Livermore National Laboratory
+ *
+ * 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: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+#ifndef ENVIRONMENT_VARIABLE_H
+#define ENVIRONMENT_VARIABLE_H
+
+/**
+ * \file
+ * \ingroup core-environ
+ * Class Environment declaration.
+ */
+
+#include <memory> // shared_ptr
+#include <string>
+#include <unordered_map>
+#include <utility> // pair
+
+namespace ns3
+{
+
+/// \todo Reconsider the name?
+/// Rename to just EnvironmentVariableDictionary?
+/// System::EnvironmentVariable?
+
+// Forward declaration
+namespace tests
+{
+class EnvVarTestCase;
+}
+
+/**
+ * \ingroup core-environ
+ * Hold key,value dictionaries for environment variables.
+ *
+ * The environment variable can have multiple key,value pairs
+ * separated by a delimiter, which is ";" by default.
+ *
+ * Individual pairs are connected by '='.  As an extension a bare key
+ * will be assigned the empty string \c "".
+ *
+ * For example, `ENVVAR="key1=value1;key2;key3=value3"`.
+ */
+class EnvironmentVariable
+{
+  public:
+    /**
+     * Result of a key lookup.
+     * The \p first is \c true if the key was found.
+     *
+     * The \p second contains the value associated with the key.
+     */
+    using KeyFoundType = std::pair<bool, std::string>;
+
+    /**
+     * Get the value corresponding to a key from an environment variable.
+     *
+     * If the \p key is empty just return the environment variable,
+     * (or `{false, ""}` if the variable doesn't exist).
+     *
+     * If the \p key is not empty return the associated value
+     * (or `{false, ""}` if the key is not found).
+     * If the key is present but has no value return `{true, ""}`.
+     *
+     * Key-value pairs are separated by \p delim.  Individual keys and
+     * values are separated by an `=` sign. If the `=` is not present
+     * the value returned is the empty string.
+     *
+     * Notice that two cases both return `{false, ""}`:
+     * * The environment variable doesn't exist, or
+     * * It exists but the (non-empty) \p key wasn't found.
+     *
+     * Notice that two cases both return `{true, ""}`: the environment
+     * variable exists and
+     * * The \p key was empty and the environment variable was empty, or
+     * * The (not empty) key was found, but with no value.
+     *
+     * In practice neither of these ambiguities is important:
+     * * If the return contains \c false the key doesn't exist.
+     * * If the return contains \c true but the string is empty either the
+     *   (not empty) key exists with no value, or the key was empty and the
+     *   environment variable itself is empty.
+     *
+     * \param [in] envvar The environment variable.
+     * \param [in] key The key to extract from the environment variable.
+     * \param [in] delim The delimiter between key,value pairs.
+     * \returns Whether the key was found, and its value, as explained above.
+     */
+    static KeyFoundType Get(const std::string& envvar,
+                            const std::string& key = "",
+                            const std::string& delim = ";");
+
+    // Forward
+    class Dictionary;
+
+    /**
+     * Get the dictionary for a particular environment variable.
+     *
+     * This should be used when one needs to process all key,value
+     * pairs, perhaps without knowing the set of possible keys.
+     *
+     * \param [in] envvar The environment variable.
+     * \param [in] delim The delimiter between key,value pairs.
+     * \returns The Dictionary.
+     */
+    static std::shared_ptr<Dictionary> GetDictionary(const std::string& envvar,
+                                                     const std::string& delim = ";");
+
+    /** Key, value dictionary for a single environment variable. */
+    class Dictionary
+    {
+      public:
+        /**
+         * Constructor.
+         *
+         * Parse an environment variable containing keys and optional values.
+         *
+         * Keys in the environment variable are separated by the
+         * \p delim character.  Keys may be assigned values by following
+         * the key with the `=` character; any remaining text up to the next
+         * delimiter will be taken as the value.  If no `=`
+         * is given the enpty string will be stored as the value.
+         *
+         * \param [in] envvar The environment variable.
+         * \param [in] delim The delimiter between key,value pairs.
+         */
+        Dictionary(const std::string& envvar, const std::string& delim = ";");
+
+        /**
+         * Get the value corresponding to a key from this dictionary.
+         * If the \p key is empty return the full environment variable value.
+         * \param [in] key The key to extract from the environment variable.
+         * \returns \c true if the key was found, and the associated value.
+         */
+        KeyFoundType Get(const std::string& key = "") const;
+
+        /** Key, value store type. */
+        using KeyValueStore = std::unordered_map<std::string, std::string>;
+
+        /** Get the underlying store, for iterating.
+         * \returns The key, value store.
+         */
+        KeyValueStore GetStore() const;
+
+      private:
+        /** Whether the environment variable exists in the environment. */
+        bool m_exists;
+        /** The raw environment variable. */
+        std::string m_variable;
+        /** The key, value store. */
+        KeyValueStore m_dict;
+
+    }; // class Dictionary
+
+    /**
+     * Set an environment variable.
+     *
+     * To set a variable to the empty string use `Set(variable, "")`.
+     * Note: empty environment variables are not portable (unsupported on Windows).
+     *
+     * \param [in] variable The environment variable to set.  Note this may not contain the `=`
+     * character. \param [in] value The value to set.  Note this must not be an empty string on
+     * Windows. \returns \c true if the variable was set successfully
+     */
+    static bool Set(const std::string& variable, const std::string& value);
+
+    /**
+     * Unset an environment variable.
+     * This removes the variable from the environment.
+     * To set a variable to the empty string use `Set(variable, "")`.
+     *
+     * \param [in] variable The environment variable to unset.  Note this may not contain the `=`
+     * character. \returns \c true if the variable was unset successfully.
+     */
+    static bool Unset(const std::string& variable);
+
+    /**
+     * \name Singleton
+     *
+     * This class is a singleton, accessed by static member functions,
+     * so the Rule of Five functions are all deleted.
+     */
+    /** @{ */
+    EnvironmentVariable() = delete;
+    EnvironmentVariable(const EnvironmentVariable&) = delete;
+    EnvironmentVariable& operator=(const EnvironmentVariable&) = delete;
+    EnvironmentVariable(EnvironmentVariable&&) = delete;
+    EnvironmentVariable& operator=(EnvironmentVariable&&) = delete;
+    /** @} */
+
+  private:
+    /**
+     * How Dictionaries are stored.
+     *
+     * \p key: the environment variable name
+     *
+     * \p Dictionary: the parsed Dictionary for the \p key
+     */
+    using DictionaryList = std::unordered_map<std::string, std::shared_ptr<Dictionary>>;
+
+    /**
+     * Access the DictionaryStore instance.
+     * \returns the DictionaryStore.
+     */
+    static DictionaryList& Instance();
+
+    // Test needs to clear the instance
+    friend class tests::EnvVarTestCase;
+
+    /** Clear the instance, forcing all new lookups. */
+    static void Clear();
+
+}; // class EnvironmentVariable
+
+} // namespace ns3
+
+#endif /* ENVIRONMENT_VARIABLE_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/example-as-test.cc ns-3.38/src/core/model/example-as-test.cc
--- ns-3.37/src/core/model/example-as-test.cc	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/example-as-test.cc	2023-03-16 20:54:23.588868400 -0700
@@ -21,9 +21,11 @@
 
 #include "ascii-test.h"
 #include "assert.h"
+#include "environment-variable.h"
+#include "fatal-error.h"
 #include "log.h"
 
-#include <cstdlib> // itoa(), system (), getenv ()
+#include <cstdlib> // itoa(), system ()
 #include <cstring>
 #include <sstream>
 #include <string>
@@ -45,11 +47,13 @@
 ExampleAsTestCase::ExampleAsTestCase(const std::string name,
                                      const std::string program,
                                      const std::string dataDir,
-                                     const std::string args /* = "" */)
+                                     const std::string args /* = "" */,
+                                     const bool shouldNotErr /* = true */)
     : TestCase(name),
       m_program(program),
       m_dataDir(dataDir),
-      m_args(args)
+      m_args(args),
+      m_shouldNotErr(shouldNotErr)
 {
     NS_LOG_FUNCTION(this << name << program << dataDir << args);
 }
@@ -84,42 +88,59 @@
     SetDataDir(m_dataDir);
     std::string refFile = CreateDataDirFilename(GetName() + ".reflog");
     std::string testFile = CreateTempDirFilename(GetName() + ".reflog");
+    std::string post = GetPostProcessingCommand();
+
+    if (!m_shouldNotErr)
+    {
+        // Strip any system- or compiler-dependent messages
+        // resulting from invoking NS_FATAL..., which in turn
+        // calls std::terminate
+        post += " | sed '1,/" + std::string(NS_FATAL_MSG) + "/!d' ";
+    }
 
     std::stringstream ss;
 
     ss << "python3 ./ns3 run " << m_program << " --no-build --command-template=\""
-       << GetCommandTemplate()
-       << "\""
+       << GetCommandTemplate() << "\"";
 
-       // redirect std::clog, std::cerr to std::cout
-       << " 2>&1 "
-       // Suppress the waf lines from output; waf output contains directory paths which will
-       // obviously differ during a test run
-       << " " << GetPostProcessingCommand() << " > " << testFile;
+    if (post.empty())
+    {
+        // redirect to testfile, then std::clog, std::cerr to std::cout
+        ss << " > " << testFile << " 2>&1";
+    }
+    else
+    {
+        ss << " 2>&1 " << post << " > " << testFile;
+    }
 
     int status = std::system(ss.str().c_str());
 
-    std::cout << "command:  " << ss.str() << "\n"
-              << "status:   " << status << "\n"
-              << "refFile:  " << refFile << "\n"
-              << "testFile: " << testFile << "\n"
-              << std::endl;
-    std::cout << "testFile contents:" << std::endl;
+    std::cout << "\n"
+              << GetName() << ":\n"
+              << "    command:  " << ss.str() << "\n"
+              << "    status:   " << status << "\n"
+              << "    refFile:  " << refFile << "\n"
+              << "    testFile: " << testFile << "\n"
+              << "    testFile contents:" << std::endl;
 
     std::ifstream logF(testFile);
     std::string line;
     while (getline(logF, line))
     {
-        std::cout << line << "\n";
+        std::cout << "--- " << line << "\n";
     }
     logF.close();
 
-    // Make sure the example didn't outright crash
-    NS_TEST_ASSERT_MSG_EQ(status, 0, "example " + m_program + " failed");
+    if (m_shouldNotErr)
+    {
+        // Make sure the example didn't outright crash
+        NS_TEST_ASSERT_MSG_EQ(status, 0, "example " + m_program + " failed");
+    }
 
-    // Check that we're not just introspecting the command-line
-    const char* envVar = std::getenv("NS_COMMANDLINE_INTROSPECTION");
-    if (envVar != nullptr && std::strlen(envVar) != 0)
+    // If we're just introspecting the command-line
+    // we've run the example and we're done
+    auto [found, intro] = EnvironmentVariable::Get("NS_COMMANDLINE_INTROSPECTION");
+    if (found)
     {
         return;
     }
@@ -132,11 +153,12 @@
                                        const std::string program,
                                        const std::string dataDir,
                                        const std::string args /* = "" */,
-                                       const TestDuration duration /* =QUICK */)
+                                       const TestDuration duration /* =QUICK */,
+                                       const bool shouldNotErr /* = true */)
     : TestSuite(name, EXAMPLE)
 {
-    NS_LOG_FUNCTION(this << name << program << dataDir << args << duration);
-    AddTestCase(new ExampleAsTestCase(name, program, dataDir, args), duration);
+    NS_LOG_FUNCTION(this << name << program << dataDir << args << duration << shouldNotErr);
+    AddTestCase(new ExampleAsTestCase(name, program, dataDir, args, shouldNotErr), duration);
 }
 
 #endif // NS3_ENABLE_EXAMPLES
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/example-as-test.h ns-3.38/src/core/model/example-as-test.h
--- ns-3.37/src/core/model/example-as-test.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/example-as-test.h	2023-03-16 20:54:23.588868400 -0700
@@ -39,8 +39,8 @@
  * Execute an example program as a test, by comparing the output
  * to a reference file.
  *
- * User can subclass and override the GetCommandTemplate and
- * GetPostProcessingCommand methods if more complex
+ * User can subclass and override the GetCommandTemplate() and
+ * GetPostProcessingCommand() methods if more complex
  * example invocation patterns are required.
  *
  * \see examples-as-tests-test-suite.cc
@@ -64,11 +64,17 @@
      *                  `test-runner` the reference file will be created
      *                  with the correct name.
      * \param [in] args Any additional arguments to the program.
+     * \param [in] shouldNotErr Whether an error return status should be
+     *             considered a test failure. This is useful when testing
+     *             error detection which might return a non-zero status.
+     *             The output (on `std::cout` and `std::cerr`) will
+     *             be compared to the reference logs as normal.
      */
     ExampleAsTestCase(const std::string name,
                       const std::string program,
                       const std::string dataDir,
-                      const std::string args = "");
+                      const std::string args = "",
+                      const bool shouldNotErr = true);
 
     /** Destructor. */
     ~ExampleAsTestCase() override;
@@ -84,9 +90,15 @@
     /**
      * Customization point for tests requiring post-processing of stdout.
      *
-     * For example to sort return "| sort"
+     * For example to sort return `"| sort"`
      *
-     * Default is "", no processing step.
+     * One common case is to mask memory addresses, which can change
+     * when things are built on different platforms, recompiled locally,
+     * or even from run to run.  A simple post-processing filter could be
+     *
+     *    `"| sed -E 's/0x[0-9a-fA-F]{8,}/0x-address/g'"`
+     *
+     * Default is `""`, no additional processing.
      *
      * \returns The string of post-processing commands
      */
@@ -99,6 +111,7 @@
     std::string m_program; /**< The program to run. */
     std::string m_dataDir; /**< The source directory for the test. */
     std::string m_args;    /**< Any additional arguments to the program. */
+    bool m_shouldNotErr;   /**< Whether error return status is a test failure. */
 
 }; // class ExampleAsTestCase
 
@@ -135,12 +148,13 @@
  * which looks like this:
  *
  * \code{.cpp}
- * #include "ns3/example-as-test.h"
- * static ns3::ExampleAsTestSuite g_modExampleOne ("mymodule-example-mod-example-one",
- * "mod-example", NS_TEST_SOURCEDIR, "--arg-one"); static ns3::ExampleAsTestSuite g_modExampleTwo
- * ("mymodule-example-mod-example-two", "mod-example", NS_TEST_SOURCEDIR, "--arg-two"); \endcode
+ *     #include "ns3/example-as-test.h"
+ *     static ns3::ExampleAsTestSuite g_modExampleOne("mymodule-example-mod-example-one",
+ *         "mod-example", NS_TEST_SOURCEDIR, "--arg-one");
+ *     static ns3::ExampleAsTestSuite g_modExampleTwo("mymodule-example-mod-example-two",
+ *         "mod-example", NS_TEST_SOURCEDIR, "--arg-two"); \endcode
  *
- * The arguments to the constructor is the name of the test suite, the
+ * The arguments to the constructor are the name of the test suite, the
  * example to run, the directory that contains the "good" reference file
  * (the macro `NS_TEST_SOURCEDIR` is normally the correct directory),
  * and command line arguments for the example.  In the preceding code
@@ -204,7 +218,8 @@
                        const std::string program,
                        const std::string dataDir,
                        const std::string args = "",
-                       const TestDuration duration = QUICK);
+                       const TestDuration duration = QUICK,
+                       const bool shouldNotErr = true);
 }; // class ExampleAsTestSuite
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/fatal-error.h ns-3.38/src/core/model/fatal-error.h
--- ns-3.37/src/core/model/fatal-error.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/fatal-error.h	2023-03-16 20:54:23.588868400 -0700
@@ -26,6 +26,7 @@
 #include <cstdlib>
 #include <exception>
 #include <iostream>
+#include <string_view>
 
 /**
  * \file
@@ -52,6 +53,21 @@
  * on the attempt to execute the flush() function.
  */
 
+namespace ns3
+{
+
+/**
+ * \ingroup fatal
+ *
+ * \brief Output string marking imminent invocation of std::terminate.
+ *
+ * This is useful to know when capturing output and you want to strip
+ * system and compiler-dependent messages generated by std::terminate.
+ */
+constexpr std::string_view NS_FATAL_MSG{"NS_FATAL, terminating"};
+
+} // namespace ns3
+
 /**
  * \ingroup fatal
  *
@@ -77,7 +93,10 @@
         std::cerr << "file=" << __FILE__ << ", line=" << __LINE__ << std::endl;                    \
         ::ns3::FatalImpl::FlushStreams();                                                          \
         if (fatal)                                                                                 \
+        {                                                                                          \
+            std::cerr << ns3::NS_FATAL_MSG << std::endl;                                           \
             std::terminate();                                                                      \
+        }                                                                                          \
     } while (false)
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/global-value.cc ns-3.38/src/core/model/global-value.cc
--- ns-3.37/src/core/model/global-value.cc	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/global-value.cc	2023-03-16 20:54:23.588868400 -0700
@@ -19,6 +19,7 @@
 #include "global-value.h"
 
 #include "attribute.h"
+#include "environment-variable.h"
 #include "fatal-error.h"
 #include "log.h"
 #include "string.h"
@@ -26,9 +27,6 @@
 
 #include "ns3/core-config.h"
 
-#include <cstdlib> // getenv
-#include <cstring> // strlen
-
 /**
  * \file
  * \ingroup core
@@ -70,35 +68,15 @@
 {
     NS_LOG_FUNCTION(this);
 
-    const char* envVar = getenv("NS_GLOBAL_VALUE");
-    if (envVar == nullptr || std::strlen(envVar) == 0)
+    auto [found, value] = EnvironmentVariable::Get("NS_GLOBAL_VALUE", m_name);
+    if (found)
     {
-        return;
-    }
-    std::string env = envVar;
-    std::string::size_type cur = 0;
-    std::string::size_type next = 0;
-    while (next != std::string::npos)
-    {
-        next = env.find(';', cur);
-        std::string tmp = std::string(env, cur, next - cur);
-        std::string::size_type equal = tmp.find('=');
-        if (equal != std::string::npos)
+        Ptr<AttributeValue> v = m_checker->CreateValidValue(StringValue(value));
+        if (v)
         {
-            std::string name = tmp.substr(0, equal);
-            std::string value = tmp.substr(equal + 1, tmp.size() - equal - 1);
-            if (name == m_name)
-            {
-                Ptr<AttributeValue> v = m_checker->CreateValidValue(StringValue(value));
-                if (v)
-                {
-                    m_initialValue = v;
-                    m_currentValue = v;
-                }
-                return;
-            }
+            m_initialValue = v;
+            m_currentValue = v;
         }
-        cur = next + 1;
     }
 }
 
@@ -171,7 +149,7 @@
             return;
         }
     }
-    NS_FATAL_ERROR("Non-existant global value: " << name);
+    NS_FATAL_ERROR("Non-existent global value: " << name);
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/hash-fnv.h ns-3.38/src/core/model/hash-fnv.h
--- ns-3.37/src/core/model/hash-fnv.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/hash-fnv.h	2023-03-16 20:54:23.588868400 -0700
@@ -98,7 +98,7 @@
     /**
      * Seed value
      */
-    enum seed
+    enum Seed
     {
         SEED = 0x8BADF00D /**< Ate bad food */
     };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/hash-murmur3.h ns-3.38/src/core/model/hash-murmur3.h
--- ns-3.37/src/core/model/hash-murmur3.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/hash-murmur3.h	2023-03-16 20:54:23.592868600 -0700
@@ -101,7 +101,7 @@
      * This has to be a constant for all MPI ranks to generate
      * the same hash from the same string.
      */
-    enum seed
+    enum Seed
     {
         SEED = 0x8BADF00D // Ate bad food
     };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/int64x64-cairo.h ns-3.38/src/core/model/int64x64-cairo.h
--- ns-3.37/src/core/model/int64x64-cairo.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/int64x64-cairo.h	2023-03-16 20:54:23.592868600 -0700
@@ -296,7 +296,7 @@
     /**
      * Compute the inverse of an integer value.
      *
-     * Ordinary division by an integer would be limited to 64 bits of precsion.
+     * Ordinary division by an integer would be limited to 64 bits of precision.
      * Instead, we multiply by the 128-bit inverse of the divisor.
      * This function computes the inverse to 128-bit precision.
      * MulByInvert() then completes the division.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/int64x64-double.h ns-3.38/src/core/model/int64x64-double.h
--- ns-3.37/src/core/model/int64x64-double.h	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/int64x64-double.h	2023-03-16 20:54:23.592868600 -0700
@@ -314,7 +314,7 @@
      * \name Arithmetic Operators
      * Arithmetic operators for int64x64_t.
      */
-    /*
+    /**
      * @{
      * Arithmetic operator.
      * \param [in] lhs Left hand argument
@@ -367,7 +367,7 @@
      * \name Unary Operators
      * Unary operators for int64x64_t.
      */
-    /*
+    /**
      * @{
      * Unary operator.
      * \param [in] lhs Left hand argument
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/length.cc ns-3.38/src/core/model/length.cc
--- ns-3.37/src/core/model/length.cc	2022-11-01 17:37:24.467395000 -0700
+++ ns-3.38/src/core/model/length.cc	2023-03-16 20:54:23.592868600 -0700
@@ -181,7 +181,8 @@
         {{Unit::Yard, Unit::Meter}, USToMeter<std::ratio<3, 1>>},
         {{Unit::Meter, Unit::Yard}, MeterToUS<std::ratio<1, 3>>},
         {{Unit::Mile, Unit::Meter}, USToMeter<std::ratio<5280, 1>>},
-        {{Unit::Meter, Unit::Mile}, MeterToUS<std::ratio<1, 5280>>}};
+        {{Unit::Meter, Unit::Mile}, MeterToUS<std::ratio<1, 5280>>},
+    };
 
     auto iter = CONVERSIONS.find(Key{fromUnit, toUnit});
 
@@ -514,17 +515,19 @@
 {
     using StringTable = std::unordered_map<Length::Unit, std::string, EnumHash>;
 
-    static const StringTable STRINGS{{Length::Unit::Nanometer, "nm"},
-                                     {Length::Unit::Micrometer, "um"},
-                                     {Length::Unit::Millimeter, "mm"},
-                                     {Length::Unit::Centimeter, "cm"},
-                                     {Length::Unit::Meter, "m"},
-                                     {Length::Unit::Kilometer, "km"},
-                                     {Length::Unit::NauticalMile, "nmi"},
-                                     {Length::Unit::Inch, "in"},
-                                     {Length::Unit::Foot, "ft"},
-                                     {Length::Unit::Yard, "yd"},
-                                     {Length::Unit::Mile, "mi"}};
+    static const StringTable STRINGS{
+        {Length::Unit::Nanometer, "nm"},
+        {Length::Unit::Micrometer, "um"},
+        {Length::Unit::Millimeter, "mm"},
+        {Length::Unit::Centimeter, "cm"},
+        {Length::Unit::Meter, "m"},
+        {Length::Unit::Kilometer, "km"},
+        {Length::Unit::NauticalMile, "nmi"},
+        {Length::Unit::Inch, "in"},
+        {Length::Unit::Foot, "ft"},
+        {Length::Unit::Yard, "yd"},
+        {Length::Unit::Mile, "mi"},
+    };
 
     auto iter = STRINGS.find(unit);
 
@@ -554,7 +557,8 @@
         {Length::Unit::Inch, Entry{"inch", "inches"}},
         {Length::Unit::Foot, Entry{"foot", "feet"}},
         {Length::Unit::Yard, Entry{"yard", "yards"}},
-        {Length::Unit::Mile, Entry{"mile", "miles"}}};
+        {Length::Unit::Mile, Entry{"mile", "miles"}},
+    };
 
     auto iter = STRINGS.find(unit);
 
@@ -577,51 +581,53 @@
 {
     using UnitTable = std::unordered_map<std::string, Length::Unit>;
 
-    static const UnitTable UNITS{{"nm", Length::Unit::Nanometer},
-                                 {"nanometer", Length::Unit::Nanometer},
-                                 {"nanometers", Length::Unit::Nanometer},
-                                 {"nanometre", Length::Unit::Nanometer},
-                                 {"nanometres", Length::Unit::Nanometer},
-                                 {"um", Length::Unit::Micrometer},
-                                 {"micrometer", Length::Unit::Micrometer},
-                                 {"micrometers", Length::Unit::Micrometer},
-                                 {"micrometre", Length::Unit::Micrometer},
-                                 {"micrometres", Length::Unit::Micrometer},
-                                 {"mm", Length::Unit::Millimeter},
-                                 {"millimeter", Length::Unit::Millimeter},
-                                 {"millimeters", Length::Unit::Millimeter},
-                                 {"millimetre", Length::Unit::Millimeter},
-                                 {"millimetres", Length::Unit::Millimeter},
-                                 {"cm", Length::Unit::Centimeter},
-                                 {"centimeter", Length::Unit::Centimeter},
-                                 {"centimeters", Length::Unit::Centimeter},
-                                 {"centimetre", Length::Unit::Centimeter},
-                                 {"centimetres", Length::Unit::Centimeter},
-                                 {"m", Length::Unit::Meter},
-                                 {"meter", Length::Unit::Meter},
-                                 {"metre", Length::Unit::Meter},
-                                 {"meters", Length::Unit::Meter},
-                                 {"metres", Length::Unit::Meter},
-                                 {"km", Length::Unit::Kilometer},
-                                 {"kilometer", Length::Unit::Kilometer},
-                                 {"kilometers", Length::Unit::Kilometer},
-                                 {"kilometre", Length::Unit::Kilometer},
-                                 {"kilometres", Length::Unit::Kilometer},
-                                 {"nmi", Length::Unit::NauticalMile},
-                                 {"nauticalmile", Length::Unit::NauticalMile},
-                                 {"nauticalmiles", Length::Unit::NauticalMile},
-                                 {"in", Length::Unit::Inch},
-                                 {"inch", Length::Unit::Inch},
-                                 {"inches", Length::Unit::Inch},
-                                 {"ft", Length::Unit::Foot},
-                                 {"foot", Length::Unit::Foot},
-                                 {"feet", Length::Unit::Foot},
-                                 {"yd", Length::Unit::Yard},
-                                 {"yard", Length::Unit::Yard},
-                                 {"yards", Length::Unit::Yard},
-                                 {"mi", Length::Unit::Mile},
-                                 {"mile", Length::Unit::Mile},
-                                 {"miles", Length::Unit::Mile}};
+    static const UnitTable UNITS{
+        {"nm", Length::Unit::Nanometer},
+        {"nanometer", Length::Unit::Nanometer},
+        {"nanometers", Length::Unit::Nanometer},
+        {"nanometre", Length::Unit::Nanometer},
+        {"nanometres", Length::Unit::Nanometer},
+        {"um", Length::Unit::Micrometer},
+        {"micrometer", Length::Unit::Micrometer},
+        {"micrometers", Length::Unit::Micrometer},
+        {"micrometre", Length::Unit::Micrometer},
+        {"micrometres", Length::Unit::Micrometer},
+        {"mm", Length::Unit::Millimeter},
+        {"millimeter", Length::Unit::Millimeter},
+        {"millimeters", Length::Unit::Millimeter},
+        {"millimetre", Length::Unit::Millimeter},
+        {"millimetres", Length::Unit::Millimeter},
+        {"cm", Length::Unit::Centimeter},
+        {"centimeter", Length::Unit::Centimeter},
+        {"centimeters", Length::Unit::Centimeter},
+        {"centimetre", Length::Unit::Centimeter},
+        {"centimetres", Length::Unit::Centimeter},
+        {"m", Length::Unit::Meter},
+        {"meter", Length::Unit::Meter},
+        {"metre", Length::Unit::Meter},
+        {"meters", Length::Unit::Meter},
+        {"metres", Length::Unit::Meter},
+        {"km", Length::Unit::Kilometer},
+        {"kilometer", Length::Unit::Kilometer},
+        {"kilometers", Length::Unit::Kilometer},
+        {"kilometre", Length::Unit::Kilometer},
+        {"kilometres", Length::Unit::Kilometer},
+        {"nmi", Length::Unit::NauticalMile},
+        {"nauticalmile", Length::Unit::NauticalMile},
+        {"nauticalmiles", Length::Unit::NauticalMile},
+        {"in", Length::Unit::Inch},
+        {"inch", Length::Unit::Inch},
+        {"inches", Length::Unit::Inch},
+        {"ft", Length::Unit::Foot},
+        {"foot", Length::Unit::Foot},
+        {"feet", Length::Unit::Foot},
+        {"yd", Length::Unit::Yard},
+        {"yard", Length::Unit::Yard},
+        {"yards", Length::Unit::Yard},
+        {"mi", Length::Unit::Mile},
+        {"mile", Length::Unit::Mile},
+        {"miles", Length::Unit::Mile},
+    };
 
     // function to trim whitespace and convert to lowercase in one pass
     static auto Normalize = [](const std::string& str) {
@@ -843,6 +849,4 @@
     return Length(value, Length::Unit::Mile);
 }
 
-/**@}*/
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/length.h ns-3.38/src/core/model/length.h
--- ns-3.37/src/core/model/length.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/length.h	2023-03-16 20:54:23.592868600 -0700
@@ -134,7 +134,7 @@
  * Length::Unit
  *
  * \code
- *   //These two contructors are equivalent
+ *   //These two constructors are equivalent
  *   Length l1 (1, "cm");
  *   Length l2 (1, Length::Unit::Centimeter);
  * \endcode
@@ -146,7 +146,7 @@
  * from boost::unit quantities.
  *
  * \code
- *   //construct length from a boost::units quantitiy
+ *   //construct length from a boost::units quantity
  *   boost::units::quantity<boost::units::si::length> q
  *       = 5 * boost::units::si::meter;
  *   Length meters (q);
@@ -445,7 +445,7 @@
      * Initialize an object with the value from \p other.
      *
      * After the move completes, \p other is left in an undefined but
-     * useable state.
+     * usable state.
      *
      * \param other Length object to move
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/log.cc ns-3.38/src/core/model/log.cc
--- ns-3.37/src/core/model/log.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/log.cc	2023-03-16 20:54:23.592868600 -0700
@@ -19,14 +19,19 @@
 #include "log.h"
 
 #include "assert.h"
+#include "environment-variable.h"
 #include "fatal-error.h"
+#include "string.h"
 
 #include "ns3/core-config.h"
 
-#include <cstdlib> // getenv
-#include <cstring> // strlen
+#include <algorithm> // transform
+#include <cstring>   // strlen
 #include <iostream>
 #include <list>
+#include <locale> // toupper
+#include <map>
+#include <numeric> // accumulate
 #include <stdexcept>
 #include <utility>
 
@@ -36,6 +41,65 @@
  * ns3::LogComponent and related implementations.
  */
 
+/**
+ * \ingroup logging
+ * Unnamed namespace for log.cc
+ */
+namespace
+{
+/** Mapping of log level text names to values. */
+const std::map<std::string, ns3::LogLevel> LOG_LABEL_LEVELS = {
+    // clang-format off
+        {"none",           ns3::LOG_NONE},
+        {"error",          ns3::LOG_ERROR},
+        {"level_error",    ns3::LOG_LEVEL_ERROR},
+        {"warn",           ns3::LOG_WARN},
+        {"level_warn",     ns3::LOG_LEVEL_WARN},
+        {"debug",          ns3::LOG_DEBUG},
+        {"level_debug",    ns3::LOG_LEVEL_DEBUG},
+        {"info",           ns3::LOG_INFO},
+        {"level_info",     ns3::LOG_LEVEL_INFO},
+        {"function",       ns3::LOG_FUNCTION},
+        {"level_function", ns3::LOG_LEVEL_FUNCTION},
+        {"logic",          ns3::LOG_LOGIC},
+        {"level_logic",    ns3::LOG_LEVEL_LOGIC},
+        {"all",            ns3::LOG_ALL},
+        {"level_all",      ns3::LOG_LEVEL_ALL},
+        {"func",           ns3::LOG_PREFIX_FUNC},
+        {"prefix_func",    ns3::LOG_PREFIX_FUNC},
+        {"time",           ns3::LOG_PREFIX_TIME},
+        {"prefix_time",    ns3::LOG_PREFIX_TIME},
+        {"node",           ns3::LOG_PREFIX_NODE},
+        {"prefix_node",    ns3::LOG_PREFIX_NODE},
+        {"level",          ns3::LOG_PREFIX_LEVEL},
+        {"prefix_level",   ns3::LOG_PREFIX_LEVEL},
+        {"prefix_all",     ns3::LOG_PREFIX_ALL}
+    // clang-format on
+};
+
+/** Inverse mapping of level values to log level text names. */
+const std::map<ns3::LogLevel, std::string> LOG_LEVEL_LABELS = {[]() {
+    std::map<ns3::LogLevel, std::string> labels;
+    for (const auto& [label, lev] : LOG_LABEL_LEVELS)
+    {
+        // Only keep the first label for a level
+        if (labels.find(lev) == labels.end())
+        {
+            std::string pad{label};
+            // Add whitespace for alignment with "ERROR", "DEBUG" etc.
+            if (pad.size() < 5)
+            {
+                pad.insert(pad.size(), 5 - pad.size(), ' ');
+            }
+            std::transform(pad.begin(), pad.end(), pad.begin(), ::toupper);
+            labels[lev] = pad;
+        }
+    }
+    return labels;
+}()};
+
+} // Unnamed namespace
+
 namespace ns3
 {
 
@@ -53,8 +117,13 @@
 
 /**
  * \ingroup logging
- * Handler for \c print-list token in NS_LOG
- * to print the list of log components.
+ * Handler for the undocumented \c print-list token in NS_LOG
+ * which triggers printing of the list of log components, then exits.
+ *
+ * A static instance of this class is instantiated below, so the
+ * \c print-list token is handled before any other logging action
+ * can take place.
+ *
  * This is private to the logging implementation.
  */
 class PrintList
@@ -79,36 +148,23 @@
 
 PrintList::PrintList()
 {
-    const char* envVar = std::getenv("NS_LOG");
-    if (envVar == nullptr || std::strlen(envVar) == 0)
+    auto [found, value] = EnvironmentVariable::Get("NS_LOG", "print-list", ":");
+    if (found)
     {
-        return;
-    }
-    std::string env = envVar;
-    std::string::size_type cur = 0;
-    std::string::size_type next = 0;
-    while (next != std::string::npos)
-    {
-        next = env.find_first_of(':', cur);
-        std::string tmp = std::string(env, cur, next - cur);
-        if (tmp == "print-list")
-        {
-            LogComponentPrintList();
-            exit(0);
-            break;
-        }
-        cur = next + 1;
+        LogComponentPrintList();
+        exit(0);
     }
 }
 
 LogComponent::LogComponent(const std::string& name,
                            const std::string& file,
-                           const enum LogLevel mask /* = 0 */)
+                           const LogLevel mask /* = 0 */)
     : m_levels(0),
       m_mask(mask),
       m_name(name),
       m_file(file)
 {
+    // Check if we're mentioned in NS_LOG, and set our flags appropriately
     EnvVarCheck();
 
     LogComponent::ComponentList* components = GetComponentList();
@@ -144,139 +200,54 @@
 void
 LogComponent::EnvVarCheck()
 {
-    const char* envVar = std::getenv("NS_LOG");
-    if (envVar == nullptr || std::strlen(envVar) == 0)
+    auto [found, value] = EnvironmentVariable::Get("NS_LOG", m_name, ":");
+    if (!found)
+    {
+        std::tie(found, value) = EnvironmentVariable::Get("NS_LOG", "*", ":");
+    }
+    if (!found)
+    {
+        std::tie(found, value) = EnvironmentVariable::Get("NS_LOG", "***", ":");
+    }
+
+    if (!found)
     {
         return;
     }
-    std::string env = envVar;
 
-    std::string::size_type cur = 0;
-    std::string::size_type next = 0;
-    while (next != std::string::npos)
-    {
-        next = env.find_first_of(':', cur);
-        std::string tmp = std::string(env, cur, next - cur);
-        std::string::size_type equal = tmp.find('=');
-        std::string component;
-        if (equal == std::string::npos)
-        {
-            component = tmp;
-            if (component == m_name || component == "*" || component == "***")
-            {
-                int level = LOG_LEVEL_ALL | LOG_PREFIX_ALL;
-                Enable((enum LogLevel)level);
-                return;
-            }
-        }
-        else
-        {
-            component = tmp.substr(0, equal);
-            if (component == m_name || component == "*")
-            {
-                int level = 0;
-                std::string::size_type cur_lev;
-                std::string::size_type next_lev = equal;
-                bool pre_pipe = true; // before the first '|', enables positional 'all', '*'
-                do
-                {
-                    cur_lev = next_lev + 1;
-                    next_lev = tmp.find('|', cur_lev);
-                    std::string lev = tmp.substr(cur_lev, next_lev - cur_lev);
-                    if (lev == "error")
-                    {
-                        level |= LOG_ERROR;
-                    }
-                    else if (lev == "warn")
-                    {
-                        level |= LOG_WARN;
-                    }
-                    else if (lev == "debug")
-                    {
-                        level |= LOG_DEBUG;
-                    }
-                    else if (lev == "info")
-                    {
-                        level |= LOG_INFO;
-                    }
-                    else if (lev == "function")
-                    {
-                        level |= LOG_FUNCTION;
-                    }
-                    else if (lev == "logic")
-                    {
-                        level |= LOG_LOGIC;
-                    }
-                    else if (pre_pipe && ((lev == "all") || (lev == "*")))
-                    {
-                        level |= LOG_LEVEL_ALL;
-                    }
-                    else if ((lev == "prefix_func") || (lev == "func"))
-                    {
-                        level |= LOG_PREFIX_FUNC;
-                    }
-                    else if ((lev == "prefix_time") || (lev == "time"))
-                    {
-                        level |= LOG_PREFIX_TIME;
-                    }
-                    else if ((lev == "prefix_node") || (lev == "node"))
-                    {
-                        level |= LOG_PREFIX_NODE;
-                    }
-                    else if ((lev == "prefix_level") || (lev == "level"))
-                    {
-                        level |= LOG_PREFIX_LEVEL;
-                    }
-                    else if ((lev == "prefix_all") ||
-                             (!pre_pipe && ((lev == "all") || (lev == "*"))))
-                    {
-                        level |= LOG_PREFIX_ALL;
-                    }
-                    else if (lev == "level_error")
-                    {
-                        level |= LOG_LEVEL_ERROR;
-                    }
-                    else if (lev == "level_warn")
-                    {
-                        level |= LOG_LEVEL_WARN;
-                    }
-                    else if (lev == "level_debug")
-                    {
-                        level |= LOG_LEVEL_DEBUG;
-                    }
-                    else if (lev == "level_info")
-                    {
-                        level |= LOG_LEVEL_INFO;
-                    }
-                    else if (lev == "level_function")
-                    {
-                        level |= LOG_LEVEL_FUNCTION;
-                    }
-                    else if (lev == "level_logic")
-                    {
-                        level |= LOG_LEVEL_LOGIC;
-                    }
-                    else if (lev == "level_all")
-                    {
-                        level |= LOG_LEVEL_ALL;
-                    }
-                    else if (lev == "**")
-                    {
-                        level |= LOG_LEVEL_ALL | LOG_PREFIX_ALL;
-                    }
+    if (value.empty())
+    {
+        // Default is enable all levels, all prefixes
+        value = "**";
+    }
 
-                    pre_pipe = false;
-                } while (next_lev != std::string::npos);
+    // Got a value, might have flags
+    int level = 0;
+    StringVector flags = SplitString(value, "|");
+    NS_ASSERT_MSG(!flags.empty(), "Unexpected empty flags from non-empty value");
+    bool pre_pipe{true};
 
-                Enable((enum LogLevel)level);
-            }
+    for (const auto& lev : flags)
+    {
+        if (lev == "**")
+        {
+            level |= LOG_LEVEL_ALL | LOG_PREFIX_ALL;
         }
-        cur = next + 1;
+        else if (lev == "all" || lev == "*")
+        {
+            level |= (pre_pipe ? LOG_LEVEL_ALL : LOG_PREFIX_ALL);
+        }
+        else if (LOG_LABEL_LEVELS.find(lev) != LOG_LABEL_LEVELS.end())
+        {
+            level |= LOG_LABEL_LEVELS.at(lev);
+        }
+        pre_pipe = false;
     }
+    Enable((LogLevel)level);
 }
 
 bool
-LogComponent::IsEnabled(const enum LogLevel level) const
+LogComponent::IsEnabled(const LogLevel level) const
 {
     //  LogComponentEnableEnvVar ();
     return (level & m_levels) ? 1 : 0;
@@ -289,27 +260,27 @@
 }
 
 void
-LogComponent::SetMask(const enum LogLevel level)
+LogComponent::SetMask(const LogLevel level)
 {
     m_mask |= level;
 }
 
 void
-LogComponent::Enable(const enum LogLevel level)
+LogComponent::Enable(const LogLevel level)
 {
     m_levels |= (level & ~m_mask);
 }
 
 void
-LogComponent::Disable(const enum LogLevel level)
+LogComponent::Disable(const LogLevel level)
 {
     m_levels &= ~level;
 }
 
-const char*
+std::string
 LogComponent::Name() const
 {
-    return m_name.c_str();
+    return m_name;
 }
 
 std::string
@@ -320,42 +291,18 @@
 
 /* static */
 std::string
-LogComponent::GetLevelLabel(const enum LogLevel level)
+LogComponent::GetLevelLabel(const LogLevel level)
 {
-    if (level == LOG_ERROR)
+    auto it = LOG_LEVEL_LABELS.find(level);
+    if (it != LOG_LEVEL_LABELS.end())
     {
-        return "ERROR";
-    }
-    else if (level == LOG_WARN)
-    {
-        // whitespace left at the end for alignment
-        return "WARN ";
-    }
-    else if (level == LOG_DEBUG)
-    {
-        return "DEBUG";
-    }
-    else if (level == LOG_INFO)
-    {
-        // whitespace left at the end for alignment
-        return "INFO ";
-    }
-    else if (level == LOG_FUNCTION)
-    {
-        return "FUNCT";
-    }
-    else if (level == LOG_LOGIC)
-    {
-        return "LOGIC";
-    }
-    else
-    {
-        return "unknown";
+        return it->second;
     }
+    return "unknown";
 }
 
 void
-LogComponentEnable(const char* name, enum LogLevel level)
+LogComponentEnable(const std::string& name, LogLevel level)
 {
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
     LogComponent::ComponentList::const_iterator i;
@@ -370,14 +317,16 @@
     if (i == 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");
+                       << name << "\" not found."
+                       << " See above for a list of available log components");
     }
 }
 
 void
-LogComponentEnableAll(enum LogLevel level)
+LogComponentEnableAll(LogLevel level)
 {
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
     for (LogComponent::ComponentList::const_iterator i = components->begin();
@@ -389,7 +338,7 @@
 }
 
 void
-LogComponentDisable(const char* name, enum LogLevel level)
+LogComponentDisable(const std::string& name, LogLevel level)
 {
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
     for (LogComponent::ComponentList::const_iterator i = components->begin();
@@ -405,7 +354,7 @@
 }
 
 void
-LogComponentDisableAll(enum LogLevel level)
+LogComponentDisableAll(LogLevel level)
 {
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
     for (LogComponent::ComponentList::const_iterator i = components->begin();
@@ -499,12 +448,11 @@
 static bool
 ComponentExists(std::string componentName)
 {
-    const char* name = componentName.c_str();
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
     LogComponent::ComponentList::const_iterator i;
     for (i = components->begin(); i != components->end(); i++)
     {
-        if (i->first == name)
+        if (i->first == componentName)
         {
             return true;
         }
@@ -522,81 +470,42 @@
 static void
 CheckEnvironmentVariables()
 {
-    const char* envVar = std::getenv("NS_LOG");
-    if (envVar == nullptr || std::strlen(envVar) == 0)
-    {
-        return;
-    }
+    auto dict = EnvironmentVariable::GetDictionary("NS_LOG", ":")->GetStore();
 
-    std::string env = envVar;
-    std::string::size_type cur = 0;
-    std::string::size_type next = 0;
-
-    while (next != std::string::npos)
-    {
-        next = env.find_first_of(':', cur);
-        std::string tmp = std::string(env, cur, next - cur);
-        std::string::size_type equal = tmp.find('=');
-        std::string component;
-        if (equal == std::string::npos)
-        {
-            // ie no '=' characters found
-            component = tmp;
-            if (ComponentExists(component) || component == "*" || component == "***")
-            {
-                return;
-            }
-            else
-            {
-                LogComponentPrintList();
-                NS_FATAL_ERROR(
-                    "Invalid or unregistered component name \""
-                    << component
-                    << "\" in env variable NS_LOG, see above for a list of valid components");
-            }
+    for (auto& [component, value] : dict)
+    {
+        if (component != "*" && component != "***" && !ComponentExists(component))
+        {
+            NS_LOG_UNCOND("Invalid or unregistered component name \"" << component << "\"");
+            LogComponentPrintList();
+            NS_FATAL_ERROR(
+                "Invalid or unregistered component name \""
+                << component
+                << "\" in env variable NS_LOG, see above for a list of valid components");
         }
-        else
+
+        // We have a valid component or wildcard, check the flags
+        if (!value.empty())
         {
-            component = tmp.substr(0, equal);
-            if (ComponentExists(component) || component == "*")
+            // Check the flags present in value
+            StringVector flags = SplitString(value, "|");
+            for (const auto& flag : flags)
             {
-                std::string::size_type cur_lev;
-                std::string::size_type next_lev = equal;
-                do
+                // Handle wild cards
+                if (flag == "*" || flag == "**")
                 {
-                    cur_lev = next_lev + 1;
-                    next_lev = tmp.find('|', cur_lev);
-                    std::string lev = tmp.substr(cur_lev, next_lev - cur_lev);
-                    if (lev == "error" || lev == "warn" || lev == "debug" || lev == "info" ||
-                        lev == "function" || lev == "logic" || lev == "all" ||
-                        lev == "prefix_func" || lev == "func" || lev == "prefix_time" ||
-                        lev == "time" || lev == "prefix_node" || lev == "node" ||
-                        lev == "prefix_level" || lev == "level" || lev == "prefix_all" ||
-                        lev == "level_error" || lev == "level_warn" || lev == "level_debug" ||
-                        lev == "level_info" || lev == "level_function" || lev == "level_logic" ||
-                        lev == "level_all" || lev == "*" || lev == "**")
-                    {
-                        continue;
-                    }
-                    else
-                    {
-                        NS_FATAL_ERROR("Invalid log level \""
-                                       << lev << "\" in env variable NS_LOG for component name "
-                                       << component);
-                    }
-                } while (next_lev != std::string::npos);
-            }
-            else
-            {
-                LogComponentPrintList();
-                NS_FATAL_ERROR(
-                    "Invalid or unregistered component name \""
-                    << component
-                    << "\" in env variable NS_LOG, see above for a list of valid components");
-            }
-        }
-        cur = next + 1; // parse next component
-    }
+                    continue;
+                }
+                bool ok = LOG_LABEL_LEVELS.find(flag) != LOG_LABEL_LEVELS.end();
+                if (!ok)
+                {
+                    NS_FATAL_ERROR("Invalid log level \""
+                                   << flag << "\" in env variable NS_LOG for component name "
+                                   << component);
+                }
+            } // for flag
+        }     // !value.empty
+    }         // for component
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/log.h ns-3.38/src/core/model/log.h
--- ns-3.37/src/core/model/log.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/log.h	2023-03-16 20:54:23.592868600 -0700
@@ -134,7 +134,7 @@
  * \param [in] name The log component name.
  * \param [in] level The logging level.
  */
-void LogComponentEnable(const char* name, enum LogLevel level);
+void LogComponentEnable(const std::string& name, LogLevel level);
 
 /**
  * Enable the logging output for all registered log components.
@@ -144,7 +144,7 @@
  *
  * \param [in] level The logging level.
  */
-void LogComponentEnableAll(enum LogLevel level);
+void LogComponentEnableAll(LogLevel level);
 
 /**
  * Disable the logging output associated with that log component.
@@ -155,14 +155,14 @@
  * \param [in] name The log component name.
  * \param [in] level The logging level.
  */
-void LogComponentDisable(const char* name, enum LogLevel level);
+void LogComponentDisable(const std::string& name, LogLevel level);
 
 /**
  * Disable all logging for all components.
  *
  * \param [in] level The logging level.
  */
-void LogComponentDisableAll(enum LogLevel level);
+void LogComponentDisableAll(LogLevel level);
 
 } // namespace ns3
 
@@ -336,16 +336,14 @@
      *                  a log level helps prevent recursion by logging in
      *                  functions which help implement the logging facility.
      */
-    LogComponent(const std::string& name,
-                 const std::string& file,
-                 const enum LogLevel mask = LOG_NONE);
+    LogComponent(const std::string& name, const std::string& file, const LogLevel mask = LOG_NONE);
     /**
      * Check if this LogComponent is enabled for \c level
      *
      * \param [in] level The level to check for.
      * \return \c true if we are enabled at \c level.
      */
-    bool IsEnabled(const enum LogLevel level) const;
+    bool IsEnabled(const LogLevel level) const;
     /**
      * Check if all levels are disabled.
      *
@@ -357,19 +355,19 @@
      *
      * \param [in] level The LogLevel to enable.
      */
-    void Enable(const enum LogLevel level);
+    void Enable(const LogLevel level);
     /**
      * Disable logging at \c level for this LogComponent.
      *
      * \param [in] level The LogLevel to disable.
      */
-    void Disable(const enum LogLevel level);
+    void Disable(const LogLevel level);
     /**
      * Get the name of this LogComponent.
      *
      * \return The name of this LogComponent.
      */
-    const char* Name() const;
+    std::string Name() const;
     /**
      * Get the compilation unit defining this LogComponent.
      * \returns The file name.
@@ -381,13 +379,13 @@
      * \param [in] level The LogLevel to get the label for.
      * \return The string label for \c level.
      */
-    static std::string GetLevelLabel(const enum LogLevel level);
+    static std::string GetLevelLabel(const LogLevel level);
     /**
      * Prevent the enabling of a specific LogLevel.
      *
      * \param [in] level The LogLevel to block.
      */
-    void SetMask(const enum LogLevel level);
+    void SetMask(const LogLevel level);
 
     /**
      * LogComponent name map.
@@ -400,7 +398,7 @@
     typedef std::map<std::string, LogComponent*> ComponentList;
 
     /**
-     * Get the list of LogComponnents.
+     * Get the list of LogComponents.
      *
      * \internal
      * This should really be considered an internal API.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/map-scheduler.cc ns-3.38/src/core/model/map-scheduler.cc
--- ns-3.37/src/core/model/map-scheduler.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/map-scheduler.cc	2023-03-16 20:54:23.592868600 -0700
@@ -85,7 +85,7 @@
     Event ev;
     ev.impl = i->second;
     ev.key = i->first;
-    NS_LOG_DEBUG(this << ev.impl << ev.key.m_ts << ev.key.m_uid);
+    NS_LOG_DEBUG(this << ": " << ev.impl << ", " << ev.key.m_ts << ", " << ev.key.m_uid);
     return ev;
 }
 
@@ -99,7 +99,7 @@
     ev.impl = i->second;
     ev.key = i->first;
     m_list.erase(i);
-    NS_LOG_DEBUG(this << ev.impl << ev.key.m_ts << ev.key.m_uid);
+    NS_LOG_DEBUG("@" << this << ": " << ev.impl << ", " << ev.key.m_ts << ", " << ev.key.m_uid);
     return ev;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/matrix-array.cc ns-3.38/src/core/model/matrix-array.cc
--- ns-3.37/src/core/model/matrix-array.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/model/matrix-array.cc	2023-03-16 20:54:23.592868600 -0700
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2022 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
+ *
+ * Author: Biljana Bojovic <bbojovic@cttc.es>
+ */
+
+#include "matrix-array.h"
+
+#ifdef HAVE_EIGEN3
+#include <Eigen/Dense>
+#endif
+
+namespace ns3
+{
+
+#ifdef HAVE_EIGEN3
+template <class T>
+using EigenMatrix = Eigen::Map<Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>>;
+template <class T>
+using ConstEigenMatrix = Eigen::Map<const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>>;
+#endif
+
+template <class T>
+MatrixArray<T>::MatrixArray(uint16_t numRows, uint16_t numCols, uint16_t numPages)
+    : ValArray<T>(numRows, numCols, numPages)
+{
+}
+
+template <class T>
+MatrixArray<T>::MatrixArray(const std::valarray<T>& values)
+    : ValArray<T>(values)
+{
+}
+
+template <class T>
+MatrixArray<T>::MatrixArray(std::valarray<T>&& values)
+    : ValArray<T>(std::move(values))
+{
+}
+
+template <class T>
+MatrixArray<T>::MatrixArray(const std::vector<T>& values)
+    : ValArray<T>(values)
+{
+}
+
+template <class T>
+MatrixArray<T>::MatrixArray(uint16_t numRows, uint16_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)
+    : ValArray<T>(numRows, numCols, std::move(values))
+{
+}
+
+template <class T>
+MatrixArray<T>::MatrixArray(uint16_t numRows,
+                            uint16_t numCols,
+                            uint16_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,
+                            std::valarray<T>&& values)
+    : ValArray<T>(numRows, numCols, numPages, std::move(values))
+{
+}
+
+template <class T>
+MatrixArray<T>
+MatrixArray<T>::operator*(const MatrixArray<T>& rhs) const
+{
+    NS_ASSERT_MSG(m_numPages == rhs.m_numPages, "MatrixArrays have different numbers of matrices.");
+    NS_ASSERT_MSG(m_numCols == rhs.m_numRows, "Inner dimensions of matrices mismatch.");
+
+    MatrixArray<T> res{m_numRows, rhs.m_numCols, m_numPages};
+
+    for (uint16_t page = 0; page < res.m_numPages; ++page)
+    {
+#ifdef HAVE_EIGEN3 // Eigen found and enabled Eigen optimizations
+
+        ConstEigenMatrix<T> lhsEigenMatrix(GetPagePtr(page), m_numRows, m_numCols);
+        ConstEigenMatrix<T> rhsEigenMatrix(rhs.GetPagePtr(page), rhs.m_numRows, rhs.m_numCols);
+        EigenMatrix<T> resEigenMatrix(res.GetPagePtr(page), res.m_numRows, res.m_numCols);
+        resEigenMatrix = lhsEigenMatrix * rhsEigenMatrix;
+
+#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)
+        {
+            for (uint16_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();
+            }
+        }
+
+#endif
+    }
+    return res;
+}
+
+template <class T>
+MatrixArray<T>
+MatrixArray<T>::Transpose() const
+{
+    // Create the matrix where m_numRows = this.m_numCols, m_numCols = this.m_numRows,
+    // m_numPages = this.m_numPages
+    MatrixArray<T> res{m_numCols, m_numRows, m_numPages};
+
+    for (uint16_t page = 0; page < m_numPages; ++page)
+    {
+#ifdef HAVE_EIGEN3 // Eigen found and Eigen optimizations enabled
+
+        ConstEigenMatrix<T> thisMatrix(GetPagePtr(page), m_numRows, m_numCols);
+        EigenMatrix<T> resEigenMatrix(res.GetPagePtr(page), res.m_numRows, res.m_numCols);
+        resEigenMatrix = thisMatrix.transpose();
+
+#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)
+        {
+            res.m_values[std::slice(matrixIndex + i * res.m_numRows, res.m_numRows, 1)] =
+                m_values[std::slice(matrixIndex + i, m_numCols, m_numRows)];
+        }
+
+#endif
+    }
+    return res;
+}
+
+template <class T>
+MatrixArray<T>
+MatrixArray<T>::MultiplyByLeftAndRightMatrix(const MatrixArray<T>& lMatrix,
+                                             const MatrixArray<T>& rMatrix) const
+{
+    NS_ASSERT_MSG(lMatrix.m_numPages == 1 && rMatrix.m_numPages == 1,
+                  "The left and right MatrixArray should have only one page.");
+    NS_ASSERT_MSG(lMatrix.m_numCols == m_numRows,
+                  "Left vector numCols and this MatrixArray numRows mismatch.");
+    NS_ASSERT_MSG(m_numCols == rMatrix.m_numRows,
+                  "Right vector numRows and this MatrixArray numCols mismatch.");
+
+    MatrixArray<T> res{lMatrix.m_numRows, rMatrix.m_numCols, m_numPages};
+
+#ifdef HAVE_EIGEN3
+
+    ConstEigenMatrix<T> lMatrixEigen(lMatrix.GetPagePtr(0), lMatrix.m_numRows, lMatrix.m_numCols);
+    ConstEigenMatrix<T> rMatrixEigen(rMatrix.GetPagePtr(0), rMatrix.m_numRows, rMatrix.m_numCols);
+#endif
+
+    for (uint16_t page = 0; page < m_numPages; ++page)
+    {
+#ifdef HAVE_EIGEN3 // Eigen found and Eigen optimizations enabled
+
+        ConstEigenMatrix<T> matrixEigen(GetPagePtr(page), m_numRows, m_numCols);
+        EigenMatrix<T> resEigenMap(res.GetPagePtr(page), res.m_numRows, res.m_numCols);
+
+        resEigenMap = lMatrixEigen * matrixEigen * rMatrixEigen;
+
+#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 (uint16_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)
+                {
+                    interRes[thisCol] =
+                        (lMatrix
+                             .m_values[std::slice(resRow, lMatrix.m_numCols, lMatrix.m_numRows)] *
+                         m_values[std::slice(matrixOffset + thisCol * m_numRows, m_numRows, 1)])
+                            .sum();
+                }
+                // multiply intermediate results and resCol column of the rMatrix
+                res(resRow, resCol, page) =
+                    (interRes *
+                     rMatrix.m_values[std::slice(resCol * rMatrix.m_numRows, rMatrix.m_numRows, 1)])
+                        .sum();
+            }
+        }
+#endif
+    }
+    return res;
+}
+
+template <class T>
+template <bool EnableBool, typename>
+MatrixArray<T>
+MatrixArray<T>::HermitianTranspose() const
+{
+    MatrixArray<std::complex<double>> retMatrix = this->Transpose();
+
+    for (size_t index = 0; index < this->GetSize(); ++index)
+    {
+        retMatrix[index] = std::conj(m_values[index]);
+    }
+    return retMatrix;
+}
+
+template MatrixArray<std::complex<double>> MatrixArray<std::complex<double>>::HermitianTranspose()
+    const;
+template class MatrixArray<std::complex<double>>;
+template class MatrixArray<double>;
+template class MatrixArray<int>;
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/matrix-array.h ns-3.38/src/core/model/matrix-array.h
--- ns-3.37/src/core/model/matrix-array.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/model/matrix-array.h	2023-03-16 20:54:23.592868600 -0700
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2022 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
+ *
+ * Author: Biljana Bojovic <bbojovic@cttc.es>
+ */
+
+#ifndef MATRIX_ARRAY_H
+#define MATRIX_ARRAY_H
+
+#include "val-array.h"
+
+#include <valarray>
+
+namespace ns3
+{
+
+/**
+ * \ingroup Matrices
+ *
+ * \brief MatrixArray class inherits ValArray class and provides additional
+ * interfaces to ValArray which enable page-wise linear algebra operations for
+ * arrays of matrices. While ValArray class is focused on efficient storage,
+ * access and basic algebra element-wise operations on 3D numerical arrays,
+ * MatrixArray is focused on the efficient algebra operations on arrays of
+ * matrices.
+ *
+ * Each page (2-dimensional array) within a 3D array is considered to
+ * be a matrix, and operations are performed separately for each page/matrix.
+ * For example, a multiplication of two MatrixArrays means that a matrix
+ * multiplication is performed between each page in the first MatrixArray with
+ * the corresponding page in the second MatrixArray. Some of the operations can
+ * use the Eigen3 matrix library for speedup. In addition, this class can be
+ * extended to provide interfaces to the Eigen3 matrix library and enable
+ * additional linear algebra operations. The objective of this class is to two-fold:
+ *
+ * - First, it provides simple interfaces to perform matrix operations in parallel
+ * on all pages without the need to write an outer loop over all the pages.
+ *
+ * - Second, it can improve the performance of such parallel matrix operations,
+ * as this class uses a single memory allocation and one common set of size
+ * variables (rows, cols) for all pages, which can be an advantage compared to
+ * having separate memory allocations and size variables for each page/matrix,
+ * especially when those matrices are very small.
+ *
+ * Important characteristics:
+ *
+ * - Matrices are in column-major order.
+ *
+ * - Matrices can be 1-dimensional, in the sense that either the number of rows
+ * or columns is equal to 1.
+ *
+ * - The array of matrices can contain just one matrix or more matrices.
+ *
+ * - For binary operators, both MatrixArrays must have an equal number of pages,
+ * as well compatible row and column dimensions between the matrices.
+ *
+ * - Binary operators are performed among matrices with the same index from lhs
+ * and rhs matrix.
+ *
+ * - MatrixArray improves computationally complex matrix operations by using
+ * Eigen library when it is available at compile time. MatrixArray class makes
+ * use of Eigen Map class, which allows re-using already existing portion of
+ * memory as an Eigen type, e.g. ValArray can return a pointer to a memory
+ * where are placed elements of a specific matrix, and this can be passed to
+ * Eigen Map class which does not do a copy of the elements, but is using these
+ * elements directly in linear algebra operations.
+ */
+template <class T>
+class MatrixArray : public ValArray<T>
+{
+  public:
+    // instruct the compiler to generate the default constructor
+    MatrixArray<T>() = default;
+    /**
+     * \brief Constructor "pages" number of matrices that are of size "numRows"x"numCols",
+     * and are initialized with all-zero elements.
+     * If only 1 parameter, numRows, is provided then a single 1D array is being created.
+     * \param numRows the number of rows
+     * \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);
+    /**
+     * \brief Constructor creates a single array of values.size () elements and 1 column,
+     * and uses std::valarray<T> values to initialize the elements.
+     * \param values std::valarray<T> that will be used to initialize elements of 1D array
+     */
+    explicit MatrixArray<T>(const std::valarray<T>& values);
+    /**
+     * \brief Constructor creates a single array of values.size () elements and 1 column,
+     * and moves std::valarray<T> values to initialize the elements.
+     * \param values std::valarray<T> that will be moved to initialize elements of 1D array
+     */
+    MatrixArray<T>(std::valarray<T>&& values);
+    /**
+     * \brief Constructor creates a single array of values.size () elements and 1 column,
+     * and uses std::vector<T> values to initialize the elements.
+     * \param values std::vector<T> that will be used to initialize elements of 1D array
+     */
+    explicit MatrixArray<T>(const std::vector<T>& values);
+    /**
+     * \brief Constructor creates a single matrix of numRows and numCols, and uses
+     * std::valarray<T> values to initialize the elements.
+     * \param numRows the number of rows
+     * \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);
+    /**
+     * \brief Constructor creates a single matrix of numRows and numCols, and moves
+     * std::valarray<T> values to initialize the elements.
+     * \param numRows the number of rows
+     * \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);
+    /**
+     * \brief Constructor creates the array of numPages matrices of numRows x numCols dimensions,
+     * and uses std::valarray<T> values to initialize all the elements.
+     * \param numRows the number of rows
+     * \param numCols the number of columns
+     * \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);
+    /**
+     * \brief Constructor creates the array of numPages matrices of numRows x numCols dimensions,
+     * and moves std::valarray<T> values to initialize all the elements.
+     * \param numRows the number of rows
+     * \param numCols the number of columns
+     * \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);
+    /** instruct the compiler to generate the implicitly declared destructor*/
+    ~MatrixArray<T>() override = default;
+    /** instruct the compiler to generate the implicitly declared copy constructor*/
+    MatrixArray<T>(const MatrixArray<T>&) = default;
+    /**
+     * \brief Copy assignment operator.
+     * Instruct the compiler to generate the implicitly declared copy assignment operator.
+     * \return a reference to the result of the assignment
+     */
+    MatrixArray<T>& operator=(const MatrixArray<T>&) = default;
+    /** instruct the compiler to generate the implicitly declared move constructor*/
+    MatrixArray<T>(MatrixArray<T>&&) = default;
+    /**
+     * \brief Move assignment operator.
+     * Instruct the compiler to generate the implicitly declared move assignment operator.
+     * \return a reference to the result of the assignment
+     */
+    MatrixArray<T>& operator=(MatrixArray<T>&&) = default;
+    /**
+     * \brief Element-wise multiplication with a scalar value.
+     * \param rhs is a scalar value of type T
+     * \returns The matrix in which each element has been multiplied by the given
+     * scalar value.
+     */
+    MatrixArray<T> operator*(const T& rhs) const;
+    /**
+     * \brief operator+ definition for MatrixArray<T>.
+     * \param rhs The rhs MatrixArray object
+     * \returns The result of operator+ of this MatrixArray and rhs MatrixAray
+     */
+    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
+     */
+    MatrixArray<T> operator-(const MatrixArray<T>& rhs) const;
+    /**
+     * \brief unary operator- definition for MatrixArray<T>.
+     * \return the result of the operator-
+     */
+    MatrixArray<T> operator-() const;
+    /**
+     * \brief Page-wise matrix multiplication.
+     * This operator interprets the 3D array as an array of matrices,
+     * and performs a linear algebra operation on each of the matrices (pages),
+     * i.e., matrices from this MatrixArray and rhs with the same page indices are
+     * multiplied.
+     * The number of columns of this MatrixArray must be equal to the number of
+     * rows in rhs, and rhs must have the same number of pages as this MatrixArray.
+     * \param rhs is another MatrixArray instance
+     * \returns The array of results of matrix multiplications.
+     */
+    MatrixArray<T> operator*(const MatrixArray<T>& rhs) const;
+    /**
+     * \brief This operator interprets the 3D array as an array of matrices,
+     * and performs a linear algebra operation on each of the matrices (pages),
+     * i.e., transposes the matrix or array of matrices definition for MatrixArray<T>.
+     * \return The resulting MatrixArray composed of the array of transposed matrices.
+     */
+    MatrixArray<T> Transpose() const;
+    /**
+     *\brief Multiply each matrix in the array by the left and the right matrix.
+     * For each page of this MatrixArray the operation performed is
+     * lMatrix * matrix(pageIndex) * rMatrix, and the resulting MatrixArray<T>
+     * contains the array of the results per page. If "this" has dimensions
+     * M x N x P, lMatrix must have dimensions M number of columns, and a single
+     * page, and rMatrix must have N number of rows, and also a single page.
+     *
+     * The result of this function will have the dimensions J x K x P. Where J is
+     * the number of rows of the lMatrix, and K is the number of
+     * columns of rMatrix. Dimensions are:
+     *
+     * lMatrix(JxMx1) * this (MxNxP) * rMatrix(NxKx1) -> result(JxKxP)
+     *
+     * This operation is not possible when using the multiplication operator because
+     * the number of pages does not match.
+     *
+     * \param lMatrix the left matrix in the multiplication
+     * \param rMatrix the right matrix in the multiplication
+     * \returns Returns the result of the multiplication which is a 3D MatrixArray
+     * with dimensions J x K x P as explained previously.
+     */
+    MatrixArray<T> MultiplyByLeftAndRightMatrix(const MatrixArray<T>& lMatrix,
+                                                const MatrixArray<T>& rMatrix) const;
+
+    using ValArray<T>::GetPagePtr;
+    using ValArray<T>::EqualDims;
+    using ValArray<T>::AssertEqualDims;
+
+    /**
+     * \brief Function that performs the Hermitian transpose of this MatrixArray
+     * and returns a new matrix that is the result of the operation.
+     * This function assumes that the matrix contains complex numbers, because of that
+     * it is only available for the <std::complex<double>> specialization of MatrixArray.
+     * \return Returns a new matrix that is the result of the Hermitian transpose
+     */
+    template <bool EnableBool = true,
+              typename = typename std::enable_if<(std::is_same<T, std::complex<double>>::value &&
+                                                  EnableBool)>::type>
+    MatrixArray<T> HermitianTranspose() const;
+
+  private:
+    // To simplify functions in MatrixArray that are using members from the template base class
+    using ValArray<T>::m_numRows;
+    using ValArray<T>::m_numCols;
+    using ValArray<T>::m_numPages;
+    using ValArray<T>::m_values;
+};
+
+/// Create an alias for MatrixArray using int type
+using IntMatrixArray = MatrixArray<int>;
+
+/// Create an alias for MatrixArray using double type
+using DoubleMatrixArray = MatrixArray<double>;
+
+/// Create an alias for MatrixArray using complex type
+using ComplexMatrixArray = MatrixArray<std::complex<double>>;
+
+/*************************************************
+ **  Class MatrixArray inline implementations
+ ************************************************/
+template <class T>
+inline MatrixArray<T>
+MatrixArray<T>::operator*(const T& rhs) const
+{
+    return MatrixArray<T>(m_numRows,
+                          m_numCols,
+                          m_numPages,
+                          m_values * std::valarray<T>(rhs, m_numRows * m_numCols * m_numPages));
+}
+
+template <class T>
+inline MatrixArray<T>
+MatrixArray<T>::operator+(const MatrixArray<T>& rhs) const
+{
+    AssertEqualDims(rhs);
+    return MatrixArray<T>(m_numRows, m_numCols, m_numPages, m_values + rhs.m_values);
+}
+
+template <class T>
+inline MatrixArray<T>
+MatrixArray<T>::operator-(const MatrixArray<T>& rhs) const
+{
+    AssertEqualDims(rhs);
+    return MatrixArray<T>(m_numRows, m_numCols, m_numPages, m_values - rhs.m_values);
+}
+
+template <class T>
+inline MatrixArray<T>
+MatrixArray<T>::operator-() const
+{
+    return MatrixArray<T>(m_numRows, m_numCols, m_numPages, -m_values);
+}
+
+} // namespace ns3
+
+#endif // MATRIX_ARRAY_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/nstime.h ns-3.38/src/core/model/nstime.h
--- ns-3.37/src/core/model/nstime.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/nstime.h	2023-03-16 20:54:23.592868600 -0700
@@ -465,11 +465,11 @@
      * user-provided time values in Time objects and Time objects
      * in user-expected time units.
      */
-    static void SetResolution(enum Unit resolution);
+    static void SetResolution(Unit resolution);
     /**
      * \returns The current global resolution.
      */
-    static enum Unit GetResolution();
+    static Unit GetResolution();
 
     /**
      * Create a Time in the current unit.
@@ -495,7 +495,7 @@
      * \param [in] unit The unit of \pname{value}
      * \return The Time representing \pname{value} in \c unit
      */
-    inline static Time FromInteger(uint64_t value, enum Unit unit)
+    inline static Time FromInteger(uint64_t value, Unit unit)
     {
         struct Information* info = PeekInformation(unit);
 
@@ -512,12 +512,12 @@
         return Time(value);
     }
 
-    inline static Time FromDouble(double value, enum Unit unit)
+    inline static Time FromDouble(double value, Unit unit)
     {
         return From(int64x64_t(value), unit);
     }
 
-    inline static Time From(const int64x64_t& value, enum Unit unit)
+    inline static Time From(const int64x64_t& value, Unit unit)
     {
         struct Information* info = PeekInformation(unit);
 
@@ -551,7 +551,7 @@
      * \param [in] unit The desired unit
      * \return The Time expressed in \pname{unit}
      */
-    inline int64_t ToInteger(enum Unit unit) const
+    inline int64_t ToInteger(Unit unit) const
     {
         struct Information* info = PeekInformation(unit);
 
@@ -569,12 +569,12 @@
         return v;
     }
 
-    inline double ToDouble(enum Unit unit) const
+    inline double ToDouble(Unit unit) const
     {
         return To(unit).GetDouble();
     }
 
-    inline int64x64_t To(enum Unit unit) const
+    inline int64x64_t To(Unit unit) const
     {
         struct Information* info = PeekInformation(unit);
 
@@ -600,7 +600,7 @@
      * \param [in] unit The unit to round to.
      * \return The Time rounded to the specific unit.
      */
-    Time RoundTo(enum Unit unit) const
+    Time RoundTo(Unit unit) const
     {
         return From(this->To(unit).Round(), unit);
     }
@@ -618,7 +618,7 @@
      * \param [in] unit The unit to use.
      * \return The Time with embedded unit.
      */
-    TimeWithUnit As(const enum Unit unit = Time::AUTO) const;
+    TimeWithUnit As(const Unit unit = Time::AUTO) const;
 
     /**
      * TracedCallback signature for Time
@@ -642,8 +642,8 @@
     /** Current time unit, and conversion info. */
     struct Resolution
     {
-        struct Information info[LAST]; //!<  Conversion info from current unit
-        enum Time::Unit unit;          //!<  Current time unit
+        Information info[LAST]; //!<  Conversion info from current unit
+        Time::Unit unit;        //!<  Current time unit
     };
 
     /**
@@ -663,7 +663,7 @@
      *  \param [in] timeUnit The Unit to get Information for
      *  \return The Information for \pname{timeUnit}
      */
-    static inline struct Information* PeekInformation(enum Unit timeUnit)
+    static inline struct Information* PeekInformation(Unit timeUnit)
     {
         return &(PeekResolution()->info[timeUnit]);
     }
@@ -681,9 +681,7 @@
      *  \param [in,out] resolution The Resolution record to update.
      *  \param [in] convert Whether to convert existing Time objects to the new resolution.
      */
-    static void SetResolution(enum Unit unit,
-                              struct Resolution* resolution,
-                              const bool convert = true);
+    static void SetResolution(Unit unit, Resolution* resolution, const bool convert = true);
 
     /**
      *  Record all instances of Time, so we can rescale them when
@@ -762,7 +760,7 @@
      *  Convert existing Times to the new unit.
      *  \param [in] unit The Unit to convert existing Times to.
      */
-    static void ConvertTimes(const enum Unit unit);
+    static void ConvertTimes(const Unit unit);
 
     // Operator and related functions which need access
 
@@ -1060,7 +1058,7 @@
  * Exact division, returning a dimensionless fixed point number.
  *
  * This can be truncated to integer, or converted to double
- * (with loss of precison).  Assuming `ta` and `tb` are Times:
+ * (with loss of precision).  Assuming `ta` and `tb` are Times:
  *
  * \code
  *     int64x64_t ratio = ta / tb;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/object-base.cc ns-3.38/src/core/model/object-base.cc
--- ns-3.37/src/core/model/object-base.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/object-base.cc	2023-03-16 20:54:23.592868600 -0700
@@ -18,17 +18,15 @@
  */
 #include "object-base.h"
 
+#include "assert.h"
 #include "attribute-construction-list.h"
+#include "environment-variable.h"
 #include "log.h"
 #include "string.h"
 #include "trace-source-accessor.h"
 
 #include "ns3/core-config.h"
 
-#include <cstdlib> // getenv
-#include <cstring> // strlen
-#include <unordered_map>
-
 /**
  * \file
  * \ingroup object
@@ -42,64 +40,6 @@
 
 NS_OBJECT_ENSURE_REGISTERED(ObjectBase);
 
-/** Unnamed namespace */
-namespace
-{
-
-/**
- * Get key, value pairs from the "NS_ATTRIBUTE_DEFAULT" environment variable.
- *
- * \param [in] key The key to search for.
- * \return \c true if the key was found, and the associated value.
- */
-std::pair<bool, std::string>
-EnvDictionary(std::string key)
-{
-    static std::unordered_map<std::string, std::string> dict;
-
-    if (dict.size() == 0)
-    {
-        const char* envVar = getenv("NS_ATTRIBUTE_DEFAULT");
-        if (envVar != nullptr && std::strlen(envVar) > 0)
-        {
-            std::string env = envVar;
-            std::string::size_type cur = 0;
-            std::string::size_type next = 0;
-            while (next != std::string::npos)
-            {
-                next = env.find(';', cur);
-                std::string tmp = std::string(env, cur, next - cur);
-                std::string::size_type equal = tmp.find('=');
-                if (equal != std::string::npos)
-                {
-                    std::string name = tmp.substr(0, equal);
-                    std::string envval = tmp.substr(equal + 1, tmp.size() - equal - 1);
-                    dict.insert({name, envval});
-                }
-                cur = next + 1;
-            }
-        }
-        else
-        {
-            // insert an empty key, so we don't do this again
-            dict.insert({"", ""});
-        }
-    }
-
-    std::string value;
-    bool found{false};
-
-    auto loc = dict.find(key);
-    if (loc != dict.end())
-    {
-        value = loc->second;
-        found = true;
-    }
-    return {found, value};
-}
-
-} // unnamed namespace
-
 /**
  * Ensure the TypeId for ObjectBase gets fully configured
  * to anchor the inheritance tree properly.
@@ -137,27 +77,6 @@
     NS_LOG_FUNCTION(this);
 }
 
-/**
- * \def LOG_WHERE_VALUE(where, value)
- * Log where and what value we find for the attribute
- * \param where The source of the value
- * \param value The value found, or "nothing"
- */
-#ifdef NS3_LOG_ENABLE
-#define LOG_WHERE_VALUE(where, value)                                                              \
-    do                                                                                             \
-    {                                                                                              \
-        std::string valStr{"nothing"};                                                             \
-        if (value)                                                                                 \
-        {                                                                                          \
-            valStr = "\"" + value->SerializeToString(info.checker) + "\"";                         \
-        }                                                                                          \
-        NS_LOG_DEBUG(where << " gave " << valStr);                                                 \
-    } while (false)
-#else
-#define LOG_WHERE_VALUE(where, value)
-#endif
-
 void
 ObjectBase::ConstructSelf(const AttributeConstructionList& attributes)
 {
@@ -172,11 +91,10 @@
         {
             struct 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);
             std::string where = "argument";
 
-            LOG_WHERE_VALUE(where, value);
             // See if this attribute should not be set here in the
             // constructor.
             if (!(info.flags & TypeId::ATTR_CONSTRUCT))
@@ -203,25 +121,28 @@
 
             if (!value)
             {
-                auto [found, val] = EnvDictionary(tid.GetAttributeFullName(i));
+                NS_LOG_DEBUG("trying to set from environment variable NS_ATTRIBUTE_DEFAULT");
+                auto [found, val] =
+                    EnvironmentVariable::Get("NS_ATTRIBUTE_DEFAULT", tid.GetAttributeFullName(i));
                 if (found)
                 {
+                    NS_LOG_DEBUG("found in environment: " << val);
                     value = Create<StringValue>(val);
                     where = "env var";
-                    LOG_WHERE_VALUE(where, value);
                 }
             }
 
-            bool initial = false;
+            bool initial{false};
             if (!value)
             {
-                // Set from Tid initialValue, which is guaranteed to exist
+                // This is guaranteed to exist
+                NS_LOG_DEBUG("falling back to initial value from tid");
                 value = info.initialValue;
                 where = "initial value";
                 initial = true;
-                LOG_WHERE_VALUE(where, value);
             }
 
+            // We have a matching attribute value, if only from the initialValue
             if (DoSet(info.accessor, info.checker, *value) || initial)
             {
                 // Setting from initial value may fail, e.g. setting
@@ -230,6 +151,29 @@
                 NS_LOG_DEBUG("construct \"" << tid.GetName() << "::" << info.name << "\" from "
                                             << where);
             }
+            else
+            {
+                /*
+                  One would think this is an error...
+
+                  but there are cases where `attributes.Find(info.checker)`
+                  returns a non-null value which still fails the `DoSet()` call.
+                  For example, `value` is sometimes a real `PointerValue`
+                  containing 0 as the pointed-to address.  Since value
+                  is not null (it just contains null) the initial
+                  value is not used, the DoSet fails, and we end up
+                  here.
+
+                  If we were adventurous we might try to fix this deep
+                  below DoSet, but there be dragons.
+                */
+                /*
+                NS_ASSERT_MSG(false,
+                              "Failed to set attribute '" << info.name << "' from '"
+                                                          << value->SerializeToString(info.checker)
+                                                          << "'");
+                */
+            }
 
         } // for i attributes
         tid = tid.GetParent();
@@ -237,8 +181,6 @@
     NotifyConstructionCompleted();
 }
 
-#undef LOG_WHERE_VALUE
-
 bool
 ObjectBase::DoSet(Ptr<const AttributeAccessor> accessor,
                   Ptr<const AttributeChecker> checker,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/object-base.h ns-3.38/src/core/model/object-base.h
--- ns-3.37/src/core/model/object-base.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/object-base.h	2023-03-16 20:54:23.592868600 -0700
@@ -21,6 +21,7 @@
 
 #include "callback.h"
 #include "type-id.h"
+#include "warnings.h"
 
 #include <list>
 #include <string>
@@ -29,7 +30,7 @@
  * \file
  * \ingroup object
  * ns3::ObjectBase declaration and
- * NS_OBJECT_ENSURE_REGISTERED() madro definition.
+ * NS_OBJECT_ENSURE_REGISTERED() macro definition.
  */
 
 /**
@@ -47,9 +48,11 @@
     {                                                                                              \
         Object##type##RegistrationClass()                                                          \
         {                                                                                          \
+            NS_WARNING_PUSH_DEPRECATED;                                                            \
             ns3::TypeId tid = type::GetTypeId();                                                   \
             tid.SetSize(sizeof(type));                                                             \
             tid.GetParent();                                                                       \
+            NS_WARNING_POP;                                                                        \
         }                                                                                          \
     } Object##type##RegistrationVariable
 
@@ -235,7 +238,7 @@
      */
     void GetAttribute(std::string name, AttributeValue& value) const;
     /**
-     * Get the value of an attribute without raising erros.
+     * Get the value of an attribute without raising errors.
      *
      * If the attribute could not be read this will return \c false,
      * but not raise any errors.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/object-factory.cc ns-3.38/src/core/model/object-factory.cc
--- ns-3.37/src/core/model/object-factory.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/object-factory.cc	2023-03-16 20:54:23.592868600 -0700
@@ -66,7 +66,7 @@
 ObjectFactory::DoSet(const std::string& name, const AttributeValue& value)
 {
     NS_LOG_FUNCTION(this << name << &value);
-    if (name == "")
+    if (name.empty())
     {
         return;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/pointer.h ns-3.38/src/core/model/pointer.h
--- ns-3.37/src/core/model/pointer.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/pointer.h	2023-03-16 20:54:23.592868600 -0700
@@ -136,6 +136,7 @@
         }
         if (!value->GetObject())
         {
+            // a null pointer is a valid value
             return true;
         }
         T* ptr = dynamic_cast<T*>(PeekPointer(value->GetObject()));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/random-variable-stream.cc ns-3.38/src/core/model/random-variable-stream.cc
--- ns-3.37/src/core/model/random-variable-stream.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/random-variable-stream.cc	2023-03-16 20:54:23.592868600 -0700
@@ -102,6 +102,13 @@
     return m_isAntithetic;
 }
 
+uint32_t
+RandomVariableStream::GetInteger()
+{
+    NS_LOG_FUNCTION(this);
+    return static_cast<uint32_t>(GetValue());
+}
+
 void
 RandomVariableStream::SetStream(int64_t stream)
 {
@@ -216,7 +223,7 @@
 UniformRandomVariable::GetInteger()
 {
     NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_min, m_max + 1);
+    return static_cast<uint32_t>(GetValue(m_min, m_max + 1));
 }
 
 NS_OBJECT_ENSURE_REGISTERED(ConstantRandomVariable);
@@ -270,13 +277,6 @@
     return GetValue(m_constant);
 }
 
-uint32_t
-ConstantRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_constant);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(SequentialRandomVariable);
 
 TypeId
@@ -355,7 +355,7 @@
     NS_LOG_FUNCTION(this);
     if (!m_isCurrentSet)
     {
-        // Start the sequence at its minimium value.
+        // Start the sequence at its minimum value.
         m_current = m_min;
         m_isCurrentSet = true;
     }
@@ -374,13 +374,6 @@
     return r;
 }
 
-uint32_t
-SequentialRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue();
-}
-
 NS_OBJECT_ENSURE_REGISTERED(ExponentialRandomVariable);
 
 TypeId
@@ -462,13 +455,6 @@
     return GetValue(m_mean, m_bound);
 }
 
-uint32_t
-ExponentialRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_mean, m_bound);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(ParetoRandomVariable);
 
 TypeId
@@ -568,13 +554,6 @@
     return GetValue(m_scale, m_shape, m_bound);
 }
 
-uint32_t
-ParetoRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_scale, m_shape, m_bound);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(WeibullRandomVariable);
 
 TypeId
@@ -672,13 +651,6 @@
     return GetValue(m_scale, m_shape, m_bound);
 }
 
-uint32_t
-WeibullRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_scale, m_shape, m_bound);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(NormalRandomVariable);
 
 const double NormalRandomVariable::INFINITE_VALUE = 1e307;
@@ -804,13 +776,6 @@
     return GetValue(m_mean, m_variance, m_bound);
 }
 
-uint32_t
-NormalRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_mean, m_variance, m_bound);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(LogNormalRandomVariable);
 
 TypeId
@@ -934,13 +899,6 @@
     return GetValue(m_mu, m_sigma);
 }
 
-uint32_t
-LogNormalRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_mu, m_sigma);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(GammaRandomVariable);
 
 TypeId
@@ -1055,13 +1013,6 @@
     return beta * d * v;
 }
 
-uint32_t
-GammaRandomVariable::GetInteger(uint32_t alpha, uint32_t beta)
-{
-    NS_LOG_FUNCTION(this << alpha << beta);
-    return static_cast<uint32_t>(GetValue(alpha, beta));
-}
-
 double
 GammaRandomVariable::GetValue()
 {
@@ -1069,13 +1020,6 @@
     return GetValue(m_alpha, m_beta);
 }
 
-uint32_t
-GammaRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_alpha, m_beta);
-}
-
 double
 GammaRandomVariable::GetNormalValue(double mean, double variance, double bound)
 {
@@ -1213,13 +1157,6 @@
     return GetValue(m_k, m_lambda);
 }
 
-uint32_t
-ErlangRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_k, m_lambda);
-}
-
 double
 ErlangRandomVariable::GetExponentialValue(double mean, double bound)
 {
@@ -1340,13 +1277,6 @@
     return GetValue(m_mean, m_min, m_max);
 }
 
-uint32_t
-TriangularRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_mean, m_min, m_max);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(ZipfRandomVariable);
 
 TypeId
@@ -1437,13 +1367,6 @@
     return GetValue(m_n, m_alpha);
 }
 
-uint32_t
-ZipfRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_n, m_alpha);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(ZetaRandomVariable);
 
 TypeId
@@ -1525,13 +1448,6 @@
     return GetValue(m_alpha);
 }
 
-uint32_t
-ZetaRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue(m_alpha);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(DeterministicRandomVariable);
 
 TypeId
@@ -1563,7 +1479,13 @@
 }
 
 void
-DeterministicRandomVariable::SetValueArray(double* values, std::size_t length)
+DeterministicRandomVariable::SetValueArray(const std::vector<double>& values)
+{
+    SetValueArray(values.data(), values.size());
+}
+
+void
+DeterministicRandomVariable::SetValueArray(const double* values, std::size_t length)
 {
     NS_LOG_FUNCTION(this << values << length);
     // Delete any values currently set.
@@ -1598,13 +1520,6 @@
     return m_data[m_next++];
 }
 
-uint32_t
-DeterministicRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return (uint32_t)GetValue();
-}
-
 NS_OBJECT_ENSURE_REGISTERED(EmpiricalRandomVariable);
 
 // ValueCDF methods
@@ -1661,13 +1576,6 @@
     return prev;
 }
 
-uint32_t
-EmpiricalRandomVariable::GetInteger()
-{
-    NS_LOG_FUNCTION(this);
-    return static_cast<uint32_t>(GetValue());
-}
-
 bool
 EmpiricalRandomVariable::PreSample(double& value)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/random-variable-stream.h ns-3.38/src/core/model/random-variable-stream.h
--- ns-3.37/src/core/model/random-variable-stream.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/random-variable-stream.h	2023-03-16 20:54:23.592868600 -0700
@@ -143,16 +143,14 @@
     bool IsAntithetic() const;
 
     /**
-     * \brief Get the next random value as a double drawn from the distribution.
-     * \return A floating point random value.
+     * \brief Get the next random value drawn from the distribution.
+     * \return A random value.
      */
     virtual double GetValue() = 0;
 
-    /**
-     * \brief Get the next random value as an integer drawn from the distribution.
-     * \return  An integer random value.
-     */
-    virtual uint32_t GetInteger() = 0;
+    /** \copydoc GetValue() */
+    // The base implementation returns `(uint32_t)GetValue()`
+    virtual uint32_t GetInteger();
 
   protected:
     /**
@@ -181,14 +179,34 @@
  * from a fixed uniform distribution.  It also supports the generation of
  * single random numbers from various uniform distributions.
  *
- * The output range is \f$[min, max)\f$ for floating point values,
- * (\c max <i>excluded</i>), and \f$[min, max]\f$ (\c max <i>included</i>)
+ * The output range is \f$ x \in \f$ [\c Min, \c Max) for floating point values,
+ * (\c Max _excluded_), and \f$ x \in \f$ [\c Min, \c Max] (\c Max _included_)
  * for integral values.
  *
+ * This distribution has mean
+ *
+ *    \f[
+ *       \mu = \frac{\text{Max} + \text{Min}}{2}
+ *    \f]
+ *
+ * and variance
+ *
+ *   \f[
+ *       \sigma^2 = \frac{\left(\text{Max} - \text{Min}\right)^2}{12}
+ *   \f]
+ *
+ * The uniform RNG value \f$x\f$ is generated by
+ *
+ *   \f[
+ *      x = \text{Min} + u (\text{Max} - \text{Min})
+ *   \f]
+ *
+ * where \f$u\f$ is a uniform random variable on [0,1).
+ *
  * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double min = 0.0;
  *   double max = 10.0;
  *
@@ -196,23 +214,20 @@
  *   x->SetAttribute ("Min", DoubleValue (min));
  *   x->SetAttribute ("Max", DoubleValue (max));
  *
- *   // The values returned by a uniformly distributed random
- *   // variable should always be within the range
- *   //
- *   //     [min, max)  .
- *   //
  *   double value = x->GetValue ();
  * \endcode
  *
  * \par Antithetic Values.
  *
- * Normally this RNG returns values \f$x\f$ in the interval \f$[min,max)\f$.
+ * Normally this RNG returns values \f$x\f$ for floating point values
+ * (or \f$k\f$ for integer values) uniformly in the interval [\c Min, \c Max)
+ * (or [\c Min, \c Max] for integer values).
  * If an instance of this RNG is configured to return antithetic values,
  * the actual value returned is calculated as follows:
  *
  *   - Compute the initial random value \f$x\f$ as normal.
- *   - Compute the distance from the maximum, \f$y = max - x\f$
- *   - Return \f$x' = min + y = min + (max - x)\f$:
+ *   - Compute the distance from the maximum, \f$y = \text{Max} - x\f$
+ *   - Return \f$x' = \text{Min} + y = \text{Min} + (\text{Max} - x)\f$:
  */
 class UniformRandomVariable : public RandomVariableStream
 {
@@ -241,40 +256,29 @@
     double GetMax() const;
 
     /**
-     * \brief Get the next random value, as a double in the specified range
-     * \f$[min, max)\f$.
-     *
-     * \note The upper limit is excluded from the output range.
+     * \copydoc GetValue()
      *
      * \param [in] min Low end of the range (included).
      * \param [in] max High end of the range (excluded).
-     * \return A floating point random value.
      */
     double GetValue(double min, double max);
 
     /**
-     * \brief Get the next random value, as an unsigned integer in the
-     * specified range \f$[min, max]\f$.
-     *
-     * \note The upper limit is included in the output range.
-     *
-     * \param [in] min Low end of the range.
-     * \param [in] max High end of the range.
-     * \return A random unsigned integer value.
+     * \copydoc GetValue(double,double)
+     * \note The upper limit is included in the output range, unlike GetValue(double,double).
      */
     uint32_t GetInteger(uint32_t min, uint32_t max);
 
-    // Inherited from RandomVariableStream
+    // Inherited
     /**
-     * \brief Get the next random value as a double drawn from the distribution.
-     * \return A floating point random value.
-     * \note The upper limit is excluded from the output range.
+     * \copydoc RandomVariableStream::GetValue()
+     * \note The upper limit is excluded from the output range, unlike GetInteger().
      */
     double GetValue() override;
+
     /**
-     * \brief Get the next random value as an integer drawn from the distribution.
-     * \return  An integer random value.
-     * \note The upper limit is included in the output range.
+     * \copydoc RandomVariableStream::GetInteger()
+     * \note The upper limit is included in the output range, unlike GetValue().
      */
     uint32_t GetInteger() override;
 
@@ -291,7 +295,9 @@
  * \ingroup randomvariable
  * \brief The Random Number Generator (RNG) that returns a constant.
  *
- * This RNG returns the same value for every sample.
+ * This RNG returns the same \c Constant value for every sample.
+ *
+ * This distribution has mean equal to the \c Constant and zero variance.
  *
  * \par Antithetic Values.
  *
@@ -318,23 +324,21 @@
     double GetConstant() const;
 
     /**
-     * \brief Get the next random value, as a double equal to the argument.
+     * \copydoc GetValue()
      * \param [in] constant The value to return.
-     * \return The floating point argument.
      */
     double GetValue(double constant);
-    /**
-     * \brief Get the next random value, as an integer equal to the argument.
-     * \param [in] constant The value to return.
-     * \return The integer argument.
-     */
+    /** \copydoc GetValue(double) */
     uint32_t GetInteger(uint32_t constant);
 
-    // Inherited from RandomVariableStream
-    /* \note This RNG always returns the same value. */
+    // Inherited
+    /*
+     * \copydoc RandomVariableStream::GetValue()
+     * \note This RNG always returns the same value.
+     */
     double GetValue() override;
     /* \note This RNG always returns the same value. */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The constant value returned by this RNG stream. */
@@ -350,14 +354,31 @@
  * This RNG has four configuration attributes:
  *
  *  - An increment, \c Increment.
- *  - A consecutive repeat numer, \c Consecutive.
+ *  - A consecutive repeat number, \c Consecutive.
  *  - The minimum value, \c Min.
  *  - The maximum value, \c Max.
  *
  * The RNG starts at the \c Min value.  Each return value is
- * repeated \c Consecutive times, before advancing by the \c Increment.
- * When the \c Increment would cause the value to equal or exceed
- * \c Max it is reset to \c Min first.
+ * repeated \c Consecutive times, before advancing by the \c Increment,
+ * modulo the \c Max.  In other words when the \c Increment would cause
+ * the value to equal or exceed \c Max it is reset to \c Min plus the
+ * remainder:
+ *
+ * \code{.cc}
+ *     m_current += m_increment->GetValue();
+ *     if (m_current >= m_max)
+ *     {
+ *         m_current = m_min + (m_current - m_max);
+ *     }
+ * \endcode
+ *
+ * This RNG returns values in the range \f$ x \in [\text{Min}, \text{Max}) \f$.
+ * See the Example, below, for how this executes in practice.
+ *
+ * Note the \c Increment attribute is itself a RandomVariableStream,
+ * which enables more varied patterns than in the example given here.
+ *
+ * \par Example
  *
  * For example, if an instance is configured with:
  *
@@ -368,10 +389,19 @@
  *   Increment   |    4
  *   Consecutive |    3
  *
- * The sequence will repeat this pattern:  2 2 2 6 6 6 10 10 10.
+ * The sequence will return this pattern:
  *
- * Notice that \c Max will be a strict upper bound on the values:
- * all values in the sequence will be less than \c Max.
+ *    \f[
+ *       x \in \\
+ *       \underbrace{  2, 2,  2, }_{\times 3} \\
+ *       \underbrace{ 6,  6,  6, }_{\times 3} \\
+ *       \underbrace{10, 10, 10, }_{\times 3} \\
+ *       \underbrace{ 3,  3,  3, }_{\times 3} \\
+ *       \dots
+ *    \f]
+ *
+ * The last value (3) is the result of the update rule in the code snippet
+ * above, `2 + (14 - 13)`.
  *
  * \par Antithetic Values.
  *
@@ -418,9 +448,9 @@
      */
     uint32_t GetConsecutive() const;
 
-    // Inherited from RandomVariableStream
+    // Inherited
     double GetValue() override;
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The first value of the sequence. */
@@ -456,43 +486,47 @@
  *
  * The probability density function of an exponential variable
  * is defined as:
+ *
  *   \f[
- *       P(x) dx = \alpha  e^{-\alpha x} dx, \quad x \in [0, +\infty)
+ *      P(x; \alpha) dx = \alpha  e^{-\alpha x} dx, \\
+ *          \quad x \in [0, +\infty)
  *   \f]
- * over the interval \f$[0, +\infty)\f$, where \f$ \alpha = \frac{1}{Mean} \f$
- * and \c Mean is a configurable attribute.
  *
- * The normal RNG value \f$x\f$ is calculated by
+ * where \f$ \alpha = \frac{1}{\mu} \f$
+ * and \f$\mu\f$ is the \c Mean configurable attribute.  This distribution
+ * has variance \f$ \sigma^2 = \alpha^2 \f$.
+ *
+ * The exponential RNG value \f$x\f$ is generated by
  *
  *   \f[
- *       x = - 1/\alpha \log(u)
+ *      x = - \frac{\log(u)}{\alpha} = - \text{Mean} \log(u)
  *   \f]
  *
- * where \f$u\f$ is a uniform random variable on \f$[0,1)\f$.
+ * where \f$u\f$ is a uniform random variable on [0,1).
  *
  * \par Bounded Distribution
  *
- * Since exponential distributions can theoretically return unbounded
- * values, it is sometimes useful to specify a fixed upper limit.  The
+ * Since the exponential distributions can theoretically return unbounded
+ * values, it is sometimes useful to specify a fixed upper \c Bound.  The
  * bounded version is defined over the interval \f$[0,b]\f$ as:
  *
  *   \f[
- *       P(x; b) dx = \alpha e^{-\alpha x} dx \quad x \in [0,b]
+ *      P(x; \alpha, b) dx = \alpha  e^{-\alpha x} dx, \\
+ *          \quad x \in [0, b]
  *   \f]
  *
- * where the \c Bound \f$b\f$ is a configurable attribute.
- *
  * Note that in this case the true mean of the distribution is smaller
  * than the nominal mean value:
  *
  *   \f[
- *       <X: P(x; b)> = 1/\alpha - b/(e^{\alpha \, b} -1)
+ *      \langle x | P(x; \alpha, b) \rangle = \frac{1}{\alpha} -
+ *          \left(\frac{1}{\alpha} + b\right)\exp^{-\alpha b}
  *   \f]
  *
  * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double mean = 3.14;
  *   double bound = 0.0;
  *
@@ -500,20 +534,19 @@
  *   x->SetAttribute ("Mean", DoubleValue (mean));
  *   x->SetAttribute ("Bound", DoubleValue (bound));
  *
- *   // The expected value for the mean of the values returned by an
- *   // exponentially distributed random variable is equal to mean.
  *   double value = x->GetValue ();
  * \endcode
  *
  * \par Antithetic Values.
  *
- * The antithetic value is calculated from
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated as follows:
  *
  *   \f[
- *       x' = - mean * \log(1 - u),
+ *      x' = - \frac{\log(1 - u)}{\alpha} = - Mean \log(1 - u),
  *   \f]
  *
- * where again \f$u\f$ is a uniform random variable on \f$[0,1)\f$.
+ * where \f$u\f$ is a uniform random variable on [0,1).
  */
 class ExponentialRandomVariable : public RandomVariableStream
 {
@@ -546,26 +579,18 @@
     double GetBound() const;
 
     /**
-     * \brief Get the next random value, as a double from
-     * the exponential distribution with the specified mean and upper bound.
+     * \copydoc GetValue()
      * \param [in] mean Mean value of the unbounded exponential distribution.
      * \param [in] bound Upper bound on values returned.
-     * \return A floating point random value.
      */
     double GetValue(double mean, double bound);
 
-    /**
-     * \brief Get the next random value, as an unsigned integer from
-     * the exponential distribution with the specified mean and upper bound.
-     * \param [in] mean Mean value of the unbounded exponential distribution.
-     * \param [in] bound Upper bound on values returned.
-     * \return A random unsigned integer value.
-     */
+    /** \copydoc GetValue(double,double) */
     uint32_t GetInteger(uint32_t mean, uint32_t bound);
 
-    // Inherited from RandomVariableStream
+    // Inherited
     double GetValue() override;
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The mean value of the unbounded exponential distribution. */
@@ -584,21 +609,56 @@
  * from a fixed Pareto distribution.  It also supports the generation of
  * single random numbers from various Pareto distributions.
  *
- * The probability density function of a Pareto variable is defined
- * over the range [\f$x_m\f$,\f$+\infty\f$) as: \f$ k \frac{x_m^k}{x^{k+1}}\f$
- * where \f$x_m > 0\f$ is called the scale parameter and \f$ k > 0\f$
- * is called the Pareto index or shape.
- *
- * The parameter \f$ x_m \f$ can be inferred from the mean and the parameter \f$ k \f$
- * with the equation \f$ x_m = mean \frac{k-1}{k},  k > 1\f$.
- *
- * Since Pareto distributions can theoretically return unbounded values,
- * it is sometimes useful to specify a fixed upper limit.  Note however
- * when the upper limit is specified, the true mean of the distribution
- * is slightly smaller than the mean value specified.
+ * The probability density function of a Pareto variable is:
+ *
+ *   \f[
+ *      P(x; x_m, \alpha) dx = \alpha \frac{x_m^\alpha}{x^{\alpha + 1}} dx, \\
+ *          \quad x \in [x_m, +\infty)
+ *   \f]
+ *
+ * where the minimum value \f$x_m > 0\f$ is called the \c Scale parameter
+ * and \f$ \alpha > 0\f$ is called the Pareto index or \c Shape parameter.
+ *
+ * The resulting distribution will have mean
+ *
+ *   \f[
+ *      \mu = x_m \frac{\alpha}{\alpha - 1} \mbox{ for $\alpha > 1$}
+ *   \f]
+ *
+ * and variance
+ *
+ *   \f[
+ *      \sigma^2 = x_m \frac{1}{(\alpha - 1)(\alpha - 2)} \mbox { for $\alpha > 2$}
+ *   \f]
+ *
+ * The minimum value \f$x_m\f$ can be inferred from the desired mean \f$\mu\f$
+ * and the parameter \f$\alpha\f$ with the equation
+ *
+ *   \f[
+ *      x_m = \mu \frac{\alpha - 1}{\alpha} \mbox{ for $\alpha > 1$}
+ *   \f]
+ *
+ * The Pareto RNG value \f$x\f$ is generated by
+ *
+ *   \f[
+ *      x = \frac{x_m}{u^{\frac{1}{\alpha}}}
+ *   \f]
+ *
+ * where \f$u\f$ is a uniform random variable on [0,1).
+ *
+ * \par Bounded Distribution
+ *
+ * Since Pareto distributions can theoretically return unbounded
+ * values, it is sometimes useful to specify a fixed upper \c Bound.  The
+ * bounded version is defined over the interval \f$ x \in [x_m, b] \f$.
+ * Note however when the upper limit is specified, the mean
+ * of the resulting distribution is slightly smaller than the mean value
+ * in the unbounded case.
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double scale = 5.0;
  *   double shape = 2.0;
  *
@@ -606,15 +666,19 @@
  *   x->SetAttribute ("Scale", DoubleValue (scale));
  *   x->SetAttribute ("Shape", DoubleValue (shape));
  *
- *   // The expected value for the mean of the values returned by a
- *   // Pareto distributed random variable is
- *   //
- *   //                   shape * scale
- *   //     E[value]  =  ---------------  ,
- *   //                     shape - 1
- *
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated as follows:
+ *
+ *   \f[
+ *      x' = \frac{x_m}{{(1 - u)}^{\frac{1}{\alpha}}} ,
+ *   \f]
+ *
+ * which now involves the distance \f$u\f$ is from 1 in the denominator.
  */
 class ParetoRandomVariable : public RandomVariableStream
 {
@@ -650,123 +714,19 @@
     double GetBound() const;
 
     /**
-     * \brief Returns a random double from a Pareto distribution with the specified scale, shape,
-     * and upper bound.
+     * \copydoc GetValue()
      * \param [in] scale Mean parameter for the Pareto distribution.
      * \param [in] shape Shape parameter for the Pareto distribution.
      * \param [in] bound Upper bound on values returned.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = \frac{scale}{u^{\frac{1}{shape}}}
-     *    \f]
-     *
-     * is a value that would be returned normally.
-     *
-     * The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *         x' = \frac{scale}{{(1 - u)}^{\frac{1}{shape}}} ,
-     *    \f]
-     *
-     * which now involves the distance \f$u\f$ is from 1 in the denominator.
      */
     double GetValue(double scale, double shape, double bound);
 
-    /**
-     * \brief Returns a random unsigned integer from a Pareto distribution with the specified mean,
-     * shape, and upper bound.
-     * \param [in] scale Scale parameter for the Pareto distribution.
-     * \param [in] shape Shape parameter for the Pareto distribution.
-     * \param [in] bound Upper bound on values returned.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = \frac{scale}{u^{\frac{1}{shape}}}
-     *    \f]
-     *
-     * is a value that would be returned normally.
-     *
-     * The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *         x' = \frac{scale}{{(1 - u)}^{\frac{1}{shape}}} ,
-     *    \f]
-     *
-     * which now involves the distance \f$u\f$ is from 1 in the denominator.
-     */
+    /** \copydoc GetValue(double,double,double) */
     uint32_t GetInteger(uint32_t scale, uint32_t shape, uint32_t bound);
 
-    /**
-     * \brief Returns a random double from a Pareto distribution with the current mean, shape, and
-     * upper bound.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = \frac{scale}{u^{\frac{1}{shape}}}
-     *    \f]
-     *
-     * is a value that would be returned normally, where
-     *
-     *    \f[
-     *         scale  =  mean * (shape - 1.0) / shape  .
-     *    \f]
-     *
-     * The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *         x' = \frac{scale}{{(1 - u)}^{\frac{1}{shape}}} ,
-     *    \f]
-     *
-     * which now involves the distance \f$u\f$ is from 1 in the denominator.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the three-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a Pareto distribution with the current mean,
-     * shape, and upper bound.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = \frac{scale}{u^{\frac{1}{shape}}}
-     *    \f]
-     *
-     * is a value that would be returned normally.
-     *
-     * The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *         x' = \frac{scale}{{(1 - u)}^{\frac{1}{shape}}} ,
-     *    \f]
-     *
-     * which now involves the distance \f$u\f$ is from 1 in the denominator.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The scale parameter for the Pareto distribution returned by this RNG stream. */
@@ -782,28 +742,70 @@
 
 /**
  * \ingroup randomvariable
- * \brief The Weibull distribution Random Number Generator (RNG) that allows stream numbers to be
- * set deterministically.
+ * \brief The Weibull distribution Random Number Generator (RNG)
+ * which allows stream numbers to be set deterministically.
  *
  * This class supports the creation of objects that return random numbers
  * from a fixed Weibull distribution.  It also supports the generation of
  * single random numbers from various Weibull distributions.
  *
- * The probability density function is defined over the interval [0, \f$+\infty\f$]
- * as: \f$
- * \frac{k}{\lambda}\left(\frac{x}{\lambda}\right)^{k-1}e^{-\left(\frac{x}{\lambda}\right)^k} \f$
- * where \f$ k > 0\f$ is the shape parameter and \f$ \lambda > 0\f$  is the scale parameter. The
- * specified mean is related to the scale and shape parameters by the following relation:
- * \f$ mean = \lambda\Gamma\left(1+\frac{1}{k}\right) \f$ where \f$ \Gamma \f$ is the Gamma
- * function.
+ * The probability density function is:
+ *
+ *   \f[
+ *      P(x; \lambda, k) dx = \frac{k}{\lambda} \\
+ *          \left(\frac{x}{\lambda}\right)^{k-1} \\
+ *          e^{-\left(\frac{x}{\lambda}\right)^k} dx, \\
+ *          \quad x \in [0, +\infty)
+ *   \f]
+ *
+ * where \f$ k > 0\f$ is the \c Shape parameter and \f$ \lambda > 0\f$
+ * is the \c Scale parameter.
+ *
+ * The mean \f$\mu\f$ is related to the \c Scale and \c Shape parameters
+ * by the following relation:
+ *
+ *   \f[
+ *      \mu = \lambda\Gamma\left(1+\frac{1}{k}\right)
+ *   \f]
+ *
+ * where \f$ \Gamma \f$ is the Gamma function.
+ *
+ * The variance of the distribution is
+ *
+ *   \f[
+ *      \sigma^2 = \lambda^2 \left[ \Gamma\left(1 + \frac{2}{k}\right) - \\
+ *                                  \left( \Gamma\left(1 + \frac{1}{k}\right)\right)^2
+ *                           \right]
+ *   \f]
+ *
+ * For \f$ k > 1 \f$ the mean rapidly approaches just \f$\lambda\f$,
+ * with variance
+ *
+ *   \f[
+ *      \sigma^2 \approx \frac{\pi^2}{6 k^2} + \mathcal{O}(k^{-3})
+ *   \f]
+ *
+ * The Weibull RNG value \f$x\f$ is generated by
+ *
+ *   \f[
+ *      x = \lambda {(-\log(u))}^{\frac{1}{k}}
+ *   \f]
+ *
+ * where \f$u\f$ is a uniform random variable on [0,1).
+ *
+ * \par Bounded Distribution
  *
  * Since Weibull distributions can theoretically return unbounded values,
- * it is sometimes useful to specify a fixed upper limit.  Note however
- * when the upper limit is specified, the true mean of the distribution
- * is slightly smaller than the mean value specified.
+ * it is sometimes useful to specify a fixed upper limit.    The
+ * bounded version is defined over the interval \f$ x \in [0, b] \f$.
+ * Note however when the upper limit is specified, the mean of the
+ * resulting distribution is slightly smaller than the mean value
+ * in the unbounded case.
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double scale = 5.0;
  *   double shape = 1.0;
  *
@@ -811,30 +813,19 @@
  *   x->SetAttribute ("Scale", DoubleValue (scale));
  *   x->SetAttribute ("Shape", DoubleValue (shape));
  *
- *   // The expected value for the mean of the values returned by a
- *   // Weibull distributed random variable is
- *   //
- *   //     E[value]  =  scale * Gamma(1 + 1 / shape)  ,
- *   //
- *   // where Gamma() is the Gamma function.  Note that
- *   //
- *   //     Gamma(n)  =  (n - 1)!
- *   //
- *   // if n is a positive integer.
- *   //
- *   // For this example,
- *   //
- *   //     Gamma(1 + 1 / shape)  =  Gamma(1 + 1 / 1)
- *   //                           =  Gamma(2)
- *   //                           =  (2 - 1)!
- *   //                           =  1
- *   //
- *   // which means
- *   //
- *   //     E[value]  =  scale  .
- *   //
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated as follows:
+ *
+ *   \f[
+ *      x' = \lambda {(-\log(1 - u))}^{\frac{1}{k}} ,
+ *   \f]
+ *
+ * which now involves the log of the distance \f$u\f$ is from 1.
  */
 class WeibullRandomVariable : public RandomVariableStream
 {
@@ -870,115 +861,19 @@
     double GetBound() const;
 
     /**
-     * \brief Returns a random double from a Weibull distribution with the specified scale, shape,
-     * and upper bound.
+     * \copydoc GetValue()
      * \param [in] scale Scale parameter for the Weibull distribution.
      * \param [in] shape Shape parameter for the Weibull distribution.
      * \param [in] bound Upper bound on values returned.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = scale * {(-\log(u))}^{\frac{1}{shape}}
-     *    \f]
-     *
-     * is a value that would be returned normally, then \f$(1 - u\f$) is
-     * the distance that \f$u\f$ would be from \f$1\f$.  The value
-     * returned in the antithetic case, \f$x'\f$, is calculated as
-     *
-     *    \f[
-     *         x' = scale * {(-\log(1 - u))}^{\frac{1}{shape}} ,
-     *    \f]
-     *
-     * which now involves the log of the distance \f$u\f$ is from 1.
      */
     double GetValue(double scale, double shape, double bound);
 
-    /**
-     * \brief Returns a random unsigned integer from a Weibull distribution with the specified
-     * scale, shape, and upper bound.
-     * \param [in] scale Scale parameter for the Weibull distribution.
-     * \param [in] shape Shape parameter for the Weibull distribution.
-     * \param [in] bound Upper bound on values returned.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = scale * {(-\log(u))}^{\frac{1}{shape}}
-     *    \f]
-     *
-     * is a value that would be returned normally, then \f$(1 - u\f$) is
-     * the distance that \f$u\f$ would be from \f$1\f$.  The value
-     * returned in the antithetic case, \f$x'\f$, is calculated as
-     *
-     *    \f[
-     *         x' = scale * {(-\log(1 - u))}^{\frac{1}{shape}} ,
-     *    \f]
-     *
-     * which now involves the log of the distance \f$u\f$ is from 1.
-     */
+    /** \copydoc GetValue(double,double,double) */
     uint32_t GetInteger(uint32_t scale, uint32_t shape, uint32_t bound);
 
-    /**
-     * \brief Returns a random double from a Weibull distribution with the current scale, shape, and
-     * upper bound.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = scale * {(-\log(u))}^{\frac{1}{shape}}
-     *    \f]
-     *
-     * is a value that would be returned normally, then \f$(1 - u\f$) is
-     * the distance that \f$u\f$ would be from \f$1\f$.  The value
-     * returned in the antithetic case, \f$x'\f$, is calculated as
-     *
-     *    \f[
-     *         x' = scale * {(-\log(1 - u))}^{\frac{1}{shape}} ,
-     *    \f]
-     *
-     * which now involves the log of the distance \f$u\f$ is from 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the three-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a Weibull distribution with the current scale,
-     * shape, and upper bound.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = scale * {(-\log(u))}^{\frac{1}{shape}}
-     *    \f]
-     *
-     * is a value that would be returned normally, then \f$(1 - u\f$) is
-     * the distance that \f$u\f$ would be from \f$1\f$.  The value
-     * returned in the antithetic case, \f$x'\f$, is calculated as
-     *
-     *    \f[
-     *         x' = scale * {(-\log(1 - u))}^{\frac{1}{shape}} ,
-     *    \f]
-     *
-     * which now involves the log of the distance \f$u\f$ is from 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The scale parameter for the Weibull distribution returned by this RNG stream. */
@@ -1001,18 +896,45 @@
  * from a fixed normal distribution.  It also supports the generation of
  * single random numbers from various normal distributions.
  *
- * The density probability function is defined over the interval (\f$-\infty\f$,\f$+\infty\f$)
- * as: \f$ \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{(x-\mu)^2}{s\sigma^2}}\f$
- * where \f$ mean = \mu \f$ and \f$ variance = \sigma^2 \f$
+ * The probability density function is:
+ *
+ *   \f[
+ *      P(x; \mu, \sigma) dx = \frac{1}{\sqrt{2\pi\sigma^2}}
+ *          e^{-\frac{(x-\mu)^2}{2\sigma^2}} dx, \\
+ *          \quad x \in (-\infty, +\infty)
+ *   \f]
+ *
+ * where the \c Mean is given by \f$\mu\f$ and the \c Variance is \f$\sigma^2\f$
+ *
+ * If \f$u_1\f$, \f$u_2\f$ are uniform variables over [0,1]
+ * then the Gaussian RNG values, \f$x_1\f$ and \f$x_2\f$, are
+ * calculated as follows:
+ *
+ *   \f{eqnarray*}{
+ *      v_1 & = & 2 u_1 - 1     \\
+ *      v_2 & = & 2 u_2 - 1     \\
+ *      r^2 & = & v_1^2 + v_2^2     \\
+ *      y & = & \sqrt{\frac{-2 \log(r^2)}{r^2}}     \\
+ *      x_1 & = & \mu + v_1 y \sqrt{\sigma^2}     \\
+ *      x_2 & = & \mu + v_2 y \sqrt{\sigma^2}  .
+ *   \f}
+ *
+ * Note this algorithm consumes two uniform random values and produces
+ * two normally distributed values.  The implementation used here
+ * caches \f$y\f$ and \f$v_2\f$ to generate \f$x_2\f$ on the next call.
+ *
+ * \par Bounded Distribution
  *
  * Since normal distributions can theoretically return unbounded
  * values, it is sometimes useful to specify a fixed bound.  The
  * NormalRandomVariable is bounded symmetrically about the mean by
- * this bound, i.e. its values are confined to the interval
- * [\f$mean-bound\f$,\f$mean+bound\f$].
+ * the \c Bound parameter, \f$b\f$, _i.e._ its values are confined to the interval
+ * \f$[\mu - b, \mu + b]\f$.  This preserves the mean but decreases the variance.
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double mean = 5.0;
  *   double variance = 2.0;
  *
@@ -1024,6 +946,23 @@
  *   // normally distributed random variable is equal to mean.
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual values returned, \f$x_1^{\prime}\f$ and \f$x_2^{\prime}\f$,
+ * are calculated as follows:
+ *
+ *   \f{eqnarray*}{
+ *      v_1^{\prime} & = & 2 (1 - u_1) - 1     \\
+ *      v_2^{\prime} & = & 2 (1 - u_2) - 1     \\
+ *      r^{\prime 2} & = & v_1^{\prime 2}  + v_2^{\prime 2}     \\
+ *      y^{\prime} & = & \sqrt{\frac{-2 \log(r^{\prime 2})}{r^{\prime 2}}}     \\
+ *      x_1^{\prime} & = & \mu + v_1^{\prime} y^{\prime} \sqrt{\sigma^2}     \\
+ *      x_2^{\prime} & = & \mu + v_2^{\prime} y^{\prime} \sqrt{\sigma^2}  ,
+ *   \f}
+ *
+ * which now involves the distances \f$u_1\f$ and \f$u_2\f$ are from 1.
  */
 class NormalRandomVariable : public RandomVariableStream
 {
@@ -1062,165 +1001,21 @@
     double GetBound() const;
 
     /**
-     * \brief Returns a random double from a normal distribution with the specified mean, variance,
-     * and bound.
+     * \copydoc GetValue()
      * \param [in] mean Mean value for the normal distribution.
      * \param [in] variance Variance value for the normal distribution.
      * \param [in] bound Bound on values returned.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the values that would be returned normally, \f$x1\f$ and \f$x2\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & 2 * u1 - 1     \\
-     *         v2 & = & 2 * u2 - 1     \\
-     *         w & = & v1 * v1 + v2 * v2     \\
-     *         y & = & \sqrt{\frac{-2 * \log(w)}{w}}     \\
-     *         x1 & = & mean + v1 * y * \sqrt{variance}     \\
-     *         x2 & = & mean + v2 * y * \sqrt{variance}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic values returned, \f$x1'\f$ and \f$x2'\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & 2 * (1 - u1) - 1     \\
-     *         v2' & = & 2 * (1 - u2) - 1     \\
-     *         w' & = & v1' * v1' + v2' * v2'     \\
-     *         y' & = & \sqrt{\frac{-2 * \log(w')}{w'}}     \\
-     *         x1' & = & mean + v1' * y' * \sqrt{variance}     \\
-     *         x2' & = & mean + v2' * y' * \sqrt{variance}  ,
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
      */
     double GetValue(double mean,
                     double variance,
                     double bound = NormalRandomVariable::INFINITE_VALUE);
 
-    /**
-     * \brief Returns a random unsigned integer from a normal distribution with the specified mean,
-     * variance, and bound.
-     * \param [in] mean Mean value for the normal distribution.
-     * \param [in] variance Variance value for the normal distribution.
-     * \param [in] bound Bound on values returned.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the values that would be returned normally, \f$x1\f$ and \f$x2\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & 2 * u1 - 1     \\
-     *         v2 & = & 2 * u2 - 1     \\
-     *         w & = & v1 * v1 + v2 * v2     \\
-     *         y & = & \sqrt{\frac{-2 * \log(w)}{w}}     \\
-     *         x1 & = & mean + v1 * y * \sqrt{variance}     \\
-     *         x2 & = & mean + v2 * y * \sqrt{variance}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic values returned, \f$x1'\f$ and \f$x2'\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & 2 * (1 - u1) - 1     \\
-     *         v2' & = & 2 * (1 - u2) - 1     \\
-     *         w' & = & v1' * v1' + v2' * v2'     \\
-     *         y' & = & \sqrt{\frac{-2 * \log(w')}{w'}}     \\
-     *         x1' & = & mean + v1' * y' * \sqrt{variance}     \\
-     *         x2' & = & mean + v2' * y' * \sqrt{variance}  ,
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
-     */
+    /** \copydoc GetValue(double,double,double) */
     uint32_t GetInteger(uint32_t mean, uint32_t variance, uint32_t bound);
 
-    /**
-     * \brief Returns a random double from a normal distribution with the current mean, variance,
-     * and bound.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the values that would be returned normally, \f$x1\f$ and \f$x2\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & 2 * u1 - 1     \\
-     *         v2 & = & 2 * u2 - 1     \\
-     *         w & = & v1 * v1 + v2 * v2     \\
-     *         y & = & \sqrt{\frac{-2 * \log(w)}{w}}     \\
-     *         x1 & = & mean + v1 * y * \sqrt{variance}     \\
-     *         x2 & = & mean + v2 * y * \sqrt{variance}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic values returned, \f$x1'\f$ and \f$x2'\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & 2 * (1 - u1) - 1     \\
-     *         v2' & = & 2 * (1 - u2) - 1     \\
-     *         w' & = & v1' * v1' + v2' * v2'     \\
-     *         y' & = & \sqrt{\frac{-2 * \log(w')}{w'}}     \\
-     *         x1' & = & mean + v1' * y' * \sqrt{variance}     \\
-     *         x2' & = & mean + v2' * y' * \sqrt{variance}  ,
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the three-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a normal distribution with the current mean,
-     * variance, and bound.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the values that would be returned normally, \f$x1\f$ and \f$x2\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & 2 * u1 - 1     \\
-     *         v2 & = & 2 * u2 - 1     \\
-     *         w & = & v1 * v1 + v2 * v2     \\
-     *         y & = & \sqrt{\frac{-2 * \log(w)}{w}}     \\
-     *         x1 & = & mean + v1 * y * \sqrt{variance}     \\
-     *         x2 & = & mean + v2 * y * \sqrt{variance}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic values returned, \f$x1'\f$ and \f$x2'\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & 2 * (1 - u1) - 1     \\
-     *         v2' & = & 2 * (1 - u2) - 1     \\
-     *         w' & = & v1' * v1' + v2' * v2'     \\
-     *         y' & = & \sqrt{\frac{-2 * \log(w')}{w'}}     \\
-     *         x1' & = & mean + v1' * y' * \sqrt{variance}     \\
-     *         x2' & = & mean + v2' * y' * \sqrt{variance}  ,
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The mean value for the normal distribution returned by this RNG stream. */
@@ -1247,27 +1042,66 @@
  * \brief The log-normal distribution Random Number Generator
  * (RNG) that allows stream numbers to be set deterministically.
  *
- * This class supports the creation of objects that return random numbers
- * from a fixed log-normal distribution.  It also supports the generation of
- * single random numbers from various log-normal distributions.
- *
- * LogNormalRandomVariable defines a random variable with a log-normal
- * distribution.  If one takes the natural logarithm of random
+ * This class supports the creation of objects that return random
+ * numbers from a fixed log-normal distribution.  It also supports the
+ * generation of single random numbers from various log-normal
+ * distributions.  If one takes the natural logarithm of a random
  * variable following the log-normal distribution, the obtained values
  * follow a normal distribution.
  *
- * The probability density function is defined over the interval [0,\f$+\infty\f$) as:
- * \f$ \frac{1}{x\sigma\sqrt{2\pi}} e^{-\frac{(ln(x) - \mu)^2}{2\sigma^2}}\f$
- * where \f$ mean = e^{\mu+\frac{\sigma^2}{2}} \f$ and
- * \f$ variance = (e^{\sigma^2}-1)e^{2\mu+\sigma^2}\f$
- *
- * The \f$ \mu \f$ and \f$ \sigma \f$ parameters can be calculated instead if
- * the mean and variance are known with the following equations:
- * \f$ \mu = ln(mean) - \frac{1}{2}ln\left(1+\frac{variance}{mean^2}\right)\f$, and,
- * \f$ \sigma = \sqrt{ln\left(1+\frac{variance}{mean^2}\right)}\f$
+ * The probability density function is defined using two parameters
+ * \c Mu = \f$\mu\f$ and \c Sigma = \f$\sigma\f$ as:
+ *
+ *   \f[
+ *      P(x; \mu, \sigma) dx = \frac{1}{x\sqrt{2\pi\sigma^2}}
+ *          e^{-\frac{(ln(x) - \mu)^2}{2\sigma^2}} dx, \\
+ *          \quad x \in [0, +\infty)
+ *   \f]
+ *
+ * The distribution has mean value
+ *
+ *   \f[
+ *      \langle x | P(x; \mu, \sigma) \rangle = e^{\mu+\frac{\sigma^2}{2}}
+ *   \f]
+ *
+ * and variance
+ *
+ *   \f[
+ *      var(x) = (e^{\sigma^2}-1)e^{2\mu+\sigma^2}
+ *   \f]
+ *
+ * Note these are the mean and variance of the log-normal distribution,
+ * not the \c Mu or \c Sigma configuration variables.
+ *
+ * If the desired mean and variance are known the \f$\mu\f$ and \f$\sigma\f$
+ * parameters can be calculated instead with the following equations:
+ *
+ *   \f[
+ *      \mu = ln(\langle x \rangle) - \\
+ *            \frac{1}{2}ln\left(1+\frac{var(x)}{{\langle x \rangle}^2}\right)
+ *   \f]
+ *
+ * and
+ *
+ *   \f[
+ *      \sigma^2 = ln\left(1+\frac{var(x)}{{\langle x \rangle}^2}\right)
+ *   \f]
+ *
+ * If \f$u_1\f$, \f$u_2\f$ are uniform variables over [0,1]
+ * then the log-normal RNG value, \f$x\f$ is generated as follows:
+ *
+ *   \f{eqnarray*}{
+ *      v_1 & = & 2 u_1 - 1     \\
+ *      v_2 & = & 2 u_2 - 1     \\
+ *      r^2 & = & v_1^2 + v_2^2     \\
+ *      y   & = & \sqrt{\frac{-2 \log{r^2}}{r^2}}       \\
+ *      x   & = &  \exp\left(\mu + v_1 y \sigma\right)  .
+ *   \f}
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double mu = 5.0;
  *   double sigma = 2.0;
  *
@@ -1275,15 +1109,23 @@
  *   x->SetAttribute ("Mu", DoubleValue (mu));
  *   x->SetAttribute ("Sigma", DoubleValue (sigma));
  *
- *   // The expected value for the mean of the values returned by a
- *   // log-normally distributed random variable is equal to
- *   //
- *   //                             2
- *   //                   mu + sigma  / 2
- *   //     E[value]  =  e                 .
- *   //
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated as follows:
+ *
+ *   \f{eqnarray*}{
+ *      v_1^{\prime} & = & 2 (1 - u_1) - 1     \\
+ *      v_2^{\prime} & = & 2 (1 - u_2) - 1     \\
+ *      r^{\prime 2} & = & v_1^{\prime 2}  + v_2^{\prime 2}     \\
+ *      y^{\prime} & = & v_1^{\prime}\sqrt{\frac{-2 \log(r^{\prime 2})}{r^{\prime 2}}}     \\
+ *      x^{\prime} & = & \exp\left(\mu + y^{\prime} \sigma\right)  .
+ *   \f}
+ *
+ * which now involves the distances \f$u_1\f$ and \f$u_2\f$ are from 1.
  */
 class LogNormalRandomVariable : public RandomVariableStream
 {
@@ -1313,152 +1155,18 @@
     double GetSigma() const;
 
     /**
-     * \brief Returns a random double from a log-normal distribution with the specified mu and
-     * sigma.
+     * \copydoc GetValue()
      * \param [in] mu Mu value for the log-normal distribution.
      * \param [in] sigma Sigma value for the log-normal distribution.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the value that would be returned normally, \f$x\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & -1 + 2 * u1       \\
-     *         v2 & = & -1 + 2 * u2       \\
-     *         r2 & = & v1 * v1 + v2 * v2       \\
-     *         normal & = & v1 * \sqrt{\frac{-2.0 * \log{r2}}{r2}}       \\
-     *         x & = &  \exp{sigma * normal + mu}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic value returned, \f$x'\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & -1 + 2 * (1 - u1)       \\
-     *         v2' & = & -1 + 2 * (1 - u2)       \\
-     *         r2' & = & v1' * v1' + v2' * v2'       \\
-     *         normal' & = & v1' * \sqrt{\frac{-2.0 * \log{r2'}}{r2'}}       \\
-     *         x' & = &  \exp{sigma * normal' + mu}  .
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
      */
     double GetValue(double mu, double sigma);
 
-    /**
-     * \brief Returns a random unsigned integer from a log-normal distribution with the specified mu
-     * and sigma.
-     * \param [in] mu Mu value for the log-normal distribution.
-     * \param [in] sigma Sigma value for the log-normal distribution.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the value that would be returned normally, \f$x\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & -1 + 2 * u1       \\
-     *         v2 & = & -1 + 2 * u2       \\
-     *         r2 & = & v1 * v1 + v2 * v2       \\
-     *         normal & = & v1 * \sqrt{\frac{-2.0 * \log{r2}}{r2}}       \\
-     *         x & = &  \exp{sigma * normal + mu}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic value returned, \f$x'\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & -1 + 2 * (1 - u1)       \\
-     *         v2' & = & -1 + 2 * (1 - u2)       \\
-     *         r2' & = & v1' * v1' + v2' * v2'       \\
-     *         normal' & = & v1' * \sqrt{\frac{-2.0 * \log{r2'}}{r2'}}       \\
-     *         x' & = &  \exp{sigma * normal' + mu}  .
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
-     */
+    /** \copydoc GetValue(double,double) */
     uint32_t GetInteger(uint32_t mu, uint32_t sigma);
 
-    /**
-     * \brief Returns a random double from a log-normal distribution with the current mu and sigma.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the value that would be returned normally, \f$x\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & -1 + 2 * u1       \\
-     *         v2 & = & -1 + 2 * u2       \\
-     *         r2 & = & v1 * v1 + v2 * v2       \\
-     *         normal & = & v1 * \sqrt{\frac{-2.0 * \log{r2}}{r2}}       \\
-     *         x & = &  \exp{sigma * normal + mu}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic value returned, \f$x'\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & -1 + 2 * (1 - u1)       \\
-     *         v2' & = & -1 + 2 * (1 - u2)       \\
-     *         r2' & = & v1' * v1' + v2' * v2'       \\
-     *         normal' & = & v1' * \sqrt{\frac{-2.0 * \log{r2'}}{r2'}}       \\
-     *         x' & = &  \exp{sigma * normal' + mu}  .
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the two-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a log-normal distribution with the current mu
-     * and sigma.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the value that would be returned normally, \f$x\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & -1 + 2 * u1       \\
-     *         v2 & = & -1 + 2 * u2       \\
-     *         r2 & = & v1 * v1 + v2 * v2       \\
-     *         normal & = & v1 * \sqrt{\frac{-2.0 * \log{r2}}{r2}}       \\
-     *         x & = &  \exp{sigma * normal + mu}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic value returned, \f$x'\f$, is calculated as
-     * follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & -1 + 2 * (1 - u1)       \\
-     *         v2' & = & -1 + 2 * (1 - u2)       \\
-     *         r2' & = & v1' * v1' + v2' * v2'       \\
-     *         normal' & = & v1' * \sqrt{\frac{-2.0 * \log{r2'}}{r2'}}       \\
-     *         x' & = &  \exp{sigma * normal' + mu}  .
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The mu value for the log-normal distribution returned by this RNG stream. */
@@ -1478,13 +1186,35 @@
  * from a fixed gamma distribution.  It also supports the generation of
  * single random numbers from various gamma distributions.
  *
- * The probability density function is defined over the interval [0,\f$+\infty\f$) as:
- * \f$ x^{\alpha-1} \frac{e^{-\frac{x}{\beta}}}{\beta^\alpha \Gamma(\alpha)}\f$
- * where \f$ mean = \alpha\beta \f$ and
- * \f$ variance = \alpha \beta^2\f$
+ * The probability distribution is defined in terms two parameters,
+ * \c Alpha = \f$\alpha > 0\f$ and \c Beta = \f$ \beta > 0\f$.
+ * (Note the Wikipedia entry for the
+ * [Gamma Distribution](https://en.wikipedia.org/wiki/Gamma_distribution)
+ * uses either the parameters \f$k, \theta\f$ or \f$\alpha, \beta\f$.
+ * The parameters used here \f$(\alpha, \beta)_{\mbox{ns-3}}\f$ correspond to
+ * \f$(\alpha, \frac{1}{\beta})_{\mbox{Wikipedia}}\f$.)
+ *
+ * The probability density function is:
+ *
+ *   \f[
+ *      P(x; \alpha, \beta) dx = x^{\alpha-1} \\
+ *          \frac{e^{-\frac{x}{\beta}}}{\beta^\alpha \Gamma(\alpha)} dx, \\
+ *          \quad x \in [0, +\infty)
+ *   \f]
+ *
+ * where the mean is \f$ \mu = \alpha\beta \f$ and the variance is
+ * \f$ \sigma^2 = \alpha \beta^2\f$.
+ *
+ * While gamma RNG values can be generated by an algorithm similar to
+ * normal RNGs, the implementation used here is based on the paper
+ * G. Marsaglia and W. W. Tsang,
+ * [A simple method for generating Gamma variables](https://dl.acm.org/doi/10.1145/358407.358414),
+ * ACM Transactions on Mathematical Software, Vol. 26, No. 3, Sept. 2000.
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double alpha = 5.0;
  *   double beta = 2.0;
  *
@@ -1492,13 +1222,14 @@
  *   x->SetAttribute ("Alpha", DoubleValue (alpha));
  *   x->SetAttribute ("Beta", DoubleValue (beta));
  *
- *   // The expected value for the mean of the values returned by a
- *   // gammaly distributed random variable is equal to
- *   //
- *   //     E[value]  =  alpha * beta  .
- *   //
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated using the prescription
+ * in the Marsaglia, _et al_. paper cited above.
  */
 class GammaRandomVariable : public RandomVariableStream
 {
@@ -1528,67 +1259,18 @@
     double GetBeta() const;
 
     /**
-     * \brief Returns a random double from a gamma distribution with the specified alpha and beta.
+     * \copydoc GetValue()
      * \param [in] alpha Alpha value for the gamma distribution.
      * \param [in] beta Beta value for the gamma distribution.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
      */
     double GetValue(double alpha, double beta);
 
-    /**
-     * \brief Returns a random unsigned integer from a gamma distribution with the specified alpha
-     * and beta.
-     * \param [in] alpha Alpha value for the gamma distribution.
-     * \param [in] beta Beta value for the gamma distribution.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
+    /** \copydoc GetValue(double,double) */
     uint32_t GetInteger(uint32_t alpha, uint32_t beta);
 
-    /**
-     * \brief Returns a random double from a gamma distribution with the current alpha and beta.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the two-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a gamma distribution with the current alpha and
-     * beta.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /**
@@ -1598,36 +1280,6 @@
      * \param [in] variance Variance value for the normal distribution.
      * \param [in] bound Bound on values returned.
      * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u1\f$ and \f$u2\f$ are uniform variables
-     * over [0,1], then the values that would be returned normally, \f$x1\f$ and \f$x2\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1 & = & 2 * u1 - 1     \\
-     *         v2 & = & 2 * u2 - 1     \\
-     *         w & = & v1 * v1 + v2 * v2     \\
-     *         y & = & \sqrt{\frac{-2 * \log(w)}{w}}     \\
-     *         x1 & = & mean + v1 * y * \sqrt{variance}     \\
-     *         x2 & = & mean + v2 * y * \sqrt{variance}  .
-     *    \f}
-     *
-     * For the antithetic case, \f$(1 - u1\f$) and \f$(1 - u2\f$) are
-     * the distances that \f$u1\f$ and \f$u2\f$ would be from \f$1\f$.
-     * The antithetic values returned, \f$x1'\f$ and \f$x2'\f$, are
-     * calculated as follows:
-     *
-     *    \f{eqnarray*}{
-     *         v1' & = & 2 * (1 - u1) - 1     \\
-     *         v2' & = & 2 * (1 - u2) - 1     \\
-     *         w' & = & v1' * v1' + v2' * v2'     \\
-     *         y' & = & \sqrt{\frac{-2 * \log(w')}{w'}}     \\
-     *         x1' & = & mean + v1' * y' * \sqrt{variance}     \\
-     *         x2' & = & mean + v2' * y' * \sqrt{variance}  ,
-     *    \f}
-     *
-     * which now involves the distances \f$u1\f$ and \f$u2\f$ are from 1.
      */
     double GetNormalValue(double mean, double variance, double bound);
 
@@ -1656,17 +1308,42 @@
  * from a fixed Erlang distribution.  It also supports the generation of
  * single random numbers from various Erlang distributions.
  *
- * The Erlang distribution is a special case of the Gamma distribution where k
- * (= alpha) is a non-negative integer. Erlang distributed variables can be
- * generated using a much faster algorithm than gamma variables.
- *
- * The probability density function is defined over the interval [0,\f$+\infty\f$) as:
- * \f$ \frac{x^{k-1} e^{-\frac{x}{\lambda}}}{\lambda^k (k-1)!}\f$
- * where \f$ mean = k \lambda \f$ and
- * \f$ variance = k \lambda^2\f$
+ * The Erlang distribution is a special case of the Gamma distribution
+ * where \f$k = \alpha > 0 \f$ is a positive definite integer.
+ * Erlang distributed variables can be generated using a much faster
+ * algorithm than Gamma variables.
+ *
+ * The probability distribution is defined in terms two parameters,
+ * the \c K or shape parameter \f$ \in {1,2 \dots} \f$, and
+ * the \c Lambda or scale parameter \f$ \in (0,1] \f$.
+ * (Note the Wikipedia entry for the
+ * [Erlang Distribution](https://en.wikipedia.org/wiki/Erlang_distribution)
+ * uses the parameters \f$(k, \lambda)\f$ or \f$(k, \beta)\f$.
+ * The parameters used here \f$(k, \lambda)_{\mbox{ns-3}}\f$ correspond to
+ * \f$(k, \frac{1}{\lambda} = \beta)_{\mbox{Wikipedia}}\f$.)
+ *
+ * The probability density function is:
+ *
+ *   \f[
+ *      P(x; k, \lambda) dx = \lambda^k \\
+ *                            \frac{x^{k-1} e^{-\frac{x}{\lambda}}}{(k-1)!} dx, \\
+ *          \quad x \in [0, +\infty)
+ *   \f]
+ *
+ * with mean \f$ \mu = k \lambda \f$ and variance \f$ \sigma^2 = k \lambda^2 \f$.
+ *
+ * The Erlang RNG value \f$x\f$ is generated by
+ *
+ *   \f[
+ *      x = - \lambda \sum_{i = 1}^{k}{\ln u_i}
+ *   \f]
+ *
+ * where the \f$u_i\f$ are \f$k\f$ uniform random variables on [0,1).
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   uint32_t k = 5;
  *   double lambda = 2.0;
  *
@@ -1674,13 +1351,19 @@
  *   x->SetAttribute ("K", IntegerValue (k));
  *   x->SetAttribute ("Lambda", DoubleValue (lambda));
  *
- *   // The expected value for the mean of the values returned by a
- *   // Erlangly distributed random variable is equal to
- *   //
- *   //     E[value]  =  k * lambda  .
- *   //
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated as follows:
+ *
+ *   \f[
+ *      x' = - \lambda \sum_{i = 1}^{k}{\ln (1 - u_i)}
+ *   \f]
+ *
+ * which now involves the log of the distance \f$u\f$ is from 1.
  */
 class ErlangRandomVariable : public RandomVariableStream
 {
@@ -1710,67 +1393,18 @@
     double GetLambda() const;
 
     /**
-     * \brief Returns a random double from an Erlang distribution with the specified k and lambda.
+     * \copydoc GetValue()
      * \param [in] k K value for the Erlang distribution.
      * \param [in] lambda Lambda value for the Erlang distribution.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
      */
     double GetValue(uint32_t k, double lambda);
 
-    /**
-     * \brief Returns a random unsigned integer from an Erlang distribution with the specified k and
-     * lambda.
-     * \param [in] k K value for the Erlang distribution.
-     * \param [in] lambda Lambda value for the Erlang distribution.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
+    /** \copydoc GetValue(uint32_t,double) */
     uint32_t GetInteger(uint32_t k, uint32_t lambda);
 
-    /**
-     * \brief Returns a random double from an Erlang distribution with the current k and lambda.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the two-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from an Erlang distribution with the current k and
-     * lambda.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /**
@@ -1779,24 +1413,6 @@
      * \param [in] mean Mean value of the random variables.
      * \param [in] bound Upper bound on values returned.
      * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *         x = - mean * \log(u)
-     *    \f]
-     *
-     * is a value that would be returned normally, then \f$(1 - u\f$) is
-     * the distance that \f$u\f$ would be from \f$1\f$.  The value
-     * returned in the antithetic case, \f$x'\f$, is calculated as
-     *
-     *    \f[
-     *         x' = - mean * \log(1 - u),
-     *    \f]
-     *
-     * which now involves the log of the distance \f$u\f$ is from the 1.
      */
     double GetExponentialValue(double mean, double bound);
 
@@ -1817,11 +1433,41 @@
  * from a fixed triangular distribution.  It also supports the generation of
  * single random numbers from various triangular distributions.
  *
- * This distribution is a triangular distribution.  The probability density
- * is in the shape of a triangle.
+ * The probability density depends on three parameters, the end points
+ * \f$a\f$ = \c Min and \f$b\f$ = \c Max, and the location of the peak or mode,
+ * \f$c\f$. For historical reasons this formulation uses the \c Mean,
+ * \f$\mu = \frac{(a + b + c)}{3}\f$ instead of the mode.
+ * In terms of the \c Mean, the mode is \f$c = 3 \mu - a - b\f$.
+ *
+ * The probability is in the shape of a triangle defined on the interval
+ * \f$ x \in [a, b] \f$:
+ *
+ *   \f[
+ *      P(x; a, b, c) dx = \begin{array}{ll}
+ *          0                                  &\mbox{ for $x \le a$} \\
+ *          \frac{2(x - a)}{(b - a)(c - a)} dx &\mbox{ for $a \le x \le c$} \\
+ *          \frac{2}{b - 1} dx                 &\mbox{ for $x = c$} \\
+ *          \frac{2(b - x)}{(b - a)(b - c)} dx &\mbox{ for $c \le x \le b$} \\
+ *          0                                  &\mbox{ for $b \le x$}
+ *          \end{array}
+ *   \f]
+ *
+ * The triangle RNG \f$x\f$ is generated by
+ *
+ *   \f[
+ *      x = \left\{ \begin{array}{rl}
+ *          a + \sqrt{u (b - a) (c - a)} &\mbox{ if $u \le (c - a)/(b - a)$} \\
+ *          b - \sqrt{(1 - u) (b - a) (b - c) } &\mbox{ otherwise}
+ *          \end{array}
+ *          \right.
+ *   \f]
+ *
+ * where \f$u\f$ is a uniform random variable on [0,1).
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double mean = 5.0;
  *   double min = 2.0;
  *   double max = 10.0;
@@ -1831,10 +1477,23 @@
  *   x->SetAttribute ("Min", DoubleValue (min));
  *   x->SetAttribute ("Max", DoubleValue (max));
  *
- *   // The expected value for the mean of the values returned by a
- *   // triangularly distributed random variable is equal to mean.
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated as follows:
+ *
+ *   \f[
+ *      x = \left\{ \begin{array}{rl}
+ *          a + \sqrt{(1 - u) (b - a) (c - a)} &\mbox{ if $(1 - u) \le (c - a)/(b - a)$} \\
+ *          b - \sqrt{u (b - a) (b - c) } &\mbox{ otherwise}
+ *          \end{array}
+ *          \right.
+ *   \f]
+ *
+ * which now involves the distance \f$u\f$ is from 1.
  */
 class TriangularRandomVariable : public RandomVariableStream
 {
@@ -1870,163 +1529,19 @@
     double GetMax() const;
 
     /**
-     * \brief Returns a random double from a triangular distribution with the specified mean, min,
-     * and max.
+     * \copydoc GetValue()
      * \param [in] mean Mean value for the triangular distribution.
      * \param [in] min Low end of the range.
      * \param [in] max High end of the range.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *        x = \left\{ \begin{array}{rl}
-     *           min + \sqrt{u * (max - min) * (mode - min)} &\mbox{ if $u <= (mode - min)/(max -
-     * min)$} \\ max - \sqrt{ (1 - u) * (max - min) * (max - mode) } &\mbox{ otherwise} \end{array}
-     * \right. \f]
-     *
-     * is a value that would be returned normally, where the mode or
-     * peak of the triangle is calculated as
-     *
-     *    \f[
-     *         mode =  3.0 * mean - min - max  .
-     *    \f]
-     *
-     * Then, \f$(1 - u\f$) is the distance that \f$u\f$ would be from
-     * \f$1\f$.  The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *        x' = \left\{ \begin{array}{rl}
-     *           min + \sqrt{(1 - u) * (max - min) * (mode - min)} &\mbox{ if $(1 - u) <= (mode -
-     * min)/(max - min)$} \\ max - \sqrt{ u * (max - min) * (max - mode) } &\mbox{ otherwise}
-     *                 \end{array} \right.
-     *     \f]
-     *
-     * which now involves the distance \f$u\f$ is from the 1.
      */
     double GetValue(double mean, double min, double max);
 
-    /**
-     * \brief Returns a random unsigned integer from a triangular distribution with the specified
-     * mean, min, and max.
-     * \param [in] mean Mean value for the triangular distribution.
-     * \param [in] min Low end of the range.
-     * \param [in] max High end of the range.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *        x = \left\{ \begin{array}{rl}
-     *           min + \sqrt{u * (max - min) * (mode - min)} &\mbox{ if $u <= (mode - min)/(max -
-     * min)$} \\ max - \sqrt{ (1 - u) * (max - min) * (max - mode) } &\mbox{ otherwise} \end{array}
-     * \right. \f]
-     *
-     * is a value that would be returned normally, where the mode or
-     * peak of the triangle is calculated as
-     *
-     *    \f[
-     *         mode =  3.0 * mean - min - max  .
-     *    \f]
-     *
-     * Then, \f$(1 - u\f$) is the distance that \f$u\f$ would be from
-     * \f$1\f$.  The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *        x' = \left\{ \begin{array}{rl}
-     *           min + \sqrt{(1 - u) * (max - min) * (mode - min)} &\mbox{ if $(1 - u) <= (mode -
-     * min)/(max - min)$} \\ max - \sqrt{ u * (max - min) * (max - mode) } &\mbox{ otherwise}
-     *                 \end{array} \right.
-     *     \f]
-     *
-     * which now involves the distance \f$u\f$ is from the 1.
-     */
+    /** \copydoc GetValue(double,double,double) */
     uint32_t GetInteger(uint32_t mean, uint32_t min, uint32_t max);
 
-    /**
-     * \brief Returns a random double from a triangular distribution with the current mean, min, and
-     * max.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *        x = \left\{ \begin{array}{rl}
-     *           min + \sqrt{u * (max - min) * (mode - min)} &\mbox{ if $u <= (mode - min)/(max -
-     * min)$} \\ max - \sqrt{ (1 - u) * (max - min) * (max - mode) } &\mbox{ otherwise} \end{array}
-     * \right. \f]
-     *
-     * is a value that would be returned normally, where the mode or
-     * peak of the triangle is calculated as
-     *
-     *    \f[
-     *         mode =  3.0 * mean - min - max  .
-     *    \f]
-     *
-     * Then, \f$(1 - u\f$) is the distance that \f$u\f$ would be from
-     * \f$1\f$.  The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *        x' = \left\{ \begin{array}{rl}
-     *           min + \sqrt{(1 - u) * (max - min) * (mode - min)} &\mbox{ if $(1 - u) <= (mode -
-     * min)/(max - min)$} \\ max - \sqrt{ u * (max - min) * (max - mode) } &\mbox{ otherwise}
-     *                 \end{array} \right.
-     *     \f]
-     *
-     * which now involves the distance \f$u\f$ is from the 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the three-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a triangular distribution with the current
-     * mean, min, and max.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if
-     * m_isAntithetic is equal to true.  If \f$u\f$ is a uniform variable
-     * over [0,1] and
-     *
-     *    \f[
-     *        x = \left\{ \begin{array}{rl}
-     *           min + \sqrt{u * (max - min) * (mode - min)} &\mbox{ if $u <= (mode - min)/(max -
-     * min)$} \\ max - \sqrt{ (1 - u) * (max - min) * (max - mode) } &\mbox{ otherwise} \end{array}
-     * \right. \f]
-     *
-     * is a value that would be returned normally, where the mode or
-     * peak of the triangle is calculated as
-     *
-     *    \f[
-     *         mode =  3.0 * mean - min - max  .
-     *    \f]
-     *
-     * Then, \f$(1 - u\f$) is the distance that \f$u\f$ would be from
-     * \f$1\f$.  The value returned in the antithetic case, \f$x'\f$, is
-     * calculated as
-     *
-     *    \f[
-     *        x' = \left\{ \begin{array}{rl}
-     *           min + \sqrt{(1 - u) * (max - min) * (mode - min)} &\mbox{ if $(1 - u) <= (mode -
-     * min)/(max - min)$} \\ max - \sqrt{ u * (max - min) * (max - mode) } &\mbox{ otherwise}
-     *                 \end{array} \right.
-     *     \f]
-     *
-     * which now involves the distance \f$u\f$ is from the 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The mean value for the triangular distribution returned by this RNG stream. */
@@ -2049,17 +1564,50 @@
  * from a fixed Zipf distribution.  It also supports the generation of
  * single random numbers from various Zipf distributions.
  *
- * The Zipf's law states that given some corpus of natural language
+ * Zipf's law states that given some corpus of natural language
  * utterances, the frequency of any word is inversely proportional
  * to its rank in the frequency table.
  *
- * Zipf's distribution has two parameters, alpha and N, where:
- * \f$ \alpha > 0 \f$ (real) and \f$ N \in \{1,2,3 \dots\}\f$ (integer).
- * Probability Mass Function is \f$ f(k; \alpha, N) = k^{-\alpha}/ H_{N,\alpha} \f$
- * where \f$ H_{N,\alpha} = \sum_{m=1}^N m^{-\alpha} \f$
+ * Zipf's distribution has two parameters, \c Alpha and \c N, where:
+ * \f$ \alpha \ge 0 \f$ (real) and \f$ N \in \{1,2,3 \dots\} \f$ (integer).
+ * (Note the Wikipedia entry for the
+ * [Zipf Distribution](https://en.wikipedia.org/wiki/Zipf%27s_law)
+ * uses the symbol \f$s\f$ instead of \f$\alpha\f$.)
+ *
+ * The probability mass function is:
+ *
+ *   \f[
+ *      P(k; \alpha, N) = \frac{1}{k^\alpha H_{N,\alpha}}
+ *   \f]
+ *
+ * where the \c N-th generalized harmonic number is
+ *
+ *    \f[
+ *        H_{N,\alpha} = \sum_{m=1}^N \frac{1}{m^\alpha}
+ *    \f]
+ *
+ * Note the Zipf distribution is a discrete distribution, so the
+ * returned values \f$k\f$ will always be integers in the range \f$k
+ * \in {1,2 \dots N} \f$.
+ *
+ * The mean of the distribution is
+ *
+ *   \f[
+ *      \mu = \frac{H_{N,\alpha - 1}}{H_{N,\alpha}}
+ *   \f]
+ *
+ * The Zipf RNG value \f$k\f$ is the smallest value such that
+ *
+ *   \f[
+ *      u < \frac{H_k,\alpha}{H_N,\alpha}
+ *   \f]
+ *
+ * where \f$u\f$ is a uniform random variable on [0,1).
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   uint32_t n = 1;
  *   double alpha = 2.0;
  *
@@ -2067,36 +1615,18 @@
  *   x->SetAttribute ("N", IntegerValue (n));
  *   x->SetAttribute ("Alpha", DoubleValue (alpha));
  *
- *   // The expected value for the mean of the values returned by a
- *   // Zipfly distributed random variable is equal to
- *   //
- *   //                   H
- *   //                    N, alpha - 1
- *   //     E[value]  =  ---------------
- *   //                     H
- *   //                      N, alpha
- *   //
- *   // where
- *   //
- *   //                    N
- *   //                   ---
- *   //                   \     -alpha
- *   //     H          =  /    m        .
- *   //      N, alpha     ---
- *   //                   m=1
- *   //
- *   // For this test,
- *   //
- *   //                      -(alpha - 1)
- *   //                     1
- *   //     E[value]  =  ---------------
- *   //                      -alpha
- *   //                     1
- *   //
- *   //               =  1  .
- *   //
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$k'\f$, is the value such that
+ *
+ *   \f[
+ *      1 - u < \frac{H_{k'},\alpha}{H_N,\alpha}
+ *   \f]
+ *
  */
 class ZipfRandomVariable : public RandomVariableStream
 {
@@ -2126,67 +1656,19 @@
     double GetAlpha() const;
 
     /**
-     * \brief Returns a random double from a Zipf distribution with the specified n and alpha.
+     * \copydoc GetValue()
      * \param [in] n N value for the Zipf distribution.
      * \param [in] alpha Alpha value for the Zipf distribution.
      * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
      */
     double GetValue(uint32_t n, double alpha);
 
-    /**
-     * \brief Returns a random unsigned integer from a Zipf distribution with the specified n and
-     * alpha.
-     * \param [in] n N value for the Zipf distribution.
-     * \param [in] alpha Alpha value for the Zipf distribution.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
+    /** \copydoc GetValue(uint32_t,double) */
     uint32_t GetInteger(uint32_t n, uint32_t alpha);
 
-    /**
-     * \brief Returns a random double from a Zipf distribution with the current n and alpha.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the two-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a Zipf distribution with the current n and
-     * alpha.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The n value for the Zipf distribution returned by this RNG stream. */
@@ -2209,36 +1691,55 @@
  * from a fixed zeta distribution.  It also supports the generation of
  * single random numbers from various zeta distributions.
  *
- * The Zeta distribution is closely related to Zipf distribution when
- * N goes to infinity.
+ * The Zeta distribution is related to Zipf distribution by letting
+ * \f$N \rightarrow \infty\f$.
+ *
+ * Zeta distribution has one parameter, \c Alpha, \f$ \alpha > 1 \f$ (real).
+ * (Note the Wikipedia entry for the
+ * [Zeta Distribution](https://en.wikipedia.org/wiki/Zeta_distribution)
+ * uses the symbol \f$s\f$ instead of \f$\alpha\f$.)
+ * The probability mass Function is
  *
- * Zeta distribution has one parameter, alpha, \f$ \alpha > 1 \f$ (real).
- * Probability Mass Function is \f$ f(k; \alpha) = k^{-\alpha}/\zeta(\alpha) \f$
- * where \f$ \zeta(\alpha) \f$ is the Riemann zeta function ( \f$ \sum_{n=1}^\infty n^{-\alpha} )
- * \f$
+ *   \f[
+ *      P(k; \alpha) = k^{-\alpha}/\zeta(\alpha)
+ *   \f]
+ *
+ * where \f$ \zeta(\alpha) \f$ is the Riemann zeta function
+ *
+ *   \f[
+ *     \zeta(\alpha) = \sum_{n=1}^\infty \frac{1}{n^\alpha}
+ *   \f]
+ *
+ * Note the Zeta distribution is a discrete distribution, so the
+ * returned values \f$k\f$ will always be integers in the range
+ * \f$k \in {1,2 \dots} \f$.
+ *
+ * The mean value of the distribution is
+ *
+ *    \f[
+ *       \mu = \frac{\zeta(\alpha - 1)}{\zeta(\alpha)}, \quad \alpha > 2
+ *    \f]
+ *
+ * The Zeta RNG \f$x\f$ is generated by an accept-reject algorithm;
+ * see the implementation of GetValue(double).
+ *
+ * \par Example
  *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   double alpha = 2.0;
  *
  *   Ptr<ZetaRandomVariable> x = CreateObject<ZetaRandomVariable> ();
  *   x->SetAttribute ("Alpha", DoubleValue (alpha));
  *
- *   // The expected value for the mean of the values returned by a
- *   // zetaly distributed random variable is equal to
- *   //
- *   //                   zeta(alpha - 1)
- *   //     E[value]  =  ---------------   for alpha > 2 ,
- *   //                     zeta(alpha)
- *   //
- *   // where zeta(alpha) is the Riemann zeta function.
- *   //
- *   // There are no simple analytic forms for the Riemann zeta
- *   // function, which is the reason the known mean of the values
- *   // cannot be calculated in this example.
- *   //
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated by using
+ * \f$ 1 - u \f$ instead. of \f$u\f$ on [0, 1].
  */
 class ZetaRandomVariable : public RandomVariableStream
 {
@@ -2262,63 +1763,17 @@
     double GetAlpha() const;
 
     /**
-     * \brief Returns a random double from a zeta distribution with the specified alpha.
+     * \copydoc GetValue()
      * \param [in] alpha Alpha value for the zeta distribution.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
      */
     double GetValue(double alpha);
 
-    /**
-     * \brief Returns a random unsigned integer from a zeta distribution with the specified alpha.
-     * \param [in] alpha Alpha value for the zeta distribution.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
+    /** \copydoc GetValue(double) */
     uint32_t GetInteger(uint32_t alpha);
 
-    /**
-     * \brief Returns a random double from a zeta distribution with the current alpha.
-     * \return A floating point random value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     *
-     * Note that we have to re-implement this method here because the method is
-     * overloaded above for the two-argument variant and the c++ name resolution
-     * rules don't work well with overloads split between parent and child
-     * classes.
-     */
+    // Inherited
     double GetValue() override;
-
-    /**
-     * \brief Returns a random unsigned integer from a zeta distribution with the current alpha.
-     * \return A random unsigned integer value.
-     *
-     * Note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** The alpha value for the zeta distribution returned by this RNG stream. */
@@ -2338,26 +1793,33 @@
  * return a known sequence, perhaps to compare ns-3 to some other
  * simulator
  *
- * Creates a generator that returns successive elements of the values
- * array on successive calls to RandomVariableStream::GetValue.  Note
+ * Creates a generator that returns successive elements from the value
+ * array on successive calls to GetValue().  Note
  * that the values in the array are copied and stored by the generator
  * (deep-copy).  Also note that the sequence repeats if more values
  * are requested than are present in the array.
  *
+ * \par Example
+ *
  * Here is an example of how to use this class:
- * \code
+ * \code{.cc}
  *   Ptr<DeterministicRandomVariable> s = CreateObject<DeterministicRandomVariable> ();
  *
- *   // The following array should give the sequence
- *   //
- *   //    4, 4, 7, 7, 10, 10 .
- *   //
- *   double array [] = { 4, 4, 7, 7, 10, 10};
- *   uint64_t count = 6;
- *   s->SetValueArray (array, count);
+ *   std::vector array{ 4, 4, 7, 7, 10, 10};
+ *   s->SetValueArray (array);
  *
  *   double value = x->GetValue ();
  * \endcode
+ *
+ * This will return values in the repeating sequence
+ *
+ *   \f[
+ *      x \in 4, 4, 7, 7, 10, 10, 4, \dots
+ *   \f]
+ *
+ * \par Antithetic Values.
+ *
+ * This RNG ignores the antithetic setting.
  */
 class DeterministicRandomVariable : public RandomVariableStream
 {
@@ -2377,25 +1839,25 @@
 
     /**
      * \brief Sets the array of values that holds the predetermined sequence.
-     * \param [in] values Array of random values to return in sequence.
-     * \param [in] length Number of values in the array.
      *
      * Note that the values in the array are copied and stored
      * (deep-copy).
+     * \param [in] values Array of random values to return in sequence.
      */
-    void SetValueArray(double* values, std::size_t length);
-
+    void SetValueArray(const std::vector<double>& values);
     /**
-     * \brief Returns the next value in the sequence.
-     * \return The floating point next value in the sequence.
+     * \brief Sets the array of values that holds the predetermined sequence.
+     *
+     * Note that the values in the array are copied and stored
+     * (deep-copy).
+     * \param [in] values Array of random values to return in sequence.
+     * \param [in] length Number of values in the array.
      */
-    double GetValue() override;
+    void SetValueArray(const double* values, std::size_t length);
 
-    /**
-     * \brief Returns the next value in the sequence.
-     * \return The integer next value in the sequence.
-     */
-    uint32_t GetInteger() override;
+    // Inherited
+    double GetValue() override;
+    using RandomVariableStream::GetInteger;
 
   private:
     /** Size of the array of values. */
@@ -2414,17 +1876,16 @@
  * \brief The Random Number Generator (RNG) that has a specified
  * empirical distribution.
  *
- * Defines a random variable  that has a specified, empirical
- * distribution.  The cumulative probability distribution function (CDF)
- * is specified by a
- * series of calls to the CDF() member function, specifying a
- * value and the probability that the distribution is less than
- * the specified value.  When random values are requested,
- * a uniform random variable `r` is used to select a probability,
- * and the return value is chosen from the largest input value with CDF
- * less than the random value. The method is known
- * as inverse transform sampling:
- * (http://en.wikipedia.org/wiki/Inverse_transform_sampling).
+ * Defines a random variable that has a specified, empirical
+ * distribution.  The cumulative probability distribution function
+ * (CDF) is specified by a series of calls to the CDF() member
+ * function, specifying a value \f$x\f$ and the probability \f$P(x)\f$
+ * that the distribution is less than the specified value.  When
+ * random values are requested, a uniform random variable
+ * \f$ u \in [0, 1] \f$ is used to select a probability,
+ * and the return value is chosen as the largest input value
+ * with CDF less than the random value. This method is known as
+ * [inverse transform sampling](http://en.wikipedia.org/wiki/Inverse_transform_sampling).
  *
  * This generator has two modes: *sampling* and *interpolating*.
  * In *sampling* mode this random variable generator
@@ -2438,24 +1899,27 @@
  * This is appropriate when the configured CDF is an approximation
  * to a continuous underlying probability distribution.
  *
- * For historical reasons the default is interpolating.
- * To switch modes use the \c Interpolate Attribute, or call SetInterpolate().
- * You can change modes at any time.
+ * For historical reasons the default is sampling.  To switch modes
+ * use the \c Interpolate Attribute, or call SetInterpolate().  You
+ * can change modes at any time.
  *
  * If you find yourself switching frequently it could be simpler to
  * set the mode to sampling, then use the GetValue() function for
  * sampled values, and Interpolate() function for interpolated values.
  *
- * The CDF need not start with a probability of zero,
- * nor end with a probability of 1.0.  If the selected uniform
- * random value `r` is less than the first CDF point, that point
- * is selected. If `r` is greater than the last CDF point the last
- * point is selected.  In either case the interpolating mode will *not*
- * interpolate (since there is no value beyond `r` to work with), but
- * simply return the extremal CDF value, as in sampling.
+ * The CDF need not start with a probability of zero, nor end with a
+ * probability of 1.0.  If the selected uniform random value
+ * \f$ u \in [0,1] \f$ is less than the probability of the first CDF point,
+ * that point is selected. If \f$u\f$ is greater than the probability of
+ * the last CDF point the last point is selected.  In either case the
+ * interpolating mode will *not* interpolate (since there is no value
+ * beyond the first/last to work with), but simply return the extremal CDF
+ * value, as in sampling.
  *
- * Here is an example of how to use this class:
+ * \par Example
  *
+ * Here is an example of how to use this class:
+ * \code{.cc}
  *    // Create the RNG with a non-uniform distribution between 0 and 10.
  *    // in sampling mode.
  *    Ptr<EmpiricalRandomVariable> x = CreateObject<EmpiricalRandomVariable> ();
@@ -2465,8 +1929,9 @@
  *    x->CDF (10.0,  1.0);
  *
  *    double value = x->GetValue ();
+ * \endcode
  *
- * The expected values and probabilities returned by GetValue are
+ * The expected values and probabilities returned by GetValue() are
  *
  * Value | Probability
  * ----: | ----------:
@@ -2474,16 +1939,23 @@
  *  5.0  | 25%
  * 10.0  | 75%
  *
- * The only two values ever returned are 5 and 10, with unequal probability.
+ * The only two values ever returned are 5 and 10, in the ratio 1:3.
  *
  * If instead you want linear interpolation between the points of the CDF
  * use the Interpolate() function:
  *
  *     double interp = x->Interpolate ();
  *
- * This will return continuous values on the range [0,1).
+ * This will return continuous values on the range [0,1), 25% of the time
+ * less than 5, and 75% of the time between 5 and 10.
  *
  * See empirical-random-variable-example.cc for an example.
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is generated by using
+ * \f$ 1 - u \f$ instead. of \f$u\f$ on [0, 1].
  */
 class EmpiricalRandomVariable : public RandomVariableStream
 {
@@ -2506,39 +1978,19 @@
      *
      * \param [in] v The function value for this point
      * \param [in] c Probability that the function is less than or equal to \p v
+     *               In other words this is cumulative distribution function
+     *               at \p v.
      */
     void CDF(double v, double c); // Value, prob <= Value
 
+    // Inherited
     /**
-     * \brief Returns the next value in the empirical distribution.
-     * \return The floating point next value in the empirical distribution.
-     *
-     * Note that this does not interpolate the CDF, but treats it as a
+     * \copydoc RandomVariableStream::GetValue()
+     * \note This does not interpolate the CDF, but treats it as a
      * stepwise continuous function.
-     *
-     * Also note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
      */
     double GetValue() override;
-
-    /**
-     * \brief Returns the next value in the empirical distribution.
-     * \return The integer next value in the empirical distribution.
-     *
-     * Note that this does not interpolate the CDF, but treats it as a
-     * stepwise continuous function.
-     * Also note that antithetic values are being generated if m_isAntithetic
-     * is equal to true.  If \f$u\f$ is a uniform variable over [0,1]
-     * and \f$x\f$ is a value that would be returned normally, then
-     * \f$(1 - u\f$) is the distance that \f$u\f$ would be from \f$1\f$.
-     * The value returned in the antithetic case, \f$x'\f$, uses (1-u),
-     * which is the distance \f$u\f$ is from the 1.
-     */
-    uint32_t GetInteger() override;
+    using RandomVariableStream::GetInteger;
 
     /**
      * \brief Returns the next value in the empirical distribution using
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/realtime-simulator-impl.cc ns-3.38/src/core/model/realtime-simulator-impl.cc
--- ns-3.37/src/core/model/realtime-simulator-impl.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/realtime-simulator-impl.cc	2023-03-16 20:54:23.592868600 -0700
@@ -266,7 +266,7 @@
         //
         // It's easiest to understand if you just consider a short tsDelay that only
         // requires a SpinWait down in the synchronizer.  What will happen is that
-        // whan Synchronize calls SpinWait, SpinWait will look directly at its
+        // when Synchronize calls SpinWait, SpinWait will look directly at its
         // condition variable.  Note that we set this condition variable to false
         // inside the critical section above.
         //
@@ -811,7 +811,7 @@
 }
 
 void
-RealtimeSimulatorImpl::SetSynchronizationMode(enum SynchronizationMode mode)
+RealtimeSimulatorImpl::SetSynchronizationMode(SynchronizationMode mode)
 {
     NS_LOG_FUNCTION(this << mode);
     m_synchronizationMode = mode;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/ref-count-base.cc ns-3.38/src/core/model/ref-count-base.cc
--- ns-3.37/src/core/model/ref-count-base.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/ref-count-base.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2007 Georgia Tech Research Corporation
- *
- * 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:  George Riley <riley@ece.gatech.edu>
- * Adapted from original code in object.h by:
- * Authors: Gustavo Carneiro <gjcarneiro@gmail.com>,
- *          Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
- */
-
-#include "ref-count-base.h"
-
-#include "log.h"
-
-/**
- * \file
- * \ingroup ptr
- * ns3::RefCountBase implementation.
- * \deprecated See \ref ns3::SimpleRefCount
- */
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("RefCountBase");
-
-RefCountBase::~RefCountBase()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/ref-count-base.h ns-3.38/src/core/model/ref-count-base.h
--- ns-3.37/src/core/model/ref-count-base.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/ref-count-base.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2007 Georgia Tech Research Corporation
- *
- * 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:  George Riley <riley@ece.gatech.edu>
- * Adapted from original code in object.h by:
- * Authors: Gustavo Carneiro <gjcarneiro@gmail.com>,
- *          Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
- */
-#ifndef REF_COUNT_BASE_H
-#define REF_COUNT_BASE_H
-
-#include "simple-ref-count.h"
-
-/**
- * \file
- * \ingroup ptr
- * ns3::RefCountBase declaration.
- * \deprecated See \ref ns3::SimpleRefCount
- */
-
-namespace ns3
-{
-
-/**
- * \brief A deprecated way to get reference-counting powers
- *
- * \deprecated
- * Users who wish to use reference counting for a class of their own should use
- * instead the template \ref ns3::SimpleRefCount. This class is maintained
- * purely for compatibility to avoid breaking the code of users.
- */
-class RefCountBase : public SimpleRefCount<RefCountBase>
-{
-  public:
-    /**
-     * This only thing this class does it declare a virtual destructor
-     */
-    virtual ~RefCountBase();
-};
-
-} // namespace ns3
-
-#endif /* REF_COUNT_BASE_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/simple-ref-count.h ns-3.38/src/core/model/simple-ref-count.h
--- ns-3.37/src/core/model/simple-ref-count.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/simple-ref-count.h	2023-03-16 20:54:23.596868500 -0700
@@ -40,7 +40,7 @@
  * \ingroup ptr
  * \brief Empty class, used as a default parent class for SimpleRefCount
  */
-class empty
+class Empty
 {
 };
 
@@ -66,7 +66,7 @@
  *      common C++ template pattern whose name is CRTP (Curiously
  *      Recursive Template Pattern)
  * \tparam PARENT \explicit The typename of the parent of this template.
- *      By default, this typename is "'ns3::empty'" which is an empty
+ *      By default, this typename is "'ns3::Empty'" which is an empty
  *      class: compilers which implement the EBCO optimization (empty
  *      base class optimization) will make this a no-op
  * \tparam DELETER \explicit The typename of a class which implements
@@ -76,7 +76,7 @@
  *
  * Interesting users of this class include ns3::Object as well as ns3::Packet.
  */
-template <typename T, typename PARENT = empty, typename DELETER = DefaultDeleter<T>>
+template <typename T, typename PARENT = Empty, typename DELETER = DefaultDeleter<T>>
 class SimpleRefCount : public PARENT
 {
   public:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/string.cc ns-3.38/src/core/model/string.cc
--- ns-3.37/src/core/model/string.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/string.cc	2023-03-16 20:54:23.596868500 -0700
@@ -30,4 +30,27 @@
 ATTRIBUTE_CHECKER_IMPLEMENT_WITH_NAME(String, "std::string");
 ATTRIBUTE_VALUE_IMPLEMENT_WITH_NAME(std::string, String);
 
+StringVector
+SplitString(const std::string& str, const std::string& delim)
+{
+    std::vector<std::string> result;
+    std::string token;
+    std::size_t pos{0};
+    do
+    {
+        std::size_t next = str.find(delim, pos);
+        std::string token{str.substr(pos, next - pos)};
+        result.push_back(token);
+        if (next < str.size())
+        {
+            pos = next + delim.size();
+        }
+        else
+        {
+            pos = str.size() + 1;
+        }
+    } while (pos <= str.size());
+    return result;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/string.h ns-3.38/src/core/model/string.h
--- ns-3.37/src/core/model/string.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/string.h	2023-03-16 20:54:23.596868500 -0700
@@ -22,6 +22,7 @@
 #include "attribute-helper.h"
 
 #include <string>
+#include <vector>
 
 /**
  * \file
@@ -32,6 +33,19 @@
 namespace ns3
 {
 
+/** Return type of SplitString. */
+using StringVector = std::vector<std::string>;
+
+/**
+ * Split a string on a delimiter.
+ * The input string is ummodified.
+ * \param [in] str The string.
+ * \param [in] delim The delimiter.
+ * \returns A vector of the components of \p str which were separated
+ * by \p delim.
+ */
+StringVector SplitString(const std::string& str, const std::string& delim);
+
 //  Additional docs for class StringValue:
 /**
  * Hold variables of type string
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/system-path.cc ns-3.38/src/core/model/system-path.cc
--- ns-3.37/src/core/model/system-path.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/system-path.cc	2023-03-16 20:54:23.596868500 -0700
@@ -19,12 +19,12 @@
 #include "system-path.h"
 
 #include "assert.h"
+#include "environment-variable.h"
 #include "fatal-error.h"
 #include "log.h"
+#include "string.h"
 
 #include <algorithm>
-#include <cstdlib> // getenv
-#include <cstring> // strlen
 #include <ctime>
 #include <regex>
 #include <sstream>
@@ -61,6 +61,7 @@
 #endif
 
 #ifdef __linux__
+#include <cstring>
 #include <unistd.h>
 #endif
 
@@ -257,19 +258,8 @@
 Split(std::string path)
 {
     NS_LOG_FUNCTION(path);
-    std::list<std::string> retval;
-    std::string::size_type current = 0;
-    std::string::size_type next = 0;
-    next = path.find(SYSTEM_PATH_SEP, current);
-    while (next != std::string::npos)
-    {
-        std::string item = path.substr(current, next - current);
-        retval.push_back(item);
-        current = next + 1;
-        next = path.find(SYSTEM_PATH_SEP, current);
-    }
-    std::string item = path.substr(current, next - current);
-    retval.push_back(item);
+    std::vector<std::string> items = SplitString(path, SYSTEM_PATH_SEP);
+    std::list<std::string> retval(items.begin(), items.end());
     return retval;
 }
 
@@ -280,7 +270,7 @@
     std::string retval = "";
     for (std::list<std::string>::const_iterator i = begin; i != end; i++)
     {
-        if (*i == "")
+        if ((*i).empty())
         {
             // skip empty strings in the path list
             continue;
@@ -315,15 +305,13 @@
 MakeTemporaryDirectoryName()
 {
     NS_LOG_FUNCTION_NOARGS();
-    char* path = nullptr;
-
-    path = std::getenv("TMP");
-    if (!path || std::strlen(path) == 0)
+    auto [found, path] = EnvironmentVariable::Get("TMP");
+    if (!found)
     {
-        path = std::getenv("TEMP");
-        if (!path || std::strlen(path) == 0)
+        std::tie(found, path) = EnvironmentVariable::Get("TEMP");
+        if (!found)
         {
-            path = const_cast<char*>("/tmp");
+            path = "/tmp";
         }
     }
 
@@ -396,7 +384,7 @@
     auto tokens = Split(path);
     std::string file = tokens.back();
 
-    if (file == "")
+    if (file.empty())
     {
         // Last component was a directory, not a file name
         // We already checked that the directory exists,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/system-path.h ns-3.38/src/core/model/system-path.h
--- ns-3.37/src/core/model/system-path.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/system-path.h	2023-03-16 20:54:23.596868500 -0700
@@ -120,12 +120,12 @@
  *
  * The first part, "/tmp/" is the absolute path found by inspecting
  * the environment variables `TMP`and `TEMP`, in order. If neither
- * exists the hard-codes root path `/tmp/` is used.
+ * exists the hard-coded root path `/tmp/` is used.
  *
  * The directory name itself starts with the "ns3" identifier telling folks
  * who is making all of the temp directories.
  *
- * The next three number give the hour, minute and second, separated by
+ * The next three numbers give the hour, minute and second, separated by
  * periods.
  *
  * The final number is randomly generated, to avoid name collisions.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/system-wall-clock-ms.h ns-3.38/src/core/model/system-wall-clock-ms.h
--- ns-3.37/src/core/model/system-wall-clock-ms.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/system-wall-clock-ms.h	2023-03-16 20:54:23.596868500 -0700
@@ -32,21 +32,6 @@
 {
 
 /**
- * \ingroup core
- * \defgroup system System Services
- *
- * System-independent interfaces to operating system services:
- * files system, threading, wall clock time.
- *
- * Services provided:
- *
- *   - File and directory paths.
- *   - Thread primitives:  threads, conditional waits, mutex, critical sections.
- *   - Asynchronous input from a file descriptor.
- *   - Wall clock time.
- */
-
-/**
  * \ingroup system
  * \brief Measure elapsed wall clock time in milliseconds.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/test.cc ns-3.38/src/core/model/test.cc
--- ns-3.37/src/core/model/test.cc	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/test.cc	2023-03-16 20:54:23.596868500 -0700
@@ -54,7 +54,7 @@
     //
     {
         double max = (std::fabs(x1) > std::fabs(x2)) ? x1 : x2;
-        (void)std::frexp(max, &exponent);
+        std::frexp(max, &exponent);
     }
 
     //
@@ -217,7 +217,7 @@
      *
      * \param [in] begin Iterator to the first TestCase to print.
      * \param [in] end Iterator to the end of the list.
-     * \param [in] printTestType Preprend the test type label if \c true.
+     * \param [in] printTestType Prepend the test type label if \c true.
      */
     void PrintTestNameList(std::list<TestCase*>::const_iterator begin,
                            std::list<TestCase*>::const_iterator end,
@@ -424,7 +424,7 @@
 {
     NS_LOG_FUNCTION(this << filename);
     const TestCase* current = this;
-    while (current != nullptr && current->m_dataDir == "")
+    while (current != nullptr && current->m_dataDir.empty())
     {
         current = current->m_parent;
     }
@@ -849,7 +849,7 @@
             // skip test
             continue;
         }
-        if (testName != "" && test->GetName() != testName)
+        if (!testName.empty() && test->GetName() != testName)
         {
             // skip test
             continue;
@@ -1007,7 +1007,7 @@
         argi++;
     }
     enum TestSuite::Type testType;
-    if (testTypeString == "")
+    if (testTypeString.empty())
     {
         testType = TestSuite::ALL;
     }
@@ -1040,7 +1040,7 @@
 
     std::list<TestCase*> tests = FilterTests(testName, testType, maximumTestDuration);
 
-    if (m_tempDir == "")
+    if (m_tempDir.empty())
     {
         m_tempDir = SystemPath::MakeTemporaryDirectoryName();
     }
@@ -1060,7 +1060,7 @@
     }
 
     std::ostream* os;
-    if (out != "")
+    if (!out.empty())
     {
         std::ofstream* ofs;
         ofs = new std::ofstream();
@@ -1083,11 +1083,17 @@
 
     // let's run our tests now.
     bool failed = false;
-    if (tests.size() == 0)
+    if (tests.empty())
     {
         std::cerr << "Error:  no tests match the requested string" << std::endl;
         return 1;
     }
+    else if (tests.size() > 1)
+    {
+        std::cerr << "Error:  tests should be launched separately (one at a time)" << std::endl;
+        return 1;
+    }
+
     for (std::list<TestCase*>::const_iterator i = tests.begin(); i != tests.end(); ++i)
     {
         TestCase* test = *i;
@@ -1128,7 +1134,7 @@
         }
     }
 
-    if (out != "")
+    if (!out.empty())
     {
         delete os;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/test.h ns-3.38/src/core/model/test.h
--- ns-3.37/src/core/model/test.h	2022-11-01 17:37:24.471395000 -0700
+++ ns-3.38/src/core/model/test.h	2023-03-16 20:54:23.596868500 -0700
@@ -225,7 +225,7 @@
  * \brief Test that an actual and expected (limit) value are equal and
  * report if not.
  *
- * Check to see if the expected (lmit) value is equal to the actual
+ * Check to see if the expected (limit) value is equal to the actual
  * value found in a test case.  If the two values are equal nothing
  * happens, but if the comparison fails, an error is reported in a
  * consistent way.  EXPECT* macros do not return if an error is
@@ -1130,7 +1130,7 @@
     bool IsStatusSuccess() const;
 
     /**
-     * \brief Get the parent of this TestCsse.
+     * \brief Get the parent of this TestCase.
      *
      * \return A pointer to the parent of this test.
      */
@@ -1242,7 +1242,7 @@
     TestRunnerImpl* m_runner;          //!< Pointer to the TestRunner
     struct Result* m_result;           //!< Results data
     std::string m_name;                //!< TestCase name
-    enum TestDuration m_duration;      //!< TestCase duration
+    TestDuration m_duration;           //!< TestCase duration
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/time.cc ns-3.38/src/core/model/time.cc
--- ns-3.37/src/core/model/time.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/model/time.cc	2023-03-16 20:54:23.596868500 -0700
@@ -203,14 +203,14 @@
 Time::SetDefaultNsResolution()
 {
     NS_LOG_FUNCTION_NOARGS();
-    static struct Resolution resolution;
+    static Resolution resolution;
     SetResolution(Time::NS, &resolution, false);
     return resolution;
 }
 
 // static
 void
-Time::SetResolution(enum Unit resolution)
+Time::SetResolution(Unit resolution)
 {
     NS_LOG_FUNCTION(resolution);
     SetResolution(resolution, PeekResolution());
@@ -218,7 +218,7 @@
 
 // static
 void
-Time::SetResolution(enum Unit unit, struct Resolution* resolution, const bool convert /* = true */)
+Time::SetResolution(Unit unit, Resolution* resolution, const bool convert /* = true */)
 {
     NS_LOG_FUNCTION(resolution);
     if (convert)
@@ -375,7 +375,7 @@
 
 // static
 void
-Time::ConvertTimes(const enum Unit unit)
+Time::ConvertTimes(const Unit unit)
 {
     std::unique_lock lock{g_markingMutex};
 
@@ -414,7 +414,7 @@
 }
 
 TimeWithUnit
-Time::As(const enum Unit unit /* = Time::AUTO */) const
+Time::As(const Unit unit /* = Time::AUTO */) const
 {
     return TimeWithUnit(*this, unit);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/timer.cc ns-3.38/src/core/model/timer.cc
--- ns-3.37/src/core/model/timer.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/model/timer.cc	2023-03-16 20:54:23.596868500 -0700
@@ -42,7 +42,7 @@
     NS_LOG_FUNCTION(this);
 }
 
-Timer::Timer(enum DestroyPolicy destroyPolicy)
+Timer::Timer(DestroyPolicy destroyPolicy)
     : m_flags(destroyPolicy),
       m_delay(FemtoSeconds(0)),
       m_event(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/timer.h ns-3.38/src/core/model/timer.h
--- ns-3.37/src/core/model/timer.h	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/model/timer.h	2023-03-16 20:54:23.596868500 -0700
@@ -121,7 +121,7 @@
      * \param [in] destroyPolicy the event lifetime management policies
      * to use for destroy events
      */
-    Timer(enum DestroyPolicy destroyPolicy);
+    Timer(DestroyPolicy destroyPolicy);
     ~Timer();
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/type-id.cc ns-3.38/src/core/model/type-id.cc
--- ns-3.37/src/core/model/type-id.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/model/type-id.cc	2023-03-16 20:54:23.596868500 -0700
@@ -430,7 +430,7 @@
         }
     }
 
-    struct IidInformation information;
+    IidInformation information;
     information.name = name;
     information.hash = hash;
     information.parent = 0;
@@ -1201,7 +1201,7 @@
 Ptr<const TraceSourceAccessor>
 TypeId::LookupTraceSourceByName(std::string name) const
 {
-    struct TraceSourceInformation info;
+    TraceSourceInformation info;
     return LookupTraceSourceByName(name, &info);
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/val-array.h ns-3.38/src/core/model/val-array.h
--- ns-3.37/src/core/model/val-array.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/model/val-array.h	2023-03-16 20:54:23.596868500 -0700
@@ -0,0 +1,743 @@
+/*
+ * Copyright (c) 2022 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
+ *
+ * Author: Biljana Bojovic <bbojovic@cttc.es>
+ */
+#ifndef VAL_ARRAY_H
+#define VAL_ARRAY_H
+
+#include <ns3/assert.h>
+#include <ns3/simple-ref-count.h>
+
+#include <complex>
+#include <valarray>
+#include <vector>
+
+namespace ns3
+{
+
+/**
+ * \ingroup Matrices
+ *
+ * \brief ValArray is a class to efficiently store 3D array. The class is general
+ * enough to represent 1D array or 2D arrays. ValArray also provides basic
+ * algebra element-wise operations over the whole array (1D, 2D, 3D).
+ *
+ * Main characteristics of ValArray are the following:
+ *
+ * - ValArray uses std::valarray to efficiently store data.
+ *
+ * - In general, the elements are stored in memory as a sequence of consecutive
+ * 2D arrays. The dimensions of 2D arrays are defined by numRows and numCols,
+ * while the number of 2D arrays is defined by numPages. Notice that if we set
+ * the number of pages to 1, we will have only a single 2D array. If we
+ * additionally set numRows or numCols to 1, we will have 1D array.
+ *
+ * - All 2D arrays have the same dimensions, i.e. numRows and numCols.
+ *
+ * - 2D arrays are stored in column-major order, which is the default
+ * order in Eigen and Armadillo libraries, which allows a straightforward mapping
+ * of any page (2D array) within ValArray to Eigen or Armadillo matrices.
+ *
+ * Examples of column-major order:
+ *
+ * a) in the case of a 2D array, we will have in memory the following order of
+ * elements, assuming that the indexes are rowIndex, colIndex, pageIndex:
+ *
+ * a000 a100 a010 a110 a020 a120.
+ *
+ * b) in the case of a 3D array, e.g, if there are two 2D arrays of 2x3 dimensions
+ * we will have in memory the following order of elements,
+ * assuming that the indexes are rowIndex, colIndex, pageIndex:
+ *
+ * a000 a100 a010 a110 a020 a120 a001 a101 a011 a111 a021 a121.
+ *
+ * - The access to the elements is implemented in operators:
+ * - operator (rowIndex) and operator[] (rowIndex) for 1D array (assuming colIndex=0, pageIndex=0),
+ * - operator (rowIndex,colIndex) for 2D array (assuming pageIndex=0) and
+ * - operator(rowIndex, colIndex, pageIndex) for 3D array.
+ *
+ * Definition of ValArray as a template class allows using different numerical
+ * types as the elements of the vectors/matrices, e.g., complex numbers, double,
+ * int, etc.
+ */
+
+template <class T>
+class ValArray : public SimpleRefCount<ValArray<T>>
+{
+  public:
+    // instruct the compiler to generate the default constructor
+    ValArray<T>() = default;
+    /**
+     * \brief Constructor that creates "numPages" number of 2D arrays that are of
+     * dimensions "numRows"x"numCols", and are initialized with all-zero elements.
+     * If only 1 parameter, numRows, is provided then a single 1D array is being created.
+     * \param numRows the number of rows
+     * \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);
+    /**
+     * \brief Constructor creates a single 1D array of values.size () elements and 1 column,
+     * and uses std::valarray<T> values to initialize the elements.
+     * \param values std::valarray<T> that will be used to initialize elements of 1D array
+     */
+    explicit ValArray<T>(const std::valarray<T>& values);
+    /**
+     * \brief Constructor creates a single 1D array of values.size () elements and 1 column,
+     * and moves std::valarray<T> values to initialize the elements.
+     * \param values std::valarray<T> that will be moved to initialize elements of 1D array
+     */
+    ValArray<T>(std::valarray<T>&& values);
+    /**
+     * \brief Constructor creates a single 1D array of values.size () elements and 1 column,
+     * and uses values std::vector<T> to initialize the elements.
+     * \param values std::vector<T> that will be used to initialize elements of 1D array
+     */
+    explicit ValArray<T>(const std::vector<T>& values);
+    /**
+     * \brief Constructor creates a single 2D array of numRows and numCols, and uses
+     * std::valarray<T> values to initialize the elements.
+     * \param numRows the number of rows
+     * \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);
+    /**
+     * \brief Constructor creates a single 2D array of numRows and numCols, and moves
+     * std::valarray<T> values to initialize the elements.
+     * \param numRows the number of rows
+     * \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);
+    /**
+     * \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
+     * numRows*numCols elements will belong to the first 2D array.
+     * \param numRows the number of rows
+     * \param numCols the number of columns
+     * \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);
+    /**
+     * \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
+     * numRows*numCols elements will belong to the first 2D array.
+     * \param numRows the number of rows
+     * \param numCols the number of columns
+     * \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);
+    /** instruct the compiler to generate the implicitly declared destructor*/
+    virtual ~ValArray<T>() = default;
+    /** instruct the compiler to generate the implicitly declared copy constructor*/
+    ValArray<T>(const ValArray<T>&) = default;
+    /**
+     * \brief Copy assignment operator.
+     * Instruct the compiler to generate the implicitly declared copy assignment operator.
+     * \return a reference to the assigned object
+     */
+    ValArray<T>& operator=(const ValArray<T>&) = default;
+    /** instruct the compiler to generate the implicitly declared move constructor*/
+    ValArray<T>(ValArray<T>&&) = default;
+    /**
+     * \brief Move assignment operator.
+     * Instruct the compiler to generate the implicitly declared move assignment operator.
+     * \return a reference to the assigned object
+     */
+    ValArray<T>& operator=(ValArray<T>&&) = default;
+    /**
+     * \returns Number of rows
+     */
+    uint16_t GetNumRows() const;
+    /**
+     * \returns Number of columns
+     */
+    uint16_t GetNumCols() const;
+    /**
+     * \returns Number of pages, i.e., the number of 2D arrays
+     */
+    uint16_t GetNumPages() const;
+    /**
+     * \returns Total number of elements
+     */
+    size_t GetSize() const;
+    /**
+     * \brief Access operator, with bound-checking in debug profile
+     * \param rowIndex The index of the row
+     * \param colIndex The index of the column
+     * \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);
+    /**
+     * \brief Const access operator, with bound-checking in debug profile
+     * \param rowIndex The index of the row
+     * \param colIndex The index of the column
+     * \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;
+    /**
+     * \brief Access operator for 2D ValArrays.
+     * Assuming that the third dimension is equal to 1, e.g. ValArray contains
+     * a single 2D array.
+     * Note: intentionally not implemented through three parameters access operator,
+     * to avoid accidental mistakes by user, e.g., providing 2 parameters when
+     * 3 are necessary, but access operator would return valid value if default
+     * value of pages provided is 0.
+     * \param rowIndex The index of the row
+     * \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);
+    /**
+     * \brief Const access operator for 2D ValArrays.
+     * Assuming that the third dimension is equal to 1, e.g. ValArray contains
+     * a single 2D array.
+     * \param rowIndex row index
+     * \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;
+    /**
+     * \brief Single-element access operator() for 1D ValArrays.
+     * Assuming that the number of columns and pages is equal to 1, e.g. ValArray
+     * contains a single column or a single row.
+     *
+     * Note: intentionally not implemented through three parameters access operator,
+     * to avoid accidental mistakes by user, e.g., providing 1 parameters when
+     * 2 or 3 are necessary.
+     * \param index The index of the 1D ValArray.
+     * \returns A reference to the value with the specified index.
+     */
+    T& operator()(uint16_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;
+    /**
+     * \brief Element-wise multiplication with a scalar value.
+     * \param rhs A scalar value of type T
+     * \returns ValArray in which each element has been multiplied by the given
+     * scalar value.
+     */
+    ValArray<T> operator*(const T& rhs) const;
+    /**
+     * \brief operator+ definition for ValArray<T>.
+     * \param rhs The rhs ValArray to be added to this ValArray.
+     * \return the ValArray instance that holds the results of the operator+
+     */
+    ValArray<T> operator+(const ValArray<T>& rhs) const;
+    /**
+     * \brief binary operator- definition for ValArray<T>.
+     * \param rhs The rhs ValArray to be subtracted from this ValArray.
+     * \return the ValArray instance that holds the results of the operator-
+     */
+    ValArray<T> operator-(const ValArray<T>& rhs) const;
+    /**
+     * \brief unary operator- definition for ValArray<T>.
+     * \return the ValArray instance that holds the results of the operator-
+     */
+    ValArray<T> operator-() const;
+    /**
+     * \brief operator+= definition for ValArray<T>.
+     * \param rhs The rhs ValArray to be added to this ValArray.
+     * \return a reference to this ValArray instance
+     */
+    ValArray<T>& operator+=(const ValArray<T>& rhs);
+    /**
+     * \brief operator-= definition for ValArray<T>.
+     * \param rhs The rhs ValArray to be subtracted from this ValArray.
+     ** \return a reference to this ValArray instance
+     */
+    ValArray<T>& operator-=(const ValArray<T>& rhs);
+    /**
+     * \brief operator== definition for ValArray<T>.
+     * \param rhs The ValArray instance to be compared with lhs ValArray instance
+     * \return true if rhs ValArray is equal to this ValArray, otherwise it returns false
+     */
+    bool operator==(const ValArray<T>& rhs) const;
+    /**
+     * \brief operator!= definition for ValArray<T>.
+     * \param rhs The ValArray instance to be compared with lhs ValArray instance
+     * \return true if rhs ValArray is not equal to this ValArray, otherwise it returns true
+     */
+    bool operator!=(const ValArray<T>& rhs) const;
+    /**
+     * \brief Compare Valarray up to a given absolute tolerance. This operation
+     * is element-wise operation, i.e., the elements with the same indices from
+     * the lhs and rhs ValArray are being compared, allowing the tolerance defined
+     * byt "tol" parameter.
+     * \param rhs The rhs ValArray
+     * \param tol The absolute tolerance
+     * \returns true if the differences in each element-wise comparison is less
+     * or equal to tol.
+     */
+    bool IsAlmostEqual(const ValArray<T>& rhs, T tol) const;
+    /**
+     * \brief Get a data pointer to a specific 2D array for use in linear
+     * algebra libraries
+     * \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);
+    /**
+     * \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;
+    /**
+     * \brief Checks whether rhs and lhs ValArray objects have the same dimensions.
+     * \param rhs The rhs ValArray
+     * \returns true if the dimensions of lhs and rhs are equal, otherwise it returns false
+     */
+    bool EqualDims(const ValArray<T>& rhs) const;
+    /**
+     * \brief Function that asserts if the dimensions of lhs and rhs ValArray are
+     * not equal and prints a message with the matrices dimensions.
+     * \param rhs the rhs ValArray
+     */
+    void AssertEqualDims(const ValArray<T>& rhs) const;
+    /**
+     * \brief Single-element access operator[] that can be used to access a specific
+     * element of 1D ValArray. It mimics operator[] from std::vector.
+     * This function is introduced for compatibility with ns-3 usage of 1D arrays,
+     * which are usually represented through std::vector operators in spectrum
+     * and antenna module.
+     *
+     * \param index The index of the element to be returned
+     * \returns A reference to a specific element from the underlying std::valarray.
+     */
+    T& operator[](size_t index);
+    /**
+     * \brief Const access operator that can be used to access a specific element of
+     * 1D ValArray.
+     *
+     * \param index The index of the element to be returned
+     * \returns A const reference to a specific element from the underlying std::valarray.
+     */
+    const T& operator[](size_t index) const;
+    /**
+     * \brief Returns underlying values. This function allows to directly work
+     * with the underlying values, which can be faster then using access operators.
+     * \returns A const reference to the underlying std::valarray<T>.
+     */
+    const std::valarray<T>& GetValues() const;
+    /**
+     * \brief Alternative access operator to access a specific element.
+     * \param row the row index of the element to be obtained
+     * \param col the col index of the element to be obtained
+     * \param page the page index of the element to be obtained
+     * \return a reference to the element of this ValArray
+     */
+    T& Elem(size_t row, size_t col, size_t page);
+    /**
+     * \brief Alternative const access operator to access a specific element.
+     * \param row the row index of the element to be obtained
+     * \param col the column index of the element to be obtained
+     * \param page the page index of the element to be obtained
+     * \return a const reference to the element of this ValArray
+     */
+    const T& Elem(size_t row, size_t col, size_t page) const;
+
+  protected:
+    uint16_t m_numRows =
+        0; //!< The size of the first dimension, i.e., the number of rows of each 2D array
+    uint16_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
+    std::valarray<T> m_values; //!< The data values
+};
+
+/*************************************************
+ **  Class ValArray inline implementations
+ ************************************************/
+
+template <class T>
+inline uint16_t
+ValArray<T>::GetNumRows() const
+{
+    return m_numRows;
+};
+
+template <class T>
+inline uint16_t
+ValArray<T>::GetNumCols() const
+{
+    return m_numCols;
+};
+
+template <class T>
+inline uint16_t
+ValArray<T>::GetNumPages() const
+{
+    return m_numPages;
+};
+
+template <class T>
+inline size_t
+ValArray<T>::GetSize() const
+{
+    return m_values.size();
+}
+
+template <class T>
+inline T&
+ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex, uint16_t pageIndex)
+{
+    NS_ASSERT_MSG(rowIndex < m_numRows, "Row index out of bounds");
+    NS_ASSERT_MSG(colIndex < m_numCols, "Column index out of bounds");
+    NS_ASSERT_MSG(pageIndex < m_numPages, "Pages index out of bounds");
+    size_t index = (rowIndex + m_numRows * (colIndex + m_numCols * pageIndex));
+    return m_values[index];
+};
+
+template <class T>
+inline const T&
+ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex, uint16_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");
+    NS_ASSERT_MSG(pageIndex < m_numPages, "Pages index out of bounds");
+    size_t index = (rowIndex + m_numRows * (colIndex + m_numCols * pageIndex));
+    return m_values[index];
+};
+
+template <class T>
+inline T&
+ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex)
+{
+    NS_ASSERT_MSG(m_numPages == 1, "Cannot use 2D access operator for 3D ValArray.");
+    return (*this)(rowIndex, colIndex, 0);
+};
+
+template <class T>
+inline const T&
+ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex) const
+{
+    NS_ASSERT_MSG(m_numPages == 1, "Cannot use 2D access operator for 3D ValArray.");
+    return (*this)(rowIndex, colIndex, 0);
+};
+
+template <class T>
+inline T&
+ValArray<T>::operator()(uint16_t index)
+{
+    NS_ASSERT_MSG(index < m_values.size(),
+                  "Invalid index to 1D ValArray. The size of the array should be set through "
+                  "constructor.");
+    NS_ASSERT_MSG(((m_numRows == 1 || m_numCols == 1) && (m_numPages == 1)) ||
+                      (m_numRows == 1 && m_numCols == 1),
+                  "Access operator allowed only for 1D ValArray.");
+    return m_values[index];
+};
+
+template <class T>
+inline const T&
+ValArray<T>::operator()(uint16_t index) const
+{
+    NS_ASSERT_MSG(index < m_values.size(),
+                  "Invalid index to 1D ValArray.The size of the array should be set through "
+                  "constructor.");
+    NS_ASSERT_MSG(((m_numRows == 1 || m_numCols == 1) && (m_numPages == 1)) ||
+                      (m_numRows == 1 && m_numCols == 1),
+                  "Access operator allowed only for 1D ValArray.");
+    return m_values[index];
+};
+
+template <class T>
+inline ValArray<T>
+ValArray<T>::operator*(const T& rhs) const
+{
+    return ValArray<T>(m_numRows,
+                       m_numCols,
+                       m_numPages,
+                       m_values * std::valarray<T>(rhs, m_numRows * m_numCols * m_numPages));
+}
+
+template <class T>
+inline ValArray<T>
+ValArray<T>::operator+(const ValArray<T>& rhs) const
+{
+    AssertEqualDims(rhs);
+    return ValArray<T>(m_numRows, m_numCols, m_numPages, m_values + rhs.m_values);
+}
+
+template <class T>
+inline ValArray<T>
+ValArray<T>::operator-(const ValArray<T>& rhs) const
+{
+    AssertEqualDims(rhs);
+    return ValArray<T>(m_numRows, m_numCols, m_numPages, m_values - rhs.m_values);
+}
+
+template <class T>
+inline ValArray<T>
+ValArray<T>::operator-() const
+{
+    return ValArray<T>(m_numRows, m_numCols, m_numPages, -m_values);
+}
+
+template <class T>
+inline ValArray<T>&
+ValArray<T>::operator+=(const ValArray<T>& rhs)
+{
+    AssertEqualDims(rhs);
+    m_values += rhs.m_values;
+    return *this;
+}
+
+template <class T>
+inline ValArray<T>&
+ValArray<T>::operator-=(const ValArray<T>& rhs)
+{
+    AssertEqualDims(rhs);
+    m_values -= rhs.m_values;
+    return *this;
+}
+
+template <class T>
+inline T*
+ValArray<T>::GetPagePtr(uint16_t pageIndex)
+{
+    NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index.");
+    return &(m_values[m_numRows * m_numCols * pageIndex]);
+};
+
+template <class T>
+inline const T*
+ValArray<T>::GetPagePtr(uint16_t pageIndex) const
+{
+    NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index.");
+    return &(m_values[m_numRows * m_numCols * pageIndex]);
+};
+
+template <class T>
+inline bool
+ValArray<T>::EqualDims(const ValArray<T>& rhs) const
+{
+    return (m_numRows == rhs.m_numRows) && (m_numCols == rhs.m_numCols) &&
+           (m_numPages == rhs.m_numPages);
+}
+
+template <class T>
+inline T&
+ValArray<T>::operator[](size_t index)
+{
+    return (*this)(index);
+}
+
+template <class T>
+inline const T&
+ValArray<T>::operator[](size_t index) const
+{
+    return (*this)(index);
+}
+
+template <class T>
+inline const std::valarray<T>&
+ValArray<T>::GetValues() const
+{
+    return m_values;
+}
+
+template <class T>
+inline T&
+ValArray<T>::Elem(size_t row, size_t col, size_t page)
+{
+    return (*this)(row, col, page);
+};
+
+template <class T>
+inline const T&
+ValArray<T>::Elem(size_t row, size_t col, size_t page) const
+{
+    return (*this)(row, col, page);
+};
+
+/*************************************************
+ **  Class ValArray non-inline implementations
+ ************************************************/
+
+template <class T>
+ValArray<T>::ValArray(uint16_t numRows, uint16_t numCols, uint16_t numPages)
+    : m_numRows{numRows},
+      m_numCols{numCols},
+      m_numPages{numPages}
+{
+    m_values.resize(m_numRows * m_numCols * m_numPages);
+};
+
+template <class T>
+ValArray<T>::ValArray(const std::valarray<T>& values)
+    : m_numRows{(uint16_t)values.size()},
+      m_numCols{1},
+      m_numPages{1},
+      m_values{values}
+{
+}
+
+template <class T>
+ValArray<T>::ValArray(std::valarray<T>&& values)
+    : m_numRows{(uint16_t)values.size()},
+      m_numCols{1},
+      m_numPages{1},
+      m_values{std::move(values)}
+{
+}
+
+template <class T>
+ValArray<T>::ValArray(const std::vector<T>& values)
+    : m_numRows{(uint16_t)values.size()},
+      m_numCols{1},
+      m_numPages{1}
+{
+    m_values.resize(values.size());
+    std::copy(values.begin(), values.end(), std::begin(m_values));
+}
+
+template <class T>
+ValArray<T>::ValArray(uint16_t numRows, uint16_t numCols, const std::valarray<T>& values)
+    : m_numRows{numRows},
+      m_numCols{numCols},
+      m_numPages{1},
+      m_values{values}
+{
+    NS_ASSERT_MSG(m_numRows * m_numCols == values.size(),
+                  "Dimensions and the initialization array size do not match.");
+};
+
+template <class T>
+ValArray<T>::ValArray(uint16_t numRows, uint16_t numCols, std::valarray<T>&& values)
+    : m_numRows{numRows},
+      m_numCols{numCols},
+      m_numPages{1}
+{
+    NS_ASSERT_MSG(m_numRows * m_numCols == values.size(),
+                  "Dimensions and the initialization array size do not match.");
+    m_values = std::move(values);
+};
+
+template <class T>
+ValArray<T>::ValArray(uint16_t numRows,
+                      uint16_t numCols,
+                      uint16_t numPages,
+                      const std::valarray<T>& values)
+    : m_numRows{numRows},
+      m_numCols{numCols},
+      m_numPages{numPages},
+      m_values{values}
+{
+    NS_ASSERT_MSG(m_numRows * m_numCols * m_numPages == values.size(),
+                  "Dimensions and the initialization array size do not match.");
+};
+
+template <class T>
+ValArray<T>::ValArray(uint16_t numRows,
+                      uint16_t numCols,
+                      uint16_t numPages,
+                      std::valarray<T>&& values)
+    : m_numRows{numRows},
+      m_numCols{numCols},
+      m_numPages{numPages}
+{
+    NS_ASSERT_MSG(m_numRows * m_numCols * m_numPages == values.size(),
+                  "Dimensions and the initialization array size do not match.");
+    m_values = std::move(values);
+};
+
+template <class T>
+bool
+ValArray<T>::operator==(const ValArray<T>& rhs) const
+{
+    return EqualDims(rhs) &&
+           std::equal(std::begin(m_values), std::end(m_values), std::begin(rhs.m_values));
+}
+
+template <class T>
+bool
+ValArray<T>::operator!=(const ValArray<T>& rhs) const
+{
+    return !((*this) == rhs);
+}
+
+template <class T>
+bool
+ValArray<T>::IsAlmostEqual(const ValArray<T>& rhs, T tol) const
+{
+    return EqualDims(rhs) && std::equal(std::begin(m_values),
+                                        std::end(m_values),
+                                        std::begin(rhs.m_values),
+                                        [tol](T lhsValue, T rhsValue) {
+                                            return lhsValue == rhsValue ||
+                                                   std::abs(lhsValue - rhsValue) <= std::abs(tol);
+                                        });
+}
+
+template <class T>
+void
+ValArray<T>::AssertEqualDims(const ValArray<T>& rhs) const
+{
+    NS_ASSERT_MSG(EqualDims(rhs),
+                  "Dimensions mismatch: "
+                  "lhs (rows, cols, pages) = ("
+                      << m_numRows << ", " << m_numCols << ", " << m_numPages
+                      << ") and "
+                         "rhs (rows, cols, pages) = ("
+                      << rhs.m_numRows << ", " << rhs.m_numCols << ", " << rhs.m_numPages << ")");
+}
+
+/**
+ * \brief Overloads output stream operator.
+ * \tparam T the type of the ValArray for which will be called this function
+ * \param os a reference to the output stream
+ * \param a the ValArray instance using type T
+ * \return a reference to the output stream
+ */
+template <class T>
+std::ostream&
+operator<<(std::ostream& os, const ValArray<T>& a)
+{
+    os << "\n";
+    for (auto p = 0; p != a.GetNumPages(); ++p)
+    {
+        os << "Page " << p << ":\n";
+        for (auto i = 0; i != a.GetNumRows(); ++i)
+        {
+            for (auto j = 0; j != a.GetNumCols(); ++j)
+            {
+                os << "\t" << a(i, j, p);
+            }
+            os << "\n";
+        }
+    }
+    return os;
+}
+
+} // namespace ns3
+
+#endif // VAL_ARRAY_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/valgrind.h ns-3.38/src/core/model/valgrind.h
--- ns-3.37/src/core/model/valgrind.h	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/model/valgrind.h	2023-03-16 20:54:23.596868500 -0700
@@ -974,7 +974,7 @@
 
 /* Use these to write the name of your wrapper.  NOTE: duplicates
    VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h.  NOTE also: inserts
-   the default behaviour equivalance class tag "0000" into the name.
+   the default behaviour equivalence class tag "0000" into the name.
    See pub_tool_redir.h for details -- normally you don't need to
    think about this, though. */
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/version.cache ns-3.38/src/core/model/version.cache
--- ns-3.37/src/core/model/version.cache	2022-11-01 17:39:19.751728500 -0700
+++ ns-3.38/src/core/model/version.cache	2023-03-16 20:55:00.740985900 -0700
@@ -1,10 +1,10 @@
-CLOSEST_TAG = '"ns-3.37"'
-VERSION_COMMIT_HASH = '"g4407a9528"'
+CLOSEST_TAG = '"ns-3.38"'
+VERSION_COMMIT_HASH = '"g392e36833"'
 VERSION_DIRTY_FLAG = '0'
 VERSION_MAJOR = '3'
-VERSION_MINOR = '37'
+VERSION_MINOR = '38'
 VERSION_PATCH = '0'
 VERSION_RELEASE_CANDIDATE = '""'
-VERSION_TAG = '"ns-3.37"'
+VERSION_TAG = '"ns-3.38"'
 VERSION_TAG_DISTANCE = '0'
 VERSION_BUILD_PROFILE = 'default'
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/model/warnings.h ns-3.38/src/core/model/warnings.h
--- ns-3.37/src/core/model/warnings.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/model/warnings.h	2023-03-16 20:54:23.596868500 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2022 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>
+ */
+
+#ifndef NS3_WARNINGS_H
+#define NS3_WARNINGS_H
+
+/**
+ * \ingroup core
+ * \def NS_WARNING_POP
+ * Pops the diagnostic warning list from the stack, restoring it to the previous state.
+ * \sa NS_WARNING_PUSH
+ */
+
+/**
+ * \ingroup core
+ * \def NS_WARNING_PUSH
+ * Push the diagnostic warning list to the stack, allowing it to be restored later.
+ * \sa NS_WARNING_POP
+ */
+
+/**
+ * \ingroup core
+ * \def NS_WARNING_SILENCE_DEPRECATED
+ * Silences the "-Wdeprecated-declarations" warnings.
+ * \sa NS_WARNING_POP
+ */
+
+/**
+ * \ingroup core
+ * \def NS_WARNING_PUSH_DEPRECATED
+ * Save the current warning list and disables the ones about deprecated functions and classes.
+ *
+ * This macro can be used to silence deprecation warnings and should be used as a last resort
+ * to silence the compiler for very specific lines of code.
+ * The typical pattern is:
+ * \code
+ *   NS_WARNING_PUSH_DEPRECATED;
+ *   // call to a function or class that has been deprecated.
+ *   NS_WARNING_POP;
+ * \endcode
+ *
+ * Its use is, of course, not suggested unless strictly necessary.
+ */
+
+#if defined(_MSC_VER)
+// You can find the MSC warning codes at
+// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warnings-c4000-c5999
+#define NS_WARNING_PUSH __pragma(warning(push))
+#define NS_WARNING_SILENCE_DEPRECATED __pragma(warning(disable : 4996))
+#define NS_WARNING_POP __pragma(warning(pop))
+
+#elif defined(__GNUC__) || defined(__clang__)
+// Clang seems to understand these GCC pragmas
+#define NS_WARNING_PUSH _Pragma("GCC diagnostic push")
+#define NS_WARNING_SILENCE_DEPRECATED                                                              \
+    _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#define NS_WARNING_POP _Pragma("GCC diagnostic pop")
+
+#else
+#define NS_WARNING_PUSH
+#define NS_WARNING_SILENCE_DEPRECATED
+#define NS_WARNING_POP
+
+#endif
+
+#define NS_WARNING_PUSH_DEPRECATED                                                                 \
+    NS_WARNING_PUSH;                                                                               \
+    NS_WARNING_SILENCE_DEPRECATED
+
+#endif /* NS3_WARNINGS_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/attribute-test-suite.cc ns-3.38/src/core/test/attribute-test-suite.cc
--- ns-3.37/src/core/test/attribute-test-suite.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/test/attribute-test-suite.cc	2023-03-16 20:54:23.596868500 -0700
@@ -265,7 +265,7 @@
                               MakeBooleanChecker())
                 .AddAttribute("EnumTraceSource",
                               "help text",
-                              EnumValue(false),
+                              EnumValue(TEST_A),
                               MakeEnumAccessor(&AttributeObjectTest::m_enumSrc),
                               MakeEnumChecker(TEST_A, "TestA"))
                 .AddAttribute("ValueClassSource",
@@ -495,7 +495,7 @@
     Ptr<Derived> m_ptrInitialized;           //!< Pointer to Derived class.
     Ptr<Derived> m_ptrInitialized2;          //!< Pointer to Derived class.
     TracedValue<uint8_t> m_uintSrc;          //!< uint8_t Traced value.
-    TracedValue<enum Test_e> m_enumSrc;      //!< enum Traced value.
+    TracedValue<Test_e> m_enumSrc;           //!< enum Traced value.
     TracedValue<double> m_doubleSrc;         //!< double Traced value.
     TracedValue<bool> m_boolSrc;             //!< bool Traced value.
     Time m_timeWithBounds;                   //!< Time with bounds
@@ -908,16 +908,16 @@
     // When the object is first created, the Attribute should have the default
     // value.
     //
-    ok = CheckGetCodePaths(p, "TestFloat", "-1.1", DoubleValue((float)-1.1));
+    ok = CheckGetCodePaths(p, "TestFloat", "-1.1", DoubleValue(-1.1F));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
 
     //
     // Set the Attribute.
     //
-    ok = p->SetAttributeFailSafe("TestFloat", DoubleValue((float)2.3));
+    ok = p->SetAttributeFailSafe("TestFloat", DoubleValue(2.3F));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() to 2.3");
 
-    ok = CheckGetCodePaths(p, "TestFloat", "2.3", DoubleValue((float)2.3));
+    ok = CheckGetCodePaths(p, "TestFloat", "2.3", DoubleValue(2.3F));
     NS_TEST_ASSERT_MSG_EQ(ok,
                           true,
                           "Attribute not set properly by SetAttributeFailSafe() via DoubleValue");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/callback-test-suite.cc ns-3.38/src/core/test/callback-test-suite.cc
--- ns-3.37/src/core/test/callback-test-suite.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/test/callback-test-suite.cc	2023-03-16 20:54:23.596868500 -0700
@@ -97,7 +97,7 @@
 };
 
 /**
- * Variable to verify that a calback has been called.
+ * Variable to verify that a callback has been called.
  * @{
  */
 static bool gBasicCallbackTest5;
@@ -138,7 +138,7 @@
 }
 
 BasicCallbackTestCase::BasicCallbackTestCase()
-    : TestCase("Check basic Callback mechansim")
+    : TestCase("Check basic Callback mechanism")
 {
 }
 
@@ -294,7 +294,7 @@
 };
 
 /**
- * Variable to verify that a calback has been called.
+ * Variable to verify that a callback has been called.
  * @{
  */
 static bool gMakeCallbackTest5;
@@ -449,7 +449,7 @@
 };
 
 /**
- * Variable to verify that a calback has been called.
+ * Variable to verify that a callback has been called.
  * @{
  */
 static int gMakeBoundCallbackTest1;
@@ -913,16 +913,16 @@
     // Make sure that a callback pointing to a lambda and a copy of it compare equal,
     // after binding the first argument.
     //
-    Callback<double, int> target8b = target7b.Bind(1);
-    Callback<double, int> target8c(target7c, 1);
+    Callback<double, double> target8b = target7b.Bind(1);
+    Callback<double, double> target8c(target7c, 1);
     NS_TEST_ASSERT_MSG_EQ(target8b.IsEqual(target8c), true, "Equality test failed");
 
     //
     // Make sure that a callback pointing to a lambda and a copy of it compare equal,
     // after binding the first two arguments.
     //
-    Callback<double> target9b = target8b.Bind(2);
-    Callback<double> target9c(target8c, 2);
+    Callback<double> target9b = target8b.Bind(2.0);
+    Callback<double> target9c(target8c, 2.0);
     NS_TEST_ASSERT_MSG_EQ(target9b.IsEqual(target9c), true, "Equality test failed");
 
     //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/environment-variable-test-suite.cc ns-3.38/src/core/test/environment-variable-test-suite.cc
--- ns-3.37/src/core/test/environment-variable-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/test/environment-variable-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2022 Lawrence Livermore National Laboratory
+ *
+ * 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: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+#include "ns3/environment-variable.h"
+#include "ns3/test.h"
+
+#include <cstdlib> // getenv
+
+namespace ns3
+{
+
+namespace tests
+{
+
+/**
+ * \file
+ * \ingroup environ-var-tests
+ * Environment variable caching test suite
+ */
+
+/**
+ * \ingroup core-tests
+ * \defgroup environ-var-tests Environment variable caching tests
+ */
+
+/**
+ * \ingroup environ-var-tests
+ *
+ * EnvironmentVariable tests
+ */
+class EnvVarTestCase : public TestCase
+{
+  public:
+    /** Constructor */
+    EnvVarTestCase();
+
+    /** Destructor */
+    ~EnvVarTestCase() override;
+
+  private:
+    /** Run the tests */
+    void DoRun() override;
+
+    /** The key,value store */
+    using KeyValueStore = EnvironmentVariable::Dictionary::KeyValueStore;
+
+    /** The return type from EnvironmentVariable::Get() */
+    using KeyFoundType = EnvironmentVariable::KeyFoundType;
+
+    /**
+     * Set the test environment variable.
+     * \param where The test condition being checked.
+     * \param value The value to set.
+     */
+    void SetVariable(const std::string& where, const std::string& value);
+
+    /**
+     * Unset the test environment variable.
+     * \param where The test condition being checked.
+     */
+    void UnsetVariable(const std::string& where);
+
+    /**
+     * Read \p envValue and check that it contains only the key,value pairs
+     * from \p expect.
+     * \param where The test condition being checked.
+     * \param envValue The environment variable to parse and check.
+     * \param expect The set of key,values expected.
+     */
+    void Check(const std::string& where, const std::string& envValue, KeyValueStore expect);
+
+    /**
+     * Set and Check the variable.
+     * \param where The test condition being checked.
+     * \param envValue The environment variable to parse and check.
+     * \param expect The set of key,values expected.
+     */
+    void SetAndCheck(const std::string& where, const std::string& envValue, KeyValueStore expect);
+
+    /**
+     * Check the result from a Get.
+     * \param where The test condition being checked.
+     * \param key The key to check.
+     * \param expect The expected result.
+     */
+    void CheckGet(const std::string& where, const std::string& key, KeyFoundType expect);
+
+    /**
+     * Set, Check, and Get a variable.
+     * \param where The test condition being checked.
+     * \param envValue The environment variable to parse and check.
+     * \param expectDict The set of key,values expected.
+     * \param key The key to check.
+     * \param expectValue The expected result.
+     */
+    void SetCheckAndGet(const std::string& where,
+                        const std::string& envValue,
+                        KeyValueStore expectDict,
+                        const std::string& key,
+                        KeyFoundType expectValue);
+
+    /** Always use a non-default delimiter. */
+    const std::string m_delimiter{"|"};
+
+    /** Test environment variable name. */
+    const std::string m_variable{"NS_ENVVAR_TEST"};
+
+}; // class EnvVarTestCase
+
+EnvVarTestCase::EnvVarTestCase()
+    : TestCase("environment-variable-cache")
+{
+}
+
+EnvVarTestCase::~EnvVarTestCase()
+{
+    UnsetVariable("destructor");
+}
+
+void
+EnvVarTestCase::SetVariable(const std::string& where, const std::string& value)
+{
+    EnvironmentVariable::Clear();
+    bool ok = EnvironmentVariable::Set(m_variable, value);
+    NS_TEST_EXPECT_MSG_EQ(ok, true, where << ": failed to set variable");
+
+    // Double check
+    const char* envCstr = std::getenv(m_variable.c_str());
+    NS_TEST_EXPECT_MSG_NE(envCstr, nullptr, where << ": failed to retrieve variable just set");
+    NS_TEST_EXPECT_MSG_EQ(envCstr, value, where << ": failed to retrieve value just set");
+}
+
+void
+EnvVarTestCase::UnsetVariable(const std::string& where)
+{
+    EnvironmentVariable::Clear();
+    bool ok = EnvironmentVariable::Unset(where);
+    NS_TEST_EXPECT_MSG_EQ(ok, true, where << ": failed to unset variable");
+}
+
+void
+EnvVarTestCase::Check(const std::string& where, const std::string& envValue, KeyValueStore expect)
+{
+    auto dict = EnvironmentVariable::GetDictionary(m_variable, m_delimiter)->GetStore();
+
+    // Print the expect and which ones were found in dict
+    std::cout << "\n"
+              << where << " variable: '" << envValue << "', expect[" << expect.size() << "]"
+              << ", dict[" << dict.size() << "]\n";
+
+    NS_TEST_EXPECT_MSG_EQ(dict.size(), expect.size(), where << ": unequal dictionary sizes");
+
+    std::size_t i{0};
+    for (const auto& kv : expect)
+    {
+        std::cout << "    [" << i++ << "] '" << kv.first << "'\t'" << kv.second << "'";
+
+        auto loc = dict.find(kv.first);
+        bool found = loc != dict.end();
+        std::cout << (found ? "\tfound" : "\tNOT FOUND");
+        NS_TEST_EXPECT_MSG_EQ(found, true, where << ": expected key not found: " << kv.second);
+
+        if (found)
+        {
+            bool match = kv.second == loc->second;
+            if (match)
+            {
+                std::cout << ", match";
+            }
+            else
+            {
+                std::cout << ", NO MATCH: '" << loc->second << "'";
+            }
+            NS_TEST_EXPECT_MSG_EQ(kv.second, loc->second, where << ": key found, value mismatch");
+        }
+        std::cout << "\n";
+        ++i;
+    }
+
+    // Now just check dict for unexpected values
+    i = 0;
+    bool first{true};
+    for (const auto& kv : dict)
+    {
+        bool found = expect.find(kv.first) != expect.end();
+        if (!found)
+        {
+            std::cout << (first ? "Unexpected keys:" : "");
+            first = false;
+            std::cout << "    [" << i << "] '" << kv.first << "'\t'" << kv.second << "'"
+                      << " unexpected key, value\n";
+        }
+        ++i;
+    }
+}
+
+void
+EnvVarTestCase::SetAndCheck(const std::string& where,
+                            const std::string& envValue,
+                            KeyValueStore expect)
+{
+    SetVariable(where, envValue);
+    Check(where, envValue, expect);
+}
+
+void
+EnvVarTestCase::CheckGet(const std::string& where, const std::string& key, KeyFoundType expect)
+{
+    auto [found, value] = EnvironmentVariable::Get(m_variable, key, m_delimiter);
+    NS_TEST_EXPECT_MSG_EQ(found,
+                          expect.first,
+                          where << ": key '" << key << "' " << (expect.first ? "not " : "")
+                                << "found unexpectedly");
+    NS_TEST_EXPECT_MSG_EQ(value,
+                          expect.second,
+                          where << ": incorrect value for key '" << key << "'");
+}
+
+void
+EnvVarTestCase::SetCheckAndGet(const std::string& where,
+                               const std::string& envValue,
+                               KeyValueStore expectDict,
+                               const std::string& key,
+                               KeyFoundType expectValue)
+{
+    SetAndCheck(where, envValue, expectDict);
+    CheckGet(where, key, expectValue);
+}
+
+void
+EnvVarTestCase::DoRun()
+{
+    // Environment variable not set.
+    UnsetVariable("unset");
+    Check("unset", "", {});
+    auto [found, value] = EnvironmentVariable::Get(m_variable);
+    NS_TEST_EXPECT_MSG_EQ(found, false, "unset: variable found when not set");
+    NS_TEST_EXPECT_MSG_EQ(value.empty(), true, "unset: non-empty value from unset variable");
+
+    // Variable set but empty
+#ifndef __WIN32__
+    // Windows doesn't support environment variables with empty values
+    SetCheckAndGet("empty", "", {}, "", {true, ""});
+#endif
+
+    // Key not in variable
+    SetCheckAndGet("no-key",
+                   "not|the|right=value",
+                   {{"not", ""}, {"the", ""}, {"right", "value"}},
+                   "key",
+                   {false, ""});
+
+    // Key only (no delimiter):  "key"
+    SetCheckAndGet("key-only", "key", {{"key", ""}}, "key", {true, ""});
+
+    // Extra delimiter: ":key",  "key:"
+    SetCheckAndGet("front-|", "|key", {{"key", ""}}, "key", {true, ""});
+    SetCheckAndGet("back-|", "key|", {{"key", ""}}, "key", {true, ""});
+
+    // Double delimiter: "||key", "key||"
+    SetCheckAndGet("front-||", "||key", {{"key", ""}}, "key", {true, ""});
+    SetCheckAndGet("back-||", "key||", {{"key", ""}}, "key", {true, ""});
+
+    // Two keys: "key1|key2"
+    SetCheckAndGet("two keys", "key1|key2", {{"key1", ""}, {"key2", ""}}, "key1", {true, ""});
+    CheckGet("two keys", "key2", {true, ""});
+
+    // Extra/double delimiters| "||key1|key2", "|key1|key2", "key1||key2", "key1|key2|",
+    // "key1|key2||"
+    SetCheckAndGet("||two keys", "||key1|key2", {{"key1", ""}, {"key2", ""}}, "key1", {true, ""});
+    CheckGet("||two keys", "key2", {true, ""});
+    SetCheckAndGet("two keys||", "key1|key2||", {{"key1", ""}, {"key2", ""}}, "key1", {true, ""});
+    CheckGet("two keys||", "key2", {true, ""});
+
+    // Key=value: "key=value"
+    SetCheckAndGet("key-val", "key=value", {{"key", "value"}}, "key", {true, "value"});
+
+    // Mixed key-only, key=value| "key1|key2=|key3|key4=value"
+    SetCheckAndGet("mixed",
+                   "key1|key2=value|key3|key4=value",
+                   {{"key1", ""}, {"key2", "value"}, {"key3", ""}, {"key4", "value"}},
+                   "key1",
+                   {true, ""});
+    CheckGet("mixed", "key2", {true, "value"});
+    CheckGet("mixed", "key3", {true, ""});
+    CheckGet("mixed", "key4", {true, "value"});
+
+    // Empty/missing value| "key="
+    SetCheckAndGet("key=", "key=", {{"key", ""}}, "key", {true, ""});
+
+    // Extra `=`| "key==value"
+    SetCheckAndGet("key==", "key==", {{"key", "="}}, "key", {true, "="});
+
+    // Finish last line of verbose output
+    std::cout << std::endl;
+}
+
+/**
+ * \ingroup environ-var-tests
+ *
+ * Environment variable handling test suite.
+ */
+class EnvironmentVariableTestSuite : public TestSuite
+{
+  public:
+    EnvironmentVariableTestSuite();
+};
+
+EnvironmentVariableTestSuite::EnvironmentVariableTestSuite()
+    : TestSuite("environment-variables")
+{
+    AddTestCase(new EnvVarTestCase);
+}
+
+/**
+ * \ingroup environ-var-tests
+ * Static variable for test initialization.
+ */
+static EnvironmentVariableTestSuite g_EnvironmentVariableTestSuite;
+
+} // namespace tests
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/int64x64-test-suite.cc ns-3.38/src/core/test/int64x64-test-suite.cc
--- ns-3.37/src/core/test/int64x64-test-suite.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/test/int64x64-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -212,7 +212,7 @@
      * Check the int64x64 value for correctness.
      * \param value The int64x64_t value.
      * \param expectInt The expected integer value.
-     * \param expectRnd TThe expected rounding value.
+     * \param expectRnd The expected rounding value.
      */
     void Check(const int64x64_t value, const int64_t expectInt, const int64_t expectRnd);
 };
@@ -866,7 +866,7 @@
      * Check the int64x64 for correctness.
      * \param result The actual value.
      * \param expect The expected value.
-     * \param msg The error mesage to print.
+     * \param msg The error message to print.
      */
     void Check(const bool result, const bool expect, const std::string& msg);
 };
@@ -986,7 +986,7 @@
      * \param factor The factor used to invert the number.
      * \param result The value.
      * \param expect The expected value.
-     * \param msg The error mesage to print.
+     * \param msg The error message to print.
      * \param tolerance The allowed tolerance.
      */
     void CheckCase(const uint64_t factor,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/length-test-suite.cc ns-3.38/src/core/test/length-test-suite.cc
--- ns-3.37/src/core/test/length-test-suite.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/test/length-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -631,17 +631,19 @@
 
     double inputValue = 10;
 
-    std::map<Unit, Builder> TESTDATA{{Unit::Nanometer, NanoMeters},
-                                     {Unit::Micrometer, MicroMeters},
-                                     {Unit::Millimeter, MilliMeters},
-                                     {Unit::Centimeter, CentiMeters},
-                                     {Unit::Meter, Meters},
-                                     {Unit::Kilometer, KiloMeters},
-                                     {Unit::NauticalMile, NauticalMiles},
-                                     {Unit::Inch, Inches},
-                                     {Unit::Foot, Feet},
-                                     {Unit::Yard, Yards},
-                                     {Unit::Mile, Miles}};
+    std::map<Unit, Builder> TESTDATA{
+        {Unit::Nanometer, NanoMeters},
+        {Unit::Micrometer, MicroMeters},
+        {Unit::Millimeter, MilliMeters},
+        {Unit::Centimeter, CentiMeters},
+        {Unit::Meter, Meters},
+        {Unit::Kilometer, KiloMeters},
+        {Unit::NauticalMile, NauticalMiles},
+        {Unit::Inch, Inches},
+        {Unit::Foot, Feet},
+        {Unit::Yard, Yards},
+        {Unit::Mile, Miles},
+    };
 
     for (auto& entry : TESTDATA)
     {
@@ -670,10 +672,12 @@
 {
     using TestInput = std::pair<double, std::string>;
     using TestArgs = std::pair<double, double>;
-    std::map<TestInput, TestArgs> tests{{{5, "m"}, {5, 0}},
-                                        {{5, " m"}, {5, 0}},
-                                        {{5, "kilometer"}, {5e3, 0}},
-                                        {{5, " kilometer"}, {5e3, 0}}};
+    std::map<TestInput, TestArgs> tests{
+        {{5, "m"}, {5, 0}},
+        {{5, " m"}, {5, 0}},
+        {{5, "kilometer"}, {5e3, 0}},
+        {{5, " kilometer"}, {5e3, 0}},
+    };
 
     for (auto& entry : tests)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/matrix-array-test-suite.cc ns-3.38/src/core/test/matrix-array-test-suite.cc
--- ns-3.37/src/core/test/matrix-array-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/test/matrix-array-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2022 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
+ *
+ * Author: Biljana Bojovic <bbojovic@cttc.es>
+ */
+
+#include "ns3/log.h"
+#include "ns3/matrix-array.h"
+#include "ns3/test.h"
+
+/**
+ * \file
+ * \ingroup core-tests
+ * \ingroup matrixArray
+ * \ingroup matrixArray-tests
+ * MatrixArray test suite
+ */
+namespace ns3
+{
+
+namespace tests
+{
+
+NS_LOG_COMPONENT_DEFINE("MatrixArrayTest");
+
+/**
+ * \ingroup matrixArray-tests
+ *  MatrixArray test for testing constructors
+ */
+template <class T>
+class MatrixArrayTestCase : public TestCase
+{
+  public:
+    MatrixArrayTestCase<T>() = default;
+    /**
+     * Constructor
+     *
+     * \param [in] name reference name
+     */
+    MatrixArrayTestCase<T>(const std::string name);
+
+    /** Destructor. */
+    ~MatrixArrayTestCase<T>() override;
+    /**
+     * \brief Copy constructor.
+     * Instruct the compiler to generate the implicitly declared copy constructor
+     */
+    MatrixArrayTestCase<T>(const MatrixArrayTestCase<T>&) = default;
+    /**
+     * \brief Copy assignment operator.
+     * Instruct the compiler to generate the implicitly declared copy assignment operator.
+     * \return A reference to this MatrixArrayTestCase
+     */
+    MatrixArrayTestCase<T>& operator=(const MatrixArrayTestCase<T>&) = default;
+    /**
+     * \brief Move constructor.
+     * Instruct the compiler to generate the implicitly declared move constructor
+     */
+    MatrixArrayTestCase<T>(MatrixArrayTestCase<T>&&) = default;
+    /**
+     * \brief Move assignment operator.
+     * Instruct the compiler to generate the implicitly declared copy constructor
+     * \return A reference to this MatrixArrayTestCase
+     */
+    MatrixArrayTestCase<T>& operator=(MatrixArrayTestCase<T>&&) = default;
+
+  protected:
+  private:
+    void DoRun() override;
+};
+
+template <class T>
+MatrixArrayTestCase<T>::MatrixArrayTestCase(const std::string name)
+    : TestCase(name)
+{
+}
+
+template <class T>
+MatrixArrayTestCase<T>::~MatrixArrayTestCase<T>()
+{
+}
+
+template <class T>
+void
+MatrixArrayTestCase<T>::DoRun()
+{
+    // 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 (auto j = 0; j < m1.GetNumCols(); ++j)
+        {
+            m1(i, j) = 1;
+            m2(j, i) = 1;
+        }
+    }
+    MatrixArray<T> m3 = m1 * m2;
+    NS_LOG_INFO("m1:" << m1);
+    NS_LOG_INFO("m2:" << m2);
+    NS_LOG_INFO("m3 = m1 * m2:" << m3);
+    NS_TEST_ASSERT_MSG_EQ(m3.GetNumRows(),
+                          m1.GetNumRows(),
+                          "The number of rows in resulting matrix is not correct");
+    NS_TEST_ASSERT_MSG_EQ(m3.GetNumCols(),
+                          m2.GetNumCols(),
+                          "The number of cols in resulting matrix is not correct");
+    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 (auto j = 0; j < m3.GetNumRows(); ++j)
+        {
+            NS_TEST_ASSERT_MSG_EQ(std::real(m3(i, j)),
+                                  m1.GetNumCols(),
+                                  "The element value should be " << m1.GetNumCols());
+        }
+    }
+
+    // multiplication with a scalar value
+    MatrixArray<T> m4 = m3 * (static_cast<T>(5.0));
+    for (auto i = 0; i < m4.GetNumCols(); ++i)
+    {
+        for (auto j = 0; j < m4.GetNumRows(); ++j)
+        {
+            NS_TEST_ASSERT_MSG_EQ(m3(i, j) * (static_cast<T>(5.0)),
+                                  m4(i, j),
+                                  "The values are not equal");
+        }
+    }
+    NS_LOG_INFO("m4 = m3 * 5:" << m4);
+
+    // 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 (auto i = 0; i < m5.GetNumRows(); ++i)
+        {
+            for (auto j = 0; j < m5.GetNumCols(); ++j)
+            {
+                m5(i, j, p) = 1;
+                m6(j, i, p) = 1;
+            }
+        }
+    }
+    MatrixArray<T> m7 = m5 * m6;
+    NS_TEST_ASSERT_MSG_EQ(m7.GetNumRows(),
+                          m5.GetNumRows(),
+                          "The number of rows in resulting matrix is not correct");
+    NS_TEST_ASSERT_MSG_EQ(m7.GetNumCols(),
+                          m6.GetNumCols(),
+                          "The number of cols in resulting matrix is not correct");
+    NS_TEST_ASSERT_MSG_EQ(m7.GetNumRows(),
+                          m7.GetNumCols(),
+                          "The number of rows and cols should be equal");
+
+    for (auto p = 0; p < m7.GetNumPages(); ++p)
+    {
+        for (auto i = 0; i < m7.GetNumCols(); ++i)
+        {
+            for (auto j = 0; j < m7.GetNumRows(); ++j)
+            {
+                NS_TEST_ASSERT_MSG_EQ(std::real(m7(i, j, p)),
+                                      m5.GetNumCols(),
+                                      "The element value should be " << m5.GetNumCols());
+            }
+        }
+    }
+    // test ostream operator
+    NS_LOG_INFO("m5:" << m5);
+    NS_LOG_INFO("m6:" << m6);
+    NS_LOG_INFO("m7 = m5 * m6:" << m7);
+
+    // test transpose function
+    MatrixArray<T> m8 = m5.Transpose();
+    NS_TEST_ASSERT_MSG_EQ(m6, m8, "These two matrices should be equal");
+    NS_LOG_INFO("m8 = 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) &&
+                              (m9.GetNumPages() == 1),
+                          true,
+                          "Creation of vector is not correct.");
+
+    NS_LOG_INFO("Vector:" << m9);
+    NS_LOG_INFO("Vector after transposing:" << m9.Transpose());
+
+    // Test basic operators
+    MatrixArray<T> m10 =
+        MatrixArray<T>(m9.GetNumRows(), m9.GetNumCols(), m9.GetNumPages(), m9.GetValues());
+    NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
+    m10 -= m9;
+    NS_TEST_ASSERT_MSG_NE(m10, m9, "m10 and m9 should not be equal");
+    m10 += m9;
+    NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
+    m10 = m9;
+    NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
+    m10 = m9 + m9;
+    NS_TEST_ASSERT_MSG_NE(m10, m9, "m10 and m9 should not be equal");
+    m10 = m10 - m9;
+    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};
+    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());
+
+    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]);
+    }
+    MatrixArray<T> m11 = MatrixArray<T>(2, 3, 2, aCasted);
+    MatrixArray<T> m12 = MatrixArray<T>(3, 2, 2, bCasted);
+    MatrixArray<T> m13 = m11 * m12;
+    MatrixArray<T> m14 = MatrixArray<T>(2, 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:" << m11);
+    NS_LOG_INFO("m12:" << m12);
+    NS_LOG_INFO("m13 = matrixArrayA * matrixArrayB:" << m13);
+
+    // test MultiplyByLeftAndRightMatrix
+    std::valarray<int> d{1, 1, 1};
+    std::valarray<int> e{1, 1};
+    std::valarray<int> f{1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3};
+    std::valarray<int> g{12, 12};
+    std::valarray<T> dCasted(d.size());
+    std::valarray<T> eCasted(e.size());
+    std::valarray<T> fCasted(f.size());
+    std::valarray<T> gCasted(g.size());
+    for (size_t i = 0; i < d.size(); ++i)
+    {
+        dCasted[i] = static_cast<T>(d[i]);
+    }
+    for (size_t i = 0; i < e.size(); ++i)
+    {
+        eCasted[i] = static_cast<T>(e[i]);
+    }
+    for (size_t i = 0; i < f.size(); ++i)
+    {
+        fCasted[i] = static_cast<T>(f[i]);
+    }
+    for (size_t i = 0; i < g.size(); ++i)
+    {
+        gCasted[i] = static_cast<T>(g[i]);
+    }
+    MatrixArray<T> m15 = MatrixArray<T>(1, 3, dCasted);
+    MatrixArray<T> m16 = MatrixArray<T>(2, 1, eCasted);
+    MatrixArray<T> m17 = MatrixArray<T>(3, 2, 2, fCasted);
+    MatrixArray<T> m18 = MatrixArray<T>(1, 1, 2, gCasted);
+    MatrixArray<T> m19 = m17.MultiplyByLeftAndRightMatrix(m15, m16);
+    NS_TEST_ASSERT_MSG_EQ(m19, m18, "The matrices should be equal.");
+
+    // test MultiplyByLeftAndRightMatrix
+    std::valarray<int> h{1, 3, 2, 2, 4, 0};
+    std::valarray<int> j{2, 2, 3, 4, 1, 3, 0, 5};
+    std::valarray<int> k{1, 2, 0, 0, 2, 3, 4, 1, 2, 3, 4, 1, 1, 2, 0, 0, 2, 3, 4, 1, 2, 3, 4, 1};
+    std::valarray<int> l{144, 132, 128, 104, 144, 132, 128, 104};
+    std::valarray<T> hCasted(h.size());
+    std::valarray<T> jCasted(j.size());
+    std::valarray<T> kCasted(k.size());
+    std::valarray<T> lCasted(l.size());
+    for (size_t i = 0; i < h.size(); ++i)
+    {
+        hCasted[i] = static_cast<T>(h[i]);
+    }
+    for (size_t i = 0; i < j.size(); ++i)
+    {
+        jCasted[i] = static_cast<T>(j[i]);
+    }
+    for (size_t i = 0; i < k.size(); ++i)
+    {
+        kCasted[i] = static_cast<T>(k[i]);
+    }
+    for (size_t i = 0; i < l.size(); ++i)
+    {
+        lCasted[i] = static_cast<T>(l[i]);
+    }
+    MatrixArray<T> m20 = MatrixArray<T>(2, 3, hCasted);
+    MatrixArray<T> m21 = MatrixArray<T>(4, 2, jCasted);
+    MatrixArray<T> m22 = MatrixArray<T>(3, 4, 2, kCasted);
+    MatrixArray<T> m23 = MatrixArray<T>(2, 2, 2, lCasted);
+    MatrixArray<T> m24 = m22.MultiplyByLeftAndRightMatrix(m20, m21);
+    NS_TEST_ASSERT_MSG_EQ(m24, m23, "The matrices should be equal.");
+    NS_LOG_INFO("m20:" << m20);
+    NS_LOG_INFO("m21:" << m21);
+    NS_LOG_INFO("m22:" << m22);
+    NS_LOG_INFO("m24 = m20 * m22 * m21" << m24);
+
+    // test initialization with moving
+    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_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_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.");
+}
+
+/**
+ * \ingroup matrixArray-tests
+ * MatrixArray test suite
+ */
+class MatrixArrayTestSuite : public TestSuite
+{
+  public:
+    /** Constructor. */
+    MatrixArrayTestSuite();
+};
+
+MatrixArrayTestSuite::MatrixArrayTestSuite()
+    : TestSuite("matrix-array-test")
+{
+    AddTestCase(new MatrixArrayTestCase<double>("Test MatrixArray<double>"));
+    AddTestCase(
+        new MatrixArrayTestCase<std::complex<double>>("Test MatrixArray<std::complex<double>>"));
+    AddTestCase(new MatrixArrayTestCase<int>("Test MatrixArray<int>"));
+}
+
+/**
+ * \ingroup matrixArray-tests
+ * MatrixArrayTestSuite instance variable.
+ */
+static MatrixArrayTestSuite g_matrixArrayTestSuite;
+
+} // namespace tests
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/names-test-suite.cc ns-3.38/src/core/test/names-test-suite.cc
--- ns-3.37/src/core/test/names-test-suite.cc	2022-11-01 17:37:24.475395000 -0700
+++ ns-3.38/src/core/test/names-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -713,7 +713,7 @@
 
     Ptr<TestObject> objectNotThere = CreateObject<TestObject>();
     found = Names::FindPath(objectNotThere);
-    NS_TEST_ASSERT_MSG_EQ(found, "", "Unexpectedly found a non-existent Object");
+    NS_TEST_ASSERT_MSG_EQ(found.empty(), true, "Unexpectedly found a non-existent Object");
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/random-variable-stream-test-suite.cc ns-3.38/src/core/test/random-variable-stream-test-suite.cc
--- ns-3.37/src/core/test/random-variable-stream-test-suite.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/core/test/random-variable-stream-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -1835,7 +1835,7 @@
     double lambda = 1.0;
 
     // Note that Erlang distribution is equal to the gamma distribution
-    // when k is an iteger, which is why the gamma distribution's cdf
+    // when k is an integer, which is why the gamma distribution's cdf
     // function can be used here.
     for (std::size_t i = 0; i < N_BINS; ++i)
     {
@@ -1930,7 +1930,7 @@
     double lambda = 1.0;
 
     // Note that Erlang distribution is equal to the gamma distribution
-    // when k is an iteger, which is why the gamma distribution's cdf
+    // when k is an integer, which is why the gamma distribution's cdf
     // function can be used here.
     for (std::size_t i = 0; i < N_BINS; ++i)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/simulator-test-suite.cc ns-3.38/src/core/test/simulator-test-suite.cc
--- ns-3.37/src/core/test/simulator-test-suite.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/core/test/simulator-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -73,7 +73,7 @@
      */
     uint64_t NowUs();
     /**
-     * Checks that the events has been detroyed.
+     * Checks that the events has been destroyed.
      */
     void Destroy();
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/splitstring-test-suite.cc ns-3.38/src/core/test/splitstring-test-suite.cc
--- ns-3.37/src/core/test/splitstring-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/test/splitstring-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2022 Lawrence Livermore National Laboratory
+ *
+ * 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: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+#include "ns3/string.h"
+#include "ns3/test.h"
+
+namespace ns3
+{
+
+namespace tests
+{
+
+/**
+ * \file
+ * \ingroup core-tests
+ * SplitString test suite implementation.
+ */
+
+/**
+ * \ingroup core-tests
+ * \defgroup environ-var-tests Environment variable caching tests
+ */
+
+/**
+ * \ingroup core-tests
+ *
+ * SplitString tests.
+ */
+class SplitStringTestCase : public TestCase
+{
+  public:
+    /** Constructor */
+    SplitStringTestCase();
+
+    /** Destructor */
+    ~SplitStringTestCase() override = default;
+
+  private:
+    /** Run the tests */
+    void DoRun() override;
+
+    /**
+     * Read \p str and check that it contains only the key,value pairs
+     * from \p expect.
+     * \param where The test condition being checked.
+     * \param str The environment variable to parse and check.
+     * \param expect The set of key,values expected.
+     */
+    void Check(const std::string& where, const std::string& str, const StringVector& expect);
+
+    /** Test suite delimiter. */
+    const std::string m_delimiter{":|:"};
+
+}; // class SplitStringTestCase
+
+SplitStringTestCase::SplitStringTestCase()
+    : TestCase("split-string")
+{
+}
+
+void
+SplitStringTestCase::Check(const std::string& where,
+                           const std::string& str,
+                           const StringVector& expect)
+{
+    const StringVector res = SplitString(str, m_delimiter);
+
+    // Print the res and expect
+    std::cout << where << ": '" << str << "'\nindex\texpect[" << expect.size() << "]\t\tresult["
+              << res.size() << "]" << (expect.size() != res.size() ? "\tFAIL SIZE" : "")
+              << "\n    ";
+    NS_TEST_EXPECT_MSG_EQ(expect.size(),
+                          res.size(),
+                          "res and expect have different number of entries");
+    for (std::size_t i = 0; i < std::max(res.size(), expect.size()); ++i)
+    {
+        const std::string r = (i < res.size() ? res[i] : "''" + std::to_string(i));
+        const std::string e = (i < expect.size() ? expect[i] : "''" + std::to_string(i));
+        const bool ok = (r == e);
+        std::cout << i << "\t'" << e << (ok ? "'\t== '" : "'\t!= '") << r
+                  << (!ok ? "'\tFAIL MATCH" : "'") << "\n    ";
+        NS_TEST_EXPECT_MSG_EQ(e, r, "res[i] does not match expect[i]");
+    }
+    std::cout << std::endl;
+}
+
+void
+SplitStringTestCase::DoRun()
+{
+    // Test points
+
+    // Empty string
+    Check("empty", "", {""});
+
+    // No delimiter
+    Check("no-delim", "token", {"token"});
+
+    // Extra leading, trailing delimiter: ":string",  "string:"
+    Check("front-:|:", ":|:token", {"", "token"});
+    Check("back-:|:", "token:|:", {"token", ""});
+
+    // Double delimiter: ":|::|:token", "token:|::|:"
+    Check("front-:|::|:", ":|::|:token", {"", "", "token"});
+    Check("back-:|::|:", "token:|::|:", {"token", "", ""});
+
+    // Two tokens: "token1:|:token2"
+    Check("two", "token1:|:token2", {"token1", "token2"});
+
+    // Extra/double delimiters:|: ":|::|:token1:|:token2", ":|:token1:|:token2",
+    // "token1:|::|:token2", "token1:|:token2:|:",
+    Check(":|::|:two", ":|::|:token1:|:token2", {"", "", "token1", "token2"});
+    Check(":|:one:|:two", ":|:token1:|:token2", {"", "token1", "token2"});
+    Check("double:|:", "token1:|::|:token2", {"token1", "", "token2"});
+    Check("two:|:", "token1:|:token2:|::|:", {"token1", "token2", "", ""});
+}
+
+/**
+ * \ingroup typeid-tests
+ *
+ * TypeId test suites.
+ */
+class SplitStringTestSuite : public TestSuite
+{
+  public:
+    SplitStringTestSuite();
+};
+
+SplitStringTestSuite::SplitStringTestSuite()
+    : TestSuite("split-string")
+{
+    AddTestCase(new SplitStringTestCase);
+}
+
+/**
+ * \ingroup environ-var-tests
+ * Static variable for test initialization.
+ */
+static SplitStringTestSuite g_SplitStringTestSuite;
+
+} // namespace tests
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/threaded-test-suite.cc ns-3.38/src/core/test/threaded-test-suite.cc
--- ns-3.37/src/core/test/threaded-test-suite.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/core/test/threaded-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -289,11 +289,16 @@
     ThreadedSimulatorTestSuite()
         : TestSuite("threaded-simulator")
     {
-        std::string simulatorTypes[] = {"ns3::RealtimeSimulatorImpl", "ns3::DefaultSimulatorImpl"};
-        std::string schedulerTypes[] = {"ns3::ListScheduler",
-                                        "ns3::HeapScheduler",
-                                        "ns3::MapScheduler",
-                                        "ns3::CalendarScheduler"};
+        std::string simulatorTypes[] = {
+            "ns3::RealtimeSimulatorImpl",
+            "ns3::DefaultSimulatorImpl",
+        };
+        std::string schedulerTypes[] = {
+            "ns3::ListScheduler",
+            "ns3::HeapScheduler",
+            "ns3::MapScheduler",
+            "ns3::CalendarScheduler",
+        };
         unsigned int threadCounts[] = {0, 2, 10, 20};
         ObjectFactory factory;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/time-test-suite.cc ns-3.38/src/core/test/time-test-suite.cc
--- ns-3.37/src/core/test/time-test-suite.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/core/test/time-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -94,7 +94,7 @@
      * \tparam T type of division value
      *
      * \param t Time value to divide
-     * \param expected Expected result of the divsion
+     * \param expected Expected result of the division
      * \param val Value to divide by
      * \param msg Error message to print if test fails
      */
@@ -152,7 +152,7 @@
 
     t1 = Time(101LL * oneSec);
     NS_TEST_ASSERT_MSG_EQ((t2 % t1).GetSeconds(), 81, "Remainder Operation (2000 % 101 = 81)");
-    NS_TEST_ASSERT_MSG_EQ(Div(t2, t1), 19, "Modular Divison");
+    NS_TEST_ASSERT_MSG_EQ(Div(t2, t1), 19, "Modular Division");
     NS_TEST_ASSERT_MSG_EQ(Rem(t2, t1).GetSeconds(), 81, "Remainder Operation (2000 % 101 = 81)");
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/core/test/val-array-test-suite.cc ns-3.38/src/core/test/val-array-test-suite.cc
--- ns-3.37/src/core/test/val-array-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/core/test/val-array-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2022 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
+ *
+ * Author: Biljana Bojovic <bbojovic@cttc.es>
+ */
+
+#include "ns3/log.h"
+#include "ns3/test.h"
+#include "ns3/val-array.h"
+
+/**
+ * \ingroup core-tests
+ */
+
+namespace ns3
+{
+namespace tests
+{
+
+NS_LOG_COMPONENT_DEFINE("ValArrayTest");
+
+/**
+ * @brief ValArray test case for testing ValArray class
+ *
+ * @tparam T the template parameter that can be a complex number, double or int
+ */
+template <class T>
+class ValArrayTestCase : public TestCase
+{
+  public:
+    /** Default constructor*/
+    ValArrayTestCase<T>() = default;
+    /**
+     * Constructor
+     *
+     * \param [in] name reference name
+     */
+    ValArrayTestCase<T>(const std::string name);
+
+    /** Destructor. */
+    ~ValArrayTestCase<T>() override;
+    /**
+     * \brief Copy constructor.
+     * Instruct the compiler to generate the implicitly declared copy constructor
+     */
+    ValArrayTestCase<T>(const ValArrayTestCase<T>&) = default;
+    /**
+     * \brief Copy assignment operator.
+     * Instruct the compiler to generate the implicitly declared copy assignment operator.
+     * \return A reference to this ValArrayTestCase
+     */
+    ValArrayTestCase<T>& operator=(const ValArrayTestCase<T>&) = default;
+    /**
+     * \brief Move constructor.
+     * Instruct the compiler to generate the implicitly declared move constructor
+     */
+    ValArrayTestCase<T>(ValArrayTestCase<T>&&) = default;
+    /**
+     * \brief Move assignment operator.
+     * Instruct the compiler to generate the implicitly declared copy constructor
+     * \return A reference to this ValArrayTestCase
+     */
+    ValArrayTestCase<T>& operator=(ValArrayTestCase<T>&&) = default;
+
+  private:
+    void DoRun() override;
+};
+
+template <class T>
+ValArrayTestCase<T>::ValArrayTestCase(const std::string name)
+    : TestCase(name)
+{
+}
+
+template <class T>
+ValArrayTestCase<T>::~ValArrayTestCase<T>()
+{
+}
+
+template <class T>
+void
+ValArrayTestCase<T>::DoRun()
+{
+    ValArray<T> v1 = ValArray<T>(2, 3);
+    for (uint16_t i = 0; i < v1.GetNumRows(); ++i)
+    {
+        for (uint16_t j = 0; j < v1.GetNumCols(); ++j)
+        {
+            v1(i, j) = 1;
+        }
+    }
+
+    ValArray<T> v2 = ValArray<T>(v1);
+    NS_TEST_ASSERT_MSG_EQ(v1.GetNumRows(), v2.GetNumRows(), "The number of rows are not equal.");
+    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 (uint16_t j = 0; j < v1.GetNumCols(); ++j)
+        {
+            NS_TEST_ASSERT_MSG_EQ(v1(i, j), v2(i, j), "The elements are not equal.");
+        }
+    }
+
+    // test assign constructor
+    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 (uint16_t j = 0; j < v1.GetNumCols(); ++j)
+        {
+            NS_TEST_ASSERT_MSG_EQ(v1(i, j), v2(i, j), "The elements are not equal.");
+        }
+    }
+
+    // test move assignment operator
+    ValArray<T> v4;
+    NS_LOG_INFO("v1 size before move: " << v1.GetSize());
+    NS_LOG_INFO("v4 size before move: " << v4.GetSize());
+    size_t v1size = v1.GetSize();
+    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 (uint16_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.");
+        }
+    }
+
+    // test move constructor
+    NS_LOG_INFO("v3 size before move: " << v3.GetSize());
+    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 (uint16_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.");
+        }
+    }
+
+    // test constructor with initialization valArray
+    std::valarray<int> initArray1{0, 1, 2, 3, 4, 5, 6, 7};
+    std::valarray<T> valArray1(initArray1.size()); // length is 8 elements
+    for (size_t i = 0; i < initArray1.size(); i++)
+    {
+        valArray1[i] = static_cast<T>(initArray1[i]);
+    }
+    ValArray<T> v6 = ValArray<T>(2, 4, valArray1);
+
+    // test constructor that moves valArray
+    NS_LOG_INFO("valarray1 size before move: " << valArray1.size());
+    ValArray<T> v11 = ValArray<T>(2, 4, std::move(valArray1));
+    NS_LOG_INFO("valarray1 size after move: " << valArray1.size());
+    NS_LOG_INFO("v11 size after move: " << v11.GetSize());
+
+    // 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)
+    {
+        for (uint16_t j = 0; j < v6.GetNumCols(); ++j)
+        {
+            NS_TEST_ASSERT_MSG_EQ(v6(i, j),
+                                  static_cast<T>(initArray2[testIndex]),
+                                  "The values are not equal.");
+            testIndex++;
+        }
+    }
+
+    // test constructor with initialization valArray for 3D array
+    std::valarray<int> initArray3{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7};
+    std::valarray<T> valArray2(initArray3.size()); // length is 8 elements
+    for (size_t i = 0; i < initArray3.size(); i++)
+    {
+        valArray2[i] = static_cast<T>(initArray3[i]);
+    }
+
+    ValArray<T> v7 = ValArray<T>(2, 4, 2, valArray2);
+    // 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> 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 (uint16_t i = 0; i < v7.GetNumRows(); ++i)
+        {
+            for (uint16_t j = 0; j < v7.GetNumCols(); ++j)
+            {
+                NS_TEST_ASSERT_MSG_EQ(v7(i, j, p),
+                                      static_cast<T>(initArray4[testIndex]),
+                                      "The values are not equal.");
+                testIndex++;
+            }
+        }
+    }
+
+    // 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 (uint16_t i = 0; i < v8.GetNumRows(); ++i)
+        {
+            for (uint16_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),
+                                      "The values are not equal");
+            }
+        }
+    }
+
+    NS_LOG_INFO("v8 = v7 * 5:" << v8);
+    // test +, - (binary, unary) operators
+    NS_LOG_INFO("v8 + v8" << v8 + v8);
+    NS_LOG_INFO("v8 - v8" << v8 - v8);
+    NS_LOG_INFO("-v8" << -v8);
+
+    // test +=  and -= assignment operators
+    ValArray<T> v9(v8.GetNumRows(), v8.GetNumCols(), v8.GetNumPages());
+    v9 += v8;
+    NS_LOG_INFO("v9 += v8" << v9);
+    ValArray<T> v10(v8.GetNumRows(), v8.GetNumCols(), v8.GetNumPages());
+    v10 -= v8;
+    NS_LOG_INFO("v10 -= v8" << v10);
+
+    // test == and != operators
+    NS_TEST_ASSERT_MSG_EQ(bool(v9 == v8), true, "Matrices v8 and v9 should be equal");
+    NS_TEST_ASSERT_MSG_EQ(bool(v10 == v8), false, "Matrices v8 and v10 should not be equal");
+    NS_TEST_ASSERT_MSG_EQ(bool(v10 != v8), true, "Matrices v8 and v10 should not be equal");
+    // test whether arrays are equal when they have different lengths
+    NS_TEST_ASSERT_MSG_NE(ValArray<int>(std::valarray({1, 2, 3})),
+                          ValArray<int>(std::valarray({1, 2, 3, 4})),
+                          "Arrays should not be equal, they have different dimensions.");
+
+    // test the function IsAlmostEqual
+    v9(0, 0, 0) = v9(0, 0, 0) + static_cast<T>(1);
+    NS_TEST_ASSERT_MSG_EQ(v9.IsAlmostEqual(v8, 2) && (v9 != v8),
+                          true,
+                          "Matrices should be almost equal, but not equal.");
+
+    // test the initialization with std::vector
+    ValArray<T> v12 = ValArray(std::vector<T>({1, 2, 3}));
+    NS_LOG_INFO("v12:" << v12);
+}
+
+/**
+ * \ingroup valArray-tests
+ * ValArray test suite
+ *
+ * \brief The test checks the correct behaviour of ValArray class
+ */
+class ValArrayTestSuite : public TestSuite
+{
+  public:
+    /** Constructor. */
+    ValArrayTestSuite();
+};
+
+ValArrayTestSuite::ValArrayTestSuite()
+    : TestSuite("val-array-test")
+{
+    AddTestCase(new ValArrayTestCase<double>("Test ValArray<double>"));
+    AddTestCase(new ValArrayTestCase<std::complex<double>>("Test ValArray<std::complex<double>>"));
+    AddTestCase(new ValArrayTestCase<int>("Test ValArray<int>"));
+}
+
+/**
+ * \ingroup valArray-tests
+ * ValArrayTestSuite instance variable.
+ */
+static ValArrayTestSuite g_valArrayTestSuite;
+
+} // namespace tests
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/doc/csma.rst ns-3.38/src/csma/doc/csma.rst
--- ns-3.37/src/csma/doc/csma.rst	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/doc/csma.rst	2023-03-16 20:54:23.600868500 -0700
@@ -210,24 +210,24 @@
 ``Nodes``) as you need on your network::
 
   NodeContainer csmaNodes;
-  csmaNodes.Create (nCsmaNodes);
+  csmaNodes.Create(nCsmaNodes);
 
 Once you have your nodes, you need to instantiate a ``CsmaHelper`` and set any
 attributes you may want to change.::
 
   CsmaHelper csma;
-  csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps"));
-  csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560)));
+  csma.SetChannelAttribute("DataRate", StringValue("100Mbps"));
+  csma.SetChannelAttribute("Delay", TimeValue(NanoSeconds(6560)));
 
-  csma.SetDeviceAttribute ("EncapsulationMode", StringValue ("Dix"));
-  csma.SetDeviceAttribute ("FrameSize", UintegerValue (2000));
+  csma.SetDeviceAttribute("EncapsulationMode", StringValue("Dix"));
+  csma.SetDeviceAttribute("FrameSize", UintegerValue(2000));
 
 Once the attributes are set, all that remains is to create the devices and
 install them on the required nodes, and to connect the devices together using a
 CSMA channel. When we create the net devices, we add them to a container to
 allow you to use them in the future. This all takes just one line of code.::
 
-  NetDeviceContainer csmaDevices = csma.Install (csmaNodes);
+  NetDeviceContainer csmaDevices = csma.Install(csmaNodes);
 
 We recommend thinking carefully about changing these Attributes, since
 it can result in behavior that surprises users.  We allow this because
@@ -370,4 +370,3 @@
 network you could build or buy, it does provide us with some useful
 functionality.  You should, however, understand that it is explicitly not
 Ethernet or any flavor of IEEE 802.3 but an interesting subset.
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/examples/csma-broadcast.cc ns-3.38/src/csma/examples/csma-broadcast.cc
--- ns-3.37/src/csma/examples/csma-broadcast.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/examples/csma-broadcast.cc	2023-03-16 20:54:23.600868500 -0700
@@ -121,4 +121,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/examples/csma-multicast.cc ns-3.38/src/csma/examples/csma-multicast.cc
--- ns-3.37/src/csma/examples/csma-multicast.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/examples/csma-multicast.cc	2023-03-16 20:54:23.600868500 -0700
@@ -178,4 +178,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/examples/csma-one-subnet.cc ns-3.38/src/csma/examples/csma-one-subnet.cc
--- ns-3.37/src/csma/examples/csma-one-subnet.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/examples/csma-one-subnet.cc	2023-03-16 20:54:23.600868500 -0700
@@ -138,4 +138,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/examples/csma-packet-socket.cc ns-3.38/src/csma/examples/csma-packet-socket.cc
--- ns-3.37/src/csma/examples/csma-packet-socket.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/examples/csma-packet-socket.cc	2023-03-16 20:54:23.600868500 -0700
@@ -40,7 +40,7 @@
 
 NS_LOG_COMPONENT_DEFINE("CsmaPacketSocketExample");
 
-/// Ouput stream.
+/// Output stream.
 std::ofstream g_os;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/examples/csma-ping.cc ns-3.38/src/csma/examples/csma-ping.cc
--- ns-3.37/src/csma/examples/csma-ping.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/examples/csma-ping.cc	2023-03-16 20:54:23.600868500 -0700
@@ -47,25 +47,31 @@
 static void
 SinkRx(Ptr<const Packet> p, const Address& ad)
 {
-    // std::cout << *p << std::endl;
+    std::cout << *p << std::endl;
 }
 
 /**
  * Ping RTT trace sink
  *
  * \param context The context.
+ * \param seqNo The Sequence Number.
  * \param rtt The RTT.
  */
 static void
-PingRtt(std::string context, Time rtt)
+PingRtt(std::string context, uint16_t seqNo, Time rtt)
 {
-    // std::cout << context << " " << rtt << std::endl;
+    std::cout << context << " " << seqNo << " " << rtt << std::endl;
 }
 
 int
 main(int argc, char* argv[])
 {
+    bool verbose{false};
+
     CommandLine cmd(__FILE__);
+    cmd.AddValue("verbose",
+                 "print received background traffic packets and ping RTT values",
+                 verbose);
     cmd.Parse(argc, argv);
 
     // Here, we will explicitly create four nodes.
@@ -110,7 +116,7 @@
     apps.Stop(Seconds(11.0));
 
     NS_LOG_INFO("Create pinger");
-    V4PingHelper ping = V4PingHelper(addresses.GetAddress(2));
+    PingHelper ping(addresses.GetAddress(2));
     NodeContainer pingers;
     pingers.Add(c.Get(0));
     pingers.Add(c.Get(1));
@@ -123,11 +129,14 @@
     // first, pcap tracing in non-promiscuous mode
     csma.EnablePcapAll("csma-ping", false);
 
-    // then, print what the packet sink receives.
-    Config::ConnectWithoutContext("/NodeList/3/ApplicationList/0/$ns3::PacketSink/Rx",
-                                  MakeCallback(&SinkRx));
-    // finally, print the ping rtts.
-    Config::Connect("/NodeList/*/ApplicationList/*/$ns3::V4Ping/Rtt", MakeCallback(&PingRtt));
+    if (verbose)
+    {
+        // then, print what the packet sink receives.
+        Config::ConnectWithoutContext("/NodeList/3/ApplicationList/0/$ns3::PacketSink/Rx",
+                                      MakeCallback(&SinkRx));
+        // finally, print the ping rtts.
+        Config::Connect("/NodeList/*/ApplicationList/*/$ns3::Ping/Rtt", MakeCallback(&PingRtt));
+    }
 
     Packet::EnablePrinting();
 
@@ -135,4 +144,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/examples/csma-raw-ip-socket.cc ns-3.38/src/csma/examples/csma-raw-ip-socket.cc
--- ns-3.37/src/csma/examples/csma-raw-ip-socket.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/examples/csma-raw-ip-socket.cc	2023-03-16 20:54:23.600868500 -0700
@@ -121,4 +121,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/model/backoff.cc ns-3.38/src/csma/model/backoff.cc
--- ns-3.37/src/csma/model/backoff.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/model/backoff.cc	2023-03-16 20:54:23.600868500 -0700
@@ -88,7 +88,7 @@
 }
 
 bool
-Backoff::MaxRetriesReached()
+Backoff::MaxRetriesReached() const
 {
     return (m_numBackoffRetries >= m_maxRetries);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/model/backoff.h ns-3.38/src/csma/model/backoff.h
--- ns-3.37/src/csma/model/backoff.h	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/model/backoff.h	2023-03-16 20:54:23.600868500 -0700
@@ -97,7 +97,7 @@
     /**
      * \return True if the maximum number of retries has been reached
      */
-    bool MaxRetriesReached();
+    bool MaxRetriesReached() const;
 
     /**
      * Increments the number of retries by 1.
@@ -116,7 +116,7 @@
 
   private:
     /**
-     * Number of times that the transmitter has tried to unsuccessfully transmit the current packet.
+     * Number of times that the transmitter has tried to unsuccessfuly transmit the current packet.
      */
     uint32_t m_numBackoffRetries;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/model/csma-channel.cc ns-3.38/src/csma/model/csma-channel.cc
--- ns-3.37/src/csma/model/csma-channel.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/model/csma-channel.cc	2023-03-16 20:54:23.600868500 -0700
@@ -362,7 +362,7 @@
 }
 
 bool
-CsmaDeviceRec::IsActive()
+CsmaDeviceRec::IsActive() const
 {
     return active;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/model/csma-channel.h ns-3.38/src/csma/model/csma-channel.h
--- ns-3.37/src/csma/model/csma-channel.h	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/model/csma-channel.h	2023-03-16 20:54:23.600868500 -0700
@@ -65,7 +65,7 @@
      * \return If the net device pointed to by the devicePtr is active
      * and ready to RX/TX.
      */
-    bool IsActive();
+    bool IsActive() const;
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/model/csma-net-device.cc ns-3.38/src/csma/model/csma-net-device.cc
--- ns-3.37/src/csma/model/csma-net-device.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/model/csma-net-device.cc	2023-03-16 20:54:23.600868500 -0700
@@ -223,7 +223,7 @@
 }
 
 void
-CsmaNetDevice::SetEncapsulationMode(enum EncapsulationMode mode)
+CsmaNetDevice::SetEncapsulationMode(EncapsulationMode mode)
 {
     NS_LOG_FUNCTION(mode);
 
@@ -274,14 +274,14 @@
 }
 
 bool
-CsmaNetDevice::IsSendEnabled()
+CsmaNetDevice::IsSendEnabled() const
 {
     NS_LOG_FUNCTION_NOARGS();
     return m_sendEnable;
 }
 
 bool
-CsmaNetDevice::IsReceiveEnabled()
+CsmaNetDevice::IsReceiveEnabled() const
 {
     NS_LOG_FUNCTION_NOARGS();
     return m_receiveEnable;
@@ -368,7 +368,7 @@
 
         //
         // All Ethernet frames must carry a minimum payload of 46 bytes.  The
-        // LLC SNAP header counts as part of this payload.  We need to padd out
+        // LLC SNAP header counts as part of this payload.  We need to pad out
         // if we don't have enough bytes.  These must be real bytes since they
         // will be written to pcap files and compared in regression trace files.
         //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma/model/csma-net-device.h ns-3.38/src/csma/model/csma-net-device.h
--- ns-3.37/src/csma/model/csma-net-device.h	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma/model/csma-net-device.h	2023-03-16 20:54:23.600868500 -0700
@@ -178,7 +178,7 @@
      *
      * \returns True if the send side is enabled, otherwise false.
      */
-    bool IsSendEnabled();
+    bool IsSendEnabled() const;
 
     /**
      * Enable or disable the send side of the network device.
@@ -192,7 +192,7 @@
      *
      * \returns True if the receiver side is enabled, otherwise false.
      */
-    bool IsReceiveEnabled();
+    bool IsReceiveEnabled() const;
 
     /**
      * Enable or disable the receive side of the network device.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/csma-layout/examples/csma-star.cc ns-3.38/src/csma-layout/examples/csma-star.cc
--- ns-3.37/src/csma-layout/examples/csma-star.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/csma-layout/examples/csma-star.cc	2023-03-16 20:54:23.600868500 -0700
@@ -116,7 +116,7 @@
     // assign addresses to them as well.  We put all of the fill devices into
     // a single device container, so the first nFill devices are associated
     // with the channel connected to spokeDevices.Get (0), the second nFill
-    // devices afe associated with the channel connected to spokeDevices.Get (1)
+    // devices are associated with the channel connected to spokeDevices.Get (1)
     // etc.
     //
     Ipv4AddressHelper address;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsdv/examples/dsdv-manet.cc ns-3.38/src/dsdv/examples/dsdv-manet.cc
--- ns-3.37/src/dsdv/examples/dsdv-manet.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsdv/examples/dsdv-manet.cc	2023-03-16 20:54:23.600868500 -0700
@@ -377,7 +377,7 @@
     {
         Ptr<OutputStreamWrapper> routingStream =
             Create<OutputStreamWrapper>((tr_name + ".routes"), std::ios::out);
-        dsdv.PrintRoutingTableAllAt(Seconds(m_periodicUpdateInterval), routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(m_periodicUpdateInterval), routingStream);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsdv/model/dsdv-packet-queue.cc ns-3.38/src/dsdv/model/dsdv-packet-queue.cc
--- ns-3.37/src/dsdv/model/dsdv-packet-queue.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsdv/model/dsdv-packet-queue.cc	2023-03-16 20:54:23.600868500 -0700
@@ -53,7 +53,7 @@
 bool
 PacketQueue::Enqueue(QueueEntry& entry)
 {
-    NS_LOG_FUNCTION("Enqueing packet destined for" << entry.GetIpv4Header().GetDestination());
+    NS_LOG_FUNCTION("Enqueuing packet destined for" << entry.GetIpv4Header().GetDestination());
     Purge();
     uint32_t numPacketswithdst;
     for (std::vector<QueueEntry>::const_iterator i = m_queue.begin(); i != m_queue.end(); ++i)
@@ -74,7 +74,7 @@
     }
     else
     {
-        // NS_LOG_DEBUG("Packet size while enqueing "<<entry.GetPacket()->GetSize());
+        // NS_LOG_DEBUG("Packet size while enqueuing "<<entry.GetPacket()->GetSize());
         entry.SetExpireTime(m_queueTimeout);
         m_queue.push_back(entry);
         return true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsdv/model/dsdv-routing-protocol.cc ns-3.38/src/dsdv/model/dsdv-routing-protocol.cc
--- ns-3.37/src/dsdv/model/dsdv-routing-protocol.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsdv/model/dsdv-routing-protocol.cc	2023-03-16 20:54:23.600868500 -0700
@@ -826,7 +826,7 @@
     }
     std::map<Ipv4Address, RoutingTableEntry> allRoutes;
     m_advRoutingTable.GetListOfAllRoutes(allRoutes);
-    if (EnableRouteAggregation && allRoutes.size() > 0)
+    if (EnableRouteAggregation && !allRoutes.empty())
     {
         Simulator::Schedule(m_routeAggregationTime, &RoutingProtocol::SendTriggeredUpdate, this);
     }
@@ -1280,7 +1280,7 @@
         "Merging advertised table changes with main table before sending out periodic update");
     std::map<Ipv4Address, RoutingTableEntry> allRoutes;
     m_advRoutingTable.GetListOfAllRoutes(allRoutes);
-    if (allRoutes.size() > 0)
+    if (!allRoutes.empty())
     {
         for (std::map<Ipv4Address, RoutingTableEntry>::const_iterator i = allRoutes.begin();
              i != allRoutes.end();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsdv/model/dsdv-routing-protocol.h ns-3.38/src/dsdv/model/dsdv-routing-protocol.h
--- ns-3.37/src/dsdv/model/dsdv-routing-protocol.h	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsdv/model/dsdv-routing-protocol.h	2023-03-16 20:54:23.600868500 -0700
@@ -179,7 +179,7 @@
     bool EnableBuffering;
     /// Flag that is used to enable or disable Weighted Settling Time
     bool EnableWST;
-    /// This is the wighted factor to determine the weighted settling time
+    /// This is the weighted factor to determine the weighted settling time
     double m_weightedFactor;
     /// This is a flag to enable route aggregation. Route aggregation will aggregate all routes for
     /// 'RouteAggregationTime' from the time an update is received by a node and sends them as a
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsdv/model/dsdv-rtable.h ns-3.38/src/dsdv/model/dsdv-rtable.h
--- ns-3.37/src/dsdv/model/dsdv-rtable.h	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsdv/model/dsdv-rtable.h	2023-03-16 20:54:23.600868500 -0700
@@ -432,7 +432,7 @@
      */
     bool ForceDeleteIpv4Event(Ipv4Address address);
     /**
-     * Get the EcentId associated with that address.
+     * Get the EventId associated with that address.
      * \param address destination address for which this event is running.
      * \return EventId on finding out an event is associated else return NULL.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsdv/test/dsdv-testcase.cc ns-3.38/src/dsdv/test/dsdv-testcase.cc
--- ns-3.37/src/dsdv/test/dsdv-testcase.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsdv/test/dsdv-testcase.cc	2023-03-16 20:54:23.600868500 -0700
@@ -46,13 +46,13 @@
 using namespace ns3;
 
 /**
- * \ingroup dsdv-test
+ * \ingroup dsdv
+ * \ingroup tests
  * \defgroup dsdv-test DSDV module tests
  */
 
 /**
  * \ingroup dsdv-test
- * \ingroup tests
  *
  * \brief DSDV test case to verify the DSDV header
  *
@@ -111,7 +111,6 @@
 
 /**
  * \ingroup dsdv-test
- * \ingroup tests
  *
  * \brief DSDV routing table tests (adding and looking up routes)
  */
@@ -213,7 +212,6 @@
 
 /**
  * \ingroup dsdv-test
- * \ingroup tests
  *
  * \brief DSDV test suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/doc/dsr.rst ns-3.38/src/dsr/doc/dsr.rst
--- ns-3.37/src/dsr/doc/dsr.rst	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsr/doc/dsr.rst	2023-03-16 20:54:23.600868500 -0700
@@ -162,7 +162,7 @@
 
   DsrHelper dsr;
   DsrMainHelper dsrMain;
-  dsrMain.Install (dsr, adhocNodes);
+  dsrMain.Install(dsr, adhocNodes);
 
 The example scripts inside ``src/dsr/examples/`` demonstrate the use of DSR based nodes in different scenarios.
 The helper source can be found inside ``src/dsr/helper/dsr-main-helper.{h,cc}``
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/examples/dsr.cc ns-3.38/src/dsr/examples/dsr.cc
--- ns-3.37/src/dsr/examples/dsr.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsr/examples/dsr.cc	2023-03-16 20:54:23.600868500 -0700
@@ -214,4 +214,6 @@
     Simulator::Stop(Seconds(TotalTime));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-fs-header.cc ns-3.38/src/dsr/model/dsr-fs-header.cc
--- ns-3.37/src/dsr/model/dsr-fs-header.cc	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsr/model/dsr-fs-header.cc	2023-03-16 20:54:23.600868500 -0700
@@ -273,7 +273,7 @@
 }
 
 uint32_t
-DsrOptionField::GetDsrOptionsOffset()
+DsrOptionField::GetDsrOptionsOffset() const
 {
     return m_optionsOffset;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-fs-header.h ns-3.38/src/dsr/model/dsr-fs-header.h
--- ns-3.37/src/dsr/model/dsr-fs-header.h	2022-11-01 17:37:24.479395200 -0700
+++ ns-3.38/src/dsr/model/dsr-fs-header.h	2023-03-16 20:54:23.604868400 -0700
@@ -247,7 +247,7 @@
      * the extension header.
      * \return the offset from the start of the extension header
      */
-    uint32_t GetDsrOptionsOffset();
+    uint32_t GetDsrOptionsOffset() const;
     /**
      * \brief Get the buffer.
      * \return buffer
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-option-header.cc ns-3.38/src/dsr/model/dsr-option-header.cc
--- ns-3.37/src/dsr/model/dsr-option-header.cc	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/dsr/model/dsr-option-header.cc	2023-03-16 20:54:23.604868400 -0700
@@ -1085,15 +1085,15 @@
 }
 
 void
-DsrOptionRerrUnsupportHeader::SetUnsupported(uint16_t unsupport)
+DsrOptionRerrUnsupportHeader::SetUnsupported(uint16_t unsupported)
 {
-    m_unsupport = unsupport;
+    m_unsupported = unsupported;
 }
 
 uint16_t
 DsrOptionRerrUnsupportHeader::GetUnsupported() const
 {
-    return m_unsupport;
+    return m_unsupported;
 }
 
 void
@@ -1102,7 +1102,7 @@
     os << "( type = " << (uint32_t)GetType() << " length = " << (uint32_t)GetLength()
        << " errorType = " << (uint32_t)m_errorType << " salvage = " << (uint32_t)m_salvage
        << " error source = " << m_errorSrcAddress << " error dst = " << m_errorDstAddress
-       << " unsupported option = " << m_unsupport << " )";
+       << " unsupported option = " << m_unsupported << " )";
 }
 
 uint32_t
@@ -1122,7 +1122,7 @@
     i.WriteU8(m_salvage);
     WriteTo(i, m_errorSrcAddress);
     WriteTo(i, m_errorDstAddress);
-    i.WriteU16(m_unsupport);
+    i.WriteU16(m_unsupported);
 }
 
 uint32_t
@@ -1136,7 +1136,7 @@
     m_salvage = i.ReadU8();
     ReadFrom(i, m_errorSrcAddress);
     ReadFrom(i, m_errorDstAddress);
-    m_unsupport = i.ReadU16();
+    m_unsupported = i.ReadU16();
 
     return GetSerializedSize();
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-option-header.h ns-3.38/src/dsr/model/dsr-option-header.h
--- ns-3.37/src/dsr/model/dsr-option-header.h	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/dsr/model/dsr-option-header.h	2023-03-16 20:54:23.604868400 -0700
@@ -1136,7 +1136,7 @@
     /**
      * \brief The unsupported option
      */
-    uint16_t m_unsupport;
+    uint16_t m_unsupported;
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-options.cc ns-3.38/src/dsr/model/dsr-options.cc
--- ns-3.37/src/dsr/model/dsr-options.cc	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/dsr/model/dsr-options.cc	2023-03-16 20:54:23.604868400 -0700
@@ -266,7 +266,7 @@
     /*
      * Check elements in a route vector
      */
-    if (!vec.size())
+    if (vec.empty())
     {
         NS_LOG_DEBUG("The vector is empty");
     }
@@ -1112,7 +1112,7 @@
     {
         RemoveDuplicates(nodeList); // This is for the route reply from intermediate node since we
                                     // didn't remove duplicate there
-        if (nodeList.size() == 0)
+        if (nodeList.empty())
         {
             NS_LOG_DEBUG("The route we have contains 0 entries");
             return 0;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-rcache.cc ns-3.38/src/dsr/model/dsr-rcache.cc
--- ns-3.37/src/dsr/model/dsr-rcache.cc	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/dsr/model/dsr-rcache.cc	2023-03-16 20:54:23.604868400 -0700
@@ -374,7 +374,7 @@
             if (s.find(ip) == s.end())
             {
                 /*
-                 * \brief The followings are for comparison
+                 * \brief The following are for comparison
                  */
                 if (j->second <= temp)
                 {
@@ -821,7 +821,7 @@
         // Purge the link node cache first
         PurgeLinkNode();
         /*
-         * The followings are for cleaning the broken link in link cache
+         * The following are for cleaning the broken link in link cache
          * We basically remove the link between errorSrc and unreachNode
          */
         Link link1(errorSrc, unreachNode);
@@ -837,7 +837,7 @@
         std::map<Ipv4Address, DsrNodeStab>::iterator i = m_nodeCache.find(errorSrc);
         if (i == m_nodeCache.end())
         {
-            NS_LOG_LOGIC("Update the node stability unsuccessfully");
+            NS_LOG_LOGIC("Update the node stability unsuccessfuly");
         }
         else
         {
@@ -846,7 +846,7 @@
         i = m_nodeCache.find(unreachNode);
         if (i == m_nodeCache.end())
         {
-            NS_LOG_LOGIC("Update the node stability unsuccessfully");
+            NS_LOG_LOGIC("Update the node stability unsuccessfuly");
         }
         else
         {
@@ -858,7 +858,7 @@
     else
     {
         /*
-         * the followings are for cleaning the broken link in pathcache
+         * the following are for cleaning the broken link in pathcache
          *
          */
         Purge();
@@ -960,7 +960,7 @@
             {
                 m_sortedRoutes.erase(jtmp);
             }
-            if (rtVector.size())
+            if (!rtVector.empty())
             {
                 /*
                  * Save the new route cache along with the destination address in map
@@ -983,7 +983,7 @@
     /*
      * Check elements in a route vector, used when one wants to check the IP addresses saved in
      */
-    if (!vec.size())
+    if (vec.empty())
     {
         NS_LOG_DEBUG("The vector is empty");
     }
@@ -1030,7 +1030,7 @@
         Ipv4Address dst = i->first;
         std::list<DsrRouteCacheEntry> rtVector = i->second;
         NS_LOG_DEBUG("The route vector size of 1 " << dst << " " << rtVector.size());
-        if (rtVector.size())
+        if (!rtVector.empty())
         {
             for (std::list<DsrRouteCacheEntry>::iterator j = rtVector.begin(); j != rtVector.end();)
             {
@@ -1054,7 +1054,7 @@
                 }
             }
             NS_LOG_DEBUG("The route vector size of 2 " << dst << " " << rtVector.size());
-            if (rtVector.size())
+            if (!rtVector.empty())
             {
                 ++i;
                 m_sortedRoutes.erase(itmp); // erase the entry first
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-routing.cc ns-3.38/src/dsr/model/dsr-routing.cc
--- ns-3.37/src/dsr/model/dsr-routing.cc	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/dsr/model/dsr-routing.cc	2023-03-16 20:54:23.604868400 -0700
@@ -551,7 +551,7 @@
     std::vector<std::string> elements;
     size_t pos1 = 0;
     size_t pos2;
-    while (pos1 != context.npos)
+    while (pos1 != std::string::npos)
     {
         pos1 = context.find('/', pos1);
         pos2 = context.find('/', pos1 + 1);
@@ -743,7 +743,7 @@
     /*
      * Check elements in a route vector
      */
-    if (!vec.size())
+    if (vec.empty())
     {
         NS_LOG_DEBUG("The vector is empty");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/dsr/model/dsr-rreq-table.h ns-3.38/src/dsr/model/dsr-rreq-table.h
--- ns-3.37/src/dsr/model/dsr-rreq-table.h	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/dsr/model/dsr-rreq-table.h	2023-03-16 20:54:23.604868400 -0700
@@ -199,7 +199,7 @@
 
     // \}
   private:
-    Ipv4Address m_destination; //!< IPv4 address of the destinaton
+    Ipv4Address m_destination; //!< IPv4 address of the destination
     Ipv4Address m_source;      //!< IPv4 address of the source
     uint16_t m_identification; //!< Route request identification
     Time m_expire;             //!< Route request expire time
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/energy/doc/energy.rst ns-3.38/src/energy/doc/energy.rst
--- ns-3.37/src/energy/doc/energy.rst	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/energy/doc/energy.rst	2023-03-16 20:54:23.604868400 -0700
@@ -162,34 +162,34 @@
 References
 **********
 
-.. [1] ns-2 Energy model:
-   https://web.archive.org/web/20130428021737/http://www.cubinlab.ee.unimelb.edu.au/~jrid/Docs/Manuel-NS2/node204.html
+[1] ns-2 Energy model:
+    https://web.archive.org/web/20130428021737/http://www.cubinlab.ee.unimelb.edu.au/~jrid/Docs/Manuel-NS2/node204.html
 
-.. [2] H. Wu, S. Nabar and R. Poovendran. An Energy Framework for the
-   Network Simulator 3 (ns-3).
+[2] H. Wu, S. Nabar and R. Poovendran. An Energy Framework for the
+    Network Simulator 3 (ns-3).
 
-.. [3] M. Handy and D. Timmermann. Simulation of mobile wireless
-   networks with accurate modelling of non-linear battery effects. In
-   Proc. of Applied simulation and Modeling (ASM), 2003.
-
-.. [4] D. N. Rakhmatov and S. B. Vrudhula. An analytical high-level
-   battery model for use in energy management of portable electronic
-   systems. In Proc. of IEEE/ACM International Conference on Computer
-   Aided Design (ICCAD'01), pages 488-493, November 2001.
-
-.. [5] D. N. Rakhmatov, S. B. Vrudhula, and D. A. Wallach. Battery
-   lifetime prediction for energy-aware computing. In Proc. of the 2002
-   International Symposium on Low Power Electronics and Design
-   (ISLPED'02), pages 154-159, 2002.
-
-.. [6] C. Tapparello, H. Ayatollahi and W. Heinzelman. Extending the
-   Energy Framework for Network Simulator 3 (ns-3). Workshop on ns-3
-   (WNS3), Poster Session, Atlanta, GA, USA. May, 2014.
-
-.. [7] C. Tapparello, H. Ayatollahi and W. Heinzelman. Energy Harvesting
-   Framework for Network Simulator 3 (ns-3). 2nd International Workshop on
-   Energy Neutral Sensing Systems (ENSsys), Memphis, TN, USA. November 6,
-   2014.
+[3] M. Handy and D. Timmermann. Simulation of mobile wireless
+    networks with accurate modelling of non-linear battery effects. In
+    Proc. of Applied simulation and Modeling (ASM), 2003.
+
+[4] D. N. Rakhmatov and S. B. Vrudhula. An analytical high-level
+    battery model for use in energy management of portable electronic
+    systems. In Proc. of IEEE/ACM International Conference on Computer
+    Aided Design (ICCAD'01), pages 488-493, November 2001.
+
+[5] D. N. Rakhmatov, S. B. Vrudhula, and D. A. Wallach. Battery
+    lifetime prediction for energy-aware computing. In Proc. of the 2002
+    International Symposium on Low Power Electronics and Design
+    (ISLPED'02), pages 154-159, 2002.
+
+[6] C. Tapparello, H. Ayatollahi and W. Heinzelman. Extending the
+    Energy Framework for Network Simulator 3 (ns-3). Workshop on ns-3
+    (WNS3), Poster Session, Atlanta, GA, USA. May, 2014.
+
+[7] C. Tapparello, H. Ayatollahi and W. Heinzelman. Energy Harvesting
+    Framework for Network Simulator 3 (ns-3). 2nd International Workshop on
+    Energy Neutral Sensing Systems (ENSsys), Memphis, TN, USA. November 6,
+    2014.
 
 Usage
 =====
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/energy/examples/CMakeLists.txt ns-3.38/src/energy/examples/CMakeLists.txt
--- ns-3.37/src/energy/examples/CMakeLists.txt	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/energy/examples/CMakeLists.txt	2023-03-16 20:54:23.604868400 -0700
@@ -1,6 +1,6 @@
 build_lib_example(
-  NAME li-ion-energy-source
-  SOURCE_FILES li-ion-energy-source.cc
+  NAME li-ion-energy-source-example
+  SOURCE_FILES li-ion-energy-source-example.cc
   LIBRARIES_TO_LINK ${libcore}
                     ${libenergy}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/energy/examples/li-ion-energy-source.cc ns-3.38/src/energy/examples/li-ion-energy-source.cc
--- ns-3.37/src/energy/examples/li-ion-energy-source.cc	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/energy/examples/li-ion-energy-source.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2010 Andrea Sacco
- *
- * 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: Andrea Sacco <andrea.sacco85@gmail.com>
- */
-
-#include "ns3/li-ion-energy-source.h"
-
-#include "ns3/command-line.h"
-#include "ns3/energy-source-container.h"
-#include "ns3/log.h"
-#include "ns3/simple-device-energy-model.h"
-#include "ns3/simulator.h"
-
-using namespace ns3;
-
-/**
- * In this simple example, we show how to create and drain energy from a
- * LiIonEnergySource.
- * We make a series of discharge calls to the energy source class with
- * different current drain and duration until all the energy is depleted
- * from the cell.
- *
- * Every 20 seconds it is printed out the actual cell voltage to verify
- * that it follows the discharge curve of the datasheet [1].
- *
- * At the end of the example it is verified that after the energy depletion
- * call, the cell voltage is below the threshold voltage.
- *
- * References:
- * [1] Panasonic CGR18650DA Datasheet,
- * http://www.panasonic.com/industrial/includes/pdf/Panasonic_LiIon_CGR18650DA.pdf
- */
-
-static void
-PrintCellInfo(Ptr<LiIonEnergySource> es)
-{
-    std::cout << "At " << Simulator::Now().As(Time::S)
-              << " Cell voltage: " << es->GetSupplyVoltage()
-              << " V Remaining Capacity: " << es->GetRemainingEnergy() / (3.6 * 3600) << " Ah"
-              << std::endl;
-
-    if (!Simulator::IsFinished())
-    {
-        Simulator::Schedule(Seconds(20), &PrintCellInfo, es);
-    }
-}
-
-int
-main(int argc, char** argv)
-{
-    CommandLine cmd(__FILE__);
-    cmd.Parse(argc, argv);
-
-    // uncomment below to see the energy consumption details
-    // LogComponentEnable ("LiIonEnergySource", LOG_LEVEL_DEBUG);
-
-    Ptr<Node> node = CreateObject<Node>();
-
-    Ptr<SimpleDeviceEnergyModel> sem = CreateObject<SimpleDeviceEnergyModel>();
-    Ptr<EnergySourceContainer> esCont = CreateObject<EnergySourceContainer>();
-    Ptr<LiIonEnergySource> es = CreateObject<LiIonEnergySource>();
-    esCont->Add(es);
-    es->SetNode(node);
-    sem->SetEnergySource(es);
-    es->AppendDeviceEnergyModel(sem);
-    sem->SetNode(node);
-    node->AggregateObject(esCont);
-
-    Time now = Simulator::Now();
-
-    // discharge at 2.33 A for 1700 seconds
-    sem->SetCurrentA(2.33);
-    now += Seconds(1701);
-
-    // discharge at 4.66 A for 628 seconds
-    Simulator::Schedule(now, &SimpleDeviceEnergyModel::SetCurrentA, sem, 4.66);
-    now += Seconds(600);
-
-    PrintCellInfo(es);
-
-    Simulator::Stop(now);
-    Simulator::Run();
-    Simulator::Destroy();
-
-    // the cell voltage should be under 3.3v
-    DoubleValue v;
-    es->GetAttribute("ThresholdVoltage", v);
-    NS_ASSERT(es->GetSupplyVoltage() <= v.Get());
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/energy/examples/li-ion-energy-source-example.cc ns-3.38/src/energy/examples/li-ion-energy-source-example.cc
--- ns-3.37/src/energy/examples/li-ion-energy-source-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/energy/examples/li-ion-energy-source-example.cc	2023-03-16 20:54:23.604868400 -0700
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010 Andrea Sacco
+ *
+ * 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: Andrea Sacco <andrea.sacco85@gmail.com>
+ */
+
+#include "ns3/command-line.h"
+#include "ns3/energy-source-container.h"
+#include "ns3/li-ion-energy-source.h"
+#include "ns3/log.h"
+#include "ns3/simple-device-energy-model.h"
+#include "ns3/simulator.h"
+
+using namespace ns3;
+
+/**
+ * In this simple example, we show how to create and drain energy from a
+ * LiIonEnergySource.
+ * We make a series of discharge calls to the energy source class with
+ * different current drain and duration until all the energy is depleted
+ * from the cell.
+ *
+ * Every 20 seconds it is printed out the actual cell voltage to verify
+ * that it follows the discharge curve of the datasheet [1].
+ *
+ * At the end of the example it is verified that after the energy depletion
+ * call, the cell voltage is below the threshold voltage.
+ *
+ * References:
+ * [1] Panasonic CGR18650DA Datasheet,
+ * http://www.panasonic.com/industrial/includes/pdf/Panasonic_LiIon_CGR18650DA.pdf
+ */
+
+static void
+PrintCellInfo(Ptr<LiIonEnergySource> es)
+{
+    std::cout << "At " << Simulator::Now().As(Time::S)
+              << " Cell voltage: " << es->GetSupplyVoltage()
+              << " V Remaining Capacity: " << es->GetRemainingEnergy() / (3.6 * 3600) << " Ah"
+              << std::endl;
+
+    if (!Simulator::IsFinished())
+    {
+        Simulator::Schedule(Seconds(20), &PrintCellInfo, es);
+    }
+}
+
+int
+main(int argc, char** argv)
+{
+    CommandLine cmd(__FILE__);
+    cmd.Parse(argc, argv);
+
+    // uncomment below to see the energy consumption details
+    // LogComponentEnable ("LiIonEnergySource", LOG_LEVEL_DEBUG);
+
+    Ptr<Node> node = CreateObject<Node>();
+
+    Ptr<SimpleDeviceEnergyModel> sem = CreateObject<SimpleDeviceEnergyModel>();
+    Ptr<EnergySourceContainer> esCont = CreateObject<EnergySourceContainer>();
+    Ptr<LiIonEnergySource> es = CreateObject<LiIonEnergySource>();
+    esCont->Add(es);
+    es->SetNode(node);
+    sem->SetEnergySource(es);
+    es->AppendDeviceEnergyModel(sem);
+    sem->SetNode(node);
+    node->AggregateObject(esCont);
+
+    Time now = Simulator::Now();
+
+    // discharge at 2.33 A for 1700 seconds
+    sem->SetCurrentA(2.33);
+    now += Seconds(1701);
+
+    // discharge at 4.66 A for 628 seconds
+    Simulator::Schedule(now, &SimpleDeviceEnergyModel::SetCurrentA, sem, 4.66);
+    now += Seconds(600);
+
+    PrintCellInfo(es);
+
+    Simulator::Stop(now);
+    Simulator::Run();
+    Simulator::Destroy();
+
+    // the cell voltage should be under 3.3v
+    DoubleValue v;
+    es->GetAttribute("ThresholdVoltage", v);
+    NS_ASSERT(es->GetSupplyVoltage() <= v.Get());
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/energy/examples/rv-battery-model-test.cc ns-3.38/src/energy/examples/rv-battery-model-test.cc
--- ns-3.37/src/energy/examples/rv-battery-model-test.cc	2022-11-01 17:37:24.483395000 -0700
+++ ns-3.38/src/energy/examples/rv-battery-model-test.cc	2023-03-16 20:54:23.604868400 -0700
@@ -73,7 +73,7 @@
      * Runs simulation with constant load and checks the battery lifetime with
      * known results.
      */
-    bool ConstantLoadTest(double load, Time expLifetime);
+    bool ConstantLoadTest(double load, Time expLifetime) const;
 
     /**
      * \param loads Load profile.
@@ -86,16 +86,16 @@
      */
     bool VariableLoadTest(std::vector<double> loads,
                           std::vector<Time> timeStamps,
-                          Time expLifetime);
+                          Time expLifetime) const;
 
     /// Load profile of the battery
-    typedef struct LoadProfile
+    struct LoadProfile
     {
         std::vector<double> loads;    //!< Loads container
         std::vector<Time> timeStamps; //!< Timestamps container
         Time itsyLifetime;            //!< Expected lifetime for an ITSY battery
         Time dualFoilLifeTime;        //!< Expected lifetime for a Dualfoil battery
-    } LoadProfile;
+    };
 
     std::vector<LoadProfile> m_loadProfiles; //!< Load profiles
     double m_alpha;                          //!< Alpha parameter of the battery model
@@ -715,7 +715,7 @@
 }
 
 bool
-BatteryLifetimeTest::ConstantLoadTest(double load, Time expLifetime)
+BatteryLifetimeTest::ConstantLoadTest(double load, Time expLifetime) const
 {
     // create single node
     NodeContainer c;
@@ -800,7 +800,7 @@
 bool
 BatteryLifetimeTest::VariableLoadTest(std::vector<double> loads,
                                       std::vector<Time> timeStamps,
-                                      Time expLifetime)
+                                      Time expLifetime) const
 {
     NS_ASSERT(loads.size() == timeStamps.size());
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/doc/dpdk-net-device.rst ns-3.38/src/fd-net-device/doc/dpdk-net-device.rst
--- ns-3.37/src/fd-net-device/doc/dpdk-net-device.rst	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/doc/dpdk-net-device.rst	2023-03-16 20:54:23.604868400 -0700
@@ -271,14 +271,14 @@
 
 .. sourcecode:: text
 
- DpdkNetDeviceHelper* dpdk = new DpdkNetDeviceHelper ();
+ DpdkNetDeviceHelper* dpdk = new DpdkNetDeviceHelper();
  dpdk->SetPmdLibrary("librte_pmd_e1000.so");
 
 Also, NIC should be bound to DPDK Driver in order to be used with EAL. The default driver used is ``uio_pci_generic`` which supports most of the NICs. You can change it using ``DpdkNetDeviceHelper::SetDpdkDriver``, as follows:
 
 .. sourcecode:: text
 
- DpdkNetDeviceHelper* dpdk = new DpdkNetDeviceHelper ();
+ DpdkNetDeviceHelper* dpdk = new DpdkNetDeviceHelper();
  dpdk->SetDpdkDriver("igb_uio");
 
 Attributes
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/doc/fd-net-device.rst ns-3.38/src/fd-net-device/doc/fd-net-device.rst
--- ns-3.37/src/fd-net-device/doc/fd-net-device.rst	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/doc/fd-net-device.rst	2023-03-16 20:54:23.604868400 -0700
@@ -167,12 +167,12 @@
 ::
 
    FdNetDeviceHelper fd;
-   NetDeviceContainer devices = fd.Install (nodes);
+   NetDeviceContainer devices = fd.Install(nodes);
 
    // file descriptor generation
    ...
 
-   device->SetFileDescriptor (fd);
+   device->SetFileDescriptor(fd);
 
 
 Most commonly a FdNetDevice will be used to interact with the host system.
@@ -182,8 +182,8 @@
 
 ::
 
-   GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::RealtimeSimulatorImpl"));
-   GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true));
+   GlobalValue::Bind("SimulatorImplementationType", StringValue("ns3::RealtimeSimulatorImpl"));
+   GlobalValue::Bind("ChecksumEnabled", BooleanValue(true));
 
 The easiest way to set up an experiment that interacts with a Linux host
 system is to user the ``Emu`` and ``Tap`` helpers.
@@ -285,10 +285,10 @@
 ::
 
   EmuFdNetDeviceHelper emu;
-  emu.SetDeviceName (deviceName);
-  NetDeviceContainer devices = emu.Install (node);
-  Ptr<NetDevice> device = devices.Get (0);
-  device->SetAttribute ("Address", Mac48AddressValue (Mac48Address::Allocate ()));
+  emu.SetDeviceName(deviceName);
+  NetDeviceContainer devices = emu.Install(node);
+  Ptr<NetDevice> device = devices.Get(0);
+  device->SetAttribute("Address", Mac48AddressValue(Mac48Address::Allocate()));
 
 
 TapFdNetDeviceHelper
@@ -366,12 +366,12 @@
 ::
 
   TapFdNetDeviceHelper helper;
-  helper.SetDeviceName (deviceName);
-  helper.SetModePi (modePi);
-  helper.SetTapIpv4Address (tapIp);
-  helper.SetTapIpv4Mask (tapMask);
+  helper.SetDeviceName(deviceName);
+  helper.SetModePi(modePi);
+  helper.SetTapIpv4Address(tapIp);
+  helper.SetTapIpv4Mask(tapMask);
   ...
-  helper.Install (node);
+  helper.Install(node);
 
 
 Attributes
@@ -394,7 +394,7 @@
 
 ::
 
-   device->SetAttribute ("Address", Mac48AddressValue (Mac48Address::Allocate ()));
+   device->SetAttribute("Address", Mac48AddressValue(Mac48Address::Allocate()));
 
 Output
 ======
@@ -404,9 +404,9 @@
 
 ::
     EmuFdNetDeviceHelper emu;
-    NetDeviceContainer devices = emu.Install (node);
+    NetDeviceContainer devices = emu.Install(node);
     ...
-    emu.EnablePcap ("emu-ping", device, true);
+    emu.EnablePcap("emu-ping", device, true);
 
 
 The standard set of Mac-level NetDevice trace sources is provided.
@@ -443,4 +443,3 @@
   traffic over a real channel.
 * ``fd-tap-ping.cc``: This example uses the TapFdNetDeviceHelper to send ICMP
   traffic over a real channel.
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/doc/netmap-net-device.rst ns-3.38/src/fd-net-device/doc/netmap-net-device.rst
--- ns-3.37/src/fd-net-device/doc/netmap-net-device.rst	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/doc/netmap-net-device.rst	2023-03-16 20:54:23.604868400 -0700
@@ -26,7 +26,7 @@
 per second than emulation using FdNetDevice with raw sockets (which pass
 through the Linux networking kernel).
 
-.. [Rizzo2012] Luigi Rizzo, "netmap: A Novel Framework for Fast Packet I/O", Proceedings of 2012 USENIX Annual Techincal Conference, June 2012.
+.. [Rizzo2012] Luigi Rizzo, "netmap: A Novel Framework for Fast Packet I/O", Proceedings of 2012 USENIX Annual Technical Conference, June 2012.
 
 .. [Imputato2019] Pasquale Imputato, Stefano Avallone, Enhancing the fidelity of network emulation through direct access to device buffers, Journal of Network and Computer Applications, Volume 130, 2019, Pages 63-75, (http://www.sciencedirect.com/science/article/pii/S1084804519300220)
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/dummy-network.cc ns-3.38/src/fd-net-device/examples/dummy-network.cc
--- ns-3.37/src/fd-net-device/examples/dummy-network.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/dummy-network.cc	2023-03-16 20:54:23.604868400 -0700
@@ -64,9 +64,9 @@
     addresses.SetBase("10.0.0.0", "255.255.255.0");
     Ipv4InterfaceContainer interfaces = addresses.Assign(devices);
 
-    Ptr<V4Ping> app = CreateObject<V4Ping>();
-    app->SetAttribute("Remote", Ipv4AddressValue(interfaces.GetAddress(0)));
-    app->SetAttribute("Verbose", BooleanValue(true));
+    Ptr<Ping> app = CreateObject<Ping>();
+    app->SetAttribute("Destination", AddressValue(interfaces.GetAddress(0)));
+    app->SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE));
     nodes.Get(1)->AddApplication(app);
     app->SetStartTime(Seconds(0.0));
     app->SetStopTime(Seconds(4.0));
@@ -85,4 +85,6 @@
     Simulator::Stop(Seconds(5) + TimeStep(1));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd2fd-onoff.cc ns-3.38/src/fd-net-device/examples/fd2fd-onoff.cc
--- ns-3.37/src/fd-net-device/examples/fd2fd-onoff.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd2fd-onoff.cc	2023-03-16 20:54:23.608868600 -0700
@@ -140,4 +140,6 @@
     Simulator::Stop(Seconds(30.0));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd-emu-onoff.cc ns-3.38/src/fd-net-device/examples/fd-emu-onoff.cc
--- ns-3.37/src/fd-net-device/examples/fd-emu-onoff.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd-emu-onoff.cc	2023-03-16 20:54:23.604868400 -0700
@@ -218,7 +218,7 @@
     if (emuMode == "dpdk")
     {
         DpdkNetDeviceHelper* dpdk = new DpdkNetDeviceHelper();
-        // Use e1000 driver library (this is for IGb PMD supproting Intel 1GbE NIC)
+        // Use e1000 driver library (this is for IGb PMD supporting Intel 1GbE NIC)
         // NOTE: DPDK supports multiple Poll Mode Drivers (PMDs) and you can use it
         // based on your NIC. You just need to set pmd library as follows:
         dpdk->SetPmdLibrary("librte_pmd_e1000.so");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd-emu-ping.cc ns-3.38/src/fd-net-device/examples/fd-emu-ping.cc
--- ns-3.37/src/fd-net-device/examples/fd-emu-ping.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd-emu-ping.cc	2023-03-16 20:54:23.604868400 -0700
@@ -94,9 +94,9 @@
 NS_LOG_COMPONENT_DEFINE("PingEmulationExample");
 
 static void
-PingRtt(std::string context, Time rtt)
+PingRtt(std::string context, uint16_t seqNo, Time rtt)
 {
-    NS_LOG_UNCOND("Received Response with RTT = " << rtt);
+    NS_LOG_UNCOND("Received " << seqNo << " Response with RTT = " << rtt);
 }
 
 int
@@ -201,7 +201,7 @@
     if (emuMode == "dpdk")
     {
         DpdkNetDeviceHelper* dpdk = new DpdkNetDeviceHelper();
-        // Use e1000 driver library (this is for IGb PMD supproting Intel 1GbE NIC)
+        // Use e1000 driver library (this is for IGb PMD supporting Intel 1GbE NIC)
         // NOTE: DPDK supports multiple Poll Mode Drivers (PMDs) and you can use it
         // based on your NIC. You just need to set pmd library as follows:
         dpdk->SetPmdLibrary("librte_pmd_e1000.so");
@@ -269,10 +269,10 @@
     // of a hassle and since there is no law that says we cannot mix the
     // helper API with the low level API, let's just use the helper.
     //
-    NS_LOG_INFO("Create V4Ping Appliation");
-    Ptr<V4Ping> app = CreateObject<V4Ping>();
-    app->SetAttribute("Remote", Ipv4AddressValue(remoteIp));
-    app->SetAttribute("Verbose", BooleanValue(true));
+    NS_LOG_INFO("Create Ping Application");
+    Ptr<Ping> app = CreateObject<Ping>();
+    app->SetAttribute("Destination", AddressValue(remoteIp));
+    app->SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE));
     node->AddApplication(app);
     app->SetStartTime(Seconds(1.0));
     app->SetStopTime(Seconds(22.0));
@@ -302,4 +302,6 @@
     Simulator::Destroy();
     delete helper;
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd-emu-send.cc ns-3.38/src/fd-net-device/examples/fd-emu-send.cc
--- ns-3.37/src/fd-net-device/examples/fd-emu-send.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd-emu-send.cc	2023-03-16 20:54:23.604868400 -0700
@@ -197,4 +197,6 @@
     Simulator::Destroy();
     delete helper;
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd-emu-tc.cc ns-3.38/src/fd-net-device/examples/fd-emu-tc.cc
--- ns-3.37/src/fd-net-device/examples/fd-emu-tc.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd-emu-tc.cc	2023-03-16 20:54:23.604868400 -0700
@@ -304,10 +304,9 @@
         }
 #endif
 
-        Ipv4GlobalRoutingHelper g;
         Ptr<OutputStreamWrapper> routingStream =
             Create<OutputStreamWrapper>("router-routes.txt", std::ios::out);
-        g.PrintRoutingTableAllAt(Seconds(3), routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(3), routingStream);
     }
 
     NS_LOG_INFO("Run Emulation.");
@@ -317,4 +316,6 @@
     delete helper0;
     delete helper1;
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd-emu-udp-echo.cc ns-3.38/src/fd-net-device/examples/fd-emu-udp-echo.cc
--- ns-3.37/src/fd-net-device/examples/fd-emu-udp-echo.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd-emu-udp-echo.cc	2023-03-16 20:54:23.608868600 -0700
@@ -40,7 +40,7 @@
 // (OUI) 00:00:00 as a base.  This vendor code is not assigned to any
 // organization and so should not conflict with any real hardware.  We'll use
 // the first n of these addresses, where n is the number of nodes, in this
-// simualtion.  It is up to you to determine that using these MAC addresses is
+// simulation.  It is up to you to determine that using these MAC addresses is
 // okay on your network and won't conflict with anything else (including another
 // simulation using emu devices) on your network.  Once you have made this
 // determination, you need to put the interface you chose into promiscuous mode.
@@ -236,4 +236,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd-tap-ping6.cc ns-3.38/src/fd-net-device/examples/fd-tap-ping6.cc
--- ns-3.37/src/fd-net-device/examples/fd-tap-ping6.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd-tap-ping6.cc	2023-03-16 20:54:23.608868600 -0700
@@ -102,12 +102,12 @@
     //
     Ipv6AddressHelper ipv6;
 
-    ipv6.SetBase(Ipv6Address("2001:0DB8:1::"), Ipv6Prefix(64));
+    ipv6.SetBase(Ipv6Address("4001:beef:1::"), Ipv6Prefix(64));
     Ipv6InterfaceContainer i1 = ipv6.Assign(devs);
     i1.SetForwarding(1, true);
     i1.SetDefaultRouteInAllNodes(1);
 
-    ipv6.SetBase(Ipv6Address("2001:0DB8:2::"), Ipv6Prefix(64));
+    ipv6.SetBase(Ipv6Address("4001:beef:2::"), Ipv6Prefix(64));
     Ipv6Address tapAddr = ipv6.NewAddress();
     std::stringstream ss;
     std::string tapIp;
@@ -137,14 +137,20 @@
     uint32_t maxPacketCount = 1;
     Time interPacketInterval = Seconds(1.0);
 
-    Ping6Helper ping6;
-
-    ping6.SetRemote(tapIp.c_str());
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(n);
+    PingHelper ping(Ipv6Address(tapIp.c_str()));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Interval", TimeValue(interPacketInterval));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(n);
+
+    // Ping6Helper ping6;
+
+    // ping6.SetRemote(tapIp.c_str());
+
+    // ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
+    // ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
+    // ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
+    // ApplicationContainer apps = ping6.Install(n);
     apps.Start(Seconds(2.0));
     apps.Stop(Seconds(20.0));
 
@@ -165,4 +171,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/fd-tap-ping.cc ns-3.38/src/fd-net-device/examples/fd-tap-ping.cc
--- ns-3.37/src/fd-net-device/examples/fd-tap-ping.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/fd-tap-ping.cc	2023-03-16 20:54:23.608868600 -0700
@@ -83,9 +83,9 @@
 NS_LOG_COMPONENT_DEFINE("TAPPingExample");
 
 static void
-PingRtt(std::string context, Time rtt)
+PingRtt(std::string context, uint16_t seqNo, Time rtt)
 {
-    NS_LOG_UNCOND("Received Response with RTT = " << rtt);
+    NS_LOG_UNCOND("Received " << seqNo << " Response with RTT = " << rtt);
 }
 
 int
@@ -201,10 +201,10 @@
     // of a hassle and since there is no law that says we cannot mix the
     // helper API with the low level API, let's just use the helper.
     //
-    NS_LOG_INFO("Create V4Ping Appliation");
-    Ptr<V4Ping> app = CreateObject<V4Ping>();
-    app->SetAttribute("Remote", Ipv4AddressValue(remoteIp));
-    app->SetAttribute("Verbose", BooleanValue(true));
+    NS_LOG_INFO("Create Ping Application");
+    Ptr<Ping> app = CreateObject<Ping>();
+    app->SetAttribute("Destination", AddressValue(remoteIp));
+    app->SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE));
     node->AddApplication(app);
     app->SetStartTime(Seconds(1.0));
     app->SetStopTime(Seconds(21.0));
@@ -233,4 +233,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/realtime-dummy-network.cc ns-3.38/src/fd-net-device/examples/realtime-dummy-network.cc
--- ns-3.37/src/fd-net-device/examples/realtime-dummy-network.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/realtime-dummy-network.cc	2023-03-16 20:54:23.608868600 -0700
@@ -67,9 +67,9 @@
     addresses.SetBase("10.0.0.0", "255.255.255.0");
     Ipv4InterfaceContainer interfaces = addresses.Assign(devices);
 
-    Ptr<V4Ping> app = CreateObject<V4Ping>();
-    app->SetAttribute("Remote", Ipv4AddressValue(interfaces.GetAddress(0)));
-    app->SetAttribute("Verbose", BooleanValue(true));
+    Ptr<Ping> app = CreateObject<Ping>();
+    app->SetAttribute("Destination", AddressValue(interfaces.GetAddress(0)));
+    app->SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE));
     nodes.Get(1)->AddApplication(app);
     app->SetStartTime(Seconds(0.0));
     app->SetStopTime(Seconds(4.0));
@@ -79,4 +79,6 @@
     Simulator::Stop(Seconds(5.));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/examples/realtime-fd2fd-onoff.cc ns-3.38/src/fd-net-device/examples/realtime-fd2fd-onoff.cc
--- ns-3.37/src/fd-net-device/examples/realtime-fd2fd-onoff.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/examples/realtime-fd2fd-onoff.cc	2023-03-16 20:54:23.608868600 -0700
@@ -28,7 +28,7 @@
 //  |  fd-net-device |--------------|  fd-net-device |
 //  +----------------+              +----------------+
 //
-// This example is aimed at meassuring the thoughput of the FdNetDevice
+// This example is aimed at measuring the thoughput of the FdNetDevice
 // in a pure simulation. For this purpose two FdNetDevices, attached to
 // different nodes but in a same simulation, are connected using a socket pair.
 // TCP traffic is sent at a saturating data rate. Then the thoughput can
@@ -125,4 +125,6 @@
     Simulator::Stop(Seconds(40.0));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/helper/creator-utils.cc ns-3.38/src/fd-net-device/helper/creator-utils.cc
--- ns-3.37/src/fd-net-device/helper/creator-utils.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/helper/creator-utils.cc	2023-03-16 20:54:23.608868600 -0700
@@ -39,7 +39,7 @@
 {
 
 /// Flag to enable / disable verbose log mode
-int gVerbose = 0;
+bool gVerbose = false;
 
 /**
  * \brief Send the file descriptor back to the code that invoked the creation.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/helper/creator-utils.h ns-3.38/src/fd-net-device/helper/creator-utils.h
--- ns-3.37/src/fd-net-device/helper/creator-utils.h	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/helper/creator-utils.h	2023-03-16 20:54:23.608868600 -0700
@@ -31,7 +31,7 @@
 namespace ns3
 {
 
-extern int gVerbose;
+extern bool gVerbose;
 
 #define LOG(msg)                                                                                   \
     if (gVerbose)                                                                                  \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/helper/dpdk-net-device-helper.h ns-3.38/src/fd-net-device/helper/dpdk-net-device-helper.h
--- ns-3.37/src/fd-net-device/helper/dpdk-net-device-helper.h	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/helper/dpdk-net-device-helper.h	2023-03-16 20:54:23.608868600 -0700
@@ -37,9 +37,6 @@
   public:
     /**
      * Construct a DpdkNetDeviceHelper and initialize DPDK EAL
-     *
-     * \param argc Dpdk EAL args count.
-     * \param argv Dpdk EAL args list.
      */
     DpdkNetDeviceHelper();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/helper/emu-fd-net-device-helper.cc ns-3.38/src/fd-net-device/helper/emu-fd-net-device-helper.cc
--- ns-3.37/src/fd-net-device/helper/emu-fd-net-device-helper.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/helper/emu-fd-net-device-helper.cc	2023-03-16 20:54:23.608868600 -0700
@@ -111,7 +111,7 @@
     // Figure out which interface index corresponds to the device name in the corresponding
     // attribute.
     //
-    struct ifreq ifr;
+    ifreq ifr;
     bzero(&ifr, sizeof(ifr));
     strncpy((char*)ifr.ifr_name, m_deviceName.c_str(), IFNAMSIZ - 1);
 
@@ -200,7 +200,7 @@
     }
 
     // Set the MTU of the device to the mtu of the associated network interface
-    struct ifreq ifr2;
+    ifreq ifr2;
 
     bzero(&ifr2, sizeof(ifr2));
     strcpy(ifr2.ifr_name, m_deviceName.c_str());
@@ -364,7 +364,7 @@
         // get the data that comes back from the socket creator process.  It will
         // be a magic number that we use as a consistency/sanity check.
         //
-        struct iovec iov;
+        iovec iov;
         uint32_t magic;
         iov.iov_base = &magic;
         iov.iov_len = sizeof(magic);
@@ -393,7 +393,7 @@
         // So, initialize the message header that describes the ancillary/control
         // data we expect to receive and point it to buffer.
         //
-        struct msghdr msg;
+        msghdr msg;
         msg.msg_name = nullptr;
         msg.msg_namelen = 0;
         msg.msg_iov = &iov;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/helper/netmap-net-device-helper.cc ns-3.38/src/fd-net-device/helper/netmap-net-device-helper.cc
--- ns-3.37/src/fd-net-device/helper/netmap-net-device-helper.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/helper/netmap-net-device-helper.cc	2023-03-16 20:54:23.608868600 -0700
@@ -258,7 +258,7 @@
     // we wait for the child (the socket creator) to complete and read the
     // socket it created using the ancillary data mechanism.
     //
-    // Tom Goff reports the possiblility of a deadlock when trying to acquire the
+    // Tom Goff reports the possibility of a deadlock when trying to acquire the
     // python GIL here.  He says that this might be due to trying to access Python
     // objects after fork() without calling PyOS_AfterFork() to properly reset
     // Python state (including the GIL).  There is no code to cause the problem
@@ -375,7 +375,7 @@
         // data we expect to receive and point it to buffer.
         //
         struct msghdr msg;
-        msg.msg_name = 0;
+        msg.msg_name = nullptr;
         msg.msg_namelen = 0;
         msg.msg_iov = &iov;
         msg.msg_iovlen = 1;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/helper/tap-fd-net-device-helper.cc ns-3.38/src/fd-net-device/helper/tap-fd-net-device-helper.cc
--- ns-3.37/src/fd-net-device/helper/tap-fd-net-device-helper.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/helper/tap-fd-net-device-helper.cc	2023-03-16 20:54:23.608868600 -0700
@@ -224,7 +224,7 @@
         // which tells the system to make up a device name such as "tap123".
         //
         std::ostringstream ossDeviceName;
-        if (m_deviceName != "")
+        if (!m_deviceName.empty())
         {
             ossDeviceName << "-d" << m_deviceName;
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/fd-net-device/model/fd-net-device.cc ns-3.38/src/fd-net-device/model/fd-net-device.cc
--- ns-3.37/src/fd-net-device/model/fd-net-device.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/fd-net-device/model/fd-net-device.cc	2023-03-16 20:54:23.608868600 -0700
@@ -207,7 +207,7 @@
 }
 
 void
-FdNetDevice::SetEncapsulationMode(enum EncapsulationMode mode)
+FdNetDevice::SetEncapsulationMode(EncapsulationMode mode)
 {
     NS_LOG_FUNCTION(this << mode);
     m_encapMode = mode;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/flow-monitor/doc/flow-monitor.rst ns-3.38/src/flow-monitor/doc/flow-monitor.rst
--- ns-3.37/src/flow-monitor/doc/flow-monitor.rst	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/flow-monitor/doc/flow-monitor.rst	2023-03-16 20:54:23.608868600 -0700
@@ -125,13 +125,13 @@
   FlowMonitorHelper flowHelper;
   flowMonitor = flowHelper.InstallAll();
 
-  -yourApplicationsContainer-.Stop (Seconds (stop_time));;
-  Simulator::Stop (Seconds(stop_time+cleanup_time));
-  Simulator::Run ();
+  -yourApplicationsContainer-.Stop(Seconds(stop_time));;
+  Simulator::Stop(Seconds(stop_time+cleanup_time));
+  Simulator::Run();
 
   flowMonitor->SerializeToXmlFile("NameOfFile.xml", true, true);
 
-the ``SerializeToXmlFile ()`` function 2nd and 3rd parameters are used respectively to
+the ``SerializeToXmlFile()`` function 2nd and 3rd parameters are used respectively to
 activate/deactivate the histograms and the per-probe detailed stats.
 Other possible alternatives can be found in the Doxygen documentation, while
 ``cleanup_time`` is the time needed by in-flight packets to reach their destinations.
@@ -211,7 +211,7 @@
 * examples/routing/manet-routing-compare.cc
 * examples/routing/simple-global-routing.cc
 * examples/tcp/tcp-variants-comparison.cc
-* examples/wireless/multirate.cc
+* examples/wireless/wifi-multirate.cc
 * examples/wireless/wifi-hidden-terminal.cc
 
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/flow-monitor/examples/flowmon-parse-results.py ns-3.38/src/flow-monitor/examples/flowmon-parse-results.py
--- ns-3.37/src/flow-monitor/examples/flowmon-parse-results.py	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/flow-monitor/examples/flowmon-parse-results.py	2023-03-16 20:54:23.608868600 -0700
@@ -45,10 +45,6 @@
     ## class variables
     ## @var bins
     #  histogram bins
-    ## @var nbins
-    #  number of bins
-    ## @var number_of_flows
-    #  number of flows
     ## @var __slots_
     #  class variable list
     __slots_ = 'bins', 'nbins', 'number_of_flows'
@@ -76,8 +72,6 @@
     #  receive bit rate
     ## @var txBitrate
     #  transmit bit rate
-    ## @var fiveTuple
-    #  five tuple
     ## @var packetSizeMean
     #  packet size mean
     ## @var probe_stats_unsorted
@@ -140,14 +134,10 @@
 ## ProbeFlowStats
 class ProbeFlowStats(object):
     ## class variables
-    ## @var probeId
-    #  probe ID
     ## @var packets
     #  network packets
     ## @var bytes
     #  bytes
-    ## @var delayFromFirstProbe
-    #  delay from first probe
     ## @var __slots_
     #  class variable list
     __slots_ = ['probeId', 'packets', 'bytes', 'delayFromFirstProbe']
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/flow-monitor/examples/wifi-olsr-flowmon.py ns-3.38/src/flow-monitor/examples/wifi-olsr-flowmon.py
--- ns-3.37/src/flow-monitor/examples/wifi-olsr-flowmon.py	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/flow-monitor/examples/wifi-olsr-flowmon.py	2023-03-16 20:54:23.608868600 -0700
@@ -62,8 +62,7 @@
 
     port = 9   # Discard port(RFC 863)
     inetAddress = ns.network.InetSocketAddress(ns.network.Ipv4Address("10.0.0.1"), port)
-    onOffHelper = ns.applications.OnOffHelper("ns3::UdpSocketFactory",
-                                  ns.network.Address(ns.addressFromInetSocketAddress(inetAddress)))
+    onOffHelper = ns.applications.OnOffHelper("ns3::UdpSocketFactory", inetAddress.ConvertTo())
     onOffHelper.SetAttribute("DataRate", ns.network.DataRateValue(ns.network.DataRate("100kbps")))
     onOffHelper.SetAttribute("OnTime", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=1]"))
     onOffHelper.SetAttribute("OffTime", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=0]"))
@@ -97,8 +96,7 @@
     for i, node in [(i, nodes.Get(i)) for i in range(nodes.GetN())]:
         destaddr = addresses[(len(addresses) - 1 - i) % len(addresses)]
         #print (i, destaddr)
-        genericAddress = ns.addressFromInetSocketAddress(ns.network.InetSocketAddress(destaddr, port))
-        onOffHelper.SetAttribute("Remote", ns.network.AddressValue(genericAddress))
+        onOffHelper.SetAttribute("Remote", ns.network.AddressValue(ns.network.InetSocketAddress(destaddr, port).ConvertTo()))
         container = ns.network.NodeContainer(node)
         app = onOffHelper.Install(container)
         urv = ns.CreateObject("UniformRandomVariable")#ns.cppyy.gbl.get_rng()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/flow-monitor/model/ipv4-flow-classifier.cc ns-3.38/src/flow-monitor/model/ipv4-flow-classifier.cc
--- ns-3.37/src/flow-monitor/model/ipv4-flow-classifier.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/flow-monitor/model/ipv4-flow-classifier.cc	2023-03-16 20:54:23.608868600 -0700
@@ -124,7 +124,7 @@
     }
 
     // we rely on the fact that for both TCP and UDP the ports are
-    // carried in the first 4 octects.
+    // carried in the first 4 octets.
     // This allows to read the ports even on fragmented packets
     // not carrying a full TCP or UDP header.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/flow-monitor/model/ipv4-flow-probe.cc ns-3.38/src/flow-monitor/model/ipv4-flow-probe.cc
--- ns-3.37/src/flow-monitor/model/ipv4-flow-probe.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/flow-monitor/model/ipv4-flow-probe.cc	2023-03-16 20:54:23.608868600 -0700
@@ -61,7 +61,7 @@
     void Print(std::ostream& os) const override;
     Ipv4FlowProbeTag();
     /**
-     * \brief Consructor
+     * \brief Constructor
      * \param flowId the flow identifier
      * \param packetId the packet identifier
      * \param packetSize the packet size
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/flow-monitor/model/ipv6-flow-classifier.cc ns-3.38/src/flow-monitor/model/ipv6-flow-classifier.cc
--- ns-3.37/src/flow-monitor/model/ipv6-flow-classifier.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/flow-monitor/model/ipv6-flow-classifier.cc	2023-03-16 20:54:23.608868600 -0700
@@ -125,7 +125,7 @@
     }
 
     // we rely on the fact that for both TCP and UDP the ports are
-    // carried in the first 4 octects.
+    // carried in the first 4 octets.
     // This allows to read the ports even on fragmented packets
     // not carrying a full TCP or UDP header.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/flow-monitor/model/ipv6-flow-probe.cc ns-3.38/src/flow-monitor/model/ipv6-flow-probe.cc
--- ns-3.37/src/flow-monitor/model/ipv6-flow-probe.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/flow-monitor/model/ipv6-flow-probe.cc	2023-03-16 20:54:23.608868600 -0700
@@ -62,7 +62,7 @@
     void Print(std::ostream& os) const override;
     Ipv6FlowProbeTag();
     /**
-     * \brief Consructor
+     * \brief Constructor
      * \param flowId the flow identifier
      * \param packetId the packet identifier
      * \param packetSize the packet size
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/CMakeLists.txt ns-3.38/src/internet/CMakeLists.txt
--- ns-3.37/src/internet/CMakeLists.txt	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/CMakeLists.txt	2023-03-16 20:54:23.612868500 -0700
@@ -79,7 +79,6 @@
     model/ipv6.cc
     model/loopback-net-device.cc
     model/ndisc-cache.cc
-    model/pending-data.cc
     model/rip-header.cc
     model/rip.cc
     model/ripng-header.cc
@@ -119,7 +118,7 @@
     model/tcp-tx-item.cc
     model/tcp-vegas.cc
     model/tcp-veno.cc
-    model/tcp-westwood.cc
+    model/tcp-westwood-plus.cc
     model/tcp-yeah.cc
     model/udp-header.cc
     model/udp-l4-protocol.cc
@@ -244,7 +243,7 @@
     model/tcp-tx-item.h
     model/tcp-vegas.h
     model/tcp-veno.h
-    model/tcp-westwood.h
+    model/tcp-westwood-plus.h
     model/tcp-yeah.h
     model/udp-header.h
     model/udp-l4-protocol.h
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/doc/internet-stack.rst ns-3.38/src/internet/doc/internet-stack.rst
--- ns-3.37/src/internet/doc/internet-stack.rst	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/doc/internet-stack.rst	2023-03-16 20:54:23.612868500 -0700
@@ -20,56 +20,56 @@
 which does the following to all nodes passed in as arguments::
 
     void
-    InternetStackHelper::Install (Ptr<Node> node) const
+    InternetStackHelper::Install(Ptr<Node> node) const
     {
       if (m_ipv4Enabled)
         {
           /* IPv4 stack */
-          if (node->GetObject<Ipv4> () != 0)
+          if (node->GetObject<Ipv4>() != 0)
             {
-              NS_FATAL_ERROR ("InternetStackHelper::Install (): Aggregating "
-                              "an InternetStack to a node with an existing Ipv4 object");
+              NS_FATAL_ERROR("InternetStackHelper::Install(): Aggregating "
+                             "an InternetStack to a node with an existing Ipv4 object");
               return;
             }
 
-          CreateAndAggregateObjectFromTypeId (node, "ns3::ArpL3Protocol");
-          CreateAndAggregateObjectFromTypeId (node, "ns3::Ipv4L3Protocol");
-          CreateAndAggregateObjectFromTypeId (node, "ns3::Icmpv4L4Protocol");
+          CreateAndAggregateObjectFromTypeId(node, "ns3::ArpL3Protocol");
+          CreateAndAggregateObjectFromTypeId(node, "ns3::Ipv4L3Protocol");
+          CreateAndAggregateObjectFromTypeId(node, "ns3::Icmpv4L4Protocol");
           // Set routing
-          Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
-          Ptr<Ipv4RoutingProtocol> ipv4Routing = m_routing->Create (node);
-          ipv4->SetRoutingProtocol (ipv4Routing);
+          Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
+          Ptr<Ipv4RoutingProtocol> ipv4Routing = m_routing->Create(node);
+          ipv4->SetRoutingProtocol(ipv4Routing);
         }
 
       if (m_ipv6Enabled)
         {
           /* IPv6 stack */
-          if (node->GetObject<Ipv6> () != 0)
+          if (node->GetObject<Ipv6>() != 0)
             {
-              NS_FATAL_ERROR ("InternetStackHelper::Install (): Aggregating "
-                              "an InternetStack to a node with an existing Ipv6 object");
+              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");
+          CreateAndAggregateObjectFromTypeId(node, "ns3::Ipv6L3Protocol");
+          CreateAndAggregateObjectFromTypeId(node, "ns3::Icmpv6L4Protocol");
           // Set routing
-          Ptr<Ipv6> ipv6 = node->GetObject<Ipv6> ();
-          Ptr<Ipv6RoutingProtocol> ipv6Routing = m_routingv6->Create (node);
-          ipv6->SetRoutingProtocol (ipv6Routing);
+          Ptr<Ipv6> ipv6 = node->GetObject<Ipv6>();
+          Ptr<Ipv6RoutingProtocol> ipv6Routing = m_routingv6->Create(node);
+          ipv6->SetRoutingProtocol(ipv6Routing);
 
           /* register IPv6 extensions and options */
-          ipv6->RegisterExtensions ();
-          ipv6->RegisterOptions ();
+          ipv6->RegisterExtensions();
+          ipv6->RegisterOptions();
         }
 
       if (m_ipv4Enabled || m_ipv6Enabled)
         {
           /* UDP and TCP stacks */
-          CreateAndAggregateObjectFromTypeId (node, "ns3::UdpL4Protocol");
-          node->AggregateObject (m_tcpFactory.Create<Object> ());
-          Ptr<PacketSocketFactory> factory = CreateObject<PacketSocketFactory> ();
-          node->AggregateObject (factory);
+          CreateAndAggregateObjectFromTypeId(node, "ns3::UdpL4Protocol");
+          node->AggregateObject(m_tcpFactory.Create<Object>());
+          Ptr<PacketSocketFactory> factory = CreateObject<PacketSocketFactory>();
+          node->AggregateObject(factory);
         }
     }
 
@@ -79,19 +79,19 @@
 Note that the routing protocol is configured and set outside this
 function. By default, the following protocols are added::
 
-    void InternetStackHelper::Initialize ()
+    void InternetStackHelper::Initialize()
     {
-      SetTcp ("ns3::TcpL4Protocol");
+      SetTcp("ns3::TcpL4Protocol");
       Ipv4StaticRoutingHelper staticRouting;
       Ipv4GlobalRoutingHelper globalRouting;
       Ipv4ListRoutingHelper listRouting;
       Ipv6ListRoutingHelper listRoutingv6;
       Ipv6StaticRoutingHelper staticRoutingv6;
-      listRouting.Add (staticRouting, 0);
-      listRouting.Add (globalRouting, -10);
-      listRoutingv6.Add (staticRoutingv6, 0);
-      SetRoutingHelper (listRouting);
-      SetRoutingHelper (listRoutingv6);
+      listRouting.Add(staticRouting, 0);
+      listRouting.Add(globalRouting, -10);
+      listRoutingv6.Add(staticRoutingv6, 0);
+      SetRoutingHelper(listRouting);
+      SetRoutingHelper(listRoutingv6);
     }
 
 By default, IPv4 and IPv6 are enabled.
@@ -129,18 +129,18 @@
 The actual registration is done
 with a statement such as follows::
 
-     RegisterProtocolHandler ( MakeCallback (&Ipv4Protocol::Receive, ipv4),
-                               Ipv4L3Protocol::PROT_NUMBER, 0);
+     RegisterProtocolHandler( MakeCallback(&Ipv4Protocol::Receive, ipv4),
+                              Ipv4L3Protocol::PROT_NUMBER, 0);
 
 The Ipv4L3Protocol object is aggregated to the Node; there is only one such
 Ipv4L3Protocol object. Higher-layer protocols that have a packet to send down to
-the Ipv4L3Protocol object can call ``GetObject<Ipv4L3Protocol> ()`` to obtain a
+the Ipv4L3Protocol object can call ``GetObject<Ipv4L3Protocol>()`` to obtain a
 pointer, as follows::
 
-  Ptr<Ipv4L3Protocol> ipv4 = m_node->GetObject<Ipv4L3Protocol> ();
+  Ptr<Ipv4L3Protocol> ipv4 = m_node->GetObject<Ipv4L3Protocol>();
   if (ipv4 != 0)
     {
-      ipv4->Send (packet, saddr, daddr, PROT_NUMBER);
+      ipv4->Send(packet, saddr, daddr, PROT_NUMBER);
     }
 
 This class nicely demonstrates two techniques we exploit in |ns3| to bind
@@ -150,7 +150,7 @@
 forwards it up the stack.  This is done with the following function::
 
     void
-    Ipv4L3Protocol::LocalDeliver (Ptr<const Packet> packet, Ipv4Header const&ip, uint32_t iif)
+    Ipv4L3Protocol::LocalDeliver(Ptr<const Packet> packet, Ipv4Header const&ip, uint32_t iif)
 
 The first step is to find the right Ipv4L4Protocol object, based on IP protocol
 number. For instance, TCP is registered in the demux as protocol number 6.
@@ -171,7 +171,7 @@
 overfilling the ARP cache queue size. In those cases it is useful to increase the ARP cache
 pending size to a proper value, e.g.::
 
-    Config::SetDefault ("ns3::ArpCache::PendingQueueSize", UintegerValue (MAX_BURST_SIZE/L2MTU*3));
+    Config::SetDefault("ns3::ArpCache::PendingQueueSize", UintegerValue(MAX_BURST_SIZE/L2MTU*3));
 
 The IPv6 implementation follows a similar architecture.  Dual-stacked nodes (one with
 support for both IPv4 and IPv6) will allow an IPv6 socket to receive IPv4 connections
@@ -190,47 +190,47 @@
 For instance, to create a UDP socket, an application would use a code snippet
 such as the following::
 
-      Ptr<Udp> udpSocketFactory = GetNode ()->GetObject<Udp> ();
-      Ptr<Socket> m_socket = socketFactory->CreateSocket ();
-      m_socket->Bind (m_local_address);
+      Ptr<Udp> udpSocketFactory = GetNode()->GetObject<Udp>();
+      Ptr<Socket> m_socket = socketFactory->CreateSocket();
+      m_socket->Bind(m_local_address);
       ...
 
 The above will query the node to get a pointer to its UDP socket factory, will
 create one such socket, and will use the socket with an API similar to the
-C-based sockets API, such as ``Connect ()`` and ``Send ()``.  The address passed
-to the ``Bind ()``, ``Connect ()``, or ``Send ()`` functions may be a
+C-based sockets API, such as ``Connect()`` and ``Send()``.  The address passed
+to the ``Bind()``, ``Connect()``, or ``Send()`` functions may be a
 :cpp:class:`Ipv4Address`, :cpp:class:`Ipv6Address`, or :cpp:class:`Address`.
 If a :cpp:class:`Address` is passed in and contains anything other than
 a :cpp:class:`Ipv4Address` or :cpp:class:`Ipv6Address`, these functions will
-return an error.  The ``Bind (void)`` and ``Bind6 (void)`` functions bind to
+return an error.  The ``Bind()`` and ``Bind6()`` functions bind to
 "0.0.0.0" and "::" respectively.
 
 The socket can also be bound to a specific NetDevice though the
-``BindToNetDevice (Ptr<NetDevice> netdevice)`` function.
-``BindToNetDevice (Ptr<NetDevice> netdevice)`` will bind the socket
-to "0.0.0.0" and "::" (equivalent to calling ``Bind ()`` and ``Bind6 ()``,
+``BindToNetDevice(Ptr<NetDevice> netdevice)`` function.
+``BindToNetDevice(Ptr<NetDevice> netdevice)`` will bind the socket
+to "0.0.0.0" and "::"(equivalent to calling ``Bind()`` and ``Bind6()``,
 unless the socket has been already bound to a specific address.
 Summarizing, the correct sequence is::
 
-      Ptr<Udp> udpSocketFactory = GetNode ()->GetObject<Udp> ();
-      Ptr<Socket> m_socket = socketFactory->CreateSocket ();
-      m_socket->BindToNetDevice (n_netDevice);
+      Ptr<Udp> udpSocketFactory = GetNode()->GetObject<Udp>();
+      Ptr<Socket> m_socket = socketFactory->CreateSocket();
+      m_socket->BindToNetDevice(n_netDevice);
      ...
 
 or::
 
-      Ptr<Udp> udpSocketFactory = GetNode ()->GetObject<Udp> ();
-      Ptr<Socket> m_socket = socketFactory->CreateSocket ();
-      m_socket->Bind (m_local_address);
-      m_socket->BindToNetDevice (n_netDevice);
+      Ptr<Udp> udpSocketFactory = GetNode()->GetObject<Udp>();
+      Ptr<Socket> m_socket = socketFactory->CreateSocket();
+      m_socket->Bind(m_local_address);
+      m_socket->BindToNetDevice(n_netDevice);
       ...
 
 The following raises an error::
 
-      Ptr<Udp> udpSocketFactory = GetNode ()->GetObject<Udp> ();
-      Ptr<Socket> m_socket = socketFactory->CreateSocket ();
-      m_socket->BindToNetDevice (n_netDevice);
-      m_socket->Bind (m_local_address);
+      Ptr<Udp> udpSocketFactory = GetNode()->GetObject<Udp>();
+      Ptr<Socket> m_socket = socketFactory->CreateSocket();
+      m_socket->BindToNetDevice(n_netDevice);
+      m_socket->Bind(m_local_address);
       ...
 
 See the chapter on |ns3| sockets for more information.
@@ -243,11 +243,11 @@
 :cpp:class:`Ipv4EndPoint`.  This class holds the addressing/port tuple (local
 port, local address, destination port, destination address) associated with the
 socket, and a receive callback. This receive callback has a receive function
-registered by the socket. The ``Lookup ()`` function to Ipv4EndPointDemux
-returns a list of Ipv4EndPoint objects (there may be a list since more than one
+registered by the socket. The ``Lookup()`` function to Ipv4EndPointDemux
+returns a list of Ipv4EndPoint objects(there may be a list since more than one
 socket may match the packet). The layer-4 protocol copies the packet to each
-Ipv4EndPoint and calls its ``ForwardUp ()`` method, which then calls the
-``Receive ()`` function registered by the socket.
+Ipv4EndPoint and calls its ``ForwardUp()`` method, which then calls the
+``Receive()`` function registered by the socket.
 
 An issue that arises when working with the sockets API on real
 systems is the need to manage the reading from a socket, using
@@ -257,14 +257,14 @@
 callback is invoked by the transport protocol when data is available.
 This callback is specified as follows::
 
-  void Socket::SetRecvCallback (Callback<void, Ptr<Socket>,
-                                Ptr<Packet>,
-                                const Address&> receivedData);
+  void Socket::SetRecvCallback(Callback<void, Ptr<Socket>,
+                               Ptr<Packet>,
+                               const Address&> receivedData);
 
 The data being received is conveyed in the Packet data buffer.  An example
 usage is in class :cpp:class:`PacketSink`::
 
-  m_socket->SetRecvCallback (MakeCallback(&PacketSink::HandleRead, this));
+  m_socket->SetRecvCallback(MakeCallback(&PacketSink::HandleRead, this));
 
 To summarize, internally, the UDP implementation is organized as follows:
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/doc/ipv4.rst ns-3.38/src/internet/doc/ipv4.rst
--- ns-3.37/src/internet/doc/ipv4.rst	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/doc/ipv4.rst	2023-03-16 20:54:23.612868500 -0700
@@ -40,16 +40,16 @@
 
 ::
 
-    Ptr<Node> n0 = CreateObject<Node> ();
-    Ptr<Node> n1 = CreateObject<Node> ();
-    NodeContainer net (n0, n1);
+    Ptr<Node> n0 = CreateObject<Node>();
+    Ptr<Node> n1 = CreateObject<Node>();
+    NodeContainer net(n0, n1);
     CsmaHelper csma;
-    NetDeviceContainer ndc = csma.Install (net);
+    NetDeviceContainer ndc = csma.Install(net);
 
-    NS_LOG_INFO ("Assign IPv4 Addresses.");
+    NS_LOG_INFO("Assign IPv4 Addresses.");
     Ipv4AddressHelper ipv4;
-    ipv4.SetBase (Ipv4Address ("192.168.1.0"), NetMask ("/24"));
-    Ipv4InterfaceContainer ic = ipv4.Assign (ndc);
+    ipv4.SetBase(Ipv4Address("192.168.1.0"), NetMask("/24"));
+    Ipv4InterfaceContainer ic = ipv4.Assign(ndc);
 
 This method will add two global IPv4 addresses to the nodes.
 
@@ -64,20 +64,20 @@
 
 ::
 
-    Ptr<Node> n0 = CreateObject<Node> ();
-    NodeContainer net (n0);
+    Ptr<Node> n0 = CreateObject<Node>();
+    NodeContainer net(n0);
     CsmaHelper csma;
-    NetDeviceContainer ndc = csma.Install (net);
+    NetDeviceContainer ndc = csma.Install(net);
 
-    NS_LOG_INFO ("Specifically Assign an IPv4 Address.");
+    NS_LOG_INFO("Specifically Assign an IPv4 Address.");
     Ipv4AddressHelper ipv4;
-    Ptr<NetDevice> device = ndc.Get (0);
-    Ptr<Node> node = device->GetNode ();
-    Ptr<Ipv4> ipv4proto = node->GetObject<Ipv4> ();
+    Ptr<NetDevice> device = ndc.Get(0);
+    Ptr<Node> node = device->GetNode();
+    Ptr<Ipv4> ipv4proto = node->GetObject<Ipv4>();
     int32_t ifIndex = 0;
-    ifIndex = ipv4proto->GetInterfaceForDevice (device);
-    Ipv4InterfaceAddress ipv4Addr = Ipv4InterfaceAddress (Ipv4Address ("192.168.1.42"), NetMask ("/24"));
-    ipv4proto->AddAddress (ifIndex, ipv4Addr);
+    ifIndex = ipv4proto->GetInterfaceForDevice(device);
+    Ipv4InterfaceAddress ipv4Addr = Ipv4InterfaceAddress(Ipv4Address("192.168.1.42"), NetMask("/24"));
+    ipv4proto->AddAddress(ifIndex, ipv4Addr);
 
 
 DHCP assigned IPv4 addresses
@@ -156,7 +156,7 @@
 ns3::Ipv4L3Protocol::Receive function.
 
 - In the receive function, the Ipv4InterfaceList is iterated, and if the
-  Ipv4Interface corresponding to the receiving device is fount to be in the
+  Ipv4Interface corresponding to the receiving device is found to be in the
   UP state, the trace is fired.
 
 The "Drop" trace is fired in any case where the packet is dropped (in both
@@ -239,7 +239,7 @@
 *************
 
 NeighborCacheHelper provides a way to generate ARP cache automatically. It
-generates needed ARP cache before simulation start to avoid the delay and mesage overhead of
+generates needed ARP cache before simulation start to avoid the delay and message overhead of
 address resolution in simulations that are focused on other performance aspects.
 The state of entries which are generated by NeighborCacheHelper is ``STATIC_AUTOGENERATED``,
 which is similar to ``PERMANENT``, but they are not manually added or removed by user, they
@@ -264,25 +264,25 @@
 .. code-block:: c++
 
   NeighborCacheHelper neighborCache;
-  neighborCache.PopulateNeighborCache ();
+  neighborCache.PopulateNeighborCache();
 
 * Populate neighbor ARP caches for a given channel:
 
 .. code-block:: c++
 
   NeighborCacheHelper neighborCache;
-  neighborCache.PopulateNeighborCache (channel);     // channel is the Ptr<Channel> want to generate ARP caches
+  neighborCache.PopulateNeighborCache(channel);     // channel is the Ptr<Channel> want to generate ARP caches
 
 * Populate neighbor ARP caches for devices in a given NetDeviceContainer:
 
 .. code-block:: c++
 
   NeighborCacheHelper neighborCache;
-  neighborCache.PopulateNeighborCache (netDevices);   // netDevices is the NetDeviceContainer want to generate ARP caches
+  neighborCache.PopulateNeighborCache(netDevices);   // netDevices is the NetDeviceContainer want to generate ARP caches
 
 * Populate neighbor ARP caches for a given Ipv4InterfaceContainer:
 
 .. code-block:: c++
 
   NeighborCacheHelper neighborCache;
-  neighborCache.PopulateNeighborCache (interfaces);    // interfaces is the Ipv4InterfaceContainer want to generate ARP caches
+  neighborCache.PopulateNeighborCache(interfaces);    // interfaces is the Ipv4InterfaceContainer want to generate ARP caches
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/doc/ipv6.rst ns-3.38/src/internet/doc/ipv6.rst
--- ns-3.37/src/internet/doc/ipv6.rst	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/doc/ipv6.rst	2023-03-16 20:54:23.612868500 -0700
@@ -98,18 +98,18 @@
 ::
 
     NodeContainer n;
-    n.Create (3);
+    n.Create(3);
 
     InternetStackHelper internet;
     InternetStackHelper internetV4only;
     InternetStackHelper internetV6only;
 
-    internetV4only.SetIpv6StackInstall (false);
-    internetV6only.SetIpv4StackInstall (false);
+    internetV4only.SetIpv6StackInstall(false);
+    internetV6only.SetIpv4StackInstall(false);
 
-    internet.Install (n.Get (0));
-    internetV6only.Install (n.Get (1));
-    internetV4only.Install (n.Get (2));
+    internet.Install(n.Get(0));
+    internetV6only.Install(n.Get(1));
+    internetV4only.Install(n.Get(2));
 
 
 
@@ -142,16 +142,16 @@
 
 ::
 
-    Ptr<Node> n0 = CreateObject<Node> ();
-    Ptr<Node> n1 = CreateObject<Node> ();
-    NodeContainer net (n0, n1);
+    Ptr<Node> n0 = CreateObject<Node>();
+    Ptr<Node> n1 = CreateObject<Node>();
+    NodeContainer net(n0, n1);
     CsmaHelper csma;
-    NetDeviceContainer ndc = csma.Install (net);
+    NetDeviceContainer ndc = csma.Install(net);
 
-    NS_LOG_INFO ("Assign IPv6 Addresses.");
+    NS_LOG_INFO("Assign IPv6 Addresses.");
     Ipv6AddressHelper ipv6;
-    ipv6.SetBase (Ipv6Address ("2001:db8::"), Ipv6Prefix (64));
-    Ipv6InterfaceContainer ic = ipv6.Assign (ndc);
+    ipv6.SetBase(Ipv6Address("2001:db8::"), Ipv6Prefix(64));
+    Ipv6InterfaceContainer ic = ipv6.Assign(ndc);
 
 This method will add two global IPv6 addresses to the nodes. Note that, as usual for IPv6,
 all the nodes will also have a link-local address. Typically the first address on an
@@ -168,20 +168,20 @@
 
 ::
 
-    Ptr<Node> n0 = CreateObject<Node> ();
-    NodeContainer net (n0);
+    Ptr<Node> n0 = CreateObject<Node>();
+    NodeContainer net(n0);
     CsmaHelper csma;
-    NetDeviceContainer ndc = csma.Install (net);
+    NetDeviceContainer ndc = csma.Install(net);
 
-    NS_LOG_INFO ("Specifically Assign an IPv6 Address.");
+    NS_LOG_INFO("Specifically Assign an IPv6 Address.");
     Ipv6AddressHelper ipv6;
-    Ptr<NetDevice> device = ndc.Get (0);
-    Ptr<Node> node = device->GetNode ();
-    Ptr<Ipv6> ipv6proto = node->GetObject<Ipv6> ();
+    Ptr<NetDevice> device = ndc.Get(0);
+    Ptr<Node> node = device->GetNode();
+    Ptr<Ipv6> ipv6proto = node->GetObject<Ipv6>();
     int32_t ifIndex = 0;
-    ifIndex = ipv6proto->GetInterfaceForDevice (device);
-    Ipv6InterfaceAddress ipv6Addr = Ipv6InterfaceAddress (Ipv6Address ("2001:db8:f00d:cafe::42"), Ipv6Prefix (64));
-    ipv6proto->AddAddress (ifIndex, ipv6Addr);
+    ifIndex = ipv6proto->GetInterfaceForDevice(device);
+    Ipv6InterfaceAddress ipv6Addr = Ipv6InterfaceAddress(Ipv6Address("2001:db8:f00d:cafe::42"), Ipv6Prefix(64));
+    ipv6proto->AddAddress(ifIndex, ipv6Addr);
 
 
 Auto-generated IPv6 addresses
@@ -213,8 +213,8 @@
 
     Ipv6AddressHelper ipv6;
     NetDeviceContainer tmp;
-    tmp.Add (d1.Get (0)); /* n0 */
-    Ipv6InterfaceContainer iic1 = ipv6.AssignWithoutAddress (tmp); /* n0 interface */
+    tmp.Add (d1.Get(0)); /* n0 */
+    Ipv6InterfaceContainer iic1 = ipv6.AssignWithoutAddress(tmp); /* n0 interface */
 
 
 Random-generated IPv6 addresses
@@ -341,28 +341,28 @@
 ::
 
     NodeContainer n;
-    n.Create (4);
+    n.Create(4);
 
-    NS_LOG_INFO ("Create IPv6 Internet Stack");
+    NS_LOG_INFO("Create IPv6 Internet Stack");
     InternetStackHelper internetv6;
-    internetv6.Install (n);
+    internetv6.Install(n);
 
-    NS_LOG_INFO ("Create channels.");
+    NS_LOG_INFO("Create channels.");
     CsmaHelper csma;
-    NetDeviceContainer d = csma.Install (n);
+    NetDeviceContainer d = csma.Install(n);
 
-    NS_LOG_INFO ("Create networks and assign IPv6 Addresses.");
+    NS_LOG_INFO("Create networks and assign IPv6 Addresses.");
     Ipv6AddressHelper ipv6;
-    ipv6.SetBase (Ipv6Address ("2001:db8::"), Ipv6Prefix (64));
-    Ipv6InterfaceContainer iic = ipv6.Assign (d);
+    ipv6.SetBase(Ipv6Address("2001:db8::"), Ipv6Prefix(64));
+    Ipv6InterfaceContainer iic = ipv6.Assign(d);
 
 Additionally, a common task is to enable forwarding on one of the nodes and to
 setup a default route toward it in the other nodes, e.g.:
 
 ::
 
-    iic.SetForwarding (0, true);
-    iic.SetDefaultRouteInAllNodes (0);
+    iic.SetForwarding(0, true);
+    iic.SetDefaultRouteInAllNodes(0);
 
 This will enable forwarding on the node *0* and will setup a default route in
 :cpp:class:`ns3::Ipv6StaticRouting` on all the other nodes. Note that this
@@ -496,7 +496,7 @@
 *************
 
 NeighborCacheHelper provides a way to generate NDISC cache automatically. It generates
-needed NDISC cache before simulation start to avoid the delay and mesage overhead of
+needed NDISC cache before simulation start to avoid the delay and message overhead of
 neighbor discovery in simulations that are focused on other performance aspects.
 The state of entries generate by NeighborCacheHelper is ``STATIC_AUTOGENERATED``,
 which is similar to ``PERMANENT``, but they are not manually added or removed by user, they
@@ -522,4 +522,4 @@
 .. code-block:: c++
 
   NeighborCacheHelper neighborCache;
-  neighborCache.PopulateNeighborCache (interfaces);    // interfaces is the Ipv6InterfaceContainer want to generate ARP caches
+  neighborCache.PopulateNeighborCache(interfaces);    // interfaces is the Ipv6InterfaceContainer want to generate ARP caches
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/doc/routing-overview.rst ns-3.38/src/internet/doc/routing-overview.rst
--- ns-3.37/src/internet/doc/routing-overview.rst	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/doc/routing-overview.rst	2023-03-16 20:54:23.612868500 -0700
@@ -130,11 +130,11 @@
 Classes Ipv4ListRouting and Ipv6ListRouting provides a pure virtual function declaration
 for the method that allows one to add a routing protocol::
 
-  void AddRoutingProtocol (Ptr<Ipv4RoutingProtocol> routingProtocol,
-                           int16_t priority);
+  void AddRoutingProtocol(Ptr<Ipv4RoutingProtocol> routingProtocol,
+                          int16_t priority);
 
-  void AddRoutingProtocol (Ptr<Ipv6RoutingProtocol> routingProtocol,
-                           int16_t priority);
+  void AddRoutingProtocol(Ptr<Ipv6RoutingProtocol> routingProtocol,
+                          int16_t priority);
 
 These methods are implemented respectively by class Ipv4ListRoutingImpl and by class
 Ipv6ListRoutingImpl in the internet module.
@@ -148,8 +148,8 @@
 you want your Ipv4RoutingProtocol to have priority lower than the static
 routing, insert it with priority less than 0; e.g.::
 
-  Ptr<MyRoutingProtocol> myRoutingProto = CreateObject<MyRoutingProtocol> ();
-  listRoutingPtr->AddRoutingProtocol (myRoutingProto, -10);
+  Ptr<MyRoutingProtocol> myRoutingProto = CreateObject<MyRoutingProtocol>();
+  listRoutingPtr->AddRoutingProtocol(myRoutingProto, -10);
 
 Upon calls to RouteOutput() or RouteInput(), the list routing object will search
 the list of routing protocols, in priority order, until a route is found. Such
@@ -195,7 +195,7 @@
 following function call will cause all of the nodes that have an Ipv4 interface
 to receive forwarding tables entered automatically by the GlobalRouteManager::
 
-  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
+  Ipv4GlobalRoutingHelper::PopulateRoutingTables();
 
 *Note:* A reminder that the wifi NetDevice will work but does not take any
 wireless effects into account. For wireless, we recommend OLSR dynamic routing
@@ -204,7 +204,7 @@
 It is possible to call this function again in the midst of a simulation using
 the following additional public function::
 
-  Ipv4GlobalRoutingHelper::RecomputeRoutingTables ();
+  Ipv4GlobalRoutingHelper::RecomputeRoutingTables();
 
 which flushes the old tables, queries the nodes for new interface information,
 and rebuilds the routes.
@@ -212,8 +212,8 @@
 For instance, this scheduling call will cause the tables to be rebuilt
 at time 5 seconds::
 
-  Simulator::Schedule (Seconds (5),
-                       &Ipv4GlobalRoutingHelper::RecomputeRoutingTables);
+  Simulator::Schedule(Seconds(5),
+                      &Ipv4GlobalRoutingHelper::RecomputeRoutingTables);
 
 
 There are two attributes that govern the behavior. The first is
@@ -272,12 +272,12 @@
 The GlobalRouteManager first walks the list of nodes and aggregates
 a GlobalRouter interface to each one as follows::
 
-  typedef std::vector < Ptr<Node> >::iterator Iterator;
-  for (Iterator i = NodeList::Begin (); i != NodeList::End (); i++)
+  typedef std::vector<Ptr<Node>>::iterator Iterator;
+  for (Iterator i = NodeList::Begin(); i != NodeList::End(); i++)
     {
       Ptr<Node> node = *i;
-      Ptr<GlobalRouter> globalRouter = CreateObject<GlobalRouter> (node);
-      node->AggregateObject (globalRouter);
+      Ptr<GlobalRouter> globalRouter = CreateObject<GlobalRouter>(node);
+      node->AggregateObject(globalRouter);
     }
 
 This interface is later queried and used to generate a Link State
@@ -429,10 +429,10 @@
 to a node::
 
     void
-    Ipv4StaticRouting::AddMulticastRoute (Ipv4Address origin,
-                                          Ipv4Address group,
-                                          uint32_t inputInterface,
-                                          std::vector<uint32_t> outputInterfaces);
+    Ipv4StaticRouting::AddMulticastRoute(Ipv4Address origin,
+                                         Ipv4Address group,
+                                         uint32_t inputInterface,
+                                         std::vector<uint32_t> outputInterfaces);
 
 A multicast route must specify an origin IP address, a multicast group and an
 input network interface index as conditions and provide a vector of output
@@ -452,7 +452,7 @@
 Another command sets the default multicast route::
 
     void
-    Ipv4StaticRouting::SetDefaultMulticastRoute (uint32_t outputInterface);
+    Ipv4StaticRouting::SetDefaultMulticastRoute(uint32_t outputInterface);
 
 This is the multicast equivalent of the unicast version SetDefaultRoute. We
 tell the routing system what to do in the case where a specific route to a
@@ -472,14 +472,14 @@
 Finally, a number of additional functions are provided to fetch and remove
 multicast routes::
 
-  uint32_t GetNMulticastRoutes (void) const;
+  uint32_t GetNMulticastRoutes() const;
 
-  Ipv4MulticastRoute *GetMulticastRoute (uint32_t i) const;
+  Ipv4MulticastRoute *GetMulticastRoute(uint32_t i) const;
 
-  Ipv4MulticastRoute *GetDefaultMulticastRoute (void) const;
+  Ipv4MulticastRoute *GetDefaultMulticastRoute() const;
 
-  bool RemoveMulticastRoute (Ipv4Address origin,
-                             Ipv4Address group,
-                             uint32_t inputInterface);
+  bool RemoveMulticastRoute(Ipv4Address origin,
+                            Ipv4Address group,
+                            uint32_t inputInterface);
 
-  void RemoveMulticastRoute (uint32_t index);
+  void RemoveMulticastRoute(uint32_t index);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/doc/tcp.rst ns-3.38/src/internet/doc/tcp.rst
--- ns-3.37/src/internet/doc/tcp.rst	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/doc/tcp.rst	2023-03-16 20:54:23.612868500 -0700
@@ -117,17 +117,17 @@
 
   // Create a packet sink on the star "hub" to receive these packets
   uint16_t port = 50000;
-  Address sinkLocalAddress(InetSocketAddress (Ipv4Address::GetAny (), port));
-  PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", sinkLocalAddress);
-  ApplicationContainer sinkApp = sinkHelper.Install (serverNode);
-  sinkApp.Start (Seconds (1.0));
-  sinkApp.Stop (Seconds (10.0));
+  Address sinkLocalAddress(InetSocketAddress(Ipv4Address::GetAny(), port));
+  PacketSinkHelper sinkHelper("ns3::TcpSocketFactory", sinkLocalAddress);
+  ApplicationContainer sinkApp = sinkHelper.Install(serverNode);
+  sinkApp.Start(Seconds(1.0));
+  sinkApp.Stop(Seconds(10.0));
 
 Similarly, the below snippet configures OnOffApplication traffic source to use
 TCP::
 
   // Create the OnOff applications to send TCP to the server
-  OnOffHelper clientHelper ("ns3::TcpSocketFactory", Address ());
+  OnOffHelper clientHelper("ns3::TcpSocketFactory", Address());
 
 The careful reader will note above that we have specified the TypeId of an
 abstract base class :cpp:class:`TcpSocketFactory`. How does the script tell
@@ -139,7 +139,7 @@
 
 To configure behavior of TCP, a number of parameters are exported through the
 |ns3| attribute system. These are documented in the `Doxygen
-<http://www.nsnam.org/doxygen/classns3_1_1_tcp_socket.html>`_ for class
+<https://www.nsnam.org/docs/doxygen/d3/dea/classns3_1_1_tcp_socket.html>`_ for class
 :cpp:class:`TcpSocket`. For example, the maximum segment size is a
 settable attribute.
 
@@ -147,7 +147,7 @@
 created, one may put the following statement at the top of the simulation
 program::
 
-  Config::SetDefault ("ns3::TcpL4Protocol::SocketType", StringValue ("ns3::TcpNewReno"));
+  Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue("ns3::TcpNewReno"));
 
 For users who wish to have a pointer to the actual socket (so that
 socket operations like Bind(), setting socket options, etc. can be
@@ -161,10 +161,10 @@
 created on this node::
 
   // Create and bind the socket...
-  TypeId tid = TypeId::LookupByName ("ns3::TcpNewReno");
-  Config::Set ("/NodeList/*/$ns3::TcpL4Protocol/SocketType", TypeIdValue (tid));
+  TypeId tid = TypeId::LookupByName("ns3::TcpNewReno");
+  Config::Set("/NodeList/*/$ns3::TcpL4Protocol/SocketType", TypeIdValue(tid));
   Ptr<Socket> localSocket =
-    Socket::CreateSocket (n0n1.Get (0), TcpSocketFactory::GetTypeId ());
+    Socket::CreateSocket(n0n1.Get(0), TcpSocketFactory::GetTypeId());
 
 Above, the "*" wild card for node number is passed to the attribute
 configuration system, so that all future sockets on all nodes are set to
@@ -172,13 +172,13 @@
 the specified node, one would have to do something like::
 
   // Create and bind the socket...
-  TypeId tid = TypeId::LookupByName ("ns3::TcpNewReno");
+  TypeId tid = TypeId::LookupByName("ns3::TcpNewReno");
   std::stringstream nodeId;
-  nodeId << n0n1.Get (0)->GetId ();
-  std::string specificNode = "/NodeList/" + nodeId.str () + "/$ns3::TcpL4Protocol/SocketType";
-  Config::Set (specificNode, TypeIdValue (tid));
+  nodeId << n0n1.Get(0)->GetId();
+  std::string specificNode = "/NodeList/" + nodeId.str() + "/$ns3::TcpL4Protocol/SocketType";
+  Config::Set(specificNode, TypeIdValue(tid));
   Ptr<Socket> localSocket =
-    Socket::CreateSocket (n0n1.Get (0), TcpSocketFactory::GetTypeId ());
+    Socket::CreateSocket(n0n1.Get(0), TcpSocketFactory::GetTypeId());
 
 Once a TCP socket is created, one will want to follow conventional socket logic
 and either connect() and send() (for a TCP client) or bind(), listen(), and
@@ -456,31 +456,31 @@
 by a full-sized segment (SMSS).  In contrast, in ns-3 NewReno, cwnd is increased
 by (1/cwnd) with a rounding off due to type casting into int.
 
-.. code-block:: c++
+.. code-block::
+   :caption: Linux Reno `cwnd` update
 
    if (m_cWndCnt >= w)
-    {
+   {
       uint32_t delta = m_cWndCnt / w;
 
       m_cWndCnt -= delta * w;
       tcb->m_cWnd += delta * tcb->m_segmentSize;
-      NS_LOG_DEBUG ("Subtracting delta * w from m_cWndCnt " << delta * w);
-    }
+      NS_LOG_DEBUG("Subtracting delta * w from m_cWndCnt " << delta * w);
+   }
 
-   :label: linuxrenocongavoid
 
-.. code-block:: c++
+.. code-block::
+   :caption: New Reno `cwnd` update
 
    if (segmentsAcked > 0)
     {
-      double adder = static_cast<double> (tcb->m_segmentSize * tcb->m_segmentSize) / tcb->m_cWnd.Get ();
-      adder = std::max (1.0, adder);
-      tcb->m_cWnd += static_cast<uint32_t> (adder);
-      NS_LOG_INFO ("In CongAvoid, updated to cwnd " << tcb->m_cWnd <<
+      double adder = static_cast<double>(tcb->m_segmentSize * tcb->m_segmentSize) / tcb->m_cWnd.Get();
+      adder = std::max(1.0, adder);
+      tcb->m_cWnd += static_cast<uint32_t>(adder);
+      NS_LOG_INFO("In CongAvoid, updated to cwnd " << tcb->m_cWnd <<
                    " ssthresh " << tcb->m_ssThresh);
     }
 
-   :label: newrenocongavoid
 
 So, there are two main difference between the TCP Linux Reno and TCP NewReno
 in ns-3:
@@ -525,7 +525,7 @@
 size of the slow start threshold itself. Then, the congestion window is set
 to such value.
 
-.. math::   cWnd = (1-b(cWnd)) \cdot cWnd
+.. math::   cWnd = (1 - b(cWnd)) \cdot cWnd
    :label: highspeedcwnddecrement
 
 The lookup table for the function b() is taken from the same RFC.
@@ -554,6 +554,12 @@
 While Westwood performs the bandwidth sampling every ACK reception,
 Westwood+ samples the bandwidth every RTT.
 
+The TCP Westwood model has been removed in ns-3.38 due to bugs that are impossible
+to fix without modifying the original Westwood model as presented in the published papers.
+For further info refer to https://gitlab.com/nsnam/ns-3-dev/-/issues/579
+
+The Westwood+ model does not have such issues, and is still available.
+
 WARNING: this TCP model lacks validation and regression tests; use with caution.
 
 More information at: http://dl.acm.org/citation.cfm?id=381704 and
@@ -833,13 +839,12 @@
 
 As a first approximation, the LEDBAT sender operates as shown below:
 
-On receipt of an ACK:
+On receipt of an ACK::
 
-::
        currentdelay = acknowledgement.delay
-       basedelay = min (basedelay, currentdelay)
+       basedelay = min(basedelay, currentdelay)
        queuingdelay = currentdelay - basedelay
-       offtarget = (TARGET - queuingdelay) / TARGET
+       offtarget =(TARGET - queuingdelay) / TARGET
        cWnd += GAIN * offtarget * bytesnewlyacked * MSS / cWnd
 
 ``TARGET`` is the maximum queueing delay that LEDBAT itself may introduce in the
@@ -856,17 +861,13 @@
 * noiseFilterLen = 4
 * Gain = 1
 
-To enable LEDBAT on all TCP sockets, the following configuration can be used:
+To enable LEDBAT on all TCP sockets, the following configuration can be used::
 
-::
-
-  Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpLedbat::GetTypeId ()));
+  Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpLedbat::GetTypeId()));
 
-To enable LEDBAT on a chosen TCP socket, the following configuration can be used:
+To enable LEDBAT on a chosen TCP socket, the following configuration can be used::
 
-::
-
-  Config::Set ("$ns3::NodeListPriv/NodeList/1/$ns3::TcpL4Protocol/SocketType", TypeIdValue (TcpLedbat::GetTypeId ()));
+  Config::Set("$ns3::NodeListPriv/NodeList/1/$ns3::TcpL4Protocol/SocketType", TypeIdValue(TcpLedbat::GetTypeId()));
 
 The following unit tests have been written to validate the implementation of LEDBAT:
 
@@ -968,11 +969,9 @@
   initial alpha (\alpha) = 1
 
 
-To enable DCTCP on all TCP sockets, the following configuration can be used:
-
-::
+To enable DCTCP on all TCP sockets, the following configuration can be used::
 
-  Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpDctcp::GetTypeId ()));
+  Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpDctcp::GetTypeId()));
 
 To enable DCTCP on a selected node, one can set the "SocketType" attribute
 on the TcpL4Protocol object of that node to the TcpDctcp TypeId.
@@ -985,23 +984,19 @@
 a simple
 configuration to achieve the behavior of desired queue management algorithm.
 
-To configure RED router for DCTCP:
+To configure RED router for DCTCP::
 
-::
-
-  Config::SetDefault ("ns3::RedQueueDisc::UseEcn", BooleanValue (true));
-  Config::SetDefault ("ns3::RedQueueDisc::QW", DoubleValue (1.0));
-  Config::SetDefault ("ns3::RedQueueDisc::MinTh", DoubleValue (16));
-  Config::SetDefault ("ns3::RedQueueDisc::MaxTh", DoubleValue (16));
+  Config::SetDefault("ns3::RedQueueDisc::UseEcn", BooleanValue(true));
+  Config::SetDefault("ns3::RedQueueDisc::QW", DoubleValue(1.0));
+  Config::SetDefault("ns3::RedQueueDisc::MinTh", DoubleValue(16));
+  Config::SetDefault("ns3::RedQueueDisc::MaxTh", DoubleValue(16));
 
 There is also the option, when running CoDel or FqCoDel, to enable ECN
 on the queue and to set the "CeThreshold" value to a low value such as 1ms.
-The following example uses CoDel:
+The following example uses CoDel::
 
-::
-
-  Config::SetDefault ("ns3::CoDelQueueDisc::UseEcn", BooleanValue (true));
-  Config::SetDefault ("ns3::CoDelQueueDisc::CeThreshold", TimeValue (MilliSeconds (1)));
+  Config::SetDefault("ns3::CoDelQueueDisc::UseEcn", BooleanValue(true));
+  Config::SetDefault("ns3::CoDelQueueDisc::CeThreshold", TimeValue(MilliSeconds(1)));
 
 The following unit tests have been written to validate the implementation of DCTCP:
 
@@ -1056,37 +1051,35 @@
 
 The following is a high level overview of BBR congestion control algorithm:
 
-On receiving an ACK:
+On receiving an ACK::
+
     rtt = now - packet.sent_time
-    update_minimum_rtt (rtt)
-    delivery_rate = estimate_delivery_rate (packet)
-    update_maximum_bandwidth (delivery_rate)
+    update_minimum_rtt(rtt)
+    delivery_rate = estimate_delivery_rate(packet)
+    update_maximum_bandwidth(delivery_rate)
+
+After transmitting a data packet::
 
-After transmitting a data packet:
     bdp = max_bandwidth * min_rtt
     if (cwnd * bdp < inflight)
       return
     if (now > nextSendTime)
       {
-        transmit (packet)
-        nextSendTime = now + packet.size / (pacing_gain * max_bandwidth)
+        transmit(packet)
+        nextSendTime = now + packet.size /(pacing_gain * max_bandwidth)
       }
     else
       return
-    Schedule (nextSendTime, Send)
+    Schedule(nextSendTime, Send)
 
-To enable BBR on all TCP sockets, the following configuration can be used:
-
-::
+To enable BBR on all TCP sockets, the following configuration can be used::
 
-  Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpBbr::GetTypeId ()));
+  Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpBbr::GetTypeId()));
 
 To enable BBR on a chosen TCP socket, the following configuration can be used
-(note that an appropriate Node ID must be used instead of 1):
+(note that an appropriate Node ID must be used instead of 1)::
 
-::
-
-  Config::Set ("$ns3::NodeListPriv/NodeList/1/$ns3::TcpL4Protocol/SocketType", TypeIdValue (TcpBbr::GetTypeId ()));
+  Config::Set("$ns3::NodeListPriv/NodeList/1/$ns3::TcpL4Protocol/SocketType", TypeIdValue(TcpBbr::GetTypeId()));
 
 The ns-3 implementation of BBR is based on its Linux implementation. Linux 5.4
 kernel implementation has been used to validate the behavior of ns-3
@@ -1097,7 +1090,7 @@
 
 * BBR should enable (if not already done) TCP pacing feature.
 * Test to validate the values of pacing_gain and cwnd_gain in different phases
-of BBR.
+  of BBR.
 
 An example program, examples/tcp/tcp-bbr-example.cc, is provided to experiment
 with BBR for one long running flow. This example uses a simple topology
@@ -1143,9 +1136,7 @@
 
 Current implementation of ECN is based on RFC 3168 and is referred as Classic ECN.
 
-The following enum represents the mode of ECN:
-
-::
+The following enum represents the mode of ECN::
 
   typedef enum
     {
@@ -1153,9 +1144,7 @@
       DctcpEcn,    //!< ECN functionality as described in RFC 8257. Note: this mode is specific to DCTCP.
     } EcnMode_t;
 
-The following are some important ECN parameters:
-
-::
+The following are some important ECN parameters::
 
   // ECN parameters
   EcnMode_t              m_ecnMode {ClassicEcn}; //!< ECN mode
@@ -1178,11 +1167,9 @@
       AcceptOnly = 2,   //!< Enable only when the peer endpoint is ECN capable
     } UseEcn_t;
 
-For example:
+For example::
 
-::
-
-  Config::SetDefault ("ns3::TcpSocketBase::UseEcn", StringValue ("On"))
+  Config::SetDefault("ns3::TcpSocketBase::UseEcn", StringValue("On"))
 
 ECN negotiation
 ^^^^^^^^^^^^^^^
@@ -1195,11 +1182,11 @@
 
     if (m_useEcn == UseEcn_t::On)
       {
-        SendEmptyPacket (TcpHeader::SYN | TcpHeader::ECE | TcpHeader::CWR);
+        SendEmptyPacket(TcpHeader::SYN | TcpHeader::ECE | TcpHeader::CWR);
       }
     else
       {
-        SendEmptyPacket (TcpHeader::SYN);
+        SendEmptyPacket(TcpHeader::SYN);
       }
     m_ecnState = ECN_DISABLED;
 
@@ -1207,14 +1194,14 @@
 
 ::
 
-    if (m_useEcn != UseEcn_t::Off && (tcpHeader.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE))
+    if (m_useEcn != UseEcn_t::Off &&(tcpHeader.GetFlags() &(TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE))
       {
-        SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK |TcpHeader::ECE);
+        SendEmptyPacket(TcpHeader::SYN | TcpHeader::ACK |TcpHeader::ECE);
         m_ecnState = ECN_IDLE;
       }
     else
       {
-        SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK);
+        SendEmptyPacket(TcpHeader::SYN | TcpHeader::ACK);
         m_ecnState = ECN_DISABLED;
       }
 
@@ -1222,7 +1209,7 @@
 
 ::
 
-    if (m_useEcn != UseEcn_t::Off &&  (tcpHeader.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::ECE))
+    if (m_useEcn != UseEcn_t::Off && (tcpHeader.GetFlags() &(TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::ECE))
       {
         m_ecnState = ECN_IDLE;
       }
@@ -1550,14 +1537,14 @@
 the class TcpCongestionOps. It mimics the structure tcp_congestion_ops of
 Linux, and the following operations are defined:
 
-.. code-block:: c++
+::
 
-  virtual std::string GetName () const;
-  virtual uint32_t GetSsThresh (Ptr<const TcpSocketState> tcb, uint32_t bytesInFlight);
-  virtual void IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked);
-  virtual void PktsAcked (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked,const Time& rtt);
-  virtual Ptr<TcpCongestionOps> Fork ();
-  virtual void CwndEvent (Ptr<TcpSocketState> tcb, const TcpSocketState::TcpCaEvent_t event);
+  virtual std::string GetName() const;
+  virtual uint32_t GetSsThresh(Ptr<const TcpSocketState> tcb, uint32_t bytesInFlight);
+  virtual void IncreaseWindow(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked);
+  virtual void PktsAcked(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked,const Time& rtt);
+  virtual Ptr<TcpCongestionOps> Fork();
+  virtual void CwndEvent(Ptr<TcpSocketState> tcb, const TcpSocketState::TcpCaEvent_t event);
 
 The most interesting methods to write are GetSsThresh and IncreaseWindow.
 The latter is called when TcpSocketBase decides that it is time to increase
@@ -1652,7 +1639,7 @@
 
 .. math::  limit = MAX(prrDelivered - prrOut, DeliveredData) + MSS
 
-where ``DeliveredData`` represets the total number of bytes delivered to the
+where ``DeliveredData`` represents the total number of bytes delivered to the
 receiver as indicated by the current ACK and ``MSS`` is the maximum segment size.
 
 After limit calculation, the cWnd is updated as given below:
@@ -1674,24 +1661,24 @@
 All operations that are delegated to a loss recovery are contained in
 the class TcpRecoveryOps and are given below:
 
-.. code-block:: c++
+::
 
-  virtual std::string GetName () const;
-  virtual void EnterRecovery (Ptr<const TcpSocketState> tcb, uint32_t unAckDataCount,
-                              bool isSackEnabled, uint32_t dupAckCount,
-                              uint32_t bytesInFlight, uint32_t lastDeliveredBytes);
-  virtual void DoRecovery (Ptr<const TcpSocketState> tcb, uint32_t unAckDataCount,
-                           bool isSackEnabled, uint32_t dupAckCount,
-                           uint32_t bytesInFlight, uint32_t lastDeliveredBytes);
-  virtual void ExitRecovery (Ptr<TcpSocketState> tcb, uint32_t bytesInFlight);
-  virtual void UpdateBytesSent (uint32_t bytesSent);
-  virtual Ptr<TcpRecoveryOps> Fork ();
+  virtual std::string GetName() const;
+  virtual void EnterRecovery(Ptr<const TcpSocketState> tcb, uint32_t unAckDataCount,
+                             bool isSackEnabled, uint32_t dupAckCount,
+                             uint32_t bytesInFlight, uint32_t lastDeliveredBytes);
+  virtual void DoRecovery(Ptr<const TcpSocketState> tcb, uint32_t unAckDataCount,
+                          bool isSackEnabled, uint32_t dupAckCount,
+                          uint32_t bytesInFlight, uint32_t lastDeliveredBytes);
+  virtual void ExitRecovery(Ptr<TcpSocketState> tcb, uint32_t bytesInFlight);
+  virtual void UpdateBytesSent(uint32_t bytesSent);
+  virtual Ptr<TcpRecoveryOps> Fork();
 
 EnterRecovery is called when packet loss is detected and recovery is triggered.
 While in recovery phase, each time when an ACK arrives, DoRecovery is called which
 performs the necessary congestion window changes as per the recovery algorithm.
 ExitRecovery is called just prior to exiting recovery phase in order to perform the
-required congestion window ajustments. UpdateBytesSent is used to keep track of
+required congestion window adjustments. UpdateBytesSent is used to keep track of
 bytes sent and is called whenever a data packet is sent during recovery phase.
 
 Delivery Rate Estimation
@@ -1780,10 +1767,10 @@
 
 The code is the following:
 
-.. code-block:: c++
+::
 
-   TcpZeroWindowTest::TcpZeroWindowTest (const std::string &desc)
-      : TcpGeneralTest (desc)
+   TcpZeroWindowTest::TcpZeroWindowTest(const std::string &desc)
+      : TcpGeneralTest(desc)
    {
    }
 
@@ -1809,16 +1796,16 @@
 set before the object creation, such as propagation delay) one next implements
 the method ConfigureEnvironment:
 
-.. code-block:: c++
+::
 
    void
-   TcpZeroWindowTest::ConfigureEnvironment ()
+   TcpZeroWindowTest::ConfigureEnvironment()
    {
-     TcpGeneralTest::ConfigureEnvironment ();
-     SetAppPktCount (20);
-     SetMTU (500);
-     SetTransmitStart (Seconds (2.0));
-     SetPropagationDelay (MilliSeconds (50));
+     TcpGeneralTest::ConfigureEnvironment();
+     SetAppPktCount(20);
+     SetMTU(500);
+     SetTransmitStart(Seconds(2.0));
+     SetPropagationDelay(MilliSeconds(50));
    }
 
 For other properties, set after the object creation, one can use
@@ -1829,13 +1816,13 @@
 are meant to be called inside ConfigureProperties (). Please see the Doxygen
 documentation for an exhaustive list of the tunable properties.
 
-.. code-block:: c++
+::
 
    void
-   TcpZeroWindowTest::ConfigureProperties ()
+   TcpZeroWindowTest::ConfigureProperties()
    {
-     TcpGeneralTest::ConfigureProperties ();
-     SetInitialCwnd (SENDER, 10);
+     TcpGeneralTest::ConfigureProperties();
+     SetInitialCwnd(SENDER, 10);
    }
 
 To see the default value for the experiment, please see the implementation of
@@ -1853,18 +1840,18 @@
 CreateReceiverSocket, setting an Rx buffer value of 0 bytes (at line 6 of the
 following code):
 
-.. code-block:: c++
+.. code-block::
    :linenos:
    :emphasize-lines: 6,7,8
 
    Ptr<TcpSocketMsgBase>
-   TcpZeroWindowTest::CreateReceiverSocket (Ptr<Node> node)
+   TcpZeroWindowTest::CreateReceiverSocket(Ptr<Node> node)
    {
-     Ptr<TcpSocketMsgBase> socket = TcpGeneralTest::CreateReceiverSocket (node);
+     Ptr<TcpSocketMsgBase> socket = TcpGeneralTest::CreateReceiverSocket(node);
 
-     socket->SetAttribute("RcvBufSize", UintegerValue (0));
-     Simulator::Schedule (Seconds (10.0),
-                          &TcpZeroWindowTest::IncreaseBufSize, this);
+     socket->SetAttribute("RcvBufSize", UintegerValue(0));
+     Simulator::Schedule(Seconds(10.0),
+                         &TcpZeroWindowTest::IncreaseBufSize, this);
 
      return socket;
    }
@@ -1873,12 +1860,12 @@
 of the buffer size. We do this at line 7 and 8, scheduling the function
 IncreaseBufSize.
 
-.. code-block:: c++
+::
 
    void
-   TcpZeroWindowTest::IncreaseBufSize ()
+   TcpZeroWindowTest::IncreaseBufSize()
    {
-     SetRcvBufSize (RECEIVER, 2500);
+     SetRcvBufSize(RECEIVER, 2500);
    }
 
 Which utilizes the SetRcvBufSize method to edit the RxBuffer object of the
@@ -1894,22 +1881,22 @@
    of such method is SetRcvBufSize, which allows TcpGeneralSocket subclasses
    to forcefully set the RxBuffer size.
 
-   .. code-block:: c++
+   ::
 
       void
-      TcpGeneralTest::SetRcvBufSize (SocketWho who, uint32_t size)
+      TcpGeneralTest::SetRcvBufSize(SocketWho who, uint32_t size)
       {
         if (who == SENDER)
           {
-            m_senderSocket->SetRcvBufSize (size);
+            m_senderSocket->SetRcvBufSize(size);
           }
         else if (who == RECEIVER)
           {
-            m_receiverSocket->SetRcvBufSize (size);
+            m_receiverSocket->SetRcvBufSize(size);
           }
         else
           {
-            NS_FATAL_ERROR ("Not defined");
+            NS_FATAL_ERROR("Not defined");
           }
       }
 
@@ -1930,7 +1917,7 @@
 that the behavior will stay consistent through releases). We start by ensuring that
 the first SYN-ACK has 0 as advertised window size:
 
-.. code-block:: c++
+::
 
    void
    TcpZeroWindowTest::Tx(const Ptr<const Packet> p, const TcpHeader &h, SocketWho who)
@@ -1938,18 +1925,18 @@
      ...
      else if (who == RECEIVER)
        {
-         NS_LOG_INFO ("\tRECEIVER TX " << h << " size " << p->GetSize());
+         NS_LOG_INFO("\tRECEIVER TX " << h << " size " << p->GetSize());
 
-         if (h.GetFlags () & TcpHeader::SYN)
+         if (h.GetFlags() & TcpHeader::SYN)
            {
-             NS_TEST_ASSERT_MSG_EQ (h.GetWindowSize(), 0,
-                                    "RECEIVER window size is not 0 in the SYN-ACK");
+             NS_TEST_ASSERT_MSG_EQ(h.GetWindowSize(), 0,
+                                   "RECEIVER window size is not 0 in the SYN-ACK");
            }
        }
        ....
     }
 
-Pratically, we are checking that every SYN packet sent by the RECEIVER has the
+Practically, we are checking that every SYN packet sent by the RECEIVER has the
 advertised window set to 0. The same thing is done also by checking, in the Rx
 method, that each SYN received by SENDER has the advertised window set to 0.
 Thanks to the log subsystem, we can print what is happening through messages.
@@ -1978,19 +1965,19 @@
 following, we show how to check if the persistent event is running after the
 processing of the SYN-ACK:
 
-.. code-block:: c++
+::
 
    void
-   TcpZeroWindowTest::ProcessedAck (const Ptr<const TcpSocketState> tcb,
-                                    const TcpHeader& h, SocketWho who)
+   TcpZeroWindowTest::ProcessedAck(const Ptr<const TcpSocketState> tcb,
+                                   const TcpHeader& h, SocketWho who)
    {
      if (who == SENDER)
        {
-         if (h.GetFlags () & TcpHeader::SYN)
+         if (h.GetFlags() & TcpHeader::SYN)
            {
-             EventId persistentEvent = GetPersistentEvent (SENDER);
-             NS_TEST_ASSERT_MSG_EQ (persistentEvent.IsRunning (), true,
-                                    "Persistent event not started");
+             EventId persistentEvent = GetPersistentEvent(SENDER);
+             NS_TEST_ASSERT_MSG_EQ(persistentEvent.IsRunning(), true,
+                                   "Persistent event not started");
            }
        }
     }
@@ -2000,26 +1987,26 @@
 the SENDER should send a window probe, and the receiver should reply reporting
 again a zero window situation. At first, we investigates on what the sender sends:
 
-..  code-block:: c++
-    :linenos:
-    :emphasize-lines: 1,6,7,11
+.. code-block::
+      :linenos:
+      :emphasize-lines: 1,6,7,11
 
-      if (Simulator::Now ().GetSeconds () <= 6.0)
+      if (Simulator::Now().GetSeconds() <= 6.0)
         {
-          NS_TEST_ASSERT_MSG_EQ (p->GetSize () - h.GetSerializedSize(), 0,
-                                 "Data packet sent anyway");
+          NS_TEST_ASSERT_MSG_EQ(p->GetSize() - h.GetSerializedSize(), 0,
+                                "Data packet sent anyway");
         }
-      else if (Simulator::Now ().GetSeconds () > 6.0 &&
-               Simulator::Now ().GetSeconds () <= 7.0)
+      else if (Simulator::Now().GetSeconds() > 6.0 &&
+               Simulator::Now().GetSeconds() <= 7.0)
         {
-          NS_TEST_ASSERT_MSG_EQ (m_zeroWindowProbe, false, "Sent another probe");
+          NS_TEST_ASSERT_MSG_EQ(m_zeroWindowProbe, false, "Sent another probe");
 
           if (! m_zeroWindowProbe)
             {
-              NS_TEST_ASSERT_MSG_EQ (p->GetSize () - h.GetSerializedSize(), 1,
-                                     "Data packet sent instead of window probe");
-              NS_TEST_ASSERT_MSG_EQ (h.GetSequenceNumber(), SequenceNumber32 (1),
-                                     "Data packet sent instead of window probe");
+              NS_TEST_ASSERT_MSG_EQ(p->GetSize() - h.GetSerializedSize(), 1,
+                                    "Data packet sent instead of window probe");
+              NS_TEST_ASSERT_MSG_EQ(h.GetSequenceNumber(), SequenceNumber32(1),
+                                    "Data packet sent instead of window probe");
               m_zeroWindowProbe = true;
             }
         }
@@ -2033,17 +2020,17 @@
 to check (line 6) between 6.0 and 7.0 simulated seconds that the probe is sent.
 Only one probe is allowed, and this is the reason for the check at line 11.
 
-.. code-block:: c++
+.. code-block::
    :linenos:
    :emphasize-lines: 6,7
 
-   if (Simulator::Now ().GetSeconds () > 6.0 &&
-       Simulator::Now ().GetSeconds () <= 7.0)
+   if (Simulator::Now().GetSeconds() > 6.0 &&
+       Simulator::Now().GetSeconds() <= 7.0)
      {
-       NS_TEST_ASSERT_MSG_EQ (h.GetSequenceNumber(), SequenceNumber32 (1),
-                              "Data packet sent instead of window probe");
-       NS_TEST_ASSERT_MSG_EQ (h.GetWindowSize(), 0,
-                              "No zero window advertised by RECEIVER");
+       NS_TEST_ASSERT_MSG_EQ(h.GetSequenceNumber(), SequenceNumber32(1),
+                             "Data packet sent instead of window probe");
+       NS_TEST_ASSERT_MSG_EQ(h.GetWindowSize(), 0,
+                             "No zero window advertised by RECEIVER");
      }
 
 For the RECEIVER, the interval between 6 and 7 seconds is when the zero-window
@@ -2052,47 +2039,47 @@
 Other checks are redundant; the safest approach is to deny any other packet
 exchange between the 7 and 10 seconds mark.
 
-.. code-block:: c++
+::
 
-   else if (Simulator::Now ().GetSeconds () > 7.0 &&
-            Simulator::Now ().GetSeconds () < 10.0)
+   else if (Simulator::Now().GetSeconds() > 7.0 &&
+            Simulator::Now().GetSeconds() < 10.0)
      {
-       NS_FATAL_ERROR ("No packets should be sent before the window update");
+       NS_FATAL_ERROR("No packets should be sent before the window update");
      }
 
 The state checks are performed at the end of the methods, since they are valid
 in every condition:
 
-.. code-block:: c++
+::
 
-   NS_TEST_ASSERT_MSG_EQ (GetCongStateFrom (GetTcb(SENDER)), TcpSocketState::CA_OPEN,
-                          "Sender State is not OPEN");
-   NS_TEST_ASSERT_MSG_EQ (GetCongStateFrom (GetTcb(RECEIVER)), TcpSocketState::CA_OPEN,
-                          "Receiver State is not OPEN");
+   NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(GetTcb(SENDER)), TcpSocketState::CA_OPEN,
+                         "Sender State is not OPEN");
+   NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(GetTcb(RECEIVER)), TcpSocketState::CA_OPEN,
+                         "Receiver State is not OPEN");
 
 Now, the interesting part in the Tx method is to check that after the 10.0
 seconds mark (when the RECEIVER sends the active window update) the value of
 the window should be greater than zero (and precisely, set to 2500):
 
-.. code-block:: c++
+::
 
    else if (Simulator::Now().GetSeconds() >= 10.0)
      {
-       NS_TEST_ASSERT_MSG_EQ (h.GetWindowSize(), 2500,
-                              "Receiver window not updated");
+       NS_TEST_ASSERT_MSG_EQ(h.GetWindowSize(), 2500,
+                             "Receiver window not updated");
      }
 
 To be sure that the sender receives the window update, we can use the Rx
 method:
 
-.. code-block:: c++
+.. code-block::
    :linenos:
    :emphasize-lines: 5
 
    if (Simulator::Now().GetSeconds() >= 10.0)
      {
-       NS_TEST_ASSERT_MSG_EQ (h.GetWindowSize(), 2500,
-                              "Receiver window not updated");
+       NS_TEST_ASSERT_MSG_EQ(h.GetWindowSize(), 2500,
+                             "Receiver window not updated");
        m_windowUpdated = true;
      }
 
@@ -2103,10 +2090,10 @@
 Last but not least, we implement also the NormalClose() method, to check that
 the connection ends with a success:
 
-.. code-block:: c++
+::
 
    void
-   TcpZeroWindowTest::NormalClose (SocketWho who)
+   TcpZeroWindowTest::NormalClose(SocketWho who)
    {
      if (who == SENDER)
        {
@@ -2122,19 +2109,19 @@
 the method FinalChecks(), we check all variables, which should be true (which
 indicates that we have perfectly closed the connection).
 
-.. code-block:: c++
+::
 
    void
-   TcpZeroWindowTest::FinalChecks ()
+   TcpZeroWindowTest::FinalChecks()
    {
-     NS_TEST_ASSERT_MSG_EQ (m_zeroWindowProbe, true,
-                            "Zero window probe not sent");
-     NS_TEST_ASSERT_MSG_EQ (m_windowUpdated, true,
-                            "Window has not updated during the connection");
-     NS_TEST_ASSERT_MSG_EQ (m_senderFinished, true,
-                            "Connection not closed successfully (SENDER)");
-     NS_TEST_ASSERT_MSG_EQ (m_receiverFinished, true,
-                            "Connection not closed successfully (RECEIVER)");
+     NS_TEST_ASSERT_MSG_EQ(m_zeroWindowProbe, true,
+                           "Zero window probe not sent");
+     NS_TEST_ASSERT_MSG_EQ(m_windowUpdated, true,
+                           "Window has not updated during the connection");
+     NS_TEST_ASSERT_MSG_EQ(m_senderFinished, true,
+                           "Connection not closed successfully(SENDER)");
+     NS_TEST_ASSERT_MSG_EQ(m_receiverFinished, true,
+                           "Connection not closed successfully(RECEIVER)");
    }
 
 To run the test, the usual way is
@@ -2158,8 +2145,7 @@
 .. note::
    This code magically runs without any reported errors; however, in real cases,
    when you discover a bug you should expect the existing test to fail (this
-   could indicate a well-written test and a bad-writted model, or a bad-written
+   could indicate a well-written test and a bad-written model, or a bad-written
    test; hopefully the first situation). Correcting bugs is an iterative
    process. For instance, commits created to make this test case running without
    errors are 11633:6b74df04cf44, (others to be merged).
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/doc/udp.rst ns-3.38/src/internet/doc/udp.rst
--- ns-3.37/src/internet/doc/udp.rst	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/doc/udp.rst	2023-03-16 20:54:23.612868500 -0700
@@ -63,34 +63,34 @@
 
   // Create a packet sink on the receiver
   uint16_t port = 50000;
-  Address sinkLocalAddress(InetSocketAddress (Ipv4Address::GetAny (), port));
-  PacketSinkHelper sinkHelper ("ns3::UdpSocketFactory", sinkLocalAddress);
-  ApplicationContainer sinkApp = sinkHelper.Install (serverNode);
-  sinkApp.Start (Seconds (1.0));
-  sinkApp.Stop (Seconds (10.0));
+  Address sinkLocalAddress(InetSocketAddress(Ipv4Address::GetAny(), port));
+  PacketSinkHelper sinkHelper("ns3::UdpSocketFactory", sinkLocalAddress);
+  ApplicationContainer sinkApp = sinkHelper.Install(serverNode);
+  sinkApp.Start(Seconds(1.0));
+  sinkApp.Stop(Seconds(10.0));
 
 Similarly, the below snippet configures OnOffApplication traffic source to use
 UDP::
 
   // Create the OnOff applications to send data to the UDP receiver
-  OnOffHelper clientHelper ("ns3::UdpSocketFactory", Address ());
-  clientHelper.SetAttribute ("Remote", remoteAddress);
-  ApplicationContainer clientApps = (clientHelper.Install (clientNode);
-  clientApps.Start (Seconds (2.0));
-  clientApps.Stop (Seconds (9.0));
+  OnOffHelper clientHelper("ns3::UdpSocketFactory", Address());
+  clientHelper.SetAttribute("Remote", remoteAddress);
+  ApplicationContainer clientApps =(clientHelper.Install(clientNode);
+  clientApps.Start(Seconds(2.0));
+  clientApps.Stop(Seconds(9.0));
 
-For users who wish to have a pointer to the actual socket (so that
+For users who wish to have a pointer to the actual socket(so that
 socket operations like ``Bind()``, setting socket options, etc. can be
 done on a per-socket basis), UDP sockets can be created by using the
 ``Socket::CreateSocket()`` method as given below::
 
-  Ptr<Node> node = CreateObject<Node> ();
+  Ptr<Node> node = CreateObject<Node>();
   InternetStackHelper internet;
-  internet.Install (node);
+  internet.Install(node);
 
-  Ptr<SocketFactory> socketFactory = node->GetObject<UdpSocketFactory> ();
-  Ptr<Socket> socket = socketFactory->CreateSocket ();
-  socket->Bind (InetSocketAddress (Ipv4Address::GetAny (), 80));
+  Ptr<SocketFactory> socketFactory = node->GetObject<UdpSocketFactory>();
+  Ptr<Socket> socket = socketFactory->CreateSocket();
+  socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), 80));
 
 Once a UDP socket is created, we do not need an explicit connection setup before
 sending and receiving data. Being a connectionless protocol, all we need to do
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/examples/neighbor-cache-dynamic.cc ns-3.38/src/internet/examples/neighbor-cache-dynamic.cc
--- ns-3.37/src/internet/examples/neighbor-cache-dynamic.cc	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/examples/neighbor-cache-dynamic.cc	2023-03-16 20:54:23.612868500 -0700
@@ -116,7 +116,7 @@
   fe80::200:ff:fe00:1 dev 0 lladdr 02-06-00:00:00:00:00:01 STATIC_AUTOGENERATED
   fe80::200:ff:fe00:2 dev 0 lladdr 02-06-00:00:00:00:00:02 STATIC_AUTOGENERATED
 
-  Arp caches after remove the second address (2001:1::200:ff:fe00:1) from n1
+  Ndisc caches after remove the second address (2001:1::200:ff:fe00:1) from n1
   NDISC Cache of node 0 at time +2s
   2001:1::200:ff:fe00:2 dev 0 lladdr 02-06-00:00:00:00:00:02 STATIC_AUTOGENERATED
   2001:1::200:ff:fe00:3 dev 0 lladdr 02-06-00:00:00:00:00:03 STATIC_AUTOGENERATED
@@ -169,7 +169,7 @@
 RemoveIpv6Address(Ptr<Ipv6Interface> ipv6Interface, uint32_t index)
 {
     ipv6Interface->RemoveAddress(index);
-    std::cout << "\nArp caches after remove the second address (2001:1::200:ff:fe00:1) from n1"
+    std::cout << "\nNdisc caches after remove the second address (2001:1::200:ff:fe00:1) from n1"
               << std::endl;
 }
 
@@ -202,6 +202,14 @@
     csmaDevices = csma.Install(csmaNodes);
 
     InternetStackHelper stack;
+    if (!useIpv6)
+    {
+        stack.SetIpv6StackInstall(false);
+    }
+    else
+    {
+        stack.SetIpv4StackInstall(false);
+    }
     stack.Install(csmaNodes);
 
     if (!useIpv6)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/examples/neighbor-cache-example.cc ns-3.38/src/internet/examples/neighbor-cache-example.cc
--- ns-3.37/src/internet/examples/neighbor-cache-example.cc	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet/examples/neighbor-cache-example.cc	2023-03-16 20:54:23.612868500 -0700
@@ -21,7 +21,7 @@
  * This example shows how to generate neighbor caches on the scope of
  * a channel, a NetDeviceContainer, an InterfaceContainer and globally.
  * The example also shows a scenario that auto-generated neighbor caches
- * can avoid droping packets because of ARP message or Neighbor Discover
+ * can avoid dropping packets because of ARP message or Neighbor Discover
  * message.
  * If the user runs the example without any command line option, NeighborCacheHelper
  * will generate neighbor cache for all devices and the output is their
@@ -290,7 +290,7 @@
 NS_LOG_COMPONENT_DEFINE("NeighborCacheExample");
 
 /**
- * Neigbor Cache example class.
+ * neighbor Cache example class.
  *
  * It handles the creation and run of an example.
  */
@@ -409,6 +409,14 @@
     csmaDevicesRight = csmaRight.Install(csmaNodesRight);
 
     InternetStackHelper stack;
+    if (!m_useIpv6)
+    {
+        stack.SetIpv6StackInstall(false);
+    }
+    else
+    {
+        stack.SetIpv4StackInstall(false);
+    }
     // disabled Ipv4ArpJitter and Ipv6NsRsJitter to avoid the influence on packet dropped
     stack.SetIpv4ArpJitter(false);
     stack.SetIpv6NsRsJitter(false);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/helper/ipv4-routing-helper.cc ns-3.38/src/internet/helper/ipv4-routing-helper.cc
--- ns-3.37/src/internet/helper/ipv4-routing-helper.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/helper/ipv4-routing-helper.cc	2023-03-16 20:54:23.612868500 -0700
@@ -183,7 +183,7 @@
 
         *os << "ARP Cache of node ";
         std::string found = Names::FindName(node);
-        if (Names::FindName(node) != "")
+        if (!Names::FindName(node).empty())
         {
             *os << found;
         }
@@ -217,7 +217,7 @@
 
         *os << "ARP Cache of node ";
         std::string found = Names::FindName(node);
-        if (Names::FindName(node) != "")
+        if (!Names::FindName(node).empty())
         {
             *os << found;
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/helper/ipv4-routing-helper.h ns-3.38/src/internet/helper/ipv4-routing-helper.h
--- ns-3.37/src/internet/helper/ipv4-routing-helper.h	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/helper/ipv4-routing-helper.h	2023-03-16 20:54:23.612868500 -0700
@@ -284,15 +284,6 @@
                                    Time::Unit unit = Time::S);
 };
 
-/**
- * \brief Request a specified routing protocol &lt;T&gt; from Ipv4RoutingProtocol protocol
- *
- * If protocol is Ipv4ListRouting, then protocol will be searched in the list,
- * otherwise a simple DynamicCast will be performed
- *
- * \param protocol Smart pointer to Ipv4RoutingProtocol object
- * \return a Smart Pointer to the requested protocol (zero if the protocol can't be found)
- */
 template <class T>
 Ptr<T>
 Ipv4RoutingHelper::GetRouting(Ptr<Ipv4RoutingProtocol> protocol)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/helper/ipv6-routing-helper.cc ns-3.38/src/internet/helper/ipv6-routing-helper.cc
--- ns-3.37/src/internet/helper/ipv6-routing-helper.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/helper/ipv6-routing-helper.cc	2023-03-16 20:54:23.612868500 -0700
@@ -185,7 +185,7 @@
 
         *os << "NDISC Cache of node ";
         std::string found = Names::FindName(node);
-        if (Names::FindName(node) != "")
+        if (!Names::FindName(node).empty())
         {
             *os << found;
         }
@@ -219,7 +219,7 @@
 
         *os << "NDISC Cache of node ";
         std::string found = Names::FindName(node);
-        if (Names::FindName(node) != "")
+        if (!Names::FindName(node).empty())
         {
             *os << found;
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/helper/ipv6-routing-helper.h ns-3.38/src/internet/helper/ipv6-routing-helper.h
--- ns-3.37/src/internet/helper/ipv6-routing-helper.h	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/helper/ipv6-routing-helper.h	2023-03-16 20:54:23.612868500 -0700
@@ -284,15 +284,6 @@
                                      Time::Unit unit = Time::S);
 };
 
-/**
- * \brief Request a specified routing protocol &lt;T&gt; from Ipv6RoutingProtocol protocol
- *
- * If protocol is Ipv6ListRouting, then protocol will be searched in the list,
- * otherwise a simple DynamicCast will be performed
- *
- * \param protocol Smart pointer to Ipv6RoutingProtocol object
- * \return a Smart Pointer to the requested protocol (zero if the protocol can't be found)
- */
 template <class T>
 Ptr<T>
 Ipv6RoutingHelper::GetRouting(Ptr<Ipv6RoutingProtocol> protocol)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/helper/neighbor-cache-helper.cc ns-3.38/src/internet/helper/neighbor-cache-helper.cc
--- ns-3.37/src/internet/helper/neighbor-cache-helper.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/helper/neighbor-cache-helper.cc	2023-03-16 20:54:23.612868500 -0700
@@ -64,16 +64,36 @@
     {
         Ptr<NetDevice> netDevice = channel->GetDevice(i);
         Ptr<Node> node = netDevice->GetNode();
-        int32_t ipv4InterfaceIndex = node->GetObject<Ipv4>()->GetInterfaceForDevice(netDevice);
-        int32_t ipv6InterfaceIndex = node->GetObject<Ipv6>()->GetInterfaceForDevice(netDevice);
+
+        int32_t ipv4InterfaceIndex = -1;
+        if (node->GetObject<Ipv4>())
+        {
+            ipv4InterfaceIndex = node->GetObject<Ipv4>()->GetInterfaceForDevice(netDevice);
+        }
+        int32_t ipv6InterfaceIndex = -1;
+        if (node->GetObject<Ipv6>())
+        {
+            ipv6InterfaceIndex = node->GetObject<Ipv6>()->GetInterfaceForDevice(netDevice);
+        }
+
         for (std::size_t j = 0; j < channel->GetNDevices(); ++j)
         {
             Ptr<NetDevice> neighborDevice = channel->GetDevice(j);
             Ptr<Node> neighborNode = neighborDevice->GetNode();
-            int32_t ipv4NeighborInterfaceIndex =
-                neighborNode->GetObject<Ipv4>()->GetInterfaceForDevice(neighborDevice);
-            int32_t ipv6NeighborInterfaceIndex =
-                neighborNode->GetObject<Ipv6>()->GetInterfaceForDevice(neighborDevice);
+
+            int32_t ipv4NeighborInterfaceIndex = -1;
+            if (neighborNode->GetObject<Ipv4>())
+            {
+                ipv4NeighborInterfaceIndex =
+                    neighborNode->GetObject<Ipv4>()->GetInterfaceForDevice(neighborDevice);
+            }
+            int32_t ipv6NeighborInterfaceIndex = -1;
+            if (neighborNode->GetObject<Ipv6>())
+            {
+                ipv6NeighborInterfaceIndex =
+                    neighborNode->GetObject<Ipv6>()->GetInterfaceForDevice(neighborDevice);
+            }
+
             if (neighborDevice != netDevice)
             {
                 if (ipv4InterfaceIndex != -1)
@@ -114,16 +134,36 @@
         Ptr<NetDevice> netDevice = c.Get(i);
         Ptr<Channel> channel = netDevice->GetChannel();
         Ptr<Node> node = netDevice->GetNode();
-        int32_t ipv4InterfaceIndex = node->GetObject<Ipv4>()->GetInterfaceForDevice(netDevice);
-        int32_t ipv6InterfaceIndex = node->GetObject<Ipv6>()->GetInterfaceForDevice(netDevice);
+
+        int32_t ipv4InterfaceIndex = -1;
+        if (node->GetObject<Ipv4>())
+        {
+            ipv4InterfaceIndex = node->GetObject<Ipv4>()->GetInterfaceForDevice(netDevice);
+        }
+        int32_t ipv6InterfaceIndex = -1;
+        if (node->GetObject<Ipv6>())
+        {
+            ipv6InterfaceIndex = node->GetObject<Ipv6>()->GetInterfaceForDevice(netDevice);
+        }
+
         for (std::size_t j = 0; j < channel->GetNDevices(); ++j)
         {
             Ptr<NetDevice> neighborDevice = channel->GetDevice(j);
             Ptr<Node> neighborNode = neighborDevice->GetNode();
-            int32_t ipv4NeighborInterfaceIndex =
-                neighborNode->GetObject<Ipv4>()->GetInterfaceForDevice(neighborDevice);
-            int32_t ipv6NeighborInterfaceIndex =
-                neighborNode->GetObject<Ipv6>()->GetInterfaceForDevice(neighborDevice);
+
+            int32_t ipv4NeighborInterfaceIndex = -1;
+            if (neighborNode->GetObject<Ipv4>())
+            {
+                ipv4NeighborInterfaceIndex =
+                    neighborNode->GetObject<Ipv4>()->GetInterfaceForDevice(neighborDevice);
+            }
+            int32_t ipv6NeighborInterfaceIndex = -1;
+            if (neighborNode->GetObject<Ipv6>())
+            {
+                ipv6NeighborInterfaceIndex =
+                    neighborNode->GetObject<Ipv6>()->GetInterfaceForDevice(neighborDevice);
+            }
+
             if (neighborDevice != netDevice)
             {
                 if (ipv4InterfaceIndex != -1)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/helper/neighbor-cache-helper.h ns-3.38/src/internet/helper/neighbor-cache-helper.h
--- ns-3.37/src/internet/helper/neighbor-cache-helper.h	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/helper/neighbor-cache-helper.h	2023-03-16 20:54:23.612868500 -0700
@@ -95,7 +95,7 @@
     void FlushAutoGenerated() const;
 
     /**
-     * \brief Enable/diable dynamic neighbor cache, auto-generated neighbor cache will update
+     * \brief Enable/disable dynamic neighbor cache, auto-generated neighbor cache will update
      * by IP addresses changed when dynamic neighbor cache enabled. User should ensure that
      * the NeighborCacheHelper object persists for the duration of the simulation when dynamic
      * neighbor cache is enabled.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/arp-cache.cc ns-3.38/src/internet/model/arp-cache.cc
--- ns-3.37/src/internet/model/arp-cache.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/arp-cache.cc	2023-03-16 20:54:23.616868500 -0700
@@ -269,7 +269,7 @@
     {
         *os << i->first << " dev ";
         std::string found = Names::FindName(m_device);
-        if (Names::FindName(m_device) != "")
+        if (!Names::FindName(m_device).empty())
         {
             *os << found;
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/global-route-manager-impl.cc ns-3.38/src/internet/model/global-route-manager-impl.cc
--- ns-3.37/src/internet/model/global-route-manager-impl.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/global-route-manager-impl.cc	2023-03-16 20:54:23.616868500 -0700
@@ -150,7 +150,7 @@
     }
 
     // delete children
-    while (m_children.size() > 0)
+    while (!m_children.empty())
     {
         // pop out children one by one. Some children may disappear
         // when deleting some other children in the list. As a result,
@@ -347,7 +347,7 @@
 
     // discard all exit direction currently associated with this vertex,
     // and copy all the exit directions from the given vertex
-    if (m_ecmpRootExits.size() > 0)
+    if (!m_ecmpRootExits.empty())
     {
         NS_LOG_WARN("x root exit directions in this vertex are going to be discarded");
     }
@@ -1137,7 +1137,7 @@
         // Above, when we were considering the root node, we calculated the next hop
         // address and outgoing interface required to get off of the root network.
         // At this point, we are further away from the root network along one of the
-        // (shortest) paths.  So the next hop and outoing interface remain the same
+        // (shortest) paths.  So the next hop and outgoing interface remain the same
         // (are inherited).
         //
         w->InheritAllRootExitDirections(v);
@@ -1702,7 +1702,7 @@
 
     NS_ASSERT_MSG(m_spfroot, "GlobalRouteManagerImpl::SPFIntraAddStub (): Root pointer not set");
 
-    // XXX simplifed logic for the moment.  There are two cases to consider:
+    // XXX simplified logic for the moment.  There are two cases to consider:
     // 1) the stub network is on this router; do nothing for now
     //    (already handled above)
     // 2) the stub network is on a remote router, so I should use the
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/global-router-interface.cc ns-3.38/src/internet/model/global-router-interface.cc
--- ns-3.37/src/internet/model/global-router-interface.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/global-router-interface.cc	2023-03-16 20:54:23.616868500 -0700
@@ -272,7 +272,7 @@
 GlobalRoutingLSA::IsEmpty() const
 {
     NS_LOG_FUNCTION(this);
-    return m_linkRecords.size() == 0;
+    return m_linkRecords.empty();
 }
 
 GlobalRoutingLSA::LSType
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/icmpv4-l4-protocol.cc ns-3.38/src/internet/model/icmpv4-l4-protocol.cc
--- ns-3.37/src/internet/model/icmpv4-l4-protocol.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/icmpv4-l4-protocol.cc	2023-03-16 20:54:23.616868500 -0700
@@ -300,9 +300,35 @@
     p->RemoveHeader(icmp);
     switch (icmp.GetType())
     {
-    case Icmpv4Header::ICMPV4_ECHO:
-        HandleEcho(p, icmp, header.GetSource(), header.GetDestination());
+    case Icmpv4Header::ICMPV4_ECHO: {
+        Ipv4Address dst = header.GetDestination();
+        // We could have received an Echo request to a broadcast-type address.
+        if (dst.IsBroadcast())
+        {
+            Ipv4Address src = header.GetSource();
+            for (uint32_t index = 0; index < incomingInterface->GetNAddresses(); index++)
+            {
+                Ipv4InterfaceAddress addr = incomingInterface->GetAddress(index);
+                if (addr.IsInSameSubnet(src))
+                {
+                    dst = addr.GetAddress();
+                }
+            }
+        }
+        else
+        {
+            for (uint32_t index = 0; index < incomingInterface->GetNAddresses(); index++)
+            {
+                Ipv4InterfaceAddress addr = incomingInterface->GetAddress(index);
+                if (dst == addr.GetBroadcast())
+                {
+                    dst = addr.GetAddress();
+                }
+            }
+        }
+        HandleEcho(p, icmp, header.GetSource(), dst);
         break;
+    }
     case Icmpv4Header::ICMPV4_DEST_UNREACH:
         HandleDestUnreach(p, icmp, header.GetSource(), header.GetDestination());
         break;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/icmpv6-header.cc ns-3.38/src/internet/model/icmpv6-header.cc
--- ns-3.37/src/internet/model/icmpv6-header.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/icmpv6-header.cc	2023-03-16 20:54:23.616868500 -0700
@@ -657,19 +657,6 @@
     m_flagH = h;
 }
 
-uint8_t
-Icmpv6RA::GetFlags() const
-{
-    NS_LOG_FUNCTION(this);
-    return 0;
-}
-
-void
-Icmpv6RA::SetFlags(uint8_t f)
-{
-    NS_LOG_FUNCTION(this << static_cast<uint32_t>(f));
-}
-
 void
 Icmpv6RA::Print(std::ostream& os) const
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/icmpv6-header.h ns-3.38/src/internet/model/icmpv6-header.h
--- ns-3.37/src/internet/model/icmpv6-header.h	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/icmpv6-header.h	2023-03-16 20:54:23.616868500 -0700
@@ -683,20 +683,6 @@
     void Print(std::ostream& os) const override;
 
     /**
-     * \brief Getflags.
-     * \return the flags value
-     */
-    NS_DEPRECATED_3_34
-    uint8_t GetFlags() const;
-
-    /**
-     * \brief Setflags.
-     * \param f the flags value
-     */
-    NS_DEPRECATED_3_34
-    void SetFlags(uint8_t f);
-
-    /**
      * \brief Get the serialized size.
      * \return serialized size
      */
@@ -1474,13 +1460,13 @@
     /**
      * \brief Icmpv6 Option Prefix Information flag field values
      */
-    typedef enum
+    enum Flags_t
     {
         NONE = 0,         //!< No flags
         ROUTERADDR = 32,  //!< Router Address
         AUTADDRCONF = 64, //!< Autonomous Address Configuration
         ONLINK = 128      //!< On-link
-    } Flags_t;
+    };
 
     /**
      * \brief Get the prefix length.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/icmpv6-l4-protocol.cc ns-3.38/src/internet/model/icmpv6-l4-protocol.cc
--- ns-3.37/src/internet/model/icmpv6-l4-protocol.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/icmpv6-l4-protocol.cc	2023-03-16 20:54:23.616868500 -0700
@@ -703,7 +703,7 @@
             }
             break;
         default:
-            /* unknow option, quit */
+            /* unknown option, quit */
             next = false;
         }
         if (packet->GetSize() == 0)
@@ -1383,7 +1383,7 @@
         }
         else
         {
-            // Following RS transmission - adding further jitter is unnecesary.
+            // Following RS transmission - adding further jitter is unnecessary.
             rsTimeout = m_rsPrevRetransmissionTimeout * (2 + m_rsRetransmissionJitter->GetValue());
             if (rsTimeout > m_rsMaxRetransmissionTime)
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ip-l4-protocol.h ns-3.38/src/internet/model/ip-l4-protocol.h
--- ns-3.37/src/internet/model/ip-l4-protocol.h	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/ip-l4-protocol.h	2023-03-16 20:54:23.616868500 -0700
@@ -83,9 +83,9 @@
      * \param incomingInterface the Ipv4Interface on which the packet arrived
      * \returns Rx status code
      */
-    virtual enum RxStatus Receive(Ptr<Packet> p,
-                                  const Ipv4Header& header,
-                                  Ptr<Ipv4Interface> incomingInterface) = 0;
+    virtual RxStatus Receive(Ptr<Packet> p,
+                             const Ipv4Header& header,
+                             Ptr<Ipv4Interface> incomingInterface) = 0;
 
     /**
      * \brief Called from lower-level layers to send the packet up in the stack.
@@ -94,9 +94,9 @@
      * \param incomingInterface the Ipv6Interface on which the packet arrived
      * \returns Rx status code
      */
-    virtual enum RxStatus Receive(Ptr<Packet> p,
-                                  const Ipv6Header& header,
-                                  Ptr<Ipv6Interface> incomingInterface) = 0;
+    virtual RxStatus Receive(Ptr<Packet> p,
+                             const Ipv6Header& header,
+                             Ptr<Ipv6Interface> incomingInterface) = 0;
 
     /**
      * \brief Called from lower-level layers to send the ICMP packet up in the stack.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv4-end-point.cc ns-3.38/src/internet/model/ipv4-end-point.cc
--- ns-3.37/src/internet/model/ipv4-end-point.cc	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/ipv4-end-point.cc	2023-03-16 20:54:23.616868500 -0700
@@ -166,7 +166,7 @@
 }
 
 bool
-Ipv4EndPoint::IsRxEnabled()
+Ipv4EndPoint::IsRxEnabled() const
 {
     return m_rxEnabled;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv4-end-point.h ns-3.38/src/internet/model/ipv4-end-point.h
--- ns-3.37/src/internet/model/ipv4-end-point.h	2022-11-01 17:37:24.495395200 -0700
+++ ns-3.38/src/internet/model/ipv4-end-point.h	2023-03-16 20:54:23.616868500 -0700
@@ -187,7 +187,7 @@
      * \brief Checks if the endpoint can receive packets.
      * \returns true if the endpoint can receive packets.
      */
-    bool IsRxEnabled();
+    bool IsRxEnabled() const;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv4-global-routing.cc ns-3.38/src/internet/model/ipv4-global-routing.cc
--- ns-3.37/src/internet/model/ipv4-global-routing.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv4-global-routing.cc	2023-03-16 20:54:23.616868500 -0700
@@ -155,7 +155,7 @@
             NS_LOG_LOGIC(allRoutes.size() << "Found global host route" << *i);
         }
     }
-    if (allRoutes.size() == 0) // if no host route is found
+    if (allRoutes.empty()) // if no host route is found
     {
         NS_LOG_LOGIC("Number of m_networkRoutes" << m_networkRoutes.size());
         for (NetworkRoutesI j = m_networkRoutes.begin(); j != m_networkRoutes.end(); j++)
@@ -177,7 +177,7 @@
             }
         }
     }
-    if (allRoutes.size() == 0) // consider external if no host/network found
+    if (allRoutes.empty()) // consider external if no host/network found
     {
         for (ASExternalRoutesI k = m_ASexternalRoutes.begin(); k != m_ASexternalRoutes.end(); k++)
         {
@@ -199,7 +199,7 @@
             }
         }
     }
-    if (allRoutes.size() > 0) // if route(s) is found
+    if (!allRoutes.empty()) // if route(s) is found
     {
         // pick up one of the routes uniformly at random if random
         // ECMP routing is enabled, or always select the first route
@@ -421,7 +421,7 @@
             // Use not implemented
             *os << "-"
                 << "   ";
-            if (Names::FindName(m_ipv4->GetNetDevice(route.GetInterface())) != "")
+            if (!Names::FindName(m_ipv4->GetNetDevice(route.GetInterface())).empty())
             {
                 *os << Names::FindName(m_ipv4->GetNetDevice(route.GetInterface()));
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv4-interface.cc ns-3.38/src/internet/model/ipv4-interface.cc
--- ns-3.37/src/internet/model/ipv4-interface.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv4-interface.cc	2023-03-16 20:54:23.616868500 -0700
@@ -387,7 +387,7 @@
 {
     NS_LOG_FUNCTION(this << address);
 
-    if (address == address.GetLoopback())
+    if (address == Ipv4Address::GetLoopback())
     {
         NS_LOG_WARN("Cannot remove loopback address.");
         return Ipv4InterfaceAddress();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv4-l3-protocol.cc ns-3.38/src/internet/model/ipv4-l3-protocol.cc
--- ns-3.37/src/internet/model/ipv4-l3-protocol.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv4-l3-protocol.cc	2023-03-16 20:54:23.616868500 -0700
@@ -1395,7 +1395,7 @@
         NS_LOG_LOGIC(
             "Interface "
             << int(i)
-            << " is set to be down for IPv4. Reason: not respecting minimum IPv4 MTU (68 octects)");
+            << " is set to be down for IPv4. Reason: not respecting minimum IPv4 MTU (68 octets)");
     }
 }
 
@@ -1650,7 +1650,7 @@
 {
     NS_LOG_FUNCTION(this);
 
-    bool ret = !m_moreFragment && m_fragments.size() > 0;
+    bool ret = !m_moreFragment && !m_fragments.empty();
 
     if (ret)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv4-raw-socket-impl.cc ns-3.38/src/internet/model/ipv4-raw-socket-impl.cc
--- ns-3.37/src/internet/model/ipv4-raw-socket-impl.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv4-raw-socket-impl.cc	2023-03-16 20:54:23.620868400 -0700
@@ -275,10 +275,19 @@
         p->AddPacketTag(tag);
     }
 
+    Ptr<NetDevice> boundNetDevice = m_boundnetdevice;
+
+    if (!m_src.IsAny())
+    {
+        int32_t index = ipv4->GetInterfaceForAddress(m_src);
+        NS_ASSERT(index >= 0);
+        boundNetDevice = ipv4->GetNetDevice(index);
+    }
+
     bool subnetDirectedBroadcast = false;
-    if (m_boundnetdevice)
+    if (boundNetDevice)
     {
-        uint32_t iif = ipv4->GetInterfaceForDevice(m_boundnetdevice);
+        uint32_t iif = ipv4->GetInterfaceForDevice(boundNetDevice);
         for (uint32_t j = 0; j < ipv4->GetNAddresses(iif); j++)
         {
             Ipv4InterfaceAddress ifAddr = ipv4->GetAddress(iif, j);
@@ -291,7 +300,6 @@
 
     if (dst.IsBroadcast() || subnetDirectedBroadcast)
     {
-        Ptr<NetDevice> boundNetDevice = m_boundnetdevice;
         if (ipv4->GetNInterfaces() == 1)
         {
             boundNetDevice = ipv4->GetNetDevice(0);
@@ -312,6 +320,7 @@
             route->SetSource(src);
             route->SetDestination(dst);
             route->SetOutputDevice(boundNetDevice);
+            route->SetGateway("0.0.0.0");
             ipv4->Send(p, route->GetSource(), dst, m_protocol, route);
         }
         else
@@ -324,6 +333,7 @@
             route->SetSource(src);
             route->SetDestination(dst);
             route->SetOutputDevice(boundNetDevice);
+            route->SetGateway("0.0.0.0");
             ipv4->SendWithHeader(p, header, route);
         }
         NotifyDataSent(pktSize);
@@ -413,7 +423,7 @@
     {
         return nullptr;
     }
-    struct Data data = m_recv.front();
+    Data data = m_recv.front();
     m_recv.pop_front();
     InetSocketAddress inet = InetSocketAddress(data.fromIp, data.fromProtocol);
     fromAddress = inet;
@@ -501,7 +511,7 @@
             }
         }
         copy->AddHeader(ipHeader);
-        struct Data data;
+        Data data;
         data.packet = copy;
         data.fromIp = ipHeader.GetSource();
         data.fromProtocol = ipHeader.GetProtocol();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv4-static-routing.cc ns-3.38/src/internet/model/ipv4-static-routing.cc
--- ns-3.37/src/internet/model/ipv4-static-routing.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv4-static-routing.cc	2023-03-16 20:54:23.620868400 -0700
@@ -756,7 +756,7 @@
             // Use not implemented
             *os << "-"
                 << "   ";
-            if (Names::FindName(m_ipv4->GetNetDevice(route.GetInterface())) != "")
+            if (!Names::FindName(m_ipv4->GetNetDevice(route.GetInterface())).empty())
             {
                 *os << Names::FindName(m_ipv4->GetNetDevice(route.GetInterface()));
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-end-point.cc ns-3.38/src/internet/model/ipv6-end-point.cc
--- ns-3.37/src/internet/model/ipv6-end-point.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-end-point.cc	2023-03-16 20:54:23.620868400 -0700
@@ -61,7 +61,7 @@
 }
 
 uint16_t
-Ipv6EndPoint::GetLocalPort()
+Ipv6EndPoint::GetLocalPort() const
 {
     return m_localPort;
 }
@@ -79,7 +79,7 @@
 }
 
 uint16_t
-Ipv6EndPoint::GetPeerPort()
+Ipv6EndPoint::GetPeerPort() const
 {
     return m_peerPort;
 }
@@ -151,7 +151,7 @@
 }
 
 bool
-Ipv6EndPoint::IsRxEnabled()
+Ipv6EndPoint::IsRxEnabled() const
 {
     return m_rxEnabled;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-end-point.h ns-3.38/src/internet/model/ipv6-end-point.h
--- ns-3.37/src/internet/model/ipv6-end-point.h	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-end-point.h	2023-03-16 20:54:23.620868400 -0700
@@ -74,7 +74,7 @@
      * \brief Get the local port.
      * \return the local port
      */
-    uint16_t GetLocalPort();
+    uint16_t GetLocalPort() const;
 
     /**
      * \brief Set the local port.
@@ -92,7 +92,7 @@
      * \brief Get the peer port.
      * \return the peer port
      */
-    uint16_t GetPeerPort();
+    uint16_t GetPeerPort() const;
 
     /**
      * \brief Set the peer information (address and port).
@@ -191,7 +191,7 @@
      * \brief Checks if the endpoint can receive packets.
      * \returns true if the endpoint can receive packets.
      */
-    bool IsRxEnabled();
+    bool IsRxEnabled() const;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-extension.cc ns-3.38/src/internet/model/ipv6-extension.cc
--- ns-3.37/src/internet/model/ipv6-extension.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-extension.cc	2023-03-16 20:54:23.620868400 -0700
@@ -770,7 +770,7 @@
 bool
 Ipv6ExtensionFragment::Fragments::IsEntire() const
 {
-    bool ret = !m_moreFragment && m_packetFragments.size() > 0;
+    bool ret = !m_moreFragment && !m_packetFragments.empty();
 
     if (ret)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-extension-header.cc ns-3.38/src/internet/model/ipv6-extension-header.cc
--- ns-3.37/src/internet/model/ipv6-extension-header.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-extension-header.cc	2023-03-16 20:54:23.620868400 -0700
@@ -216,7 +216,7 @@
 }
 
 uint32_t
-OptionField::GetOptionsOffset()
+OptionField::GetOptionsOffset() const
 {
     return m_optionsOffset;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-extension-header.h ns-3.38/src/internet/model/ipv6-extension-header.h
--- ns-3.37/src/internet/model/ipv6-extension-header.h	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-extension-header.h	2023-03-16 20:54:23.620868400 -0700
@@ -186,7 +186,7 @@
      * the extension header.
      * \return the offset from the start of the extension header
      */
-    uint32_t GetOptionsOffset();
+    uint32_t GetOptionsOffset() const;
 
     /**
      * \brief Get the buffer.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-header.cc ns-3.38/src/internet/model/ipv6-header.cc
--- ns-3.37/src/internet/model/ipv6-header.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-header.cc	2023-03-16 20:54:23.620868400 -0700
@@ -108,48 +108,24 @@
     m_sourceAddress = src;
 }
 
-void
-Ipv6Header::SetSourceAddress(Ipv6Address src)
-{
-    SetSource(src);
-}
-
 Ipv6Address
 Ipv6Header::GetSource() const
 {
     return m_sourceAddress;
 }
 
-Ipv6Address
-Ipv6Header::GetSourceAddress() const
-{
-    return GetSource();
-}
-
 void
 Ipv6Header::SetDestination(Ipv6Address dst)
 {
     m_destinationAddress = dst;
 }
 
-void
-Ipv6Header::SetDestinationAddress(Ipv6Address dst)
-{
-    SetDestination(dst);
-}
-
 Ipv6Address
 Ipv6Header::GetDestination() const
 {
     return m_destinationAddress;
 }
 
-Ipv6Address
-Ipv6Header::GetDestinationAddress() const
-{
-    return GetDestination();
-}
-
 TypeId
 Ipv6Header::GetTypeId()
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-header.h ns-3.38/src/internet/model/ipv6-header.h
--- ns-3.37/src/internet/model/ipv6-header.h	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-header.h	2023-03-16 20:54:51.628957000 -0700
@@ -229,56 +229,24 @@
     void SetSource(Ipv6Address src);
 
     /**
-     * \brief Set the "Source address" field.
-     * \deprecated Use SetSource () function instead (same functionality)
-     * \param src the source address
-     */
-    NS_DEPRECATED_3_35
-    void SetSourceAddress(Ipv6Address src);
-
-    /**
      * \brief Get the "Source address" field.
      * \return the source address
      */
     Ipv6Address GetSource() const;
 
     /**
-     * \brief Get the "Source address" field.
-     * \deprecated Use GetSource () function instead (same functionality)
-     * \return the source address
-     */
-    NS_DEPRECATED_3_35
-    Ipv6Address GetSourceAddress() const;
-
-    /**
      * \brief Set the "Destination address" field.
      * \param dst the destination address
      */
     void SetDestination(Ipv6Address dst);
 
     /**
-     * \brief Set the "Destination address" field.
-     * \deprecated Use SetDestination () function instead (same functionality)
-     * \param dst the destination address
-     */
-    NS_DEPRECATED_3_35
-    void SetDestinationAddress(Ipv6Address dst);
-
-    /**
      * \brief Get the "Destination address" field.
      * \return the destination address
      */
     Ipv6Address GetDestination() const;
 
     /**
-     * \brief Get the "Destination address" field.
-     * \deprecated Use GetDestination () function instead (same functionality)
-     * \return the destination address
-     */
-    NS_DEPRECATED_3_35
-    Ipv6Address GetDestinationAddress() const;
-
-    /**
      * \brief Print some information about the packet.
      * \param os output stream
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-interface.cc ns-3.38/src/internet/model/ipv6-interface.cc
--- ns-3.37/src/internet/model/ipv6-interface.cc	2022-11-01 17:37:24.499395100 -0700
+++ ns-3.38/src/internet/model/ipv6-interface.cc	2023-03-16 20:54:23.620868400 -0700
@@ -361,7 +361,7 @@
 {
     NS_LOG_FUNCTION(this << address);
 
-    if (address == address.GetLoopback())
+    if (address == Ipv6Address::GetLoopback())
     {
         NS_LOG_WARN("Cannot remove loopback address.");
         return Ipv6InterfaceAddress();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-l3-protocol.cc ns-3.38/src/internet/model/ipv6-l3-protocol.cc
--- ns-3.37/src/internet/model/ipv6-l3-protocol.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/ipv6-l3-protocol.cc	2023-03-16 20:54:23.620868400 -0700
@@ -1292,7 +1292,7 @@
         {
             NS_LOG_LOGIC("Send to gateway " << route->GetGateway());
 
-            if (fragments.size() != 0)
+            if (!fragments.empty())
             {
                 std::ostringstream oss;
 
@@ -1323,7 +1323,7 @@
         {
             NS_LOG_LOGIC("Send to destination " << ipHeader.GetDestination());
 
-            if (fragments.size() != 0)
+            if (!fragments.empty())
             {
                 std::ostringstream oss;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-raw-socket-impl.h ns-3.38/src/internet/model/ipv6-raw-socket-impl.h
--- ns-3.37/src/internet/model/ipv6-raw-socket-impl.h	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/ipv6-raw-socket-impl.h	2023-03-16 20:54:23.620868400 -0700
@@ -170,12 +170,12 @@
     /**
      * \brief IPv6 raw data and additional information.
      */
-    typedef struct
+    struct Data
     {
         Ptr<Packet> packet;    /**< Packet data */
         Ipv6Address fromIp;    /**< Source address */
         uint16_t fromProtocol; /**< Protocol used */
-    } Data;
+    };
 
     /**
      * \brief Dispose object.
@@ -225,10 +225,10 @@
     /**
      * \brief Struct to hold the ICMPv6 filter
      */
-    typedef struct
+    struct Icmpv6Filter
     {
         uint32_t icmpv6Filt[8]; //!< ICMPv6 filter specification
-    } Icmpv6Filter;
+    };
 
     /**
      * \brief ICMPv6 filter.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ipv6-static-routing.cc ns-3.38/src/internet/model/ipv6-static-routing.cc
--- ns-3.37/src/internet/model/ipv6-static-routing.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/ipv6-static-routing.cc	2023-03-16 20:54:23.620868400 -0700
@@ -128,7 +128,7 @@
             // Use not implemented
             *os << "-"
                 << "   ";
-            if (Names::FindName(m_ipv6->GetNetDevice(route.GetInterface())) != "")
+            if (!Names::FindName(m_ipv6->GetNetDevice(route.GetInterface())).empty())
             {
                 *os << Names::FindName(m_ipv6->GetNetDevice(route.GetInterface()));
             }
@@ -458,7 +458,7 @@
                 else
                 {
                     rtentry->SetSource(
-                        m_ipv6->SourceAddressSelection(interfaceIdx, route->GetGateway()));
+                        m_ipv6->SourceAddressSelection(interfaceIdx, route->GetDest()));
                 }
 
                 rtentry->SetDestination(route->GetDest());
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ndisc-cache.cc ns-3.38/src/internet/model/ndisc-cache.cc
--- ns-3.37/src/internet/model/ndisc-cache.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/ndisc-cache.cc	2023-03-16 20:54:23.620868400 -0700
@@ -196,7 +196,7 @@
     {
         *os << i->first << " dev ";
         std::string found = Names::FindName(m_device);
-        if (Names::FindName(m_device) != "")
+        if (!Names::FindName(m_device).empty())
         {
             *os << found;
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/pending-data.cc ns-3.38/src/internet/model/pending-data.cc
--- ns-3.37/src/internet/model/pending-data.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/pending-data.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,270 +0,0 @@
-//
-// Copyright (c) 2006 Georgia Tech Research Corporation
-//
-// 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: Rajib Bhattacharjea<raj.b@gatech.edu>
-//
-
-// This is a port of Data PDU Headers from:
-// Georgia Tech Network Simulator
-// George F. Riley.  Georgia Tech, Spring 2002
-
-#include "pending-data.h"
-
-#include "ns3/fatal-error.h"
-#include "ns3/log.h"
-
-#include <algorithm>
-#include <cstring>
-#include <iostream>
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("PendingData");
-
-PendingData::PendingData()
-    : size(0),
-      data(0),
-      msgSize(0),
-      responseSize(0)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-PendingData::PendingData(uint32_t s, uint8_t* d, uint32_t msg, uint32_t resp)
-    : size(s),
-      data(0),
-      msgSize(msg),
-      responseSize(resp)
-{
-    NS_LOG_FUNCTION(this << s);
-    if (d)
-    {
-        data.push_back(Create<Packet>(d, size));
-    }
-}
-
-PendingData::PendingData(const std::string& s)
-    : size(s.length() + 1),
-      data(0),
-      msgSize(0),
-      responseSize(0)
-{
-    NS_LOG_FUNCTION(this << s.length() + 1);
-    data.push_back(Create<Packet>((uint8_t*)s.c_str(), size));
-}
-
-PendingData::PendingData(const PendingData& c)
-    : size(c.Size()),
-      data(c.data),
-      msgSize(c.msgSize),
-      responseSize(c.responseSize)
-{
-    NS_LOG_FUNCTION(this << c.Size());
-}
-
-PendingData::~PendingData()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-PendingData*
-PendingData::Copy() const
-{
-    NS_LOG_FUNCTION(this);
-    return new PendingData(*this);
-};
-
-PendingData*
-PendingData::CopyS(uint32_t s)
-{ // Copy, but with new size (assumes no associated data);
-    NS_LOG_FUNCTION(this << s);
-    return new PendingData(s, nullptr, msgSize, responseSize);
-}
-
-PendingData*
-PendingData::CopySD(uint32_t s, uint8_t* d)
-{ // Copy, but with new size (assumes no associated data);
-    NS_LOG_FUNCTION(this << s);
-    return new PendingData(s, d, msgSize, responseSize);
-}
-
-void
-PendingData::Clear()
-{ // Remove all pending data
-    NS_LOG_FUNCTION(this);
-    data.clear();
-    size = 0;
-}
-
-void
-PendingData::Add(uint32_t s, const uint8_t* d)
-{
-    NS_LOG_FUNCTION(this << s);
-    data.push_back(Create<Packet>(d, s));
-    size += s;
-}
-
-void
-PendingData::Add(Ptr<Packet> p)
-{
-    NS_LOG_FUNCTION(this);
-    data.push_back(p);
-    size += p->GetSize();
-}
-
-uint32_t
-PendingData::SizeFromSeq(const SequenceNumber32& seqFront, const SequenceNumber32& seqOffset)
-{
-    NS_LOG_FUNCTION(this << seqFront << seqOffset);
-    uint32_t o1 = OffsetFromSeq(seqFront, seqOffset); // Offset to start of unused data
-    return SizeFromOffset(o1);                        // Amount of data after offset
-}
-
-uint32_t
-PendingData::SizeFromOffset(uint32_t offset)
-{ // Find out how much data is available from offset
-    NS_LOG_FUNCTION(this << offset);
-    /// \todo should this return zero, or error out?
-    if (offset > size)
-    {
-        return 0; // No data at requested offset
-    }
-    return size - offset; // Available data after offset
-}
-
-uint32_t
-PendingData::OffsetFromSeq(const SequenceNumber32& seqFront, const SequenceNumber32& seqOffset)
-{ // f is the first sequence number in this data, o is offset sequence
-    NS_LOG_FUNCTION(this << seqFront << seqOffset);
-    if (seqOffset < seqFront)
-    {
-        return 0; // HuH?  Shouldn't happen
-    }
-    return seqOffset - seqFront;
-}
-
-Ptr<Packet>
-PendingData::CopyFromOffset(uint32_t s, uint32_t o)
-{ // Make a copy of data from starting position "o" for "s" bytes
-    // Return NULL if results in zero length data
-    NS_LOG_FUNCTION(this << s << o);
-    uint32_t s1 = std::min(s, SizeFromOffset(o)); // Insure not beyond end of data
-    if (s1 == 0)
-    {
-        return Create<Packet>(); // No data requested
-    }
-    if (data.size() != 0)
-    { // Actual data exists, make copy and return it
-        uint32_t count = 0;
-        std::vector<Ptr<Packet>>::size_type begin = 0;
-        bool beginFound = false;
-        std::vector<Ptr<Packet>>::size_type end = 0;
-        Ptr<Packet> outPacket;
-        Ptr<Packet> endFragment;
-        for (std::vector<Ptr<Packet>>::size_type i = 0; i < data.size(); ++i)
-        {
-            count += data[i]->GetSize();
-            if (!beginFound)
-            {
-                if (count > o)
-                {
-                    if (count >= o + s1) // then just copy within this packet
-                    {
-                        Ptr<Packet> toFragment = data[i];
-                        uint32_t packetStart = count - toFragment->GetSize();
-                        uint32_t packetOffset = o - packetStart;
-                        outPacket = toFragment->CreateFragment(packetOffset, s1);
-                        return outPacket;
-                    }
-                    begin = i;
-                    beginFound = true;
-                    Ptr<Packet> toFragment = data[begin];
-                    uint32_t packetStart = count - toFragment->GetSize();
-                    uint32_t packetOffset = o - packetStart;
-                    uint32_t fragmentLength = count - o;
-                    outPacket = toFragment->CreateFragment(packetOffset, fragmentLength);
-                }
-            }
-            else
-            {
-                if (count >= o + s1)
-                {
-                    end = i;
-                    Ptr<Packet> toFragment = data[end];
-                    uint32_t packetStart = count - toFragment->GetSize();
-                    uint32_t fragmentLength = o + s1 - packetStart;
-                    endFragment = toFragment->CreateFragment(0, fragmentLength);
-                    break;
-                }
-            }
-        }
-        for (std::vector<Ptr<Packet>>::size_type i = begin + 1; i < end; ++i)
-        {
-            outPacket->AddAtEnd(data[i]);
-        }
-        if (endFragment)
-        {
-            outPacket->AddAtEnd(endFragment);
-        }
-        NS_ASSERT(outPacket->GetSize() == s1);
-        return outPacket;
-    }
-    else
-    { // No actual data, just return dummy-data packet of correct size
-        return Create<Packet>(s1);
-    }
-}
-
-Ptr<Packet>
-PendingData::CopyFromSeq(uint32_t s, const SequenceNumber32& f, const SequenceNumber32& o)
-{
-    NS_LOG_FUNCTION(this << s << f << o);
-    return CopyFromOffset(s, OffsetFromSeq(f, o));
-}
-
-uint32_t
-PendingData::RemoveToSeq(const SequenceNumber32& seqFront, const SequenceNumber32& seqOffset)
-{
-    NS_LOG_FUNCTION(this << seqFront << seqOffset);
-    uint32_t count = OffsetFromSeq(seqFront, seqOffset);
-    NS_ASSERT_MSG(count <= size, "Trying to remove more data than in the buffer");
-    if (count == size)
-    {
-        Clear();
-        return size;
-    }
-    // Remove whole packets, if possible, from the front of the data
-    // Do not perform buffer manipulations within packet; if a whole packet
-    // cannot be removed, leave it alone
-    std::vector<Ptr<Packet>>::iterator endI = data.begin();
-    uint32_t current = 0;
-    // Any packet whose data has been completely acked can be removed
-    for (std::vector<Ptr<Packet>>::iterator dataI = data.begin(); dataI < data.end(); dataI++)
-    {
-        if (current + (*dataI)->GetSize() > count)
-        {
-            break;
-        }
-        current += (*dataI)->GetSize();
-        ++endI;
-    }
-    data.erase(data.begin(), endI);
-    size -= current;
-    return current;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/pending-data.h ns-3.38/src/internet/model/pending-data.h
--- ns-3.37/src/internet/model/pending-data.h	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/pending-data.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,194 +0,0 @@
-//
-// Copyright (c) 2006 Georgia Tech Research Corporation
-//
-// 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: Rajib Bhattacharjea<raj.b@gatech.edu>
-//
-
-// Georgia Tech Network Simulator - Data Descriptors
-// George F. Riley.  Georgia Tech, Spring 2002
-
-#ifndef PENDING_DATA_H
-#define PENDING_DATA_H
-
-#include "pending-data.h"
-
-#include "ns3/packet.h"
-#include "ns3/ptr.h"
-#include "ns3/sequence-number.h"
-
-namespace ns3
-{
-class Packet;
-
-/**
- * \ingroup tcp
- *
- * \brief class for managing I/O between applications and TCP
- */
-class PendingData
-{
-  public:
-    PendingData();
-    /**
-     * Constructor
-     * \param s size
-     * \param d data
-     * \param msg message size
-     * \param resp response size
-     */
-    PendingData(uint32_t s, uint8_t* d = nullptr, uint32_t msg = 0, uint32_t resp = 0);
-    /**
-     * Constructor from string
-     * \param s string
-     */
-    PendingData(const std::string& s); // Construct from string
-
-    /**
-     * Copy constructor
-     * \param o object to copy
-     */
-    PendingData(const PendingData& o); // Copy constructor
-    virtual ~PendingData();            // Destructor
-
-    /**
-     * Returns the size of the pending data
-     * \returns size of pending data
-     */
-    uint32_t Size() const
-    {
-        return size;
-    }
-
-    /**
-     * \brief Remove all associated data
-     */
-    virtual void Clear();
-
-    /**
-     * \brief Add some data to end
-     * \param s the data size.
-     * \param d the data to store.
-     */
-    virtual void Add(uint32_t s, const uint8_t* d = nullptr); //
-    /**
-     * \brief Add some data to end
-     * \param p packet containing the data.
-     */
-    virtual void Add(Ptr<Packet> p);
-    /**
-     * This method returns the number of bytes in the PendingData buffer
-     * beyond the sequence number specified by seqOffset.
-     *
-     * The variables seqFront and seqOffset correspond to a sequence number
-     * space in use by the user.  What is significant in this method is the
-     * difference between them; i.e. the quantity (seqOffset - seqFront).
-     * This difference is subtracted from Size(), yielding the number of
-     * bytes beyond seqOffset, from the user perspective, in the PendingData
-     * buffer.
-     *
-     * If the first number specified is not a sequence number that corresponds
-     * to the first data byte in the PendingData buffer, the computation
-     * returned will be in error.
-     *
-     * \return number of bytes
-     * \param seqFront sequence number of assumed first byte in the PendingData
-     * \param seqOffset sequence number of offset
-     */
-    virtual uint32_t SizeFromSeq(const SequenceNumber32& seqFront,
-                                 const SequenceNumber32& seqOffset);
-    // Inquire available data from offset
-    /**
-     * \return number of bytes in the data buffer beyond the offset specified
-     * \param offset offset (from zero)
-     */
-    virtual uint32_t SizeFromOffset(uint32_t offset);
-    // Available size from sequence difference
-    /**
-     * Subtracts seqFront from seqOffset after enforcing seqFront is less
-     * than seqOffset
-     *
-     * \param seqFront sequence number to be subtracted from seqOffset
-     * \param seqOffset higher sequence number
-     * \return seqOffset-seqFront
-     */
-    virtual uint32_t OffsetFromSeq(const SequenceNumber32& seqFront,
-                                   const SequenceNumber32& seqOffset);
-
-    /**
-     * \brief Copy data starting from a give offset
-     * \param s size of data to copy
-     * \param o offset
-     * \returns a packet containing the requested data
-     */
-    virtual Ptr<Packet> CopyFromOffset(uint32_t s, uint32_t o); // Size, offset, ret packet
-    /**
-     * \brief Copy data starting from a give offset
-     * \param s size of data to copy
-     * \param f Front sequence
-     * \param o Offset sequence
-     *
-     * \see PendingData::OffsetFromSeq()
-     * \returns a packet containing the requested data
-     */
-    virtual Ptr<Packet> CopyFromSeq(uint32_t s,
-                                    const SequenceNumber32& f,
-                                    const SequenceNumber32& o);
-    /**
-     * Permits object to clear any pending data between seqFront and
-     * seqOffset - 1).  Callers should check the return value to determine
-     * whether any data was removed from the front.
-     *
-     * \param seqFront sequence number to start to try to remove from
-     * \param seqOffset first sequence number in buffer that should be retained
-     * \return number of bytes from the front that were removed from the buffer
-     */
-    virtual uint32_t RemoveToSeq(const SequenceNumber32& seqFront,
-                                 const SequenceNumber32& seqOffset);
-
-    /**
-     * \brief Create a copy of self
-     * \returns copy of pending data
-     */
-    PendingData* Copy() const;
-    /**
-     * \brief Create a copy of self with new size
-     *
-     * Assumes no associated data
-     * \param s new size
-     * \returns copy of pending data
-     */
-    PendingData* CopyS(uint32_t s); // Copy
-    /**
-     * \brief Create a copy of self with new size, new data
-     *
-     * Assumes no associated data
-     * \param s new size
-     * \param d new data
-     * \returns copy of pending data
-     */
-    PendingData* CopySD(uint32_t s, uint8_t* d);
-
-  public:
-    uint32_t size;                 //!< Number of data bytes
-    std::vector<Ptr<Packet>> data; //!< Corresponding data (may be null)
-    // The next two fields allow simulated applications to exchange some info
-    uint32_t msgSize;      //!< Total size of message
-    uint32_t responseSize; //!< Size of response requested
-};
-
-} // namespace ns3
-
-#endif /* PENDING_DATA_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/rip.cc ns-3.38/src/internet/model/rip.cc
--- ns-3.37/src/internet/model/rip.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/rip.cc	2023-03-16 20:54:23.620868400 -0700
@@ -570,7 +570,7 @@
                 // Use not implemented
                 *os << "-"
                     << "   ";
-                if (Names::FindName(m_ipv4->GetNetDevice(route->GetInterface())) != "")
+                if (!Names::FindName(m_ipv4->GetNetDevice(route->GetInterface())).empty())
                 {
                     *os << Names::FindName(m_ipv4->GetNetDevice(route->GetInterface()));
                 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/ripng.cc ns-3.38/src/internet/model/ripng.cc
--- ns-3.37/src/internet/model/ripng.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/ripng.cc	2023-03-16 20:54:23.620868400 -0700
@@ -555,7 +555,7 @@
                 // Use not implemented
                 *os << "-"
                     << "   ";
-                if (Names::FindName(m_ipv6->GetNetDevice(route->GetInterface())) != "")
+                if (!Names::FindName(m_ipv6->GetNetDevice(route->GetInterface())).empty())
                 {
                     *os << Names::FindName(m_ipv6->GetNetDevice(route->GetInterface()));
                 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-bbr.cc ns-3.38/src/internet/model/tcp-bbr.cc
--- ns-3.37/src/internet/model/tcp-bbr.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-bbr.cc	2023-03-16 20:54:23.624868600 -0700
@@ -131,10 +131,12 @@
     NS_LOG_FUNCTION(this);
 }
 
-const char* const TcpBbr::BbrModeName[BBR_PROBE_RTT + 1] = {"BBR_STARTUP",
-                                                            "BBR_DRAIN",
-                                                            "BBR_PROBE_BW",
-                                                            "BBR_PROBE_RTT"};
+const char* const TcpBbr::BbrModeName[BBR_PROBE_RTT + 1] = {
+    "BBR_STARTUP",
+    "BBR_DRAIN",
+    "BBR_PROBE_BW",
+    "BBR_PROBE_RTT",
+};
 
 void
 TcpBbr::SetStream(uint32_t stream)
@@ -409,7 +411,9 @@
 TcpBbr::HandleProbeRTT(Ptr<TcpSocketState> tcb)
 {
     NS_LOG_FUNCTION(this << tcb);
-    m_appLimited = (m_delivered + tcb->m_bytesInFlight.Get()) ?: 1;
+
+    uint32_t totalBytes = m_delivered + tcb->m_bytesInFlight.Get();
+    m_appLimited = (totalBytes > 0 ? totalBytes : 1);
 
     if (m_probeRttDoneStamp == Seconds(0) && tcb->m_bytesInFlight <= m_minPipeCwnd)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-bbr.h ns-3.38/src/internet/model/tcp-bbr.h
--- ns-3.37/src/internet/model/tcp-bbr.h	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-bbr.h	2023-03-16 20:54:23.624868600 -0700
@@ -74,13 +74,13 @@
     /**
      * \brief BBR has the following 4 modes for deciding how fast to send:
      */
-    typedef enum
+    enum BbrMode_t
     {
         BBR_STARTUP,   /**< Ramp up sending rate rapidly to fill pipe */
         BBR_DRAIN,     /**< Drain any queue created during startup */
         BBR_PROBE_BW,  /**< Discover, share bw: pace around estimated bw */
         BBR_PROBE_RTT, /**< Cut inflight to min to probe min_rtt */
-    } BbrMode_t;
+    };
 
     typedef WindowedFilter<DataRate,
                            MaxFilter<DataRate>,
@@ -217,18 +217,18 @@
     uint32_t InFlight(Ptr<TcpSocketState> tcb, double gain);
 
     /**
-     * \brief Intializes the full pipe estimator.
+     * \brief Initializes the full pipe estimator.
      */
     void InitFullPipe();
 
     /**
-     * \brief Intializes the pacing rate.
+     * \brief Initializes the pacing rate.
      * \param tcb  the socket state.
      */
     void InitPacingRate(Ptr<TcpSocketState> tcb);
 
     /**
-     * \brief Intializes the round counting related variables.
+     * \brief Initializes the round counting related variables.
      */
     void InitRoundCounting();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-congestion-ops.h ns-3.38/src/internet/model/tcp-congestion-ops.h
--- ns-3.37/src/internet/model/tcp-congestion-ops.h	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-congestion-ops.h	2023-03-16 20:54:23.624868600 -0700
@@ -88,7 +88,7 @@
      *
      * Is guaranteed that the congestion control state (\p TcpAckState_t) is
      * changed BEFORE the invocation of this method.
-     * The implementator should return the slow start threshold (and not change
+     * The implementer should return the slow start threshold (and not change
      * it directly) because, in the future, the TCP implementation may require to
      * instantly recover from a loss event (e.g. when there is a network with an high
      * reordering factor).
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-cubic.cc ns-3.38/src/internet/model/tcp-cubic.cc
--- ns-3.37/src/internet/model/tcp-cubic.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-cubic.cc	2023-03-16 20:54:23.624868600 -0700
@@ -62,10 +62,15 @@
                           MakeUintegerChecker<uint32_t>())
             .AddAttribute("HyStartDetect",
                           "Hybrid Slow Start detection mechanisms:"
-                          "1: packet train, 2: delay, 3: both",
-                          IntegerValue(3),
-                          MakeIntegerAccessor(&TcpCubic::m_hystartDetect),
-                          MakeIntegerChecker<int>(1, 3))
+                          "packet train, delay, both",
+                          EnumValue(HybridSSDetectionMode::BOTH),
+                          MakeEnumAccessor(&TcpCubic::m_hystartDetect),
+                          MakeEnumChecker(HybridSSDetectionMode::PACKET_TRAIN,
+                                          "PACKET_TRAIN",
+                                          HybridSSDetectionMode::DELAY,
+                                          "DELAY",
+                                          HybridSSDetectionMode::BOTH,
+                                          "BOTH"))
             .AddAttribute("HyStartMinSamples",
                           "Number of delay samples for detecting the increase of delay",
                           UintegerValue(8),
@@ -341,7 +346,7 @@
 {
     NS_LOG_FUNCTION(this << delay);
 
-    if (!(m_found & m_hystartDetect))
+    if (!m_found)
     {
         Time now = Simulator::Now();
 
@@ -352,7 +357,11 @@
 
             if ((now - m_roundStart) > m_delayMin)
             {
-                m_found |= PACKET_TRAIN;
+                if (m_hystartDetect == HybridSSDetectionMode::PACKET_TRAIN ||
+                    m_hystartDetect == HybridSSDetectionMode::BOTH)
+                {
+                    m_found = true;
+                }
             }
         }
 
@@ -366,18 +375,20 @@
 
             ++m_sampleCnt;
         }
-        else
+        else if (m_currRtt > m_delayMin + HystartDelayThresh(m_delayMin))
         {
-            if (m_currRtt > m_delayMin + HystartDelayThresh(m_delayMin))
+            if (m_hystartDetect == HybridSSDetectionMode::DELAY ||
+                m_hystartDetect == HybridSSDetectionMode::BOTH)
             {
-                m_found |= DELAY;
+                m_found = true;
             }
         }
+
         /*
          * Either one of two conditions are met,
          * we exit from slow start immediately.
          */
-        if (m_found & m_hystartDetect)
+        if (m_found)
         {
             NS_LOG_DEBUG("Exit from SS, immediately :-)");
             tcb->m_ssThresh = tcb->m_cWnd;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-cubic.h ns-3.38/src/internet/model/tcp-cubic.h
--- ns-3.37/src/internet/model/tcp-cubic.h	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-cubic.h	2023-03-16 20:54:23.624868600 -0700
@@ -70,6 +70,16 @@
 {
   public:
     /**
+     * \brief Values to detect the Slow Start mode of HyStart
+     */
+    enum HybridSSDetectionMode
+    {
+        PACKET_TRAIN = 1, //!< Detection by trains of packet
+        DELAY = 2,        //!< Detection by delay value
+        BOTH = 3,         //!< Detection by both
+    };
+
+    /**
      * \brief Get the type ID.
      * \return the object TypeId
      */
@@ -93,24 +103,15 @@
     Ptr<TcpCongestionOps> Fork() override;
 
   private:
-    /**
-     * \brief Values to detect the Slow Start mode of HyStart
-     */
-    enum HybridSSDetectionMode
-    {
-        PACKET_TRAIN = 0x1, //!< Detection by trains of packet
-        DELAY = 0x2         //!< Detection by delay value
-    };
-
     bool m_fastConvergence; //!< Enable or disable fast convergence algorithm
     double m_beta;          //!< Beta for cubic multiplicative increase
 
-    bool m_hystart;              //!< Enable or disable HyStart algorithm
-    int m_hystartDetect;         //!< Detect way for HyStart algorithm \see HybridSSDetectionMode
-    uint32_t m_hystartLowWindow; //!< Lower bound cWnd for hybrid slow start (segments)
-    Time m_hystartAckDelta;      //!< Spacing between ack's indicating train
-    Time m_hystartDelayMin;      //!< Minimum time for hystart algorithm
-    Time m_hystartDelayMax;      //!< Maximum time for hystart algorithm
+    bool m_hystart;                        //!< Enable or disable HyStart algorithm
+    HybridSSDetectionMode m_hystartDetect; //!< Detect way for HyStart algorithm
+    uint32_t m_hystartLowWindow;           //!< Lower bound cWnd for hybrid slow start (segments)
+    Time m_hystartAckDelta;                //!< Spacing between ack's indicating train
+    Time m_hystartDelayMin;                //!< Minimum time for hystart algorithm
+    Time m_hystartDelayMax;                //!< Maximum time for hystart algorithm
     uint8_t m_hystartMinSamples; //!< Number of delay samples for detecting the increase of delay
 
     uint32_t m_initialCwnd; //!< Initial cWnd
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-header.h ns-3.38/src/internet/model/tcp-header.h
--- ns-3.37/src/internet/model/tcp-header.h	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-header.h	2023-03-16 20:54:23.624868600 -0700
@@ -275,7 +275,7 @@
     /**
      * \brief TCP flag field values
      */
-    typedef enum
+    enum Flags_t
     {
         NONE = 0, //!< No flags
         FIN = 1,  //!< FIN
@@ -286,7 +286,7 @@
         URG = 32, //!< Urgent
         ECE = 64, //!< ECE
         CWR = 128 //!< CWR
-    } Flags_t;
+    };
 
     /**
      * \brief Get the type ID.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-ledbat.cc ns-3.38/src/internet/model/tcp-ledbat.cc
--- ns-3.37/src/internet/model/tcp-ledbat.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-ledbat.cc	2023-03-16 20:54:23.624868600 -0700
@@ -150,7 +150,7 @@
 TcpLedbat::MinCircBuf(struct OwdCircBuf& b)
 {
     NS_LOG_FUNCTION_NOARGS();
-    if (b.buffer.size() == 0)
+    if (b.buffer.empty())
     {
         return ~0U;
     }
@@ -242,7 +242,7 @@
 TcpLedbat::AddDelay(struct OwdCircBuf& cb, uint32_t owd, uint32_t maxlen)
 {
     NS_LOG_FUNCTION(this << owd << maxlen << cb.buffer.size());
-    if (cb.buffer.size() == 0)
+    if (cb.buffer.empty())
     {
         NS_LOG_LOGIC("First Value for queue");
         cb.buffer.push_back(owd);
@@ -274,7 +274,7 @@
 TcpLedbat::UpdateBaseDelay(uint32_t owd)
 {
     NS_LOG_FUNCTION(this << owd);
-    if (m_baseHistory.buffer.size() == 0)
+    if (m_baseHistory.buffer.empty())
     {
         AddDelay(m_baseHistory, owd, m_baseHistoLen);
         return;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-option.cc ns-3.38/src/internet/model/tcp-option.cc
--- ns-3.37/src/internet/model/tcp-option.cc	2022-11-01 17:37:24.503395000 -0700
+++ ns-3.38/src/internet/model/tcp-option.cc	2023-03-16 20:54:23.624868600 -0700
@@ -61,23 +61,25 @@
 Ptr<TcpOption>
 TcpOption::CreateOption(uint8_t kind)
 {
-    struct kindToTid
+    struct KindToTid
     {
         TcpOption::Kind kind;
         TypeId tid;
     };
 
     static ObjectFactory objectFactory;
-    static kindToTid toTid[] = {{TcpOption::END, TcpOptionEnd::GetTypeId()},
-                                {TcpOption::MSS, TcpOptionMSS::GetTypeId()},
-                                {TcpOption::NOP, TcpOptionNOP::GetTypeId()},
-                                {TcpOption::TS, TcpOptionTS::GetTypeId()},
-                                {TcpOption::WINSCALE, TcpOptionWinScale::GetTypeId()},
-                                {TcpOption::SACKPERMITTED, TcpOptionSackPermitted::GetTypeId()},
-                                {TcpOption::SACK, TcpOptionSack::GetTypeId()},
-                                {TcpOption::UNKNOWN, TcpOptionUnknown::GetTypeId()}};
+    static KindToTid toTid[] = {
+        {TcpOption::END, TcpOptionEnd::GetTypeId()},
+        {TcpOption::MSS, TcpOptionMSS::GetTypeId()},
+        {TcpOption::NOP, TcpOptionNOP::GetTypeId()},
+        {TcpOption::TS, TcpOptionTS::GetTypeId()},
+        {TcpOption::WINSCALE, TcpOptionWinScale::GetTypeId()},
+        {TcpOption::SACKPERMITTED, TcpOptionSackPermitted::GetTypeId()},
+        {TcpOption::SACK, TcpOptionSack::GetTypeId()},
+        {TcpOption::UNKNOWN, TcpOptionUnknown::GetTypeId()},
+    };
 
-    for (unsigned int i = 0; i < sizeof(toTid) / sizeof(kindToTid); ++i)
+    for (unsigned int i = 0; i < sizeof(toTid) / sizeof(KindToTid); ++i)
     {
         if (toTid[i].kind == kind)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-prr-recovery.h ns-3.38/src/internet/model/tcp-prr-recovery.h
--- ns-3.37/src/internet/model/tcp-prr-recovery.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-prr-recovery.h	2023-03-16 20:54:23.624868600 -0700
@@ -64,11 +64,11 @@
     /**
      * \brief Reduction Bound variant (CRB or SSRB)
      */
-    typedef enum
+    enum ReductionBound_t
     {
         CRB, /**< Conservative Reduction Bound */
         SSRB /**< Slow Start Reduction Bound */
-    } ReductionBound_t;
+    };
 
     std::string GetName() const override;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-rate-ops.cc ns-3.38/src/internet/model/tcp-rate-ops.cc
--- ns-3.37/src/internet/model/tcp-rate-ops.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-rate-ops.cc	2023-03-16 20:54:23.624868600 -0700
@@ -238,7 +238,7 @@
 }
 
 std::ostream&
-operator<<(std::ostream& os, const TcpRateLinux::TcpRateConnection& rate)
+operator<<(std::ostream& os, const TcpRateOps::TcpRateConnection& rate)
 {
     os << "m_delivered      = " << rate.m_delivered << std::endl;
     os << "m_deliveredTime  = " << rate.m_deliveredTime << std::endl;
@@ -252,7 +252,7 @@
 }
 
 std::ostream&
-operator<<(std::ostream& os, const TcpRateLinux::TcpRateSample& sample)
+operator<<(std::ostream& os, const TcpRateOps::TcpRateSample& sample)
 {
     os << "m_deliveryRate  = " << sample.m_deliveryRate << std::endl;
     os << " m_isAppLimited = " << sample.m_isAppLimited << std::endl;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-rate-ops.h ns-3.38/src/internet/model/tcp-rate-ops.h
--- ns-3.37/src/internet/model/tcp-rate-ops.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-rate-ops.h	2023-03-16 20:54:23.624868600 -0700
@@ -247,7 +247,7 @@
   private:
     // Rate sample related variables
     TcpRateConnection m_rate;   //!< Rate information
-    TcpRateSample m_rateSample; //!< Rate sample (continuosly updated)
+    TcpRateSample m_rateSample; //!< Rate sample (continuously updated)
 
     TracedCallback<const TcpRateConnection&> m_rateTrace;   //!< Rate trace
     TracedCallback<const TcpRateSample&> m_rateSampleTrace; //!< Rate Sample trace
@@ -259,7 +259,7 @@
  * \param sample the TcpRateLinux::TcpRateSample to print.
  * \returns The output stream.
  */
-std::ostream& operator<<(std::ostream& os, const TcpRateLinux::TcpRateSample& sample);
+std::ostream& operator<<(std::ostream& os, const TcpRateOps::TcpRateSample& sample);
 
 /**
  * \brief Output operator.
@@ -267,7 +267,7 @@
  * \param rate the TcpRateLinux::TcpRateConnection to print.
  * \returns The output stream.
  */
-std::ostream& operator<<(std::ostream& os, const TcpRateLinux::TcpRateConnection& rate);
+std::ostream& operator<<(std::ostream& os, const TcpRateOps::TcpRateConnection& rate);
 
 /**
  * Comparison operator
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-rx-buffer.cc ns-3.38/src/internet/model/tcp-rx-buffer.cc
--- ns-3.37/src/internet/model/tcp-rx-buffer.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-rx-buffer.cc	2023-03-16 20:54:23.624868600 -0700
@@ -117,7 +117,7 @@
     { // No data allowed beyond FIN
         return m_finSeq;
     }
-    else if (m_data.size() && m_nextRxSeq > m_data.begin()->first)
+    else if (!m_data.empty() && m_nextRxSeq > m_data.begin()->first)
     { // No data allowed beyond Rx window allowed
         return m_data.begin()->first + SequenceNumber32(m_maxBuffer);
     }
@@ -159,7 +159,7 @@
     {
         headSeq = m_nextRxSeq;
     }
-    if (m_data.size())
+    if (!m_data.empty())
     {
         SequenceNumber32 maxSeq = m_data.begin()->first + SequenceNumber32(m_maxBuffer);
         if (maxSeq < tailSeq)
@@ -388,7 +388,7 @@
     {
         return nullptr; // No contiguous block to return
     }
-    NS_ASSERT(m_data.size());              // At least we have something to extract
+    NS_ASSERT(!m_data.empty());            // At least we have something to extract
     Ptr<Packet> outPkt = Create<Packet>(); // The packet that contains all the data to return
     BufIterator i;
     while (extractSize)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-rx-buffer.h ns-3.38/src/internet/model/tcp-rx-buffer.h
--- ns-3.37/src/internet/model/tcp-rx-buffer.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-rx-buffer.h	2023-03-16 20:54:23.624868600 -0700
@@ -140,7 +140,7 @@
     /**
      * Insert a packet into the buffer and update the availBytes counter to
      * reflect the number of bytes ready to send to the application. This
-     * function handles overlap by triming the head of the inputted packet and
+     * function handles overlap by trimming the head of the inputted packet and
      * removing data from the buffer that overlaps the tail of the inputted
      * packet
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-socket-base.cc ns-3.38/src/internet/model/tcp-socket-base.cc
--- ns-3.37/src/internet/model/tcp-socket-base.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-socket-base.cc	2023-03-16 20:54:23.624868600 -0700
@@ -1150,7 +1150,7 @@
 }
 
 /* Tell if a sequence number range is out side the range that my rx buffer can
-    accpet */
+    accept */
 bool
 TcpSocketBase::OutOfRange(SequenceNumber32 head, SequenceNumber32 tail) const
 {
@@ -1708,7 +1708,7 @@
     // NOTE: We do not count the DupAcks received in CA_LOSS, because we
     // don't know if they are generated by a spurious retransmission or because
     // of a real packet loss. With SACK, it is easy to know, but we do not consider
-    // dupacks. Without SACK, there are some euristics in the RFC 6582, but
+    // dupacks. Without SACK, there are some heuristics in the RFC 6582, but
     // for now, we do not implement it, leading to ignoring the dupacks.
     if (m_tcb->m_congState == TcpSocketState::CA_LOSS)
     {
@@ -1772,7 +1772,7 @@
         //     CA_RECOVERY when sequence number is advanced significantly since
         //     the last congestion event, which could be common for
         //     bandwidth-greedy application in high speed and reliable network
-        //     (such as datacenter network) whose sending rate is constrainted by
+        //     (such as datacenter network) whose sending rate is constrained by
         //     TCP socket buffer size at receiver side.
         if ((m_dupAckCount == m_retxThresh) &&
             ((m_highRxAckMark >= m_recover) || (!m_recoverActive)))
@@ -4463,64 +4463,64 @@
 }
 
 void
-TcpSocketBase::UpdatePacingRateTrace(DataRate oldValue, DataRate newValue)
+TcpSocketBase::UpdatePacingRateTrace(DataRate oldValue, DataRate newValue) const
 {
     m_pacingRateTrace(oldValue, newValue);
 }
 
 void
-TcpSocketBase::UpdateCwnd(uint32_t oldValue, uint32_t newValue)
+TcpSocketBase::UpdateCwnd(uint32_t oldValue, uint32_t newValue) const
 {
     m_cWndTrace(oldValue, newValue);
 }
 
 void
-TcpSocketBase::UpdateCwndInfl(uint32_t oldValue, uint32_t newValue)
+TcpSocketBase::UpdateCwndInfl(uint32_t oldValue, uint32_t newValue) const
 {
     m_cWndInflTrace(oldValue, newValue);
 }
 
 void
-TcpSocketBase::UpdateSsThresh(uint32_t oldValue, uint32_t newValue)
+TcpSocketBase::UpdateSsThresh(uint32_t oldValue, uint32_t newValue) const
 {
     m_ssThTrace(oldValue, newValue);
 }
 
 void
 TcpSocketBase::UpdateCongState(TcpSocketState::TcpCongState_t oldValue,
-                               TcpSocketState::TcpCongState_t newValue)
+                               TcpSocketState::TcpCongState_t newValue) const
 {
     m_congStateTrace(oldValue, newValue);
 }
 
 void
 TcpSocketBase::UpdateEcnState(TcpSocketState::EcnState_t oldValue,
-                              TcpSocketState::EcnState_t newValue)
+                              TcpSocketState::EcnState_t newValue) const
 {
     m_ecnStateTrace(oldValue, newValue);
 }
 
 void
-TcpSocketBase::UpdateNextTxSequence(SequenceNumber32 oldValue, SequenceNumber32 newValue)
+TcpSocketBase::UpdateNextTxSequence(SequenceNumber32 oldValue, SequenceNumber32 newValue) const
 
 {
     m_nextTxSequenceTrace(oldValue, newValue);
 }
 
 void
-TcpSocketBase::UpdateHighTxMark(SequenceNumber32 oldValue, SequenceNumber32 newValue)
+TcpSocketBase::UpdateHighTxMark(SequenceNumber32 oldValue, SequenceNumber32 newValue) const
 {
     m_highTxMarkTrace(oldValue, newValue);
 }
 
 void
-TcpSocketBase::UpdateBytesInFlight(uint32_t oldValue, uint32_t newValue)
+TcpSocketBase::UpdateBytesInFlight(uint32_t oldValue, uint32_t newValue) const
 {
     m_bytesInFlightTrace(oldValue, newValue);
 }
 
 void
-TcpSocketBase::UpdateRtt(Time oldValue, Time newValue)
+TcpSocketBase::UpdateRtt(Time oldValue, Time newValue) const
 {
     m_lastRttTrace(oldValue, newValue);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-socket-base.h ns-3.38/src/internet/model/tcp-socket-base.h
--- ns-3.37/src/internet/model/tcp-socket-base.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-socket-base.h	2023-03-16 20:54:23.624868600 -0700
@@ -376,28 +376,28 @@
      * \param oldValue old pacing rate value
      * \param newValue new pacing rate value
      */
-    void UpdatePacingRateTrace(DataRate oldValue, DataRate newValue);
+    void UpdatePacingRateTrace(DataRate oldValue, DataRate newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState congestion window
      * \param oldValue old cWnd value
      * \param newValue new cWnd value
      */
-    void UpdateCwnd(uint32_t oldValue, uint32_t newValue);
+    void UpdateCwnd(uint32_t oldValue, uint32_t newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState inflated congestion window
      * \param oldValue old cWndInfl value
      * \param newValue new cWndInfl value
      */
-    void UpdateCwndInfl(uint32_t oldValue, uint32_t newValue);
+    void UpdateCwndInfl(uint32_t oldValue, uint32_t newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState slow start threshold
      * \param oldValue old ssTh value
      * \param newValue new ssTh value
      */
-    void UpdateSsThresh(uint32_t oldValue, uint32_t newValue);
+    void UpdateSsThresh(uint32_t oldValue, uint32_t newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState congestion state
@@ -405,42 +405,43 @@
      * \param newValue new congestion state value
      */
     void UpdateCongState(TcpSocketState::TcpCongState_t oldValue,
-                         TcpSocketState::TcpCongState_t newValue);
+                         TcpSocketState::TcpCongState_t newValue) const;
 
     /**
      * \brief Callback function to hook to EcnState state
      * \param oldValue old ecn state value
      * \param newValue new ecn state value
      */
-    void UpdateEcnState(TcpSocketState::EcnState_t oldValue, TcpSocketState::EcnState_t newValue);
+    void UpdateEcnState(TcpSocketState::EcnState_t oldValue,
+                        TcpSocketState::EcnState_t newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState high tx mark
      * \param oldValue old high tx mark
      * \param newValue new high tx mark
      */
-    void UpdateHighTxMark(SequenceNumber32 oldValue, SequenceNumber32 newValue);
+    void UpdateHighTxMark(SequenceNumber32 oldValue, SequenceNumber32 newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState next tx sequence
      * \param oldValue old nextTxSeq value
      * \param newValue new nextTxSeq value
      */
-    void UpdateNextTxSequence(SequenceNumber32 oldValue, SequenceNumber32 newValue);
+    void UpdateNextTxSequence(SequenceNumber32 oldValue, SequenceNumber32 newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState bytes inflight
      * \param oldValue old bytesInFlight value
      * \param newValue new bytesInFlight value
      */
-    void UpdateBytesInFlight(uint32_t oldValue, uint32_t newValue);
+    void UpdateBytesInFlight(uint32_t oldValue, uint32_t newValue) const;
 
     /**
      * \brief Callback function to hook to TcpSocketState rtt
      * \param oldValue old rtt value
      * \param newValue new rtt value
      */
-    void UpdateRtt(Time oldValue, Time newValue);
+    void UpdateRtt(Time oldValue, Time newValue) const;
 
     /**
      * \brief Install a congestion control algorithm on this socket
@@ -577,9 +578,9 @@
     void SetPaceInitialWindow(bool paceWindow);
 
     // Necessary implementations of null functions from ns3::Socket
-    enum SocketErrno GetErrno() const override;     // returns m_errno
-    enum SocketType GetSocketType() const override; // returns socket type
-    Ptr<Node> GetNode() const override;             // returns m_node
+    SocketErrno GetErrno() const override;     // returns m_errno
+    SocketType GetSocketType() const override; // returns socket type
+    Ptr<Node> GetNode() const override;        // returns m_node
     int Bind() override;  // Bind a socket by setting up endpoint in TcpL4Protocol
     int Bind6() override; // Bind a socket by setting up endpoint in TcpL4Protocol
     int Bind(const Address& address) override; // ... endpoint of specific addr or port
@@ -1323,10 +1324,7 @@
     // State-related attributes
     TracedValue<TcpStates_t> m_state{CLOSED}; //!< TCP state
 
-    mutable enum SocketErrno m_errno
-    {
-        ERROR_NOTERROR
-    }; //!< Socket error code
+    mutable SocketErrno m_errno{ERROR_NOTERROR}; //!< Socket error code
 
     bool m_closeNotified{false}; //!< Told app to close socket
     bool m_closeOnEmpty{false};  //!< Close socket upon tx buffer emptied
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-socket.cc ns-3.38/src/internet/model/tcp-socket.cc
--- ns-3.37/src/internet/model/tcp-socket.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-socket.cc	2023-03-16 20:54:23.624868600 -0700
@@ -36,17 +36,19 @@
 
 NS_OBJECT_ENSURE_REGISTERED(TcpSocket);
 
-const char* const TcpSocket::TcpStateName[TcpSocket::LAST_STATE] = {"CLOSED",
-                                                                    "LISTEN",
-                                                                    "SYN_SENT",
-                                                                    "SYN_RCVD",
-                                                                    "ESTABLISHED",
-                                                                    "CLOSE_WAIT",
-                                                                    "LAST_ACK",
-                                                                    "FIN_WAIT_1",
-                                                                    "FIN_WAIT_2",
-                                                                    "CLOSING",
-                                                                    "TIME_WAIT"};
+const char* const TcpSocket::TcpStateName[TcpSocket::LAST_STATE] = {
+    "CLOSED",
+    "LISTEN",
+    "SYN_SENT",
+    "SYN_RCVD",
+    "ESTABLISHED",
+    "CLOSE_WAIT",
+    "LAST_ACK",
+    "FIN_WAIT_1",
+    "FIN_WAIT_2",
+    "CLOSING",
+    "TIME_WAIT",
+};
 
 TypeId
 TcpSocket::GetTypeId()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-socket.h ns-3.38/src/internet/model/tcp-socket.h
--- ns-3.37/src/internet/model/tcp-socket.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-socket.h	2023-03-16 20:54:23.624868600 -0700
@@ -62,7 +62,7 @@
      * \brief Names of the 11 TCP states
      *
      */
-    typedef enum
+    enum TcpStates_t
     {
         CLOSED = 0,  /**< Socket is finished                                     */
         LISTEN,      /**< Listening for a connection                             */
@@ -87,7 +87,7 @@
                       *  may not have gotten our last ACK causing it
                       *  to retransmit the data packet (which we ignore)        */
         LAST_STATE   /**< Last state, used only in debug messages                */
-    } TcpStates_t;
+    };
 
     /**
      * \brief Literal names of TCP states for use in log messages
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-socket-state.cc ns-3.38/src/internet/model/tcp-socket-state.cc
--- ns-3.37/src/internet/model/tcp-socket-state.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-socket-state.cc	2023-03-16 20:54:23.624868600 -0700
@@ -128,13 +128,21 @@
 {
 }
 
-const char* const TcpSocketState::TcpCongStateName[TcpSocketState::CA_LAST_STATE] = {"CA_OPEN",
-                                                                                     "CA_DISORDER",
-                                                                                     "CA_CWR",
-                                                                                     "CA_RECOVERY",
-                                                                                     "CA_LOSS"};
+const char* const TcpSocketState::TcpCongStateName[TcpSocketState::CA_LAST_STATE] = {
+    "CA_OPEN",
+    "CA_DISORDER",
+    "CA_CWR",
+    "CA_RECOVERY",
+    "CA_LOSS",
+};
 
-const char* const TcpSocketState::EcnStateName[TcpSocketState::ECN_CWR_SENT + 1] =
-    {"ECN_DISABLED", "ECN_IDLE", "ECN_CE_RCVD", "ECN_SENDING_ECE", "ECN_ECE_RCVD", "ECN_CWR_SENT"};
+const char* const TcpSocketState::EcnStateName[TcpSocketState::ECN_CWR_SENT + 1] = {
+    "ECN_DISABLED",
+    "ECN_IDLE",
+    "ECN_CE_RCVD",
+    "ECN_SENDING_ECE",
+    "ECN_ECE_RCVD",
+    "ECN_CWR_SENT",
+};
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-socket-state.h ns-3.38/src/internet/model/tcp-socket-state.h
--- ns-3.37/src/internet/model/tcp-socket-state.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-socket-state.h	2023-03-16 20:54:23.624868600 -0700
@@ -77,7 +77,7 @@
      * depending on the case.
      *
      */
-    typedef enum
+    enum TcpCongState_t
     {
         CA_OPEN,     //!< Normal state, no dubious events
         CA_DISORDER, //!< In all the respects it is "Open",
@@ -88,13 +88,13 @@
         CA_RECOVERY, //!< CWND was reduced, we are fast-retransmitting.
         CA_LOSS,     //!< CWND was reduced due to RTO timeout or SACK reneging.
         CA_LAST_STATE //!< Used only in debug messages
-    } TcpCongState_t;
+    };
 
     // Note: "not triggered" events are currently not triggered by the code.
     /**
      * \brief Congestion avoidance events
      */
-    typedef enum
+    enum TcpCAEvent_t
     {
         CA_EVENT_TX_START,        //!< first transmit when no packets in flight
         CA_EVENT_CWND_RESTART,    //!< congestion window restart. Not triggered
@@ -104,7 +104,7 @@
         CA_EVENT_ECN_IS_CE,       //!< received CE marked IP packet. Not triggered
         CA_EVENT_DELAYED_ACK,     //!< Delayed ack is sent
         CA_EVENT_NON_DELAYED_ACK, //!< Non-delayed ack is sent
-    } TcpCAEvent_t;
+    };
 
     /**
      * \brief Parameter value related to ECN enable/disable functionality
@@ -112,39 +112,39 @@
      *        https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
      *        is not implemented.
      */
-    typedef enum
+    enum UseEcn_t
     {
         Off = 0,        //!< Disable
         On = 1,         //!< Enable
         AcceptOnly = 2, //!< Enable only when the peer endpoint is ECN capable
-    } UseEcn_t;
+    };
 
     /**
      * \brief ECN code points
      */
-    typedef enum
+    enum EcnCodePoint_t
     {
         NotECT = 0,  //!< Unmarkable
         Ect1 = 1,    //!< Markable
         Ect0 = 2,    //!< Markable
         CongExp = 3, //!< Marked
-    } EcnCodePoint_t;
+    };
 
     /**
      * \brief ECN Modes
      */
-    typedef enum
+    enum EcnMode_t
     {
         ClassicEcn, //!< ECN functionality as described in RFC 3168.
         DctcpEcn,   //!< ECN functionality as described in RFC 8257. Note: this mode is specific to
                     //!< DCTCP.
-    } EcnMode_t;
+    };
 
     /**
      * \brief Definition of the Ecn state machine
      *
      */
-    typedef enum
+    enum EcnState_t
     {
         ECN_DISABLED = 0, //!< ECN disabled traffic
         ECN_IDLE, //!< ECN is enabled  but currently there is no action pertaining to ECE or CWR to
@@ -154,7 +154,7 @@
         ECN_ECE_RCVD,    //!< Last ACK received had ECE bit set in TCP header
         ECN_CWR_SENT //!< Sender has reduced the congestion window, and sent a packet with CWR bit
                      //!< set in TCP header. This state is used for tracing.
-    } EcnState_t;
+    };
 
     /**
      * \brief Literal names of TCP states for use in log messages
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-tx-buffer.cc ns-3.38/src/internet/model/tcp-tx-buffer.cc
--- ns-3.37/src/internet/model/tcp-tx-buffer.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-tx-buffer.cc	2023-03-16 20:54:23.624868600 -0700
@@ -169,13 +169,13 @@
     NS_LOG_FUNCTION(this << seq);
     m_firstByteSeq = seq;
 
-    if (m_sentList.size() > 0)
+    if (!m_sentList.empty())
     {
         m_sentList.front()->m_startSeq = seq;
     }
 
     // if you change the head with data already sent, something bad will happen
-    NS_ASSERT(m_sentList.size() == 0);
+    NS_ASSERT(m_sentList.empty());
     m_highestSack = std::make_pair(m_sentList.end(), SequenceNumber32(0));
 }
 
@@ -309,7 +309,7 @@
     NS_LOG_FUNCTION(this << numBytes << seq);
     NS_ASSERT(seq >= m_firstByteSeq);
     NS_ASSERT(numBytes <= m_sentSize);
-    NS_ASSERT(m_sentList.size() >= 1);
+    NS_ASSERT(!m_sentList.empty());
 
     auto it = m_sentList.begin();
     bool listEdited = false;
@@ -1242,7 +1242,7 @@
     TcpTxItem* item;
 
     // Keep the head items; they will then marked as lost
-    while (m_sentList.size() > 0)
+    while (!m_sentList.empty())
     {
         item = m_sentList.back();
         item->m_retrans = item->m_sacked = item->m_lost = false;
@@ -1357,7 +1357,7 @@
 void
 TcpTxBuffer::MarkHeadAsLost()
 {
-    if (m_sentList.size() > 0)
+    if (!m_sentList.empty())
     {
         // If the head is sacked (reneging by the receiver the previously sent
         // information) we revert the sacked flag.
@@ -1394,7 +1394,7 @@
     }
     else
     {
-        NS_ASSERT(m_sentList.size() > 0);
+        NS_ASSERT(!m_sentList.empty());
     }
 
     m_renoSack = true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-westwood.cc ns-3.38/src/internet/model/tcp-westwood.cc
--- ns-3.37/src/internet/model/tcp-westwood.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-westwood.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,194 +0,0 @@
-/*
- * Copyright (c) 2013 ResiliNets, ITTC, University of Kansas
- *
- * 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: Siddharth Gangadhar <siddharth@ittc.ku.edu>,
- *          Truc Anh N. Nguyen <annguyen@ittc.ku.edu>,
- *          Greeshma Umapathi
- *
- * James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
- * ResiliNets Research Group  https://resilinets.org/
- * Information and Telecommunication Technology Center (ITTC)
- * and Department of Electrical Engineering and Computer Science
- * The University of Kansas Lawrence, KS USA.
- *
- * Work supported in part by NSF FIND (Future Internet Design) Program
- * under grant CNS-0626918 (Postmodern Internet Architecture),
- * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI),
- * US Department of Defense (DoD), and ITTC at The University of Kansas.
- */
-
-#include "tcp-westwood.h"
-
-#include "rtt-estimator.h"
-#include "tcp-socket-base.h"
-
-#include "ns3/log.h"
-#include "ns3/simulator.h"
-
-NS_LOG_COMPONENT_DEFINE("TcpWestwood");
-
-namespace ns3
-{
-
-NS_OBJECT_ENSURE_REGISTERED(TcpWestwood);
-
-TypeId
-TcpWestwood::GetTypeId()
-{
-    static TypeId tid =
-        TypeId("ns3::TcpWestwood")
-            .SetParent<TcpNewReno>()
-            .SetGroupName("Internet")
-            .AddConstructor<TcpWestwood>()
-            .AddAttribute("FilterType",
-                          "Use this to choose no filter or Tustin's approximation filter",
-                          EnumValue(TcpWestwood::TUSTIN),
-                          MakeEnumAccessor(&TcpWestwood::m_fType),
-                          MakeEnumChecker(TcpWestwood::NONE, "None", TcpWestwood::TUSTIN, "Tustin"))
-            .AddAttribute("ProtocolType",
-                          "Use this to let the code run as Westwood or WestwoodPlus",
-                          EnumValue(TcpWestwood::WESTWOOD),
-                          MakeEnumAccessor(&TcpWestwood::m_pType),
-                          MakeEnumChecker(TcpWestwood::WESTWOOD,
-                                          "Westwood",
-                                          TcpWestwood::WESTWOODPLUS,
-                                          "WestwoodPlus"))
-            .AddTraceSource("EstimatedBW",
-                            "The estimated bandwidth",
-                            MakeTraceSourceAccessor(&TcpWestwood::m_currentBW),
-                            "ns3::TracedValueCallback::DataRate");
-    return tid;
-}
-
-TcpWestwood::TcpWestwood()
-    : TcpNewReno(),
-      m_currentBW(0),
-      m_lastSampleBW(0),
-      m_lastBW(0),
-      m_ackedSegments(0),
-      m_IsCount(false),
-      m_lastAck(0)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-TcpWestwood::TcpWestwood(const TcpWestwood& sock)
-    : TcpNewReno(sock),
-      m_currentBW(sock.m_currentBW),
-      m_lastSampleBW(sock.m_lastSampleBW),
-      m_lastBW(sock.m_lastBW),
-      m_pType(sock.m_pType),
-      m_fType(sock.m_fType),
-      m_IsCount(sock.m_IsCount)
-{
-    NS_LOG_FUNCTION(this);
-    NS_LOG_LOGIC("Invoked the copy constructor");
-}
-
-TcpWestwood::~TcpWestwood()
-{
-}
-
-void
-TcpWestwood::PktsAcked(Ptr<TcpSocketState> tcb, uint32_t packetsAcked, const Time& rtt)
-{
-    NS_LOG_FUNCTION(this << tcb << packetsAcked << rtt);
-
-    if (rtt.IsZero())
-    {
-        NS_LOG_WARN("RTT measured is zero!");
-        return;
-    }
-
-    m_ackedSegments += packetsAcked;
-
-    if (m_pType == TcpWestwood::WESTWOOD)
-    {
-        EstimateBW(rtt, tcb);
-    }
-    else if (m_pType == TcpWestwood::WESTWOODPLUS)
-    {
-        if (!(rtt.IsZero() || m_IsCount))
-        {
-            m_IsCount = true;
-            m_bwEstimateEvent.Cancel();
-            m_bwEstimateEvent = Simulator::Schedule(rtt, &TcpWestwood::EstimateBW, this, rtt, tcb);
-        }
-    }
-}
-
-void
-TcpWestwood::EstimateBW(const Time& rtt, Ptr<TcpSocketState> tcb)
-{
-    NS_LOG_FUNCTION(this);
-
-    NS_ASSERT(!rtt.IsZero());
-
-    if (m_pType == TcpWestwood::WESTWOOD)
-    {
-        Time currentAck = Simulator::Now();
-
-        NS_ABORT_MSG_IF(currentAck == m_lastAck,
-                        "This violates a model assumption and would lead to divide-by-zero; please "
-                        "report to ns-3 maintainers if this occurs.");
-
-        m_currentBW = DataRate(m_ackedSegments * tcb->m_segmentSize * 8.0 /
-                               (currentAck - m_lastAck).GetSeconds());
-        m_lastAck = currentAck;
-    }
-    else if (m_pType == TcpWestwood::WESTWOODPLUS)
-    {
-        m_currentBW = DataRate(m_ackedSegments * tcb->m_segmentSize * 8.0 / rtt.GetSeconds());
-        m_IsCount = false;
-    }
-
-    m_ackedSegments = 0;
-
-    NS_LOG_LOGIC("Estimated BW: " << m_currentBW);
-
-    // Filter the BW sample
-
-    constexpr double alpha = 0.9;
-
-    if (m_fType == TcpWestwood::TUSTIN)
-    {
-        DataRate sample_bwe = m_currentBW;
-        m_currentBW = (m_lastBW * alpha) + (((sample_bwe + m_lastSampleBW) * 0.5) * (1 - alpha));
-        m_lastSampleBW = sample_bwe;
-        m_lastBW = m_currentBW;
-    }
-
-    NS_LOG_LOGIC("Estimated BW after filtering: " << m_currentBW);
-}
-
-uint32_t
-TcpWestwood::GetSsThresh(Ptr<const TcpSocketState> tcb, uint32_t bytesInFlight [[maybe_unused]])
-{
-    uint32_t ssThresh = static_cast<uint32_t>((m_currentBW * tcb->m_minRtt) / 8.0);
-
-    NS_LOG_LOGIC("CurrentBW: " << m_currentBW << " minRtt: " << tcb->m_minRtt
-                               << " ssThresh: " << ssThresh);
-
-    return std::max(2 * tcb->m_segmentSize, ssThresh);
-}
-
-Ptr<TcpCongestionOps>
-TcpWestwood::Fork()
-{
-    return CreateObject<TcpWestwood>(*this);
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-westwood.h ns-3.38/src/internet/model/tcp-westwood.h
--- ns-3.37/src/internet/model/tcp-westwood.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/tcp-westwood.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2013 ResiliNets, ITTC, University of Kansas
- *
- * 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: Siddharth Gangadhar <siddharth@ittc.ku.edu>, Truc Anh N. Nguyen <annguyen@ittc.ku.edu>,
- * and Greeshma Umapathi
- *
- * James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
- * ResiliNets Research Group  https://resilinets.org/
- * Information and Telecommunication Technology Center (ITTC)
- * and Department of Electrical Engineering and Computer Science
- * The University of Kansas Lawrence, KS USA.
- *
- * Work supported in part by NSF FIND (Future Internet Design) Program
- * under grant CNS-0626918 (Postmodern Internet Architecture),
- * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI),
- * US Department of Defense (DoD), and ITTC at The University of Kansas.
- */
-
-#ifndef TCP_WESTWOOD_H
-#define TCP_WESTWOOD_H
-
-#include "tcp-congestion-ops.h"
-
-#include "ns3/data-rate.h"
-#include "ns3/event-id.h"
-#include "ns3/tcp-recovery-ops.h"
-#include "ns3/traced-value.h"
-
-namespace ns3
-{
-
-class Time;
-
-/**
- * \ingroup congestionOps
- *
- * \brief An implementation of TCP Westwood and Westwood+.
- *
- * Westwood and Westwood+ employ the AIAD (Additive Increase/Adaptive Decrease)
- * congestion control paradigm. When a congestion episode happens,
- * instead of halving the cwnd, these protocols try to estimate the network's
- * bandwidth and use the estimated value to adjust the cwnd.
- * While Westwood performs the bandwidth sampling every ACK reception,
- * Westwood+ samples the bandwidth every RTT.
- *
- * The two main methods in the implementation are the CountAck (const TCPHeader&)
- * and the EstimateBW (int, const, Time). The CountAck method calculates
- * the number of acknowledged segments on the receipt of an ACK.
- * The EstimateBW estimates the bandwidth based on the value returned by CountAck
- * and the sampling interval (last ACK inter-arrival time for Westwood and last RTT for Westwood+).
- *
- * WARNING: this TCP model lacks validation and regression tests; use with caution.
- */
-class TcpWestwood : public TcpNewReno
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-
-    TcpWestwood();
-    /**
-     * \brief Copy constructor
-     * \param sock the object to copy
-     */
-    TcpWestwood(const TcpWestwood& sock);
-    ~TcpWestwood() override;
-
-    /**
-     * \brief Protocol variant (Westwood or Westwood+)
-     */
-    enum ProtocolType
-    {
-        WESTWOOD,
-        WESTWOODPLUS
-    };
-
-    /**
-     * \brief Filter type (None or Tustin)
-     */
-    enum FilterType
-    {
-        NONE,
-        TUSTIN
-    };
-
-    uint32_t GetSsThresh(Ptr<const TcpSocketState> tcb, uint32_t bytesInFlight) override;
-
-    void PktsAcked(Ptr<TcpSocketState> tcb, uint32_t packetsAcked, const Time& rtt) override;
-
-    Ptr<TcpCongestionOps> Fork() override;
-
-  private:
-    /**
-     * Update the total number of acknowledged packets during the current RTT
-     *
-     * \param [in] acked the number of packets the currently received ACK acknowledges
-     */
-    void UpdateAckedSegments(int acked);
-
-    /**
-     * Estimate the network's bandwidth
-     *
-     * \param [in] rtt the RTT estimation.
-     * \param [in] tcb the socket state.
-     */
-    void EstimateBW(const Time& rtt, Ptr<TcpSocketState> tcb);
-
-  protected:
-    TracedValue<DataRate> m_currentBW; //!< Current value of the estimated BW
-    DataRate m_lastSampleBW;           //!< Last bandwidth sample
-    DataRate m_lastBW;                 //!< Last bandwidth sample after being filtered
-    enum ProtocolType m_pType;         //!< 0 for Westwood, 1 for Westwood+
-    enum FilterType m_fType;           //!< 0 for none, 1 for Tustin
-
-    uint32_t m_ackedSegments;  //!< The number of segments ACKed between RTTs
-    bool m_IsCount;            //!< Start keeping track of m_ackedSegments for Westwood+ if TRUE
-    EventId m_bwEstimateEvent; //!< The BW estimation event for Westwood+
-    Time m_lastAck;            //!< The last ACK time
-};
-
-} // namespace ns3
-
-#endif /* TCP_WESTWOOD_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-westwood-plus.cc ns-3.38/src/internet/model/tcp-westwood-plus.cc
--- ns-3.37/src/internet/model/tcp-westwood-plus.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet/model/tcp-westwood-plus.cc	2023-03-16 20:54:23.624868600 -0700
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013 ResiliNets, ITTC, University of Kansas
+ *
+ * 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: Siddharth Gangadhar <siddharth@ittc.ku.edu>,
+ *          Truc Anh N. Nguyen <annguyen@ittc.ku.edu>,
+ *          Greeshma Umapathi
+ *
+ * James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
+ * ResiliNets Research Group  https://resilinets.org/
+ * Information and Telecommunication Technology Center (ITTC)
+ * and Department of Electrical Engineering and Computer Science
+ * The University of Kansas Lawrence, KS USA.
+ *
+ * Work supported in part by NSF FIND (Future Internet Design) Program
+ * under grant CNS-0626918 (Postmodern Internet Architecture),
+ * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI),
+ * US Department of Defense (DoD), and ITTC at The University of Kansas.
+ */
+
+#include "tcp-westwood-plus.h"
+
+#include "rtt-estimator.h"
+#include "tcp-socket-base.h"
+
+#include "ns3/log.h"
+#include "ns3/simulator.h"
+
+NS_LOG_COMPONENT_DEFINE("TcpWestwoodPlus");
+
+namespace ns3
+{
+
+NS_OBJECT_ENSURE_REGISTERED(TcpWestwoodPlus);
+
+TypeId
+TcpWestwoodPlus::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::TcpWestwoodPlus")
+            .SetParent<TcpNewReno>()
+            .SetGroupName("Internet")
+            .AddConstructor<TcpWestwoodPlus>()
+            .AddAttribute(
+                "FilterType",
+                "Use this to choose no filter or Tustin's approximation filter",
+                EnumValue(TcpWestwoodPlus::TUSTIN),
+                MakeEnumAccessor(&TcpWestwoodPlus::m_fType),
+                MakeEnumChecker(TcpWestwoodPlus::NONE, "None", TcpWestwoodPlus::TUSTIN, "Tustin"))
+            .AddTraceSource("EstimatedBW",
+                            "The estimated bandwidth",
+                            MakeTraceSourceAccessor(&TcpWestwoodPlus::m_currentBW),
+                            "ns3::TracedValueCallback::DataRate");
+    return tid;
+}
+
+TcpWestwoodPlus::TcpWestwoodPlus()
+    : TcpNewReno(),
+      m_currentBW(0),
+      m_lastSampleBW(0),
+      m_lastBW(0),
+      m_ackedSegments(0),
+      m_IsCount(false),
+      m_lastAck(0)
+{
+    NS_LOG_FUNCTION(this);
+}
+
+TcpWestwoodPlus::TcpWestwoodPlus(const TcpWestwoodPlus& sock)
+    : TcpNewReno(sock),
+      m_currentBW(sock.m_currentBW),
+      m_lastSampleBW(sock.m_lastSampleBW),
+      m_lastBW(sock.m_lastBW),
+      m_fType(sock.m_fType),
+      m_IsCount(sock.m_IsCount)
+{
+    NS_LOG_FUNCTION(this);
+    NS_LOG_LOGIC("Invoked the copy constructor");
+}
+
+TcpWestwoodPlus::~TcpWestwoodPlus()
+{
+}
+
+void
+TcpWestwoodPlus::PktsAcked(Ptr<TcpSocketState> tcb, uint32_t packetsAcked, const Time& rtt)
+{
+    NS_LOG_FUNCTION(this << tcb << packetsAcked << rtt);
+
+    if (rtt.IsZero())
+    {
+        NS_LOG_WARN("RTT measured is zero!");
+        return;
+    }
+
+    m_ackedSegments += packetsAcked;
+
+    if (!(rtt.IsZero() || m_IsCount))
+    {
+        m_IsCount = true;
+        m_bwEstimateEvent.Cancel();
+        m_bwEstimateEvent = Simulator::Schedule(rtt, &TcpWestwoodPlus::EstimateBW, this, rtt, tcb);
+    }
+}
+
+void
+TcpWestwoodPlus::EstimateBW(const Time& rtt, Ptr<TcpSocketState> tcb)
+{
+    NS_LOG_FUNCTION(this);
+
+    NS_ASSERT(!rtt.IsZero());
+
+    m_currentBW = DataRate(m_ackedSegments * tcb->m_segmentSize * 8.0 / rtt.GetSeconds());
+    m_IsCount = false;
+
+    m_ackedSegments = 0;
+
+    NS_LOG_LOGIC("Estimated BW: " << m_currentBW);
+
+    // Filter the BW sample
+
+    constexpr double ALPHA = 0.9;
+
+    if (m_fType == TcpWestwoodPlus::TUSTIN)
+    {
+        DataRate sample_bwe = m_currentBW;
+        m_currentBW = (m_lastBW * ALPHA) + (((sample_bwe + m_lastSampleBW) * 0.5) * (1 - ALPHA));
+        m_lastSampleBW = sample_bwe;
+        m_lastBW = m_currentBW;
+    }
+
+    NS_LOG_LOGIC("Estimated BW after filtering: " << m_currentBW);
+}
+
+uint32_t
+TcpWestwoodPlus::GetSsThresh(Ptr<const TcpSocketState> tcb, uint32_t bytesInFlight [[maybe_unused]])
+{
+    uint32_t ssThresh = static_cast<uint32_t>((m_currentBW * tcb->m_minRtt) / 8.0);
+
+    NS_LOG_LOGIC("CurrentBW: " << m_currentBW << " minRtt: " << tcb->m_minRtt
+                               << " ssThresh: " << ssThresh);
+
+    return std::max(2 * tcb->m_segmentSize, ssThresh);
+}
+
+Ptr<TcpCongestionOps>
+TcpWestwoodPlus::Fork()
+{
+    return CreateObject<TcpWestwoodPlus>(*this);
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/tcp-westwood-plus.h ns-3.38/src/internet/model/tcp-westwood-plus.h
--- ns-3.37/src/internet/model/tcp-westwood-plus.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet/model/tcp-westwood-plus.h	2023-03-16 20:54:23.624868600 -0700
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2013 ResiliNets, ITTC, University of Kansas
+ *
+ * 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: Siddharth Gangadhar <siddharth@ittc.ku.edu>, Truc Anh N. Nguyen <annguyen@ittc.ku.edu>,
+ * and Greeshma Umapathi
+ *
+ * James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
+ * ResiliNets Research Group  https://resilinets.org/
+ * Information and Telecommunication Technology Center (ITTC)
+ * and Department of Electrical Engineering and Computer Science
+ * The University of Kansas Lawrence, KS USA.
+ *
+ * Work supported in part by NSF FIND (Future Internet Design) Program
+ * under grant CNS-0626918 (Postmodern Internet Architecture),
+ * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI),
+ * US Department of Defense (DoD), and ITTC at The University of Kansas.
+ */
+
+#ifndef TCP_WESTWOOD_H
+#define TCP_WESTWOOD_H
+
+#include "tcp-congestion-ops.h"
+
+#include "ns3/data-rate.h"
+#include "ns3/event-id.h"
+#include "ns3/tcp-recovery-ops.h"
+#include "ns3/traced-value.h"
+
+namespace ns3
+{
+
+class Time;
+
+/**
+ * \ingroup congestionOps
+ *
+ * \brief An implementation of TCP Westwood+.
+ *
+ * Westwood+ employ the AIAD (Additive Increase/Adaptive Decrease)
+ * congestion control paradigm. When a congestion episode happens,
+ * instead of halving the cwnd, these protocols try to estimate the network's
+ * bandwidth and use the estimated value to adjust the cwnd.
+ * While Westwood performs the bandwidth sampling every ACK reception,
+ * Westwood+ samples the bandwidth every RTT.
+ *
+ * The two main methods in the implementation are the CountAck (const TCPHeader&)
+ * and the EstimateBW (int, const, Time). The CountAck method calculates
+ * the number of acknowledged segments on the receipt of an ACK.
+ * The EstimateBW estimates the bandwidth based on the value returned by CountAck
+ * and the sampling interval (last RTT).
+ *
+ * WARNING: this TCP model lacks validation and regression tests; use with caution.
+ */
+class TcpWestwoodPlus : public TcpNewReno
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    TcpWestwoodPlus();
+    /**
+     * \brief Copy constructor
+     * \param sock the object to copy
+     */
+    TcpWestwoodPlus(const TcpWestwoodPlus& sock);
+    ~TcpWestwoodPlus() override;
+
+    /**
+     * \brief Filter type (None or Tustin)
+     */
+    enum FilterType
+    {
+        NONE,
+        TUSTIN
+    };
+
+    uint32_t GetSsThresh(Ptr<const TcpSocketState> tcb, uint32_t bytesInFlight) override;
+
+    void PktsAcked(Ptr<TcpSocketState> tcb, uint32_t packetsAcked, const Time& rtt) override;
+
+    Ptr<TcpCongestionOps> Fork() override;
+
+  private:
+    /**
+     * Update the total number of acknowledged packets during the current RTT
+     *
+     * \param [in] acked the number of packets the currently received ACK acknowledges
+     */
+    void UpdateAckedSegments(int acked);
+
+    /**
+     * Estimate the network's bandwidth
+     *
+     * \param [in] rtt the RTT estimation.
+     * \param [in] tcb the socket state.
+     */
+    void EstimateBW(const Time& rtt, Ptr<TcpSocketState> tcb);
+
+  protected:
+    TracedValue<DataRate> m_currentBW; //!< Current value of the estimated BW
+    DataRate m_lastSampleBW;           //!< Last bandwidth sample
+    DataRate m_lastBW;                 //!< Last bandwidth sample after being filtered
+    FilterType m_fType;                //!< 0 for none, 1 for Tustin
+
+    uint32_t m_ackedSegments;  //!< The number of segments ACKed between RTTs
+    bool m_IsCount;            //!< Start keeping track of m_ackedSegments for Westwood+ if TRUE
+    EventId m_bwEstimateEvent; //!< The BW estimation event for Westwood+
+    Time m_lastAck;            //!< The last ACK time
+};
+
+} // namespace ns3
+
+#endif /* TCP_WESTWOOD_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/udp-header.cc ns-3.38/src/internet/model/udp-header.cc
--- ns-3.37/src/internet/model/udp-header.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/udp-header.cc	2023-03-16 20:54:23.624868600 -0700
@@ -241,7 +241,7 @@
 }
 
 uint16_t
-UdpHeader::GetChecksum()
+UdpHeader::GetChecksum() const
 {
     return m_checksum;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/udp-header.h ns-3.38/src/internet/model/udp-header.h
--- ns-3.37/src/internet/model/udp-header.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/udp-header.h	2023-03-16 20:54:23.624868600 -0700
@@ -158,7 +158,7 @@
      * \brief Return the checksum (only known after a Deserialize)
      * \return The checksum for this UdpHeader
      */
-    uint16_t GetChecksum();
+    uint16_t GetChecksum() const;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/udp-socket-impl.cc ns-3.38/src/internet/model/udp-socket-impl.cc
--- ns-3.37/src/internet/model/udp-socket-impl.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/udp-socket-impl.cc	2023-03-16 20:54:23.624868600 -0700
@@ -232,6 +232,8 @@
     }
     if (done)
     {
+        m_shutdownRecv = false;
+        m_shutdownSend = false;
         return 0;
     }
     return -1;
@@ -752,7 +754,7 @@
         tag.SetHopLimit(GetIpv6HopLimit());
         p->AddPacketTag(tag);
     }
-    // There is no analgous to an IPv4 broadcast address in IPv6.
+    // There is no analogous to an IPv4 broadcast address in IPv6.
     // Instead, we use a set of link-local, site-local, and global
     // multicast addresses.  The Ipv6 routing layers should all
     // provide an interface-specific route to these addresses such
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/udp-socket-impl.h ns-3.38/src/internet/model/udp-socket-impl.h
--- ns-3.37/src/internet/model/udp-socket-impl.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/udp-socket-impl.h	2023-03-16 20:54:23.628868600 -0700
@@ -93,8 +93,8 @@
      */
     void SetUdp(Ptr<UdpL4Protocol> udp);
 
-    enum SocketErrno GetErrno() const override;
-    enum SocketType GetSocketType() const override;
+    SocketErrno GetErrno() const override;
+    SocketType GetSocketType() const override;
     Ptr<Node> GetNode() const override;
     int Bind() override;
     int Bind6() override;
@@ -262,11 +262,11 @@
     uint16_t m_defaultPort;                        //!< Default port
     TracedCallback<Ptr<const Packet>> m_dropTrace; //!< Trace for dropped packets
 
-    mutable enum SocketErrno m_errno; //!< Socket error code
-    bool m_shutdownSend;              //!< Send no longer allowed
-    bool m_shutdownRecv;              //!< Receive no longer allowed
-    bool m_connected;                 //!< Connection established
-    bool m_allowBroadcast;            //!< Allow send broadcast packets
+    mutable SocketErrno m_errno; //!< Socket error code
+    bool m_shutdownSend;         //!< Send no longer allowed
+    bool m_shutdownRecv;         //!< Receive no longer allowed
+    bool m_connected;            //!< Connection established
+    bool m_allowBroadcast;       //!< Allow send broadcast packets
 
     std::queue<std::pair<Ptr<Packet>, Address>> m_deliveryQueue; //!< Queue for incoming packets
     uint32_t m_rxAvailable; //!< Number of available bytes to be received
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/model/windowed-filter.h ns-3.38/src/internet/model/windowed-filter.h
--- ns-3.37/src/internet/model/windowed-filter.h	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/model/windowed-filter.h	2023-03-16 20:54:23.628868600 -0700
@@ -136,14 +136,14 @@
 {
   public:
     /**
-     * \brief contructor
+     * \brief constructor
      */
     WindowedFilter()
     {
     }
 
     /**
-     * \brief contructor
+     * \brief constructor
      * \param windowLength is the period after which a best estimate expires.
      * \param zeroValue is used as the uninitialized value for objects of T. Importantly,
      * zeroValue should be an invalid value for a true sample.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/global-route-manager-impl-test-suite.cc ns-3.38/src/internet/test/global-route-manager-impl-test-suite.cc
--- ns-3.37/src/internet/test/global-route-manager-impl-test-suite.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/global-route-manager-impl-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -32,12 +32,12 @@
 
 /**
  * \ingroup internet
+ * \ingroup tests
  * \defgroup internet-test internet module tests
  */
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Global Route Manager Test
  */
@@ -224,7 +224,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Global Route Manager TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-address-generator-test-suite.cc ns-3.38/src/internet/test/ipv4-address-generator-test-suite.cc
--- ns-3.37/src/internet/test/ipv4-address-generator-test-suite.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-address-generator-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -23,7 +23,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 network number Test
  */
@@ -84,7 +83,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 address allocator Test
  */
@@ -142,7 +140,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 network and address allocator Test
  */
@@ -214,7 +211,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 AddressGenerator example (sort of) Test
  */
@@ -273,7 +269,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 address collision Test
  */
@@ -346,7 +341,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Address Generator TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-address-helper-test-suite.cc ns-3.38/src/internet/test/ipv4-address-helper-test-suite.cc
--- ns-3.37/src/internet/test/ipv4-address-helper-test-suite.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-address-helper-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -24,7 +24,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 network allocator helper Test
  */
@@ -78,7 +77,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 address allocator helper Test
  */
@@ -132,7 +130,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 reset allocator helper Test
  */
@@ -201,7 +198,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 address helper Test
  */
@@ -281,7 +277,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Address Helper TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-deduplication-test.cc ns-3.38/src/internet/test/ipv4-deduplication-test.cc
--- ns-3.37/src/internet/test/ipv4-deduplication-test.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-deduplication-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -57,7 +57,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Deduplication Test
  *
@@ -447,28 +446,34 @@
 Ipv4DeduplicationTest::CheckPackets(const std::string& name)
 {
     // a priori determined packet receptions based on initial TTL of 4, disabled de-dup
-    std::map<std::string, uint32_t> packets = {{"A", 14},
-                                               {"B", 16},
-                                               {"C", 16},
-                                               {"D", 16},
-                                               {"E", 4}};
+    std::map<std::string, uint32_t> packets = {
+        {"A", 14},
+        {"B", 16},
+        {"C", 16},
+        {"D", 16},
+        {"E", 4},
+    };
 
     // a priori determined packet receptions based on
-    std::map<std::string, uint32_t> packetsDuped = {{"A", 0},
-                                                    {"B", 1},
-                                                    {"C", 1},
-                                                    {"D", 1},
-                                                    {"E", 1}};
+    std::map<std::string, uint32_t> packetsDuped = {
+        {"A", 0},
+        {"B", 1},
+        {"C", 1},
+        {"D", 1},
+        {"E", 1},
+    };
     // a priori determined packet receptions based on initial TTL of 4, degenerate de-dup
     // There are TTL (4) rounds of packets.  Each round a node will register a
     // received packet if another connected node transmits.  A misses the 1st round
     // since it is the only one transmitting.  D is not connected to A in 1st round
     // either.  E only hears a packet in the 3rd and 4th rounds.
-    std::map<std::string, uint32_t> degenerates = {{"A", 3},
-                                                   {"B", 4},
-                                                   {"C", 4},
-                                                   {"D", 3},
-                                                   {"E", 2}};
+    std::map<std::string, uint32_t> degenerates = {
+        {"A", 3},
+        {"B", 4},
+        {"C", 4},
+        {"D", 3},
+        {"E", 2},
+    };
 
     switch (m_mode)
     {
@@ -540,7 +545,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Deduplication TestSuite
  */
@@ -566,7 +570,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Deduplication Performance Test
  *
@@ -724,7 +727,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Deduplication Performance TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-forwarding-test.cc ns-3.38/src/internet/test/ipv4-forwarding-test.cc
--- ns-3.37/src/internet/test/ipv4-forwarding-test.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-forwarding-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -44,7 +44,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Forwarding Test
  */
@@ -227,7 +226,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Forwarding TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-fragmentation-test.cc ns-3.38/src/internet/test/ipv4-fragmentation-test.cc
--- ns-3.37/src/internet/test/ipv4-fragmentation-test.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-fragmentation-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -59,7 +59,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Tag used in IPv4 Fragmentation Test
  */
@@ -117,7 +116,7 @@
      * \brief Get the token.
      * \returns The token.
      */
-    uint64_t GetToken()
+    uint64_t GetToken() const
     {
         return token;
     }
@@ -125,7 +124,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Fragmentation Test
  */
@@ -579,7 +577,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Fragmentation TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-global-routing-test-suite.cc ns-3.38/src/internet/test/ipv4-global-routing-test-suite.cc
--- ns-3.37/src/internet/test/ipv4-global-routing-test-suite.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-global-routing-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -118,7 +118,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 GlobalRouting Link test
  */
@@ -203,7 +202,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 GlobalRouting LAN test
  */
@@ -285,7 +283,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 GlobalRouting Two Link test
  */
@@ -405,7 +402,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 GlobalRouting Two LAN test
  */
@@ -506,7 +502,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 GlobalRouting Bridge test
  */
@@ -690,7 +685,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 GlobalRouting Two bridges test
  */
@@ -848,7 +842,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Dynamic GlobalRouting test
  */
@@ -1159,7 +1152,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Dynamic GlobalRouting /32 test
  */
@@ -1323,7 +1315,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 GlobalRouting TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-header-test.cc ns-3.38/src/internet/test/ipv4-header-test.cc
--- ns-3.37/src/internet/test/ipv4-header-test.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-header-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -53,7 +53,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Header Test
  */
@@ -63,7 +62,7 @@
     Ipv4Header m_receivedHeader;  //!< Received header.
 
     /**
-     * \brief Send a packet with speciic DSCP and ECN fields.
+     * \brief Send a packet with specific DSCP and ECN fields.
      * \param socket The source socket.
      * \param to The destination address.
      * \param dscp The DSCP field.
@@ -75,7 +74,7 @@
                                Ipv4Header::EcnType ecn);
 
     /**
-     * \brief Send a packet with speciic DSCP and ECN fields.
+     * \brief Send a packet with specific DSCP and ECN fields.
      * \param socket The source socket.
      * \param to The destination address.
      * \param dscp The DSCP field.
@@ -293,7 +292,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Header TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-list-routing-test-suite.cc ns-3.38/src/internet/test/ipv4-list-routing-test-suite.cc
--- ns-3.37/src/internet/test/ipv4-list-routing-test-suite.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-list-routing-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -24,7 +24,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 dummy routing class (A)
  */
@@ -77,7 +76,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 dummy routing class (B)
  */
@@ -130,7 +128,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 ListRouting negative test.
  */
@@ -165,7 +162,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 ListRouting positive test.
  */
@@ -205,7 +201,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 ListRouting TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-packet-info-tag-test-suite.cc ns-3.38/src/internet/test/ipv4-packet-info-tag-test-suite.cc
--- ns-3.37/src/internet/test/ipv4-packet-info-tag-test-suite.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-packet-info-tag-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -54,7 +54,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 PacketInfoTag Test
  */
@@ -209,7 +208,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 PacketInfoTag TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-raw-test.cc ns-3.38/src/internet/test/ipv4-raw-test.cc
--- ns-3.37/src/internet/test/ipv4-raw-test.cc	2022-11-01 17:37:24.507395000 -0700
+++ ns-3.38/src/internet/test/ipv4-raw-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -54,7 +54,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 RAW Socket Test
  */
@@ -372,7 +371,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 RAW Socket TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-rip-test.cc ns-3.38/src/internet/test/ipv4-rip-test.cc
--- ns-3.37/src/internet/test/ipv4-rip-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv4-rip-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -46,7 +46,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 RIP Test
  */
@@ -276,7 +275,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 RIP count to infinity Test
  */
@@ -510,7 +508,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 RIP SplitHorizon strategy Test
  */
@@ -699,7 +696,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 RIP TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-static-routing-test-suite.cc ns-3.38/src/internet/test/ipv4-static-routing-test-suite.cc
--- ns-3.37/src/internet/test/ipv4-static-routing-test-suite.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv4-static-routing-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -39,7 +39,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 StaticRouting /32 Test
  */
@@ -210,7 +209,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 StaticRouting /32 TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv4-test.cc ns-3.38/src/internet/test/ipv4-test.cc
--- ns-3.37/src/internet/test/ipv4-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv4-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -32,7 +32,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 Test
  */
@@ -123,7 +122,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv4 TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-address-duplication-test.cc ns-3.38/src/internet/test/ipv6-address-duplication-test.cc
--- ns-3.37/src/internet/test/ipv6-address-duplication-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-address-duplication-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -44,7 +44,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 Duplicate Address Detection Test
  */
@@ -118,7 +117,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 Duplicate Address Detection TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-address-generator-test-suite.cc ns-3.38/src/internet/test/ipv6-address-generator-test-suite.cc
--- ns-3.37/src/internet/test/ipv6-address-generator-test-suite.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-address-generator-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -24,7 +24,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 network number allocator Test
  */
@@ -83,7 +82,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 address allocator Test
  */
@@ -148,7 +146,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 network number and address allocator Test
  */
@@ -200,7 +197,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 example of an address generator Test
  */
@@ -265,7 +261,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 address collision Test
  */
@@ -338,7 +333,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 address generator TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-address-helper-test-suite.cc ns-3.38/src/internet/test/ipv6-address-helper-test-suite.cc
--- ns-3.37/src/internet/test/ipv6-address-helper-test-suite.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-address-helper-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 AddressHelper Test
  */
@@ -170,7 +169,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 AddressHelper TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-dual-stack-test-suite.cc ns-3.38/src/internet/test/ipv6-dual-stack-test-suite.cc
--- ns-3.37/src/internet/test/ipv6-dual-stack-test-suite.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-dual-stack-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -52,7 +52,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 dual stack Test
  */
@@ -354,7 +353,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 dual stack TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-extension-header-test-suite.cc ns-3.38/src/internet/test/ipv6-extension-header-test-suite.cc
--- ns-3.37/src/internet/test/ipv6-extension-header-test-suite.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-extension-header-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 extensions Test: Empty option field.
  */
@@ -76,7 +75,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 extensions Test: Option without alignment.
  */
@@ -100,7 +98,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 extensions Test: Test the option without alignment.
  */
@@ -151,7 +148,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 extensions Test: Option with alignment.
  */
@@ -180,7 +176,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 extensions Test: Test the option with alignment.
  */
@@ -238,7 +233,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 extensions Test: Test an option already aligned.
  */
@@ -278,7 +272,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 extensions TestSuite.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-forwarding-test.cc ns-3.38/src/internet/test/ipv6-forwarding-test.cc
--- ns-3.37/src/internet/test/ipv6-forwarding-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-forwarding-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -44,7 +44,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 Forwarding Test
  */
@@ -212,7 +211,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 Forwarding TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-fragmentation-test.cc ns-3.38/src/internet/test/ipv6-fragmentation-test.cc
--- ns-3.37/src/internet/test/ipv6-fragmentation-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-fragmentation-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -67,7 +67,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Tag used in IPv6 Fragmentation Test
  */
@@ -125,7 +124,7 @@
      * \brief Get the token.
      * \returns The token.
      */
-    uint64_t GetToken()
+    uint64_t GetToken() const
     {
         return token;
     }
@@ -133,7 +132,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 Fragmentation Test
  */
@@ -618,7 +616,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 Fragmentation TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-list-routing-test-suite.cc ns-3.38/src/internet/test/ipv6-list-routing-test-suite.cc
--- ns-3.37/src/internet/test/ipv6-list-routing-test-suite.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-list-routing-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -25,7 +25,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 dummy routing class (A)
  */
@@ -92,7 +91,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 dummy routing class (B)
  */
@@ -159,7 +157,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 ListRouting negative test.
  */
@@ -194,7 +191,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 ListRouting positive test.
  */
@@ -234,7 +230,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 ListRouting TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-packet-info-tag-test-suite.cc ns-3.38/src/internet/test/ipv6-packet-info-tag-test-suite.cc
--- ns-3.37/src/internet/test/ipv6-packet-info-tag-test-suite.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-packet-info-tag-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
@@ -76,7 +76,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 PacketInfoTag Test
  */
@@ -245,7 +244,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 PacketInfoTag TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-raw-test.cc ns-3.38/src/internet/test/ipv6-raw-test.cc
--- ns-3.37/src/internet/test/ipv6-raw-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-raw-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -56,7 +56,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 RAW Socket Test
  */
@@ -324,7 +323,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 RAW Socket TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-ripng-test.cc ns-3.38/src/internet/test/ipv6-ripng-test.cc
--- ns-3.37/src/internet/test/ipv6-ripng-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-ripng-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -45,7 +45,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 RIPng Test
  */
@@ -276,7 +275,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 RIPng count to infinity Test
  */
@@ -509,7 +507,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 RIPng SplitHorizon strategy Test
  */
@@ -698,7 +695,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 RIPng TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/ipv6-test.cc ns-3.38/src/internet/test/ipv6-test.cc
--- ns-3.37/src/internet/test/ipv6-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/ipv6-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -32,7 +32,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 Test
  */
@@ -180,7 +179,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief IPv6 TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/neighbor-cache-test.cc ns-3.38/src/internet/test/neighbor-cache-test.cc
--- ns-3.37/src/internet/test/neighbor-cache-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/neighbor-cache-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -41,7 +41,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Dynamic Neighbor Cache Test
  */
@@ -237,7 +236,7 @@
     }
     net.Add(tx2Dev);
 
-    // Recieve node
+    // Receive node
     Ptr<SimpleNetDevice> rxDev;
     {
         rxDev = CreateObject<SimpleNetDevice>();
@@ -542,7 +541,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Neighbor cache on Channel Test
  */
@@ -632,7 +630,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Neighbor Cache on NetDeviceContainer Test
  */
@@ -723,7 +720,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Neighbor Cache on InterfaceContainer Test
  */
@@ -814,7 +810,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Neighbor Cache Flush Test
  */
@@ -922,7 +917,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Neighbor Cache on Overlapped Scope Test
  */
@@ -1022,7 +1016,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Dynamic Neighbor Cache on Reduced Scope Test
  */
@@ -1230,7 +1223,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief NeighborCache TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/rtt-test.cc ns-3.38/src/internet/test/rtt-test.cc
--- ns-3.37/src/internet/test/rtt-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/rtt-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief RTT estimator Test
  */
@@ -181,7 +180,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief RTT estimator TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-advertised-window-test.cc ns-3.38/src/internet/test/tcp-advertised-window-test.cc
--- ns-3.37/src/internet/test/tcp-advertised-window-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-advertised-window-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -272,7 +272,7 @@
  *
  *   m_tcb->m_rxBuffer->MaxBufferSize ()
  *
- * This change was introduced with regard to situations in which the receiviing
+ * This change was introduced with regard to situations in which the receiving
  * application does not read from the socket as fast as possible (see bug 2559
  * for details). This test ensures that no regression is introduced for other,
  * "normal" cases.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-bbr-test.cc ns-3.38/src/internet/test/tcp-bbr-test.cc
--- ns-3.37/src/internet/test/tcp-bbr-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-bbr-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -182,7 +182,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP BBR TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-bic-test.cc ns-3.38/src/internet/test/tcp-bic-test.cc
--- ns-3.37/src/internet/test/tcp-bic-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-bic-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance increment on TcpBic
  */
@@ -191,7 +190,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance decrement on TcpBic
  */
@@ -305,7 +303,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Bic TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-bytes-in-flight-test.cc ns-3.38/src/internet/test/tcp-bytes-in-flight-test.cc
--- ns-3.37/src/internet/test/tcp-bytes-in-flight-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-bytes-in-flight-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Check the value of BytesInFlight against a home-made guess
  *
@@ -283,7 +282,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TestSuite: Check the value of BytesInFlight against a home-made guess
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-classic-recovery-test.cc ns-3.38/src/internet/test/tcp-classic-recovery-test.cc
--- ns-3.37/src/internet/test/tcp-classic-recovery-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-classic-recovery-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -102,9 +102,10 @@
     uint32_t cWndInflPrevious = m_state->m_cWndInfl;
     uint32_t cWndPrevious = m_state->m_cWnd;
     recovery->DoRecovery(m_state, 500);
-    NS_TEST_ASSERT_MSG_EQ(m_state->m_cWndInfl,
-                          (cWndInflPrevious + m_state->m_segmentSize),
-                          "m_cWndInfl should be incresed by one segmentSize on calling DoRecovery");
+    NS_TEST_ASSERT_MSG_EQ(
+        m_state->m_cWndInfl,
+        (cWndInflPrevious + m_state->m_segmentSize),
+        "m_cWndInfl should be increased by one segmentSize on calling DoRecovery");
     NS_TEST_ASSERT_MSG_EQ(m_state->m_cWnd, cWndPrevious, "cWnd should not change in recovery");
 
     recovery->ExitRecovery(m_state);
@@ -118,7 +119,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Classic Recovery TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-cong-avoid-test.cc ns-3.38/src/internet/test/tcp-cong-avoid-test.cc
--- ns-3.37/src/internet/test/tcp-cong-avoid-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-cong-avoid-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test the behavior of RFC congestion avoidance
  *
@@ -181,7 +180,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TestSuite for the behavior of RFC congestion avoidance
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-datasentcb-test.cc ns-3.38/src/internet/test/tcp-datasentcb-test.cc
--- ns-3.37/src/internet/test/tcp-datasentcb-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-datasentcb-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Socket that the 50% of the times saves the entire packet in the buffer,
  * while in the other 50% saves only half the packet.
@@ -90,7 +89,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Data Sent callback test
  *
@@ -164,7 +162,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TestSuite: Data Sent callback
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-dctcp-test.cc ns-3.38/src/internet/test/tcp-dctcp-test.cc
--- ns-3.37/src/internet/test/tcp-dctcp-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-dctcp-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -39,7 +39,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Validates the setting of ECT and ECE codepoints for DCTCP enabled traffic
  */
@@ -187,7 +186,7 @@
                                   0,
                                   "The flag ECE should be not be set in TCP header of the packet "
                                   "sent by the receiver if it receives a packet without CE bit set "
-                                  "in IP header inspite of Sender not sending CWR flags to it");
+                                  "in IP header in spite of Sender not sending CWR flags to it");
         }
     }
 }
@@ -209,13 +208,12 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief A TCP socket which sends a data packet with CE flags set for test 3.
  *
  * The SendDataPacket function of this class sends data packet numbered 1  with CE flags set and
  * also doesn't set CWR flags on receipt of ECE flags for test 3. This is done to verify that DCTCP
- * receiver sends ECE only if it receives CE inspite of sender not sending CWR flags for ECE
+ * receiver sends ECE only if it receives CE in spite of sender not sending CWR flags for ECE
  *
  */
 class TcpDctcpCongestedRouter : public TcpSocketMsgBase
@@ -554,7 +552,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief DCTCP should be same as Linux during slow start
  */
@@ -655,7 +652,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP DCTCP TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-ecn-test.cc ns-3.38/src/internet/test/tcp-ecn-test.cc
--- ns-3.37/src/internet/test/tcp-ecn-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-ecn-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -42,7 +42,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief checks if ECT, CWR and ECE bits are set correctly in different scenarios
  *
@@ -81,7 +80,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief A TCP socket which sends certain data packets with CE flags set for tests 5 and 6.
  *
@@ -580,7 +578,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP ECN TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-endpoint-bug2211.cc ns-3.38/src/internet/test/tcp-endpoint-bug2211.cc
--- ns-3.37/src/internet/test/tcp-endpoint-bug2211.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-endpoint-bug2211.cc	2023-03-16 20:54:23.628868600 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test for bug 2211.
  *
@@ -151,7 +150,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TestSuite for bug 2211 - It must be used with valgrind.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-error-model.h ns-3.38/src/internet/test/tcp-error-model.h
--- ns-3.37/src/internet/test/tcp-error-model.h	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-error-model.h	2023-03-16 20:54:23.628868600 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief A general (TCP-aware) error model.
  *
@@ -74,7 +73,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief An error model TCP aware: it drops the sequence number declared.
  *
@@ -121,7 +119,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Error model which drop packets with specified TCP flags.
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-fast-retr-test.cc ns-3.38/src/internet/test/tcp-fast-retr-test.cc
--- ns-3.37/src/internet/test/tcp-fast-retr-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-fast-retr-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -21,7 +21,7 @@
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/simple-channel.h"
-#include "ns3/tcp-westwood.h"
+#include "ns3/tcp-westwood-plus.h"
 
 using namespace ns3;
 
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test the fast retransmission
  *
@@ -291,7 +290,7 @@
     {
         if (h.GetAckNumber().GetValue() < m_seqToKill)
         {
-            NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+            NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                                   TcpSocketState::CA_OPEN,
                                   "Not in OPEN state to respond to a loss");
             NS_TEST_ASSERT_MSG_EQ(GetDupAckCount(SENDER),
@@ -304,20 +303,20 @@
 
             if (GetDupAckCount(SENDER) == 0 && GetDupAckCount(SENDER) < GetReTxThreshold(SENDER))
             {
-                NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+                NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                                       TcpSocketState::CA_OPEN,
                                       "Not in OPEN state for processing dupack");
             }
             else if (GetDupAckCount(SENDER) > 0 &&
                      GetDupAckCount(SENDER) < GetReTxThreshold(SENDER))
             {
-                NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+                NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                                       TcpSocketState::CA_DISORDER,
                                       "Not in DISORDER state after receiving dupacks");
             }
             else if (GetDupAckCount(SENDER) >= GetReTxThreshold(SENDER))
             {
-                NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+                NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                                       TcpSocketState::CA_RECOVERY,
                                       "Not in RECOVERY state after reaching retxthresh");
             }
@@ -325,7 +324,7 @@
     }
     else if (who == RECEIVER)
     {
-        NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+        NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                               TcpSocketState::CA_OPEN,
                               "Receiver not in OPEN state");
     }
@@ -350,14 +349,14 @@
 
             if (GetDupAckCount(SENDER) < GetReTxThreshold(SENDER))
             {
-                NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+                NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                                       TcpSocketState::CA_DISORDER,
                                       "DupAck less than ReTxThreshold but not "
                                       "in DISORDER state");
             }
             else
             {
-                NS_TEST_ASSERT_MSG_GT_OR_EQ(GetCongStateFrom(tcb),
+                NS_TEST_ASSERT_MSG_GT_OR_EQ(tcb->m_congState.Get(),
                                             TcpSocketState::CA_RECOVERY,
                                             "DupAck greater than ReTxThreshold but not "
                                             "in RECOVERY or LOSS state");
@@ -373,7 +372,7 @@
     }
     else if (who == RECEIVER)
     {
-        NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+        NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                               TcpSocketState::CA_OPEN,
                               "Different state than OPEN in the receiver");
     }
@@ -431,7 +430,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testsuite for the fast retransmission
  */
@@ -442,7 +440,7 @@
         : TestSuite("tcp-fast-retr-test", UNIT)
     {
         std::list<TypeId> types;
-        types.insert(types.begin(), TcpWestwood::GetTypeId());
+        types.insert(types.begin(), TcpWestwoodPlus::GetTypeId());
         types.insert(types.begin(), TcpNewReno::GetTypeId());
 
         for (std::list<TypeId>::iterator it = types.begin(); it != types.end(); ++it)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-general-test.h ns-3.38/src/internet/test/tcp-general-test.h
--- ns-3.37/src/internet/test/tcp-general-test.h	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-general-test.h	2023-03-16 20:54:23.628868600 -0700
@@ -31,7 +31,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Class for inserting callbacks special points of the flow of TCP sockets
  *
@@ -151,7 +150,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief A TCP socket which sends ACKs smaller than the segment received.
  *
@@ -215,7 +213,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief General infrastructure for TCP testing
  *
@@ -1160,21 +1157,6 @@
     InetSocketAddress m_remoteAddr; //!< Remote peer address.
 };
 
-/**
- * \ingroup internet-test
- * \ingroup tests
- *
- * \brief Convenience function to retrieve the ACK state from a TCB
- *
- * \param tcb Transmission control block
- * \return the state of the ACK state machine
- */
-static inline TcpSocketState::TcpCongState_t
-GetCongStateFrom(Ptr<const TcpSocketState> tcb)
-{
-    return tcb->m_congState.Get();
-}
-
 } // namespace ns3
 
 #endif // TCPGENERALTEST_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-header-test.cc ns-3.38/src/internet/test/tcp-header-test.cc
--- ns-3.37/src/internet/test/tcp-header-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-header-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -41,7 +41,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP header Get/Set test.
  */
@@ -159,7 +158,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP header with RFC793 Options test.
  */
@@ -386,9 +384,8 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
- * \brief TCP header Flags to Striing test.
+ * \brief TCP header Flags to String test.
  */
 class TcpHeaderFlagsToString : public TestCase
 {
@@ -456,7 +453,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP header TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-highspeed-test.cc ns-3.38/src/internet/test/tcp-highspeed-test.cc
--- ns-3.37/src/internet/test/tcp-highspeed-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-highspeed-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance increment on TcpHighSpeed
  */
@@ -83,7 +82,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance decrement on TcpHighSpeed
  */
@@ -137,15 +135,17 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TcpHighSpeed Congestion window values to test.
  */
-static const struct HighSpeedImportantValues
+struct HighSpeedImportantValues
 {
     unsigned int cwnd; //!< Congestion window.
     unsigned int md;   //!< Currently unused.
-} highSpeedImportantValues[] = {
+};
+
+/// List of data to be tested
+static const HighSpeedImportantValues highSpeedImportantValues[]{
     {38, 128, /*  0.50 */},   {118, 112, /*  0.44 */},  {221, 104, /*  0.41 */},
     {347, 98, /*  0.38 */},   {495, 93, /*  0.37 */},   {663, 89, /*  0.35 */},
     {851, 86, /*  0.34 */},   {1058, 83, /*  0.33 */},  {1284, 81, /*  0.32 */},
@@ -170,13 +170,12 @@
     {58932, 30, /*  0.12 */}, {61799, 29, /*  0.12 */}, {64851, 28, /*  0.11 */},
     {68113, 28, /*  0.11 */}, {71617, 27, /*  0.11 */}, {75401, 26, /*  0.10 */},
     {79517, 26, /*  0.10 */}, {84035, 25, /*  0.10 */}, {89053, 24, /*  0.10 */},
-}; //!< List of data to be tested.
+};
 
 #define HIGHSPEED_VALUES_N 71
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP HighSpeed TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-htcp-test.cc ns-3.38/src/internet/test/tcp-htcp-test.cc
--- ns-3.37/src/internet/test/tcp-htcp-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-htcp-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -36,7 +36,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance increment on TcpHtcp
  */
@@ -148,7 +147,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Htcp TestSuite.
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-hybla-test.cc ns-3.38/src/internet/test/tcp-hybla-test.cc
--- ns-3.37/src/internet/test/tcp-hybla-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-hybla-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance increment on TcpHybla
  */
@@ -149,7 +148,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Hybla TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-illinois-test.cc ns-3.38/src/internet/test/tcp-illinois-test.cc
--- ns-3.37/src/internet/test/tcp-illinois-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-illinois-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TcpIllinois C-AIMD algorithm tests.
  */
@@ -346,7 +345,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Illinois TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-ledbat-test.cc ns-3.38/src/internet/test/tcp-ledbat-test.cc
--- ns-3.37/src/internet/test/tcp-ledbat-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-ledbat-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -31,7 +31,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief LEDBAT should be same as NewReno during slow start, and when timestamps are disabled
  */
@@ -132,7 +131,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test to validate cWnd increment in LEDBAT
  */
@@ -234,7 +232,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test to validate cWnd decrement in LEDBAT
  */
@@ -339,7 +336,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Ledbat TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-linux-reno-test.cc ns-3.38/src/internet/test/tcp-linux-reno-test.cc
--- ns-3.37/src/internet/test/tcp-linux-reno-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-linux-reno-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -31,7 +31,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * This unit test checks that the slow start and congestion avoidance
  * behavior matches Linux behavior as follows:
@@ -195,7 +194,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * This unit test checks that the slow start and congestion avoidance
  * behavior matches Linux behavior as follows:
@@ -372,7 +370,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TestSuite for the behavior of Linux Reno
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-loss-test.cc ns-3.38/src/internet/test/tcp-loss-test.cc
--- ns-3.37/src/internet/test/tcp-loss-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-loss-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Check rollover of sequence number and how that affects loss recovery
  *
@@ -176,7 +175,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * Test various packet losses
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-lp-test.cc ns-3.38/src/internet/test/tcp-lp-test.cc
--- ns-3.37/src/internet/test/tcp-lp-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-lp-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -33,7 +33,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the behaviour common to New Reno
  */
@@ -114,7 +113,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing TcpLp when cwd exceeds threshold
  */
@@ -184,7 +182,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing TcpLp when it is inference phase
  */
@@ -258,7 +255,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * Test the behaviour common to New Reno
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-option-test.cc ns-3.38/src/internet/test/tcp-option-test.cc
--- ns-3.37/src/internet/test/tcp-option-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-option-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Window Scaling option Test
  */
@@ -107,7 +106,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP TimeStamp option Test
  */
@@ -198,7 +196,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP options TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-pacing-test.cc ns-3.38/src/internet/test/tcp-pacing-test.cc
--- ns-3.37/src/internet/test/tcp-pacing-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-pacing-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -30,7 +30,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test the behavior of TCP pacing
  *
@@ -373,7 +372,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TestSuite for the behavior of TCP pacing
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-pkts-acked-test.cc ns-3.38/src/internet/test/tcp-pkts-acked-test.cc
--- ns-3.37/src/internet/test/tcp-pkts-acked-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-pkts-acked-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -30,7 +30,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Check the number of times that PktsAcked is called
  *
@@ -76,7 +75,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Behaves as NewReno, except that each time PktsAcked is called,
  * a notification is sent to TcpPktsAckedOpenTest.
@@ -173,7 +171,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief PktsAcked is calls TestSuite.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-prr-recovery-test.cc ns-3.38/src/internet/test/tcp-prr-recovery-test.cc
--- ns-3.37/src/internet/test/tcp-prr-recovery-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-prr-recovery-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -145,7 +145,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief PRR Recovery TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-rate-ops-test.cc ns-3.38/src/internet/test/tcp-rate-ops-test.cc
--- ns-3.37/src/internet/test/tcp-rate-ops-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-rate-ops-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -184,7 +184,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Behaves as NewReno except HasCongControl returns true
  */
@@ -439,7 +438,7 @@
  * \ingroup internet-tests
  * \ingroup tests
  *
- * \brief The TcpRateLinuxWithBufferTest tests rate sample functionality with arbitary SACK
+ * \brief The TcpRateLinuxWithBufferTest tests rate sample functionality with arbitrary SACK
  * scenario. Check the value of delivered against a home-made guess
  */
 class TcpRateLinuxWithBufferTest : public TestCase
@@ -471,7 +470,7 @@
     /** \brief Test with acks without drop */
     void TestWithStraightAcks();
 
-    /** \brief Test with arbitary SACK scenario */
+    /** \brief Test with arbitrary SACK scenario */
     void TestWithSackBlocks();
 
     uint32_t m_expectedDelivered{0};   //!< Amount of expected delivered data
@@ -595,7 +594,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief the TestSuite for the TcpRateLinux test case
  */
@@ -647,12 +645,12 @@
 
         AddTestCase(
             new TcpRateLinuxWithBufferTest(1000,
-                                           "Checking rate sample values with arbitary SACK Block"),
+                                           "Checking rate sample values with arbitrary SACK Block"),
             TestCase::QUICK);
 
         AddTestCase(
             new TcpRateLinuxWithBufferTest(500,
-                                           "Checking rate sample values with arbitary SACK Block"),
+                                           "Checking rate sample values with arbitrary SACK Block"),
             TestCase::QUICK);
     }
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-rto-test.cc ns-3.38/src/internet/test/tcp-rto-test.cc
--- ns-3.37/src/internet/test/tcp-rto-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-rto-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -30,7 +30,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the moments after an RTO expiration
  *
@@ -107,7 +106,7 @@
     // This function is called after the management of the RTO expiration,
     // and because of this we must check all the involved variables.
     NS_TEST_ASSERT_MSG_EQ(m_afterRTOExpired, false, "Second RTO expired");
-    NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+    NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                           TcpSocketState::CA_LOSS,
                           "Ack state machine not in LOSS state after a loss");
 
@@ -125,13 +124,13 @@
 
     if (m_afterRTOExpired && who == SENDER)
     {
-        NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+        NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                               TcpSocketState::CA_LOSS,
                               "Ack state machine not in LOSS state after a loss");
     }
     else
     {
-        NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+        NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                               TcpSocketState::CA_OPEN,
                               "Ack state machine not in OPEN state after recovering "
                               "from loss");
@@ -144,7 +143,7 @@
     // Called after the ACK processing. Every time we should be in OPEN state,
     // without any packet lost or marked as retransmitted, in both the sockets
 
-    NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(tcb),
+    NS_TEST_ASSERT_MSG_EQ(tcb->m_congState.Get(),
                           TcpSocketState::CA_OPEN,
                           "Ack state machine not in OPEN state after recovering "
                           "from loss");
@@ -167,7 +166,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the ssthresh behavior after the RTO expires
  *
@@ -314,7 +312,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the timing of RTO
  *
@@ -510,7 +507,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP RTO TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-rtt-estimation.cc ns-3.38/src/internet/test/tcp-rtt-estimation.cc
--- ns-3.37/src/internet/test/tcp-rtt-estimation.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-rtt-estimation.cc	2023-03-16 20:54:23.628868600 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Check Rtt calculations
  *
@@ -192,7 +191,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Check Rtt calculations with packet losses.
  *
@@ -246,7 +244,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP RTT estimation TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-rx-buffer-test.cc ns-3.38/src/internet/test/tcp-rx-buffer-test.cc
--- ns-3.37/src/internet/test/tcp-rx-buffer-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-rx-buffer-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -296,7 +296,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief the TestSuite for the TcpRxBuffer test case
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-sack-permitted-test.cc ns-3.38/src/internet/test/tcp-sack-permitted-test.cc
--- ns-3.37/src/internet/test/tcp-sack-permitted-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-sack-permitted-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test case for checking the SACK-PERMITTED option.
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-scalable-test.cc ns-3.38/src/internet/test/tcp-scalable-test.cc
--- ns-3.37/src/internet/test/tcp-scalable-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-scalable-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -36,7 +36,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance increment on TcpScalable
  */
@@ -106,7 +105,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the multiplicative decrease on TcpScalable
  */
@@ -165,7 +163,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TcpScalable TestSuite.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-slow-start-test.cc ns-3.38/src/internet/test/tcp-slow-start-test.cc
--- ns-3.37/src/internet/test/tcp-slow-start-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-slow-start-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -30,7 +30,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test the normal behavior for slow start
  *
@@ -208,7 +207,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief A slow start test using a socket which sends smaller ACKs
  *
@@ -262,7 +260,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Slow Start TestSuite.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-syn-connection-failed-test.cc ns-3.38/src/internet/test/tcp-syn-connection-failed-test.cc
--- ns-3.37/src/internet/test/tcp-syn-connection-failed-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-syn-connection-failed-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Test that connection failed callback is called when
  * SYN retransmission number is exceeded.
@@ -96,7 +95,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-test.cc ns-3.38/src/internet/test/tcp-test.cc
--- ns-3.37/src/internet/test/tcp-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -52,7 +52,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Test - send string data from client to server and back.
  */
@@ -537,7 +536,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP TestSuite - send string data from client to server and back.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-timestamp-test.cc ns-3.38/src/internet/test/tcp-timestamp-test.cc
--- ns-3.37/src/internet/test/tcp-timestamp-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-timestamp-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP TimeStamp enabling Test.
  */
@@ -210,7 +209,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP TimeStamp values Test.
  */
@@ -291,7 +289,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP TimeStamp TestSuite.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-tx-buffer-test.cc ns-3.38/src/internet/test/tcp-tx-buffer-test.cc
--- ns-3.37/src/internet/test/tcp-tx-buffer-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-tx-buffer-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -432,7 +432,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief the TestSuite for the TcpTxBuffer test case
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-vegas-test.cc ns-3.38/src/internet/test/tcp-vegas-test.cc
--- ns-3.37/src/internet/test/tcp-vegas-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-vegas-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -202,7 +202,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Vegas TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-veno-test.cc ns-3.38/src/internet/test/tcp-veno-test.cc
--- ns-3.37/src/internet/test/tcp-veno-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-veno-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the additive increase and multiplicative decrease of TcpVeno
  */
@@ -80,7 +79,7 @@
      */
     uint32_t MultiplicativeDecrease(uint32_t diff,
                                     const UintegerValue& beta,
-                                    uint32_t bytesInFlight);
+                                    uint32_t bytesInFlight) const;
 
     /**
      * \brief Mimics the NewReno IncreaseWindow algorithm.
@@ -244,7 +243,7 @@
 uint32_t
 TcpVenoTest::MultiplicativeDecrease(uint32_t diff,
                                     const UintegerValue& beta,
-                                    uint32_t bytesInFlight)
+                                    uint32_t bytesInFlight) const
 {
     uint32_t calculatedSsThresh;
     if (diff < beta.Get())
@@ -300,7 +299,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Veno TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-wscaling-test.cc ns-3.38/src/internet/test/tcp-wscaling-test.cc
--- ns-3.37/src/internet/test/tcp-wscaling-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-wscaling-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -32,7 +32,6 @@
 // TODO: Check the buffer size and scaling option value
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Window Scaling enabling Test.
  */
@@ -226,7 +225,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Window Scaling TestSuite.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-yeah-test.cc ns-3.38/src/internet/test/tcp-yeah-test.cc
--- ns-3.37/src/internet/test/tcp-yeah-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-yeah-test.cc	2023-03-16 20:54:23.628868600 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing TcpYeah additive increase algorithm.
  */
@@ -216,7 +215,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing TcpYeah multiplicative decrease algorithm
  */
@@ -359,7 +357,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP Yeah TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/tcp-zero-window-test.cc ns-3.38/src/internet/test/tcp-zero-window-test.cc
--- ns-3.37/src/internet/test/tcp-zero-window-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/tcp-zero-window-test.cc	2023-03-16 20:54:23.632868500 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief Testing the congestion avoidance increment on TCP ZeroWindow
  */
@@ -169,10 +168,10 @@
         }
     }
 
-    NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(GetTcb(SENDER)),
+    NS_TEST_ASSERT_MSG_EQ(GetTcb(SENDER)->m_congState.Get(),
                           TcpSocketState::CA_OPEN,
                           "Sender State is not OPEN");
-    NS_TEST_ASSERT_MSG_EQ(GetCongStateFrom(GetTcb(RECEIVER)),
+    NS_TEST_ASSERT_MSG_EQ(GetTcb(RECEIVER)->m_congState.Get(),
                           TcpSocketState::CA_OPEN,
                           "Receiver State is not OPEN");
 }
@@ -249,7 +248,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief TCP ZeroWindow TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet/test/udp-test.cc ns-3.38/src/internet/test/udp-test.cc
--- ns-3.37/src/internet/test/udp-test.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/internet/test/udp-test.cc	2023-03-16 20:54:23.632868500 -0700
@@ -57,7 +57,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief UDP Socket Loopback over IPv4 Test
  */
@@ -115,7 +114,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief UDP Socket Loopback over IPv6 Test
  */
@@ -172,7 +170,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief UDP Socket over IPv4 Test
  */
@@ -524,7 +521,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief UDP Socket over IPv6 Test
  */
@@ -768,7 +764,6 @@
 
 /**
  * \ingroup internet-test
- * \ingroup tests
  *
  * \brief UDP TestSuite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/CMakeLists.txt ns-3.38/src/internet-apps/CMakeLists.txt
--- ns-3.37/src/internet-apps/CMakeLists.txt	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/CMakeLists.txt	2023-03-16 20:54:23.608868600 -0700
@@ -2,6 +2,7 @@
   LIBNAME internet-apps
   SOURCE_FILES
     helper/dhcp-helper.cc
+    helper/ping-helper.cc
     helper/ping6-helper.cc
     helper/radvd-helper.cc
     helper/v4ping-helper.cc
@@ -9,6 +10,7 @@
     model/dhcp-client.cc
     model/dhcp-header.cc
     model/dhcp-server.cc
+    model/ping.cc
     model/ping6.cc
     model/radvd-interface.cc
     model/radvd-prefix.cc
@@ -17,6 +19,7 @@
     model/v4traceroute.cc
   HEADER_FILES
     helper/dhcp-helper.h
+    helper/ping-helper.h
     helper/ping6-helper.h
     helper/radvd-helper.h
     helper/v4ping-helper.h
@@ -24,6 +27,7 @@
     model/dhcp-client.h
     model/dhcp-header.h
     model/dhcp-server.h
+    model/ping.h
     model/ping6.h
     model/radvd-interface.h
     model/radvd-prefix.h
@@ -31,6 +35,8 @@
     model/v4ping.h
     model/v4traceroute.h
   LIBRARIES_TO_LINK ${libinternet}
-  TEST_SOURCES test/dhcp-test.cc
-               test/ipv6-radvd-test.cc
+  TEST_SOURCES
+    test/dhcp-test.cc
+    test/ipv6-radvd-test.cc
+    test/ping-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/doc/internet-apps.rst ns-3.38/src/internet-apps/doc/internet-apps.rst
--- ns-3.37/src/internet-apps/doc/internet-apps.rst	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/doc/internet-apps.rst	2023-03-16 20:54:23.608868600 -0700
@@ -24,30 +24,231 @@
 clear overview of the various options and usage tricks.
 
 
-V4Ping
-******
+Ping
+****
 
-This app mimics a "ping" (ICMP Echo) using IPv4. The application allows the
-following attributes to be set:
+The ``Ping`` application supports both IPv4 and IPv6 and replaces earlier
+|ns3| implementations called ``v4Ping`` and ``Ping6`` that were
+address family dependent.  ``Ping`` was introduced in the ns-3.38 release cycle.
+
+Model Description
+=================
+
+This application behaves similarly to the Unix ``ping`` application, although
+with fewer options supported.  ``Ping`` sends ICMP Echo Request messages to
+a remote address, and collects statistics and reports on the ICMP Echo
+Reply responses that are received.  The application can be used to send
+ICMP echo requests to unicast, broadcast, and multicast IPv4 and IPv6
+addresses.  The application can produce a verbose output similar to the real
+application, and can also export statistics and results via trace sources.
+The following can be controlled via attributes of this class:
 
-* Remote address
-* Verbose mode
+* Destination address
+* Local address (sender address)
 * Packet size (default 56 bytes)
 * Packet interval  (default 1 second)
+* Timeout value (default 1 second)
+* The count, or maximum number of packets to send
+* Verbose mode
 
-Moreover, the user can access the measured RTT value (as a Traced Source).
+In practice, the real-world ``ping`` application behavior varies slightly
+depending on the operating system (Linux, macOS, Windows, etc.).  Most
+implementations also support a very large number of options.  The |ns3|
+model is intended to handle the most common use cases of testing for
+reachability.
+
+Design
+######
+
+The aim of |ns3| ``Ping`` application is to mimic the built-in application
+found in most operating systems.  In practice, ``ping`` is usually used to
+check reachability of a destination, but additional options have been
+added over time and the tool can be used in different ways to gather
+statistics about reachability and round trip times (RTT).  Since |ns3| is
+mainly used for performance studies and not for operational forensics,
+some options of real ``ping`` implementations may not be useful for simulations.
+However, the |ns3| application can deliver output and RTT samples similar
+to how the real application operates.
+
+``Ping`` is usually installed on a source node and does not require any
+|ns3| application installation on the destination node.  ``Ping`` is an
+``Application`` that can be started and stopped using the base class
+Application APIs.
+
+Behavior
+########
+
+The behavior of real ``ping`` applications varies across operating systems.  For
+example, on Linux, the first ICMP sequence number sent is one, while on
+macOS, the first sequence number is zero.  The behavior when pinging
+non-existent hosts also can differ (Linux is quiet while macOS is verbose).
+Windows and other operating systems like Cisco routers also can behave
+slightly differently.
+
+This implementation tries to generally follow the Linux behavior, except
+that it will print out a verbose 'request timed out' message when an
+echo request is sent and no reply arrives in a timely manner.  The timeout
+value (time that ping waits for a response to return) defaults to one
+second, but once there are RTT samples available, the timeout is set
+to twice the observed RTT.  In contrast to Linux (but aligned with macOS),
+the first sequence number sent is zero.
 
-Ping6
-*****
+Scope and Limitations
+#####################
 
-This app mimics a "ping" (ICMP Echo) using IPv6. The application allows the
-following attributes to be set:
+``ping`` implementations have a lot of command-line options.  The |ns3|
+implementation only supports a few of the most commonly-used options;
+patches to add additional options would be welcome.
 
-* Remote address
-* Local address (sender address)
-* Packet size (default 56 bytes)
-* Packet interval  (default 1 second)
-* Max number of packets to send
+At the present time, fragmentation (sending an ICMP Echo Request larger
+than the path MTU) is not handled correctly during Echo Response reassembly.
+
+Usage
+=====
+
+Users may create and install ``Ping`` applications on nodes on a one-by-one
+basis using ``CreateObject`` or by using the ``PingHelper``.  For
+``CreateObject``, the following can be used::
+
+  Ptr<Node> n = ...;
+  Ptr<Ping> ping = CreateObject<Ping> ();
+  // Configure ping as needed...
+  n->AddApplication (ping);
+
+Users should be aware of how this application stops.  For most |ns3|
+applications, ``StopApplication()`` should be called before the simulation
+is stopped.  If the ``Count`` attribute of this application is set to
+a positive integer, the application will stop (and a report will be printed)
+either when ``Count`` responses have been received or when ``StopApplication()``
+is called, whichever comes first.  If ``Count`` is zero, meaning infinite
+pings, then ``StopApplication()`` should be used to eventually stop the
+application and generate the report.  If ``StopApplication()`` is called
+while a packet (echo request) is in-flight, the response cannot be
+received and the packet will be treated as lost in the report-- real
+ping applications work this way as well.  To avoid this, it is recommended
+to call ``StopApplication()`` at a time when an Echo Request or Echo Response
+packet is not expected to be in flight.
+
+Helpers
+#######
+
+The ``PingHelper`` supports the typical ``Install`` usage pattern in |ns3|.
+The following sample code is from the program ``examples/tcp/tcp-validation.cc``.
+
+.. sourcecode:: cpp
+
+    PingHelper pingHelper(Ipv4Address("192.168.1.2"));
+    pingHelper.SetAttribute("Interval", TimeValue(pingInterval));
+    pingHelper.SetAttribute("Size", UintegerValue(pingSize));
+    pingHelper.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::SILENT));
+    ApplicationContainer pingContainer = pingHelper.Install(pingServer);
+    Ptr<Ping> ping = pingContainer.Get(0)->GetObject<Ping>();
+    ping->TraceConnectWithoutContext("Rtt", MakeBoundCallback(&TracePingRtt, &pingOfStream));
+    pingContainer.Start(Seconds(1));
+    pingContainer.Stop(stopTime - Seconds(1));
+
+The first statement sets the remote address (destination) for all application
+instances created with this helper.  The second and third statements perform
+further configuration.  The fourth statement configures the verbosity to
+be totally silent.  The fifth statement is a typical ``Install()``
+method that returns an ApplicationContainer (in this case, of size 1).
+The sixth and seventh statements fetch the application instance created and
+configure a trace sink (``TracePingRtt``) for the ``Rtt`` trace source.
+The eighth and ninth statements configure the start and stop time,
+respectively.
+
+The helper is most useful when there are many similarly configured
+applications to install on a collection of nodes (a NodeContainer).
+When there is only one Ping application to configure in a program,
+or when the configuration between different instances is different,
+it may be more straightforward to directly create the Ping applications
+without the PingHelper.
+
+Attributes
+##########
+
+The following attributes can be configured:
+
+* ``Destination``: The IPv4 or IPv6 address of the machine we want to ping
+* ``VerboseMode``: Configure verbose, quiet, or silent output
+* ``Interval``: Time interval between sending each packet
+* ``Size``: The number of data bytes to be sent, before ICMP and IP headers are added
+* ``Count``: The maximum number of packets the application will send
+* ``InterfaceAddress``: Local address of the sender
+* ``Timeout``: Time to wait for response if no RTT samples are available
+
+Output
+######
+
+If ``VerboseMode`` mode is set to ``VERBOSE``, ping will output the results of
+ICMP Echo Reply responses to ``std::cout`` output stream.  If the mode is
+set to ``QUIET``, only the initial statement and summary are printed.  If the
+mode is set to ``SILENT``, no output will be printed to ``std::cout``.  These
+behavioral differences can be seen with the ``ping-example.cc`` as follows:
+
+.. sourcecode:: bash
+
+  $ ./ns3 run --no-build 'ping-example --ns3::Ping::VerboseMode=Verbose'
+  $ ./ns3 run --no-build 'ping-example --ns3::Ping::VerboseMode=Quiet'
+  $ ./ns3 run --no-build 'ping-example --ns3::Ping::VerboseMode=Silent'
+
+Additional output can be gathered by using the four trace sources provided
+by Ping:
+
+* ``Tx``: This trace executes when a new packet is sent, and returns the sequence number and full packet (including ICMP header).
+* ``Rtt``:  Each time an ICMP echo reply is received, this trace is called and reports the sequence number and RTT.
+* ``Drop``:  If an ICMP error is returned instead of an echo reply, the sequence number and reason for reported drop are returned.
+* ``Report``: When ping completes and exits, it prints output statistics to the terminal.  These values are copied to a ``struct PingReport`` and returned in this trace source.
+
+Example
+#######
+
+A basic ``ping-example.cc`` program is provided to highlight the following
+usage.  The topology has three nodes interconnected by two point-to-point links.
+Each link has 5 ms one-way delay, for a round-trip propagation delay
+of 20 ms.  The transmission rate on each link is 100 Mbps.  The routing
+between links is enabled by ns-3's NixVector routing.
+
+By default, this program will send 5 pings from node A to node C.
+When using the default IPv6, the output will look like this:
+
+.. sourcecode::txt
+
+  PING 2001:1:0:1:200:ff:fe00:4 - 56 bytes of data; 104 bytes including ICMP and IPv6 headers.
+  64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=0 ttl=63 time=20.033 ms
+  64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=1 ttl=63 time=20.033 ms
+  64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=2 ttl=63 time=20.033 ms
+  64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=3 ttl=63 time=20.033 ms
+  64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=4 ttl=63 time=20.033 ms
+  --- 2001:1:0:1:200:ff:fe00:4 ping statistics ---
+  5 packets transmitted, 5 received, 0% packet loss, time 4020ms
+  rtt min/avg/max/mdev = 20/20/20/0 ms
+
+The example program will also produce four pcap traces (one for each
+NetDevice in the scenario) that can be viewed using tcpdump or Wireshark.
+
+Other program options include options to change the destination and
+source addresses, number of packets (count), packet size, interval,
+and whether to enable logging (if logging is enabled in the build).
+These program options will override any corresponding attribute settings.
+
+Finally, the program has some code that can be enabled to selectively
+force packet drops to check such behavior.
+
+Validation
+==========
+
+The following test cases have been added for regression testing:
+
+#. Unlimited pings, no losses, StopApplication () with no packets in flight
+#. Unlimited pings, no losses, StopApplication () with one packet in flight
+#. Test for operation of count attribute and exit time after all pings are received, for IPv4"
+#. Test the operation of interval attribute, for IPv4
+#. Test for behavior of pinging an unreachable host when the network does not send an ICMP unreachable message
+#. Test pinging to IPv4 broadcast address and IPv6 all nodes multicast address
+#. Test behavior of first reply lost in a count-limited configuration
+#. Test behavior of second reply lost in a count-limited configuration
+#. Test behavior of last reply lost in a count-limited configuration.
 
 Radvd
 *****
@@ -124,3 +325,7 @@
 Multiple DHCP servers can be configured, but the implementation does not support
 the use of a DHCP Relay yet.
 
+V4TraceRoute
+************
+
+Documentation is missing for this application.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/examples/CMakeLists.txt ns-3.38/src/internet-apps/examples/CMakeLists.txt
--- ns-3.37/src/internet-apps/examples/CMakeLists.txt	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/examples/CMakeLists.txt	2023-03-16 20:54:23.608868600 -0700
@@ -21,3 +21,14 @@
     ${libwifi}
     ${libinternet-apps}
 )
+
+build_lib_example(
+  NAME ping-example
+  SOURCE_FILES ping-example.cc
+  LIBRARIES_TO_LINK
+    ${libcore}
+    ${libinternet}
+    ${libinternet-apps}
+    ${libnix-vector-routing}
+    ${libpoint-to-point}
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/examples/dhcp-example.cc ns-3.38/src/internet-apps/examples/dhcp-example.cc
--- ns-3.37/src/internet-apps/examples/dhcp-example.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/examples/dhcp-example.cc	2023-03-16 20:54:23.608868600 -0700
@@ -196,4 +196,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/examples/ping-example.cc ns-3.38/src/internet-apps/examples/ping-example.cc
--- ns-3.37/src/internet-apps/examples/ping-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet-apps/examples/ping-example.cc	2023-03-16 20:54:23.608868600 -0700
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2022 Chandrakant Jena
+ *
+ * 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: Chandrakant Jena <chandrakant.barcelona@gmail.com>
+ */
+
+// Example to demonstrate the working of the Ping Application
+// Network topology:
+// A ------------------------------ B ------------------------------ C
+//             100 Mbps                           100 Mbps
+//               5 ms (one way)                     5 ms (one way)
+// IPv4 addresses:
+// 10.1.1.1    <->         10.1.1.2 / 10.1.2.1     <->         10.1.2.2
+//
+// IPv6 addresses:
+// 2001:1::200:ff:fe00:1
+//     <->    2001:1::200:ff:fe00:2 / 2001:1:0:1:200:ff:fe00:3
+//                                       <-> 2001:1:0:1:200:ff:fe00:4
+//
+// The topology has three nodes interconnected by two point-to-point links.
+// Each link has 5 ms one-way delay, for a round-trip propagation delay
+// of 20 ms.  The transmission rate on each link is 100 Mbps.  The routing
+// between links is enabled by ns-3's NixVector routing.
+//
+// By default, this program will send 5 pings from node A to node C using IPv6.
+// When using IPv6, the output will look like this:
+//
+// PING 2001:1:0:1:200:ff:fe00:4 - 56 bytes of data; 104 bytes including ICMP and IPv6 headers.
+// 64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=0 ttl=63 time=20.033 ms
+// 64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=1 ttl=63 time=20.033 ms
+// 64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=2 ttl=63 time=20.033 ms
+// 64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=3 ttl=63 time=20.033 ms
+// 64 bytes from (2001:1:0:1:200:ff:fe00:4): icmp_seq=4 ttl=63 time=20.033 ms
+//
+// --- 2001:1:0:1:200:ff:fe00:4 ping statistics ---
+// 5 packets transmitted, 5 received, 0% packet loss, time 4020ms
+// rtt min/avg/max/mdev = 20/20/20/0 ms
+//
+// When using IPv4, the output will look like this:
+//
+//   PING 10.1.2.2 - 56 bytes of data; 84 bytes including ICMP and IPv4 headers.
+//   64 bytes from (10.1.2.2): icmp_seq=0 ttl=63 time=20.027 ms
+//   64 bytes from (10.1.2.2): icmp_seq=1 ttl=63 time=20.027 ms
+//   64 bytes from (10.1.2.2): icmp_seq=2 ttl=63 time=20.027 ms
+//   64 bytes from (10.1.2.2): icmp_seq=3 ttl=63 time=20.027 ms
+//   64 bytes from (10.1.2.2): icmp_seq=4 ttl=63 time=20.027 ms
+//   --- 10.1.2.2 ping statistics ---
+//   5 packets transmitted, 5 received, 0% packet loss, time 4020ms
+//   rtt min/avg/max/mdev = 20/20/20/0 ms
+//
+// The example program will also produce four pcap traces (one for each
+// NetDevice in the scenario) that can be viewed using tcpdump or Wireshark.
+//
+// Other program options include options to change the destination and
+// source addresses, number of packets (count), packet size, interval,
+// and whether to enable logging (if logging is enabled in the build).
+//
+// The Ping application in this example starts at simulation time 1 and will
+// stop either at simulation time 50 or once 'Count' pings have been responded
+// to, whichever comes first.
+
+#include "ns3/core-module.h"
+#include "ns3/internet-apps-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/network-module.h"
+#include "ns3/nix-vector-routing-module.h"
+#include "ns3/point-to-point-module.h"
+
+#include <fstream>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("PingExample");
+
+int
+main(int argc, char* argv[])
+{
+    bool logging{false};
+    Time interPacketInterval{Seconds(1.0)};
+    uint32_t size{56};
+    uint32_t count{5};
+    std::string destinationStr;
+    Address destination;
+    std::string sourceStr;
+    Address source;
+    bool useIpv6{true};
+
+    GlobalValue::Bind("ChecksumEnabled", BooleanValue(true));
+
+    CommandLine cmd(__FILE__);
+    cmd.AddValue("logging", "Tell application to log if true", logging);
+    cmd.AddValue("interval", "The time to wait between two packets", interPacketInterval);
+    cmd.AddValue("size", "Data bytes to be sent, per-packet", size);
+    cmd.AddValue("count", "Number of packets to be sent", count);
+    cmd.AddValue("destination",
+                 "Destination IPv4 or IPv6 address, e.g., \"10.1.2.2\"",
+                 destinationStr);
+    cmd.AddValue("source",
+                 "Source address, needed only for multicast or broadcast destinations",
+                 sourceStr);
+    cmd.Parse(argc, argv);
+
+    if (!destinationStr.empty())
+    {
+        Ipv4Address v4Dst = Ipv4Address(destinationStr.c_str());
+        Ipv6Address v6Dst = Ipv6Address(destinationStr.c_str());
+        if (v4Dst.IsInitialized())
+        {
+            useIpv6 = false;
+            destination = v4Dst;
+        }
+        else if (v6Dst.IsInitialized())
+        {
+            useIpv6 = true;
+            destination = v6Dst;
+        }
+    }
+
+    if (!sourceStr.empty())
+    {
+        Ipv4Address v4Src = Ipv4Address(sourceStr.c_str());
+        Ipv6Address v6Src = Ipv6Address(sourceStr.c_str());
+        if (v4Src.IsInitialized())
+        {
+            source = v4Src;
+        }
+        else if (v6Src.IsInitialized())
+        {
+            source = v6Src;
+        }
+    }
+    if (sourceStr.empty())
+    {
+        if (useIpv6)
+        {
+            Ipv6Address v6Dst = Ipv6Address(destinationStr.c_str());
+            if (v6Dst.IsInitialized() && v6Dst.IsMulticast())
+            {
+                std::cout << "Specify a source address to use when pinging multicast addresses"
+                          << std::endl;
+                std::cout << "Program exiting..." << std::endl;
+                return 0;
+            }
+        }
+        else
+        {
+            Ipv4Address v4Dst = Ipv4Address(destinationStr.c_str());
+            if (v4Dst.IsInitialized() && (v4Dst.IsBroadcast() || v4Dst.IsMulticast()))
+            {
+                std::cout << "Specify a source address to use when pinging broadcast or multicast "
+                             "addresses"
+                          << std::endl;
+                std::cout << "Program exiting..." << std::endl;
+                return 0;
+            }
+        }
+    }
+
+    if (logging)
+    {
+        LogComponentEnableAll(LogLevel(LOG_PREFIX_TIME | LOG_PREFIX_NODE));
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
+    }
+
+    NodeContainer nodes;
+    nodes.Create(3);
+    NodeContainer link1Nodes;
+    link1Nodes.Add(nodes.Get(0));
+    link1Nodes.Add(nodes.Get(1));
+    NodeContainer link2Nodes;
+    link2Nodes.Add(nodes.Get(1));
+    link2Nodes.Add(nodes.Get(2));
+
+    PointToPointHelper pointToPoint;
+    pointToPoint.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
+    pointToPoint.SetChannelAttribute("Delay", StringValue("5ms"));
+
+    NetDeviceContainer link1Devices;
+    link1Devices = pointToPoint.Install(link1Nodes);
+    NetDeviceContainer link2Devices;
+    link2Devices = pointToPoint.Install(link2Nodes);
+
+    // The following block of code inserts an optional packet loss model
+    Ptr<PointToPointNetDevice> p2pSender = DynamicCast<PointToPointNetDevice>(link1Devices.Get(0));
+    Ptr<ReceiveListErrorModel> errorModel = CreateObject<ReceiveListErrorModel>();
+    std::list<uint32_t> dropList;
+    // Enable one or more of the below lines to force specific packet losses
+    // dropList.push_back (0);
+    // dropList.push_back (1);
+    // dropList.push_back (2);
+    // dropList.push_back (3);
+    // dropList.push_back (4);
+    // etc. (other lines may be added)
+    errorModel->SetList(dropList);
+    p2pSender->SetReceiveErrorModel(errorModel);
+
+    if (!useIpv6)
+    {
+        Ipv4NixVectorHelper nixRouting;
+        InternetStackHelper stack;
+        stack.SetRoutingHelper(nixRouting);
+        stack.SetIpv6StackInstall(false);
+        stack.Install(nodes);
+
+        Ipv4AddressHelper addressV4;
+        addressV4.SetBase("10.1.1.0", "255.255.255.0");
+        addressV4.Assign(link1Devices);
+        addressV4.NewNetwork();
+        Ipv4InterfaceContainer link2InterfacesV4 = addressV4.Assign(link2Devices);
+
+        if (destination.IsInvalid())
+        {
+            destination = link2InterfacesV4.GetAddress(1, 0);
+        }
+    }
+    else
+    {
+        Ipv6NixVectorHelper nixRouting;
+        InternetStackHelper stack;
+        stack.SetRoutingHelper(nixRouting);
+        stack.SetIpv4StackInstall(false);
+        stack.Install(nodes);
+
+        Ipv6AddressHelper addressV6;
+        addressV6.SetBase("2001:1::", 64);
+        addressV6.Assign(link1Devices);
+        addressV6.NewNetwork();
+        Ipv6InterfaceContainer link2InterfacesV6 = addressV6.Assign(link2Devices);
+
+        if (destination.IsInvalid())
+        {
+            destination = link2InterfacesV6.GetAddress(1, 1);
+        }
+    }
+
+    // Create Ping application and installing on node A
+    PingHelper pingHelper(destination, source);
+    pingHelper.SetAttribute("Interval", TimeValue(interPacketInterval));
+    pingHelper.SetAttribute("Size", UintegerValue(size));
+    pingHelper.SetAttribute("Count", UintegerValue(count));
+    ApplicationContainer apps = pingHelper.Install(nodes.Get(0));
+    apps.Start(Seconds(1));
+    apps.Stop(Seconds(50));
+
+    pointToPoint.EnablePcapAll("ping-example");
+
+    Simulator::Stop(Seconds(60.0));
+    Simulator::Run();
+    Simulator::Destroy();
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/examples/traceroute-example.cc ns-3.38/src/internet-apps/examples/traceroute-example.cc
--- ns-3.37/src/internet-apps/examples/traceroute-example.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/examples/traceroute-example.cc	2023-03-16 20:54:23.608868600 -0700
@@ -236,7 +236,7 @@
     {
         Ptr<OutputStreamWrapper> routingStream =
             Create<OutputStreamWrapper>("aodv.routes", std::ios::out);
-        aodv.PrintRoutingTableAllAt(Seconds(8), routingStream);
+        Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(8), routingStream);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/helper/ping6-helper.cc ns-3.38/src/internet-apps/helper/ping6-helper.cc
--- ns-3.37/src/internet-apps/helper/ping6-helper.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/helper/ping6-helper.cc	2023-03-16 20:54:23.608868600 -0700
@@ -21,6 +21,7 @@
 
 #include "ns3/ping6.h"
 #include "ns3/uinteger.h"
+#include "ns3/warnings.h"
 
 namespace ns3
 {
@@ -28,7 +29,9 @@
 Ping6Helper::Ping6Helper()
     : m_ifIndex(0)
 {
+    NS_WARNING_PUSH_DEPRECATED;
     m_factory.SetTypeId(Ping6::GetTypeId());
+    NS_WARNING_POP;
 }
 
 void
@@ -52,6 +55,9 @@
 ApplicationContainer
 Ping6Helper::Install(NodeContainer c)
 {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
     ApplicationContainer apps;
     for (NodeContainer::Iterator i = c.Begin(); i != c.End(); ++i)
     {
@@ -65,6 +71,8 @@
         apps.Add(client);
     }
     return apps;
+
+#pragma GCC diagnostic pop
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/helper/ping6-helper.h ns-3.38/src/internet-apps/helper/ping6-helper.h
--- ns-3.37/src/internet-apps/helper/ping6-helper.h	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/helper/ping6-helper.h	2023-03-16 20:54:23.608868600 -0700
@@ -40,6 +40,7 @@
     /**
      * \brief Constructor.
      */
+    NS_DEPRECATED_3_38("Use PingHelper instead - the attributes might have been renamed.")
     Ping6Helper();
 
     /**
@@ -74,6 +75,7 @@
      * when a node has multiple interfaces.
      * \param ifIndex interface index
      */
+    NS_DEPRECATED_3_38("Use a source address")
     void SetIfIndex(uint32_t ifIndex);
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/helper/ping-helper.cc ns-3.38/src/internet-apps/helper/ping-helper.cc
--- ns-3.37/src/internet-apps/helper/ping-helper.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet-apps/helper/ping-helper.cc	2023-03-16 20:54:23.608868600 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2022 Universita' di Firenze, Italy
+ * Copyright (c) 2008-2009 Strasbourg University (original Ping6 helper)
+ * Copyright (c) 2008 INRIA (original v4Ping helper)
+ *
+ * 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>
+ *
+ * Derived from original v4Ping helper (author: Mathieu Lacage)
+ * Derived from original ping6 helper (author: Sebastien Vincent)
+ */
+
+#include "ping-helper.h"
+
+#include "ns3/names.h"
+#include "ns3/ping.h"
+
+namespace ns3
+{
+
+PingHelper::PingHelper()
+{
+    m_factory.SetTypeId("ns3::Ping");
+}
+
+PingHelper::PingHelper(Address remote, Address local)
+{
+    m_factory.SetTypeId("ns3::Ping");
+    m_factory.Set("Destination", AddressValue(remote));
+    m_factory.Set("InterfaceAddress", AddressValue(local));
+}
+
+void
+PingHelper::SetAttribute(std::string name, const AttributeValue& value)
+{
+    m_factory.Set(name, value);
+}
+
+ApplicationContainer
+PingHelper::Install(Ptr<Node> node) const
+{
+    return ApplicationContainer(InstallPriv(node));
+}
+
+ApplicationContainer
+PingHelper::Install(std::string nodeName) const
+{
+    Ptr<Node> node = Names::Find<Node>(nodeName);
+    return ApplicationContainer(InstallPriv(node));
+}
+
+ApplicationContainer
+PingHelper::Install(NodeContainer c) const
+{
+    ApplicationContainer apps;
+    for (NodeContainer::Iterator i = c.Begin(); i != c.End(); ++i)
+    {
+        apps.Add(InstallPriv(*i));
+    }
+
+    return apps;
+}
+
+Ptr<Application>
+PingHelper::InstallPriv(Ptr<Node> node) const
+{
+    Ptr<Ping> app = m_factory.Create<Ping>();
+    node->AddApplication(app);
+
+    return app;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/helper/ping-helper.h ns-3.38/src/internet-apps/helper/ping-helper.h
--- ns-3.37/src/internet-apps/helper/ping-helper.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet-apps/helper/ping-helper.h	2023-03-16 20:54:23.608868600 -0700
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2022 Universita' di Firenze, Italy
+ * Copyright (c) 2008-2009 Strasbourg University (original Ping6 helper)
+ * Copyright (c) 2008 INRIA (original v4Ping helper)
+ *
+ * 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>
+ *
+ * Derived from original v4Ping helper (author: Mathieu Lacage)
+ * Derived from original ping6 helper (author: Sebastien Vincent)
+ */
+
+#ifndef PING_HELPER_H
+#define PING_HELPER_H
+
+#include "ns3/application-container.h"
+#include "ns3/ipv4-address.h"
+#include "ns3/ipv6-address.h"
+#include "ns3/node-container.h"
+#include "ns3/object-factory.h"
+#include "ns3/ping.h"
+
+#include <stdint.h>
+
+namespace ns3
+{
+
+/**
+ * \ingroup ping
+ * \brief Create a ping application and associate it to a node
+ *
+ * This class creates one or multiple instances of ns3::Ping and associates
+ * it/them to one/multiple node(s).
+ */
+class PingHelper
+{
+  public:
+    /**
+     * Create a PingHelper which is used to make life easier for people wanting
+     * to use ping Applications.
+     */
+    PingHelper();
+
+    /**
+     * Create a PingHelper which is used to make life easier for people wanting
+     * to use ping Applications.
+     *
+     * \param remote The address which should be pinged
+     * \param local The source address
+     */
+    PingHelper(Address remote, Address local = Address());
+
+    /**
+     * Install a Ping application on each Node in the provided NodeContainer.
+     *
+     * \param nodes The NodeContainer containing all of the nodes to get a Ping
+     *              application.
+     *
+     * \returns A list of Ping applications, one for each input node
+     */
+    ApplicationContainer Install(NodeContainer nodes) const;
+
+    /**
+     * Install a Ping application on the provided Node.  The Node is specified
+     * directly by a Ptr<Node>
+     *
+     * \param node The node to install the Ping application on.
+     *
+     * \returns An ApplicationContainer holding the Ping application created.
+     */
+    ApplicationContainer Install(Ptr<Node> node) const;
+
+    /**
+     * Install a Ping application on the provided Node.  The Node is specified
+     * by a string that must have previously been associated with a Node using the
+     * Object Name Service.
+     *
+     * \param nodeName The node to install the Ping application on.
+     *
+     * \returns An ApplicationContainer holding the Ping application created.
+     */
+    ApplicationContainer Install(std::string nodeName) const;
+
+    /**
+     * \brief Configure ping applications attribute
+     * \param name   attribute's name
+     * \param value  attribute's value
+     */
+    void SetAttribute(std::string name, const AttributeValue& value);
+
+  private:
+    /**
+     * \brief Do the actual application installation in the node
+     * \param node the node
+     * \returns a Smart pointer to the installed application
+     */
+    Ptr<Application> InstallPriv(Ptr<Node> node) const;
+    /// Object factory
+    ObjectFactory m_factory;
+};
+
+} // namespace ns3
+
+#endif /* PING_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/helper/v4ping-helper.cc ns-3.38/src/internet-apps/helper/v4ping-helper.cc
--- ns-3.37/src/internet-apps/helper/v4ping-helper.cc	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/helper/v4ping-helper.cc	2023-03-16 20:54:23.608868600 -0700
@@ -21,14 +21,17 @@
 
 #include "ns3/names.h"
 #include "ns3/v4ping.h"
+#include "ns3/warnings.h"
 
 namespace ns3
 {
 
 V4PingHelper::V4PingHelper(Ipv4Address remote)
 {
+    NS_WARNING_PUSH_DEPRECATED;
     m_factory.SetTypeId("ns3::V4Ping");
     m_factory.Set("Remote", Ipv4AddressValue(remote));
+    NS_WARNING_POP;
 }
 
 void
@@ -65,10 +68,15 @@
 Ptr<Application>
 V4PingHelper::InstallPriv(Ptr<Node> node) const
 {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
     Ptr<V4Ping> app = m_factory.Create<V4Ping>();
     node->AddApplication(app);
 
     return app;
+
+#pragma GCC diagnostic pop
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/helper/v4ping-helper.h ns-3.38/src/internet-apps/helper/v4ping-helper.h
--- ns-3.37/src/internet-apps/helper/v4ping-helper.h	2022-11-01 17:37:24.487395000 -0700
+++ ns-3.38/src/internet-apps/helper/v4ping-helper.h	2023-03-16 20:54:23.608868600 -0700
@@ -43,6 +43,7 @@
      *
      * \param remote The address which should be pinged
      */
+    NS_DEPRECATED_3_38("Use PingHelper instead - the attributes might have been renamed.")
     V4PingHelper(Ipv4Address remote);
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/ping6.cc ns-3.38/src/internet-apps/model/ping6.cc
--- ns-3.37/src/internet-apps/model/ping6.cc	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet-apps/model/ping6.cc	2023-03-16 20:54:23.608868600 -0700
@@ -42,35 +42,37 @@
 TypeId
 Ping6::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::Ping6")
-                            .SetParent<Application>()
-                            .SetGroupName("Internet-Apps")
-                            .AddConstructor<Ping6>()
-                            .AddAttribute("MaxPackets",
-                                          "The maximum number of packets the application will send",
-                                          UintegerValue(100),
-                                          MakeUintegerAccessor(&Ping6::m_count),
-                                          MakeUintegerChecker<uint32_t>())
-                            .AddAttribute("Interval",
-                                          "The time to wait between packets",
-                                          TimeValue(Seconds(1.0)),
-                                          MakeTimeAccessor(&Ping6::m_interval),
-                                          MakeTimeChecker())
-                            .AddAttribute("RemoteIpv6",
-                                          "The Ipv6Address of the outbound packets",
-                                          Ipv6AddressValue(),
-                                          MakeIpv6AddressAccessor(&Ping6::m_peerAddress),
-                                          MakeIpv6AddressChecker())
-                            .AddAttribute("LocalIpv6",
-                                          "Local Ipv6Address of the sender",
-                                          Ipv6AddressValue(),
-                                          MakeIpv6AddressAccessor(&Ping6::m_localAddress),
-                                          MakeIpv6AddressChecker())
-                            .AddAttribute("PacketSize",
-                                          "Size of packets generated",
-                                          UintegerValue(100),
-                                          MakeUintegerAccessor(&Ping6::m_size),
-                                          MakeUintegerChecker<uint32_t>());
+    static TypeId tid =
+        TypeId("ns3::Ping6")
+            .SetParent<Application>()
+            .SetGroupName("Internet-Apps")
+            .AddConstructor<Ping6>()
+            .AddAttribute(
+                "MaxPackets",
+                "The maximum number of packets the application will send (zero means infinite)",
+                UintegerValue(100),
+                MakeUintegerAccessor(&Ping6::m_count),
+                MakeUintegerChecker<uint32_t>())
+            .AddAttribute("Interval",
+                          "The time to wait between packets",
+                          TimeValue(Seconds(1.0)),
+                          MakeTimeAccessor(&Ping6::m_interval),
+                          MakeTimeChecker())
+            .AddAttribute("RemoteIpv6",
+                          "The Ipv6Address of the outbound packets",
+                          Ipv6AddressValue(),
+                          MakeIpv6AddressAccessor(&Ping6::m_peerAddress),
+                          MakeIpv6AddressChecker())
+            .AddAttribute("LocalIpv6",
+                          "Local Ipv6Address of the sender",
+                          Ipv6AddressValue(),
+                          MakeIpv6AddressAccessor(&Ping6::m_localAddress),
+                          MakeIpv6AddressChecker())
+            .AddAttribute("PacketSize",
+                          "Size of packets generated",
+                          UintegerValue(100),
+                          MakeUintegerAccessor(&Ping6::m_size),
+                          MakeUintegerChecker<uint32_t>());
     return tid;
 }
 
@@ -230,7 +232,7 @@
     m_socket->Bind(Inet6SocketAddress(src, 0));
 
     /* use Loose Routing (routing type 0) */
-    if (m_routers.size())
+    if (!m_routers.empty())
     {
         Ipv6ExtensionLooseRoutingHeader routingHeader;
         routingHeader.SetNextHeader(Ipv6Header::IPV6_ICMPV6);
@@ -246,7 +248,7 @@
 
     NS_LOG_INFO("Sent " << p->GetSize() << " bytes to " << m_peerAddress);
 
-    if (m_sent < m_count)
+    if (m_sent < m_count || m_count == 0)
     {
         ScheduleTransmit(m_interval);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/ping6.h ns-3.38/src/internet-apps/model/ping6.h
--- ns-3.37/src/internet-apps/model/ping6.h	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet-apps/model/ping6.h	2023-03-16 20:54:23.608868600 -0700
@@ -39,7 +39,8 @@
  * \ingroup ping6
  * \brief A ping6 application.
  */
-class Ping6 : public Application
+class NS_DEPRECATED_3_38("Use Ping instead - the attributes might have been renamed.") Ping6
+    : public Application
 {
   public:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/ping.cc ns-3.38/src/internet-apps/model/ping.cc
--- ns-3.37/src/internet-apps/model/ping.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet-apps/model/ping.cc	2023-03-16 20:54:23.608868600 -0700
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) 2022 Chandrakant Jena
+ * Copyright (c) 2007-2009 Strasbourg University (original Ping6 code)
+ * Copyright (c) 2008 INRIA (original Ping code)
+ *
+ * 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: Chandrakant Jena <chandrakant.barcelona@gmail.com>
+ *         Tommaso Pecorella <tommaso.pecorella@unifi.it>
+ *
+ * Derived from original v4Ping application (author: Mathieu Lacage)
+ * Derived from original ping6 application (author: Sebastien Vincent)
+ */
+
+#include "ping.h"
+
+#include "ns3/abort.h"
+#include "ns3/assert.h"
+#include "ns3/boolean.h"
+#include "ns3/enum.h"
+#include "ns3/icmpv4.h"
+#include "ns3/icmpv6-header.h"
+#include "ns3/inet-socket-address.h"
+#include "ns3/ipv4-address.h"
+#include "ns3/ipv6-extension-header.h"
+#include "ns3/ipv6-header.h"
+#include "ns3/ipv6-l3-protocol.h"
+#include "ns3/ipv6-packet-info-tag.h"
+#include "ns3/log.h"
+#include "ns3/packet.h"
+#include "ns3/socket.h"
+#include "ns3/trace-source-accessor.h"
+#include "ns3/uinteger.h"
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("Ping");
+
+NS_OBJECT_ENSURE_REGISTERED(Ping);
+
+/// This value is used to quickly identify ECHO packets generated by this app.
+constexpr uint16_t PING_ID{0xbeef};
+
+TypeId
+Ping::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::Ping")
+            .SetParent<Application>()
+            .SetGroupName("Internet-Apps")
+            .AddConstructor<Ping>()
+            .AddAttribute("Destination",
+                          "The unicast IPv4 or IPv6 address of the machine we want to ping",
+                          AddressValue(),
+                          MakeAddressAccessor(&Ping::m_destination),
+                          MakeAddressChecker())
+            .AddAttribute("VerboseMode",
+                          "Configure verbose, quiet, or silent output",
+                          EnumValue(VerboseMode::VERBOSE),
+                          MakeEnumAccessor(&Ping::m_verbose),
+                          MakeEnumChecker(VerboseMode::VERBOSE,
+                                          "Verbose",
+                                          VerboseMode::QUIET,
+                                          "Quiet",
+                                          VerboseMode::SILENT,
+                                          "Silent"))
+            .AddAttribute("Interval",
+                          "Time interval between sending each packet",
+                          TimeValue(Seconds(1)),
+                          MakeTimeAccessor(&Ping::m_interval),
+                          MakeTimeChecker())
+            .AddAttribute(
+                "Size",
+                "The number of data bytes to be sent, before ICMP and IP headers are added",
+                UintegerValue(56),
+                MakeUintegerAccessor(&Ping::m_size),
+                MakeUintegerChecker<uint32_t>(16))
+            .AddAttribute(
+                "Count",
+                "The maximum number of packets the application will send (zero means no limits)",
+                UintegerValue(0),
+                MakeUintegerAccessor(&Ping::m_count),
+                MakeUintegerChecker<uint32_t>())
+            .AddAttribute("InterfaceAddress",
+                          "Local address of the sender",
+                          AddressValue(),
+                          MakeAddressAccessor(&Ping::m_interfaceAddress),
+                          MakeAddressChecker())
+            .AddAttribute("Timeout",
+                          "Time to wait for a response if no RTT samples are available",
+                          TimeValue(Seconds(1)),
+                          MakeTimeAccessor(&Ping::m_timeout),
+                          MakeTimeChecker())
+            .AddTraceSource("Tx",
+                            "The sequence number and ICMP echo response packet.",
+                            MakeTraceSourceAccessor(&Ping::m_txTrace),
+                            "ns3::Ping::TxTrace")
+            .AddTraceSource("Rtt",
+                            "The sequence number and RTT sample.",
+                            MakeTraceSourceAccessor(&Ping::m_rttTrace),
+                            "ns3::Ping::RttTrace")
+            .AddTraceSource("Drop",
+                            "Drop events due to destination unreachable or other errors.",
+                            MakeTraceSourceAccessor(&Ping::m_dropTrace),
+                            "ns3::Ping::DropTrace")
+            .AddTraceSource("Report",
+                            "Summary report at close of application.",
+                            MakeTraceSourceAccessor(&Ping::m_reportTrace),
+                            "ns3::Ping::ReportTrace");
+    return tid;
+}
+
+Ping::Ping()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+Ping::~Ping()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+void
+Ping::DoDispose()
+{
+    NS_LOG_FUNCTION(this);
+    StopApplication();
+    m_socket = nullptr;
+    Application::DoDispose();
+}
+
+uint64_t
+Ping::GetApplicationSignature() const
+{
+    NS_LOG_FUNCTION(this);
+
+    uint64_t appSignature = GetNode()->GetId();
+    appSignature <<= 32;
+
+    Ptr<Node> node = GetNode();
+    for (uint32_t i = 0; i < node->GetNApplications(); ++i)
+    {
+        if (node->GetApplication(i) == this)
+        {
+            appSignature += i;
+            return appSignature;
+        }
+    }
+    NS_ASSERT_MSG(false, "forgot to add application to node");
+    return 0; // quiet compiler
+}
+
+void
+Ping::Receive(Ptr<Socket> socket)
+{
+    NS_LOG_FUNCTION(this << socket);
+
+    while (m_socket->GetRxAvailable() > 0)
+    {
+        Address from;
+        Ptr<Packet> packet = m_socket->RecvFrom(from);
+        uint32_t recvSize = packet->GetSize();
+        NS_LOG_DEBUG("recv " << recvSize << " bytes " << *packet);
+
+        if (InetSocketAddress::IsMatchingType(from))
+        {
+            InetSocketAddress realFrom = InetSocketAddress::ConvertFrom(from);
+            Ipv4Header ipv4Hdr;
+            packet->RemoveHeader(ipv4Hdr);
+            recvSize -= ipv4Hdr.GetSerializedSize();
+            Icmpv4Header icmp;
+            packet->RemoveHeader(icmp);
+
+            switch (icmp.GetType())
+            {
+            case Icmpv4Header::ICMPV4_ECHO_REPLY: {
+                Icmpv4Echo echo;
+                packet->RemoveHeader(echo);
+
+                if (echo.GetIdentifier() != PING_ID)
+                {
+                    return;
+                }
+
+                NS_LOG_INFO("Received Echo Reply size  = "
+                            << std::dec << recvSize << " bytes from " << realFrom.GetIpv4()
+                            << " id =  " << echo.GetIdentifier()
+                            << " seq = " << echo.GetSequenceNumber()
+                            << " TTL = " << static_cast<uint16_t>(ipv4Hdr.GetTtl()));
+
+                uint32_t dataSize = echo.GetDataSize();
+                if (dataSize < 8)
+                {
+                    NS_LOG_INFO("Packet too short, discarding");
+                    return;
+                }
+                uint8_t* buf = new uint8_t[dataSize];
+                echo.GetData(buf);
+                uint64_t appSignature = Read64(buf);
+                delete[] buf;
+
+                if (appSignature == m_appSignature)
+                {
+                    Time sendTime = m_sent.at(echo.GetSequenceNumber()).txTime;
+                    NS_ASSERT(Simulator::Now() >= sendTime);
+                    Time delta = Simulator::Now() - sendTime;
+
+                    bool dupReply = false;
+                    if (m_sent.at(echo.GetSequenceNumber()).acked)
+                    {
+                        m_duplicate++;
+                        dupReply = true;
+                    }
+                    else
+                    {
+                        m_recv++;
+                        m_sent.at(echo.GetSequenceNumber()).acked = true;
+                    }
+
+                    m_avgRtt.Update(delta.GetMilliSeconds());
+                    m_rttTrace(echo.GetSequenceNumber(), delta);
+
+                    if (m_verbose == VerboseMode::VERBOSE)
+                    {
+                        std::cout << recvSize << " bytes from " << realFrom.GetIpv4() << ":"
+                                  << " icmp_seq=" << echo.GetSequenceNumber()
+                                  << " ttl=" << static_cast<uint16_t>(ipv4Hdr.GetTtl())
+                                  << " time=" << delta.GetMicroSeconds() / 1000.0 << " ms";
+                        if (dupReply && !m_multipleDestinations)
+                        {
+                            std::cout << " (DUP!)";
+                        }
+                        std::cout << "\n";
+                    }
+                }
+
+                break;
+            }
+            case Icmpv4Header::ICMPV4_DEST_UNREACH: {
+                Icmpv4DestinationUnreachable destUnreach;
+                packet->RemoveHeader(destUnreach);
+
+                NS_LOG_INFO("Received Destination Unreachable from " << realFrom.GetIpv4());
+                break;
+            }
+            case Icmpv4Header::ICMPV4_TIME_EXCEEDED: {
+                Icmpv4TimeExceeded timeExceeded;
+                packet->RemoveHeader(timeExceeded);
+
+                NS_LOG_INFO("Received Time Exceeded from " << realFrom.GetIpv4());
+                break;
+            }
+            default:
+                break;
+            }
+        }
+        else if (Inet6SocketAddress::IsMatchingType(from))
+        {
+            Inet6SocketAddress realFrom = Inet6SocketAddress::ConvertFrom(from);
+            Ipv6Header ipv6Hdr;
+
+            // We need the IP interface index.
+            Ipv6PacketInfoTag infoTag;
+            packet->RemovePacketTag(infoTag);
+            Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol>();
+            Ipv6Address myAddr = infoTag.GetAddress();
+            int32_t ipIfIndex = ipv6->GetInterfaceForAddress(myAddr);
+
+            packet->RemoveHeader(ipv6Hdr);
+            recvSize -= ipv6Hdr.GetSerializedSize();
+            uint8_t type;
+            packet->CopyData(&type, sizeof(type));
+
+            switch (type)
+            {
+            case Icmpv6Header::ICMPV6_ECHO_REPLY: {
+                Icmpv6Echo echo(0);
+                packet->RemoveHeader(echo);
+
+                if (echo.GetId() != PING_ID)
+                {
+                    return;
+                }
+
+                NS_LOG_INFO("Received Echo Reply size  = "
+                            << std::dec << recvSize << " bytes from " << realFrom.GetIpv6()
+                            << " id =  " << echo.GetId() << " seq = " << echo.GetSeq()
+                            << " Hop Count = " << static_cast<uint16_t>(ipv6Hdr.GetHopLimit()));
+
+                uint32_t dataSize = packet->GetSize();
+                if (dataSize < 8)
+                {
+                    NS_LOG_INFO("Packet too short, discarding");
+                    return;
+                }
+                uint8_t* buf = new uint8_t[dataSize];
+                packet->CopyData(buf, dataSize);
+                uint64_t appSignature = Read64(buf);
+                delete[] buf;
+
+                if (appSignature == m_appSignature)
+                {
+                    Time sendTime = m_sent.at(echo.GetSeq()).txTime;
+                    NS_ASSERT(Simulator::Now() >= sendTime);
+                    Time delta = Simulator::Now() - sendTime;
+
+                    bool dupReply = false;
+                    if (m_sent.at(echo.GetSeq()).acked)
+                    {
+                        m_duplicate++;
+                        dupReply = true;
+                    }
+                    else
+                    {
+                        m_recv++;
+                        m_sent.at(echo.GetSeq()).acked = true;
+                    }
+
+                    m_avgRtt.Update(delta.GetMilliSeconds());
+                    m_rttTrace(echo.GetSeq(), delta);
+
+                    if (m_verbose == VerboseMode::VERBOSE)
+                    {
+                        std::cout << recvSize << " bytes from (" << realFrom.GetIpv6() << "):"
+                                  << " icmp_seq=" << echo.GetSeq()
+                                  << " ttl=" << static_cast<uint16_t>(ipv6Hdr.GetHopLimit())
+                                  << " time=" << delta.GetMicroSeconds() / 1000.0 << " ms";
+                        if (dupReply)
+                        {
+                            std::cout << " (DUP!)";
+                        }
+                        std::cout << "\n";
+                    }
+                }
+                ipv6->ReachabilityHint(ipIfIndex, realFrom.GetIpv6());
+                break;
+            }
+            case Icmpv6Header::ICMPV6_ERROR_DESTINATION_UNREACHABLE: {
+                Icmpv6DestinationUnreachable destUnreach;
+                packet->RemoveHeader(destUnreach);
+
+                NS_LOG_INFO("Received Destination Unreachable from " << realFrom.GetIpv6());
+                break;
+            }
+            case Icmpv6Header::ICMPV6_ERROR_TIME_EXCEEDED: {
+                Icmpv6TimeExceeded timeExceeded;
+                packet->RemoveHeader(timeExceeded);
+
+                NS_LOG_INFO("Received Time Exceeded from " << realFrom.GetIpv6());
+                break;
+            }
+            default:
+                break;
+            }
+        }
+    }
+
+    if (!m_multipleDestinations && m_count > 0 && m_recv == m_count)
+    {
+        Simulator::ScheduleNow(&Ping::StopApplication, this);
+    }
+}
+
+// Writes data to buffer in little-endian format; least significant byte
+// of data is at lowest buffer address
+void
+Ping::Write64(uint8_t* buffer, const uint64_t data)
+{
+    NS_LOG_FUNCTION(this << (void*)buffer << data);
+    buffer[0] = (data >> 0) & 0xff;
+    buffer[1] = (data >> 8) & 0xff;
+    buffer[2] = (data >> 16) & 0xff;
+    buffer[3] = (data >> 24) & 0xff;
+    buffer[4] = (data >> 32) & 0xff;
+    buffer[5] = (data >> 40) & 0xff;
+    buffer[6] = (data >> 48) & 0xff;
+    buffer[7] = (data >> 56) & 0xff;
+}
+
+// Read data from a little-endian formatted buffer to data
+uint64_t
+Ping::Read64(const uint8_t* buffer)
+{
+    NS_LOG_FUNCTION(this << (void*)buffer);
+    uint64_t data = buffer[7];
+    data <<= 8;
+    data |= buffer[6];
+    data <<= 8;
+    data |= buffer[5];
+    data <<= 8;
+    data |= buffer[4];
+    data <<= 8;
+    data |= buffer[3];
+    data <<= 8;
+    data |= buffer[2];
+    data <<= 8;
+    data |= buffer[1];
+    data <<= 8;
+    data |= buffer[0];
+
+    return data;
+}
+
+void
+Ping::Send()
+{
+    NS_LOG_FUNCTION(this);
+
+    NS_LOG_INFO("m_seq=" << m_seq);
+
+    // Prepare the payload.
+    // We must write quantities out in some form of network order.  Since there
+    // isn't an htonl to work with we just follow the convention in pcap traces
+    // (where any difference would show up anyway) and borrow that code.  Don't
+    // be too surprised when you see that this is a little endian convention.
+    //
+    uint8_t* data = new uint8_t[m_size];
+    memset(data, 0, m_size);
+    NS_ASSERT_MSG(m_size >= 16, "ECHO Payload size must be at least 16 bytes");
+
+    Write64(data, m_appSignature);
+    Ptr<Packet> dataPacket = Create<Packet>(data, m_size);
+
+    Ptr<Packet> p = Create<Packet>();
+    int returnValue = 0;
+
+    if (!m_useIpv6)
+    {
+        // Using IPv4
+        Icmpv4Echo echo;
+        echo.SetSequenceNumber(m_seq);
+        echo.SetIdentifier(PING_ID);
+
+        // In the Icmpv4Echo the payload is part of the header.
+        echo.SetData(dataPacket);
+
+        p->AddHeader(echo);
+        Icmpv4Header header;
+        header.SetType(Icmpv4Header::ICMPV4_ECHO);
+        header.SetCode(0);
+        if (Node::ChecksumEnabled())
+        {
+            header.EnableChecksum();
+        }
+        p->AddHeader(header);
+        returnValue =
+            m_socket->SendTo(p, 0, InetSocketAddress(Ipv4Address::ConvertFrom(m_destination), 0));
+    }
+    else
+    {
+        // Using IPv6
+        Icmpv6Echo echo(true);
+
+        echo.SetSeq(m_seq);
+        echo.SetId(PING_ID);
+
+        // In the Icmpv6Echo the payload is just the content of the packet.
+        p = dataPacket->Copy();
+
+        p->AddHeader(echo);
+
+        /* use Loose Routing (routing type 0) */
+        if (!m_routers.empty())
+        {
+            Ipv6ExtensionLooseRoutingHeader routingHeader;
+            routingHeader.SetNextHeader(Ipv6Header::IPV6_ICMPV6);
+            routingHeader.SetTypeRouting(0);
+            routingHeader.SetSegmentsLeft(m_routers.size());
+            routingHeader.SetRoutersAddress(m_routers);
+            p->AddHeader(routingHeader);
+            m_socket->SetAttribute("Protocol", UintegerValue(Ipv6Header::IPV6_EXT_ROUTING));
+        }
+
+        returnValue =
+            m_socket->SendTo(p, 0, Inet6SocketAddress(Ipv6Address::ConvertFrom(m_destination), 0));
+
+        // Loose routing could have changed (temporarily) the protocol. Set it again to receive the
+        // replies.
+        m_socket->SetAttribute("Protocol", UintegerValue(Ipv6Header::IPV6_ICMPV6));
+    }
+    if (returnValue > 0)
+    {
+        m_sent.emplace_back(Simulator::Now(), false);
+        m_txTrace(m_seq, p);
+    }
+    else
+    {
+        NS_LOG_INFO("Send failure; socket return value: " << returnValue);
+    }
+    m_seq++;
+    delete[] data;
+
+    if (m_count == 0 || m_seq < m_count)
+    {
+        m_next = Simulator::Schedule(m_interval, &Ping::Send, this);
+    }
+
+    // We have sent all the requests. Schedule a shutdown after the linger time
+    if (m_count > 0 && m_seq == m_count)
+    {
+        Time lingerTime = m_avgRtt.Count() > 0 ? MilliSeconds(2 * m_avgRtt.Max()) : m_timeout;
+        Simulator::Schedule(lingerTime, &Ping::StopApplication, this);
+    }
+}
+
+void
+Ping::StartApplication()
+{
+    NS_LOG_FUNCTION(this);
+
+    if (m_destination.IsInvalid())
+    {
+        NS_ABORT_MSG("Destination Address value must be set when starting application");
+    }
+
+    m_appSignature = GetApplicationSignature();
+
+    m_started = Simulator::Now();
+    m_reportPrinted = false;
+    if (m_verbose == VerboseMode::VERBOSE || m_verbose == VerboseMode::QUIET)
+    {
+        if (Ipv4Address::IsMatchingType(m_destination))
+        {
+            InetSocketAddress realFrom = Ipv4Address::ConvertFrom(m_destination);
+            std::cout << "PING " << realFrom.GetIpv4() << " - " << m_size << " bytes of data; "
+                      << m_size + 28 << " bytes including ICMP and IPv4 headers.\n";
+        }
+        else if (Ipv6Address::IsMatchingType(m_destination))
+        {
+            Inet6SocketAddress realFrom = Ipv6Address::ConvertFrom(m_destination);
+            std::cout << "PING " << realFrom.GetIpv6() << " - " << m_size << " bytes of data; "
+                      << m_size + 48 << " bytes including ICMP and IPv6 headers.\n";
+        }
+        else
+        {
+            NS_ABORT_MSG("Invalid Address");
+        }
+    }
+
+    if (Ipv4Address::IsMatchingType(m_destination))
+    {
+        m_socket =
+            Socket::CreateSocket(GetNode(), TypeId::LookupByName("ns3::Ipv4RawSocketFactory"));
+        NS_ASSERT_MSG(m_socket, "Ping::StartApplication: can not create socket.");
+        m_socket->SetAttribute("Protocol", UintegerValue(1)); // icmp
+        m_socket->SetRecvCallback(MakeCallback(&Ping::Receive, this));
+        m_useIpv6 = false;
+
+        Ipv4Address dst = Ipv4Address::ConvertFrom(m_destination);
+        m_multipleDestinations = dst.IsMulticast() || dst.IsBroadcast();
+    }
+    else if (Ipv6Address::IsMatchingType(m_destination))
+    {
+        m_socket =
+            Socket::CreateSocket(GetNode(), TypeId::LookupByName("ns3::Ipv6RawSocketFactory"));
+        NS_ASSERT_MSG(m_socket, "Ping::StartApplication: can not create socket.");
+        m_socket->SetAttribute("Protocol", UintegerValue(Ipv6Header::IPV6_ICMPV6));
+        m_socket->SetRecvCallback(MakeCallback(&Ping::Receive, this));
+        m_socket->SetRecvPktInfo(true);
+        m_useIpv6 = true;
+
+        Ipv6Address dst = Ipv6Address::ConvertFrom(m_destination);
+        m_multipleDestinations = dst.IsMulticast();
+    }
+    else
+    {
+        NS_ABORT_MSG("Destination Address value must be of type Ipv4 or Ipv6");
+    }
+
+    if (!m_interfaceAddress.IsInvalid())
+    {
+        if (Ipv4Address::IsMatchingType(m_interfaceAddress))
+        {
+            InetSocketAddress senderInet =
+                InetSocketAddress(Ipv4Address::ConvertFrom(m_interfaceAddress));
+            int status = m_socket->Bind(senderInet);
+            NS_ASSERT_MSG(status == 0, "Failed to bind IPv4 socket");
+        }
+        else if (Ipv6Address::IsMatchingType(m_interfaceAddress))
+        {
+            Inet6SocketAddress senderInet =
+                Inet6SocketAddress(Ipv6Address::ConvertFrom(m_interfaceAddress));
+            int status = m_socket->Bind(senderInet);
+            NS_ASSERT_MSG(status == 0, "Failed to bind IPv6 socket");
+        }
+        else
+        {
+            NS_ABORT_MSG("Sender Address value must be of type Ipv4 or Ipv6");
+        }
+    }
+
+    // Guess how large should be the data storage and pre-book it.
+    if (m_count == 0)
+    {
+        Time delta = m_stopTime - Now();
+        int64_t guessedTx = Div(delta, m_interval) + 1;
+        m_sent.reserve(guessedTx);
+    }
+    else
+    {
+        m_sent.reserve(m_count);
+    }
+
+    Send();
+}
+
+void
+Ping::StopApplication()
+{
+    NS_LOG_FUNCTION(this);
+    if (m_stopEvent.IsRunning())
+    {
+        m_stopEvent.Cancel();
+    }
+    if (m_next.IsRunning())
+    {
+        m_next.Cancel();
+    }
+    if (m_socket)
+    {
+        m_socket->Close();
+    }
+    PrintReport();
+}
+
+void
+Ping::PrintReport()
+{
+    if (m_reportPrinted)
+    {
+        return;
+    }
+    m_reportPrinted = true;
+
+    if (m_verbose == VerboseMode::VERBOSE || m_verbose == VerboseMode::QUIET)
+    {
+        std::ostringstream os;
+        os.precision(4);
+        if (Ipv4Address::IsMatchingType(m_destination))
+        {
+            InetSocketAddress realFrom = Ipv4Address::ConvertFrom(m_destination);
+            os << "\n--- " << realFrom.GetIpv4() << " ping statistics ---\n";
+        }
+        else if (Ipv6Address::IsMatchingType(m_destination))
+        {
+            Inet6SocketAddress realFrom = Ipv6Address::ConvertFrom(m_destination);
+            os << "\n--- " << realFrom.GetIpv6() << " ping statistics ---\n";
+        }
+        os << m_seq << " packets transmitted, " << m_recv << " received, ";
+        if (m_duplicate)
+        {
+            os << m_duplicate << " duplicates, ";
+        }
+
+        // note: integer math to match Linux implementation and avoid turning a 99.9% into a 100%.
+        os << ((m_seq - m_recv) * 100 / m_seq) << "% packet loss, "
+           << "time " << (Simulator::Now() - m_started).GetMilliSeconds() << "ms\n";
+
+        if (m_avgRtt.Count() > 0)
+        {
+            os << "rtt min/avg/max/mdev = " << m_avgRtt.Min() << "/" << m_avgRtt.Avg() << "/"
+               << m_avgRtt.Max() << "/" << m_avgRtt.Stddev() << " ms\n";
+        }
+        std::cout << os.str();
+    }
+    PingReport report;
+    report.m_transmitted = m_seq;
+    report.m_received = m_recv;
+    // note: integer math to match Linux implementation and avoid turning a 99.9% into a 100%.
+    report.m_loss = (m_seq - m_recv) * 100 / m_seq;
+    report.m_duration = (Simulator::Now() - m_started);
+    report.m_rttMin = m_avgRtt.Min();
+    report.m_rttAvg = m_avgRtt.Avg();
+    report.m_rttMax = m_avgRtt.Max();
+    report.m_rttMdev = m_avgRtt.Stddev();
+    m_reportTrace(report);
+}
+
+void
+Ping::SetRouters(const std::vector<Ipv6Address>& routers)
+{
+    m_routers = routers;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/ping.h ns-3.38/src/internet-apps/model/ping.h
--- ns-3.37/src/internet-apps/model/ping.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet-apps/model/ping.h	2023-03-16 20:54:23.608868600 -0700
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2022 Chandrakant Jena
+ * Copyright (c) 2007-2009 Strasbourg University (original Ping6 code)
+ * Copyright (c) 2008 INRIA (original v4Ping code)
+ *
+ * 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: Chandrakant Jena <chandrakant.barcelona@gmail.com>
+ *
+ * Derived from original v4Ping application (author: Mathieu Lacage)
+ * Derived from original ping6 application (author: Sebastien Vincent)
+ */
+
+#ifndef PING_H
+#define PING_H
+
+#include "ns3/application.h"
+#include "ns3/average.h"
+#include "ns3/traced-callback.h"
+
+#include <map>
+
+namespace ns3
+{
+
+class Socket;
+
+/**
+ * \ingroup internet-apps
+ * \defgroup ping Ping
+ */
+
+/**
+ * \ingroup ping
+ *
+ * This application behaves similarly to the Unix ping application, although
+ * with fewer options supported.  The application can be used to send
+ * ICMP echo requests to unicast IPv4 and IPv6 addresses.
+ * The application can produce a verbose output similar to the real
+ * application, and can also export statistics via a trace source.
+ * The ping packet count, packet size, and interval between pings can
+ * be controlled via attributes of this class.
+ */
+class Ping : public Application
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    /**
+     * \enum VerboseMode
+     * \brief Encode three possible levels of verbose output
+     */
+    enum VerboseMode
+    {
+        VERBOSE = 0, //!< Verbose output (similar to real ping output)
+        QUIET = 1,   //!< Quiet output (similar to real 'ping -q' output)
+        SILENT = 2,  //!< Silent output (no terminal output at all)
+    };
+
+    /**
+     * \enum DropReason
+     * \brief Reason why a ping was dropped
+     */
+    enum DropReason
+    {
+        DROP_TIMEOUT = 0,      //!< Response timed out
+        DROP_HOST_UNREACHABLE, //!< Received ICMP Destination Host Unreachable
+        DROP_NET_UNREACHABLE,  //!< Received ICMP Destination Network Unreachable
+    };
+
+    /**
+     * A ping report provides all of the data that is typically output to the
+     * terminal when the application stops, including number sent and received
+     * and the RTT statistics.
+     */
+    struct PingReport
+    {
+        uint32_t m_transmitted{0}; //!< Number of echo requests sent
+        uint32_t m_received{0};    //!< Number of echo replies received
+        uint16_t m_loss{0};        //!< Percentage of lost packets (decimal value 0-100)
+        Time m_duration{0};        //!< Duration of the application
+        double m_rttMin{0};        //!< rtt min value
+        double m_rttAvg{0};        //!< rtt avg value
+        double m_rttMax{0};        //!< rtt max value
+        double m_rttMdev{0};       //!< rtt mdev value
+    };
+
+    /**
+     * Constructor
+     */
+    Ping();
+
+    /**
+     * Destructor
+     */
+    ~Ping() override;
+
+    /**
+     * \brief Set routers for IPv6 routing type 0 (loose routing).
+     * \param routers routers addresses
+     */
+    void SetRouters(const std::vector<Ipv6Address>& routers);
+
+    /**
+     * TracedCallback signature for Rtt trace
+     *
+     * \param [in] seq The ICMP sequence number
+     * \param [in] p The ICMP echo request packet (including ICMP header)
+     */
+    typedef void (*TxTrace)(uint16_t seq, Ptr<const Packet> p);
+
+    /**
+     * TracedCallback signature for Rtt trace
+     *
+     * \param [in] seq The ICMP sequence number
+     * \param [in] rtt The reported RTT
+     */
+    typedef void (*RttTrace)(uint16_t seq, Time rtt);
+
+    /**
+     * TracedCallback signature for Drop trace
+     *
+     * \param [in] seq The ICMP sequence number
+     * \param [in] reason The reason for the reported drop
+     */
+    typedef void (*DropTrace)(uint16_t seq, DropReason reason);
+
+    /**
+     * TracedCallback signature for Report trace
+     *
+     * \param [in] report The report information
+     */
+    typedef void (*ReportTrace)(const struct PingReport& report);
+
+  private:
+    /**
+     * \brief Writes data to buffer in little-endian format.
+     *
+     * Least significant byte of data is at lowest buffer address
+     *
+     * \param[out] buffer the buffer to write to
+     * \param[in] data the data to write
+     */
+    void Write64(uint8_t* buffer, const uint64_t data);
+
+    /**
+     * \brief Writes data from a little-endian formatted buffer to data.
+     *
+     * \param buffer the buffer to read from
+     * \return the read data
+     */
+    uint64_t Read64(const uint8_t* buffer);
+
+    // inherited from Application base class.
+    void StartApplication() override;
+    void StopApplication() override;
+    void DoDispose() override;
+
+    /**
+     * \brief Return the application signatiure.
+     * \returns the application signature.
+     *
+     * The application signature is the NodeId concatenated with the
+     * application index in the node.
+     */
+    uint64_t GetApplicationSignature() const;
+
+    /**
+     * \brief Receive an ICMPv4 or an ICMPv6 Echo reply
+     * \param socket the receiving socket
+     *
+     * This function is called by lower layers through a callback.
+     */
+    void Receive(Ptr<Socket> socket);
+
+    /**
+     * \brief Send one Ping (ICMPv4 ECHO or ICMPv6 ECHO) to the destination
+     */
+    void Send();
+
+    /**
+     * Print the report
+     */
+    void PrintReport();
+
+    /// Sender Local Address
+    Address m_interfaceAddress;
+    /// Remote address
+    Address m_destination;
+    /// Wait interval between ECHO requests
+    Time m_interval{Seconds(1)};
+
+    /**
+     * Specifies the number of data bytes to be sent.
+     * The default is 56, which translates into 64 ICMP data bytes when combined with the 8 bytes of
+     * ICMP header data.
+     */
+    uint32_t m_size{56};
+    /// The socket we send packets from
+    Ptr<Socket> m_socket;
+    /// ICMP ECHO sequence number
+    uint16_t m_seq{0};
+    /// Callbacks for tracing the packet Tx events
+    TracedCallback<uint16_t, Ptr<Packet>> m_txTrace;
+    /// TracedCallback for RTT samples
+    TracedCallback<uint16_t, Time> m_rttTrace;
+    /// TracedCallback for drop events
+    TracedCallback<uint16_t, DropReason> m_dropTrace;
+    /// TracedCallback for final ping report
+    TracedCallback<const struct PingReport&> m_reportTrace;
+    /// Variable to stor verbose mode
+    VerboseMode m_verbose{VerboseMode::VERBOSE};
+    /// Received packets counter
+    uint32_t m_recv{0};
+    /// Duplicate packets counter
+    uint32_t m_duplicate{0};
+    /// Start time to report total ping time
+    Time m_started;
+    /// Average rtt is ms
+    Average<double> m_avgRtt;
+    /// Next packet will be sent
+    EventId m_next;
+
+    /**
+     * \brief Sent echo request data.
+     */
+    class EchoRequestData
+    {
+      public:
+        /**
+         * Constructor.
+         * \param txTimePar Echo request Tx time.
+         * \param ackedPar True if the Echo request has been acknowledged at least once.
+         */
+        EchoRequestData(Time txTimePar, bool ackedPar)
+            : txTime(txTimePar),
+              acked(ackedPar)
+        {
+        }
+
+        Time txTime;       //!< Tx time
+        bool acked{false}; //!< True if packet has been acknowledged
+    };
+
+    /// All sent but not answered packets. Map icmp seqno -> when sent, acked at least once.
+    std::vector<EchoRequestData> m_sent;
+    /// Number of packets to be sent.
+    uint32_t m_count{0};
+    /// Time to wait for a response, in seconds. The option affects only timeout in absence of any
+    /// responses, otherwise ping waits for two RTTs
+    Time m_timeout{Seconds(1)};
+    /// True if the report has been printed already.
+    bool m_reportPrinted{false};
+    /// Use IPv4 (false) or IPv6 (true)
+    bool m_useIpv6{false};
+    /// Destination is Broadcast or Multicast
+    bool m_multipleDestinations{false};
+
+    /// Routers addresses for IPv6 routing type 0.
+    std::vector<Ipv6Address> m_routers;
+
+    /// App signature: ID of the node where the app is installed || ID of the Application.
+    uint64_t m_appSignature{0};
+};
+
+} // namespace ns3
+
+#endif /* PING_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/radvd-interface.cc ns-3.38/src/internet-apps/model/radvd-interface.cc
--- ns-3.37/src/internet-apps/model/radvd-interface.cc	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet-apps/model/radvd-interface.cc	2023-03-16 20:54:23.608868600 -0700
@@ -397,7 +397,7 @@
 }
 
 bool
-RadvdInterface::IsInitialRtrAdv()
+RadvdInterface::IsInitialRtrAdv() const
 {
     return m_initialRtrAdvertisementsLeft;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/radvd-interface.h ns-3.38/src/internet-apps/model/radvd-interface.h
--- ns-3.37/src/internet-apps/model/radvd-interface.h	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet-apps/model/radvd-interface.h	2023-03-16 20:54:23.608868600 -0700
@@ -325,7 +325,7 @@
      * \brief Checks if the interface is subject to the initial Rtr Advertisements rule.
      * \returns true if the initial Rtr Advertisements counter is greater than zero.
      */
-    bool IsInitialRtrAdv();
+    bool IsInitialRtrAdv() const;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/v4ping.h ns-3.38/src/internet-apps/model/v4ping.h
--- ns-3.37/src/internet-apps/model/v4ping.h	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet-apps/model/v4ping.h	2023-03-16 20:54:23.608868600 -0700
@@ -39,7 +39,8 @@
  *
  * Note: The RTT calculated is reported through a trace source.
  */
-class V4Ping : public Application
+class NS_DEPRECATED_3_38("Use Ping instead - the attributes might have been renamed.") V4Ping
+    : public Application
 {
   public:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/v4traceroute.cc ns-3.38/src/internet-apps/model/v4traceroute.cc
--- ns-3.37/src/internet-apps/model/v4traceroute.cc	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet-apps/model/v4traceroute.cc	2023-03-16 20:54:23.608868600 -0700
@@ -237,7 +237,7 @@
             uint8_t data[8];
             timeoutResp.GetData(data);
 
-            // Get the 7th and 8th Octect to obtain the Sequence number from
+            // Get the 7th and 8th Octet to obtain the Sequence number from
             // the original packet.
             uint16_t recvSeq;
             recvSeq = (uint16_t)data[7] << 0;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/model/v4traceroute.h ns-3.38/src/internet-apps/model/v4traceroute.h
--- ns-3.37/src/internet-apps/model/v4traceroute.h	2022-11-01 17:37:24.491395000 -0700
+++ ns-3.38/src/internet-apps/model/v4traceroute.h	2023-03-16 20:54:23.608868600 -0700
@@ -118,7 +118,7 @@
     uint16_t m_maxProbes;
     /// The current TTL value
     uint16_t m_ttl;
-    /// The maximium Ttl (Max number of hops to trace)
+    /// The maximum Ttl (Max number of hops to trace)
     uint32_t m_maxTtl;
     /// The wait time until the response is considered lost.
     Time m_waitIcmpReplyTimeout;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/internet-apps/test/ping-test.cc ns-3.38/src/internet-apps/test/ping-test.cc
--- ns-3.37/src/internet-apps/test/ping-test.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/internet-apps/test/ping-test.cc	2023-03-16 20:54:23.612868500 -0700
@@ -0,0 +1,942 @@
+/*
+ * Copyright (c) 2022 Chandrakant Jena
+ * Copyright (c) 2022 Universita' di Firenze, Italy
+ * Copyright (c) 2022 Tom Henderson
+ *
+ * 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: Chandrakant Jena <chandrakant.barcelona@gmail.com>
+ *          Tommaso Pecorella <tommaso.pecorella@unifi.it>
+ *          Tom Henderson <tomh@tomh.org>
+ */
+
+// This test suite performs regression tests on the ns-3 Ping application.
+//
+// A simple topology is used:
+//
+// Node 0               Node 1
+//   +----SimpleChannel---+
+//
+// - the SimpleChannel implements a one-way delay of 10 ms
+// - each node has a SimpleNetDevice with a DataRate of 1 Gb/s
+// - a ReceiveErrorModel can be configured to force packet drops
+//
+// The following are described in more detail below, inline with the test cases.
+// Most tests listed below will test both IPv4 and IPv6 operation.
+//
+// 1. Unlimited pings, no losses, StopApplication () with no packets in flight
+// 2. Unlimited pings, no losses, StopApplication () with 1 packet in flight
+// 3. Test for operation of count attribute and exit time after all pings were received
+// 4. Test the operation of the interval attribute
+// 5. Test for behavior of pinging an unreachable host when the
+//    network does not send an ICMP unreachable message.
+// 6. Test pinging to IPv4 broadcast address and IPv6 all nodes multicast address
+// 7. Test behavior of first reply lost in a count-limited configuration
+// 8. Test behavior of second reply lost in a count-limited configuration
+// 9. Test behavior of last reply lost in a count-limited configuration.
+
+#include "ns3/boolean.h"
+#include "ns3/data-rate.h"
+#include "ns3/error-model.h"
+#include "ns3/internet-stack-helper.h"
+#include "ns3/ipv4-address-helper.h"
+#include "ns3/ipv4-interface-container.h"
+#include "ns3/ipv6-address-helper.h"
+#include "ns3/ipv6-interface-container.h"
+#include "ns3/log.h"
+#include "ns3/neighbor-cache-helper.h"
+#include "ns3/node-container.h"
+#include "ns3/nstime.h"
+#include "ns3/ping.h"
+#include "ns3/simple-net-device-helper.h"
+#include "ns3/simple-net-device.h"
+#include "ns3/simulator.h"
+#include "ns3/test.h"
+#include "ns3/uinteger.h"
+
+#include <list>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("PingTestSuite");
+
+constexpr bool USEIPV6_FALSE = false;
+constexpr bool USEIPV6_TRUE = true;
+
+/**
+ * \ingroup ping
+ * \defgroup ping-test ping tests
+ */
+
+/**
+ * \ingroup ping-test
+ * \ingroup tests
+ *
+ * \brief ping basic tests
+ */
+class PingTestCase : public TestCase
+{
+  public:
+    /**
+     * Constructor.
+     * \param name TestCase name.
+     * \param useIpv6 Use IPv6.
+     */
+    PingTestCase(std::string name, bool useIpv6);
+
+    /**
+     * Set the Simulation stop time.
+     * \param stopTime The simulation stop time.
+     */
+    void SetSimulatorStopTime(Time stopTime);
+
+    /**
+     * Set the destination address (either IPv4 or IPv6).
+     * \param address The destination address.
+     */
+    void SetDestinationAddress(Address address);
+
+    /**
+     * Set the PING start time.
+     * \param startTime Ping start time.
+     */
+    void SetStartTime(Time startTime)
+    {
+        m_startTime = startTime;
+    }
+
+    /**
+     * Set the PING stop time.
+     * \param stopTime Ping stop time.
+     */
+    void SetStopTime(Time stopTime)
+    {
+        m_stopTime = stopTime;
+    }
+
+    /**
+     * Set the number of pings to send.
+     * \param count Number of pings to send.
+     */
+    void SetCount(uint32_t count)
+    {
+        m_countAttribute = count;
+    }
+
+    /**
+     * Set the size of pings.
+     * \param size Size of pings.
+     */
+    void SetSize(uint32_t size)
+    {
+        m_sizeAttribute = size;
+    }
+
+    /**
+     * Set the interval of pings.
+     * \param interval Interval of pings.
+     */
+    void SetInterval(Time interval)
+    {
+        m_interpacketInterval = interval;
+    }
+
+    /**
+     * Set the packet drop list on the Ping node's interface
+     * \param dropList packet drop list
+     */
+    void SetDropList(const std::list<uint32_t>& dropList)
+    {
+        m_dropList = dropList;
+    }
+
+    /**
+     * Enable the check on Tx pings counted in Tx trace source.
+     * \param expectedTx Expected Tx.
+     */
+    void CheckTraceTx(uint32_t expectedTx);
+
+    /**
+     * Enable the check on Rtt event count in Rtt trace source.
+     * \param expectedRtt Expected Rtt.
+     */
+    void CheckTraceRtt(uint32_t expectedRtt);
+
+    /**
+     * Enable the check on Tx pings.
+     * \param expectedReportTx Expected Tx.
+     */
+    void CheckReportTransmitted(uint32_t expectedReportTx);
+
+    /**
+     * Enable the check on Rx pings.
+     * \param expectedReportRx Expected Rx.
+     */
+    void CheckReportReceived(uint32_t expectedReportRx);
+
+    /**
+     * Enable the check on Lost pings.
+     * \param expectedReportLoss Expected Lost.
+     */
+    void CheckReportLoss(uint16_t expectedReportLoss);
+
+    /**
+     * Enable the check on average RTT.
+     * \param expectedTime Expected RTT.
+     */
+    void CheckReportTime(Time expectedTime);
+
+  private:
+    void DoSetup() override;
+    void DoTeardown() override;
+    void DoRun() override;
+
+    /**
+     * Trace TX events.
+     * \param seq Sequence number.
+     * \param p Tx packet.
+     */
+    void TxTraceSink(uint16_t seq, Ptr<Packet> p);
+
+    /**
+     * Trace RTT events.
+     * \param seq Sequence number.
+     * \param rttSample RTT sample.
+     */
+    void RttTraceSink(uint16_t seq, Time rttSample);
+
+    /**
+     * Trace Drop events.
+     * \param seq Sequence number.
+     * \param reason Drop reason.
+     */
+    void DropTraceSink(uint16_t seq, Ping::DropReason reason);
+
+    /**
+     * Trace Report generation events.
+     * \param report The report sample.
+     */
+    void ReportTraceSink(const Ping::PingReport& report);
+
+    Address m_destination{Ipv4Address("10.0.0.2")}; //!< Destination address
+    uint32_t m_mtu{1500};                           //!< Link MTU
+    uint32_t m_countAttribute{0};                   //!< Number of pings to send
+    uint32_t m_sizeAttribute{56};                   //!< Size of pings
+    // The following are for setting expected counts for traced events
+    uint32_t m_expectedTraceTx{0};  //!< Expected Tx trace sink calls
+    uint32_t m_expectedTraceRtt{0}; //!< Expected Rtt trace sink calls
+    // The following are counters for traced events
+    uint32_t m_countTraceTx{0};  //!< Tx trace counter
+    uint32_t m_countTraceRtt{0}; //!< Rtt trace counter
+    // The following indicate whether trace counters should be checked
+    bool m_checkTraceTx{false};  //!< Set to true to check the Tx number
+    bool m_checkTraceRtt{false}; //!< Set to true to check the Rtt number
+    // The following are for reported values in the final report
+    uint32_t m_expectedReportTx{0};   //!< Expected reported Tx
+    uint32_t m_expectedReportRx{0};   //!< Expected reported Rx
+    uint16_t m_expectedReportLoss{0}; //!< Expected reported Loss
+    Time m_expectedReportTime;        //!< Expected reported time
+    // The following indicate whether report quantities should be checked
+    bool m_checkReportTransmitted{false}; //!< Set to true to check the Tx number
+    bool m_checkReportReceived{false};    //!< Set to true to check the Rx number
+    bool m_checkReportLoss{false};        //!< Set to true to check the Loss number
+    bool m_checkReportTime{false};        //!< Set to true to check the Time
+
+    Time m_startTime{Seconds(1)};             //!< Start time
+    Time m_stopTime{Seconds(5)};              //!< Stop time
+    Time m_simulatorStopTime{Seconds(6)};     //!< Simulator stop time
+    bool m_useIpv6{false};                    //!< Use IPv6 (true) or IPv4 (false)
+    Time m_interpacketInterval{Seconds(1.0)}; //!< Time between pings
+    Time m_lastTx;                            //!< Last ping Tx time
+    std::list<uint32_t> m_dropList;           //!< Drop first reply (true)
+
+    NodeContainer m_nodes;                   //!< The simulation nodes
+    Ipv4InterfaceContainer m_ipv4Interfaces; //!< The IPv4 interfaces
+    Ipv6InterfaceContainer m_ipv6Interfaces; //!< The IPv6 interfaces
+    NetDeviceContainer m_devices;            //!< The NetDevices
+};
+
+PingTestCase::PingTestCase(std::string name, bool useIpv6)
+    : TestCase(name),
+      m_useIpv6(useIpv6)
+{
+}
+
+void
+PingTestCase::DoSetup()
+{
+    NS_LOG_FUNCTION(this);
+    m_nodes.Create(2);
+    SimpleNetDeviceHelper deviceHelper;
+    deviceHelper.SetChannel("ns3::SimpleChannel", "Delay", TimeValue(MilliSeconds(10)));
+    deviceHelper.SetDeviceAttribute("DataRate", DataRateValue(DataRate("1Gbps")));
+    deviceHelper.SetNetDevicePointToPointMode(true);
+    m_devices = deviceHelper.Install(m_nodes);
+    // MTU is not an attribute, so set it directly
+    Ptr<SimpleNetDevice> device0 = DynamicCast<SimpleNetDevice>(m_devices.Get(0));
+    device0->SetMtu(m_mtu);
+    Ptr<SimpleNetDevice> device1 = DynamicCast<SimpleNetDevice>(m_devices.Get(1));
+    device1->SetMtu(m_mtu);
+
+    InternetStackHelper internetHelper;
+    if (!m_useIpv6)
+    {
+        internetHelper.SetIpv6StackInstall(false);
+    }
+    else
+    {
+        internetHelper.SetIpv4StackInstall(false);
+    }
+    internetHelper.Install(m_nodes);
+
+    if (!m_useIpv6)
+    {
+        Ipv4AddressHelper ipv4AddrHelper;
+        ipv4AddrHelper.SetBase("10.0.0.0", "255.255.255.0");
+        m_ipv4Interfaces = ipv4AddrHelper.Assign(m_devices);
+    }
+    else
+    {
+        // This is to disable DAD, to simplify the drop testcase.
+        m_nodes.Get(0)->GetObject<Icmpv6L4Protocol>()->SetAttribute("DAD", BooleanValue(false));
+        m_nodes.Get(1)->GetObject<Icmpv6L4Protocol>()->SetAttribute("DAD", BooleanValue(false));
+
+        Ipv6AddressHelper ipv6AddrHelper;
+        ipv6AddrHelper.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64));
+        m_ipv6Interfaces = ipv6AddrHelper.Assign(m_devices);
+
+        // This is a dirty trick to disable RS/RA, to simplify the drop testcase.
+        // Forwarding interfaces do not send RS.
+        m_nodes.Get(0)->GetObject<Ipv6L3Protocol>()->SetForwarding(1, true);
+        m_nodes.Get(1)->GetObject<Ipv6L3Protocol>()->SetForwarding(1, true);
+    }
+}
+
+void
+PingTestCase::DoTeardown()
+{
+    Simulator::Destroy();
+}
+
+void
+PingTestCase::CheckReportTransmitted(uint32_t expectedReportTx)
+{
+    m_checkReportTransmitted = true;
+    m_expectedReportTx = expectedReportTx;
+}
+
+void
+PingTestCase::CheckReportReceived(uint32_t expectedReportRx)
+{
+    m_checkReportReceived = true;
+    m_expectedReportRx = expectedReportRx;
+}
+
+void
+PingTestCase::CheckReportLoss(uint16_t expectedReportLoss)
+{
+    m_checkReportLoss = true;
+    m_expectedReportLoss = expectedReportLoss;
+}
+
+void
+PingTestCase::CheckReportTime(Time expectedTime)
+{
+    m_checkReportTime = true;
+    m_expectedReportTime = expectedTime;
+}
+
+void
+PingTestCase::RttTraceSink(uint16_t seq, Time rttSample)
+{
+    NS_LOG_FUNCTION(this << seq << rttSample.As(Time::MS));
+    m_countTraceRtt++;
+    if (m_sizeAttribute == 56)
+    {
+        if (!m_useIpv6)
+        {
+            // Each ping should be received with RTT of 20.0013 ms in this configuration
+            // Check that each ping is within 20.0012 ms <= rttSample <= 20.0014 ms
+            NS_TEST_ASSERT_MSG_EQ_TOL(
+                rttSample.GetSeconds() * 1000,
+                20.0013,
+                0.0001,
+                "Rtt sample not within 0.0001 ms of expected value of 20.0013 ms");
+        }
+        else
+        {
+            // Each ping should be received with RTT of 20.0017 ms in this configuration
+            // Check that each ping is within 20.0016 ms <= rttSample <= 20.0018 ms
+            NS_TEST_ASSERT_MSG_EQ_TOL(
+                rttSample.GetSeconds() * 1000,
+                20.0017,
+                0.0001,
+                "Rtt sample not within 0.0001 ms of expected value of 20.0017 ms");
+        }
+    }
+}
+
+void
+PingTestCase::TxTraceSink(uint16_t seq, Ptr<Packet> p)
+{
+    NS_LOG_FUNCTION(this << seq << p);
+    m_countTraceTx++;
+    if (m_lastTx == Seconds(0))
+    {
+        m_lastTx = Simulator::Now();
+    }
+    else
+    {
+        NS_TEST_ASSERT_MSG_EQ(Simulator::Now() - m_lastTx,
+                              m_interpacketInterval,
+                              "configured interval didn't match the observed interval");
+        m_lastTx = Simulator::Now();
+    }
+}
+
+void
+PingTestCase::DropTraceSink(uint16_t seq, Ping::DropReason reason)
+{
+    NS_LOG_FUNCTION(this << static_cast<uint16_t>(reason));
+    if (reason == Ping::DropReason::DROP_TIMEOUT)
+    {
+        NS_LOG_DEBUG("Drop due to timeout " << seq);
+    }
+    else if (reason == Ping::DROP_HOST_UNREACHABLE)
+    {
+        NS_LOG_DEBUG("Destination host is unreachable " << seq);
+    }
+    else if (reason == Ping::DROP_NET_UNREACHABLE)
+    {
+        NS_LOG_DEBUG("Destination network not reachable " << seq);
+    }
+}
+
+void
+PingTestCase::ReportTraceSink(const Ping::PingReport& report)
+{
+    NS_LOG_FUNCTION(this << report.m_transmitted << report.m_received << report.m_loss
+                         << report.m_rttMin << report.m_rttMax);
+    if (m_checkReportTransmitted)
+    {
+        NS_TEST_ASSERT_MSG_EQ(report.m_transmitted,
+                              m_expectedReportTx,
+                              "Report transmit count does not equal expected");
+    }
+    if (m_checkReportReceived)
+    {
+        NS_TEST_ASSERT_MSG_EQ(report.m_received,
+                              m_expectedReportRx,
+                              "Report receive count does not equal expected");
+    }
+    if (m_checkReportLoss)
+    {
+        NS_TEST_ASSERT_MSG_EQ(report.m_loss,
+                              m_expectedReportLoss,
+                              "Report lost count does not equal expected");
+    }
+    if (m_checkReportTime)
+    {
+        NS_TEST_ASSERT_MSG_EQ_TOL(Simulator::Now().GetMicroSeconds(),
+                                  m_expectedReportTime.GetMicroSeconds(),
+                                  1, // tolerance of 1 us
+                                  "Report application not stopped at expected time");
+    }
+    NS_TEST_ASSERT_MSG_GT_OR_EQ(report.m_rttMin, 0, "minimum rtt should be greater than 0");
+    NS_TEST_ASSERT_MSG_GT_OR_EQ(report.m_rttMax, 0, "maximum rtt should be greater than 0");
+}
+
+void
+PingTestCase::CheckTraceTx(uint32_t expectedTx)
+{
+    m_checkTraceTx = true;
+    m_expectedTraceTx = expectedTx;
+}
+
+void
+PingTestCase::CheckTraceRtt(uint32_t expectedRtt)
+{
+    m_checkTraceRtt = true;
+    m_expectedTraceRtt = expectedRtt;
+}
+
+void
+PingTestCase::SetSimulatorStopTime(Time stopTime)
+{
+    m_simulatorStopTime = stopTime;
+}
+
+void
+PingTestCase::SetDestinationAddress(Address address)
+{
+    m_destination = address;
+}
+
+void
+PingTestCase::DoRun()
+{
+    NS_LOG_FUNCTION(this);
+    Ptr<Ping> ping = CreateObject<Ping>();
+
+    if (m_useIpv6)
+    {
+        Ipv6Address dst = Ipv6Address::ConvertFrom(m_destination);
+        if (dst.IsLinkLocal() || dst.IsLinkLocalMulticast())
+        {
+            NS_LOG_LOGIC("Setting local interface address to "
+                         << m_ipv6Interfaces.GetAddress(0, 0));
+            ping->SetAttribute("InterfaceAddress", AddressValue(m_ipv6Interfaces.GetAddress(0, 0)));
+        }
+        else
+        {
+            NS_LOG_LOGIC("Setting local interface address to "
+                         << m_ipv6Interfaces.GetAddress(0, 1));
+            ping->SetAttribute("InterfaceAddress", AddressValue(m_ipv6Interfaces.GetAddress(0, 1)));
+        }
+        NS_LOG_LOGIC("Setting destination address to " << Ipv6Address::ConvertFrom(m_destination));
+        ping->SetAttribute("Destination", AddressValue(m_destination));
+    }
+    else
+    {
+        NS_LOG_LOGIC("Setting destination address to " << Ipv4Address::ConvertFrom(m_destination));
+        ping->SetAttribute("InterfaceAddress", AddressValue(m_ipv4Interfaces.GetAddress(0)));
+        ping->SetAttribute("Destination", AddressValue(m_destination));
+    }
+
+    if (!m_dropList.empty())
+    {
+        NS_LOG_LOGIC("Setting drop list to list of size " << m_dropList.size());
+        Ptr<ReceiveListErrorModel> errorModel = CreateObject<ReceiveListErrorModel>();
+        Ptr<SimpleNetDevice> netDev = DynamicCast<SimpleNetDevice>(m_devices.Get(0));
+        netDev->SetReceiveErrorModel(errorModel);
+        errorModel->SetList(m_dropList);
+    }
+
+    ping->SetAttribute("Count", UintegerValue(m_countAttribute));
+    ping->SetAttribute("Size", UintegerValue(m_sizeAttribute));
+    ping->SetAttribute("Interval", TimeValue(m_interpacketInterval));
+    ping->SetStartTime(m_startTime);
+    ping->SetStopTime(m_stopTime);
+    m_nodes.Get(0)->AddApplication(ping);
+    ping->TraceConnectWithoutContext("Tx", MakeCallback(&PingTestCase::TxTraceSink, this));
+    ping->TraceConnectWithoutContext("Rtt", MakeCallback(&PingTestCase::RttTraceSink, this));
+    ping->TraceConnectWithoutContext("Drop", MakeCallback(&PingTestCase::DropTraceSink, this));
+    ping->TraceConnectWithoutContext("Report", MakeCallback(&PingTestCase::ReportTraceSink, this));
+
+    NeighborCacheHelper neighborCacheHelper;
+    neighborCacheHelper.PopulateNeighborCache();
+
+    Simulator::Stop(m_simulatorStopTime);
+    Simulator::Run();
+
+    if (m_checkTraceTx)
+    {
+        NS_TEST_ASSERT_MSG_EQ(m_countTraceTx,
+                              m_expectedTraceTx,
+                              "Traced Tx events do not equal expected");
+    }
+    if (m_checkTraceRtt)
+    {
+        NS_TEST_ASSERT_MSG_EQ(m_countTraceRtt,
+                              m_expectedTraceRtt,
+                              "Traced Rtt events do not equal expected");
+    }
+}
+
+/**
+ * \ingroup ping-test
+ * \ingroup tests
+ *
+ * \brief ping TestSuite
+ */
+class PingTestSuite : public TestSuite
+{
+  public:
+    PingTestSuite();
+};
+
+PingTestSuite::PingTestSuite()
+    : TestSuite("ping", UNIT)
+{
+    // 1. Unlimited pings, no losses, StopApplication () with no packets in flight
+    //    Configuration:  Ping::Count = 0,  Ping::Interval = 1s, Ping start
+    //                    time = 1s, Ping stop time = 5.5s
+    //    Expected behavior:  Pings are sent at times 1, 2, 3, 4, 5 sec.  The
+    //                        number sent equals number received, which equals 5.
+    //    How validated:  PingReport trace is checked for number of packets
+    //                    transmitted and received (5), and number of drops (0).
+    PingTestCase* testcase1v4 = new PingTestCase(
+        "1. Unlimited pings, no losses, StopApplication () with no packets in flight IPv4",
+        USEIPV6_FALSE);
+    testcase1v4->SetStartTime(Seconds(1));
+    testcase1v4->SetStopTime(Seconds(5.5));
+    testcase1v4->SetCount(0);
+    testcase1v4->CheckReportTransmitted(5);
+    testcase1v4->CheckReportReceived(5);
+    testcase1v4->CheckTraceTx(5);
+    testcase1v4->SetDestinationAddress(Ipv4Address("10.0.0.2"));
+    AddTestCase(testcase1v4, TestCase::QUICK);
+
+    PingTestCase* testcase1v6 = new PingTestCase(
+        "1. Unlimited pings, no losses, StopApplication () with no packets in flight IPv6",
+        USEIPV6_TRUE);
+    testcase1v6->SetStartTime(Seconds(1));
+    testcase1v6->SetStopTime(Seconds(5.5));
+    testcase1v6->SetCount(0);
+    testcase1v6->CheckReportTransmitted(5);
+    testcase1v6->CheckReportReceived(5);
+    testcase1v6->CheckTraceTx(5);
+    testcase1v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:2"));
+    AddTestCase(testcase1v6, TestCase::QUICK);
+
+    // 2. Unlimited pings, no losses, StopApplication () with 1 packet in flight
+    // Configuration:  Ping::Count = 0,  Ping::Interval = 1s, Ping start time =
+    //                 1s, Ping stop time = 1.0001s
+    // Expected behavior:  The application is stopped immediately after the
+    //                     only ping is sent, and this ping will be reported
+    //                     to be lost
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (1) and received (0), and number of drops (0)
+    PingTestCase* testcase2v4 = new PingTestCase(
+        "2. Unlimited pings, no losses, StopApplication () with 1 packet in flight IPv4",
+        USEIPV6_FALSE);
+    testcase2v4->SetStartTime(Seconds(1));
+    testcase2v4->SetStopTime(Seconds(1.0001));
+    testcase2v4->SetSimulatorStopTime(Seconds(5));
+    testcase2v4->CheckReportTransmitted(1);
+    testcase2v4->CheckReportReceived(0);
+    testcase1v4->SetDestinationAddress(Ipv4Address("10.0.0.2"));
+    AddTestCase(testcase2v4, TestCase::QUICK);
+
+    PingTestCase* testcase2v6 = new PingTestCase(
+        "2. Unlimited pings, no losses, StopApplication () with 1 packet in flight IPv6",
+        USEIPV6_TRUE);
+    testcase2v6->SetStartTime(Seconds(1));
+    testcase2v6->SetStopTime(Seconds(1.0001));
+    testcase2v6->SetSimulatorStopTime(Seconds(5));
+    testcase2v6->CheckReportTransmitted(1);
+    testcase2v6->CheckReportReceived(0);
+    testcase2v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:2"));
+    AddTestCase(testcase2v6, TestCase::QUICK);
+
+    // 3. Test for operation of count attribute and the resulting StopApplication time after all
+    // pings were received. Limited pings, no losses, StopApplication () with no packet in flight
+    // Configuration:  Ping::Count = 2,  Ping::Interval = 1s, Ping start time =
+    //                 1s, Ping stop time = 5s
+    // Expected behavior:  Pings are sent at 1 second intervals.  The
+    //                     number sent (2) is equal to number received (2).
+    //                     After receiving all pings, despite configuring
+    //                     the StopApplication time to 5 seconds, the
+    //                     application will immediately stop and generate
+    //                     the report (at 2020001 us)
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (2) and received (2),number of drops (0),
+    //                 Report time will be MicroSeconds (2020001).
+    uint32_t count = 2;
+    uint32_t expectedTx = 2;
+    PingTestCase* testcase3v4 =
+        new PingTestCase("3. Test for operation of count attribute and exit "
+                         "time after all pings were received, IPv4",
+                         USEIPV6_FALSE);
+    testcase3v4->SetStartTime(Seconds(1));
+    testcase3v4->SetStopTime(Seconds(5));
+    testcase3v4->SetCount(count);
+    testcase3v4->SetSimulatorStopTime(Seconds(6));
+    testcase3v4->CheckReportTransmitted(2);
+    testcase3v4->CheckReportReceived(2);
+    testcase3v4->CheckReportTime(MicroSeconds(2020001));
+    testcase3v4->CheckTraceTx(expectedTx);
+    testcase3v4->SetDestinationAddress(Ipv4Address("10.0.0.2"));
+    AddTestCase(testcase3v4, TestCase::QUICK);
+
+    PingTestCase* testcase3v6 =
+        new PingTestCase("3. Test for operation of count attribute and exit "
+                         "time after all pings were received, IPv6",
+                         USEIPV6_TRUE);
+    testcase3v6->SetStartTime(Seconds(1));
+    testcase3v6->SetStopTime(Seconds(5));
+    testcase3v6->SetCount(count);
+    testcase3v6->SetSimulatorStopTime(Seconds(6));
+    testcase3v6->CheckReportTransmitted(2);
+    testcase3v6->CheckReportReceived(2);
+    testcase3v6->CheckReportTime(MicroSeconds(2020001));
+    testcase3v6->CheckTraceTx(expectedTx);
+    testcase3v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:2"));
+    AddTestCase(testcase3v6, TestCase::QUICK);
+
+    // 4. Test for the operation of interval attribute for IPv4
+    // Unlimited pings, no losses, StopApplication () with no packet in flight
+    // Configuration:  Ping::Count = 0,  Ping::Interval = 3s, Ping start
+    //                 time = 1s, Ping stop time = 6s
+    // Expected behavior:  Pings are sent at the interval of 3 sec.
+    //                     1st packet is sent at 1sec and 2nd packet at 4sec.
+    //                     The number sent (2) is equal to number received (2)
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (2) and received (2), and number of drops (0)
+    Time interval = Seconds(3.0);
+    PingTestCase* testcase4v4 =
+        new PingTestCase("4. Test for the operation of interval attribute for IPv4", USEIPV6_FALSE);
+    testcase4v4->SetStartTime(Seconds(1));
+    testcase4v4->SetStopTime(Seconds(5));
+    testcase4v4->SetInterval(interval);
+    testcase4v4->SetSimulatorStopTime(Seconds(6));
+    testcase4v4->CheckReportTransmitted(2);
+    testcase4v4->CheckReportReceived(2);
+    testcase4v4->SetDestinationAddress(Ipv4Address("10.0.0.2"));
+    AddTestCase(testcase4v4, TestCase::QUICK);
+
+    PingTestCase* testcase4v6 =
+        new PingTestCase("4. Test for the operation of interval attribute for IPv6", USEIPV6_TRUE);
+    testcase4v6->SetStartTime(Seconds(1));
+    testcase4v6->SetStopTime(Seconds(5));
+    testcase4v6->SetInterval(interval);
+    testcase4v6->SetSimulatorStopTime(Seconds(6));
+    testcase4v6->CheckReportTransmitted(2);
+    testcase4v6->CheckReportReceived(2);
+    testcase4v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:2"));
+    AddTestCase(testcase4v6, TestCase::QUICK);
+
+    // 5. Test for behavior of pinging an unreachable host when the
+    //    network does not send an ICMP unreachable message.
+    // Unlimited pings, StopApplication () with no packet in flight
+    // Configuration:  Ping::Count = 0,  Ping start time = 1s
+    //                 Ping stop time = 5.5s.  Ping to unknown destination.
+    // Expected behavior:  By default, the timeout value is 1 second.  Ping
+    //                     sends first packet at time 1 second, and does not
+    //                     receive a response.  At the timeout (simulation time
+    //                     2 seconds), ping should print out that there was
+    //                     a timeout, and the drop trace should be fired.
+    //                     At stop time, there should be three drops recorded
+    //                     of type DROP_TIMEOUT, and not four,
+    //                     because the packet sent at time 5 seconds has not
+    //                     had enough time elapsed to timeout.
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (5) and  received (0).
+    //                 The packet loss rate should be checked to be 100 percent
+    PingTestCase* testcase5v4 =
+        new PingTestCase("5. Test for behavior of ping to unreachable IPv4 address", USEIPV6_FALSE);
+    testcase5v4->SetStartTime(Seconds(1));
+    testcase5v4->SetStopTime(Seconds(5.5));
+    testcase5v4->SetDestinationAddress(Ipv4Address("1.2.3.4"));
+    testcase5v4->CheckReportTransmitted(5);
+    testcase5v4->CheckReportReceived(0);
+    testcase5v4->CheckReportLoss(100);
+    AddTestCase(testcase5v4, TestCase::QUICK);
+
+    PingTestCase* testcase5v6 =
+        new PingTestCase("5. Test for behavior of ping to unreachable IPv6 address", USEIPV6_TRUE);
+    testcase5v6->SetStartTime(Seconds(1));
+    testcase5v6->SetStopTime(Seconds(5.5));
+    testcase5v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:3"));
+    testcase5v6->CheckReportTransmitted(5);
+    testcase5v6->CheckReportReceived(0);
+    testcase5v6->CheckReportLoss(100);
+    AddTestCase(testcase5v6, TestCase::QUICK);
+
+    // 6. Test for behavior of pinging an broadcast (or multicast) address.
+    // Limited pings, no losses, StopApplication () with no packet in flight
+    // Configuration:  Ping::Count = 2,  Ping::Interval = 1s, Ping start time =
+    //                 1s, Ping stop time = 5s
+    // Expected behavior:  Pings are sent at 1 second intervals.  The
+    //                     number sent (2) is equal to number received (2).
+    //                     After receiving all pings, despite configuring
+    //                     the StopApplication time to 5 seconds, the
+    //                     application will immediately stop and generate
+    //                     the report (at 2020001 us)
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (2) and received (2),number of drops (0),
+    //                 Report time will be MicroSeconds (2020001).
+    PingTestCase* testcase6v4 =
+        new PingTestCase("6. Test for behavior of ping to broadcast IPv4 address", USEIPV6_FALSE);
+    testcase6v4->SetStartTime(Seconds(1));
+    testcase6v4->SetStopTime(Seconds(5.5));
+    testcase6v4->SetDestinationAddress(Ipv4Address::GetBroadcast());
+    testcase6v4->CheckReportTransmitted(5);
+    testcase6v4->CheckReportReceived(5);
+    testcase6v4->CheckReportLoss(0);
+    AddTestCase(testcase6v4, TestCase::QUICK);
+
+    PingTestCase* testcase6v6 =
+        new PingTestCase("6. Test for behavior of ping to all-nodes multicast IPv6 address",
+                         USEIPV6_TRUE);
+    testcase6v6->SetStartTime(Seconds(1));
+    testcase6v6->SetStopTime(Seconds(5.5));
+    testcase6v6->SetDestinationAddress(Ipv6Address::GetAllNodesMulticast());
+    testcase6v6->CheckReportTransmitted(5);
+    testcase6v6->CheckReportReceived(5);
+    testcase6v6->CheckReportLoss(0);
+    AddTestCase(testcase6v6, TestCase::QUICK);
+
+    // 7. Test behavior of first reply lost in a count-limited configuration.
+    // Limited pings, no losses, StopApplication () with no packet in flight
+    // Configuration:  Ping::Count = 3,  Ping::Interval = 1s, Ping start time =
+    //                 1s, Ping stop time = 5s.  Loss of first reply is forced.
+    // Expected behavior:  Pings are sent at 1 second intervals at times
+    //                     1s, 2s, 3s.  The ping Tx trace will record 3 sends,
+    //                     but the RTT trace will record 2 receptions.
+    //                     The application will stop after the third send
+    //                     after waiting for 2*RttMax (40 ms).
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (3) and received (2), loss percentage (33).
+    //                 Ping Tx trace (3) and Rtt trace (2) are also checked.
+    //                 Report time will be MicroSeconds (3040000).
+    PingTestCase* testcase7v4 = new PingTestCase(
+        "7. Test behavior of first reply lost in a count-limited configuration, IPv4",
+        USEIPV6_FALSE);
+    std::list<uint32_t> dropList{0};
+    testcase7v4->SetDropList(dropList);
+    testcase7v4->SetStartTime(Seconds(1));
+    testcase7v4->SetCount(3);
+    testcase7v4->SetStopTime(Seconds(5));
+    testcase7v4->CheckTraceTx(3);
+    testcase7v4->CheckTraceRtt(2);
+    testcase7v4->CheckReportTransmitted(3);
+    testcase7v4->CheckReportReceived(2);
+    testcase7v4->CheckReportLoss(33); // 33%
+    testcase7v4->CheckReportTime(MicroSeconds(3040000));
+    AddTestCase(testcase7v4, TestCase::QUICK);
+
+    PingTestCase* testcase7v6 = new PingTestCase(
+        "7. Test behavior of first reply lost in a count-limited configuration, IPv6",
+        USEIPV6_TRUE);
+    testcase7v6->SetDropList(dropList);
+    testcase7v6->SetStartTime(Seconds(1));
+    testcase7v6->SetCount(3);
+    testcase7v6->SetStopTime(Seconds(5));
+    testcase7v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:2"));
+    testcase7v6->CheckTraceTx(3);
+    testcase7v6->CheckTraceRtt(2);
+    testcase7v6->CheckReportTransmitted(3);
+    testcase7v6->CheckReportReceived(2);
+    testcase7v6->CheckReportLoss(33); // 33%
+    testcase7v6->CheckReportTime(MicroSeconds(3040000));
+    AddTestCase(testcase7v6, TestCase::QUICK);
+
+    // 8. Test behavior of second reply lost in a count-limited configuration.
+    // Limited pings, no losses, StopApplication () with no packet in flight
+    // Configuration:  Ping::Count = 3,  Ping::Interval = 1s, Ping start time =
+    //                 1s, Ping stop time = 5s.  Loss of second reply is forced.
+    // Expected behavior:  Pings are sent at 1 second intervals at times
+    //                     1s, 2s, 3s.  The ping Tx trace will record 3 sends,
+    //                     but the RTT trace will record 2 receptions.
+    //                     The application will stop after the third send
+    //                     after waiting for 2*RttMax (40 ms).
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (3) and received (2), loss percentage (33).
+    //                 Ping Tx trace (3) and Rtt trace (2) are also checked.
+    //                 Report time will be MicroSeconds (3040000).
+    PingTestCase* testcase8v4 = new PingTestCase(
+        "8. Test behavior of second reply lost in a count-limited configuration, IPv4",
+        USEIPV6_FALSE);
+    std::list<uint32_t> dropList2{1};
+    testcase8v4->SetDropList(dropList2);
+    testcase8v4->SetStartTime(Seconds(1));
+    testcase8v4->SetCount(3);
+    testcase8v4->SetStopTime(Seconds(5));
+    testcase8v4->CheckTraceTx(3);
+    testcase8v4->CheckTraceRtt(2);
+    testcase8v4->CheckReportTransmitted(3);
+    testcase8v4->CheckReportReceived(2);
+    testcase8v4->CheckReportLoss(33); // 33%
+    testcase8v4->CheckReportTime(MicroSeconds(3040000));
+    AddTestCase(testcase8v4, TestCase::QUICK);
+
+    PingTestCase* testcase8v6 = new PingTestCase(
+        "8. Test behavior of second reply lost in a count-limited configuration, IPv6",
+        USEIPV6_TRUE);
+    testcase8v6->SetDropList(dropList2);
+    testcase8v6->SetStartTime(Seconds(1));
+    testcase8v6->SetCount(3);
+    testcase8v6->SetStopTime(Seconds(5));
+    testcase8v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:2"));
+    testcase8v6->CheckTraceTx(3);
+    testcase8v6->CheckTraceRtt(2);
+    testcase8v6->CheckReportTransmitted(3);
+    testcase8v6->CheckReportReceived(2);
+    testcase8v6->CheckReportLoss(33); // 33%
+    testcase8v6->CheckReportTime(MicroSeconds(3040000));
+    AddTestCase(testcase8v6, TestCase::QUICK);
+
+    // 9. Test behavior of last reply lost in a count-limited configuration.
+    // Limited pings, no losses, StopApplication () with no packet in flight
+    // Configuration:  Ping::Count = 3,  Ping::Interval = 1s, Ping start time =
+    //                 1s, Ping stop time = 5s.  Loss of last reply is forced.
+    // Expected behavior:  Pings are sent at 1 second intervals at times
+    //                     1s, 2s, 3s.  The ping Tx trace will record 3 sends,
+    //                     but the RTT trace will record 2 receptions.
+    //                     The application will stop after the third send
+    //                     after waiting for 2*RttMax (40 ms).
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (3) and received (2), loss percentage (33).
+    //                 Ping Tx trace (3) and Rtt trace (2) are also checked.
+    //                 Report time will be MicroSeconds (3040000).
+    PingTestCase* testcase9v4 = new PingTestCase(
+        "9. Test behavior of last reply lost in a count-limited configuration, IPv4",
+        USEIPV6_FALSE);
+    std::list<uint32_t> dropList3{2};
+    testcase9v4->SetDropList(dropList3);
+    testcase9v4->SetStartTime(Seconds(1));
+    testcase9v4->SetCount(3);
+    testcase9v4->SetStopTime(Seconds(5));
+    testcase9v4->CheckTraceTx(3);
+    testcase9v4->CheckTraceRtt(2);
+    testcase9v4->CheckReportTransmitted(3);
+    testcase9v4->CheckReportReceived(2);
+    testcase9v4->CheckReportLoss(33); // 33%
+    testcase9v4->CheckReportTime(MicroSeconds(3040000));
+    AddTestCase(testcase9v4, TestCase::QUICK);
+
+    PingTestCase* testcase9v6 = new PingTestCase(
+        "9. Test behavior of last reply lost in a count-limited configuration, IPv6",
+        USEIPV6_TRUE);
+    testcase9v6->SetDropList(dropList3);
+    testcase9v6->SetStartTime(Seconds(1));
+    testcase9v6->SetCount(3);
+    testcase9v6->SetStopTime(Seconds(5));
+    testcase9v6->SetDestinationAddress(Ipv6Address("2001:1::200:ff:fe00:2"));
+    testcase9v6->CheckTraceTx(3);
+    testcase9v6->CheckTraceRtt(2);
+    testcase9v6->CheckReportTransmitted(3);
+    testcase9v6->CheckReportReceived(2);
+    testcase9v6->CheckReportLoss(33); // 33%
+    testcase9v6->CheckReportTime(MicroSeconds(3040000));
+    AddTestCase(testcase9v6, TestCase::QUICK);
+
+#ifdef NOTYET
+    //
+    // 10. Test for behavior of pinging on a link that causes IPv4 fragmentation
+    // Configuration:  Ping::Count = 1,  Ping start time = 1s
+    //                 Ping stop time = 2.5s.  Ping to Node 1
+    //                 Ping size set to 2000 bytes.
+    // Expected behavior:  At shortly after time 1 seconds, Ping should
+    //                     successfully exit by recording the successful
+    //                     exchange of one echo request and reply.
+    // How validated:  PingReport trace is checked for number of packets
+    //                 transmitted (5) and  received (0).
+    //                 PingReport time is checked for an explicit time
+    //                 (1.020028s) corresponding to 2000 bytes
+    //                 The packet loss rate should be checked to be 100 percent
+    PingTestCase* testcase10v4 = new PingTestCase("10. Test for IPv4 fragmentation", USEIPV6_FALSE);
+    testcase10v4->SetStartTime(Seconds(1));
+    testcase10v4->SetStopTime(Seconds(2.5));
+    testcase10v4->SetCount(1);
+    testcase10v4->SetSize(2000);
+    testcase10v4->CheckReportTransmitted(1);
+    testcase10v4->CheckReportReceived(1);
+    testcase10v4->CheckReportTime(MicroSeconds(1020028));
+    AddTestCase(testcase10v4, TestCase::QUICK);
+#endif
+}
+
+static PingTestSuite pingTestSuite; //!< Static variable for test initialization
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/CMakeLists.txt ns-3.38/src/lr-wpan/CMakeLists.txt
--- ns-3.37/src/lr-wpan/CMakeLists.txt	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/lr-wpan/CMakeLists.txt	2023-03-16 20:54:23.632868500 -0700
@@ -17,6 +17,7 @@
     model/lr-wpan-spectrum-value-helper.cc
   HEADER_FILES
     helper/lr-wpan-helper.h
+    model/lr-wpan-constants.h
     model/lr-wpan-csmaca.h
     model/lr-wpan-error-model.h
     model/lr-wpan-fields.h
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/doc/802-15-4-per-sens.eps ns-3.38/src/lr-wpan/doc/802-15-4-per-sens.eps
--- ns-3.37/src/lr-wpan/doc/802-15-4-per-sens.eps	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/lr-wpan/doc/802-15-4-per-sens.eps	2023-03-16 20:54:23.632868500 -0700
@@ -1,7 +1,7 @@
 %!PS-Adobe-2.0 EPSF-2.0
-%%Title: 802.15.4-per-sens.eps
-%%Creator: gnuplot 4.6 patchlevel 4 (Gentoo revision r2)
-%%CreationDate: Wed Apr 23 12:42:53 2014
+%%Title: 802.15.4-per-vs-rxSignal.eps
+%%Creator: gnuplot 5.2 patchlevel 8
+%%CreationDate: Mon Dec 19 13:38:56 2022
 %%DocumentFonts: (atend)
 %%BoundingBox: 50 50 410 302
 %%EndComments
@@ -18,6 +18,7 @@
 /Dashlength 1 def
 /Landscape false def
 /Level1 false def
+/Level3 false def
 /Rounded false def
 /ClipToBoundingBox false def
 /SuppressPDFMark false def
@@ -29,11 +30,11 @@
 %
 /vshift -46 def
 /dl1 {
-  10.0 Dashlength mul mul
+  10.0 Dashlength userlinewidth gnulinewidth div mul mul mul
   Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if
 } def
 /dl2 {
-  10.0 Dashlength mul mul
+  10.0 Dashlength userlinewidth gnulinewidth div mul mul mul
   Rounded { currentlinewidth 0.75 mul add } if
 } def
 /hpt_ 31.5 def
@@ -47,7 +48,7 @@
   } if
 } def
 %
-% Gnuplot Prolog Version 4.6 (September 2012)
+% Gnuplot Prolog Version 5.2 (Dec 2017)
 %
 %/SuppressPDFMark true def
 %
@@ -64,11 +65,11 @@
 /vpt2 vpt 2 mul def
 /hpt2 hpt 2 mul def
 /Lshow {currentpoint stroke M 0 vshift R 
-	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+	Blacktext {gsave 0 setgray textshow grestore} {textshow} ifelse} def
 /Rshow {currentpoint stroke M dup stringwidth pop neg vshift R
-	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+	Blacktext {gsave 0 setgray textshow grestore} {textshow} ifelse} def
 /Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R 
-	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+	Blacktext {gsave 0 setgray textshow grestore} {textshow} ifelse} def
 /UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def
   /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def
 /DL {Color {setrgbcolor Solid {pop []} if 0 setdash}
@@ -82,7 +83,7 @@
 /PL {stroke userlinewidth setlinewidth
 	Rounded {1 setlinejoin 1 setlinecap} if} def
 3.8 setmiterlimit
-% Default Line colors
+% Classic Line colors (version 5.0)
 /LCw {1 1 1} def
 /LCb {0 0 0} def
 /LCa {0 0 0} def
@@ -95,19 +96,21 @@
 /LC6 {0 0 0} def
 /LC7 {1 0.3 0} def
 /LC8 {0.5 0.5 0.5} def
-% Default Line Types
+% Default dash patterns (version 5.0)
+/LTB {BL [] LCb DL} def
 /LTw {PL [] 1 setgray} def
-/LTb {BL [] LCb DL} def
+/LTb {PL [] LCb DL} def
 /LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def
 /LT0 {PL [] LC0 DL} def
-/LT1 {PL [4 dl1 2 dl2] LC1 DL} def
-/LT2 {PL [2 dl1 3 dl2] LC2 DL} def
-/LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def
-/LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def
-/LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def
-/LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def
-/LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def
-/LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def
+/LT1 {PL [2 dl1 3 dl2] LC1 DL} def
+/LT2 {PL [1 dl1 1.5 dl2] LC2 DL} def
+/LT3 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC3 DL} def
+/LT4 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def
+/LT5 {PL [4 dl1 2 dl2] LC5 DL} def
+/LT6 {PL [1.5 dl1 1.5 dl2 1.5 dl1 1.5 dl2 1.5 dl1 6 dl2] LC6 DL} def
+/LT7 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC7 DL} def
+/LT8 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC8 DL} def
+/SL {[] 0 setdash} def
 /Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def
 /Dia {stroke [] 0 setdash 2 copy vpt add M
   hpt neg vpt neg V hpt vpt neg V
@@ -302,7 +305,7 @@
   ColR ColG ColB setrgbcolor} def
 /BoxColFill {gsave Rec PolyFill} def
 /PolyFill {gsave Density fill grestore grestore} def
-/h {rlineto rlineto rlineto gsave closepath fill grestore} bind def
+/h {rlineto rlineto rlineto closepath gsave fill grestore stroke} bind def
 %
 % PostScript Level 1 Pattern Fill routine for rectangles
 % Usage: x y w h s a XX PatternFill
@@ -329,9 +332,14 @@
 %
 /languagelevel where
  {pop languagelevel} {1} ifelse
- 2 lt
-	{/InterpretLevel1 true def}
-	{/InterpretLevel1 Level1 def}
+dup 2 lt
+	{/InterpretLevel1 true def
+	 /InterpretLevel3 false def}
+	{/InterpretLevel1 Level1 def
+	 2 gt
+	    {/InterpretLevel3 Level3 def}
+	    {/InterpretLevel3 false def}
+	 ifelse }
  ifelse
 %
 % PostScript level 2 pattern fill definitions
@@ -420,16 +428,29 @@
 /Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont
 dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall
 currentdict end definefont pop
+%
+/Metrics {ExtendTextBox Gswidth} def
+/Lwidth {currentpoint stroke M 0 vshift R Metrics} def
+/Rwidth {currentpoint stroke M dup stringwidth pop neg vshift R Metrics} def
+/Cwidth {currentpoint stroke M dup stringwidth pop -2 div vshift R Metrics} def
+/GLwidth {currentpoint stroke M 0 vshift R {ExtendTextBox} forall} def
+/GRwidth {currentpoint stroke M dup Gwidth vshift R {ExtendTextBox} forall} def
+/GCwidth {currentpoint stroke M dup Gwidth 2 div vshift R {ExtendTextBox} forall} def
+/GLwidth2 {0 Gwidth AddGlyphWidth} def
+/GRwidth2 {Gwidth -1 mul 0 AddGlyphWidth} def
+/GCwidth2 {Gwidth 2 div dup -1 mul AddGlyphWidth} def
+/AddGlyphWidth { dup TBx2 gt {userdict /TBx2 3 -1 roll put} {pop} ifelse
+                 dup TBx1 lt {userdict /TBx1 3 -1 roll put} {pop} ifelse } def
 /MFshow {
    { dup 5 get 3 ge
      { 5 get 3 eq {gsave} {grestore} ifelse }
      {dup dup 0 get findfont exch 1 get scalefont setfont
      [ currentpoint ] exch dup 2 get 0 exch R dup 5 get 2 ne {dup dup 6
-     get exch 4 get {Gshow} {stringwidth pop 0 R} ifelse }if dup 5 get 0 eq
+     get exch 4 get {textshow} {Metrics pop 0 R} ifelse }if dup 5 get 0 eq
      {dup 3 get {2 get neg 0 exch R pop} {pop aload pop M} ifelse} {dup 5
-     get 1 eq {dup 2 get exch dup 3 get exch 6 get stringwidth pop -2 div
-     dup 0 R} {dup 6 get stringwidth pop -2 div 0 R 6 get
-     show 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop
+     get 1 eq {dup 2 get exch dup 3 get exch 6 get Gswidth pop -2 div
+     dup 0 R} {dup 6 get Gswidth pop -2 div 0 R 6 get
+     textshow 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop
      pop aload pop M} ifelse }ifelse }ifelse }
      ifelse }
    forall} def
@@ -455,16 +476,41 @@
   userdict /pdfmark systemdict /cleartomark get put
 } if
 SDict begin [
-  /Title (802.15.4-per-sens.eps)
+  /Title (802.15.4-per-vs-rxSignal.eps)
   /Subject (gnuplot plot)
-  /Creator (gnuplot 4.6 patchlevel 4 (Gentoo revision r2))
-  /Author (jopen)
+  /Creator (gnuplot 5.2 patchlevel 8)
 %  /Producer (gnuplot)
 %  /Keywords ()
-  /CreationDate (Wed Apr 23 12:42:53 2014)
+  /CreationDate (Mon Dec 19 13:38:56 2022)
   /DOCINFO pdfmark
 end
 } ifelse
+%
+% Support for boxed text - Ethan A Merritt Sep 2016
+%
+/InitTextBox { userdict /TBy2 3 -1 roll put userdict /TBx2 3 -1 roll put
+           userdict /TBy1 3 -1 roll put userdict /TBx1 3 -1 roll put
+	   /Boxing true def } def
+/ExtendTextBox { dup type /stringtype eq
+    { Boxing { gsave dup false charpath pathbbox
+      dup TBy2 gt {userdict /TBy2 3 -1 roll put} {pop} ifelse
+      dup TBx2 gt {userdict /TBx2 3 -1 roll put} {pop} ifelse
+      dup TBy1 lt {userdict /TBy1 3 -1 roll put} {pop} ifelse
+      dup TBx1 lt {userdict /TBx1 3 -1 roll put} {pop} ifelse
+      grestore } if }
+    {} ifelse} def
+/PopTextBox { newpath TBx1 TBxmargin sub TBy1 TBymargin sub M
+               TBx1 TBxmargin sub TBy2 TBymargin add L
+	       TBx2 TBxmargin add TBy2 TBymargin add L
+	       TBx2 TBxmargin add TBy1 TBymargin sub L closepath } def
+/DrawTextBox { PopTextBox stroke /Boxing false def} def
+/FillTextBox { gsave PopTextBox fill grestore /Boxing false def} def
+0 0 0 0 InitTextBox
+/TBxmargin 20 def
+/TBymargin 20 def
+/Boxing false def
+/textshow { ExtendTextBox Gshow } def
+%
 end
 %%EndProlog
 %%Page: 1 1
@@ -480,12 +526,13 @@
 1.000 UL
 LTb
 LCb setrgbcolor
-1.000 UL
+0.500 UL
 LTa
 LCa setrgbcolor
 938 448 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
 938 448 M
@@ -494,3275 +541,2145 @@
 -63 0 V
 stroke
 854 448 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1e-12)]
-] -46.7 MRshow
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1x10)]
+[(Times-BoldItalic) 112.0 70.0 true true 0 (-12)]
+] -60.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
+/vshift -46 def
 1.000 UL
 LTb
 LCb setrgbcolor
-938 764 M
+938 768 M
+31 0 V
+5978 0 R
+-31 0 V
+938 851 M
+31 0 V
+5978 0 R
+-31 0 V
+938 902 M
+31 0 V
+5978 0 R
+-31 0 V
+938 938 M
+31 0 V
+5978 0 R
+-31 0 V
+938 966 M
+31 0 V
+5978 0 R
+-31 0 V
+938 989 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1009 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1026 M
 31 0 V
 5978 0 R
 -31 0 V
 stroke
+0.500 UL
 LTa
 LCa setrgbcolor
-938 1080 M
+938 1041 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-938 1080 M
+938 1041 M
 63 0 V
 5946 0 R
 -63 0 V
 stroke
-854 1080 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1e-10)]
-] -46.7 MRshow
+854 1041 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1x10)]
+[(Times-BoldItalic) 112.0 70.0 true true 0 (-10)]
+] -60.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
-938 1396 M
+938 1360 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1444 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1494 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1530 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1558 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1581 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1601 M
+31 0 V
+5978 0 R
+-31 0 V
+938 1618 M
 31 0 V
 5978 0 R
 -31 0 V
 stroke
+0.500 UL
 LTa
 LCa setrgbcolor
-938 1712 M
+938 1633 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-938 1712 M
+938 1633 M
 63 0 V
 5946 0 R
 -63 0 V
 stroke
-854 1712 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1e-08)]
-] -46.7 MRshow
+854 1633 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1x10)]
+[(Times-BoldItalic) 112.0 70.0 true true 0 (-8)]
+] -60.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
-938 2028 M
+938 1953 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2036 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2087 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2123 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2151 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2174 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2194 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2211 M
 31 0 V
 5978 0 R
 -31 0 V
 stroke
+0.500 UL
 LTa
 LCa setrgbcolor
-938 2344 M
+938 2226 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-938 2344 M
+938 2226 M
 63 0 V
 5946 0 R
 -63 0 V
 stroke
-854 2344 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1e-06)]
-] -46.7 MRshow
+854 2226 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1x10)]
+[(Times-BoldItalic) 112.0 70.0 true true 0 (-6)]
+] -60.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
-938 2660 M
+938 2545 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2629 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2679 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2715 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2743 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2766 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2786 M
+31 0 V
+5978 0 R
+-31 0 V
+938 2803 M
 31 0 V
 5978 0 R
 -31 0 V
 stroke
+0.500 UL
 LTa
 LCa setrgbcolor
-938 2975 M
+938 2818 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-938 2975 M
+938 2818 M
 63 0 V
 5946 0 R
 -63 0 V
 stroke
-854 2975 M
+854 2818 M
 [ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0.0001)]
 ] -46.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
-938 3291 M
+938 3138 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3221 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3272 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3308 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3336 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3359 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3379 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3396 M
 31 0 V
 5978 0 R
 -31 0 V
 stroke
+0.500 UL
 LTa
 LCa setrgbcolor
-938 3607 M
+938 3411 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-938 3607 M
+938 3411 M
 63 0 V
 5946 0 R
 -63 0 V
 stroke
-854 3607 M
+854 3411 M
 [ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0.01)]
 ] -46.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
-938 3923 M
+938 3730 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3814 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3864 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3900 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3928 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3952 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3971 M
+31 0 V
+5978 0 R
+-31 0 V
+938 3988 M
 31 0 V
 5978 0 R
 -31 0 V
 stroke
+0.500 UL
 LTa
 LCa setrgbcolor
-938 4239 M
+938 4003 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-938 4239 M
+938 4003 M
 63 0 V
 5946 0 R
 -63 0 V
 stroke
-854 4239 M
+854 4003 M
 [ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1)]
 ] -46.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
-938 4555 M
+938 4323 M
+31 0 V
+5978 0 R
+-31 0 V
+938 4406 M
+31 0 V
+5978 0 R
+-31 0 V
+938 4457 M
+31 0 V
+5978 0 R
+-31 0 V
+938 4493 M
+31 0 V
+5978 0 R
+-31 0 V
+938 4521 M
+31 0 V
+5978 0 R
+-31 0 V
+938 4544 M
+31 0 V
+5978 0 R
+-31 0 V
+938 4564 M
+31 0 V
+5978 0 R
+-31 0 V
+938 4581 M
 31 0 V
 5978 0 R
 -31 0 V
 stroke
+0.500 UL
 LTa
 LCa setrgbcolor
-938 4871 M
+938 4596 M
 6009 0 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-938 4871 M
+938 4596 M
 63 0 V
 5946 0 R
 -63 0 V
 stroke
-854 4871 M
+854 4596 M
 [ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 100)]
 ] -46.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
-1.000 UL
+0.500 UL
 LTa
 LCa setrgbcolor
 938 448 M
-0 4423 V
+0 4171 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
 938 448 M
 0 63 V
-0 4360 R
+0 4108 R
 0 -63 V
 stroke
 938 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-130)]
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-110)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
+1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+1367 448 M
+0 4171 V
+stroke
+1.000 UL
+LTb
+LCb setrgbcolor
+1367 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+1367 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-108)]
 ] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
+1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+1796 448 M
+0 4171 V
+stroke
 1.000 UL
 LTb
 LCb setrgbcolor
+1796 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+1796 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-106)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
 LTa
 LCa setrgbcolor
-1940 448 M
-0 4423 V
+2226 448 M
+0 4171 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-1940 448 M
+2226 448 M
 0 63 V
-0 4360 R
+0 4108 R
 0 -63 V
 stroke
-1940 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-125)]
+2226 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-104)]
 ] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
+1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+2655 448 M
+0 4171 V
+stroke
 1.000 UL
 LTb
 LCb setrgbcolor
+2655 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+2655 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-102)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
 LTa
 LCa setrgbcolor
-2941 448 M
-0 4423 V
+3084 448 M
+0 4171 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-2941 448 M
+3084 448 M
 0 63 V
-0 4360 R
+0 4108 R
 0 -63 V
 stroke
-2941 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-120)]
+3084 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-100)]
 ] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+3513 448 M
+0 4171 V
+stroke
+1.000 UL
+LTb
+LCb setrgbcolor
+3513 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+3513 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-98)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
 LTa
 LCa setrgbcolor
 3943 448 M
-0 4423 V
+0 4171 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
 3943 448 M
 0 63 V
-0 4360 R
+0 4108 R
 0 -63 V
 stroke
 3943 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-115)]
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-96)]
 ] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+4372 448 M
+0 4171 V
+stroke
+1.000 UL
+LTb
+LCb setrgbcolor
+4372 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+4372 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-94)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
 LTa
 LCa setrgbcolor
-4944 448 M
-0 4423 V
+4801 448 M
+0 4171 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-4944 448 M
+4801 448 M
 0 63 V
-0 4360 R
+0 4108 R
 0 -63 V
 stroke
-4944 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-110)]
+4801 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-92)]
 ] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+5230 448 M
+0 4171 V
+stroke
 1.000 UL
+LTb
+LCb setrgbcolor
+5230 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+5230 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-90)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
+1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+5659 448 M
+0 3828 V
+0 280 R
+0 63 V
+stroke
+1.000 UL
+LTb
+LCb setrgbcolor
+5659 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+5659 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-88)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
+1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
 LTa
 LCa setrgbcolor
-5946 448 M
-0 4220 V
-0 140 R
+6089 448 M
+0 3828 V
+0 280 R
 0 63 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
-5946 448 M
+6089 448 M
 0 63 V
-0 4360 R
+0 4108 R
 0 -63 V
 stroke
-5946 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-105)]
+6089 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-86)]
 ] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
+0.500 UL
+LTa
+LCa setrgbcolor
+6518 448 M
+0 3828 V
+0 280 R
+0 63 V
+stroke
+1.000 UL
+LTb
+LCb setrgbcolor
+6518 448 M
+0 63 V
+0 4108 R
+0 -63 V
+stroke
+6518 308 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-84)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
+LTb
+LCb setrgbcolor
+0.500 UL
 LTa
 LCa setrgbcolor
 6947 448 M
-0 4423 V
+0 4171 V
 stroke
+1.000 UL
 LTb
 LCb setrgbcolor
 6947 448 M
 0 63 V
-0 4360 R
+0 4108 R
 0 -63 V
 stroke
 6947 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-100)]
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (-82)]
 ] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
 1.000 UL
 LTb
 LCb setrgbcolor
 1.000 UL
-LTb
+LTB
 LCb setrgbcolor
-938 4871 N
+938 4619 N
 938 448 L
 6009 0 V
-0 4423 V
+0 4171 V
 -6009 0 V
 Z stroke
+1.000 UP
+1.000 UL
+LTb
 LCb setrgbcolor
-112 2659 M
+LCb setrgbcolor
+133 2533 M
 currentpoint gsave translate -270 rotate 0 0 moveto
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Packet Error Rate \(PER\))]
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Packet Error Rate \(%\))]
 ] -46.7 MCshow
 grestore
+/Times-BoldItalic findfont 140 scalefont setfont
 LTb
 LCb setrgbcolor
 3942 98 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (RxPower \(dBm\))]
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Rx signal \(dBm\))]
 ] -46.7 MCshow
-LTb
-1.000 UP
-1.000 UL
+/Times-BoldItalic findfont 140 scalefont setfont
 LTb
 LCb setrgbcolor
+3942 4829 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Pkt Payload \(MSDU\) size = 7 bytes | Tx power = 0 dBm | Rx Sensitivity \(Theo\) = -106.58 dBm)]
+] -46.7 MCshow
+/Times-BoldItalic findfont 140 scalefont setfont
+LTb
 % Begin plot #1
 5.000 UL
-LT0
+LTb
 LC0 setrgbcolor
 LCb setrgbcolor
-6296 4738 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (802.15.4)]
+6296 4486 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Experimental)]
 ] -46.7 MRshow
-LT0
-6380 4738 M
+/Times-BoldItalic findfont 140 scalefont setfont
+5.000 UL
+LTb
+LC0 setrgbcolor
+6380 4486 M
 399 0 V
-938 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 1142 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
+938 4591 M
+2 -2 V
+2 1 V
+2 -1 V
+3 1 V
+2 -1 V
+2 -1 V
 2 0 V
+2 1 V
+2 1 V
+2 -2 V
+3 1 V
+2 1 V
+2 -2 V
 2 0 V
+2 -1 V
+2 -1 V
 2 0 V
+3 -1 V
+2 1 V
 2 0 V
+2 -1 V
 2 0 V
+2 -1 V
+3 -1 V
 2 0 V
+2 2 V
+2 -1 V
+2 -1 V
 2 0 V
 2 0 V
+3 -1 V
+2 1 V
+2 -2 V
+2 1 V
+2 -1 V
 2 0 V
 2 0 V
+3 1 V
+2 -2 V
+2 -2 V
+2 3 V
+2 -1 V
+2 -1 V
+2 -4 V
+3 1 V
+2 3 V
+2 -4 V
+2 1 V
+2 -3 V
+2 3 V
+2 -2 V
+3 -1 V
+2 2 V
 2 0 V
+2 -3 V
+2 -1 V
 2 0 V
+2 -1 V
 3 0 V
+2 -2 V
 2 0 V
+2 -1 V
+2 -1 V
+2 3 V
+2 -7 V
+3 2 V
+2 2 V
+2 -1 V
+2 -1 V
+2 -2 V
+2 3 V
+3 -3 V
+2 1 V
+2 1 V
+2 -6 V
 2 0 V
+2 -2 V
+2 -3 V
+3 2 V
 2 0 V
+2 2 V
+2 -2 V
+2 -2 V
 2 0 V
+2 -3 V
+3 -2 V
+2 -1 V
+2 1 V
+2 2 V
+2 -2 V
+2 -2 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 1351 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 1559 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 1767 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
+3 -3 V
+2 3 V
+2 -9 V
+2 6 V
+2 -1 V
+2 -6 V
+2 8 V
 3 0 V
+2 -10 V
+2 5 V
+2 -4 V
+2 -4 V
+2 4 V
+2 -5 V
+3 10 V
+2 -10 V
+2 -5 V
+2 2 V
+2 2 V
+2 -8 V
+3 6 V
+2 -2 V
+2 -3 V
+2 3 V
+2 -7 V
+2 2 V
 2 0 V
+3 1 V
+2 -6 V
+2 -1 V
+2 1 V
+2 -4 V
+2 3 V
+2 -3 V
+3 -6 V
+2 6 V
+2 -12 V
+2 4 V
+2 -4 V
+2 -2 V
+2 6 V
+3 -5 V
+2 -1 V
+2 2 V
+2 -1 V
+2 -2 V
+2 -5 V
+2 2 V
+3 -4 V
+2 -4 V
+2 -7 V
+2 7 V
+2 1 V
+2 -1 V
+2 -6 V
+3 -5 V
+2 -5 V
+2 7 V
+2 -9 V
+2 2 V
+2 -8 V
+2 9 V
+3 4 V
+2 -15 V
+2 8 V
 2 0 V
+2 -1 V
+2 -7 V
+3 3 V
+2 2 V
+2 -10 V
+2 1 V
+2 2 V
+2 -5 V
+2 -3 V
+3 -1 V
 2 0 V
+2 -2 V
+2 -1 V
 2 0 V
+2 4 V
+2 -6 V
+3 -14 V
+2 15 V
+2 -10 V
+2 -28 V
+2 22 V
 2 0 V
+2 -11 V
+3 8 V
+2 -9 V
+2 -4 V
+2 8 V
+2 6 V
+2 -7 V
+2 -6 V
+3 -9 V
+2 21 V
+2 -22 V
+2 -14 V
+2 22 V
+2 -11 V
 2 0 V
+3 3 V
+2 -8 V
+2 -5 V
+2 7 V
+2 -14 V
+2 3 V
+3 -11 V
+2 8 V
+2 12 V
+2 -21 V
+2 -10 V
+2 14 V
 2 0 V
+3 -8 V
+2 6 V
+2 -7 V
+2 -5 V
+2 9 V
+2 -10 V
+2 -18 V
+3 3 V
+2 -5 V
+2 7 V
+2 8 V
+2 9 V
+2 -15 V
+2 -13 V
+3 3 V
+2 9 V
+2 -17 V
+2 -5 V
+2 9 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 1976 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 2184 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 2392 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 2600 4871 M
-2 0 V
-2 0 V
-2 0 V
+2 4 V
+3 -5 V
+2 3 V
+2 -10 V
+2 -24 V
+2 19 V
+2 -9 V
+2 -19 V
+3 17 V
+2 -5 V
+2 -8 V
+2 13 V
+2 -28 V
+2 -2 V
+2 -1 V
+3 12 V
+2 -26 V
+2 15 V
+2 9 V
+stroke 1468 4293 M
+2 0 V
+2 2 V
+3 -9 V
+2 -15 V
+2 10 V
+2 3 V
+2 -36 V
+2 18 V
+2 6 V
+3 -5 V
+2 -3 V
+2 -24 V
+2 32 V
+2 -32 V
+2 6 V
+2 7 V
+3 -28 V
+2 13 V
+2 -18 V
+2 22 V
+2 6 V
+2 -12 V
+2 6 V
+3 10 V
+2 -25 V
+2 -17 V
+2 -2 V
+2 -11 V
+2 28 V
+2 -7 V
+3 20 V
+2 -13 V
+2 -37 V
+2 6 V
+2 11 V
+2 -37 V
+2 42 V
+3 -19 V
+2 17 V
+2 -33 V
+2 19 V
+2 -37 V
+2 14 V
+3 28 V
+2 -31 V
+2 -15 V
+2 4 V
+2 42 V
+2 -39 V
+2 21 V
+3 -28 V
+2 7 V
+2 15 V
+2 -39 V
+2 58 V
+2 -96 V
+2 70 V
+3 -42 V
+2 -28 V
+2 80 V
+2 -47 V
+2 -27 V
+2 -12 V
+2 18 V
+3 -73 V
+2 99 V
+2 4 V
+2 -36 V
+2 53 V
+2 -71 V
+2 -7 V
 3 0 V
+2 -8 V
+2 33 V
+2 6 V
 2 0 V
+2 -46 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 2809 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 3017 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 3225 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
+3 22 V
+2 -7 V
+2 13 V
+2 -13 V
+2 7 V
+2 -22 V
+2 -18 V
+3 64 V
+2 -95 V
+2 40 V
+2 -81 V
+2 105 V
+2 -53 V
+3 -23 V
+2 43 V
+2 -31 V
+2 -26 V
+2 -15 V
+2 81 V
+2 -9 V
+3 -72 V
+2 63 V
+2 -22 V
+2 11 V
+2 45 V
+2 -68 V
+2 -29 V
 3 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 3434 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 3642 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 3850 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
+2 29 V
+2 -29 V
+2 41 V
+2 -26 V
+2 0 V
+2 -75 V
+3 23 V
+2 89 V
+2 -89 V
+2 -89 V
+2 109 V
+2 -43 V
+2 0 V
+3 -29 V
+2 89 V
+4 -89 R
+2 72 V
+2 -109 V
+2 126 V
 3 0 V
+2 -126 V
+2 -141 V
+2 250 V
+2 -109 V
+2 89 V
+3 -230 V
+2 89 V
+2 0 V
+2 89 V
+2 0 V
+2 52 V
+2 -141 V
+3 52 V
+2 -52 V
+2 0 V
+2 89 V
+2 0 V
+2 0 V
+2 -178 V
+3 141 V
+2 -52 V
+2 0 V
+2 52 V
+2 -141 V
+2 89 V
+2 -89 V
+3 89 V
+2 89 V
+2 -89 V
+2 0 V
+4 52 R
+2 37 V
+5 -89 R
+2 -89 V
+2 89 V
+2 0 V
+2 0 V
+2 -89 V
+3 89 V
+2 -89 V
+2 0 V
+2 141 V
+2 -141 V
+2 89 V
+2 -89 V
+5 89 R
+2 52 V
+2 -52 V
+2 -89 V
+2 141 V
+3 -141 V
+4 0 R
+2 141 V
+4 -52 R
+11 0 R
+6 0 R
+3 -89 V
+2 89 V
+2 -89 V
+2 0 V
+2 0 V
+2 0 V
+2 0 V
+3 89 V
+4 -89 R
+4 0 R
+4 0 R
+11 0 R
+30 0 R
+2 89 V
+5 -89 R
+81 0 R
+5 0 R
+4 0 R
+21 0 R
+65 0 R
+% End plot #1
+% Begin plot #2
+stroke
+3.000 UL
+LTb
+LC1 setrgbcolor
+LCb setrgbcolor
+6296 4346 M
+[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Theoretical)]
+] -46.7 MRshow
+/Times-BoldItalic findfont 140 scalefont setfont
+3.000 UL
+LTb
+LC1 setrgbcolor
+6380 4346 M
+399 0 V
+938 4586 M
 2 0 V
 2 0 V
 2 0 V
+3 -1 V
 2 0 V
 2 0 V
 2 0 V
+2 -1 V
 2 0 V
 2 0 V
+3 -1 V
 2 0 V
 2 0 V
+2 -1 V
 2 0 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 4059 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 4267 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 4475 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
+2 -1 V
 3 0 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 4684 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 4892 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-stroke 5100 4871 M
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
 2 -1 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
 2 -1 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
+3 0 V
 2 -1 V
 2 0 V
-2 0 V
-2 0 V
-2 0 V
-2 0 V
 2 -1 V
 2 0 V
 2 0 V
-2 0 V
-2 0 V
 2 -1 V
-2 0 V
-2 0 V
-2 0 V
+3 0 V
 2 -1 V
 2 0 V
-2 0 V
-2 0 V
 2 -1 V
 2 0 V
-2 0 V
 2 -1 V
 2 0 V
+3 -1 V
 2 0 V
 2 -1 V
 2 0 V
-2 0 V
 2 -1 V
 2 0 V
 2 -1 V
+3 -1 V
 2 0 V
 2 -1 V
 2 0 V
 2 -1 V
 2 0 V
 2 -1 V
+3 -1 V
 2 0 V
 2 -1 V
+2 -1 V
 2 0 V
 2 -1 V
+2 -1 V
+3 0 V
+2 -1 V
+2 -1 V
 2 0 V
 2 -1 V
 2 -1 V
+2 -1 V
+3 0 V
+2 -1 V
+2 -1 V
+2 -1 V
 2 0 V
+2 -1 V
 3 -1 V
-2 0 V
 2 -1 V
 2 -1 V
 2 -1 V
 2 0 V
 2 -1 V
 2 -1 V
+3 -1 V
+2 -1 V
+2 -1 V
 2 -1 V
 2 0 V
 2 -1 V
 2 -1 V
+3 -1 V
+2 -1 V
 2 -1 V
 2 -1 V
 2 -1 V
-2 0 V
-stroke 5309 4841 M
+2 -1 V
+2 -1 V
+3 -1 V
+2 -1 V
 2 -1 V
 2 -1 V
 2 -1 V
 2 -1 V
 2 -1 V
+3 -1 V
+2 -1 V
+2 -2 V
+2 -1 V
+2 -1 V
 2 -1 V
 2 -1 V
+3 -1 V
 2 -1 V
 2 -1 V
+2 -2 V
 2 -1 V
 2 -1 V
+3 -1 V
 2 -1 V
 2 -2 V
 2 -1 V
 2 -1 V
 2 -1 V
 2 -1 V
+3 -2 V
+2 -1 V
 2 -1 V
 2 -2 V
 2 -1 V
 2 -1 V
+2 -2 V
+3 -1 V
 2 -1 V
 2 -2 V
 2 -1 V
 2 -1 V
 2 -2 V
 2 -1 V
+3 -1 V
 2 -2 V
 2 -1 V
+2 -2 V
 2 -1 V
 2 -2 V
 2 -1 V
+3 -2 V
+2 -1 V
+2 -1 V
 2 -2 V
 2 -1 V
 2 -2 V
+2 -2 V
+3 -1 V
+2 -2 V
+2 -1 V
+2 -2 V
+2 -1 V
+2 -2 V
+2 -1 V
+3 -2 V
+2 -2 V
+2 -1 V
+2 -2 V
+2 -1 V
+2 -2 V
+3 -2 V
 2 -1 V
 2 -2 V
+2 -2 V
 2 -1 V
 2 -2 V
 2 -2 V
+3 -2 V
 2 -1 V
 2 -2 V
 2 -2 V
+2 -2 V
 2 -1 V
 2 -2 V
+3 -2 V
 2 -2 V
 2 -1 V
 2 -2 V
 2 -2 V
 2 -2 V
+2 -2 V
+3 -2 V
 2 -1 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
+3 -2 V
+2 -2 V
 2 -2 V
 2 -2 V
 2 -1 V
 2 -2 V
 2 -2 V
+3 -2 V
+2 -2 V
+2 -2 V
+2 -2 V
+2 -2 V
+2 -2 V
+3 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -3 V
+3 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
 2 -2 V
+3 -2 V
+2 -3 V
+2 -2 V
+2 -2 V
+2 -2 V
+2 -2 V
+2 -2 V
+3 -3 V
+2 -2 V
+2 -2 V
+2 -2 V
 2 -2 V
 2 -3 V
 2 -2 V
+3 -2 V
+2 -2 V
+2 -3 V
 2 -2 V
 2 -2 V
 2 -3 V
 2 -2 V
+3 -2 V
+2 -2 V
+2 -3 V
 2 -2 V
 2 -2 V
 2 -3 V
 2 -2 V
+3 -3 V
+2 -2 V
 2 -2 V
 2 -3 V
+stroke 1468 4265 M
+2 -2 V
 2 -2 V
+3 -3 V
 2 -2 V
 2 -3 V
 2 -2 V
 2 -3 V
 2 -2 V
 2 -3 V
+3 -2 V
 2 -2 V
 2 -3 V
 2 -2 V
 2 -3 V
 2 -2 V
 2 -3 V
+3 -3 V
 2 -2 V
 2 -3 V
 2 -2 V
 2 -3 V
 2 -2 V
-stroke 5517 4654 M
+2 -3 V
+3 -2 V
 2 -3 V
 2 -3 V
 2 -2 V
 2 -3 V
+2 -2 V
 2 -3 V
+3 -3 V
 2 -2 V
 2 -3 V
 2 -3 V
 2 -2 V
 2 -3 V
 2 -3 V
+3 -2 V
 2 -3 V
 2 -3 V
 2 -2 V
 2 -3 V
 2 -3 V
+3 -3 V
+2 -2 V
+2 -3 V
 2 -3 V
 2 -3 V
 2 -2 V
 2 -3 V
+3 -3 V
 2 -3 V
 2 -3 V
+2 -2 V
 2 -3 V
 2 -3 V
 2 -3 V
+3 -3 V
 2 -3 V
 2 -3 V
+2 -2 V
 2 -3 V
 2 -3 V
 2 -3 V
+3 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
+3 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
+3 -3 V
 2 -3 V
 2 -3 V
-2 -4 V
 2 -3 V
 2 -3 V
 2 -3 V
 2 -3 V
+3 -3 V
 2 -3 V
-2 -4 V
 2 -3 V
 2 -3 V
 2 -3 V
-2 -4 V
 2 -3 V
+3 -4 V
 2 -3 V
 2 -3 V
-2 -4 V
 2 -3 V
 2 -3 V
-2 -4 V
+2 -3 V
+2 -3 V
+3 -4 V
+2 -3 V
+2 -3 V
 2 -3 V
 2 -3 V
 2 -4 V
 2 -3 V
+3 -3 V
+2 -3 V
 2 -4 V
 2 -3 V
 2 -3 V
+2 -3 V
+2 -4 V
+3 -3 V
+2 -3 V
 2 -4 V
 2 -3 V
+2 -3 V
 2 -4 V
 2 -3 V
+3 -3 V
 2 -4 V
 2 -3 V
 2 -4 V
 2 -3 V
 2 -3 V
 2 -4 V
+3 -3 V
 2 -4 V
 2 -3 V
+2 -3 V
 2 -4 V
 2 -3 V
+3 -4 V
+2 -3 V
 2 -4 V
 2 -3 V
 2 -4 V
 2 -3 V
 2 -4 V
+3 -3 V
 2 -4 V
 2 -3 V
 2 -4 V
 2 -4 V
 2 -3 V
 2 -4 V
+3 -3 V
 2 -4 V
-2 -3 V
 2 -4 V
+2 -3 V
 2 -4 V
 2 -3 V
 2 -4 V
+3 -4 V
+2 -3 V
 2 -4 V
 2 -4 V
 2 -3 V
 2 -4 V
-stroke 5725 4319 M
-2 -4 V
 2 -4 V
+3 -4 V
 2 -3 V
 2 -4 V
 2 -4 V
 2 -4 V
+2 -3 V
 2 -4 V
+3 -4 V
 2 -4 V
-2 -3 V
 2 -4 V
+2 -3 V
 2 -4 V
 2 -4 V
 2 -4 V
+3 -4 V
 2 -4 V
+2 -3 V
 2 -4 V
 2 -4 V
 2 -4 V
+3 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
+3 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
+3 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
+3 -4 V
 2 -4 V
 2 -5 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
+3 -4 V
 2 -5 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -4 V
 2 -5 V
-2 -4 V
+3 -4 V
 2 -4 V
 2 -4 V
 2 -5 V
 2 -4 V
 2 -4 V
-2 -5 V
-2 -4 V
+3 -5 V
 2 -4 V
-2 -5 V
 2 -4 V
 2 -5 V
 2 -4 V
 2 -4 V
 2 -5 V
+3 -4 V
 2 -4 V
 2 -5 V
 2 -4 V
-2 -4 V
 2 -5 V
 2 -4 V
 2 -5 V
-2 -4 V
+3 -4 V
 2 -5 V
 2 -4 V
 2 -5 V
 2 -4 V
 2 -5 V
-2 -5 V
 2 -4 V
-2 -5 V
+3 -5 V
 2 -4 V
 2 -5 V
-2 -5 V
 2 -4 V
 2 -5 V
-2 -5 V
 2 -4 V
 2 -5 V
-2 -5 V
+stroke 2002 3410 M
+3 -5 V
 2 -4 V
 2 -5 V
 2 -5 V
 2 -4 V
 2 -5 V
 2 -5 V
+3 -4 V
+2 -5 V
 2 -5 V
 2 -4 V
 2 -5 V
 2 -5 V
 2 -5 V
+3 -4 V
 2 -5 V
 2 -5 V
-2 -4 V
 2 -5 V
 2 -5 V
+2 -4 V
+3 -5 V
 2 -5 V
-stroke 5933 3870 M
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
-3 -5 V
-2 -4 V
+3 -4 V
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
-2 -6 V
 2 -5 V
+3 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
+3 -6 V
 2 -5 V
 2 -5 V
-2 -6 V
 2 -5 V
 2 -5 V
 2 -5 V
 2 -5 V
+3 -5 V
 2 -6 V
 2 -5 V
 2 -5 V
 2 -5 V
-2 -6 V
-2 -5 V
-2 -5 V
 2 -5 V
 2 -6 V
+3 -5 V
 2 -5 V
 2 -5 V
 2 -6 V
 2 -5 V
 2 -5 V
-2 -6 V
+3 -6 V
 2 -5 V
-2 -6 V
 2 -5 V
 2 -6 V
 2 -5 V
 2 -6 V
 2 -5 V
-2 -5 V
-2 -6 V
+3 -5 V
 2 -6 V
 2 -5 V
 2 -6 V
 2 -5 V
 2 -6 V
 2 -5 V
-2 -6 V
+3 -6 V
+2 -5 V
 2 -6 V
 2 -5 V
 2 -6 V
 2 -5 V
 2 -6 V
+3 -5 V
 2 -6 V
 2 -6 V
 2 -5 V
 2 -6 V
 2 -6 V
 2 -5 V
-2 -6 V
-2 -6 V
-2 -6 V
+3 -6 V
 2 -6 V
 2 -5 V
 2 -6 V
 2 -6 V
 2 -6 V
+2 -5 V
+3 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
 2 -5 V
 2 -6 V
 2 -6 V
+3 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
+3 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
-2 -7 V
 2 -6 V
+3 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
 2 -6 V
-2 -7 V
-2 -6 V
 2 -6 V
-2 -6 V
-stroke 6142 3294 M
+3 -6 V
 2 -7 V
 2 -6 V
 2 -6 V
 2 -6 V
-2 -7 V
 2 -6 V
+2 -7 V
+3 -6 V
 2 -6 V
 2 -7 V
 2 -6 V
 2 -6 V
-2 -7 V
 2 -6 V
 2 -7 V
-2 -6 V
+3 -6 V
 2 -7 V
 2 -6 V
-2 -7 V
 2 -6 V
 2 -7 V
 2 -6 V
 2 -7 V
-2 -6 V
+3 -6 V
 2 -7 V
 2 -6 V
 2 -7 V
-2 -7 V
 2 -6 V
 2 -7 V
+3 -6 V
 2 -7 V
 2 -6 V
 2 -7 V
 2 -7 V
 2 -6 V
 2 -7 V
+3 -6 V
 2 -7 V
 2 -7 V
 2 -7 V
 2 -6 V
 2 -7 V
 2 -7 V
+3 -7 V
+2 -6 V
 2 -7 V
 2 -7 V
 2 -7 V
 2 -7 V
 2 -6 V
+3 -7 V
 2 -7 V
 2 -7 V
 2 -7 V
 2 -7 V
 2 -7 V
 2 -7 V
+3 -7 V
 2 -7 V
 2 -7 V
 2 -7 V
-2 -8 V
 2 -7 V
 2 -7 V
 2 -7 V
+3 -7 V
 2 -7 V
+2 -8 V
 2 -7 V
 2 -7 V
-2 -8 V
 2 -7 V
 2 -7 V
+3 -8 V
 2 -7 V
-2 -8 V
 2 -7 V
 2 -7 V
 2 -8 V
 2 -7 V
-2 -7 V
+3 -7 V
 2 -8 V
 2 -7 V
 2 -7 V
 2 -8 V
 2 -7 V
 2 -8 V
+3 -7 V
 2 -7 V
 2 -8 V
 2 -7 V
 2 -8 V
 2 -7 V
 2 -8 V
+3 -8 V
 2 -7 V
 2 -8 V
-2 -8 V
 2 -7 V
 2 -8 V
 2 -8 V
 2 -7 V
-2 -8 V
+3 -8 V
 2 -8 V
 2 -7 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -7 V
+3 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
-2 -8 V
-stroke 6350 2559 M
-2 -8 V
-2 -8 V
+3 -7 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
+3 -8 V
+2 -9 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
 2 -8 V
+3 -8 V
 2 -9 V
 2 -8 V
-2 -8 V
+stroke 2537 1853 M
 2 -8 V
 2 -8 V
 2 -9 V
 2 -8 V
+3 -8 V
 2 -8 V
 2 -9 V
 2 -8 V
-2 -8 V
 2 -9 V
 2 -8 V
-2 -9 V
 2 -8 V
+3 -9 V
 2 -8 V
 2 -9 V
 2 -8 V
 2 -9 V
-2 -9 V
 2 -8 V
 2 -9 V
+3 -9 V
 2 -8 V
 2 -9 V
-2 -9 V
 2 -8 V
 2 -9 V
 2 -9 V
 2 -8 V
+3 -9 V
 2 -9 V
 2 -9 V
 2 -9 V
 2 -8 V
 2 -9 V
 2 -9 V
+3 -9 V
 2 -9 V
 2 -9 V
 2 -9 V
 2 -9 V
 2 -9 V
+3 -8 V
 2 -9 V
+2 -10 V
 2 -9 V
 2 -9 V
 2 -9 V
 2 -9 V
-2 -9 V
-2 -9 V
-2 -9 V
-2 -9 V
+3 -9 V
 2 -9 V
 2 -9 V
 2 -10 V
 2 -9 V
 2 -9 V
 2 -9 V
-2 -10 V
+3 -10 V
 2 -9 V
 2 -9 V
 2 -10 V
 2 -9 V
 2 -9 V
 2 -10 V
-2 -9 V
+3 -9 V
 2 -10 V
 2 -9 V
 2 -10 V
 2 -9 V
 2 -10 V
 2 -9 V
+3 -10 V
 2 -10 V
 2 -9 V
 2 -10 V
-2 -10 V
 2 -9 V
 2 -10 V
 2 -10 V
-2 -10 V
+3 -10 V
 2 -9 V
 2 -10 V
 2 -10 V
 2 -10 V
 2 -10 V
-2 -10 V
+3 -10 V
 2 -9 V
 2 -10 V
 2 -10 V
-2 -10 V
-2 -10 V
-2 -10 V
-2 -10 V
-2 -10 V
-2 -10 V
-stroke 6558 1626 M
-2 -11 V
-2 -10 V
-2 -10 V
-2 -10 V
-2 -10 V
-2 -11 V
-2 -10 V
-2 -10 V
-2 -10 V
-2 -10 V
-2 -11 V
-2 -10 V
-2 -11 V
-2 -10 V
-2 -10 V
 2 -11 V
+2 -9 V
 2 -11 V
+3 -9 V
 2 -10 V
 2 -11 V
 2 -10 V
-2 -11 V
 2 -10 V
 2 -11 V
+2 -9 V
+3 -10 V
 2 -11 V
 2 -11 V
-2 -10 V
-2 -11 V
-3 -11 V
-2 -10 V
-2 -11 V
-2 -12 V
-2 -10 V
-2 -10 V
-2 -12 V
-2 -12 V
-2 -9 V
+2 -8 V
 2 -9 V
-2 -16 V
+2 -15 V
 2 -11 V
-2 -6 V
-2 -14 V
-2 -14 V
-2 -8 V
+3 -5 V
+2 -13 V
+2 -13 V
 2 -8 V
+2 -7 V
 2 -18 V
-2 -11 V
-2 -11 V
+2 -9 V
+3 -10 V
+2 -12 V
 2 -12 V
-2 -13 V
 2 0 V
-2 -14 V
+2 -13 V
 2 -16 V
-2 -19 V
-2 0 V
-2 -21 V
-2 0 V
-2 -25 V
-2 0 V
-2 0 V
-2 -30 V
-2 0 V
+2 -17 V
+3 0 V
+2 -20 V
 2 0 V
-2 -40 V
+2 -23 V
 2 0 V
 2 0 V
-2 -55 V
+2 -29 V
+3 0 V
 2 0 V
+2 -37 V
 2 0 V
 2 0 V
+2 -52 V
+3 0 V
 2 0 V
 2 0 V
-2 -96 V
 2 0 V
 2 0 V
+2 -89 V
 2 0 V
+3 0 V
 2 0 V
 2 0 V
 2 0 V
 2 0 V
 2 0 V
 2 0 V
+3 0 V
 2 0 V
 2 0 V
 2 0 V
-0 -332 V
-% End plot #1
+% End plot #2
 stroke
-1.000 UL
+2.000 UL
 LTb
 LCb setrgbcolor
-938 4871 N
+1.000 UL
+LTB
+LCb setrgbcolor
+938 4619 N
 938 448 L
 6009 0 V
-0 4423 V
+0 4171 V
 -6009 0 V
 Z stroke
 1.000 UP
 1.000 UL
+[] 0 setdash
+0.00 0.50 1.00 C
+938 4003 M
+6009 0 V
+stroke
 LTb
 LCb setrgbcolor
 stroke
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/doc/802-15-4-psr-distance.eps ns-3.38/src/lr-wpan/doc/802-15-4-psr-distance.eps
--- ns-3.37/src/lr-wpan/doc/802-15-4-psr-distance.eps	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/lr-wpan/doc/802-15-4-psr-distance.eps	2023-03-16 20:54:23.632868500 -0700
@@ -1,908 +1,590 @@
-%!PS-Adobe-2.0 EPSF-2.0
-%%Title: 802.15.4-psr-distance.eps
-%%Creator: gnuplot 4.4 patchlevel 2
-%%CreationDate: Tue Oct  4 13:04:05 2011
-%%DocumentFonts: (atend)
-%%BoundingBox: 50 50 410 302
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: cairo 1.16.0 (https://cairographics.org)
+%%CreationDate: Tue Feb  7 16:42:56 2023
+%%Pages: 1
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 2
+%%BoundingBox: 4 1 354 245
 %%EndComments
 %%BeginProlog
-/gnudict 256 dict def
-gnudict begin
-%
-% The following true/false flags may be edited by hand if desired.
-% The unit line width and grayscale image gamma correction may also be changed.
-%
-/Color true def
-/Blacktext false def
-/Solid false def
-/Dashlength 1 def
-/Landscape false def
-/Level1 false def
-/Rounded false def
-/ClipToBoundingBox false def
-/TransparentPatterns false def
-/gnulinewidth 5.000 def
-/userlinewidth gnulinewidth def
-/Gamma 1.0 def
-%
-/vshift -46 def
-/dl1 {
-  10.0 Dashlength mul mul
-  Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if
+50 dict begin
+/q { gsave } bind def
+/Q { grestore } bind def
+/cm { 6 array astore concat } bind def
+/w { setlinewidth } bind def
+/J { setlinecap } bind def
+/j { setlinejoin } bind def
+/M { setmiterlimit } bind def
+/d { setdash } bind def
+/m { moveto } bind def
+/l { lineto } bind def
+/c { curveto } bind def
+/h { closepath } bind def
+/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
+      0 exch rlineto 0 rlineto closepath } bind def
+/S { stroke } bind def
+/f { fill } bind def
+/f* { eofill } bind def
+/n { newpath } bind def
+/W { clip } bind def
+/W* { eoclip } bind def
+/BT { } bind def
+/ET { } bind def
+/BDC { mark 3 1 roll /BDC pdfmark } bind def
+/EMC { mark /EMC pdfmark } bind def
+/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
+/Tj { show currentpoint cairo_store_point } bind def
+/TJ {
+  {
+    dup
+    type /stringtype eq
+    { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
+  } forall
+  currentpoint cairo_store_point
+} bind def
+/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
+    cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
+/Tf { pop /cairo_font exch def /cairo_font_matrix where
+      { pop cairo_selectfont } if } bind def
+/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
+      /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
+      /cairo_font where { pop cairo_selectfont } if } bind def
+/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
+      cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
+/g { setgray } bind def
+/rg { setrgbcolor } bind def
+/d1 { setcachedevice } bind def
+/cairo_data_source {
+  CairoDataIndex CairoData length lt
+    { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }
+    { () } ifelse
 } def
-/dl2 {
-  10.0 Dashlength mul mul
-  Rounded { currentlinewidth 0.75 mul add } if
-} def
-/hpt_ 31.5 def
-/vpt_ 31.5 def
-/hpt hpt_ def
-/vpt vpt_ def
-Level1 {} {
-/SDict 10 dict def
-systemdict /pdfmark known not {
-  userdict /pdfmark systemdict /cleartomark get put
-} if
-SDict begin [
-  /Title (802.15.4-psr-distance.eps)
-  /Subject (gnuplot plot)
-  /Creator (gnuplot 4.4 patchlevel 2)
-  /Author (tomh)
-%  /Producer (gnuplot)
-%  /Keywords ()
-  /CreationDate (Tue Oct  4 13:04:05 2011)
-  /DOCINFO pdfmark
-end
-} ifelse
-/doclip {
-  ClipToBoundingBox {
-    newpath 50 50 moveto 410 50 lineto 410 302 lineto 50 302 lineto closepath
-    clip
-  } if
-} def
-%
-% Gnuplot Prolog Version 4.4 (August 2010)
-%
-%/SuppressPDFMark true def
-%
-/M {moveto} bind def
-/L {lineto} bind def
-/R {rmoveto} bind def
-/V {rlineto} bind def
-/N {newpath moveto} bind def
-/Z {closepath} bind def
-/C {setrgbcolor} bind def
-/f {rlineto fill} bind def
-/g {setgray} bind def
-/Gshow {show} def   % May be redefined later in the file to support UTF-8
-/vpt2 vpt 2 mul def
-/hpt2 hpt 2 mul def
-/Lshow {currentpoint stroke M 0 vshift R 
-	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
-/Rshow {currentpoint stroke M dup stringwidth pop neg vshift R
-	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
-/Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R 
-	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
-/UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def
-  /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def
-/DL {Color {setrgbcolor Solid {pop []} if 0 setdash}
- {pop pop pop 0 setgray Solid {pop []} if 0 setdash} ifelse} def
-/BL {stroke userlinewidth 2 mul setlinewidth
-	Rounded {1 setlinejoin 1 setlinecap} if} def
-/AL {stroke userlinewidth 2 div setlinewidth
-	Rounded {1 setlinejoin 1 setlinecap} if} def
-/UL {dup gnulinewidth mul /userlinewidth exch def
-	dup 1 lt {pop 1} if 10 mul /udl exch def} def
-/PL {stroke userlinewidth setlinewidth
-	Rounded {1 setlinejoin 1 setlinecap} if} def
-3.8 setmiterlimit
-% Default Line colors
-/LCw {1 1 1} def
-/LCb {0 0 0} def
-/LCa {0 0 0} def
-/LC0 {1 0 0} def
-/LC1 {0 1 0} def
-/LC2 {0 0 1} def
-/LC3 {1 0 1} def
-/LC4 {0 1 1} def
-/LC5 {1 1 0} def
-/LC6 {0 0 0} def
-/LC7 {1 0.3 0} def
-/LC8 {0.5 0.5 0.5} def
-% Default Line Types
-/LTw {PL [] 1 setgray} def
-/LTb {BL [] LCb DL} def
-/LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def
-/LT0 {PL [] LC0 DL} def
-/LT1 {PL [4 dl1 2 dl2] LC1 DL} def
-/LT2 {PL [2 dl1 3 dl2] LC2 DL} def
-/LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def
-/LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def
-/LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def
-/LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def
-/LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def
-/LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def
-/Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def
-/Dia {stroke [] 0 setdash 2 copy vpt add M
-  hpt neg vpt neg V hpt vpt neg V
-  hpt vpt V hpt neg vpt V closepath stroke
-  Pnt} def
-/Pls {stroke [] 0 setdash vpt sub M 0 vpt2 V
-  currentpoint stroke M
-  hpt neg vpt neg R hpt2 0 V stroke
- } def
-/Box {stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M
-  0 vpt2 neg V hpt2 0 V 0 vpt2 V
-  hpt2 neg 0 V closepath stroke
-  Pnt} def
-/Crs {stroke [] 0 setdash exch hpt sub exch vpt add M
-  hpt2 vpt2 neg V currentpoint stroke M
-  hpt2 neg 0 R hpt2 vpt2 V stroke} def
-/TriU {stroke [] 0 setdash 2 copy vpt 1.12 mul add M
-  hpt neg vpt -1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt 1.62 mul V closepath stroke
-  Pnt} def
-/Star {2 copy Pls Crs} def
-/BoxF {stroke [] 0 setdash exch hpt sub exch vpt add M
-  0 vpt2 neg V hpt2 0 V 0 vpt2 V
-  hpt2 neg 0 V closepath fill} def
-/TriUF {stroke [] 0 setdash vpt 1.12 mul add M
-  hpt neg vpt -1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt 1.62 mul V closepath fill} def
-/TriD {stroke [] 0 setdash 2 copy vpt 1.12 mul sub M
-  hpt neg vpt 1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt -1.62 mul V closepath stroke
-  Pnt} def
-/TriDF {stroke [] 0 setdash vpt 1.12 mul sub M
-  hpt neg vpt 1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt -1.62 mul V closepath fill} def
-/DiaF {stroke [] 0 setdash vpt add M
-  hpt neg vpt neg V hpt vpt neg V
-  hpt vpt V hpt neg vpt V closepath fill} def
-/Pent {stroke [] 0 setdash 2 copy gsave
-  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
-  closepath stroke grestore Pnt} def
-/PentF {stroke [] 0 setdash gsave
-  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
-  closepath fill grestore} def
-/Circle {stroke [] 0 setdash 2 copy
-  hpt 0 360 arc stroke Pnt} def
-/CircleF {stroke [] 0 setdash hpt 0 360 arc fill} def
-/C0 {BL [] 0 setdash 2 copy moveto vpt 90 450 arc} bind def
-/C1 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 0 90 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C2 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 90 180 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C3 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 0 180 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C4 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 180 270 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C5 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 0 90 arc
-	2 copy moveto
-	2 copy vpt 180 270 arc closepath fill
-	vpt 0 360 arc} bind def
-/C6 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 90 270 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C7 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 0 270 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C8 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 270 360 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C9 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 270 450 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C10 {BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill
-	2 copy moveto
-	2 copy vpt 90 180 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C11 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 0 180 arc closepath fill
-	2 copy moveto
-	2 copy vpt 270 360 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C12 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 180 360 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C13 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 0 90 arc closepath fill
-	2 copy moveto
-	2 copy vpt 180 360 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/C14 {BL [] 0 setdash 2 copy moveto
-	2 copy vpt 90 360 arc closepath fill
-	vpt 0 360 arc} bind def
-/C15 {BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill
-	vpt 0 360 arc closepath} bind def
-/Rec {newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
-	neg 0 rlineto closepath} bind def
-/Square {dup Rec} bind def
-/Bsquare {vpt sub exch vpt sub exch vpt2 Square} bind def
-/S0 {BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare} bind def
-/S1 {BL [] 0 setdash 2 copy vpt Square fill Bsquare} bind def
-/S2 {BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def
-/S3 {BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def
-/S4 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def
-/S5 {BL [] 0 setdash 2 copy 2 copy vpt Square fill
-	exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def
-/S6 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare} bind def
-/S7 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill
-	2 copy vpt Square fill Bsquare} bind def
-/S8 {BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare} bind def
-/S9 {BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare} bind def
-/S10 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill
-	Bsquare} bind def
-/S11 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill
-	Bsquare} bind def
-/S12 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare} bind def
-/S13 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
-	2 copy vpt Square fill Bsquare} bind def
-/S14 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
-	2 copy exch vpt sub exch vpt Square fill Bsquare} bind def
-/S15 {BL [] 0 setdash 2 copy Bsquare fill Bsquare} bind def
-/D0 {gsave translate 45 rotate 0 0 S0 stroke grestore} bind def
-/D1 {gsave translate 45 rotate 0 0 S1 stroke grestore} bind def
-/D2 {gsave translate 45 rotate 0 0 S2 stroke grestore} bind def
-/D3 {gsave translate 45 rotate 0 0 S3 stroke grestore} bind def
-/D4 {gsave translate 45 rotate 0 0 S4 stroke grestore} bind def
-/D5 {gsave translate 45 rotate 0 0 S5 stroke grestore} bind def
-/D6 {gsave translate 45 rotate 0 0 S6 stroke grestore} bind def
-/D7 {gsave translate 45 rotate 0 0 S7 stroke grestore} bind def
-/D8 {gsave translate 45 rotate 0 0 S8 stroke grestore} bind def
-/D9 {gsave translate 45 rotate 0 0 S9 stroke grestore} bind def
-/D10 {gsave translate 45 rotate 0 0 S10 stroke grestore} bind def
-/D11 {gsave translate 45 rotate 0 0 S11 stroke grestore} bind def
-/D12 {gsave translate 45 rotate 0 0 S12 stroke grestore} bind def
-/D13 {gsave translate 45 rotate 0 0 S13 stroke grestore} bind def
-/D14 {gsave translate 45 rotate 0 0 S14 stroke grestore} bind def
-/D15 {gsave translate 45 rotate 0 0 S15 stroke grestore} bind def
-/DiaE {stroke [] 0 setdash vpt add M
-  hpt neg vpt neg V hpt vpt neg V
-  hpt vpt V hpt neg vpt V closepath stroke} def
-/BoxE {stroke [] 0 setdash exch hpt sub exch vpt add M
-  0 vpt2 neg V hpt2 0 V 0 vpt2 V
-  hpt2 neg 0 V closepath stroke} def
-/TriUE {stroke [] 0 setdash vpt 1.12 mul add M
-  hpt neg vpt -1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt 1.62 mul V closepath stroke} def
-/TriDE {stroke [] 0 setdash vpt 1.12 mul sub M
-  hpt neg vpt 1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt -1.62 mul V closepath stroke} def
-/PentE {stroke [] 0 setdash gsave
-  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
-  closepath stroke grestore} def
-/CircE {stroke [] 0 setdash 
-  hpt 0 360 arc stroke} def
-/Opaque {gsave closepath 1 setgray fill grestore 0 setgray closepath} def
-/DiaW {stroke [] 0 setdash vpt add M
-  hpt neg vpt neg V hpt vpt neg V
-  hpt vpt V hpt neg vpt V Opaque stroke} def
-/BoxW {stroke [] 0 setdash exch hpt sub exch vpt add M
-  0 vpt2 neg V hpt2 0 V 0 vpt2 V
-  hpt2 neg 0 V Opaque stroke} def
-/TriUW {stroke [] 0 setdash vpt 1.12 mul add M
-  hpt neg vpt -1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt 1.62 mul V Opaque stroke} def
-/TriDW {stroke [] 0 setdash vpt 1.12 mul sub M
-  hpt neg vpt 1.62 mul V
-  hpt 2 mul 0 V
-  hpt neg vpt -1.62 mul V Opaque stroke} def
-/PentW {stroke [] 0 setdash gsave
-  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
-  Opaque stroke grestore} def
-/CircW {stroke [] 0 setdash 
-  hpt 0 360 arc Opaque stroke} def
-/BoxFill {gsave Rec 1 setgray fill grestore} def
-/Density {
-  /Fillden exch def
-  currentrgbcolor
-  /ColB exch def /ColG exch def /ColR exch def
-  /ColR ColR Fillden mul Fillden sub 1 add def
-  /ColG ColG Fillden mul Fillden sub 1 add def
-  /ColB ColB Fillden mul Fillden sub 1 add def
-  ColR ColG ColB setrgbcolor} def
-/BoxColFill {gsave Rec PolyFill} def
-/PolyFill {gsave Density fill grestore grestore} def
-/h {rlineto rlineto rlineto gsave closepath fill grestore} bind def
-%
-% PostScript Level 1 Pattern Fill routine for rectangles
-% Usage: x y w h s a XX PatternFill
-%	x,y = lower left corner of box to be filled
-%	w,h = width and height of box
-%	  a = angle in degrees between lines and x-axis
-%	 XX = 0/1 for no/yes cross-hatch
-%
-/PatternFill {gsave /PFa [ 9 2 roll ] def
-  PFa 0 get PFa 2 get 2 div add PFa 1 get PFa 3 get 2 div add translate
-  PFa 2 get -2 div PFa 3 get -2 div PFa 2 get PFa 3 get Rec
-  gsave 1 setgray fill grestore clip
-  currentlinewidth 0.5 mul setlinewidth
-  /PFs PFa 2 get dup mul PFa 3 get dup mul add sqrt def
-  0 0 M PFa 5 get rotate PFs -2 div dup translate
-  0 1 PFs PFa 4 get div 1 add floor cvi
-	{PFa 4 get mul 0 M 0 PFs V} for
-  0 PFa 6 get ne {
-	0 1 PFs PFa 4 get div 1 add floor cvi
-	{PFa 4 get mul 0 2 1 roll M PFs 0 V} for
- } if
-  stroke grestore} def
-%
-/languagelevel where
- {pop languagelevel} {1} ifelse
- 2 lt
-	{/InterpretLevel1 true def}
-	{/InterpretLevel1 Level1 def}
- ifelse
-%
-% PostScript level 2 pattern fill definitions
-%
-/Level2PatternFill {
-/Tile8x8 {/PaintType 2 /PatternType 1 /TilingType 1 /BBox [0 0 8 8] /XStep 8 /YStep 8}
-	bind def
-/KeepColor {currentrgbcolor [/Pattern /DeviceRGB] setcolorspace} bind def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke} 
->> matrix makepattern
-/Pat1 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke
-	0 4 M 4 8 L 8 4 L 4 0 L 0 4 L stroke}
->> matrix makepattern
-/Pat2 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop 0 0 M 0 8 L
-	8 8 L 8 0 L 0 0 L fill}
->> matrix makepattern
-/Pat3 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop -4 8 M 8 -4 L
-	0 12 M 12 0 L stroke}
->> matrix makepattern
-/Pat4 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop -4 0 M 8 12 L
-	0 -4 M 12 8 L stroke}
->> matrix makepattern
-/Pat5 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop -2 8 M 4 -4 L
-	0 12 M 8 -4 L 4 12 M 10 0 L stroke}
->> matrix makepattern
-/Pat6 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop -2 0 M 4 12 L
-	0 -4 M 8 12 L 4 -4 M 10 8 L stroke}
->> matrix makepattern
-/Pat7 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop 8 -2 M -4 4 L
-	12 0 M -4 8 L 12 4 M 0 10 L stroke}
->> matrix makepattern
-/Pat8 exch def
-<< Tile8x8
- /PaintProc {0.5 setlinewidth pop 0 -2 M 12 4 L
-	-4 0 M 12 8 L -4 4 M 8 10 L stroke}
->> matrix makepattern
-/Pat9 exch def
-/Pattern1 {PatternBgnd KeepColor Pat1 setpattern} bind def
-/Pattern2 {PatternBgnd KeepColor Pat2 setpattern} bind def
-/Pattern3 {PatternBgnd KeepColor Pat3 setpattern} bind def
-/Pattern4 {PatternBgnd KeepColor Landscape {Pat5} {Pat4} ifelse setpattern} bind def
-/Pattern5 {PatternBgnd KeepColor Landscape {Pat4} {Pat5} ifelse setpattern} bind def
-/Pattern6 {PatternBgnd KeepColor Landscape {Pat9} {Pat6} ifelse setpattern} bind def
-/Pattern7 {PatternBgnd KeepColor Landscape {Pat8} {Pat7} ifelse setpattern} bind def
-} def
-%
-%
-%End of PostScript Level 2 code
-%
-/PatternBgnd {
-  TransparentPatterns {} {gsave 1 setgray fill grestore} ifelse
-} def
-%
-% Substitute for Level 2 pattern fill codes with
-% grayscale if Level 2 support is not selected.
-%
-/Level1PatternFill {
-/Pattern1 {0.250 Density} bind def
-/Pattern2 {0.500 Density} bind def
-/Pattern3 {0.750 Density} bind def
-/Pattern4 {0.125 Density} bind def
-/Pattern5 {0.375 Density} bind def
-/Pattern6 {0.625 Density} bind def
-/Pattern7 {0.875 Density} bind def
-} def
-%
-% Now test for support of Level 2 code
-%
-Level1 {Level1PatternFill} {Level2PatternFill} ifelse
-%
-/Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont
-dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall
-currentdict end definefont pop
-/MFshow {
-   { dup 5 get 3 ge
-     { 5 get 3 eq {gsave} {grestore} ifelse }
-     {dup dup 0 get findfont exch 1 get scalefont setfont
-     [ currentpoint ] exch dup 2 get 0 exch R dup 5 get 2 ne {dup dup 6
-     get exch 4 get {Gshow} {stringwidth pop 0 R} ifelse }if dup 5 get 0 eq
-     {dup 3 get {2 get neg 0 exch R pop} {pop aload pop M} ifelse} {dup 5
-     get 1 eq {dup 2 get exch dup 3 get exch 6 get stringwidth pop -2 div
-     dup 0 R} {dup 6 get stringwidth pop -2 div 0 R 6 get
-     show 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop
-     pop aload pop M} ifelse }ifelse }ifelse }
-     ifelse }
-   forall} def
-/Gswidth {dup type /stringtype eq {stringwidth} {pop (n) stringwidth} ifelse} def
-/MFwidth {0 exch { dup 5 get 3 ge { 5 get 3 eq { 0 } { pop } ifelse }
- {dup 3 get{dup dup 0 get findfont exch 1 get scalefont setfont
-     6 get Gswidth pop add} {pop} ifelse} ifelse} forall} def
-/MLshow { currentpoint stroke M
-  0 exch R
-  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
-/MRshow { currentpoint stroke M
-  exch dup MFwidth neg 3 -1 roll R
-  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
-/MCshow { currentpoint stroke M
-  exch dup MFwidth -2 div 3 -1 roll R
-  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
-/XYsave    { [( ) 1 2 true false 3 ()] } bind def
-/XYrestore { [( ) 1 2 true false 4 ()] } bind def
-end
+/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def
+/cairo_image { image cairo_flush_ascii85_file } def
+/cairo_imagemask { imagemask cairo_flush_ascii85_file } def
 %%EndProlog
-gnudict begin
-gsave
-doclip
-50 50 translate
-0.050 0.050 scale
-0 setgray
-newpath
-(Times-BoldItalic) findfont 140 scalefont setfont
-1.000 UL
-LTb
-1.000 UL
-LTa
-686 448 M
-6261 0 V
-stroke
-LTb
-686 448 M
-63 0 V
-6198 0 R
--63 0 V
-stroke
-602 448 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0)]
-] -46.7 MRshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-686 1282 M
-6261 0 V
-stroke
-LTb
-686 1282 M
-63 0 V
-6198 0 R
--63 0 V
-stroke
-602 1282 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0.2)]
-] -46.7 MRshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-686 2116 M
-6261 0 V
-stroke
-LTb
-686 2116 M
-63 0 V
-6198 0 R
--63 0 V
-stroke
-602 2116 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0.4)]
-] -46.7 MRshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-686 2951 M
-6261 0 V
-stroke
-LTb
-686 2951 M
-63 0 V
-6198 0 R
--63 0 V
-stroke
-602 2951 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0.6)]
-] -46.7 MRshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-686 3785 M
-6261 0 V
-stroke
-LTb
-686 3785 M
-63 0 V
-6198 0 R
--63 0 V
-stroke
-602 3785 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0.8)]
-] -46.7 MRshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-686 4619 M
-6261 0 V
-stroke
-LTb
-686 4619 M
-63 0 V
-6198 0 R
--63 0 V
-stroke
-602 4619 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 1)]
-] -46.7 MRshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-686 448 M
-0 4171 V
-stroke
-LTb
-686 448 M
-0 63 V
-0 4108 R
-0 -63 V
-stroke
-686 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 0)]
-] -46.7 MCshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-2251 448 M
-0 4171 V
-stroke
-LTb
-2251 448 M
-0 63 V
-0 4108 R
-0 -63 V
-stroke
-2251 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 50)]
-] -46.7 MCshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-3816 448 M
-0 4171 V
-stroke
-LTb
-3816 448 M
-0 63 V
-0 4108 R
-0 -63 V
-stroke
-3816 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 100)]
-] -46.7 MCshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-5382 448 M
-0 3968 V
-0 140 R
-0 63 V
-stroke
-LTb
-5382 448 M
-0 63 V
-0 4108 R
-0 -63 V
-stroke
-5382 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 150)]
-] -46.7 MCshow
-1.000 UL
-LTb
-1.000 UL
-LTa
-6947 448 M
-0 4171 V
-stroke
-LTb
-6947 448 M
-0 63 V
-0 4108 R
-0 -63 V
-stroke
-6947 308 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 ( 200)]
-] -46.7 MCshow
-1.000 UL
-LTb
-1.000 UL
-LTb
-686 4619 N
-686 448 L
-6261 0 V
-0 4171 V
--6261 0 V
-Z stroke
-LCb setrgbcolor
-112 2533 M
-currentpoint gsave translate -270 rotate 0 0 moveto
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Packet Success Rate \(PSR\))]
-] -46.7 MCshow
-grestore
-LTb
-LCb setrgbcolor
-3816 98 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (distance \(m\))]
-] -46.7 MCshow
-LTb
-3816 4829 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (Packet \(MSDU\) size = 20 bytes; tx power = 0 dBm; channel = 11)]
-] -46.7 MCshow
-1.000 UP
-1.000 UL
-LTb
-% Begin plot #1
-5.000 UL
-LT0
-LCb setrgbcolor
-6296 4486 M
-[ [(Times-BoldItalic) 140.0 0.0 true true 0 (802.15.4-psr-vs-distance)]
-] -46.7 MRshow
-LT0
-6380 4486 M
-399 0 V
-717 4619 M
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 -4 V
-31 -4 V
-31 8 V
-31 0 V
-32 -13 V
-31 -4 V
-31 -16 V
-32 -30 V
-31 -41 V
-31 4 V
-32 -88 V
-31 -62 V
-31 -100 V
-32 -113 V
-31 -83 V
-31 -176 V
-31 -141 V
-32 -263 V
-31 -367 V
-31 -175 V
-32 -284 V
-31 -359 V
-31 -308 V
-32 -217 V
-31 -234 V
-31 -296 V
-31 -192 V
-32 -96 V
-31 -162 V
-31 -163 V
-stroke 3910 640 M
-32 -34 V
-31 -41 V
-31 -42 V
-32 -46 V
-31 -12 V
-31 -13 V
-32 17 V
-31 -13 V
-31 -8 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-31 0 V
-32 0 V
-31 0 V
-31 0 V
-32 0 V
-% End plot #1
-stroke
-1.000 UL
-LTb
-686 4619 N
-686 448 L
-6261 0 V
-0 4171 V
--6261 0 V
-Z stroke
-1.000 UP
-1.000 UL
-LTb
-stroke
-grestore
-end
+%%BeginSetup
+%%BeginResource: font f-0-0
+%!FontType1-1.1 f-0-0 1.0
+11 dict begin
+/FontName /f-0-0 def
+/PaintType 0 def
+/FontType 1 def
+/FontMatrix [0.001 0 0 0.001 0 0] readonly def
+/FontBBox {-120 -205 917 699                                 } readonly def
+/Encoding 256 array
+0 1 255 {1 index exch /.notdef put} for
+dup 32 /space put
+dup 40 /parenleft put
+dup 41 /parenright put
+dup 45 /hyphen put
+dup 46 /period put
+dup 48 /zero put
+dup 49 /one put
+dup 50 /two put
+dup 52 /four put
+dup 53 /five put
+dup 54 /six put
+dup 55 /seven put
+dup 56 /eight put
+dup 57 /nine put
+dup 59 /semicolon put
+dup 61 /equal put
+dup 66 /B put
+dup 68 /D put
+dup 77 /M put
+dup 80 /P put
+dup 82 /R put
+dup 83 /S put
+dup 85 /U put
+dup 97 /a put
+dup 98 /b put
+dup 99 /c put
+dup 100 /d put
+dup 101 /e put
+dup 104 /h put
+dup 105 /i put
+dup 107 /k put
+dup 108 /l put
+dup 109 /m put
+dup 110 /n put
+dup 111 /o put
+dup 112 /p put
+dup 114 /r put
+dup 115 /s put
+dup 116 /t put
+dup 117 /u put
+dup 119 /w put
+dup 120 /x put
+dup 121 /y put
+dup 122 /z put
+readonly def
+currentdict end
+currentfile eexec
+f983ef0097ece636fb4a96c74d26ab84185f6dfa4a16a7a1c27bbe3f1156aea698df336d20b467
+b10e7f33846656653c5ac6962759d3056cbdb3190bac614b984bf5a132dc418192443014ba63de
+800d392b6fea026574bb2535fd7bb5338f35bf15a88ea328fdaa49670c7852e3d060f3c5d6b07f
+2ef6d0f22646c5d18e19a2ae3ee120390f6dd96f76dcf1e127de5e9299077a00c17c0d71e36e5b
+9d5ec58fceda57739a6a4214d4b79d6c48d2784b60c320323c7acddddf34db833cac0cf109f799
+69d114a330d372e5c978a66acc84e3fe5557f6240856a013ffaa0199444e5c5036f775eba4a5c5
+8cde66cf604b9aca2178431127b8a1ff7ed633a65c04600af5f573483112251ca91d1147e394b8
+770feb7fd46a77e2961ec91bb9263dc0e43eb9a3b990c9761127f2236d69136be42a8ece293f20
+5e8c6985d36b970354af6976b59de1a9aee59b1b7c4d57d3e7115622fce48a99cd7903af13d778
+b61bdf26278782d0626afcd805edf6882452f3ba0b8a06550e5e742680a1db9e9c38aff42a0b05
+0a9f5f0b9355ad3cd836918714c3f69d1b8ce860e052d6ee0bf9c73c967566711ee88068b4f1e1
+8e9f1de06e22b50a1802b961bf321e086c078478c600fa019a221bda4d0691a1bb176b02a3095e
+c2a8437df5f02ae027612189ff9fa60c7236e5bf6cafe3cf2387597370a08fa1249ace038b4c9a
+ecc6e711908d08521015920bfc534c4cf64bf8005abe859dc2969a27784051abe57be19aba6937
+c8d9cd449d007816c1381fc06b1f9d0f750dc5eda5f766e4245b5797d2d8e322f2a38ed3880ae7
+13f2edc17311c12c168e50150fe0e343da65e3f2bd139450022b55e23392a2de02f289617dd4cd
+f3c47d52ea553d5a2feaf00f8aa0c17f95745f872dce13d2e1a26248be73aef3e76cf72414b24a
+a796fcf2778035fbd97ad4fc2b916c3a324e4d36cee9fcb580e5d1d9073ae58a01be98418b9425
+0619218c1fcba7037a689ec83fc42d6dea7d609e3cc45d07e289084a878c33a77c492427e6408f
+895707f95831bf9c8b230d9aa553f2e62ed301bf822df01c64e122b25bb3e929552940d69d9bea
+0db22cc1f90a5cf9eb63dd3fd52f41d5c1c8a9ac478b48459e5882b062bc78675aa1e2d6e70326
+69af62ed53d03ed4c7e7b4c3d5c63878fc98a0f891b0b3f4112f9203b201b4df315d423d009273
+6dd784bf0eebd9bc9407c803adaa20de750884ade854491b14660b23ce1830344c320cb26e2bd2
+41b916c30c94de287fd3b8ab8116011e8c5e43c528d22dbc4f1bf6400e422b7f0e02eb53adf5a0
+8f5a1d4fb192188430efa7dd558329569b4cdf8f07ca29b0f906bdb164ecd6762e55c4113af599
+e4b4abcdd41d05de6bd6add6fb8f684b0d9583a56602d13c5b64e2e837ec5c238704efe8039f95
+b5edc8221765f744db766f5224c324cab2ecc72cb20b58b5f19426e5d29f3a5b429cee7b45a71d
+781ea61d2ce93533201e8706f71d3a9c29b4b2db6398a8a9ecbcf3588ca6da6fca2a72fbe479ad
+0af932e399f3932445b10b4740fb1d437ccabc5b42701242666828fdc8b131c8ea8eca9e0b822c
+7f88e9fc49256dc6f6e52fc0a00c38428324478492d15b18959fb09cf28004562bf53b6eca151a
+7994261c2c3e6fb99fa87fe90500277c32f4d902dc6405f5cf752555994d05bd98c9902a518cc6
+1b3ac3ffd041ed82d8279a2a1c3c5729aa6dfd811b08ac5715c20fb012eb3ddb611d5aa0386a5d
+cd8d878e1b90baa9073032eb31ed3f35385621284e8fd6d31b0bdd56f214c324928eb57bbe7b4d
+24cd67ee34b6a22c77a6d80ff843102c723511c3c261dec9462b6a191384d99d52545a9bbae451
+89c114aacd395f5d2ed9b9c2b5eab9f109c36c915f36678ee984a1b8a65b38ed6d48ef11648b9a
+4e21d51bae9df39b17e0cc40a0cd3c453f85bcadcef2210d39d1b3259c3cd7bce8f8da5cc94cdd
+32201bc55a2f74431d84f5573b59e6e1ac3d7b3b80725961993759f63077b206d987d8f61ab3b2
+1173653792b1e586728bbfbbd5687d6acac39c5c984ccf92242196b4ab5d5408b6949f35803fc5
+f4840b0b561802400007802c4467b55610152573415ba53f207b104de7d912c5bf47751d85385d
+f45b5728f0c7222dd4800d300e92a631f5523738c581300c61c41099563b1cc5d5d75fdfa6a249
+6e883227020870adfff63688a6d6e32d649e3e802919138035d53be2663efcbafa6f3f01c12dd5
+e73272506a4e1e0a0ba4ee185c8f2631f1c47991bcd441a3fb971a920a995d56e51abb9273c056
+ab190ba8b3eab9924dcf25e9eaf74fd2a2adeb0fe586522738c94df776270068587b9e2951285d
+f961df30828870115939160f7baca86e9b7eeee4778159d4b82acb2b4934970ca8af657637ce28
+1fc7195fff0c26a35b5763d1c84b75f45df695b55f565e83e72dca59c528fc9c2f9fb8bb1d3836
+0b3f87cbcd36f96e6680960b5f78d1660180ea3ba73c0df17e9d1b3c4939c8dd8eb4a95277934c
+a417af85e4f3ceee61bd0d6a18687471d8dec2cfb65d8a1ee30adcd50f6354045e61aa3f1d35d1
+a5f39c1228be448f4e15fee8a8309d3144e8e494b5483680599ce430e1e7b219b8e156f6dd0049
+e34e53d0463376b77b365bebaf08f9c1514f7b66520544953582916823f17d70ba3646a8d420e0
+07cca059a45276212c40599e79d8cd2b1768f964d3a364e65baafd767f43b2ab562de3050724c3
+0e8e11fd2f429ff7590b97f861f1ee9d19a3cfa66a8c755960b45cb8dfaffd80e3426e5c57e65c
+f5ea49f3504806bc96527c9b20987e99de44c09e93de6c7dab1bd4a2ba5476fc3209c1b421dfdb
+a91a8568087d8f7b706b522af6f181829a9ae7266789ff18cbbeaedaaef7f7df4c4ae7873dac2e
+0f8b1d43cebb6087bc2398837c6b8e6b2aec3e55c66cf3db3293fc371e7d93b32853fed1723b5e
+ca9932759d854db4dd5069aaa31f51a515bb9ae6342a583080ddfd2173a7b0e9143fca950495d3
+a90a7ec9bbbfaa2ba03661b05c8e85b6f3edfd82444b1d335b21d502b430f1dbfa2fd93e0f4b3b
+dc096f564e3d4ab470152b785dd2c05a9d69973665c5b67fc0d81151141a5eb78880086ad276b4
+ef039c78a77ed5e80044d87a0767a416d5f4739a1735917d99c5b207f9c94fa903d7063c3ff62d
+c2a2079ed2707d88a77c5c5009dab9d223e0efb33bdc736af0f85564ef88d1853f13343fdeb271
+fce4d42038c0e0fe544309f063b6f1f3dc9018a0e2893da5f1fb30cf003fb23deb46d13e7b51fb
+ab4610af7efb3827e5f0a27ed173c385e7a4f03b167971b69d6e57059e5380d6b521090540c8fd
+706dc8ddfd3397a05db5d030b5cae26330de17394c40234329bec6cba36f5ee51ac0cef01d5c9a
+186d73e1242855e94eda47507a65de5c8a568ac2ea69c0446ff2faa0615532eae5ef142fe8ce7a
+c08c058d68db0bf5a1ea72a74c763f76d475ca73eec18bb3de194e564628f7986dabf118d08b49
+08a26376d508991051ade1dbb85c7188a1c5140b9b874a56d8aa08ea8243404a5c626ad5d989b0
+0df27dd5fde4d705a87b97f0cf165a4903c32b40d7db2cc9ea2a709992a1804f621468f1f1f25a
+7301cc4d96574ef8b4fe7113397b378d0650bafec63eca65cda58a490bcc6a7840141503652dd0
+69e3860d1254aa78438f6d06e702040289f25d6c333df3e938dad48118b155d4a0bd893be04744
+bc41a02adda4f451785b5f29651a70b520add99694fe534e985df16edbcabfb29fc9f80570c599
+6bc0311534500bcd860f5e5dd8c3a5595fd0c16c0b4a71e6b19d8c65ec009d85728416e98865b6
+f732c97849a322d5d15933023627c4c3b493cd25fe787bd8a602c185c58ffed03d9b64b84f68b3
+7c041eec4d32420af95c174da76b1822d66d98873d7050d124387f9454d11b6b43f978a7dc5893
+341815d40bf7489a4443acf4e6249d8ed0ac31b2fb85d9a06a9061c180106238213253423d3e16
+e62c1c035ffa6be588b5e105ccbde9537ddb9e86eb9eee887e01ca91420e20dae0b338c928423d
+5dcd021828050e57213768c79faa75ba1958d7b43eea506e5fc0683955d0d93903e6510af75c47
+97d05c1c001252743623028d78a293054bed8847112656cf36567ba99eaff4a09d2e12a7664645
+38884477d00ebdaf62fc1f110a2b59efd877c9e570c127cd89801d5d2eab8de294085771fa38b6
+1584efb3877e4edb76957de903b0bc880a703adf57b73628cf6481491360bf081f999a347a3b8c
+ed28d04a74857774ac390d7b18d0719cd4b7c14a9fea42e80026d96486b29266ad8a2a31557023
+a581a882c8f8b294bd02bcca17e0cc3b47bee6d9f378323de02482101664a0189c7b69dd1bd91d
+36d45e95050ba0a29225a9ea038f03c6635840d3c794fbf42c4e2d61d8c25c152d3ebbd52dc5c5
+8e2d8b6f36136dcfa92b409c80ce96398f9292e48894a435494d72e399b82f87fc6e2a1025371a
+f68aa5572d8eb827b649e37e6b108c8643bccd73c90aa42bef97e51ca27ff45051056b679a459e
+c938e3ad5494ef9389a4213589d526bd5cdbe0795bf78b83097b893dfa661faec6214ff7c15896
+98485ab72a3a314546fd4440dca92cc0c126aed9bc5851aca6ac87b8948cc54023da7d1ebddb67
+021b357ecad8b8f54aa91b8ffb3ecfe8addce021a7c8454186b56c1c27e720bac49021546acd47
+6a8ad779af8973e641a47f5c79a86e61a6174aa7b529dbc8c9c1042270c20aff1608ec8fcd53fc
+bbabcc21985096706d9d313adbf1adf29926fd1dde0d5c688e4b1d7d0d6802824c6c7b7fd0ea98
+d1d06622520286704add2b5f2370aaeb0533694f9883478a75344fdd3505c7b6c4ad5a37abe943
+0ae1c3d2550309668dac31d748eb4e956c8894a18d11f387d16934356ec7379494d77d0393b94d
+5245f1f71d77de7b4d89f5529f6b5b9bd2744df09173be9a5e4c573c4ef5a1096ee82a2a53e883
+04f379afc6868b0eefd62a5cb2d1d21aba066937639b9fe5b10208da697f40a39e6ee11d621502
+366c546aac60fc79c6d05cea9d9e8d8ffa67ff442e75f4f28ac62a2660346e329b56fac248f3a4
+7665157eeba01b807b65c7bc9eba5d059faf5987212330ac3f7a04278de1cfc8e01ebe136443df
+edc5df38f539f573bed294de93639ffb430dd3f982f4ba760784841dc87ce0df076c1760609276
+b67f976e226e56fd9a24f5bc442a7a9cc3872f16a3a4647bdef24151c51ea7c2554be9841f5243
+99a21a28d33dd7cb294976da44ee73e52744c8bfac0581982d7ef27be12cba1d89078051ae1c5b
+5f2ab4752035faa6bc6de10370a935d9110f47400e08f3235bbeb4ca7cffbb7dac1e6e22ee5a58
+c7b33e7acf65586470c087594454484186d92f60d3603af307e5169b10e812e344a98555d6bcd9
+b6befeefbf526274a0f8fab7eedf23f1c5523323b2c0f0e4a9141bfdd3772d02deedd69b3937bf
+8533a6f205702e6a703446a4c13828f115bd547e59971e050d6b55c176c8e32d3f09338ee665b4
+a2f6f8692e1613f8168f3dff482fc3462aca3227a0f464aa065a8b97409aecd9b2b86831f190cd
+d988af54d7e2e6ce2600d74b78473ac42534cb1a4712afae659ecbf12cb11a958609e5a5701cff
+32acaae52f3423032687119953a0fb7b4c412086e460224d7451ec10effbb707fef8de4e43d9c3
+b0c2519b1df462943db7b4d1e5cc638021e6a3c1a1a72f3a28cb372b1f639dc74270734f43ba20
+2a5bcc805421f806e068ba068bdb0ea2b3f481a5dc6d99707caef1dff7ea9359da1b8b82cfd9b7
+b3a382a2d9eb740c9fed9948bbec3878eaa4ee5f6bbc39af5e39db0af35359e8ad124646b3e786
+737e06d8d7044d2ef7dbf7fbfd6e7a19d9b236ae7262aafa4c026bdf999aca8ca153e306fafde0
+5d8ae9d74f13913aed7c6dc3386f348b84e5442cc9fb6049eec5706a79398802772c40b383657b
+bd5c4b445777e0236710339fa7c57068737504b28d36ee8115d704b5efdd0062f9007f176c85d1
+3a35a6807d4a4cab3d2a57fa2aa7b4f83f44d734cbcc59ef84e42d8e62df197853570308bff37d
+f02a7730b82559893bdad5620bd6252f7d6e7a7877968c5736b1d328a2e6e2ddac3f75914ebba7
+6ed377bdeea8a4dcfce30c81859bc0f192593683311995037b169ee87fb0a2ea898679ee597106
+3e38185ff9a53be81919b04760a07ca721589b67975e893e12f61e5637c15afddfa1bb5c8a94bd
+1610e97903f46d34830f3dcd6bc8b925812af6cd30c1889c4429d67ccae457844775e1f4638326
+f4b4dbd66575fc71b986017406ecc04e89ea26f3a1b457af7aa5c2391899a51f53620549dd9aa5
+5af140070bcc439d4ec60a749cbd2339cd2405c5223a5e9a517f62e2b50cb00c514e79614e7f9b
+40c6af1f1a842080c15022b49dd86872db7841166b777df67caf278d22a829f269047b3fe1143c
+c16f137cb6edf3d9dbb8574e5411a8e69c1fce97fafafe3bce921d280cad1c0d2c3fd3cfd39c2c
+66f92c5ba458c5bc2baca4d9c4e67d9838827a0b3bb33d1e0bfd981a8b88fb8fa542e4d07956e5
+5c773b2bcd1fa5f6d0b84bc9f35436cf21004e59c7c4ded8aa8f61fc31b61e9e5cfed76043f697
+90069da95284172fd4a769f3ac5ead35882eca37bccba6cfe3fd00a1a5320c1e583cea4d338069
+168b633c2bb960add60699076f9553d65ec1cfcbe15f4696e470bba0d2d20b810bbb94de0e066a
+53cedc32f5a667d044df997a23d38ab60630bf1ecb4a63ae6e85ddbe826f6c208e8fa013536d67
+7a57a10237989400d0fb5c5680f40fe842a6d7827eb6dfbefb66d2ffec4a21f3184d9a7b8892dc
+f1842e755bfe07c3415d50b31650cb2fe423024399e3f1d48e833c6bd1a5882b3d897a48476774
+7447a56fd2f9f785928dc623b45e04c0534414e458c8084db9c0fb7b869303f03a7db0d24e106f
+c4569f9ad4f42ef096c1e73197358bc5b79a0140eec788fd815a595d138623d724e9f46bd75c74
+0c66781572cdd5d8c4b61a71c1de857c6f7e52a0a65413cb461dcfdaec8a7f6d5a527b5da11fbb
+f0779c15a11f3f2f81f38a02238917e270ff6bb47a21b36a20348a34108f5471f5eca0d2f684f0
+18ad7ac5f52b67d145963537534e3d5d487b65d38ce310cd1f5464b8d39b904e396b343097761d
+12ee73ff8fad544bb8ae894a77a71cbb6e0bc0262360f5f2eacdb52640f3fc5071e71ded3eb0f7
+3150a3afaac2882a0f4a78eb4de7ac732b7c201afcf70a1ff3e49bb4a7e786e1c48860023768ad
+8c40390bdba129bc40a3accc154a3a55a32b16e8dec5ba2f7a51f3c22821f00e913614955c3c74
+165f4b84d6e1f43137ed572e4a387bc7dafdcf0c015fc2b7ebaf1cc69c191bae34f569f29d93ec
+df6d8ce8647b68f96ac1444530b4abd76ace164eee01f6cb07c1457761846cfceb3c7ed3e8dce5
+b173f356312ed03851a1941dad7ce47f352efd3ebdc8b1a10984fc39408fc765d3ea63e5eee4ac
+c48969e9d770d53ae1f2afb7a7690063466ef9dfdee47448671dccff63d5139be1b3cf1c33537d
+5f191a73d3a8a4956a7b8c84828fc1445ff041a84474eaf7b45bf67dc9e621b44119005512e9d8
+0a89fb10ca9227cce67c6cbfc6f45bde1433a9e389888003a31591f77f7b65727bdefb150a2298
+66eba692c0a080dc7a926cb27caf77fa352404ece37bc5f69b88a8878f735c0066554b294ac76c
+e90583a5f0dbbb0c33fd9f52b75ffa450d7aa2e3cd0f7c01874c927eb0d818f4fabddb6c50df24
+855bf3f9984a0ef65a93e2e3d782794f2ed0262ac86a8ab343fdeda76c787cb5e68dff2b3e6cda
+3ab1f8b646fe1c9b63176ce5d9721d9e217883a5809f44377c2f61291eb3a8b8d5e4f406343451
+da9c11ca53ef9eb934f8b1a44f1bb8cd202cb1742d8213735777fdbacf7f0933a0b0bc8fd13084
+db3410a6736c1acdf9682cd633bfa27714697c08b2b3ae0812bd96b2b14c7d0df99e26dc08598f
+70d24ee32b43619119bbecb71a1738e95103d9ff1513216077a2cc1c786660f544c31155eca5ff
+20deb39075b09e281a06df04dea50179e88a2bb1b98d05560b1f1bbf6df981594b47a9035b554d
+c79b44e9cd91381d2ed71c6bb7eadc4422ee3c27ab678a22a785786904b1bca4919b40cc05ab1c
+c2faa2f1f2c1da946484660942f5288e5bdb044790c15edc3c979beab118101083e94631b8b9a2
+ed92c51c056294b7863e76d01cabe5bec973ba40ee485d88196c908d032051bb55144de8966328
+66de750fb8dc983487507a7b5ec1babf6cf080c6c0dd002b6e305cfdd97c426725fd648ad995e7
+4bd8d67e4e03d68fc7c789d243a25cff3f52c7bf4ee758bcb62d1c951102c0a81eebb224fceea6
+522ae93dc8c9af630a13e2382f24bea74c6279859901aca5e1479dd17ca58e635e5768203b7314
+f7f92e74bde2a7baf3b8acea39f800ebca4b2b2a40fe5cb0a671f836607e86c42d06e51ccba1e2
+4e14918d443bd792e836edae89639f36825e698eec03b7f649858a10886a900c371c890564d199
+1a8dc48644381aced59f4e8c8fc388da4911a9d25bde9e47566e54216044a7b08d1cefe9861e6f
+651357f25b526e9b54ae62c904094616a2285491292de7fb4f95235a3902170601c15d52b9bc85
+c153bb27d4ab8cd01c80863c6f34b784dc5f28f196c822a0ae59551b92148b7ee92a428a1ac9ed
+fb51cab977ae915e0c1c80033f22da741fce179cc91b5ebb7a3dd3bf579d7ced93f14f23e09a44
+954ad9d4cd2fca916a9e444f1ebee056779087ee33dc155a16484e938d38d09a97d551f1e12c62
+c1f90ac16a297e5f73add55f9606180d34e8e37b28006da4dcfb0e842647a46f6c44df4ef27d96
+aa8b26441324a228ca896ce8a5cf37b93a6b50f95776835fa5c041069c9d8e9a9edfe302aebb01
+97756c6fd3d6d3c2d6dae32c8a1148dbde0953a20d4d75489cac0447c3100bd2d15beee4043861
+c077c7b6ff2742daf44c2562a03d85a3d91d7deddce813f8cbdff39127c94d500900a31f68bee4
+903187e2defe546dd1d7a4106049c7a902b3abeaae9297d50eecfe2df733004ed34cfb26d7e93d
+99dff2bf37759d9f75a8b0ba0b85df6fe2e208d1947f5dd2c3c44bfa0348b3484eb3f29c053a1e
+9aba025c012e85b2b1c153e67ac2dbfe7d5c0d384edf06bb9063b5541956df3990517c7ab6eaaa
+18d771fac8f1ec9f99c6c3a34a422255498d6254c1601ef1fed76719306b6cc3debf99445c53cc
+c189d3b4ce6a3b036c58414e6be5df2602a6f7b6df3dc95cbec600c62972f6be5d48ebc2104043
+b7d7659cfa004c9647f0c1d7429281cec88a51161779c2f9f872aa9a12163acf2f276e3355c102
+ca4d472167913e2ed8b8d442ac2981d730bee5afcb4f9f06871d24f79f433dea17a6d827b21c7a
+f81b5d1d58d74cec4e6903c9a5f22ec3280000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+cleartomark
+%%EndResource
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+%%PageBoundingBox: 4 1 354 245
+%%EndPageSetup
+q 4 1 350 244 rectclip
+1 0 0 -1 0 252 cm q
+0 g
+0.25 w
+0 J
+0 j
+[] 0.0 d
+3.8 M q 1 0 0 -1 0 0 cm
+34.301 -229.602 m 37.449 -229.602 l 347.352 -229.602 m 344.199 -229.602
+ l S Q
+BT
+7 0 0 -7 26.599999 231.935201 Tm
+/f-0-0 1 Tf
+(0)Tj
+ET
+q 1 0 0 -1 0 0 cm
+34.301 -187.898 m 37.449 -187.898 l 347.352 -187.898 m 344.199 -187.898
+ l S Q
+BT
+7 0 0 -7 21.349999 190.235202 Tm
+/f-0-0 1 Tf
+(0.2)Tj
+ET
+q 1 0 0 -1 0 0 cm
+34.301 -146.199 m 37.449 -146.199 l 347.352 -146.199 m 344.199 -146.199
+ l S Q
+BT
+7 0 0 -7 21.349999 148.535003 Tm
+/f-0-0 1 Tf
+(0.4)Tj
+ET
+q 1 0 0 -1 0 0 cm
+34.301 -104.449 m 37.449 -104.449 l 347.352 -104.449 m 344.199 -104.449
+ l S Q
+BT
+7 0 0 -7 21.349999 106.785004 Tm
+/f-0-0 1 Tf
+(0.6)Tj
+ET
+q 1 0 0 -1 0 0 cm
+34.301 -62.75 m 37.449 -62.75 l 347.352 -62.75 m 344.199 -62.75 l S Q
+BT
+7 0 0 -7 21.349999 65.085005 Tm
+/f-0-0 1 Tf
+(0.8)Tj
+ET
+q 1 0 0 -1 0 0 cm
+34.301 -21.051 m 37.449 -21.051 l 347.352 -21.051 m 344.199 -21.051 l S Q
+BT
+7 0 0 -7 26.599999 23.385006 Tm
+/f-0-0 1 Tf
+(1)Tj
+ET
+q 1 0 0 -1 0 0 cm
+34.301 -229.602 m 34.301 -226.449 l 34.301 -21.051 m 34.301 -24.199 l S Q
+BT
+7 0 0 -7 33.424999 238.9352 Tm
+/f-0-0 1 Tf
+(0)Tj
+ET
+q 1 0 0 -1 0 0 cm
+65.602 -229.602 m 65.602 -226.449 l 65.602 -21.051 m 65.602 -24.199 l S Q
+BT
+7 0 0 -7 62.974998 238.9352 Tm
+/f-0-0 1 Tf
+(20)Tj
+ET
+q 1 0 0 -1 0 0 cm
+96.898 -229.602 m 96.898 -226.449 l 96.898 -21.051 m 96.898 -24.199 l S Q
+BT
+7 0 0 -7 94.274998 238.9352 Tm
+/f-0-0 1 Tf
+(40)Tj
+ET
+q 1 0 0 -1 0 0 cm
+128.199 -229.602 m 128.199 -226.449 l 128.199 -21.051 m 128.199 -24.199
+ l S Q
+BT
+7 0 0 -7 125.574997 238.9352 Tm
+/f-0-0 1 Tf
+(60)Tj
+ET
+q 1 0 0 -1 0 0 cm
+159.5 -229.602 m 159.5 -226.449 l 159.5 -21.051 m 159.5 -24.199 l S Q
+BT
+7 0 0 -7 156.874996 238.9352 Tm
+/f-0-0 1 Tf
+(80)Tj
+ET
+q 1 0 0 -1 0 0 cm
+190.852 -229.602 m 190.852 -226.449 l 190.852 -21.051 m 190.852 -24.199
+ l S Q
+BT
+7 0 0 -7 186.474995 238.9352 Tm
+/f-0-0 1 Tf
+(100)Tj
+ET
+q 1 0 0 -1 0 0 cm
+222.148 -229.602 m 222.148 -226.449 l 222.148 -21.051 m 222.148 -24.199
+ l S Q
+BT
+7 0 0 -7 217.774995 238.9352 Tm
+/f-0-0 1 Tf
+(120)Tj
+ET
+q 1 0 0 -1 0 0 cm
+253.449 -229.602 m 253.449 -226.449 l 253.449 -21.051 m 253.449 -24.199
+ l S Q
+BT
+7 0 0 -7 249.074994 238.9352 Tm
+/f-0-0 1 Tf
+(140)Tj
+ET
+q 1 0 0 -1 0 0 cm
+284.75 -229.602 m 284.75 -226.449 l 284.75 -21.051 m 284.75 -24.199 l S Q
+BT
+7 0 0 -7 280.374993 238.9352 Tm
+/f-0-0 1 Tf
+(160)Tj
+ET
+q 1 0 0 -1 0 0 cm
+316.051 -229.602 m 316.051 -226.449 l 316.051 -21.051 m 316.051 -24.199
+ l S Q
+BT
+7 0 0 -7 311.674992 238.9352 Tm
+/f-0-0 1 Tf
+(180)Tj
+ET
+q 1 0 0 -1 0 0 cm
+347.352 -229.602 m 347.352 -226.449 l 347.352 -21.051 m 347.352 -24.199
+ l S Q
+BT
+7 0 0 -7 342.974991 238.9352 Tm
+/f-0-0 1 Tf
+(200)Tj
+ET
+0.5 w
+q 1 0 0 -1 0 0 cm
+34.301 -21.051 313.051 -208.551 re S Q
+BT
+0 -7 -7 0 8.98516 164.330902 Tm
+/f-0-0 1 Tf
+(Packet Success Rate \(PSR\))Tj
+7 0 0 -7 173.010996 249.43516 Tm
+(distance \(m\))Tj
+18.922714 31.342856 Td
+(-85)Tj
+ET
+0.580392 0 0.831373 rg
+0.75 w
+q 1 0 0 -1 0 0 cm
+319 -27.699 m 338.949 -27.699 l 35.852 -21.051 m 60.898 -21.051 l 62.449
+ -21.449 l 64.051 -21.449 l 65.602 -23.75 l 67.148 -36.699 l 68.75 -55.449
+ l 70.301 -110.102 l 71.852 -156.199 l 73.449 -206.051 l 75 -224.602 l 76.551
+ -229.398 l 78.148 -229.602 l 345.801 -229.602 l S Q
+0 g
+BT
+7 0 0 -7 305.468992 37.035005 Tm
+/f-0-0 1 Tf
+(-90)Tj
+ET
+0 0.619608 0.45098 rg
+q 1 0 0 -1 0 0 cm
+319 -34.699 m 338.949 -34.699 l 35.852 -21.051 m 76.551 -21.051 l 78.148
+ -22.699 l 79.699 -23.551 l 81.25 -32.5 l 82.801 -39.801 l 84.398 -62.949
+ l 85.949 -95.699 l 87.5 -134.5 l 89.102 -176.398 l 90.648 -204.551 l 92.199
+ -220 l 93.801 -227.5 l 95.352 -229.398 l 96.898 -229.199 l 98.5 -229.602
+ l 345.801 -229.602 l S Q
+0 g
+BT
+7 0 0 -7 305.468992 44.035005 Tm
+/f-0-0 1 Tf
+(-95)Tj
+ET
+0.341176 0.709804 0.909804 rg
+q 1 0 0 -1 0 0 cm
+319 -41.699 m 338.949 -41.699 l 35.852 -21.051 m 93.801 -21.051 l 95.352
+ -21.449 l 96.898 -21.25 l 98.5 -22.102 l 100.051 -24.199 l 101.602 -27.699
+ l 103.148 -34.602 l 104.75 -38.352 l 106.301 -53.148 l 107.852 -71.301 
+l 109.449 -94.25 l 111 -124.5 l 112.551 -149.5 l 114.148 -173.301 l 115.699
+ -200.199 l 117.25 -210.199 l 118.801 -221.699 l 120.398 -224.801 l 121.949
+ -228.949 l 123.5 -228.352 l 125.102 -229.602 l 345.801 -229.602 l S Q
+0 g
+BT
+7 0 0 -7 301.968992 51.035005 Tm
+/f-0-0 1 Tf
+(-100)Tj
+ET
+0.901961 0.619608 0 rg
+q 1 0 0 -1 0 0 cm
+319 -48.699 m 338.949 -48.699 l 35.852 -21.051 m 118.801 -21.051 l 120.398
+ -21.25 l 121.949 -21.449 l 123.5 -21.25 l 125.102 -21.449 l 126.648 -22.699
+ l 128.199 -22.301 l 129.801 -24.199 l 131.352 -25.449 l 132.898 -30 l 134.5
+ -29.801 l 136.051 -35.648 l 137.602 -44.602 l 139.148 -55.648 l 140.75 
+-62.949 l 142.301 -80.301 l 143.852 -94.648 l 145.449 -119.5 l 147 -133.051
+ l 148.551 -151.801 l 150.148 -168.301 l 151.699 -189.148 l 153.25 -197.25
+ l 154.801 -206.25 l 156.398 -215.398 l 157.949 -223.352 l 159.5 -222.699
+ l 161.102 -226.449 l 162.648 -229.199 l 164.199 -229.602 l 165.801 -229.602
+ l 167.352 -229.398 l 168.898 -229.602 l 345.801 -229.602 l S Q
+0 g
+BT
+7 0 0 -7 301.968992 58.035005 Tm
+/f-0-0 1 Tf
+(-105)Tj
+ET
+0.941176 0.890196 0.258824 rg
+q 1 0 0 -1 0 0 cm
+319 -55.699 m 338.949 -55.699 l 35.852 -21.051 m 157.949 -21.051 l 159.5
+ -21.25 l 161.102 -21.051 l 164.199 -21.051 l 165.801 -21.25 l 167.352 -22.102
+ l 168.898 -21.25 l 170.5 -22.102 l 172.051 -23.551 l 173.602 -22.949 l 
+175.148 -25.199 l 176.75 -25.199 l 178.301 -27.5 l 179.852 -29.398 l 181.449
+ -34.602 l 183 -39.602 l 184.551 -40.449 l 186.148 -43.551 l 187.699 -54.648
+ l 189.25 -59.852 l 190.852 -74.449 l 192.398 -80.051 l 193.949 -89.25 l
+ 195.5 -95.898 l 197.102 -119.5 l 198.648 -132.398 l 200.199 -142.648 l 
+201.801 -155.75 l 203.352 -161.398 l 204.898 -174.75 l 206.5 -192.5 l 208.051
+ -199.551 l 209.602 -206.051 l 211.148 -215.398 l 212.75 -216.648 l 214.301
+ -221.25 l 215.852 -223.551 l 217.449 -224.398 l 219 -227.301 l 220.551 
+-227.949 l 222.148 -227.301 l 223.699 -228.949 l 225.25 -228.75 l 226.852
+ -229.398 l 228.398 -229.398 l 229.949 -229.602 l 345.801 -229.602 l S Q
+0 g
+BT
+7 0 0 -7 293.218993 65.035005 Tm
+/f-0-0 1 Tf
+(-106.58)Tj
+ET
+0 0.45098 0.698039 rg
+q 1 0 0 -1 0 0 cm
+319 -62.699 m 338.949 -62.699 l 35.852 -21.051 m 170.5 -21.051 l 172.051
+ -21.25 l 173.602 -21.051 l 178.301 -21.051 l 179.852 -21.449 l 181.449 
+-21.25 l 183 -21.25 l 184.551 -21.051 l 186.148 -22.102 l 187.699 -21.898
+ l 189.25 -23.551 l 190.852 -24.801 l 192.398 -24.602 l 193.949 -25 l 195.5
+ -27.301 l 197.102 -29.602 l 198.648 -31.25 l 200.199 -32.75 l 201.801 -37.551
+ l 203.352 -38.801 l 204.898 -46.301 l 206.5 -48.801 l 208.051 -60.648 l
+ 209.602 -65.449 l 211.148 -66.5 l 212.75 -86.352 l 214.301 -92.148 l 215.852
+ -101.949 l 217.449 -117.801 l 219 -123.25 l 220.551 -133.898 l 222.148 
+-147.648 l 223.699 -164.551 l 225.25 -167.25 l 226.852 -180.602 l 228.398
+ -184.949 l 229.949 -193.75 l 231.5 -200.199 l 233.102 -210.852 l 234.648
+ -213.949 l 236.199 -217.102 l 237.801 -220.199 l 239.352 -224.398 l 240.898
+ -225.449 l 242.5 -226.051 l 244.051 -228.148 l 245.602 -228.75 l 247.148
+ -229.199 l 248.75 -229.398 l 250.301 -228.949 l 251.852 -229.602 l 345.801
+ -229.602 l S Q
+0 g
+BT
+7 0 0 -7 301.968992 72.035004 Tm
+/f-0-0 1 Tf
+(-110)Tj
+ET
+0.901961 0.121569 0.0588235 rg
+q 1 0 0 -1 0 0 cm
+319 -69.699 m 338.949 -69.699 l 35.852 -21.051 m 220.551 -21.051 l 222.148
+ -21.25 l 225.25 -21.25 l 226.852 -21.449 l 228.398 -21.449 l 229.949 -21.898
+ l 231.5 -22.102 l 233.102 -22.699 l 234.648 -21.898 l 236.199 -22.5 l 237.801
+ -22.949 l 239.352 -23.75 l 240.898 -26.051 l 242.5 -25.648 l 244.051 -27.5
+ l 245.602 -28.75 l 247.148 -29.801 l 248.75 -35 l 250.301 -35.648 l 251.852
+ -36.898 l 253.449 -38.352 l 255 -44 l 256.551 -47.551 l 258.148 -54 l 259.699
+ -58.602 l 261.25 -68.602 l 262.852 -65.25 l 264.398 -79.648 l 265.949 -85.051
+ l 267.5 -89.648 l 269.102 -94.648 l 270.648 -108.449 l 272.199 -111.551
+ l 273.801 -124.051 l 275.352 -135.148 l 276.898 -140.352 l 278.5 -152.25
+ l 280.051 -155.551 l 281.602 -167.648 l 283.148 -174.352 l 284.75 -182.25
+ l 286.301 -188.949 l 287.852 -195.398 l 289.449 -200.602 l 291 -203.949
+ l 292.551 -211.852 l 294.148 -212.898 l 295.699 -215.852 l 297.25 -219.602
+ l 298.852 -220 l 300.398 -222.102 l 301.949 -225.199 l 303.5 -227.102 l
+ 305.102 -228.148 l 306.648 -227.301 l 308.199 -228.148 l 309.801 -227.949
+ l 311.352 -228.949 l 312.898 -229.602 l 314.5 -229.602 l 316.051 -229.199
+ l 317.602 -229.398 l 319.199 -229.602 l 320.75 -229.398 l 322.301 -229.602
+ l 345.801 -229.602 l S Q
+0 g
+0.5 w
+q 1 0 0 -1 0 0 cm
+34.301 -21.051 313.051 -208.551 re S Q
+BT
+7 0 0 -7 99.558998 12.885006 Tm
+/f-0-0 1 Tf
+(Packet \(MSDU\) size = 7 bytes; tx power = 0 dBm; channel = 11)Tj
+ET
+Q Q
 showpage
 %%Trailer
-%%DocumentFonts: Times-BoldItalic
+end
+%%EOF
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/doc/lr-wpan.rst ns-3.38/src/lr-wpan/doc/lr-wpan.rst
--- ns-3.37/src/lr-wpan/doc/lr-wpan.rst	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/lr-wpan/doc/lr-wpan.rst	2023-03-16 20:54:23.632868500 -0700
@@ -14,7 +14,6 @@
 Model Description
 *****************
 
-The source code for the lr-wpan module lives in the directory ``src/lr-wpan``.
 
 Design
 ======
@@ -60,20 +59,20 @@
 In general, primitives are standardized as follows (e.g. Sec 7.1.1.1.1
 of IEEE 802.15.4-2006):::
 
-  MCPS-DATA.request      (
-                          SrcAddrMode,
-                          DstAddrMode,
-                          DstPANId,
-                          DstAddr,
-                          msduLength,
-                          msdu,
-                          msduHandle,
-                          TxOptions,
-                          SecurityLevel,
-                          KeyIdMode,
-                          KeySource,
-                          KeyIndex
-                          )
+  MCPS-DATA.request(
+                    SrcAddrMode,
+                    DstAddrMode,
+                    DstPANId,
+                    DstAddr,
+                    msduLength,
+                    msdu,
+                    msduHandle,
+                    TxOptions,
+                    SecurityLevel,
+                    KeyIdMode,
+                    KeySource,
+                    KeyIndex
+                   )
 
 This maps to |ns3| classes and methods such as:::
 
@@ -85,7 +84,7 @@
   };
 
   void
-  LrWpanMac::McpsDataRequest (McpsDataRequestParameters params)
+  LrWpanMac::McpsDataRequest(McpsDataRequestParameters params)
   {
   ...
   }
@@ -165,7 +164,7 @@
 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.
 
-In LR-WPAN, association is used to join or leave PANs. All devices in LR-WPAN must belong to a PAN to communicate. |ns3| uses a classic association procedure described in the standard. The standard also covers a more effective association procedure known as fast association (See IEEE 802.15.4-2015, fastA) but this association is currently not supported by |ns3|. Alternatively, |ns3| can do a "quick and dirty" association using either ```LrWpanHelper::AssociateToPan``` or ```LrWpanHelper::AssociateToBeaconPan```. These functions are used when a preset association can be done. For example, when the relationships between existing nodes and coordinators are known and can be set before the begining of the simulation. In other situations, like in many networks in real deployments or in large networks, it is desirable that devices "associate themselves" with the best possible available coordinator candidates. This is a process known as bootstrap, and simulating this process makes it possible to demonstrate the kind of situations a node would face in which large networks to associate in real environment.
+In LR-WPAN, association is used to join or leave PANs. All devices in LR-WPAN must belong to a PAN to communicate. |ns3| uses a classic association procedure described in the standard. The standard also covers a more effective association procedure known as fast association (See IEEE 802.15.4-2015, fastA) but this association is currently not supported by |ns3|. Alternatively, |ns3| can do a "quick and dirty" association using either ```LrWpanHelper::AssociateToPan``` or ```LrWpanHelper::AssociateToBeaconPan```. These functions are used when a preset association can be done. For example, when the relationships between existing nodes and coordinators are known and can be set before the beginning of the simulation. In other situations, like in many networks in real deployments or in large networks, it is desirable that devices "associate themselves" with the best possible available coordinator candidates. This is a process known as bootstrap, and simulating this process makes it possible to demonstrate the kind of situations a node would face in which large networks to associate in real environment.
 
 Bootstrap (a.k.a. network initialization) is possible with a combination of scan and association MAC primitives. Details on the general process for this network initialization is described in the standard. Bootstrap is a complex process that not only requires the scanning networks, but also the exchange of command frames and the use of a pending transaction list (indirect transmissions) in the coordinator to store command frames. The following summarizes the whole process:
 
@@ -214,16 +213,33 @@
 after the packet was completely transmitted. Other packets arriving during
 reception will add up to the interference/noise.
 
-Currently the receiver sensitivity is set to a fixed value of -106.58 dBm. This
-corresponds to a packet error rate of 1% for 20 byte PSDU reference packets for this
-signal power, according to IEEE Std 802.15.4-2006, section 6.1.7. In the future
-we will provide support for changing the sensitivity to different values.
+Rx sensitivity is defined as the weakest possible signal point at which a receiver can receive and decode a packet with a high success rate.
+According to the standard (IEEE Std 802.15.4-2006, section 6.1.7), this
+corresponds to the point where the packet error rate is under 1% for 20 bytes PSDU
+reference packets (11 bytes MAC header + 7 bytes payload (MSDU) + FCS 2 bytes). Setting low Rx sensitivity values (increasing the radio hearing capabilities)
+have the effect to receive more packets (and at a greater distance) but it raises the probability to have dropped packets at the
+MAC layer or the probability of corrupted packets. By default, the receiver sensitivity is set to the maximum theoretical possible value of -106.58 dBm for the supported IEEE 802.15.4 O-QPSK 250kps.
+This rx sensitivity is set for the "perfect radio" which only considers the floor noise, in essence, this do not include the noise factor (noise introduced by imperfections in the demodulator chip or external factors).
+The receiver sensitivity can be changed to different values using ``SetRxSensitivity`` function in the PHY to simulate the hearing capabilities of different compliant radio transceivers (the standard minimum compliant Rx sensitivity is -85 dBm).:::
+                                                              (defined by the standard)
+   NoiseFloor          Max Sensitivity                          Min Sensitivity
+   -106.987dBm          -106.58dBm                                   -85dBm
+    |-------------------------|------------------------------------------|
+                          Noise Factor = 1
+                              | <--------------------------------------->|
+                                    Acceptable sensitivity range
+
+The example ``lr-wpan-per-plot.cc` shows that at given Rx sensitiviy, packets are dropped regardless of their theoretical error probability.
+This program outputs a file named ``802.15.4-per-vs-rxSignal.plt``.
+Loading this file into gnuplot yields a file ``802.15.4-per-vs-rsSignal.eps``, which can
+be converted to pdf or other formats. Packet payload size, Tx power and Rx sensitivity can be configurated.
+The point where the blue line crosses with the PER indicates the Rx sensitivity. The default output is shown below.
 
 .. _fig-802-15-4-per-sens:
 
 .. figure:: figures/802-15-4-per-sens.*
 
-    Packet error rate vs. signal power
+    Default output of the program ``lr-wpan-per-plot.cc``
 
 
 NetDevice
@@ -246,7 +262,7 @@
 The 16-bit addresses are not guaranteed to be unique, and they are typically either assigned
 during the devices deployment, or assigned dynamically during the device bootstrap.
 
-The other relavant "address" to consider is the PanId (16 bits), which represents the PAN
+The other relevant "address" to consider is the PanId (16 bits), which represents the PAN
 the device is attached to.
 
 Due to the limited number of available bytes in a packet, IEEE 802.15.4 tries to use short
@@ -298,7 +314,7 @@
 - Devices are capable of associating with a single PAN coordinator. Interference is modeled as AWGN but this is currently not thoroughly tested.
 - 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 Rx Sensitivity and ED thresholds are currently not supported.
+- Configuration of the ED thresholds are currently not supported.
 - Orphan scans are not supported.
 - Disassociation primitives are not supported.
 - Security is not supported.
@@ -338,6 +354,7 @@
 
 * ``lr-wpan-data.cc``:  A simple example showing end-to-end data transfer.
 * ``lr-wpan-error-distance-plot.cc``:  An example to plot variations of the packet success ratio as a function of distance.
+* ``lr-wpan-per-plot.cc``: An example to plot the theoretical and experimental packet error rate (PER) as a function of receive signal.
 * ``lr-wpan-error-model-plot.cc``:  An example to test the phy.
 * ``lr-wpan-packet-print.cc``:  An example to print out the MAC header fields.
 * ``lr-wpan-phy-test.cc``:  An example to test the phy.
@@ -364,11 +381,14 @@
 The example ``lr-wpan-error-distance-plot.cc`` plots the packet success
 ratio (PSR) as a function of distance, using the default LogDistance
 propagation loss model and the 802.15.4 error model.  The channel (default 11),
-packet size (default 20 bytes) and transmit power (default 0 dBm) can be
-varied by command line arguments.  The program outputs a file named
-``802.15.4-psr-distance.plt``.  Loading this file into gnuplot yields
-a file ``802.15.4-psr-distance.eps``, which can be converted to pdf or
-other formats.  The default output is shown below.
+packet size (default PSDU 20 bytes = 11 bytes MAC header + data payload), transmit power (default 0 dBm)
+and Rx sensitivity (default -106.58 dBm) can be varied by command line arguments.
+The program outputs a file named ``802.15.4-psr-distance.plt``.
+Loading this file into gnuplot yields a file ``802.15.4-psr-distance.eps``, which can
+be converted to pdf or other formats.  The following image shows the output
+of multiple runs using different Rx sensitivity values. A higher Rx sensitivity (lower dBm) results
+in a increased communication distance but also makes the radio suceptible to more interference from
+surronding devices.
 
 .. _fig-802-15-4-psr-distance:
 
@@ -405,5 +425,3 @@
 .. figure:: figures/802-15-4-ber.*
 
     Default output of the program ``lr-wpan-error-model-plot.cc``
-
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/examples/CMakeLists.txt ns-3.38/src/lr-wpan/examples/CMakeLists.txt
--- ns-3.37/src/lr-wpan/examples/CMakeLists.txt	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/lr-wpan/examples/CMakeLists.txt	2023-03-16 20:54:23.632868500 -0700
@@ -38,3 +38,10 @@
   LIBRARIES_TO_LINK ${liblr-wpan}
                     ${libnetanim}
 )
+
+build_lib_example(
+  NAME lr-wpan-per-plot
+  SOURCE_FILES lr-wpan-per-plot.cc
+  LIBRARIES_TO_LINK ${liblr-wpan}
+                    ${libstats}
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/examples/lr-wpan-active-scan.cc ns-3.38/src/lr-wpan/examples/lr-wpan-active-scan.cc
--- ns-3.37/src/lr-wpan/examples/lr-wpan-active-scan.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/lr-wpan/examples/lr-wpan-active-scan.cc	2023-03-16 20:54:23.632868500 -0700
@@ -60,7 +60,7 @@
     {
         std::cout << Simulator::Now().As(Time::S) << "| Active scan status SUCCESSFUL (Completed)"
                   << "\n";
-        if (params.m_panDescList.size() > 0)
+        if (!params.m_panDescList.empty())
         {
             std::cout << "Device [" << device->GetMac()->GetShortAddress()
                       << "] found the following PANs:\n";
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/examples/lr-wpan-bootstrap.cc ns-3.38/src/lr-wpan/examples/lr-wpan-bootstrap.cc
--- ns-3.37/src/lr-wpan/examples/lr-wpan-bootstrap.cc	2022-11-01 17:37:24.511395200 -0700
+++ ns-3.38/src/lr-wpan/examples/lr-wpan-bootstrap.cc	2023-03-16 20:54:23.632868500 -0700
@@ -30,6 +30,9 @@
  * on the LQI results of the scan. A node may not find any beacons if the coordinator is outside its
  * communication range. An association request may not be send if LQI is too low for an association.
  *
+ * The coordinator in PAN 5 runs in extended addressing mode and do not assign short addresses.
+ * The coordinator in PAN 7 runs in short addressing mode and assign short addresses.
+ *
  * At the end of the simulation, an animation is generated (lrwpan-bootstrap.xml), showing the
  * results of the association with each coordinator. This simulation can take a few seconds to
  * complete.
@@ -90,7 +93,7 @@
         // Select the coordinator with the highest LQI from the PAN Descriptor List
         int maxLqi = 0;
         int panDescIndex = 0;
-        if (params.m_panDescList.size() > 0)
+        if (!params.m_panDescList.empty())
         {
             for (uint32_t i = 0; i < params.m_panDescList.size(); i++)
             {
@@ -104,11 +107,22 @@
             // Only request association if the coordinator is permitting association at this moment.
             if (params.m_panDescList[panDescIndex].m_superframeSpec.IsAssocPermit())
             {
+                std::string addressing;
+                if (params.m_panDescList[panDescIndex].m_coorAddrMode == SHORT_ADDR)
+                {
+                    addressing = "Short";
+                }
+                else if (params.m_panDescList[panDescIndex].m_coorAddrMode == EXT_ADDR)
+                {
+                    addressing = "Ext";
+                }
+
                 std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId()
                           << " [" << device->GetMac()->GetShortAddress() << " | "
                           << device->GetMac()->GetExtendedAddress() << "]"
                           << " MLME-scan.confirm:  Selected PAN ID "
-                          << params.m_panDescList[panDescIndex].m_coorPanId << " | LQI "
+                          << params.m_panDescList[panDescIndex].m_coorPanId
+                          << "| Coord addressing mode: " << addressing << " | LQI "
                           << static_cast<int>(params.m_panDescList[panDescIndex].m_linkQuality)
                           << "\n";
 
@@ -126,6 +140,7 @@
                         assocParams.m_coordAddrMode = LrWpanAddressMode::SHORT_ADDR;
                         assocParams.m_coordShortAddr =
                             params.m_panDescList[panDescIndex].m_coorShortAddr;
+                        assocParams.m_capabilityInfo.SetShortAddrAllocOn(true);
                     }
                     else if (assocParams.m_coordAddrMode == LrWpanAddressMode::EXT_ADDR)
                     {
@@ -133,6 +148,7 @@
                         assocParams.m_coordExtAddr =
                             params.m_panDescList[panDescIndex].m_coorExtAddr;
                         assocParams.m_coordShortAddr = Mac16Address("ff:fe");
+                        assocParams.m_capabilityInfo.SetShortAddrAllocOn(false);
                     }
 
                     Simulator::ScheduleNow(&LrWpanMac::MlmeAssociateRequest,
@@ -169,13 +185,14 @@
 static void
 AssociateIndication(Ptr<LrWpanNetDevice> device, MlmeAssociateIndicationParams params)
 {
-    // This is typically implemented by the  Coordinator next layer (3rd layer or higher).
+    // This is typically implemented by the coordinator next layer (3rd layer or higher).
     // The steps described below are out of the scope of the standard.
 
     // Here the 3rd layer should check:
-    //    a) Whether or not the device was previously associated with this PAN (the coordinator
-    //    keeps a list). b) The coordinator have sufficient resources available to allow the
-    //    association.
+    //    a) Whether or not the device was previously associated with this PAN
+    //       (the coordinator keeps a list).
+    //    b) The coordinator have sufficient resources available to allow the
+    //       association.
     // If the association fails, status = 1 or 2 and assocShortAddr = FFFF.
 
     // In this example, the coordinator accepts every association request and have no association
@@ -360,7 +377,8 @@
     NetDeviceContainer lrwpanDevices = lrWpanHelper.Install(nodes);
     lrwpanDevices.Add(lrWpanHelper.Install(coordinators));
 
-    lrWpanHelper.AssociateToPan(lrwpanDevices, 0xffff);
+    // Set the extended address to all devices (EUI-64)
+    lrWpanHelper.SetExtendedAddresses(lrwpanDevices);
 
     // Devices hooks & MAC MLME-scan primitive set
     for (NodeContainer::Iterator i = nodes.Begin(); i != nodes.End(); i++)
@@ -417,6 +435,16 @@
     Ptr<NetDevice> netDeviceCoor2 = coor2->GetDevice(0);
     Ptr<LrWpanNetDevice> coor2Device = DynamicCast<LrWpanNetDevice>(netDeviceCoor2);
 
+    // Coordinators require that their short address is explicitly set.
+    // Either FF:FE to indicate that only extended addresses will be used in the following
+    // data communications or any other value (except for FF:FF) to indicate that the coordinator
+    // will use the short address in these communications.
+    // The default short address for all devices is FF:FF (unassigned/no associated).
+
+    // coor1 (PAN 5) = extended addressing mode coor2 (PAN 7) = short addressing mode
+    coor1Device->GetMac()->SetShortAddress(Mac16Address("FF:FE"));
+    coor2Device->GetMac()->SetShortAddress(Mac16Address("CA:FE"));
+
     // PAN coordinator 1 (PAN 5) transmits beacons on channel 12
     MlmeStartRequestParams params;
     params.m_panCoor = true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/examples/lr-wpan-error-distance-plot.cc ns-3.38/src/lr-wpan/examples/lr-wpan-error-distance-plot.cc
--- ns-3.37/src/lr-wpan/examples/lr-wpan-error-distance-plot.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/examples/lr-wpan-error-distance-plot.cc	2023-03-16 20:54:23.632868500 -0700
@@ -17,11 +17,25 @@
  * Author: Tom Henderson <thomas.r.henderson@boeing.com>
  */
 
-// This program produces a gnuplot file that plots the packet success rate
-// as a function of distance for the 802.15.4 models, assuming a default
-// LogDistance propagation loss model, the 2.4 GHz OQPSK error model, a
-// default transmit power of 0 dBm, and a default packet size of 20 bytes of
-// 802.15.4 payload.
+/*
+   This program produces a gnuplot file that plots the packet success rate
+   as a function of distance for the 802.15.4 models, assuming a default
+   LogDistance propagation loss model, the 2.4 GHz OQPSK error model, a
+   default transmit power of 0 dBm, and a default packet size of 20 bytes of
+   802.15.4 payload and a default rx sensitivity of -106.58 dBm.
+
+   Tx power of the transmitter node and the Rx sensitivity of the receiving node
+   as well as the transmitted packet size can be adjusted to obtain a different
+   distance plot.
+
+    Node1                       Node2
+   (dev0) --------------------->(dev1)
+
+   Usage:
+
+   ./ns3 run "lr-wpan-error-distance-plot --txPower= 0 --rxSensitivity=-92"
+
+*/
 #include <ns3/abort.h>
 #include <ns3/callback.h>
 #include <ns3/command-line.h>
@@ -52,7 +66,7 @@
 
 using namespace ns3;
 
-static uint32_t g_received = 0; //!< number of packets received
+uint32_t g_packetsReceived = 0; //!< number of packets received
 
 NS_LOG_COMPONENT_DEFINE("LrWpanErrorDistancePlot");
 
@@ -61,10 +75,10 @@
  * \param params MCPS data indication parameters
  * \param p packet
  */
-static void
+void
 LrWpanErrorDistanceCallback(McpsDataIndicationParams params, Ptr<Packet> p)
 {
-    g_received++;
+    g_packetsReceived++;
 }
 
 int
@@ -77,20 +91,22 @@
     int maxDistance = 200; // meters
     int increment = 1;
     int maxPackets = 1000;
-    int packetSize = 20;
+    int packetSize = 7; // PSDU = 20 bytes (11 bytes MAC header + 7 bytes MSDU )
     double txPower = 0;
     uint32_t channelNumber = 11;
+    double rxSensitivity = -106.58; // dBm
 
     CommandLine cmd(__FILE__);
 
     cmd.AddValue("txPower", "transmit power (dBm)", txPower);
     cmd.AddValue("packetSize", "packet (MSDU) size (bytes)", packetSize);
     cmd.AddValue("channelNumber", "channel number", channelNumber);
+    cmd.AddValue("rxSensitivity", "the rx sensitivity (dBm)", rxSensitivity);
 
     cmd.Parse(argc, argv);
 
     os << "Packet (MSDU) size = " << packetSize << " bytes; tx power = " << txPower
-       << " dBm; channel = " << channelNumber;
+       << " dBm; channel = " << channelNumber << "; Rx sensitivity = " << rxSensitivity << " dBm";
 
     Gnuplot psrplot = Gnuplot("802.15.4-psr-distance.eps");
     Gnuplot2dDataset psrdataset("802.15.4-psr-vs-distance");
@@ -117,6 +133,9 @@
     Ptr<SpectrumValue> psd = svh.CreateTxPowerSpectralDensity(txPower, channelNumber);
     dev0->GetPhy()->SetTxPowerSpectralDensity(psd);
 
+    // Set Rx sensitivity of the receiving device
+    dev1->GetPhy()->SetRxSensitivity(rxSensitivity);
+
     McpsDataIndicationCallback cb0;
     cb0 = MakeCallback(&LrWpanErrorDistanceCallback);
     dev1->GetMac()->SetMcpsDataIndicationCallback(cb0);
@@ -132,7 +151,7 @@
     Ptr<Packet> p;
     mob0->SetPosition(Vector(0, 0, 0));
     mob1->SetPosition(Vector(minDistance, 0, 0));
-    for (int j = minDistance; j < maxDistance;)
+    for (int j = minDistance; j < maxDistance; j += increment)
     {
         for (int i = 0; i < maxPackets; i++)
         {
@@ -140,10 +159,10 @@
             Simulator::Schedule(Seconds(i), &LrWpanMac::McpsDataRequest, dev0->GetMac(), params, p);
         }
         Simulator::Run();
-        NS_LOG_DEBUG("Received " << g_received << " packets for distance " << j);
-        psrdataset.Add(j, g_received / 1000.0);
-        g_received = 0;
-        j += increment;
+        NS_LOG_DEBUG("Received " << g_packetsReceived << " packets for distance " << j);
+        psrdataset.Add(j, g_packetsReceived / 1000.0);
+        g_packetsReceived = 0;
+
         mob1->SetPosition(Vector(j, 0, 0));
     }
 
@@ -153,10 +172,10 @@
     psrplot.SetTerminal("postscript eps color enh \"Times-BoldItalic\"");
     psrplot.SetLegend("distance (m)", "Packet Success Rate (PSR)");
     psrplot.SetExtra("set xrange [0:200]\n\
-set yrange [0:1]\n\
-set grid\n\
-set style line 1 linewidth 5\n\
-set style increment user");
+                      set yrange [0:1]\n\
+                      set grid\n\
+                      set style line 1 linewidth 5\n\
+                      set style increment user");
     psrplot.GenerateOutput(berfile);
     berfile.close();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/examples/lr-wpan-mlme.cc ns-3.38/src/lr-wpan/examples/lr-wpan-mlme.cc
--- ns-3.37/src/lr-wpan/examples/lr-wpan-mlme.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/examples/lr-wpan-mlme.cc	2023-03-16 20:54:23.632868500 -0700
@@ -46,21 +46,20 @@
 
 using namespace ns3;
 
-static void
-BeaconIndication(MlmeBeaconNotifyIndicationParams params, Ptr<Packet> p)
+void
+BeaconIndication(MlmeBeaconNotifyIndicationParams params)
 {
-    NS_LOG_UNCOND(Simulator::Now().GetSeconds()
-                  << " secs | Received BEACON packet of size " << p->GetSize());
+    NS_LOG_UNCOND(Simulator::Now().GetSeconds() << " secs | Received BEACON packet of size ");
 }
 
-static void
+void
 DataIndication(McpsDataIndicationParams params, Ptr<Packet> p)
 {
     NS_LOG_UNCOND(Simulator::Now().GetSeconds()
                   << " secs | Received DATA packet of size " << p->GetSize());
 }
 
-static void
+void
 TransEndIndication(McpsDataConfirmParams params)
 {
     // In the case of transmissions with the Ack flag activated, the transaction is only
@@ -71,19 +70,19 @@
     }
 }
 
-static void
+void
 DataIndicationCoordinator(McpsDataIndicationParams params, Ptr<Packet> p)
 {
     NS_LOG_UNCOND(Simulator::Now().GetSeconds()
                   << "s Coordinator Received DATA packet (size " << p->GetSize() << " bytes)");
 }
 
-static void
+void
 StartConfirm(MlmeStartConfirmParams params)
 {
     if (params.m_status == MLMESTART_SUCCESS)
     {
-        NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "Beacon status SUCESSFUL");
+        NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "Beacon status SUCCESSFUL");
     }
 }
 
@@ -156,7 +155,7 @@
 
     //////////// Manual device association ////////////////////
     // Note: We manually associate the devices to a PAN coordinator
-    //       because currently there is no automatic association behavior (bootstrap);
+    //       (i.e. bootstrap is not used);
     //       The PAN COORDINATOR does not need to associate or set its
     //       PAN Id or its own coordinator id, these are set
     //       by the MLME-start.request primitive when used.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/examples/lr-wpan-per-plot.cc ns-3.38/src/lr-wpan/examples/lr-wpan-per-plot.cc
--- ns-3.37/src/lr-wpan/examples/lr-wpan-per-plot.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/lr-wpan/examples/lr-wpan-per-plot.cc	2023-03-16 20:54:23.632868500 -0700
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2022 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
+ *
+ * Authors: Alberto Gallegos Ramonet <alramonet@is.tokushima-u.ac.jp>
+ *          Tommaso Pecorella <tommaso.pecorella@unifi.it>
+ */
+
+/*
+   This program produces a gnuplot file that plots the theoretical and experimental packet error
+   rate (PER) as a function of receive signal for the 802.15.4 model. As described by the standard,
+   the PER is calculated with the transmission of frames with a PSDU of 20 bytes. This is equivalent
+   to an MPDU = MAC header (11 bytes) + FCS (2 bytes) +  payload (MSDU 7 bytes). In the experimental
+   test, 1000 frames are transmitted for each Rx signal ranging from -130 dBm to -100 dBm with
+   increments of 0.01 dBm. The point before PER is < 1 % is the device receive sensitivity.
+   Theoretical and experimental Rx sensitivity is printed at the end of the end and a plot is
+   generated.
+
+   Example usage:
+
+   ./ns3 run "lr-wpan-per-plot --rxSensitivity=-92"
+
+*/
+
+#include <ns3/core-module.h>
+#include <ns3/gnuplot.h>
+#include <ns3/lr-wpan-module.h>
+#include <ns3/mobility-module.h>
+#include <ns3/network-module.h>
+#include <ns3/propagation-module.h>
+#include <ns3/spectrum-module.h>
+
+using namespace ns3;
+
+uint32_t g_packetsReceived = 0; //!< number of packets received
+
+NS_LOG_COMPONENT_DEFINE("LrWpanErrorDistancePlot");
+
+/**
+ * Function called when a Data indication is invoked
+ * \param params MCPS data indication parameters
+ * \param p packet
+ */
+void
+PacketReceivedCallback(McpsDataIndicationParams params, Ptr<Packet> p)
+{
+    g_packetsReceived++;
+}
+
+int
+main(int argc, char* argv[])
+{
+    LogComponentEnableAll(LogLevel(LOG_PREFIX_FUNC | LOG_PREFIX_NODE | LOG_PREFIX_TIME));
+
+    std::ostringstream os;
+    std::ofstream perfile("802.15.4-per-vs-rxSignal.plt");
+
+    double minRxSignal = -111; // dBm
+    double maxRxSignal = -82;  // dBm
+    double increment = 0.01;
+    int maxPackets = 1000;
+    int packetSize = 7; // bytes (MPDU payload)
+    double txPower = 0; // dBm
+    uint32_t channelNumber = 11;
+    double rxSensitivity = -106.58; // dBm
+
+    CommandLine cmd(__FILE__);
+
+    cmd.AddValue("txPower", "transmit power (dBm)", txPower);
+    cmd.AddValue("packetSize", "packet (MSDU) size (bytes)", packetSize);
+    cmd.AddValue("channelNumber", "channel number", channelNumber);
+    cmd.AddValue("rxSensitivity", "the rx sensitivity (dBm)", rxSensitivity);
+    cmd.Parse(argc, argv);
+
+    Gnuplot perplot = Gnuplot("802.15.4-per-vs-rxSignal.eps");
+    Gnuplot2dDataset perdatasetExperimental("Experimental");
+    Gnuplot2dDataset perdatasetTheorical("Theoretical");
+
+    Ptr<Node> n0 = CreateObject<Node>();
+    Ptr<Node> n1 = CreateObject<Node>();
+    Ptr<LrWpanNetDevice> dev0 = CreateObject<LrWpanNetDevice>();
+    Ptr<LrWpanNetDevice> dev1 = CreateObject<LrWpanNetDevice>();
+
+    dev0->SetAddress(Mac16Address("00:01"));
+    dev1->SetAddress(Mac16Address("00:02"));
+    Ptr<SingleModelSpectrumChannel> channel = CreateObject<SingleModelSpectrumChannel>();
+    Ptr<FixedRssLossModel> propModel = CreateObject<FixedRssLossModel>();
+
+    channel->AddPropagationLossModel(propModel);
+    dev0->SetChannel(channel);
+    dev1->SetChannel(channel);
+    n0->AddDevice(dev0);
+    n1->AddDevice(dev1);
+    Ptr<ConstantPositionMobilityModel> mob0 = CreateObject<ConstantPositionMobilityModel>();
+    dev0->GetPhy()->SetMobility(mob0);
+    Ptr<ConstantPositionMobilityModel> mob1 = CreateObject<ConstantPositionMobilityModel>();
+    dev1->GetPhy()->SetMobility(mob1);
+    mob0->SetPosition(Vector(0, 0, 0));
+    mob1->SetPosition(Vector(0, 0, 0));
+
+    LrWpanSpectrumValueHelper svh;
+    Ptr<SpectrumValue> psd = svh.CreateTxPowerSpectralDensity(txPower, channelNumber);
+    dev0->GetPhy()->SetTxPowerSpectralDensity(psd);
+
+    // Set Rx sensitivity of the receiving device
+    dev1->GetPhy()->SetRxSensitivity(rxSensitivity);
+
+    McpsDataIndicationCallback cb0;
+    cb0 = MakeCallback(&PacketReceivedCallback);
+    dev1->GetMac()->SetMcpsDataIndicationCallback(cb0);
+
+    //////////////////////////////////
+    // Experimental  PER v.s Signal //
+    //////////////////////////////////
+
+    double per = 1;
+    double sensitivityExp = 0;
+    bool sensThreshold = true;
+
+    for (double j = minRxSignal; j < maxRxSignal; j += increment)
+    {
+        propModel->SetRss(j);
+        if (sensThreshold)
+        {
+            sensitivityExp = j;
+        }
+
+        for (int i = 0; i < maxPackets; i++)
+        {
+            McpsDataRequestParams params;
+            params.m_srcAddrMode = SHORT_ADDR;
+            params.m_dstAddrMode = SHORT_ADDR;
+            params.m_dstPanId = 0;
+            params.m_dstAddr = Mac16Address("00:02");
+            params.m_msduHandle = 0;
+            params.m_txOptions = 0;
+            Ptr<Packet> p;
+            p = Create<Packet>(packetSize);
+            Simulator::Schedule(Seconds(i), &LrWpanMac::McpsDataRequest, dev0->GetMac(), params, p);
+        }
+
+        Simulator::Run();
+
+        per = (static_cast<double>(maxPackets - g_packetsReceived) / maxPackets) * 100;
+
+        std::cout << "Experimental Test || Signal: " << j << " dBm | Received " << g_packetsReceived
+                  << " pkts"
+                  << "/" << maxPackets << " | PER " << per << " %\n";
+
+        if (per <= 1 && sensThreshold)
+        {
+            sensThreshold = false;
+        }
+
+        perdatasetExperimental.Add(j, per);
+        g_packetsReceived = 0;
+    }
+
+    /////////////////////////////////
+    // Theoretical PER v.s. Signal //
+    /////////////////////////////////
+
+    Ptr<LrWpanErrorModel> lrWpanError = CreateObject<LrWpanErrorModel>();
+    LrWpanSpectrumValueHelper psdHelper;
+
+    // Calculate the noise that accounts for both thermal noise (floor noise) and
+    // imperfections on the chip or lost before reaching the demodulator.
+    // In O-QPSK 250kbps, the point where PER is <= 1% without
+    // additional noise is -106.58 dBm (Noise Factor = 1)
+    double maxRxSensitivityW = (pow(10.0, -106.58 / 10.0) / 1000.0);
+    long double noiseFactor = (pow(10.0, rxSensitivity / 10.0) / 1000.0) / maxRxSensitivityW;
+
+    psdHelper.SetNoiseFactor(noiseFactor);
+    Ptr<const SpectrumValue> noisePsd = psdHelper.CreateNoisePowerSpectralDensity(11);
+    double noise = LrWpanSpectrumValueHelper::TotalAvgPower(noisePsd, 11);
+
+    double signal = 0;
+    double sensitivityTheo = 0;
+    double perTheoretical = 0;
+    double snr = 0;
+    sensThreshold = true;
+
+    for (double j = minRxSignal; j < maxRxSignal; j += increment)
+    {
+        if (sensThreshold)
+        {
+            sensitivityTheo = j;
+        }
+
+        signal = pow(10.0, j / 10.0) / 1000.0; // signal in Watts
+        snr = signal / noise;
+
+        // According to the standard, Packet error rate should be obtained
+        // using a PSDU of 20 bytes using
+        // the equation PER = 1 - (1 - BER)^nbits
+        perTheoretical = (1.0 - lrWpanError->GetChunkSuccessRate(snr, (packetSize + 13) * 8)) * 100;
+        std::cout << "Theoretical Test || Signal: " << j << " dBm | SNR: " << snr << "| PER "
+                  << perTheoretical << " % \n";
+
+        if (perTheoretical <= 1 && sensThreshold)
+        {
+            sensThreshold = false;
+        }
+
+        perdatasetTheorical.Add(j, perTheoretical);
+    }
+
+    std::cout << "_____________________________________________________________________________\n";
+    std::cout << "Experimental Test || Receiving with a current sensitivity of " << sensitivityExp
+              << " dBm\n";
+    std::cout << "Theoretical Test  || Receiving with a current sensitivity of " << sensitivityTheo
+              << " dBm\n";
+    std::cout << "Gnu plot generated.";
+
+    os << "Pkt Payload (MSDU) size = " << packetSize << " bytes | "
+       << "Tx power = " << txPower << " dBm | "
+       << "Rx Sensitivity (Theo) = " << sensitivityTheo << " dBm";
+
+    perplot.AddDataset(perdatasetExperimental);
+    perplot.AddDataset(perdatasetTheorical);
+
+    perplot.SetTitle(os.str());
+    perplot.SetTerminal("postscript eps color enh \"Times-BoldItalic\"");
+    perplot.SetLegend("Rx signal (dBm)", "Packet Error Rate (%)");
+    perplot.SetExtra("set xrange [-110:-82]\n\
+                      set logscale y\n\
+                      set yrange [0.000000000001:120]\n\
+                      set xtics 2\n\
+                      set grid\n\
+                      set style line 1 linewidth 5\n\
+                      set style line 2 linewidth 3\n\
+                      set style increment user\n\
+                      set arrow from -110,1 to -82,1 nohead lc 'web-blue' front");
+    perplot.GenerateOutput(perfile);
+    perfile.close();
+
+    Simulator::Destroy();
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/helper/lr-wpan-helper.cc ns-3.38/src/lr-wpan/helper/lr-wpan-helper.cc
--- ns-3.37/src/lr-wpan/helper/lr-wpan-helper.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/helper/lr-wpan-helper.cc	2023-03-16 20:54:23.632868500 -0700
@@ -231,85 +231,80 @@
 }
 
 void
-LrWpanHelper::AssociateToPan(NetDeviceContainer c, uint16_t panId)
+LrWpanHelper::CreateAssociatedPan(NetDeviceContainer c, uint16_t panId)
 {
     NetDeviceContainer devices;
     uint16_t id = 1;
-    uint8_t idBuf[2];
+    uint8_t idBuf[2] = {0, 0};
+    uint8_t idBuf2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+    Mac16Address address16;
+    Mac64Address address64;
+    Mac16Address coordShortAddr;
+    Mac64Address coordExtAddr;
 
     for (NetDeviceContainer::Iterator i = c.Begin(); i != c.End(); i++)
     {
+        if (id < 0x0001 || id > 0xFFFD)
+        {
+            NS_ABORT_MSG("Only 65533 addresses supported. Range [00:01]-[FF:FD]");
+        }
+
         Ptr<LrWpanNetDevice> device = DynamicCast<LrWpanNetDevice>(*i);
         if (device)
         {
             idBuf[0] = (id >> 8) & 0xff;
             idBuf[1] = (id >> 0) & 0xff;
-            Mac16Address address;
-            address.CopyFrom(idBuf);
+            address16.CopyFrom(idBuf);
+
+            idBuf2[6] = (id >> 8) & 0xff;
+            idBuf2[7] = (id >> 0) & 0xff;
+            address64.CopyFrom(idBuf2);
+
+            if (address64 == Mac64Address("00:00:00:00:00:00:00:01"))
+            {
+                // We use the first device in the container as coordinator
+                coordShortAddr = address16;
+                coordExtAddr = address64;
+            }
+
+            // TODO: Change this to device->GetAddress() if GetAddress can guarantee a
+            //  an extended address (currently only gives 48 address or 16 bits addresses)
+            device->GetMac()->SetExtendedAddress(address64);
+            device->SetPanAssociation(panId, coordExtAddr, coordShortAddr, address16);
 
-            device->GetMac()->SetPanId(panId);
-            device->GetMac()->SetShortAddress(address);
             id++;
         }
     }
 }
 
 void
-LrWpanHelper::AssociateToBeaconPan(NetDeviceContainer c,
-                                   uint16_t panId,
-                                   Mac16Address coor,
-                                   uint8_t bcnOrd,
-                                   uint8_t sfrmOrd)
+LrWpanHelper::SetExtendedAddresses(NetDeviceContainer c)
 {
     NetDeviceContainer devices;
-    uint16_t id = 1;
-    uint8_t idBuf[2];
-    Mac16Address address;
-
-    if (bcnOrd > 14)
-    {
-        NS_LOG_DEBUG("The Beacon Order must be an int between 0 and 14");
-        return;
-    }
-
-    if ((sfrmOrd > 14) || (sfrmOrd > bcnOrd))
-    {
-        NS_LOG_DEBUG("The Superframe Order must be an int between 0 and 14, and less or equal to "
-                     "Beacon Order");
-        return;
-    }
+    uint64_t id = 1;
+    uint8_t idBuf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+    Mac64Address address64;
 
     for (NetDeviceContainer::Iterator i = c.Begin(); i != c.End(); i++)
     {
         Ptr<LrWpanNetDevice> device = DynamicCast<LrWpanNetDevice>(*i);
         if (device)
         {
-            idBuf[0] = (id >> 8) & 0xff;
-            idBuf[1] = (id >> 0) & 0xff;
-            address.CopyFrom(idBuf);
-
-            device->GetMac()->SetShortAddress(address);
+            idBuf[0] = (id >> 56) & 0xff;
+            idBuf[1] = (id >> 48) & 0xff;
+            idBuf[2] = (id >> 40) & 0xff;
+            idBuf[3] = (id >> 32) & 0xff;
+            idBuf[4] = (id >> 24) & 0xff;
+            idBuf[5] = (id >> 16) & 0xff;
+            idBuf[6] = (id >> 8) & 0xff;
+            idBuf[7] = (id >> 0) & 0xff;
+
+            address64.CopyFrom(idBuf);
+
+            // TODO: Change this to device->SetAddress() if GetAddress can guarantee
+            //  to set only extended addresses
+            device->GetMac()->SetExtendedAddress(address64);
 
-            if (address == coor)
-            {
-                MlmeStartRequestParams params;
-                params.m_panCoor = true;
-                params.m_PanId = panId;
-                params.m_bcnOrd = bcnOrd;
-                params.m_sfrmOrd = sfrmOrd;
-
-                Ptr<UniformRandomVariable> uniformRandomVariable =
-                    CreateObject<UniformRandomVariable>();
-                ;
-                Time jitter = Time(MilliSeconds(uniformRandomVariable->GetInteger(0, 10)));
-
-                Simulator::Schedule(jitter, &LrWpanMac::MlmeStartRequest, device->GetMac(), params);
-            }
-            else
-            {
-                device->GetMac()->SetPanId(panId);
-                device->GetMac()->SetAssociatedCoor(coor);
-            }
             id++;
         }
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/helper/lr-wpan-helper.h ns-3.38/src/lr-wpan/helper/lr-wpan-helper.h
--- ns-3.37/src/lr-wpan/helper/lr-wpan-helper.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/helper/lr-wpan-helper.h	2023-03-16 20:54:23.632868500 -0700
@@ -109,29 +109,23 @@
     NetDeviceContainer Install(NodeContainer c);
 
     /**
-     * \brief Associate the nodes to the same PAN
+     * \brief Creates an PAN with associated nodes and assigned addresses(16 and 64)
+     *        from the nodes in the node container.
+     *        The first node in the container becomes the PAN coordinator.
      *
-     * \param c a set of nodes
-     * \param panId the PAN Id
+     * \param c a The node container with the nodes that will form the PAN.
+     * \param panId The PAN identifier.
      */
-    void AssociateToPan(NetDeviceContainer c, uint16_t panId);
+    void CreateAssociatedPan(NetDeviceContainer c, uint16_t panId);
 
     /**
-     * \brief Associate the nodes to the same PAN and initiate beacon enabled mode.
+     * \brief Set the extended 64 bit addresses (EUI-64) for a group of
+     *        LrWpanNetDevices
+     *
+     * \param c The NetDevice container.
      *
-     * \param c a set of nodes
-     * \param panId the PAN id
-     * \param coor the address of the PAN coordinator
-     * \param bcnOrd indicates the interval between beacons.
-     *               The value must be an int between 0 and 14.
-     * \param sfrmOrd indicates the length of the superframe.
-     *                The value must be an int between 0 and 14 and less or equal to the bcnOrd
      */
-    void AssociateToBeaconPan(NetDeviceContainer c,
-                              uint16_t panId,
-                              Mac16Address coor,
-                              uint8_t bcnOrd,
-                              uint8_t sfrmOrd);
+    void SetExtendedAddresses(NetDeviceContainer c);
 
     /**
      * Helper to enable all LrWpan log components with one statement
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-constants.h ns-3.38/src/lr-wpan/model/lr-wpan-constants.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-constants.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/lr-wpan/model/lr-wpan-constants.h	2023-03-16 20:54:23.632868500 -0700
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2011 The Boeing Company
+ *
+ * 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:
+ *  Gary Pei <guangyu.pei@boeing.com>
+ *  kwong yin <kwong-sang.yin@boeing.com>
+ *  Tom Henderson <thomas.r.henderson@boeing.com>
+ *  Sascha Alexander Jopen <jopen@cs.uni-bonn.de>
+ *  Erwan Livolant <erwan.livolant@inria.fr>
+ *  Alberto Gallegos Ramonet <ramonet@fc.ritsumei.ac.jp>
+ */
+
+#ifndef LR_WPAN_CONSTANTS_H
+#define LR_WPAN_CONSTANTS_H
+
+#include <cstdint>
+
+namespace ns3
+{
+namespace lrwpan
+{
+
+/**
+ * \defgroup LrWpanConstants
+ * \ingroup lr-wpan
+ *
+ * Contains common parameters about LR-WPAN that can be reused in multiple files.
+ * These parameters are defined in the IEEE 802.15.4-2011 standard and adopt the
+ * same names as in the standard.
+ *
+ * @{
+ */
+
+///////////////////
+// PHY constants //
+///////////////////
+
+/**
+ * The maximum packet size accepted by the PHY.
+ * See Table 22 in section 6.4.1 of IEEE 802.15.4-2006
+ */
+constexpr uint32_t aMaxPhyPacketSize{127};
+
+/**
+ * The turnaround time in symbol periods for switching the transceiver from RX to TX or
+ * vice-versa.
+ * See Table 22 in section 6.4.1 of IEEE 802.15.4-2006
+ */
+constexpr uint32_t aTurnaroundTime{12};
+
+///////////////////
+// MAC constants //
+///////////////////
+
+/**
+ * The minimum number of octets added by the MAC sublayer to the PSDU.
+ * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
+ */
+constexpr uint32_t aMinMPDUOverhead{9};
+
+/**
+ * Length of a superframe slot in symbols. Defaults to 60 symbols in each superframe slot.
+ * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
+ */
+constexpr uint32_t aBaseSlotDuration{60};
+
+/**
+ * Number of a superframe slots per superframe. Defaults to 16.
+ * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
+ */
+constexpr uint32_t aNumSuperframeSlots{16};
+
+/**
+ * Length of a superframe in symbols. Defaults to aBaseSlotDuration * aNumSuperframeSlots in
+ * symbols.
+ * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
+ */
+constexpr uint32_t aBaseSuperframeDuration{aBaseSlotDuration * aNumSuperframeSlots};
+
+/**
+ * The number of consecutive lost beacons that will cause the MAC sublayer of a receiving device to
+ * declare a loss of synchronization.
+ * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
+ */
+constexpr uint32_t aMaxLostBeacons{4};
+
+/**
+ * The maximum size of an MPDU, in octets, that can be followed by a Short InterFrame Spacing (SIFS)
+ * period.
+ * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
+ */
+constexpr uint32_t aMaxSIFSFrameSize{18};
+
+/**
+ * Number of symbols per CSMA/CA time unit, default 20 symbols.
+ */
+constexpr uint32_t aUnitBackoffPeriod{20};
+
+/**
+ * The maximum number of octets added by the MAC sublayer to the MAC payload o a a beacon frame.
+ * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
+ */
+constexpr uint32_t aMaxBeaconOverhead{75};
+
+/**
+ * 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};
+
+/** @} */
+
+} // namespace lrwpan
+} // namespace ns3
+
+#endif // LR_WPAN_CONSTANTS_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-csmaca.cc ns-3.38/src/lr-wpan/model/lr-wpan-csmaca.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-csmaca.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-csmaca.cc	2023-03-16 20:54:23.632868500 -0700
@@ -22,6 +22,8 @@
 
 #include "lr-wpan-csmaca.h"
 
+#include "lr-wpan-constants.h"
+
 #include <ns3/log.h>
 #include <ns3/random-variable-stream.h>
 #include <ns3/simulator.h>
@@ -59,7 +61,6 @@
     m_macMinBE = 3;
     m_macMaxBE = 5;
     m_macMaxCSMABackoffs = 4;
-    m_aUnitBackoffPeriod = 20; // symbols
     m_random = CreateObject<UniformRandomVariable>();
     m_BE = m_macMinBE;
     m_ccaRequestRunning = false;
@@ -169,20 +170,6 @@
     return m_macMaxCSMABackoffs;
 }
 
-void
-LrWpanCsmaCa::SetUnitBackoffPeriod(uint64_t unitBackoffPeriod)
-{
-    NS_LOG_FUNCTION(this << unitBackoffPeriod);
-    m_aUnitBackoffPeriod = unitBackoffPeriod;
-}
-
-uint64_t
-LrWpanCsmaCa::GetUnitBackoffPeriod() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_aUnitBackoffPeriod;
-}
-
 Time
 LrWpanCsmaCa::GetTimeToNextSlot() const
 {
@@ -219,8 +206,8 @@
 
     // get a close value to the the boundary in symbols
     elapsedSuperframeSymbols = elapsedSuperframe.GetSeconds() * symbolRate;
-    symbolsToBoundary =
-        m_aUnitBackoffPeriod - std::fmod((double)elapsedSuperframeSymbols, m_aUnitBackoffPeriod);
+    symbolsToBoundary = lrwpan::aUnitBackoffPeriod -
+                        std::fmod((double)elapsedSuperframeSymbols, lrwpan::aUnitBackoffPeriod);
 
     timeAtBoundary = Seconds((double)(elapsedSuperframeSymbols + symbolsToBoundary) / symbolRate);
 
@@ -308,7 +295,7 @@
     }
 
     randomBackoff =
-        Seconds((double)(m_randomBackoffPeriodsLeft * GetUnitBackoffPeriod()) / symbolRate);
+        Seconds((double)(m_randomBackoffPeriodsLeft * lrwpan::aUnitBackoffPeriod) / symbolRate);
 
     if (IsUnSlottedCsmaCa())
     {
@@ -330,14 +317,14 @@
                      << randomBackoff.As(Time::S) << ")");
 
         NS_LOG_DEBUG("Backoff periods left in CAP: "
-                     << ((timeLeftInCap.GetSeconds() * symbolRate) / m_aUnitBackoffPeriod) << " ("
-                     << (timeLeftInCap.GetSeconds() * symbolRate) << " symbols or "
+                     << ((timeLeftInCap.GetSeconds() * symbolRate) / lrwpan::aUnitBackoffPeriod)
+                     << " (" << (timeLeftInCap.GetSeconds() * symbolRate) << " symbols or "
                      << timeLeftInCap.As(Time::S) << ")");
 
         if (randomBackoff >= timeLeftInCap)
         {
             uint64_t usedBackoffs =
-                (double)(timeLeftInCap.GetSeconds() * symbolRate) / m_aUnitBackoffPeriod;
+                (double)(timeLeftInCap.GetSeconds() * symbolRate) / lrwpan::aUnitBackoffPeriod;
             m_randomBackoffPeriodsLeft -= usedBackoffs;
             NS_LOG_DEBUG("No time in CAP to complete backoff delay, deferring to the next CAP");
             m_endCapEvent =
@@ -424,7 +411,7 @@
     else
     {
         // time the PHY takes to switch from Rx to Tx and Tx to Rx
-        transactionSymbols += (m_mac->GetPhy()->aTurnaroundTime * 2);
+        transactionSymbols += (lrwpan::aTurnaroundTime * 2);
     }
     transactionSymbols += m_mac->GetIfsSize();
 
@@ -571,13 +558,13 @@
 }
 
 uint8_t
-LrWpanCsmaCa::GetNB()
+LrWpanCsmaCa::GetNB() const
 {
     return m_NB;
 }
 
 bool
-LrWpanCsmaCa::GetBatteryLifeExtension()
+LrWpanCsmaCa::GetBatteryLifeExtension() const
 {
     return m_macBattLifeExt;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-csmaca.h ns-3.38/src/lr-wpan/model/lr-wpan-csmaca.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-csmaca.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-csmaca.h	2023-03-16 20:54:23.632868500 -0700
@@ -150,22 +150,6 @@
      */
     uint8_t GetMacMaxCSMABackoffs() const;
     /**
-     * Set the number of symbols forming the basic time period used by the
-     * CSMA-CA algorithm.
-     * See IEEE 802.15.4-2006, section 7.4.1, Table 85.
-     *
-     * \param unitBackoffPeriod the period length in symbols
-     */
-    void SetUnitBackoffPeriod(uint64_t unitBackoffPeriod);
-    /**
-     * Get the number of symbols forming the basic time period used by the
-     * CSMA-CA algorithm.
-     * See IEEE 802.15.4-2006, section 7.4.1, Table 85.
-     *
-     * \return the period length in symbols
-     */
-    uint64_t GetUnitBackoffPeriod() const;
-    /**
      * Locates the time to the next backoff period boundary in the SUPERFRAME
      * and returns the amount of time left to this moment.
      *
@@ -249,13 +233,13 @@
      *
      * \returns the number of CSMA retries
      */
-    uint8_t GetNB();
+    uint8_t GetNB() const;
     /**
      * Get the value of the Battery Life Extension
      *
      * \returns  true or false to Battery Life Extension support
      */
-    bool GetBatteryLifeExtension();
+    bool GetBatteryLifeExtension() const;
 
   private:
     void DoDispose() override;
@@ -277,7 +261,7 @@
      */
     bool m_isSlotted;
     /**
-     * The MAC instance for which this CSMA/CA implemenation is configured.
+     * The MAC instance for which this CSMA/CA implementation is configured.
      */
     Ptr<LrWpanMac> m_mac;
     /**
@@ -299,7 +283,7 @@
     /**
      * Minimum backoff exponent. 0 - macMaxBE, default 3
      */
-    uint8_t m_macMinBE; //
+    uint8_t m_macMinBE;
     /**
      * Maximum backoff exponent. 3 - 8, default 5
      */
@@ -309,10 +293,6 @@
      */
     uint8_t m_macMaxCSMABackoffs;
     /**
-     * Number of symbols per CSMA/CA time unit, default 20 symbols.
-     */
-    uint64_t m_aUnitBackoffPeriod;
-    /**
      * Count the number of remaining random backoff periods left to delay.
      */
     uint64_t m_randomBackoffPeriodsLeft;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-fields.h ns-3.38/src/lr-wpan/model/lr-wpan-fields.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-fields.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-fields.h	2023-03-16 20:54:23.632868500 -0700
@@ -224,7 +224,7 @@
     /**
      * GTS Descriptor
      */
-    struct gtsDescriptor
+    struct GtsDescriptor
     {
         Mac16Address m_gtsDescDevShortAddr; //!< GTS Descriptor Device Short Address (Bit 0-15)
         uint8_t m_gtsDescStartSlot;         //!< GTS Descriptor GTS Starting Slot(Bit 16-19)
@@ -239,7 +239,7 @@
     uint8_t m_gtsDirMask; //!< GTS Direction field Directions Mask (Bit 0-6)
     // GTS Direction field Reserved (Not Necessary) (Bit 7)
     // GTS List
-    gtsDescriptor m_gtsList[7]; //!< GTS List field (maximum descriptors stored == 7)
+    GtsDescriptor m_gtsList[7]; //!< GTS List field (maximum descriptors stored == 7)
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac.cc ns-3.38/src/lr-wpan/model/lr-wpan-mac.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac.cc	2023-03-16 20:54:23.632868500 -0700
@@ -24,6 +24,7 @@
  */
 #include "lr-wpan-mac.h"
 
+#include "lr-wpan-constants.h"
 #include "lr-wpan-csmaca.h"
 #include "lr-wpan-mac-header.h"
 #include "lr-wpan-mac-pl-headers.h"
@@ -202,7 +203,7 @@
     m_channelScanIndex = 0;
     m_maxEnergyLevel = 0;
 
-    m_macResponseWaitTime = aBaseSuperframeDuration * 32;
+    m_macResponseWaitTime = lrwpan::aBaseSuperframeDuration * 32;
     m_assocRespCmdWaitTime = 960;
 
     m_maxTxQueueSize = m_txQueue.max_size();
@@ -213,7 +214,9 @@
     uniformVar->SetAttribute("Max", DoubleValue(255.0));
     m_macDsn = SequenceNumber8(uniformVar->GetValue());
     m_macBsn = SequenceNumber8(uniformVar->GetValue());
-    m_shortAddress = Mac16Address("00:00");
+    m_macBeaconPayload = nullptr;
+    m_macBeaconPayloadLength = 0;
+    m_shortAddress = Mac16Address("FF:FF"); // FF:FF = The address is not assigned.
 }
 
 LrWpanMac::~LrWpanMac()
@@ -266,7 +269,7 @@
     m_mcpsDataIndicationCallback = MakeNullCallback<void, McpsDataIndicationParams, Ptr<Packet>>();
     m_mlmeStartConfirmCallback = MakeNullCallback<void, MlmeStartConfirmParams>();
     m_mlmeBeaconNotifyIndicationCallback =
-        MakeNullCallback<void, MlmeBeaconNotifyIndicationParams, Ptr<Packet>>();
+        MakeNullCallback<void, MlmeBeaconNotifyIndicationParams>();
     m_mlmeSyncLossIndicationCallback = MakeNullCallback<void, MlmeSyncLossIndicationParams>();
     m_mlmePollConfirmCallback = MakeNullCallback<void, MlmePollConfirmParams>();
     m_mlmeScanConfirmCallback = MakeNullCallback<void, MlmeScanConfirmParams>();
@@ -280,7 +283,7 @@
 }
 
 bool
-LrWpanMac::GetRxOnWhenIdle()
+LrWpanMac::GetRxOnWhenIdle() const
 {
     return m_macRxOnWhenIdle;
 }
@@ -348,7 +351,7 @@
     LrWpanMacHeader macHdr(LrWpanMacHeader::LRWPAN_MAC_DATA, m_macDsn.GetValue());
     m_macDsn++;
 
-    if (p->GetSize() > LrWpanPhy::aMaxPhyPacketSize - aMinMPDUOverhead)
+    if (p->GetSize() > lrwpan::aMaxPhyPacketSize - lrwpan::aMinMPDUOverhead)
     {
         // Note, this is just testing maximum theoretical frame size per the spec
         // The frame could still be too large once headers are put on
@@ -795,7 +798,8 @@
     {
         m_numLostBeacons = 0;
         // search for a beacon for a time = incomingSuperframe symbols + 960 symbols
-        searchSymbols = ((uint64_t)1 << m_incomingBeaconOrder) + 1 * aBaseSuperframeDuration;
+        searchSymbols =
+            ((uint64_t)1 << m_incomingBeaconOrder) + 1 * lrwpan::aBaseSuperframeDuration;
         searchBeaconTime = Seconds((double)searchSymbols / symbolRate);
         m_beaconTrackingOn = true;
         m_trackingEvent =
@@ -823,6 +827,40 @@
 }
 
 void
+LrWpanMac::MlmeSetRequest(LrWpanMacPibAttributeIdentifier id, Ptr<LrWpanMacPibAttributes> attribute)
+{
+    MlmeSetConfirmParams confirmParams;
+    switch (id)
+    {
+    case macBeaconPayload:
+        if (attribute->macBeaconPayload->GetSize() > lrwpan::aMaxBeaconPayloadLenght)
+        {
+            confirmParams.m_status = MLMESET_INVALID_PARAMETER;
+        }
+        else
+        {
+            confirmParams.m_status = MLMESET_SUCCESS;
+            m_macBeaconPayload = attribute->macBeaconPayload;
+            m_macBeaconPayloadLength = attribute->macBeaconPayload->GetSize();
+        }
+        break;
+    case macBeaconPayloadLength:
+        confirmParams.m_status = MLMESET_INVALID_PARAMETER;
+        break;
+    default:
+        // TODO: Add support for setting other attributes
+        confirmParams.m_status = MLMESET_UNSUPPORTED_ATTRIBUTE;
+        break;
+    }
+
+    if (!m_mlmeSetConfirmCallback.IsNull())
+    {
+        confirmParams.id = id;
+        m_mlmeSetConfirmCallback(confirmParams);
+    }
+}
+
+void
 LrWpanMac::SendOneBeacon()
 {
     NS_LOG_FUNCTION(this);
@@ -831,9 +869,18 @@
     LrWpanMacHeader macHdr(LrWpanMacHeader::LRWPAN_MAC_BEACON, m_macBsn.GetValue());
     m_macBsn++;
     BeaconPayloadHeader macPayload;
-    Ptr<Packet> beaconPacket = Create<Packet>();
+    Ptr<Packet> beaconPacket;
     LrWpanMacTrailer macTrailer;
 
+    if (m_macBeaconPayload == nullptr)
+    {
+        beaconPacket = Create<Packet>();
+    }
+    else
+    {
+        beaconPacket = m_macBeaconPayload;
+    }
+
     macHdr.SetDstAddrMode(LrWpanMacHeader::SHORTADDR);
     macHdr.SetDstAddrFields(GetPanId(), Mac16Address("ff:ff"));
 
@@ -1140,9 +1187,9 @@
             m_fnlCapSlot = 15;
 
             m_beaconInterval =
-                (static_cast<uint32_t>(1 << m_macBeaconOrder)) * aBaseSuperframeDuration;
-            m_superframeDuration =
-                (static_cast<uint32_t>(1 << m_macSuperframeOrder)) * aBaseSuperframeDuration;
+                (static_cast<uint32_t>(1 << m_macBeaconOrder)) * lrwpan::aBaseSuperframeDuration;
+            m_superframeDuration = (static_cast<uint32_t>(1 << m_macSuperframeOrder)) *
+                                   lrwpan::aBaseSuperframeDuration;
 
             // TODO: change the beacon sending according to the startTime parameter (if not PAN
             // coordinator)
@@ -1415,7 +1462,7 @@
 {
     uint64_t symbolRate = (uint64_t)m_phy->GetDataOrSymbolRate(false); // symbols per second
 
-    if (m_numLostBeacons > aMaxLostBeacons)
+    if (m_numLostBeacons > lrwpan::aMaxLostBeacons)
     {
         MlmeSyncLossIndicationParams syncLossParams;
         // syncLossParams.m_logCh =
@@ -1433,7 +1480,8 @@
         // Search for one more beacon
         uint64_t searchSymbols;
         Time searchBeaconTime;
-        searchSymbols = ((uint64_t)1 << m_incomingBeaconOrder) + 1 * aBaseSuperframeDuration;
+        searchSymbols =
+            ((uint64_t)1 << m_incomingBeaconOrder) + 1 * lrwpan::aBaseSuperframeDuration;
         searchBeaconTime = Seconds((double)searchSymbols / symbolRate);
         m_trackingEvent =
             Simulator::Schedule(searchBeaconTime, &LrWpanMac::BeaconSearchTimeout, this);
@@ -1591,6 +1639,12 @@
 }
 
 void
+LrWpanMac::SetMlmeSetConfirmCallback(MlmeSetConfirmCallback c)
+{
+    m_mlmeSetConfirmCallback = c;
+}
+
+void
 LrWpanMac::PdDataIndication(uint32_t psduLength, Ptr<Packet> p, uint8_t lqi)
 {
     NS_ASSERT(m_lrWpanMacState == MAC_IDLE || m_lrWpanMacState == MAC_ACK_PENDING ||
@@ -1785,7 +1839,7 @@
                 if (acceptFrame && (m_csmaCa->IsSlottedCsmaCa() && m_capEvent.IsRunning()))
                 {
                     Time timeLeftInCap = Simulator::GetDelayLeft(m_capEvent);
-                    uint64_t ackSymbols = m_phy->aTurnaroundTime + m_phy->GetPhySHRDuration() +
+                    uint64_t ackSymbols = lrwpan::aTurnaroundTime + m_phy->GetPhySHRDuration() +
                                           ceil(6 * m_phy->GetPhySymbolsPerOctet());
                     Time ackTime = Seconds((double)ackSymbols / symbolRate);
 
@@ -1935,9 +1989,9 @@
 
                         m_incomingBeaconInterval =
                             (static_cast<uint32_t>(1 << m_incomingBeaconOrder)) *
-                            aBaseSuperframeDuration;
+                            lrwpan::aBaseSuperframeDuration;
                         m_incomingSuperframeDuration =
-                            aBaseSuperframeDuration *
+                            lrwpan::aBaseSuperframeDuration *
                             (static_cast<uint32_t>(1 << m_incomingSuperframeOrder));
 
                         if (incomingSuperframe.IsBattLifeExt())
@@ -1982,7 +2036,7 @@
                                 beaconParams.m_panDescriptor = panDescriptor;
                                 beaconParams.m_sduLength = p->GetSize();
                                 beaconParams.m_sdu = p;
-                                m_mlmeBeaconNotifyIndicationCallback(beaconParams, originalPkt);
+                                m_mlmeBeaconNotifyIndicationCallback(beaconParams);
                             }
                         }
 
@@ -2039,7 +2093,7 @@
 
                                 searchSymbols =
                                     (static_cast<uint64_t>(1 << m_incomingBeaconOrder)) +
-                                    1 * aBaseSuperframeDuration;
+                                    1 * lrwpan::aBaseSuperframeDuration;
                                 searchBeaconTime =
                                     Seconds(static_cast<double>(searchSymbols / symbolRate));
                                 m_trackingEvent =
@@ -2068,7 +2122,9 @@
                             MlmeBeaconNotifyIndicationParams beaconParams;
                             beaconParams.m_bsn = receivedMacHdr.GetSeqNum();
                             beaconParams.m_panDescriptor = panDescriptor;
-                            m_mlmeBeaconNotifyIndicationCallback(beaconParams, originalPkt);
+                            beaconParams.m_sduLength = p->GetSize();
+                            beaconParams.m_sdu = p;
+                            m_mlmeBeaconNotifyIndicationCallback(beaconParams);
                         }
                     }
                 }
@@ -2507,12 +2563,12 @@
     if (m_macBeaconOrder == 15)
     {
         // Non-beacon enabled mode
-        unit = aBaseSuperframeDuration * m_macTransactionPersistenceTime;
+        unit = lrwpan::aBaseSuperframeDuration * m_macTransactionPersistenceTime;
     }
     else
     {
         // Beacon-enabled mode
-        unit = ((static_cast<uint32_t>(1) << m_macBeaconOrder) * aBaseSuperframeDuration) *
+        unit = ((static_cast<uint32_t>(1) << m_macBeaconOrder) * lrwpan::aBaseSuperframeDuration) *
                m_macTransactionPersistenceTime;
     }
 
@@ -2789,7 +2845,7 @@
                 if (!m_mcpsDataConfirmCallback.IsNull())
                 {
                     McpsDataConfirmParams confirmParams;
-                    NS_ASSERT_MSG(m_txQueue.size() > 0, "TxQsize = 0");
+                    NS_ASSERT_MSG(!m_txQueue.empty(), "TxQsize = 0");
                     Ptr<TxQueueElement> txQElement = m_txQueue.front();
                     confirmParams.m_msduHandle = txQElement->txQMsduHandle;
                     confirmParams.m_status = IEEE_802_15_4_SUCCESS;
@@ -2838,12 +2894,12 @@
                     case CommandPayloadHeader::SUCCESSFUL:
                         confirmParams.m_status =
                             LrWpanMlmeAssociateConfirmStatus::MLMEASSOC_SUCCESS;
-                        confirmParams.m_assocShortAddr =
-                            GetShortAddress(); // the original short address used in the association
-                                               // request
-                        SetShortAddress(
-                            receivedMacPayload
-                                .GetShortAddr()); // the assigned short address by the coordinator
+                        // The original short address used in the association
+                        // used in the association request
+                        confirmParams.m_assocShortAddr = GetShortAddress();
+
+                        // The assigned short address by the coordinator
+                        SetShortAddress(receivedMacPayload.GetShortAddr());
                         m_macPanId = receivedMacHdr.GetSrcPanId();
                         break;
                     case CommandPayloadHeader::FULL_CAPACITY:
@@ -2894,7 +2950,7 @@
     {
         if (!macHdr.IsAcknowledgment())
         {
-            NS_ASSERT_MSG(m_txQueue.size() > 0, "TxQsize = 0");
+            NS_ASSERT_MSG(!m_txQueue.empty(), "TxQsize = 0");
             Ptr<TxQueueElement> txQElement = m_txQueue.front();
             m_macTxDropTrace(txQElement->txQPkt);
             if (!m_mcpsDataConfirmCallback.IsNull())
@@ -3059,7 +3115,7 @@
         if (status == LrWpanPhyEnumeration::IEEE_802_15_4_PHY_SUCCESS)
         {
             uint64_t symbolRate = static_cast<uint64_t>(m_phy->GetDataOrSymbolRate(false));
-            uint64_t scanDuration = aBaseSuperframeDuration *
+            uint64_t scanDuration = lrwpan::aBaseSuperframeDuration *
                                     ((static_cast<uint32_t>(1 << m_scanParams.m_scanDuration)) + 1);
             Time nextScanTime = Seconds(static_cast<double>(scanDuration / symbolRate));
 
@@ -3432,7 +3488,7 @@
 uint64_t
 LrWpanMac::GetMacAckWaitDuration() const
 {
-    return m_csmaCa->GetUnitBackoffPeriod() + m_phy->aTurnaroundTime + m_phy->GetPhySHRDuration() +
+    return lrwpan::aUnitBackoffPeriod + lrwpan::aTurnaroundTime + m_phy->GetPhySHRDuration() +
            ceil(6 * m_phy->GetPhySymbolsPerOctet());
 }
 
@@ -3483,7 +3539,7 @@
 {
     NS_ASSERT(m_txPkt);
 
-    if (m_txPkt->GetSize() <= aMaxSIFSFrameSize)
+    if (m_txPkt->GetSize() <= lrwpan::aMaxSIFSFrameSize)
     {
         return m_macSIFSPeriod;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac.h ns-3.38/src/lr-wpan/model/lr-wpan-mac.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac.h	2023-03-16 20:54:23.632868500 -0700
@@ -56,20 +56,20 @@
  *
  * Tx options
  */
-typedef enum
+enum LrWpanTxOption
 {
     TX_OPTION_NONE = 0,    //!< TX_OPTION_NONE
     TX_OPTION_ACK = 1,     //!< TX_OPTION_ACK
     TX_OPTION_GTS = 2,     //!< TX_OPTION_GTS
     TX_OPTION_INDIRECT = 4 //!< TX_OPTION_INDIRECT
-} LrWpanTxOption;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * MAC states
  */
-typedef enum
+enum LrWpanMacState
 {
     MAC_IDLE,               //!< MAC_IDLE
     MAC_CSMA,               //!< MAC_CSMA
@@ -81,45 +81,45 @@
     MAC_GTS,                //!< MAC_GTS
     MAC_INACTIVE,           //!< MAC_INACTIVE
     MAC_CSMA_DEFERRED       //!< MAC_CSMA_DEFERRED
-} LrWpanMacState;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Superframe status
  */
-typedef enum
+enum SuperframeStatus
 {
     BEACON,  //!< The Beacon transmission or reception Period
     CAP,     //!< Contention Access Period
     CFP,     //!< Contention Free Period
     INACTIVE //!< Inactive Period or unslotted CSMA-CA
-} SuperframeStatus;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Superframe type
  */
-typedef enum
+enum SuperframeType
 {
     OUTGOING = 0, //!< Outgoing Superframe
     INCOMING = 1  //!< Incoming Superframe
-} SuperframeType;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Indicates a pending MAC primitive
  */
-typedef enum
+enum PendingPrimitiveStatus
 {
     MLME_NONE = 0,      //!< No pending primitive
     MLME_START_REQ = 1, //!< Pending MLME-START.request primitive
     MLME_SCAN_REQ = 2,  //!< Pending MLME-SCAN.request primitive
     MLME_ASSOC_REQ = 3, //!< Pending MLME-ASSOCIATION.request primitive
     MLME_SYNC_REQ = 4   //!< Pending MLME-SYNC.request primitive
-} PendingPrimitiveStatus;
+};
 
 namespace TracedValueCallback
 {
@@ -149,47 +149,47 @@
  *
  * table 80 of 802.15.4
  */
-typedef enum
+enum LrWpanAddressMode
 {
     NO_PANID_ADDR = 0,
     ADDR_MODE_RESERVED = 1,
     SHORT_ADDR = 2,
     EXT_ADDR = 3
-} LrWpanAddressMode;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * table 83 of 802.15.4
  */
-typedef enum
+enum LrWpanAssociationStatus
 {
     ASSOCIATED = 0,
     PAN_AT_CAPACITY = 1,
     PAN_ACCESS_DENIED = 2,
     ASSOCIATED_WITHOUT_ADDRESS = 0xfe,
     DISASSOCIATED = 0xff
-} LrWpanAssociationStatus;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 30 of IEEE 802.15.4-2011
  */
-typedef enum
+enum LrWpanMlmeScanType
 {
     MLMESCAN_ED = 0x00,
     MLMESCAN_ACTIVE = 0x01,
     MLMESCAN_PASSIVE = 0x02,
     MLMESCAN_ORPHAN = 0x03
-} LrWpanMlmeScanType;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 42 of 802.15.4-2006
  */
-typedef enum
+enum LrWpanMcpsDataConfirmStatus
 {
     IEEE_802_15_4_SUCCESS = 0,
     IEEE_802_15_4_TRANSACTION_OVERFLOW = 1,
@@ -203,14 +203,14 @@
     IEEE_802_15_4_UNAVAILABLE_KEY = 9,
     IEEE_802_15_4_UNSUPPORTED_SECURITY = 10,
     IEEE_802_15_4_INVALID_PARAMETER = 11
-} LrWpanMcpsDataConfirmStatus;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 35 of IEEE 802.15.4-2011
  */
-typedef enum
+enum LrWpanMlmeStartConfirmStatus
 {
     MLMESTART_SUCCESS = 0,
     MLMESTART_NO_SHORT_ADDRESS = 1,
@@ -222,14 +222,14 @@
     MLMESTART_UNAVAILABLE_KEY = 7,
     MLMESTART_UNSUPPORTED_SECURITY = 8,
     MLMESTART_CHANNEL_ACCESS_FAILURE = 9
-} LrWpanMlmeStartConfirmStatus;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 31 of IEEE 802.15.4-2011
  */
-typedef enum
+enum LrWpanMlmeScanConfirmStatus
 {
     MLMESCAN_SUCCESS = 0,
     MLMESCAN_LIMIT_REACHED = 1,
@@ -240,14 +240,14 @@
     MLMESCAN_UNAVAILABLE_KEY = 6,
     MLMESCAN_UNSUPPORTED_SECURITY = 7,
     MLMESCAN_INVALID_PARAMETER = 8
-} LrWpanMlmeScanConfirmStatus;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 12 of IEEE 802.15.4-2011
  */
-typedef enum
+enum LrWpanMlmeAssociateConfirmStatus
 {
     MLMEASSOC_SUCCESS = 0,
     MLMEASSOC_FULL_CAPACITY = 1,
@@ -259,27 +259,27 @@
     MLMEASSOC_FRAME_TOO_LONG = 7,
     MLMEASSOC_UNSUPPORTED_LEGACY = 8,
     MLMEASSOC_INVALID_PARAMETER = 9
-} LrWpanMlmeAssociateConfirmStatus;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 37 of IEEE 802.15.4-2011
  */
-typedef enum
+enum LrWpanSyncLossReason
 {
     MLMESYNCLOSS_PAN_ID_CONFLICT = 0,
     MLMESYNCLOSS_REALIGMENT = 1,
     MLMESYNCLOSS_BEACON_LOST = 2,
     MLMESYNCLOSS_SUPERFRAME_OVERLAP = 3
-} LrWpanSyncLossReason;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 18 of IEEE 802.15.4-2011
  */
-typedef enum
+enum LrWpanMlmeCommStatus
 {
     MLMECOMMSTATUS_SUCCESS = 0,
     MLMECOMMSTATUS_TRANSACTION_OVERFLOW = 1,
@@ -289,14 +289,14 @@
     MLMECOMMSTATUS_COUNTER_ERROR = 5,
     MLMECOMMSTATUS_FRAME_TOO_LONG = 6,
     MLMECOMMSTATUS_INVALID_PARAMETER = 7
-} LrWpanMlmeCommStatus;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * Table 39 of IEEE 802.15.4-2011
  */
-typedef enum
+enum LrWpanMlmePollConfirmStatus
 {
     MLMEPOLL_SUCCESS = 0,
     MLMEPOLL_CHANNEL_ACCESS_FAILURE = 2,
@@ -307,7 +307,46 @@
     MLMEPOLL_UNAVAILABLE_KEY = 7,
     MLMEPOLL_UNSUPPORTED_SECURITY = 8,
     MLMEPOLL_INVALID_PARAMETER = 9
-} LrWpanMlmePollConfirmStatus;
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * Table 33 of IEEE 802.15.4-2011
+ */
+enum LrWpanMlmeSetConfirmStatus
+{
+    MLMESET_SUCCESS = 0,
+    MLMESET_READ_ONLY = 1,
+    MLMESET_UNSUPPORTED_ATTRIBUTE = 2,
+    MLMESET_INVALID_INDEX = 3,
+    MLMESET_INVALID_PARAMETER = 4
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * IEEE802.15.4-2011 MAC PIB Attribute Identifiers Table 52 in section 6.4.2
+ *
+ */
+enum LrWpanMacPibAttributeIdentifier
+{
+    macBeaconPayload = 0,
+    macBeaconPayloadLength = 1
+    // TODO: complete other MAC pib attributes
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * IEEE802.15.4-2011 PHY PIB Attributes Table 52 in section 6.4.2
+ */
+struct LrWpanMacPibAttributes : public SimpleRefCount<LrWpanMacPibAttributes>
+{
+    Ptr<Packet> macBeaconPayload;      //!< The contents of the beacon payload.
+    uint8_t macBeaconPayloadLength{0}; //!< The length in octets of the beacon payload.
+    // TODO: complete other MAC pib attributes
+};
 
 /**
  * \ingroup lr-wpan
@@ -318,8 +357,8 @@
 {
     LrWpanAddressMode m_coorAddrMode{SHORT_ADDR}; //!< The coordinator addressing mode corresponding
                                                   //!< to the received beacon frame.
-    uint16_t m_coorPanId{
-        0xffff}; //!< The PAN ID of the coordinator as specified in the received beacon frame.
+    uint16_t m_coorPanId{0xffff};                 //!< The PAN ID of the coordinator as specified in
+                                                  //!<  the received beacon frame.
     Mac16Address m_coorShortAddr; //!< The coordinator short address as specified in the coordinator
                                   //!< address mode.
     Mac64Address m_coorExtAddr;   //!< The coordinator extended address as specified in the
@@ -328,10 +367,10 @@
     uint8_t m_logChPage{0};       //!< The current channel page occupied by the network.
     SuperframeField m_superframeSpec; //!< The superframe specification as specified in the received
                                       //!< beacon frame.
-    bool m_gtsPermit{
-        false}; //!< TRUE if the beacon is from the PAN coordinator that is accepting GTS requests.
-    uint8_t m_linkQuality{
-        0}; //!< The LQI at which the network beacon was received. Lower values represent lower LQI.
+    bool m_gtsPermit{false};          //!< TRUE if the beacon is from the PAN coordinator
+                                      //!< that is accepting GTS requests.
+    uint8_t m_linkQuality{0};         //!< The LQI at which the network beacon was received.
+                                      //!< Lower values represent lower LQI.
     Time m_timeStamp; //!< Beacon frame reception time. Used as Time data type in ns-3 to avoid
                       //!< precision problems.
 };
@@ -603,6 +642,19 @@
 /**
  * \ingroup lr-wpan
  *
+ * MLME-SET.confirm params. See  802.15.4-2011   Section 6.2.11.2
+ */
+struct MlmeSetConfirmParams
+{
+    LrWpanMlmeSetConfirmStatus m_status{
+        MLMESET_UNSUPPORTED_ATTRIBUTE}; //!< The result of the request to write
+                                        //!< the PIB attribute.
+    LrWpanMacPibAttributeIdentifier id; //!< The id of the PIB attribute that was written.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
  * This callback is called after a McpsDataRequest has been called from
  * the higher layer.  It returns a status of the outcome of the
  * transmission request
@@ -638,8 +690,7 @@
  *  \todo for now, we do not deliver all of the parameters in section
  *  802.15.4-2006 6.2.4.1 but just send up the packet.
  */
-typedef Callback<void, MlmeBeaconNotifyIndicationParams, Ptr<Packet>>
-    MlmeBeaconNotifyIndicationCallback;
+typedef Callback<void, MlmeBeaconNotifyIndicationParams> MlmeBeaconNotifyIndicationCallback;
 
 /**
  * \ingroup lr-wpan
@@ -703,6 +754,15 @@
 /**
  * \ingroup lr-wpan
  *
+ * This callback is called after a MlmeSetRequest has been called from
+ * the higher layer to set a PIB. It returns a status of the outcome of the
+ * write attempt.
+ */
+typedef Callback<void, MlmeSetConfirmParams> MlmeSetConfirmCallback;
+
+/**
+ * \ingroup lr-wpan
+ *
  * Class that implements the LR-WPAN MAC state machine
  */
 class LrWpanMac : public Object
@@ -714,48 +774,8 @@
      * \return the object TypeId
      */
     static TypeId GetTypeId();
-    // MAC sublayer constants
-    /**
-     * The minimum number of octets added by the MAC sublayer to the PSDU.
-     * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
-     */
-    static constexpr uint32_t aMinMPDUOverhead = 9;
-
-    /**
-     * Length of a superframe slot in symbols. Defaults to 60 symbols in each
-     * superframe slot.
-     * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
-     */
-    static constexpr uint32_t aBaseSlotDuration = 60;
-
-    /**
-     * Number of a superframe slots per superframe. Defaults to 16.
-     * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
-     */
-    static constexpr uint32_t aNumSuperframeSlots = 16;
-
-    /**
-     * Length of a superframe in symbols. Defaults to
-     * aBaseSlotDuration * aNumSuperframeSlots in symbols.
-     * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
-     */
-    static constexpr uint32_t aBaseSuperframeDuration = aBaseSlotDuration * aNumSuperframeSlots;
 
     /**
-     * The number of consecutive lost beacons that will
-     * cause the MAC sublayer of a receiving device to
-     * declare a loss of synchronization.
-     * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
-     */
-    static constexpr uint32_t aMaxLostBeacons = 4;
-
-    /**
-     * The maximum size of an MPDU, in octets, that can be
-     * followed by a Short InterFrame Spacing (SIFS) period.
-     * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
-     */
-    static constexpr uint32_t aMaxSIFSFrameSize = 18;
-    /**
      * Default constructor.
      */
     LrWpanMac();
@@ -766,7 +786,7 @@
      *
      * \return true, if the receiver is enabled during idle periods, false otherwise
      */
-    bool GetRxOnWhenIdle();
+    bool GetRxOnWhenIdle() const;
 
     /**
      * Set if the receiver should be enabled when the MAC is idle.
@@ -901,6 +921,16 @@
     void MlmePollRequest(MlmePollRequestParams params);
 
     /**
+     * IEEE 802.15.4-2011, section 6.2.11.1
+     * MLME-SET.request
+     * Attempts to write the given value to the indicated PIB attribute.
+     *
+     * \param id the attributed identifier
+     * \param attribute the attribute value
+     */
+    void MlmeSetRequest(LrWpanMacPibAttributeIdentifier id, Ptr<LrWpanMacPibAttributes> attribute);
+
+    /**
      * Set the CSMA/CA implementation to be used by the MAC.
      *
      * \param csmaCa the CSMA/CA implementation
@@ -1011,6 +1041,15 @@
      */
     void SetMlmePollConfirmCallback(MlmePollConfirmCallback c);
 
+    /**
+     * Set the callback for the confirmation of an attempt to write an attribute.
+     * The callback implements MLME-SET.confirm SAP of IEEE 802.15.4-2011,
+     * section 6.2.11.2
+     *
+     * \param c the callback
+     */
+    void SetMlmeSetConfirmCallback(MlmeSetConfirmCallback c);
+
     // interfaces between MAC and PHY
 
     /**
@@ -1078,7 +1117,7 @@
     /**
      * CSMA-CA algorithm calls back the MAC after executing channel assessment.
      *
-     * \param macState indicate BUSY oder IDLE channel condition
+     * \param macState indicate BUSY or IDLE channel condition
      */
     void SetLrWpanMacState(LrWpanMacState macState);
 
@@ -1255,6 +1294,19 @@
     SequenceNumber8 m_macBsn;
 
     /**
+     * The contents of the beacon payload.
+     * This value is set directly by the MLME-SET primitive.
+     * See IEEE 802.15.4-2011, section 6.4.2, Table 52.
+     */
+    Ptr<Packet> m_macBeaconPayload;
+
+    /**
+     * The length, in octets, of the beacon payload.
+     * See IEEE 802.15.4-2011, section 6.4.2, Table 52.
+     */
+    uint32_t m_macBeaconPayloadLength;
+
+    /**
      * The maximum number of retries allowed after a transmission failure.
      * See IEEE 802.15.4-2006, section 7.4.2, Table 86.
      */
@@ -1844,6 +1896,13 @@
     Ptr<LrWpanCsmaCa> m_csmaCa;
 
     /**
+     * This callback is used to report the result of an attribute writing request
+     * to the upper layers.
+     * See IEEE 802.15.4-2011, section 6.2.11.2.
+     */
+    MlmeSetConfirmCallback m_mlmeSetConfirmCallback;
+
+    /**
      * This callback is used to notify incoming beacon packets to the upper layers.
      * See IEEE 802.15.4-2011, section 6.2.4.1.
      */
@@ -1931,7 +1990,7 @@
     /**
      * The packet which is currently being sent by the MAC layer.
      */
-    Ptr<Packet> m_txPkt; // XXX need packet buffer instead of single packet
+    Ptr<Packet> m_txPkt;
 
     /**
      * The command request packet received. Briefly stored to proceed with operations
@@ -1940,15 +1999,14 @@
     Ptr<Packet> m_rxPkt;
 
     /**
-     * The short address used by this MAC. Currently we do not have complete
-     * extended address support in the MAC, nor do we have the association
-     * primitives, so this address has to be configured manually.
+     * The short address (16 bit address) used by this MAC. If supported,
+     * the short address must be assigned to the device by the coordinator
+     * during the association process.
      */
     Mac16Address m_shortAddress;
 
     /**
-     * The extended address used by this MAC. Extended addresses are currently not
-     * really supported.
+     * The extended 64 address (IEEE EUI-64) used by this MAC.
      */
     Mac64Address m_selfExt;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac-header.cc ns-3.38/src/lr-wpan/model/lr-wpan-mac-header.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac-header.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac-header.cc	2023-03-16 20:54:23.632868500 -0700
@@ -40,7 +40,7 @@
     SetFrameVer(1); // Indicates an IEEE 802.15.4 frame
 }
 
-LrWpanMacHeader::LrWpanMacHeader(enum LrWpanMacType wpanMacType, uint8_t seqNum)
+LrWpanMacHeader::LrWpanMacHeader(LrWpanMacType wpanMacType, uint8_t seqNum)
 {
     SetType(wpanMacType);
     SetSeqNum(seqNum);
@@ -266,7 +266,7 @@
 }
 
 void
-LrWpanMacHeader::SetType(enum LrWpanMacType wpanMacType)
+LrWpanMacHeader::SetType(LrWpanMacType wpanMacType)
 {
     m_fctrlFrmType = wpanMacType;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac-header.h ns-3.38/src/lr-wpan/model/lr-wpan-mac-header.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac-header.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac-header.h	2023-03-16 20:54:23.632868500 -0700
@@ -96,7 +96,7 @@
      * Data, ACK, Control MAC Header must have frame control and sequence number.
      * Beacon MAC Header must have frame control, sequence number, source PAN Id, source address.
      */
-    LrWpanMacHeader(enum LrWpanMacType wpanMacType, uint8_t seqNum);
+    LrWpanMacHeader(LrWpanMacType wpanMacType, uint8_t seqNum);
 
     ~LrWpanMacHeader() override;
 
@@ -104,7 +104,7 @@
      * Get the header type
      * \return the header type
      */
-    enum LrWpanMacType GetType() const;
+    LrWpanMacType GetType() const;
     /**
      * Get the Frame control field
      * \return the Frame control field
@@ -186,13 +186,13 @@
      */
     Mac64Address GetExtSrcAddr() const;
     /**
-     * Get the Auxiliary Security Header - Security Control Octect
-     * \return the Auxiliary Security Header - Security Control Octect
+     * Get the Auxiliary Security Header - Security Control Octet
+     * \return the Auxiliary Security Header - Security Control Octet
      */
     uint8_t GetSecControl() const;
     /**
-     * Get the Auxiliary Security Header - Frame Counter Octects
-     * \return the Auxiliary Security Header - Frame Counter Octects
+     * Get the Auxiliary Security Header - Frame Counter Octets
+     * \return the Auxiliary Security Header - Frame Counter Octets
      */
     uint32_t GetFrmCounter() const;
 
@@ -212,13 +212,13 @@
      */
     uint8_t GetSecCtrlReserved() const;
     /**
-     * Get the Auxiliary Security Header - Key Identifier - Key Source (2 Octects)
-     * \return the Auxiliary Security Header - Key Identifier - Key Source  (2 Octects)
+     * Get the Auxiliary Security Header - Key Identifier - Key Source (2 Octets)
+     * \return the Auxiliary Security Header - Key Identifier - Key Source  (2 Octets)
      */
     uint32_t GetKeyIdSrc32() const;
     /**
-     * Get the Auxiliary Security Header - Key Identifier - Key Source (4 Octects)
-     * \return the Auxiliary Security Header - Key Identifier - Key Source  (4 Octects)
+     * Get the Auxiliary Security Header - Key Identifier - Key Source (4 Octets)
+     * \return the Auxiliary Security Header - Key Identifier - Key Source  (4 Octets)
      */
     uint64_t GetKeyIdSrc64() const;
     /**
@@ -250,7 +250,7 @@
      * Set the Frame Control field "Frame Type" bits
      * \param wpanMacType the frame type
      */
-    void SetType(enum LrWpanMacType wpanMacType);
+    void SetType(LrWpanMacType wpanMacType);
     /**
      * Set the whole Frame Control field
      * \param frameControl the Frame Control field
@@ -342,12 +342,12 @@
     /* Auxiliary Security Header is only set if Sec Enable (SecU) field is set to 1 */
     /**
      * Set the auxiliary security header "Security Control" octet
-     * \param secLevel the "Security Control" octect
+     * \param secLevel the "Security Control" octet
      */
     void SetSecControl(uint8_t secLevel);
     /**
      * Set the auxiliary security header "Frame Counter" octet
-     * \param frmCntr the "Frame Counter" octect
+     * \param frmCntr the "Frame Counter" octet
      */
     void SetFrmCounter(uint32_t frmCntr);
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc	2023-03-16 20:54:23.632868500 -0700
@@ -135,7 +135,7 @@
     SetCommandFrameType(CMD_RESERVED);
 }
 
-CommandPayloadHeader::CommandPayloadHeader(enum MacCommand macCmd)
+CommandPayloadHeader::CommandPayloadHeader(MacCommand macCmd)
 {
     SetCommandFrameType(macCmd);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac-pl-headers.h ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac-pl-headers.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.h	2023-03-16 20:54:23.632868500 -0700
@@ -136,7 +136,7 @@
      * Constructor
      * \param macCmd the command type of this command header
      */
-    CommandPayloadHeader(enum MacCommand macCmd);
+    CommandPayloadHeader(MacCommand macCmd);
     /**
      * \brief Get the type ID.
      * \return the object TypeId
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac-trailer.cc ns-3.38/src/lr-wpan/model/lr-wpan-mac-trailer.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac-trailer.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac-trailer.cc	2023-03-16 20:54:23.632868500 -0700
@@ -28,7 +28,8 @@
 
 NS_OBJECT_ENSURE_REGISTERED(LrWpanMacTrailer);
 
-const uint16_t LrWpanMacTrailer::LR_WPAN_MAC_FCS_LENGTH = 2;
+/// The length in octets of the IEEE 802.15.4 MAC FCS field
+constexpr uint16_t LR_WPAN_MAC_FCS_LENGTH = 2;
 
 LrWpanMacTrailer::LrWpanMacTrailer()
     : m_fcs(0),
@@ -135,7 +136,7 @@
 }
 
 bool
-LrWpanMacTrailer::IsFcsEnabled()
+LrWpanMacTrailer::IsFcsEnabled() const
 {
     return m_calcFcs;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-mac-trailer.h ns-3.38/src/lr-wpan/model/lr-wpan-mac-trailer.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-mac-trailer.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-mac-trailer.h	2023-03-16 20:54:23.632868500 -0700
@@ -39,11 +39,6 @@
 {
   public:
     /**
-     * The length in octets of the IEEE 802.15.4 MAC FCS field
-     */
-    static const uint16_t LR_WPAN_MAC_FCS_LENGTH;
-
-    /**
      * Get the type ID.
      *
      * \return the object TypeId
@@ -99,7 +94,7 @@
      *
      * \return true, if FCS calculation is enabled, false otherwise.
      */
-    bool IsFcsEnabled();
+    bool IsFcsEnabled() const;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-net-device.cc ns-3.38/src/lr-wpan/model/lr-wpan-net-device.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-net-device.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-net-device.cc	2023-03-16 20:54:23.632868500 -0700
@@ -257,6 +257,10 @@
     {
         m_mac->SetShortAddress(Mac16Address::ConvertFrom(address));
     }
+    else if (Mac64Address::IsMatchingType(address))
+    {
+        m_mac->SetExtendedAddress(Mac64Address::ConvertFrom(address));
+    }
     else if (Mac48Address::IsMatchingType(address))
     {
         uint8_t buf[6];
@@ -292,6 +296,19 @@
     return pseudoAddress;
 }
 
+void
+LrWpanNetDevice::SetPanAssociation(uint16_t panId,
+                                   Mac64Address coordExtAddr,
+                                   Mac16Address coordShortAddr,
+                                   Mac16Address assignedShortAddr)
+{
+    NS_LOG_FUNCTION(this);
+    m_mac->SetPanId(panId);
+    m_mac->SetAssociatedCoor(coordExtAddr);
+    m_mac->SetAssociatedCoor(coordShortAddr);
+    m_mac->SetShortAddress(assignedShortAddr);
+}
+
 bool
 LrWpanNetDevice::SetMtu(const uint16_t mtu)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-net-device.h ns-3.38/src/lr-wpan/model/lr-wpan-net-device.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-net-device.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-net-device.h	2023-03-16 20:54:23.632868500 -0700
@@ -135,6 +135,36 @@
      * \returns The short address.
      */
     Address GetAddress() const override;
+
+    /**
+     * This method is use to manually configure the coordinator through
+     * which the device or coordinator is associated. When assigning a short address
+     * the extended address must also be present.
+     *
+     * \param panId The id of the PAN used by the coordinator device.
+     *
+     * \param coordExtAddr The coordinator extended address (EUI-64) through which this
+     *                     device or coordinator is associated.
+     *
+     * \param coordShortAddr The coordinator assigned short address through which this
+     *                       device or coordinator is associated.
+     *                       [FF:FF] address indicates that the value is unknown.
+     *                       [FF:FE] indicates that the associated coordinator is using only
+     *                       its extended address.
+     *
+     *
+     * \param assignedShortAddr The assigned short address for this device.
+     *                          [FF:FF] address indicates that the device have no short address
+     *                                  and is not associated.
+     *                          [FF:FE] address indicates that the devices has associated but
+     *                          has not been allocated a short address.
+     *
+     */
+    void SetPanAssociation(uint16_t panId,
+                           Mac64Address coordExtAddr,
+                           Mac16Address coordShortAddr,
+                           Mac16Address assignedShortAddr);
+
     bool SetMtu(const uint16_t mtu) override;
     uint16_t GetMtu() const override;
     bool IsLinkUp() const override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-phy.cc ns-3.38/src/lr-wpan/model/lr-wpan-phy.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-phy.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-phy.cc	2023-03-16 20:54:23.632868500 -0700
@@ -20,6 +20,7 @@
  */
 #include "lr-wpan-phy.h"
 
+#include "lr-wpan-constants.h"
 #include "lr-wpan-error-model.h"
 #include "lr-wpan-lqi-tag.h"
 #include "lr-wpan-net-device.h"
@@ -49,13 +50,13 @@
 
 NS_OBJECT_ENSURE_REGISTERED(LrWpanPhy);
 
-// Table 22 in section 6.4.1 of ieee802.15.4
-const uint32_t LrWpanPhy::aMaxPhyPacketSize = 127; // max PSDU in octets
-const uint32_t LrWpanPhy::aTurnaroundTime = 12;    // RX-to-TX or TX-to-RX in symbol periods
-
-// IEEE802.15.4-2006 Table 1 in section 6.1.1. and IEEE 802.15.4c-2009, IEEE 802.15.4d-2009
-// The index follows LrWpanPhyOption (kb/s and ksymbol/s)
-const LrWpanPhyDataAndSymbolRates LrWpanPhy::dataSymbolRates[IEEE_802_15_4_INVALID_PHY_OPTION] = {
+/**
+ * The data and symbol rates for the different PHY options.
+ * See Table 1 in section 6.1.1 IEEE 802.15.4-2006, IEEE 802.15.4c-2009, IEEE 802.15.4d-2009.
+ * Bit rate is in kbit/s.  Symbol rate is in ksymbol/s.
+ * The index follows LrWpanPhyOption (kb/s and ksymbol/s)
+ */
+static const LrWpanPhyDataAndSymbolRates dataSymbolRates[IEEE_802_15_4_INVALID_PHY_OPTION]{
     {20.0, 20.0},
     {40.0, 40.0},
     {20.0, 20.0},
@@ -64,20 +65,27 @@
     {250.0, 62.5},
     {100.0, 25.0},
     {250.0, 62.5},
-    {250.0, 62.5}};
-// IEEE802.15.4-2006,IEEE 802.15.4c-2009, IEEE 802.15.4d-2009  Table 19 and Table 20 in section 6.3.
-// The PHR is 1 octet and it follows phySymbolsPerOctet in Table 23
-// The index follows LrWpanPhyOption
-const LrWpanPhyPpduHeaderSymbolNumber
-    LrWpanPhy::ppduHeaderSymbolNumbers[IEEE_802_15_4_INVALID_PHY_OPTION] = {{32.0, 8.0, 8.0},
-                                                                            {32.0, 8.0, 8.0},
-                                                                            {32.0, 8.0, 8.0},
-                                                                            {2.0, 1.0, 0.4},
-                                                                            {6.0, 1.0, 1.6},
-                                                                            {8.0, 2.0, 2.0},
-                                                                            {8.0, 2.0, 2.0},
-                                                                            {8.0, 2.0, 2.0},
-                                                                            {8.0, 2.0, 2.0}};
+    {250.0, 62.5},
+};
+
+/**
+ * The preamble, SFD, and PHR lengths in symbols for the different PHY options.
+ * See Table 19 and Table 20 in section 6.3 IEEE 802.15.4-2006, IEEE 802.15.4c-2009, IEEE
+ * 802.15.4d-2009.
+ * The PHR is 1 octet and it follows phySymbolsPerOctet in Table 23.
+ * The index follows LrWpanPhyOption.
+ */
+const LrWpanPhyPpduHeaderSymbolNumber ppduHeaderSymbolNumbers[IEEE_802_15_4_INVALID_PHY_OPTION]{
+    {32.0, 8.0, 8.0},
+    {32.0, 8.0, 8.0},
+    {32.0, 8.0, 8.0},
+    {2.0, 1.0, 0.4},
+    {6.0, 1.0, 1.6},
+    {8.0, 2.0, 2.0},
+    {8.0, 2.0, 2.0},
+    {8.0, 2.0, 2.0},
+    {8.0, 2.0, 2.0},
+};
 
 TypeId
 LrWpanPhy::GetTypeId()
@@ -593,7 +601,7 @@
 {
     NS_LOG_FUNCTION(this << psduLength << p);
 
-    if (psduLength > aMaxPhyPacketSize)
+    if (psduLength > lrwpan::aMaxPhyPacketSize)
     {
         if (!m_pdDataConfirmCallback.IsNull())
         {
@@ -861,7 +869,7 @@
             m_trxStatePending = IEEE_802_15_4_PHY_TX_ON;
 
             // Delay for turnaround time (BUSY_RX|RX_ON ---> TX_ON)
-            Time setTime = Seconds((double)aTurnaroundTime / GetDataOrSymbolRate(false));
+            Time setTime = Seconds((double)lrwpan::aTurnaroundTime / GetDataOrSymbolRate(false));
             m_setTRXState = Simulator::Schedule(setTime, &LrWpanPhy::EndSetTRXState, this);
             return;
         }
@@ -928,7 +936,7 @@
             //       even when the transmitter is not busy? (6.9.1)
             m_trxStatePending = IEEE_802_15_4_PHY_RX_ON;
 
-            Time setTime = Seconds((double)aTurnaroundTime / GetDataOrSymbolRate(false));
+            Time setTime = Seconds((double)lrwpan::aTurnaroundTime / GetDataOrSymbolRate(false));
             m_setTRXState = Simulator::Schedule(setTime, &LrWpanPhy::EndSetTRXState, this);
             return;
         }
@@ -1158,14 +1166,10 @@
 
             m_phyPIBAttributes.phyCurrentPage = attribute->phyCurrentPage;
 
-            LrWpanSpectrumValueHelper psdHelper;
-            m_txPsd = psdHelper.CreateTxPowerSpectralDensity(
-                GetNominalTxPowerFromPib(m_phyPIBAttributes.phyTransmitPower),
-                m_phyPIBAttributes.phyCurrentChannel);
-            // if the page is changed we need to also update the Noise Power Spectral Density
-            m_noise =
-                psdHelper.CreateNoisePowerSpectralDensity(m_phyPIBAttributes.phyCurrentChannel);
-            m_signal = Create<LrWpanInterferenceHelper>(m_noise->GetSpectrumModel());
+            // TODO: Set the maximum possible sensitivity by default.
+            //       This maximum sensitivity depends on the modulation used.
+            //       Currently Only O-QPSK 250kbps is supported so we use its max sensitivity.
+            SetRxSensitivity(-106.58);
         }
         break;
     }
@@ -1208,14 +1212,8 @@
 
             m_phyPIBAttributes.phyCurrentChannel = attribute->phyCurrentChannel;
 
-            LrWpanSpectrumValueHelper psdHelper;
-            m_txPsd = psdHelper.CreateTxPowerSpectralDensity(
-                GetNominalTxPowerFromPib(m_phyPIBAttributes.phyTransmitPower),
-                m_phyPIBAttributes.phyCurrentChannel);
-            // if the channel is changed we need to also update the Noise Power Spectral Density
-            m_noise =
-                psdHelper.CreateNoisePowerSpectralDensity(m_phyPIBAttributes.phyCurrentChannel);
-            m_signal = Create<LrWpanInterferenceHelper>(m_noise->GetSpectrumModel());
+            // use the prev configured sensitivity before changing the channel
+            SetRxSensitivity(WToDbm(m_rxSensitivity));
         }
         break;
     }
@@ -1687,15 +1685,11 @@
     m_edPower.lastUpdate = Seconds(0.0);
     m_edPower.measurementLength = Seconds(0.0);
 
-    // TODO: What is the RX sensibility that should be set for other frequencies?
-    // default -110 dBm in W for 2.4 GHz
-    m_rxSensitivity = pow(10.0, -106.58 / 10.0) / 1000.0;
-    LrWpanSpectrumValueHelper psdHelper;
-    m_txPsd = psdHelper.CreateTxPowerSpectralDensity(
-        GetNominalTxPowerFromPib(m_phyPIBAttributes.phyTransmitPower),
-        m_phyPIBAttributes.phyCurrentChannel);
-    m_noise = psdHelper.CreateNoisePowerSpectralDensity(m_phyPIBAttributes.phyCurrentChannel);
-    m_signal = Create<LrWpanInterferenceHelper>(m_noise->GetSpectrumModel());
+    // TODO: Change the limits  Rx sensitivity when other modulations are supported
+    // Currently, only O-QPSK 250kbps is supported and its maximum possible sensitivity is
+    // equal to -106.58 dBm and its minimum sensitivity is defined as -85 dBm
+    SetRxSensitivity(-106.58);
+
     m_rxLastUpdate = Seconds(0);
     Ptr<Packet> none_packet = nullptr;
     Ptr<LrWpanSpectrumSignalParameters> none_params = nullptr;
@@ -1704,6 +1698,61 @@
     m_errorModel = nullptr;
 }
 
+void
+LrWpanPhy::SetRxSensitivity(double dbmSensitivity)
+{
+    NS_LOG_FUNCTION(this << dbmSensitivity << "dBm");
+
+    // See IEEE 802.15.4-2011 Sections 10.3.4, 11.3.4, 13.3.4, 13.3.4, 14.3.4, 15.3.4
+    if (m_phyOption == IEEE_802_15_4_915MHZ_BPSK || m_phyOption == IEEE_802_15_4_950MHZ_BPSK)
+    {
+        if (dbmSensitivity > -92)
+        {
+            NS_ABORT_MSG("The minimum Rx sensitivity for this band should be at least -92 dBm");
+        }
+    }
+    else
+    {
+        if (dbmSensitivity > -85)
+        {
+            NS_ABORT_MSG("The minimum Rx sensitivity for this band should be at least -85 dBm");
+        }
+    }
+
+    // Calculate the noise factor required to reduce the Rx sensitivity.
+    // The maximum possible sensitivity in the current modulation is used as a reference
+    // to calculate the noise factor (F). The noise factor is a dimensionless ratio.
+    // Currently only one PHY modulation is supported:
+    // O-QPSK 250kpps which has a Max Rx sensitivity: -106.58 dBm (Noise factor = 1).
+    // After Rx sensitivity is set, this becomes the new point where PER < 1 % for a
+    // PSDU of 20 bytes as described by the standard.
+
+    // TODO: recalculate maxRxSensitivity (Noise factor = 1) when additional modulations are
+    // supported.
+    double maxRxSensitivityW = DbmToW(-106.58);
+
+    LrWpanSpectrumValueHelper psdHelper;
+    m_txPsd = psdHelper.CreateTxPowerSpectralDensity(
+        GetNominalTxPowerFromPib(m_phyPIBAttributes.phyTransmitPower),
+        m_phyPIBAttributes.phyCurrentChannel);
+    // Update thermal noise + noise factor added.
+    long double noiseFactor = DbmToW(dbmSensitivity) / maxRxSensitivityW;
+    psdHelper.SetNoiseFactor(noiseFactor);
+    m_noise = psdHelper.CreateNoisePowerSpectralDensity(m_phyPIBAttributes.phyCurrentChannel);
+
+    m_signal = Create<LrWpanInterferenceHelper>(m_noise->GetSpectrumModel());
+    // Change receiver sensitivity from dBm to Watts
+    m_rxSensitivity = DbmToW(dbmSensitivity);
+}
+
+double
+LrWpanPhy::GetRxSensitivity()
+{
+    NS_LOG_FUNCTION(this);
+    // Change receiver sensitivity from Watt to dBm
+    return WToDbm(m_rxSensitivity);
+}
+
 LrWpanPhyOption
 LrWpanPhy::GetMyPhyOption()
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-phy.h ns-3.38/src/lr-wpan/model/lr-wpan-phy.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-phy.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-phy.h	2023-03-16 20:54:23.632868500 -0700
@@ -48,12 +48,12 @@
  *
  * Helper structure to manage the power measurement during ED.
  */
-typedef struct
+struct LrWpanEdPower
 {
     double averagePower;    //!< Average measured power
     Time lastUpdate;        //!< Last update time
     Time measurementLength; //!< Total measurement period
-} LrWpanEdPower;
+};
 
 /**
  * \ingroup lr-wpan
@@ -61,11 +61,11 @@
  * This data structure provides the Bit rate and Symbol rate for a given channel
  * See IEEE802.15.4-2006 Table 1 and 2 in section 6.1.1 and 6.1.2
  */
-typedef struct
+struct LrWpanPhyDataAndSymbolRates
 {
     double bitRate;    //!< bit rate
     double symbolRate; //!< symbol rate
-} LrWpanPhyDataAndSymbolRates;
+};
 
 /**
  * \ingroup lr-wpan
@@ -73,19 +73,19 @@
  * This data structure provides number of symbols for the PPDU headers: SHR and PHR
  * See IEEE802.15.4-2006 Figure 16, Table 19 and 20 in section 6.3
  */
-typedef struct
+struct LrWpanPhyPpduHeaderSymbolNumber
 {
     double shrPreamble; //!< Number of symbols for the SHR preamble
     double shrSfd;      //!< Number of symbols for the SHR SFD
     double phr;         //!< Number of symbols for the PHR
-} LrWpanPhyPpduHeaderSymbolNumber;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * This Phy option will be used to index various Tables in IEEE802.15.4-2011
  */
-typedef enum
+enum LrWpanPhyOption
 {
     IEEE_802_15_4_868MHZ_BPSK,
     IEEE_802_15_4_915MHZ_BPSK,
@@ -97,7 +97,7 @@
     IEEE_802_15_4_915MHZ_OQPSK,
     IEEE_802_15_4_2_4GHZ_OQPSK,
     IEEE_802_15_4_INVALID_PHY_OPTION
-} LrWpanPhyOption;
+};
 
 /**
  * \ingroup lr-wpan
@@ -105,7 +105,7 @@
  * IEEE802.15.4-2006 PHY Emumerations Table 18
  * in section 6.2.3
  */
-typedef enum
+enum LrWpanPhyEnumeration
 {
     IEEE_802_15_4_PHY_BUSY = 0x00,
     IEEE_802_15_4_PHY_BUSY_RX = 0x01,
@@ -120,7 +120,7 @@
     IEEE_802_15_4_PHY_UNSUPPORTED_ATTRIBUTE = 0xa,
     IEEE_802_15_4_PHY_READ_ONLY = 0xb,
     IEEE_802_15_4_PHY_UNSPECIFIED = 0xc // all cases not covered by ieee802.15.4
-} LrWpanPhyEnumeration;
+};
 
 namespace TracedValueCallback
 {
@@ -139,7 +139,7 @@
  *
  * IEEE802.15.4-2006 PHY PIB Attribute Identifiers Table 23 in section 6.4.2
  */
-typedef enum
+enum LrWpanPibAttributeIdentifier
 {
     phyCurrentChannel = 0x00,
     phyChannelsSupported = 0x01,
@@ -149,14 +149,14 @@
     phyMaxFrameDuration = 0x05,
     phySHRDuration = 0x06,
     phySymbolsPerOctet = 0x07
-} LrWpanPibAttributeIdentifier;
+};
 
 /**
  * \ingroup lr-wpan
  *
  * IEEE802.15.4-2006 PHY PIB Attributes Table 23 in section 6.4.2
  */
-typedef struct
+struct LrWpanPhyPibAttributes
 {
     uint8_t phyCurrentChannel;         //!< The RF channel to use
     uint32_t phyChannelsSupported[32]; //!< BitField representing the available channels supported
@@ -168,7 +168,7 @@
     uint32_t phyMaxFrameDuration; //!< The maximum number of symbols in a frame
     uint32_t phySHRDuration;      //!< The duration of the synchronization header (SHR) in symbols
     double phySymbolsPerOctet;    //!< The number of symbols per octet
-} LrWpanPhyPibAttributes;
+};
 
 /**
  * \ingroup lr-wpan
@@ -258,19 +258,6 @@
     static TypeId GetTypeId();
 
     /**
-     * The maximum packet size accepted by the PHY.
-     * See Table 22 in section 6.4.1 of IEEE 802.15.4-2006
-     */
-    static const uint32_t aMaxPhyPacketSize;
-
-    /**
-     * The turnaround time for switching the transceiver from RX to TX or vice
-     * versa.
-     * See Table 22 in section 6.4.1 of IEEE 802.15.4-2006
-     */
-    static const uint32_t aTurnaroundTime;
-
-    /**
      * Default constructor.
      */
     LrWpanPhy();
@@ -330,6 +317,26 @@
     void SetPhyOption(LrWpanPhyOption phyOption);
 
     /**
+     * Set the receiver power sensitivity used by this device in dBm.
+     *
+     * In ns-3 , rx sensitivity is only checked to be at least what is specified by
+     * the standard (-85dBm or -92dBm for BPSK bands). Most vendors provide better sensitivity
+     * options and exceed the minimum values proposed by the standard. Default sensitivity
+     * is -106.58 dBm (This does not include any noise figure).
+     * A sensitivity of -95dBm or less is considered by many vendors a high sensitivity.
+     *
+     * \param dbmSensitivity The receiver power sensitivity to set in dBm.
+     */
+    void SetRxSensitivity(double dbmSensitivity);
+
+    /**
+     * Get the receiver power sensitivity used by this device in dBm.
+     *
+     * \return The receiver power sensitivity used by this device in dBm.
+     */
+    double GetRxSensitivity();
+
+    /**
      * Notify the SpectrumPhy instance of an incoming waveform.
      *
      * @param params the SpectrumSignalParameters associated with the incoming waveform
@@ -542,21 +549,6 @@
                                         LrWpanPhyEnumeration oldState,
                                         LrWpanPhyEnumeration newState);
 
-  protected:
-    /**
-     * The data and symbol rates for the different PHY options.
-     * See Table 1 in section 6.1.1 IEEE 802.15.4-2006, IEEE 802.15.4c-2009, IEEE 802.15.4d-2009.
-     * Bit rate is in kbit/s.  Symbol rate is in ksymbol/s.
-     */
-    static const LrWpanPhyDataAndSymbolRates dataSymbolRates[IEEE_802_15_4_INVALID_PHY_OPTION];
-    /**
-     * The preamble, SFD, and PHR lengths in symbols for the different PHY options.
-     * See Table 19 and Table 20 in section 6.3 IEEE 802.15.4-2006, IEEE 802.15.4c-2009, IEEE
-     * 802.15.4d-2009.
-     */
-    static const LrWpanPhyPpduHeaderSymbolNumber
-        ppduHeaderSymbolNumbers[IEEE_802_15_4_INVALID_PHY_OPTION];
-
   private:
     /**
      * The second is true if the first is flagged as error/invalid.
@@ -751,10 +743,10 @@
     int8_t GetNominalTxPowerFromPib(uint8_t phyTransmitPower);
 
     /**
-     * Transform watts (W) to decibels milliwatts (DBm).
+     * Transform watts (W) to decibels milliwatts (dBm).
      *
-     * \param watt The Watts that will be converted to DBm.
-     * \return The value of Watts in DBm.
+     * \param watt The Watts that will be converted to dBm.
+     * \return The value of Watts in dBm.
      */
     double WToDbm(double watt);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-spectrum-value-helper.cc ns-3.38/src/lr-wpan/model/lr-wpan-spectrum-value-helper.cc
--- ns-3.37/src/lr-wpan/model/lr-wpan-spectrum-value-helper.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-spectrum-value-helper.cc	2023-03-16 20:54:23.632868500 -0700
@@ -126,6 +126,12 @@
     return noisePsd;
 }
 
+void
+LrWpanSpectrumValueHelper::SetNoiseFactor(double f)
+{
+    m_noiseFactor = f;
+}
+
 double
 LrWpanSpectrumValueHelper::TotalAvgPower(Ptr<const SpectrumValue> psd, uint32_t channel)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/model/lr-wpan-spectrum-value-helper.h ns-3.38/src/lr-wpan/model/lr-wpan-spectrum-value-helper.h
--- ns-3.37/src/lr-wpan/model/lr-wpan-spectrum-value-helper.h	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/model/lr-wpan-spectrum-value-helper.h	2023-03-16 20:54:23.632868500 -0700
@@ -53,6 +53,12 @@
     Ptr<SpectrumValue> CreateNoisePowerSpectralDensity(uint32_t channel);
 
     /**
+     * Set the noise factor added to the thermal noise.
+     * \param f A dimensionless ratio (i.e. Not in dB)
+     */
+    void SetNoiseFactor(double f);
+
+    /**
      * \brief total average power of the signal is the integral of the PSD using
      * the limits of the given channel
      * \param psd spectral density
@@ -64,6 +70,8 @@
   private:
     /**
      * A scaling factor for the noise power.
+     * It specifies how much additional noise the device
+     * contribute to the thermal noise (floor noise).
      */
     double m_noiseFactor;
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/test/lr-wpan-ack-test.cc ns-3.38/src/lr-wpan/test/lr-wpan-ack-test.cc
--- ns-3.37/src/lr-wpan/test/lr-wpan-ack-test.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/test/lr-wpan-ack-test.cc	2023-03-16 20:54:23.632868500 -0700
@@ -57,13 +57,13 @@
     /**
      * Test modes
      */
-    typedef enum
+    enum TestMode_e
     {
         EXTENDED_ADDRESS_UNICAST, //!< extended addresses
         SHORT_ADDRESS_UNICAST,    //!< short addresses, unicast
         SHORT_ADDRESS_MULTICAST,  //!< short addresses, multicast
         SHORT_ADDRESS_BROADCAST,  //!< short addresses, broadcast
-    } TestMode_e;
+    };
 
     /**
      * Create test case
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lr-wpan/test/lr-wpan-mac-test.cc ns-3.38/src/lr-wpan/test/lr-wpan-mac-test.cc
--- ns-3.37/src/lr-wpan/test/lr-wpan-mac-test.cc	2022-11-01 17:37:24.515395200 -0700
+++ ns-3.38/src/lr-wpan/test/lr-wpan-mac-test.cc	2023-03-16 20:54:23.632868500 -0700
@@ -308,11 +308,19 @@
      * \param params MLME scan confirm parameters
      */
     void ScanConfirm(MlmeScanConfirmParams params);
+    /**
+     * Function used to notify the reception of a beacon with payload.
+     *
+     * \param params The MLME-BEACON-NOTIFY.indication parameters
+     */
+    void BeaconNotifyIndication(MlmeBeaconNotifyIndicationParams params);
 
     void DoRun() override;
 
-    std::vector<PanDescriptor>
-        m_panDescriptorList; //!< The list of PAN descriptors accumulated during the scan
+    std::vector<PanDescriptor> m_panDescriptorList; //!< The list of PAN descriptors
+                                                    //!< accumulated during the scan.
+    uint32_t g_beaconPayloadSize;                   //!< The size of the beacon payload received
+                                                    //!< from a coordinator.
 };
 
 TestActiveScanPanDescriptors::TestActiveScanPanDescriptors()
@@ -334,12 +342,18 @@
 }
 
 void
+TestActiveScanPanDescriptors::BeaconNotifyIndication(MlmeBeaconNotifyIndicationParams params)
+{
+    g_beaconPayloadSize = params.m_sdu->GetSize();
+}
+
+void
 TestActiveScanPanDescriptors::DoRun()
 {
     /*
-     *      [00:01]                   [00:02]                                   [00:03]
-     *  PAN Coordinator 1 (PAN: 5)       End Device                        PAN Coordinator 2 (PAN:
-     * 7)
+     *      [00:01]                       [00:02]                             [00:03]
+     *  PAN Coordinator 1 (PAN: 5)       End Device                  PAN Coordinator 2 (PAN: 7)
+     *
      *       |--------100 m----------------|----------106 m -----------------------|
      *  Channel 12               (Active Scan channels 11-14)                 Channel 14
      *
@@ -352,7 +366,7 @@
      * the scanned channels). On reception of such commands, coordinators reply with a single beacon
      * which contains a PAN descriptor. The test makes sure that the PAN descriptors are received (2
      * PAN descriptors) and because both PAN coordinators are set to a different distance from the
-     * end device, their LQI values should be bellow 255 but above 0. Likewise, Coordinator 2 LQI
+     * end device, their LQI values should be below 255 but above 0. Likewise, Coordinator 2 LQI
      * value should be less than Coordinator 1 LQI value. The exact expected value of LQI is not
      * tested, this is dependable on the LQI implementation.
      */
@@ -408,6 +422,10 @@
     cb0 = MakeCallback(&TestActiveScanPanDescriptors::ScanConfirm, this);
     endNodeNetDevice->GetMac()->SetMlmeScanConfirmCallback(cb0);
 
+    MlmeBeaconNotifyIndicationCallback cb1;
+    cb1 = MakeCallback(&TestActiveScanPanDescriptors::BeaconNotifyIndication, this);
+    endNodeNetDevice->GetMac()->SetMlmeBeaconNotifyIndicationCallback(cb1);
+
     /////////////////
     // ACTIVE SCAN //
     /////////////////
@@ -427,7 +445,13 @@
                                    params);
 
     // PAN coordinator N2 (PAN 7) is set to channel 14 in non-beacon mode but answer to beacon
-    // requests.
+    // requests. The second coordinator includes a beacon payload of 25 bytes using the
+    // MLME-SET.request primitive.
+    Ptr<LrWpanMacPibAttributes> pibAttribute = Create<LrWpanMacPibAttributes>();
+    pibAttribute->macBeaconPayload = Create<Packet>(25);
+    coord2NetDevice->GetMac()->MlmeSetRequest(LrWpanMacPibAttributeIdentifier::macBeaconPayload,
+                                              pibAttribute);
+
     MlmeStartRequestParams params2;
     params2.m_panCoor = true;
     params2.m_PanId = 7;
@@ -482,6 +506,10 @@
         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();
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/doc/source/lte-design.rst ns-3.38/src/lte/doc/source/lte-design.rst
--- ns-3.37/src/lte/doc/source/lte-design.rst	2022-11-01 17:37:24.543395300 -0700
+++ ns-3.38/src/lte/doc/source/lte-design.rst	2023-03-16 20:54:23.660868600 -0700
@@ -511,7 +511,7 @@
 
 To switch between this two CQI generation approaches, ``LteHelper::UsePdschForCqiGeneration`` needs to be configured: false for first approach and true for second approach (true is default value)::
 
-   Config::SetDefault ("ns3::LteHelper::UsePdschForCqiGeneration", BooleanValue (true));
+   Config::SetDefault("ns3::LteHelper::UsePdschForCqiGeneration", BooleanValue(true));
 
 In uplink, two types of CQIs are implemented:
 
@@ -683,7 +683,7 @@
 
 The model can be disabled for working with a zero-losses channel by setting the ``DataErrorModelEnabled`` attribute of the ``LteSpectrumPhy`` class (by default is active). This can be done according to the standard ns3 attribute system procedure, that is::
 
-  Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false));
+  Config::SetDefault("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue(false));
 
 .. _sec-control-channles-phy-error-model:
 
@@ -707,7 +707,7 @@
 
 The use of multiple antennas both at transmitter and receiver side, known as multiple-input and multiple-output (MIMO), is a problem well studied in literature during the past years. Most of the work concentrate on evaluating analytically the gain that the different MIMO schemes might have in term of capacity; however someones provide also information of the gain in terms of received power [CatreuxMIMO]_.
 
-According to the considerations above, a model more flexible can be obtained considering the gain that MIMO schemes bring in the system from a statistical point of view. As highlighted before, [CatreuxMIMO]_ presents the statistical gain of several MIMO solutions respect to the SISO one in case of no correlation between the antennas. In the work the gain is presented as the cumulative distribution function (CDF) of the output SINR for what concern SISO, MIMO-Alamouti, MIMO-MMSE, MIMO-OSIC-MMSE and MIMO-ZF schemes. Elaborating the results, the output SINR distribution can be approximated with a log-normal one with different mean and variance as function of the scheme considered. However, the variances are not so different and they are approximatively equal to the one of the SISO mode already included in the shadowing component of the ``BuildingsPropagationLossModel``, in detail:
+According to the considerations above, a model more flexible can be obtained considering the gain that MIMO schemes bring in the system from a statistical point of view. As highlighted before, [CatreuxMIMO]_ presents the statistical gain of several MIMO solutions respect to the SISO one in case of no correlation between the antennas. In the work the gain is presented as the cumulative distribution function (CDF) of the output SINR for what concern SISO, MIMO-Alamouti, MIMO-MMSE, MIMO-OSIC-MMSE and MIMO-ZF schemes. Elaborating the results, the output SINR distribution can be approximated with a log-normal one with different mean and variance as function of the scheme considered. However, the variances are not so different and they are approximately equal to the one of the SISO mode already included in the shadowing component of the ``BuildingsPropagationLossModel``, in detail:
 
  * SISO: :math:`\mu = 13.5` and :math:`\sigma = 20` [dB].
  * MIMO-Alamouti: :math:`\mu = 17.7` and :math:`\sigma = 11.1` [dB].
@@ -1000,7 +1000,7 @@
 
 For what concern the HARQ, RR implements the non adaptive version, which implies that in allocating the retransmission attempts RR uses the same allocation configuration of the original block, which means maintaining the same RBGs and MCS. UEs that are allocated for HARQ retransmissions are not considered for the transmission of new data in case they have a transmission opportunity available in the same TTI. Finally, HARQ can be disabled with ns3 attribute system for maintaining backward compatibility with old test cases and code, in detail::
 
-   Config::SetDefault ("ns3::RrFfMacScheduler::HarqEnabled", BooleanValue (false));
+   Config::SetDefault("ns3::RrFfMacScheduler::HarqEnabled", BooleanValue(false));
 
 The scheduler implements the filtering of the uplink CQIs according to their nature with ``UlCqiFilter`` attribute, in detail:
 
@@ -1037,7 +1037,7 @@
    \widehat{i}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}}
     \left( \frac{ R_{j}(k,t) }{ T_\mathrm{j}(t) } \right)
 
-where :math:`T_{j}(t)` is the past througput performance perceived by the
+where :math:`T_{j}(t)` is the past throughput performance perceived by the
 user :math:`j`.
 According to the above scheduling algorithm, a user can be allocated to
 different RBGs, which can be either adjacent or not, depending on the current
@@ -1079,7 +1079,7 @@
 
 For what concern the HARQ, PF implements the non adaptive version, which implies that in allocating the retransmission attempts the scheduler uses the same allocation configuration of the original block, which means maintaining the same RBGs and MCS. UEs that are allocated for HARQ retransmissions are not considered for the transmission of new data in case they have a transmission opportunity available in the same TTI. Finally, HARQ can be disabled with ns3 attribute system for maintaining backward compatibility with old test cases and code, in detail::
 
-   Config::SetDefault ("ns3::PfFfMacScheduler::HarqEnabled", BooleanValue (false));
+   Config::SetDefault("ns3::PfFfMacScheduler::HarqEnabled", BooleanValue(false));
 
 
 
@@ -1256,7 +1256,7 @@
 
 ::
 
-  Config::SetDefault ("ns3::PfFfMacScheduler::HarqEnabled", BooleanValue (false));
+  Config::SetDefault("ns3::PfFfMacScheduler::HarqEnabled", BooleanValue(false));
 
 The scheduler implements the filtering of the uplink CQIs according to their nature with ``UlCqiFilter`` attribute, in detail:
 
@@ -2269,7 +2269,7 @@
 :ref:`sec-control-channles-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 unsuccessfully decoded frames is reset to zero. At the
+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
@@ -3371,7 +3371,7 @@
 the outmost IP header matches the address of the point-to-point net
 device). The local delivery process will forward the packet to the
 EpcSgwApplication via the corresponding UDP socket. The
-EpcSgwApplication then perfoms the following operations:
+EpcSgwApplication then performs the following operations:
 
  #. it removes the GTP header and retrieves the S1-U TEID;
  #. it maps the S1-U TEID to get the S5 TEID to which the packet
@@ -3604,7 +3604,7 @@
 
   * the ``EpcX2SapProvider`` part is provided by the X2 entity and used by the RRC entity and
 
-  * the ``EpcX2SapUser`` part is provided by the RRC entity and used by the RRC enity.
+  * the ``EpcX2SapUser`` part is provided by the RRC entity and used by the RRC entity.
 
 The primitives that are supported in our X2-C model are described in the following subsections.
 
@@ -3646,7 +3646,7 @@
 
   std::vector<EpcX2Sap::UlInterferenceOverloadIndicationItem>
     m_currentUlInterferenceOverloadIndicationList;
-  std::vector <EpcX2Sap::UlHighInterferenceInformationItem>
+  std::vector<EpcX2Sap::UlHighInterferenceInformationItem>
     m_currentUlHighInterferenceInformationList;
   EpcX2Sap::RelativeNarrowbandTxBand m_currentRelativeNarrowbandTxBand;
 
@@ -3667,8 +3667,8 @@
 
   EpcX2Sap::LoadInformationParams params;
   params.targetCellId = cellId;
-  params.cellInformationList.push_back (cii);
-  m_x2SapProvider->SendLoadInformation (params);
+  params.cellInformationList.push_back(cii);
+  m_x2SapProvider->SendLoadInformation(params);
 
 
 The above code allows the source eNB to send the message. The method
@@ -3691,8 +3691,8 @@
 
   EpcX2Sap::ResourceStatusUpdateParams params;
   params.targetCellId = cellId;
-  params.cellMeasurementResultList.push_back (m_cmri);
-  m_x2SapProvider->SendResourceStatusUpdate (params);
+  params.cellMeasurementResultList.push_back(m_cmri);
+  m_x2SapProvider->SendResourceStatusUpdate(params);
 
 
 The method ``eEnbRrc::DoRecvResourceStatusUpdate`` will be called when
@@ -3805,7 +3805,7 @@
 
 Uplink Power Control is enabled by default, and can be disabled by attribute system::
 
-   Config::SetDefault ("ns3::LteUePhy::EnableUplinkPowerControl", BooleanValue (false));
+   Config::SetDefault("ns3::LteUePhy::EnableUplinkPowerControl", BooleanValue(false));
 
 
 Two Uplink Power Control mechanisms are implemented:
@@ -3818,7 +3818,7 @@
 
 To switch between these two mechanism types, one should change parameter::
 
-  Config::SetDefault ("ns3::LteUePowerControl::ClosedLoop", BooleanValue (true));
+  Config::SetDefault("ns3::LteUePowerControl::ClosedLoop", BooleanValue(true));
 
 By default, Closed Loop Power Control is enabled.
 
@@ -3829,7 +3829,7 @@
 
 To switch between these two modes, one should change parameter::
 
-  Config::SetDefault ("ns3::LteUePowerControl::AccumulationEnabled", BooleanValue (true));
+  Config::SetDefault("ns3::LteUePowerControl::AccumulationEnabled", BooleanValue(true));
 
 By default, Accumulation Mode is enabled and TPC commands in DL-DCI are set by all schedulers to 1,
 what is mapped to value of 0 in Accumulation Mode.
@@ -3894,15 +3894,15 @@
    layers for :math:`j={0,1}` for serving cell :math:`c`. SIB2 message needs to be extended to carry these two
    components, but currently they can be set via attribute system::
 
-      Config::SetDefault ("ns3::LteUePowerControl::PoNominalPusch", IntegerValue (-90));
-      Config::SetDefault ("ns3::LteUePowerControl::PoUePusch", IntegerValue (7));
+      Config::SetDefault("ns3::LteUePowerControl::PoNominalPusch", IntegerValue(-90));
+      Config::SetDefault("ns3::LteUePowerControl::PoUePusch", IntegerValue(7));
 
  * :math:`\alpha_{c} (j)` is a 3-bit parameter provided by higher layers for serving cell :math:`c`.
    For :math:`j=0,1`,   :math:`\alpha_c \in \left \{ 0, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1 \right \}`
    For :math:`j=2`,   :math:`\alpha_{c} (j) = 1`.
    This parameter is configurable by attribute system::
 
-      Config::SetDefault ("ns3::LteUePowerControl::Alpha", DoubleValue (0.8));
+      Config::SetDefault("ns3::LteUePowerControl::Alpha", DoubleValue(0.8));
 
  * :math:`PL_{c}` is the downlink pathloss estimate calculated in the UE for serving cell :math:`c` in dB
    and :math:`PL_{c} = referenceSignalPower – higher layer filtered RSRP`, where :math:`referenceSignalPower`
@@ -3979,7 +3979,7 @@
 
    This parameter is configurable by attribute system::
 
-      Config::SetDefault ("ns3::LteUePowerControl::PsrsOffset", IntegerValue (7));
+      Config::SetDefault("ns3::LteUePowerControl::PsrsOffset", IntegerValue(7));
 
  * :math:`M_{SRS,c}` is the bandwidth of the SRS transmission in subframe :math:`i` for serving
    cell :math:`c` expressed in number of resource blocks. In current implementation SRS is sent
@@ -4252,7 +4252,7 @@
 This Distributed Fractional Frequency Reuse Algorithm was presented in [DKimura2012]_. It
 automatically optimizes cell-edge sub-bands by focusing on user distribution (in particular,
 receive-power distribution). This algorithm adaptively selects RBs for cell-edge sub-band on
-basis of coordination information from adjecent cells and notifies the base stations of the
+basis of coordination information from adjacent cells and notifies the base stations of the
 adjacent cells, which RBs it selected to use in edge sub-band. The base station of each cell
 uses the received information and the following equation to compute cell-edge-band metric
 :math:`A_{k}` for each RB.
@@ -4742,4 +4742,3 @@
    :align: center
 
    Sequence diagram of the interaction between ``LteHelper`` and ``EpcHelper``.
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/doc/source/lte-references.rst ns-3.38/src/lte/doc/source/lte-references.rst
--- ns-3.37/src/lte/doc/source/lte-references.rst	2022-11-01 17:37:24.543395300 -0700
+++ ns-3.38/src/lte/doc/source/lte-references.rst	2023-03-16 20:54:23.660868600 -0700
@@ -89,7 +89,7 @@
    <http://mailman.isi.edu/pipermail/ns-developers/2011-November/009397.html>`_
 
 .. [ViennaLteSim] `"The Vienna LTE Simulators"
-   <https://www.nt.tuwien.ac.at/research/mobile-communications/vccs/vienna-lte-a-simulators/>`_
+   <https://www.tuwien.at/etit/tc/en/vienna-simulators/vienna-lte-a-simulators/lte-a-downlink-system-level-simulator/>`_
 
 .. [LozanoCost] Joan Olmos, Silvia Ruiz, Mario García-Lozano and David Martín-Sacristán,
    "Link Abstraction Models Based on Mutual Information for LTE Downlink",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/doc/source/lte-user.rst ns-3.38/src/lte/doc/source/lte-user.rst
--- ns-3.37/src/lte/doc/source/lte-user.rst	2022-11-01 17:37:24.543395300 -0700
+++ ns-3.38/src/lte/doc/source/lte-user.rst	2023-03-16 20:54:23.660868600 -0700
@@ -57,14 +57,14 @@
 
     using namespace ns3;
 
-    int main (int argc, char *argv[])
+    int main(int argc, char *argv[])
     {
       // the rest of the simulation program follows
 
 
 #. Create an ``LteHelper`` object::
 
-      Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
+      Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
 
    This will instantiate some common
    objects (e.g., the Channel object) and provide the methods to add
@@ -73,9 +73,9 @@
 #. Create ``Node`` objects for the eNB(s) and the UEs::
 
       NodeContainer enbNodes;
-      enbNodes.Create (1);
+      enbNodes.Create(1);
       NodeContainer ueNodes;
-      ueNodes.Create (2);
+      ueNodes.Create(2);
 
    Note that the above Node instances at this point still don't have
    an LTE protocol stack installed; they're just empty nodes.
@@ -83,10 +83,10 @@
 #. Configure the Mobility model for all the nodes::
 
       MobilityHelper mobility;
-      mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
-      mobility.Install (enbNodes);
-      mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
-      mobility.Install (ueNodes);
+      mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+      mobility.Install(enbNodes);
+      mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+      mobility.Install(ueNodes);
 
    The above will place all nodes at the coordinates (0,0,0). Please
    refer to the documentation of the ns-3 mobility model for how to
@@ -95,31 +95,31 @@
 #. Install an LTE protocol stack on the eNB(s)::
 
       NetDeviceContainer enbDevs;
-      enbDevs = lteHelper->InstallEnbDevice (enbNodes);
+      enbDevs = lteHelper->InstallEnbDevice(enbNodes);
 
 #. Install an LTE protocol stack on the UEs::
 
       NetDeviceContainer ueDevs;
-      ueDevs = lteHelper->InstallUeDevice (ueNodes);
+      ueDevs = lteHelper->InstallUeDevice(ueNodes);
 
 
 #. Attach the UEs to an eNB. This will configure each UE according to
    the eNB configuration, and create an RRC connection between them::
 
-      lteHelper->Attach (ueDevs, enbDevs.Get (0));
+      lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
 #. Activate a data radio bearer between each UE and the eNB it is attached to::
 
       enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
-      EpsBearer bearer (q);
-      lteHelper->ActivateDataRadioBearer (ueDevs, bearer);
+      EpsBearer bearer(q);
+      lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
    this method will also activate two saturation traffic generators for
    that bearer, one in uplink and one in downlink.
 
 #. Set the stop time::
 
-       Simulator::Stop (Seconds (0.005));
+       Simulator::Stop(Seconds(0.005));
 
    This is needed otherwise the simulation will last forever, because
    (among others) the start-of-subframe event is scheduled repeatedly, and the
@@ -127,12 +127,11 @@
 
 #. Run the simulation::
 
-       Simulator::Run ();
+       Simulator::Run();
 
 #. Cleanup and exit::
 
-
-       Simulator::Destroy ();
+       Simulator::Destroy();
        return 0;
        }
 
@@ -152,12 +151,12 @@
 First of all, you need to put the following in your simulation
 program, right after ``main ()`` starts::
 
-      CommandLine cmd (__FILE__);
-      cmd.Parse (argc, argv);
+      CommandLine cmd(__FILE__);
+      cmd.Parse(argc, argv);
       ConfigStore inputConfig;
-      inputConfig.ConfigureDefaults ();
+      inputConfig.ConfigureDefaults();
       // parse again so you can override default values from the command line
-      cmd.Parse (argc, argv);
+      cmd.Parse(argc, argv);
 
 for the above to work, make sure you also ``#include "ns3/config-store.h"``.
 Now create a text file named (for example) ``input-defaults.txt``
@@ -199,29 +198,29 @@
 
 There are several types of LTE MAC scheduler user can choose here. User can use following codes to define scheduler type::
 
- Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
- lteHelper->SetSchedulerType ("ns3::FdMtFfMacScheduler");    // FD-MT scheduler
- lteHelper->SetSchedulerType ("ns3::TdMtFfMacScheduler");    // TD-MT scheduler
- lteHelper->SetSchedulerType ("ns3::TtaFfMacScheduler");     // TTA scheduler
- lteHelper->SetSchedulerType ("ns3::FdBetFfMacScheduler");   // FD-BET scheduler
- lteHelper->SetSchedulerType ("ns3::TdBetFfMacScheduler");   // TD-BET scheduler
- lteHelper->SetSchedulerType ("ns3::FdTbfqFfMacScheduler");  // FD-TBFQ scheduler
- lteHelper->SetSchedulerType ("ns3::TdTbfqFfMacScheduler");  // TD-TBFQ scheduler
- lteHelper->SetSchedulerType ("ns3::PssFfMacScheduler");     //PSS scheduler
+ Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
+ lteHelper->SetSchedulerType("ns3::FdMtFfMacScheduler");    // FD-MT scheduler
+ lteHelper->SetSchedulerType("ns3::TdMtFfMacScheduler");    // TD-MT scheduler
+ lteHelper->SetSchedulerType("ns3::TtaFfMacScheduler");     // TTA scheduler
+ lteHelper->SetSchedulerType("ns3::FdBetFfMacScheduler");   // FD-BET scheduler
+ lteHelper->SetSchedulerType("ns3::TdBetFfMacScheduler");   // TD-BET scheduler
+ lteHelper->SetSchedulerType("ns3::FdTbfqFfMacScheduler");  // FD-TBFQ scheduler
+ lteHelper->SetSchedulerType("ns3::TdTbfqFfMacScheduler");  // TD-TBFQ scheduler
+ lteHelper->SetSchedulerType("ns3::PssFfMacScheduler");     //PSS scheduler
 
 TBFQ and PSS have more parameters than other schedulers. Users can define those parameters in following way::
 
  * TBFQ scheduler::
 
-  Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
-  lteHelper->SetSchedulerAttribute("DebtLimit", IntegerValue(yourvalue)); // default value -625000 bytes (-5Mb)
-  lteHelper->SetSchedulerAttribute("CreditLimit", UintegerValue(yourvalue)); // default value 625000 bytes (5Mb)
+  Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
+  lteHelper->SetSchedulerAttribute("DebtLimit", IntegerValue(yourvalue)); // default value -625000 bytes(-5Mb)
+  lteHelper->SetSchedulerAttribute("CreditLimit", UintegerValue(yourvalue)); // default value 625000 bytes(5Mb)
   lteHelper->SetSchedulerAttribute("TokenPoolSize", UintegerValue(yourvalue)); // default value 1 byte
   lteHelper->SetSchedulerAttribute("CreditableThreshold", UintegerValue(yourvalue)); // default value 0
 
  * PSS scheduler::
 
-  Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
+  Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
   lteHelper->SetSchedulerAttribute("nMux", UIntegerValue(yourvalue)); // the maximum number of UE selected by TD scheduler
   lteHelper->SetSchedulerAttribute("PssFdSchedulerType", StringValue("CoItA")); // PF scheduler type in PSS
 
@@ -232,15 +231,15 @@
 In addition, token generation rate in TBFQ and target bit rate in PSS need to be configured by Guarantee Bit Rate (GBR) or
 Maximum Bit Rate (MBR) in epc bearer QoS parameters. Users can use following codes to define GBR and MBR in both downlink and uplink::
 
-  Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
+  Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
   enum EpsBearer::Qci q = EpsBearer::yourvalue;  // define Qci type
   GbrQosInformation qos;
   qos.gbrDl = yourvalue; // Downlink GBR
   qos.gbrUl = yourvalue; // Uplink GBR
   qos.mbrDl = yourvalue; // Downlink MBR
   qos.mbrUl = yourvalue; // Uplink MBR
-  EpsBearer bearer (q, qos);
-  lteHelper->ActivateDedicatedEpsBearer (ueDevs, bearer, EpcTft::Default ());
+  EpsBearer bearer(q, qos);
+  lteHelper->ActivateDedicatedEpsBearer(ueDevs, bearer, EpcTft::Default());
 
 In PSS, TBR is obtained from GBR in bearer level QoS parameters. In TBFQ, token generation rate is obtained from the MBR
 setting in bearer level QoS parameters, which therefore needs to be configured consistently.
@@ -280,16 +279,16 @@
 and PDCP level Key Performance Indicators (KPIs). You can enable it in
 the following way::
 
-      Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
+      Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
 
       // configure all the simulation scenario here...
 
-      lteHelper->EnablePhyTraces ();
-      lteHelper->EnableMacTraces ();
-      lteHelper->EnableRlcTraces ();
-      lteHelper->EnablePdcpTraces ();
+      lteHelper->EnablePhyTraces();
+      lteHelper->EnableMacTraces();
+      lteHelper->EnableRlcTraces();
+      lteHelper->EnablePdcpTraces();
 
-      Simulator::Run ();
+      Simulator::Run();
 
 
 RLC and PDCP KPIs are calculated over a time interval and stored on ASCII
@@ -476,16 +475,16 @@
 
 In order to activate the fading module (which is not active by default) the following code should be included in the simulation program::
 
-  Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
+  Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
   lteHelper->SetFadingModel("ns3::TraceFadingLossModel");
 
 And for setting the parameters::
 
-  lteHelper->SetFadingModelAttribute ("TraceFilename", StringValue ("src/lte/model/fading-traces/fading_trace_EPA_3kmph.fad"));
-  lteHelper->SetFadingModelAttribute ("TraceLength", TimeValue (Seconds (10.0)));
-  lteHelper->SetFadingModelAttribute ("SamplesNum", UintegerValue (10000));
-  lteHelper->SetFadingModelAttribute ("WindowSize", TimeValue (Seconds (0.5)));
-  lteHelper->SetFadingModelAttribute ("RbNum", UintegerValue (100));
+  lteHelper->SetFadingModelAttribute("TraceFilename", StringValue("src/lte/model/fading-traces/fading_trace_EPA_3kmph.fad"));
+  lteHelper->SetFadingModelAttribute("TraceLength", TimeValue(Seconds(10.0)));
+  lteHelper->SetFadingModelAttribute("SamplesNum", UintegerValue(10000));
+  lteHelper->SetFadingModelAttribute("WindowSize", TimeValue(Seconds(0.5)));
+  lteHelper->SetFadingModelAttribute("RbNum", UintegerValue(100));
 
 It has to be noted that, ``TraceFilename`` does not have a default value, therefore is has to be always set explicitly.
 
@@ -533,23 +532,23 @@
 
 #. Pathloss model selection::
 
-    Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
+    Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
 
-    lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::BuildingsPropagationLossModel"));
+    lteHelper->SetAttribute("PathlossModel", StringValue("ns3::BuildingsPropagationLossModel"));
 
 #. EUTRA Band Selection
 
 The selection of the working frequency of the propagation model has to be done with the standard ns-3 attribute system as described in the correspond section ("Configuration of LTE model parameters") by means of the DlEarfcn and UlEarfcn parameters, for instance::
 
-   lteHelper->SetEnbDeviceAttribute ("DlEarfcn", UintegerValue (100));
-   lteHelper->SetEnbDeviceAttribute ("UlEarfcn", UintegerValue (18100));
+   lteHelper->SetEnbDeviceAttribute("DlEarfcn", UintegerValue(100));
+   lteHelper->SetEnbDeviceAttribute("UlEarfcn", UintegerValue(18100));
 
 It is to be noted that using other means to configure the frequency used by the propagation model (i.e., configuring the corresponding BuildingsPropagationLossModel attributes directly) might generates conflicts in the frequencies definition in the modules during the simulation, and is therefore not advised.
 
 #. Mobility model selection::
 
     MobilityHelper mobility;
-    mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
+    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
 
     It is to be noted that any mobility model can be used.
 
@@ -561,31 +560,31 @@
     double y_max = 20.0;
     double z_min = 0.0;
     double z_max = 10.0;
-    Ptr<Building> b = CreateObject <Building> ();
-    b->SetBoundaries (Box (x_min, x_max, y_min, y_max, z_min, z_max));
-    b->SetBuildingType (Building::Residential);
-    b->SetExtWallsType (Building::ConcreteWithWindows);
-    b->SetNFloors (3);
-    b->SetNRoomsX (3);
-    b->SetNRoomsY (2);
+    Ptr<Building> b = CreateObject<Building>();
+    b->SetBoundaries(Box(x_min, x_max, y_min, y_max, z_min, z_max));
+    b->SetBuildingType(Building::Residential);
+    b->SetExtWallsType(Building::ConcreteWithWindows);
+    b->SetNFloors(3);
+    b->SetNRoomsX(3);
+    b->SetNRoomsY(2);
 
    This will instantiate a residential building with base of 10 x 20 meters and height of 10 meters whose external walls are of concrete with windows; the building has three floors and has an internal 3 x 2  grid of rooms of equal size.
 
 #. Node creation and positioning::
 
-    ueNodes.Create (2);
-    mobility.Install (ueNodes);
-    BuildingsHelper::Install (ueNodes);
+    ueNodes.Create(2);
+    mobility.Install(ueNodes);
+    BuildingsHelper::Install(ueNodes);
     NetDeviceContainer ueDevs;
-    ueDevs = lteHelper->InstallUeDevice (ueNodes);
-    Ptr<ConstantPositionMobilityModel> mm0 = enbNodes.Get (0)->GetObject<ConstantPositionMobilityModel> ();
-    Ptr<ConstantPositionMobilityModel> mm1 = enbNodes.Get (1)->GetObject<ConstantPositionMobilityModel> ();
-    mm0->SetPosition (Vector (5.0, 5.0, 1.5));
-    mm1->SetPosition (Vector (30.0, 40.0, 1.5));
+    ueDevs = lteHelper->InstallUeDevice(ueNodes);
+    Ptr<ConstantPositionMobilityModel> mm0 = enbNodes.Get(0)->GetObject<ConstantPositionMobilityModel>();
+    Ptr<ConstantPositionMobilityModel> mm1 = enbNodes.Get(1)->GetObject<ConstantPositionMobilityModel>();
+    mm0->SetPosition(Vector(5.0, 5.0, 1.5));
+    mm1->SetPosition(Vector(30.0, 40.0, 1.5));
 
 #. Finalize the building and mobility model configuration::
 
-    BuildingsHelper::MakeMobilityModelConsistent ();
+    BuildingsHelper::MakeMobilityModelConsistent();
 
 See the documentation of the *buildings* module for more detailed information.
 
@@ -595,8 +594,8 @@
 
 The Physical error model consists of the data error model and the downlink control error model, both of them active by default. It is possible to deactivate them with the ns3 attribute system, in detail::
 
-  Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false));
-  Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false));
+  Config::SetDefault("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue(false));
+  Config::SetDefault("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue(false));
 
 
 
@@ -616,21 +615,21 @@
 
 According to model implemented, the simulator includes the first three transmission modes types. The default one is the Transmission Mode 1 (SISO). In order to change the default Transmission Mode to be used, the attribute ``DefaultTransmissionMode`` of the ``LteEnbRrc`` can be used, as shown in the following::
 
-  Config::SetDefault ("ns3::LteEnbRrc::DefaultTransmissionMode", UintegerValue (0)); // SISO
-  Config::SetDefault ("ns3::LteEnbRrc::DefaultTransmissionMode", UintegerValue (1)); // MIMO Tx diversity (1 layer)
-  Config::SetDefault ("ns3::LteEnbRrc::DefaultTransmissionMode", UintegerValue (2)); // MIMO Spatial Multiplexity (2 layers)
+  Config::SetDefault("ns3::LteEnbRrc::DefaultTransmissionMode", UintegerValue(0)); // SISO
+  Config::SetDefault("ns3::LteEnbRrc::DefaultTransmissionMode", UintegerValue(1)); // MIMO Tx diversity(1 layer)
+  Config::SetDefault("ns3::LteEnbRrc::DefaultTransmissionMode", UintegerValue(2)); // MIMO Spatial Multiplexity(2 layers)
 
 For changing the transmission mode of a certain user during the simulation a specific interface has been implemented in both standard schedulers::
 
-  void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode);
+  void TransmissionModeConfigurationUpdate(uint16_t rnti, uint8_t txMode);
 
 This method can be used both for developing transmission mode decision engine (i.e., for optimizing the transmission mode according to channel condition and/or user's requirements) and for manual switching from simulation script. In the latter case, the switching can be done as shown in the following::
 
-  Ptr<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbNetDevice> lteEnbDev = enbDevs.Get(0)->GetObject<LteEnbNetDevice>();
   PointerValue ptrval;
-  enbNetDev->GetAttribute ("FfMacScheduler", ptrval);
-  Ptr<RrFfMacScheduler> rrsched = ptrval.Get<RrFfMacScheduler> ();
-  Simulator::Schedule (Seconds (0.2), &RrFfMacScheduler::TransmissionModeConfigurationUpdate, rrsched, rnti, 1);
+  enbNetDev->GetAttribute("FfMacScheduler", ptrval);
+  Ptr<RrFfMacScheduler> rrsched = ptrval.Get<RrFfMacScheduler>();
+  Simulator::Schedule(Seconds(0.2), &RrFfMacScheduler::TransmissionModeConfigurationUpdate, rrsched, rnti, 1);
 
 Finally, the model implemented can be reconfigured according to different MIMO models by updating the gain values (the only constraints is that the gain has to be constant during simulation run-time and common for the layers). The gain of each Transmission Mode can be changed according to the standard ns3 attribute system, where the attributes are: ``TxMode1Gain``, ``TxMode2Gain``, ``TxMode3Gain``, ``TxMode4Gain``, ``TxMode5Gain``, ``TxMode6Gain`` and ``TxMode7Gain``. By default only ``TxMode1Gain``, ``TxMode2Gain`` and ``TxMode3Gain`` have a meaningful value, that are the ones derived by _[CatreuxMIMO] (i.e., respectively 0.0, 4.2 and -2.8 dB).
 
@@ -649,10 +648,10 @@
 ``LteHelper`` instance right before the creation of the
 ``EnbNetDevice``, as shown in the following::
 
-  lteHelper->SetEnbAntennaModelType ("ns3::CosineAntennaModel");
-  lteHelper->SetEnbAntennaModelAttribute ("Orientation", DoubleValue (0));
-  lteHelper->SetEnbAntennaModelAttribute ("Beamwidth",   DoubleValue (60));
-  lteHelper->SetEnbAntennaModelAttribute ("MaxGain",     DoubleValue (0.0));
+  lteHelper->SetEnbAntennaModelType("ns3::CosineAntennaModel");
+  lteHelper->SetEnbAntennaModelAttribute("Orientation", DoubleValue(0));
+  lteHelper->SetEnbAntennaModelAttribute("Beamwidth",   DoubleValue(60));
+  lteHelper->SetEnbAntennaModelAttribute("MaxGain",     DoubleValue(0.0));
 
 the above code will generate an antenna model with a 60 degrees
 beamwidth pointing along the X axis. The orientation is measured
@@ -681,21 +680,21 @@
 means that REM will generated with averaged Signal-to-noise ratio from all RBs.
 
 To do this, you just need to add the following code to your simulation
-program towards the end, right before the call to Simulator::Run ()::
+program towards the end, right before the call to Simulator::Run()::
 
-  Ptr<RadioEnvironmentMapHelper> remHelper = CreateObject<RadioEnvironmentMapHelper> ();
-  remHelper->SetAttribute ("Channel", PointerValue (lteHelper->GetDownlinkSpectrumChannel ()));
-  remHelper->SetAttribute ("OutputFile", StringValue ("rem.out"));
-  remHelper->SetAttribute ("XMin", DoubleValue (-400.0));
-  remHelper->SetAttribute ("XMax", DoubleValue (400.0));
-  remHelper->SetAttribute ("XRes", UintegerValue (100));
-  remHelper->SetAttribute ("YMin", DoubleValue (-300.0));
-  remHelper->SetAttribute ("YMax", DoubleValue (300.0));
-  remHelper->SetAttribute ("YRes", UintegerValue (75));
-  remHelper->SetAttribute ("Z", DoubleValue (0.0));
-  remHelper->SetAttribute ("UseDataChannel", BooleanValue (true));
-  remHelper->SetAttribute ("RbId", IntegerValue (10));
-  remHelper->Install ();
+  Ptr<RadioEnvironmentMapHelper> remHelper = CreateObject<RadioEnvironmentMapHelper>();
+  remHelper->SetAttribute("Channel", PointerValue(lteHelper->GetDownlinkSpectrumChannel()));
+  remHelper->SetAttribute("OutputFile", StringValue("rem.out"));
+  remHelper->SetAttribute("XMin", DoubleValue(-400.0));
+  remHelper->SetAttribute("XMax", DoubleValue(400.0));
+  remHelper->SetAttribute("XRes", UintegerValue(100));
+  remHelper->SetAttribute("YMin", DoubleValue(-300.0));
+  remHelper->SetAttribute("YMax", DoubleValue(300.0));
+  remHelper->SetAttribute("YRes", UintegerValue(75));
+  remHelper->SetAttribute("Z", DoubleValue(0.0));
+  remHelper->SetAttribute("UseDataChannel", BooleanValue(true));
+  remHelper->SetAttribute("RbId", IntegerValue(10));
+  remHelper->Install();
 
 By configuring the attributes of the ``RadioEnvironmentMapHelper`` object
 as shown above, you can tune the parameters of the REM to be
@@ -770,15 +769,15 @@
 and works per RB basis. This model can be activated with the ns3 attribute system, as presented in
 the following::
 
-  Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010));
+  Config::SetDefault("ns3::LteAmc::AmcModel", EnumValue(LteAmc::PiroEW2010));
 
 While, the solution based on the physical error model can be controlled with::
 
-  Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::MiErrorModel));
+  Config::SetDefault("ns3::LteAmc::AmcModel", EnumValue(LteAmc::MiErrorModel));
 
-Finally, the required efficiency of the ``PiroEW2010`` AMC module can be tuned thanks to the ``Ber`` attribute (), for instance::
+Finally, the required efficiency of the ``PiroEW2010`` AMC module can be tuned thanks to the ``Ber`` attribute(), for instance::
 
-  Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005));
+  Config::SetDefault("ns3::LteAmc::Ber", DoubleValue(0.00005));
 
 
 
@@ -804,12 +803,12 @@
 implements an EPC based on point-to-point links. To use it, you need
 first to insert this code in your simulation program::
 
-  Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
-  Ptr<PointToPointEpcHelper> epcHelper = CreateObject<PointToPointEpcHelper> ();
+  Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
+  Ptr<PointToPointEpcHelper> epcHelper = CreateObject<PointToPointEpcHelper>();
 
 Then, you need to tell the LTE helper that the EPC will be used::
 
-  lteHelper->SetEpcHelper (epcHelper);
+  lteHelper->SetEpcHelper(epcHelper);
 
 the above step is necessary so that the LTE helper will trigger the
 appropriate EPC configuration in correspondence with some important
@@ -819,7 +818,7 @@
 creation and S1 bearer setup. All this will be done without the
 intervention of the user.
 
-Calling ``lteHelper->SetEpcHelper (epcHelper)`` enables the use of
+Calling ``lteHelper->SetEpcHelper(epcHelper)`` enables the use of
 EPC, and has the side effect that any new ``LteEnbRrc`` that is
 created will have the ``EpsBearerToRlcMapping`` attribute set to
 ``RLC_UM_ALWAYS`` instead of ``RLC_SM_ALWAYS`` if the latter was
@@ -834,33 +833,33 @@
 simple example about how to connect a single remote host (IPv4 type)
 to the PGW via a point-to-point link::
 
-  Ptr<Node> pgw = epcHelper->GetPgwNode ();
+  Ptr<Node> pgw = epcHelper->GetPgwNode();
 
   // Create a single RemoteHost
   NodeContainer remoteHostContainer;
-  remoteHostContainer.Create (1);
-  Ptr<Node> remoteHost = remoteHostContainer.Get (0);
+  remoteHostContainer.Create(1);
+  Ptr<Node> remoteHost = remoteHostContainer.Get(0);
   InternetStackHelper internet;
-  internet.Install (remoteHostContainer);
+  internet.Install(remoteHostContainer);
 
   // Create the internet
   PointToPointHelper p2ph;
-  p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s")));
-  p2ph.SetDeviceAttribute ("Mtu", UintegerValue (1500));
-  p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.010)));
-  NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost);
+  p2ph.SetDeviceAttribute("DataRate", DataRateValue(DataRate("100Gb/s")));
+  p2ph.SetDeviceAttribute("Mtu", UintegerValue(1500));
+  p2ph.SetChannelAttribute("Delay", TimeValue(Seconds(0.010)));
+  NetDeviceContainer internetDevices = p2ph.Install(pgw, remoteHost);
   Ipv4AddressHelper ipv4h;
-  ipv4h.SetBase ("1.0.0.0", "255.0.0.0");
-  Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices);
+  ipv4h.SetBase("1.0.0.0", "255.0.0.0");
+  Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign(internetDevices);
   // interface 0 is localhost, 1 is the p2p device
-  Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress (1);
+  Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress(1);
 
 
   Ipv4StaticRoutingHelper ipv4RoutingHelper;
   Ptr<Ipv4StaticRouting> remoteHostStaticRouting;
-  remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
-  remoteHostStaticRouting->AddNetworkRouteTo (epcHelper->GetEpcIpv4NetworkAddress (),
-                                              Ipv4Mask ("255.255.0.0"), 1);
+  remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting(remoteHost->GetObject<Ipv4>());
+  remoteHostStaticRouting->AddNetworkRouteTo(epcHelper->GetEpcIpv4NetworkAddress(),
+                                             Ipv4Mask("255.255.0.0"), 1);
 
 Now, you should go on and create LTE eNBs and UEs as explained in the
 previous sections. You can of course configure other LTE aspects such
@@ -874,49 +873,49 @@
 to configure an LTE-only simulation, you would then normally do
 something like this::
 
-  NetDeviceContainer ueLteDevs = lteHelper->InstallUeDevice (ueNodes);
-  lteHelper->Attach (ueLteDevs, enbLteDevs.Get (0));
+  NetDeviceContainer ueLteDevs = lteHelper->InstallUeDevice(ueNodes);
+  lteHelper->Attach(ueLteDevs, enbLteDevs.Get(0));
 
 in order to configure the UEs for IP networking, you just need to
 additionally do like this::
 
   // we install the IP stack on the UEs
   InternetStackHelper internet;
-  internet.Install (ueNodes);
+  internet.Install(ueNodes);
 
   // assign IP address to UEs
-  for (uint32_t u = 0; u < ueNodes.GetN (); ++u)
+  for (uint32_t u = 0; u < ueNodes.GetN(); ++u)
     {
-      Ptr<Node> ue = ueNodes.Get (u);
-      Ptr<NetDevice> ueLteDevice = ueLteDevs.Get (u);
+      Ptr<Node> ue = ueNodes.Get(u);
+      Ptr<NetDevice> ueLteDevice = ueLteDevs.Get(u);
       Ipv4InterfaceContainer ueIpIface;
-      ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueLteDevice));
+      ueIpIface = epcHelper->AssignUeIpv4Address(NetDeviceContainer(ueLteDevice));
       // set the default gateway for the UE
       Ptr<Ipv4StaticRouting> ueStaticRouting;
-      ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ue->GetObject<Ipv4> ());
-      ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1);
+      ueStaticRouting = ipv4RoutingHelper.GetStaticRouting(ue->GetObject<Ipv4>());
+      ueStaticRouting->SetDefaultRoute(epcHelper->GetUeDefaultGatewayAddress(), 1);
     }
 
 The activation of bearers is done in a slightly different way with
 respect to what done for an LTE-only simulation. First, the method
 ActivateDataRadioBearer is not to be used when the EPC is
 used. Second, when EPC is used, the default EPS bearer will be
-activated automatically when you call ``LteHelper::Attach ()``. Third, if
+activated automatically when you call ``LteHelper::Attach()``. Third, if
 you want to setup dedicated EPS bearer, you can do so using the method
-``LteHelper::ActivateDedicatedEpsBearer ()``. This method takes as a
-parameter the Traffic Flow Template (TFT), which is a struct that
+``LteHelper::ActivateDedicatedEpsBearer()``. This method takes as a
+parameter the Traffic Flow Template(TFT), which is a struct that
 identifies the type of traffic that will be mapped to the dedicated
 EPS bearer. Here is an example for how to setup a dedicated bearer
 for an application at the UE communicating on port 1234::
 
-  Ptr<EpcTft> tft = Create<EpcTft> ();
+  Ptr<EpcTft> tft = Create<EpcTft>();
   EpcTft::PacketFilter pf;
   pf.localPortStart = 1234;
   pf.localPortEnd = 1234;
-  tft->Add (pf);
-  lteHelper->ActivateDedicatedEpsBearer (ueLteDevs,
-                                         EpsBearer (EpsBearer::NGBR_VIDEO_TCP_DEFAULT),
-                                         tft);
+  tft->Add(pf);
+  lteHelper->ActivateDedicatedEpsBearer(ueLteDevs,
+                                        EpsBearer(EpsBearer::NGBR_VIDEO_TCP_DEFAULT),
+                                        tft);
 
 you can of course use custom EpsBearer and EpcTft configurations,
 please refer to the doxygen documentation for how to do it.
@@ -930,18 +929,18 @@
 (using the same variable names of the previous code snippets) ::
 
   uint16_t dlPort = 1234;
-  PacketSinkHelper packetSinkHelper ("ns3::UdpSocketFactory",
-                                     InetSocketAddress (Ipv4Address::GetAny (), dlPort));
-  ApplicationContainer serverApps = packetSinkHelper.Install (ue);
-  serverApps.Start (Seconds (0.01));
-  UdpClientHelper client (ueIpIface.GetAddress (0), dlPort);
-  ApplicationContainer clientApps = client.Install (remoteHost);
-  clientApps.Start (Seconds (0.01));
+  PacketSinkHelper packetSinkHelper("ns3::UdpSocketFactory",
+                                    InetSocketAddress(Ipv4Address::GetAny(), dlPort));
+  ApplicationContainer serverApps = packetSinkHelper.Install(ue);
+  serverApps.Start(Seconds(0.01));
+  UdpClientHelper client(ueIpIface.GetAddress(0), dlPort);
+  ApplicationContainer clientApps = client.Install(remoteHost);
+  clientApps.Start(Seconds(0.01));
 
 That's all! You can now start your simulation as usual::
 
-  Simulator::Stop (Seconds (10.0));
-  Simulator::Run ();
+  Simulator::Stop(Seconds(10.0));
+  Simulator::Run();
 
 
 
@@ -950,10 +949,10 @@
 
 In the previous section we used PointToPoint links for the connection between the eNBs and the SGW (S1-U interface) and among eNBs (X2-U and X2-C interfaces). The LTE module supports using emulated links instead of PointToPoint links. This is achieved by just replacing the creation of ``LteHelper`` and ``EpcHelper`` with the following code::
 
-  Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
-  Ptr<EmuEpcHelper>  epcHelper = CreateObject<EmuEpcHelper> ();
-  lteHelper->SetEpcHelper (epcHelper);
-  epcHelper->Initialize ();
+  Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
+  Ptr<EmuEpcHelper>  epcHelper = CreateObject<EmuEpcHelper>();
+  lteHelper->SetEpcHelper(epcHelper);
+  epcHelper->Initialize();
 
 
 The attributes ``ns3::EmuEpcHelper::sgwDeviceName`` and ``ns3::EmuEpcHelper::enbDeviceName`` are used to set the name of the devices used for transporting the S1-U, X2-U and X2-C interfaces at the SGW and eNB, respectively. We will now show how this is done in an example where we execute the example program ``lena-simple-epc-emu`` using two virtual ethernet interfaces.
@@ -1054,13 +1053,13 @@
 implements an EPC but without connecting the eNBs with the core network. It just creates the network
 elements of the core network::
 
-  Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
-  Ptr<NoBackhaulEpcHelper> epcHelper = CreateObject<NoBackhaulEpcHelper> ();
+  Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
+  Ptr<NoBackhaulEpcHelper> epcHelper = CreateObject<NoBackhaulEpcHelper>();
 
 
 Then, as usual, you need to tell the LTE helper that the EPC will be used::
 
-  lteHelper->SetEpcHelper (epcHelper);
+  lteHelper->SetEpcHelper(epcHelper);
 
 
 Now, you should create the backhaul network. Here we create point-to-point links as it is done
@@ -1070,68 +1069,68 @@
 
 We get the SGW node::
 
-  Ptr<Node> sgw = epcHelper->GetSgwNode ();
+  Ptr<Node> sgw = epcHelper->GetSgwNode();
 
 And we connect every eNB from the container with the SGW with a point-to-point link. We also assign
-IPv4 addresses to the interfaces of eNB and SGW with ``s1uIpv4AddressHelper.Assign (sgwEnbDevices)``
+IPv4 addresses to the interfaces of eNB and SGW with ``s1uIpv4AddressHelper.Assign(sgwEnbDevices)``
 and finally we tell the EpcHelper that this ``enb`` has a new S1 interface with
-``epcHelper->AddS1Interface (enb, enbS1uAddress, sgwS1uAddress)``, where ``enbS1uAddress`` and
+``epcHelper->AddS1Interface(enb, enbS1uAddress, sgwS1uAddress)``, where ``enbS1uAddress`` and
 ``sgwS1uAddress`` are the IPv4 addresses of the eNB and the SGW, respectively::
 
   Ipv4AddressHelper s1uIpv4AddressHelper;
 
   // Create networks of the S1 interfaces
-  s1uIpv4AddressHelper.SetBase ("10.0.0.0", "255.255.255.252");
+  s1uIpv4AddressHelper.SetBase("10.0.0.0", "255.255.255.252");
 
-  for (uint16_t i = 0; i < enbNodes.GetN (); ++i)
+  for (uint16_t i = 0; i < enbNodes.GetN(); ++i)
     {
-      Ptr<Node> enb = enbNodes.Get (i);
+      Ptr<Node> enb = enbNodes.Get(i);
 
       // Create a point to point link between the eNB and the SGW with
       // the corresponding new NetDevices on each side
       PointToPointHelper p2ph;
-      DataRate s1uLinkDataRate = DataRate ("10Gb/s");
+      DataRate s1uLinkDataRate = DataRate("10Gb/s");
       uint16_t s1uLinkMtu = 2000;
-      Time s1uLinkDelay = Time (0);
-      p2ph.SetDeviceAttribute ("DataRate", DataRateValue (s1uLinkDataRate));
-      p2ph.SetDeviceAttribute ("Mtu", UintegerValue (s1uLinkMtu));
-      p2ph.SetChannelAttribute ("Delay", TimeValue (s1uLinkDelay));
-      NetDeviceContainer sgwEnbDevices = p2ph.Install (sgw, enb);
+      Time s1uLinkDelay = Time(0);
+      p2ph.SetDeviceAttribute("DataRate", DataRateValue(s1uLinkDataRate));
+      p2ph.SetDeviceAttribute("Mtu", UintegerValue(s1uLinkMtu));
+      p2ph.SetChannelAttribute("Delay", TimeValue(s1uLinkDelay));
+      NetDeviceContainer sgwEnbDevices = p2ph.Install(sgw, enb);
 
-      Ipv4InterfaceContainer sgwEnbIpIfaces = s1uIpv4AddressHelper.Assign (sgwEnbDevices);
-      s1uIpv4AddressHelper.NewNetwork ();
+      Ipv4InterfaceContainer sgwEnbIpIfaces = s1uIpv4AddressHelper.Assign(sgwEnbDevices);
+      s1uIpv4AddressHelper.NewNetwork();
 
-      Ipv4Address sgwS1uAddress = sgwEnbIpIfaces.GetAddress (0);
-      Ipv4Address enbS1uAddress = sgwEnbIpIfaces.GetAddress (1);
+      Ipv4Address sgwS1uAddress = sgwEnbIpIfaces.GetAddress(0);
+      Ipv4Address enbS1uAddress = sgwEnbIpIfaces.GetAddress(1);
 
       // Create S1 interface between the SGW and the eNB
-      epcHelper->AddS1Interface (enb, enbS1uAddress, sgwS1uAddress);
+      epcHelper->AddS1Interface(enb, enbS1uAddress, sgwS1uAddress);
     }
 
 This is just an example how to create a custom backhaul network. In this other example, we connect
 all eNBs and the SGW to the same CSMA network::
 
     // Create networks of the S1 interfaces
-    s1uIpv4AddressHelper.SetBase ("10.0.0.0", "255.255.255.0");
+    s1uIpv4AddressHelper.SetBase("10.0.0.0", "255.255.255.0");
 
     NodeContainer sgwEnbNodes;
-    sgwEnbNodes.Add (sgw);
-    sgwEnbNodes.Add (enbNodes);
+    sgwEnbNodes.Add(sgw);
+    sgwEnbNodes.Add(enbNodes);
 
     CsmaHelper csmah;
-    NetDeviceContainer sgwEnbDevices = csmah.Install (sgwEnbNodes);
-    Ptr<NetDevice> sgwDev = sgwEnbDevices.Get (0);
+    NetDeviceContainer sgwEnbDevices = csmah.Install(sgwEnbNodes);
+    Ptr<NetDevice> sgwDev = sgwEnbDevices.Get(0);
 
-    Ipv4InterfaceContainer sgwEnbIpIfaces = s1uIpv4AddressHelper.Assign (sgwEnbDevices);
-    Ipv4Address sgwS1uAddress = sgwEnbIpIfaces.GetAddress (0);
+    Ipv4InterfaceContainer sgwEnbIpIfaces = s1uIpv4AddressHelper.Assign(sgwEnbDevices);
+    Ipv4Address sgwS1uAddress = sgwEnbIpIfaces.GetAddress(0);
 
-    for (uint16_t i = 0; i < enbNodes.GetN (); ++i)
+    for (uint16_t i = 0; i < enbNodes.GetN(); ++i)
       {
-        Ptr<Node> enb = enbNodes.Get (i);
-        Ipv4Address enbS1uAddress = sgwEnbIpIfaces.GetAddress (i + 1);
+        Ptr<Node> enb = enbNodes.Get(i);
+        Ipv4Address enbS1uAddress = sgwEnbIpIfaces.GetAddress(i + 1);
 
         // Create S1 interface between the SGW and the eNB
-        epcHelper->AddS1Interface (enb, enbS1uAddress, sgwS1uAddress);
+        epcHelper->AddS1Interface(enb, enbS1uAddress, sgwS1uAddress);
       }
 
 As you can see, apart from how you create the backhaul network, i.e. the point-to-point links or
@@ -1163,7 +1162,7 @@
 the only available network attachment method in earlier versions of LTE module.
 It is typically invoked before the simulation begins::
 
-   lteHelper->Attach (ueDevs, enbDev); // attach one or more UEs to a single eNodeB
+   lteHelper->Attach(ueDevs, enbDev); // attach one or more UEs to a single eNodeB
 
 ``LteHelper::InstallEnbDevice`` and ``LteHelper::InstallUeDevice`` functions
 must have been called before attaching. In an EPC-enabled simulation, it is also
@@ -1196,7 +1195,7 @@
 `initial cell selection` process, which can be invoked by calling another
 version of the ``LteHelper::Attach`` function, as shown below::
 
-   lteHelper->Attach (ueDevs); // attach one or more UEs to a strongest cell
+   lteHelper->Attach(ueDevs); // attach one or more UEs to a strongest cell
 
 The difference with the manual method is that the destination eNodeB is not
 specified. The procedure will find the best cell for the UEs, based on several
@@ -1226,19 +1225,19 @@
 example using the following ``LteHelper`` functions::
 
    // label the following eNodeBs with CSG identity of 1 and CSG indication enabled
-   lteHelper->SetEnbDeviceAttribute ("CsgId", UintegerValue (1));
-   lteHelper->SetEnbDeviceAttribute ("CsgIndication", BooleanValue (true));
+   lteHelper->SetEnbDeviceAttribute("CsgId", UintegerValue(1));
+   lteHelper->SetEnbDeviceAttribute("CsgIndication", BooleanValue(true));
 
    // label one or more UEs with CSG identity of 1
-   lteHelper->SetUeDeviceAttribute ("CsgId", UintegerValue (1));
+   lteHelper->SetUeDeviceAttribute("CsgId", UintegerValue(1));
 
    // install the eNodeBs and UEs
-   NetDeviceContainer csgEnbDevs = lteHelper->InstallEnbDevice (csgEnbNodes);
-   NetDeviceContainer csgUeDevs = lteHelper->InstallUeDevice (csgUeNodes);
+   NetDeviceContainer csgEnbDevs = lteHelper->InstallEnbDevice(csgEnbNodes);
+   NetDeviceContainer csgUeDevs = lteHelper->InstallUeDevice(csgUeNodes);
 
 Then enable the initial cell selection procedure on the UEs::
 
-   lteHelper->Attach (csgUeDevs);
+   lteHelper->Attach(csgUeDevs);
 
 This is necessary because the CSG restriction only works with automatic method
 of network attachment, but not in the manual method.
@@ -1294,18 +1293,18 @@
    std::vector<uint8_t> measIdList;
 
    NetDeviceContainer::Iterator it;
-   for (it = devs.Begin (); it != devs.End (); it++)
+   for (it = devs.Begin(); it != devs.End(); it++)
    {
      Ptr<NetDevice> dev = *it;
-     Ptr<LteEnbNetDevice> enbDev = dev->GetObject<LteEnbNetDevice> ();
-     Ptr<LteEnbRrc> enbRrc = enbDev->GetRrc ();
+     Ptr<LteEnbNetDevice> enbDev = dev->GetObject<LteEnbNetDevice>();
+     Ptr<LteEnbRrc> enbRrc = enbDev->GetRrc();
 
-     uint8_t measId = enbRrc->AddUeMeasReportConfig (config);
-     measIdList.push_back (measId); // remember the measId created
+     uint8_t measId = enbRrc->AddUeMeasReportConfig(config);
+     measIdList.push_back(measId); // remember the measId created
 
-     enbRrc->TraceConnect ("RecvMeasurementReport",
-                           "context",
-                           MakeCallback (&RecvMeasurementReportCallback));
+     enbRrc->TraceConnect("RecvMeasurementReport",
+                          "context",
+                          MakeCallback(&RecvMeasurementReportCallback));
    }
 
 Note that thresholds are expressed as range. In the example above, the range 41
@@ -1316,11 +1315,11 @@
 The corresponding callback function would have a definition similar as below::
 
    void
-   RecvMeasurementReportCallback (std::string context,
-                                  uint64_t imsi,
-                                  uint16_t cellId,
-                                  uint16_t rnti,
-                                  LteRrcSap::MeasurementReport measReport);
+   RecvMeasurementReportCallback(std::string context,
+                                 uint64_t imsi,
+                                 uint16_t cellId,
+                                 uint16_t rnti,
+                                 LteRrcSap::MeasurementReport measReport);
 
 This method will register the callback function as a consumer of UE
 measurements. In the case where there are more than one consumers in the
@@ -1371,7 +1370,7 @@
 Secondly, an X2 interface must be configured between the two eNodeBs, which
 needs to be done explicitly within the simulation program::
 
-   lteHelper->AddX2Interface (enbNodes);
+   lteHelper->AddX2Interface(enbNodes);
 
 where ``enbNodes`` is a ``NodeContainer`` that contains the two eNodeBs between
 which the X2 interface is to be configured. If the container has more than two
@@ -1402,10 +1401,10 @@
 that contains the source and the target eNB. Then, a handover at 0.1s can be
 scheduled like this::
 
-   lteHelper->HandoverRequest (Seconds (0.100),
-                               ueLteDevs.Get (0),
-                               enbLteDevs.Get (0),
-                               enbLteDevs.Get (1));
+   lteHelper->HandoverRequest(Seconds(0.100),
+                              ueLteDevs.Get(0),
+                              enbLteDevs.Get(0),
+                              enbLteDevs.Get(1));
 
 Note that the UE needs to be already connected to the source eNB, otherwise the
 simulation will terminate with an error message.
@@ -1430,16 +1429,16 @@
 Selecting a handover algorithm is done via the ``LteHelper`` object and its
 ``SetHandoverAlgorithmType`` method as shown below::
 
-   Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
-   lteHelper->SetHandoverAlgorithmType ("ns3::A2A4RsrqHandoverAlgorithm");
+   Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
+   lteHelper->SetHandoverAlgorithmType("ns3::A2A4RsrqHandoverAlgorithm");
 
 The selected handover algorithm may also provide several configurable
 attributes, which can be set as follows::
 
-   lteHelper->SetHandoverAlgorithmAttribute ("ServingCellThreshold",
-                                             UintegerValue (30));
-   lteHelper->SetHandoverAlgorithmAttribute ("NeighbourCellOffset",
-                                             UintegerValue (1));
+   lteHelper->SetHandoverAlgorithmAttribute("ServingCellThreshold",
+                                            UintegerValue(30));
+   lteHelper->SetHandoverAlgorithmAttribute("NeighbourCellOffset",
+                                            UintegerValue(1));
 
 Three options of handover algorithm are included in the LTE module. The
 *A2-A4-RSRQ* handover algorithm (named as ``ns3::A2A4RsrqHandoverAlgorithm``) is
@@ -1449,11 +1448,11 @@
 ``ns3::A3RsrpHandoverAlgorithm``), which can be selected and configured by the
 following code::
 
-   lteHelper->SetHandoverAlgorithmType ("ns3::A3RsrpHandoverAlgorithm");
-   lteHelper->SetHandoverAlgorithmAttribute ("Hysteresis",
-                                             DoubleValue (3.0));
-   lteHelper->SetHandoverAlgorithmAttribute ("TimeToTrigger",
-                                             TimeValue (MilliSeconds (256)));
+   lteHelper->SetHandoverAlgorithmType("ns3::A3RsrpHandoverAlgorithm");
+   lteHelper->SetHandoverAlgorithmAttribute("Hysteresis",
+                                            DoubleValue(3.0));
+   lteHelper->SetHandoverAlgorithmAttribute("TimeToTrigger",
+                                            TimeValue(MilliSeconds(256)));
 
 The last option is a special one, called the *no-op* handover algorithm, which
 basically disables automatic handover trigger. This is useful for example in
@@ -1461,7 +1460,7 @@
 decision. It does not have any configurable attributes. The usage is as
 follows::
 
-   lteHelper->SetHandoverAlgorithmType ("ns3::NoOpHandoverAlgorithm");
+   lteHelper->SetHandoverAlgorithmType("ns3::NoOpHandoverAlgorithm");
 
 For more information on each handover algorithm's decision policy and their
 attributes, please refer to their respective subsections in Section
@@ -1472,7 +1471,7 @@
 words, make sure to select the right handover algorithm before finalizing it in
 the following line of code::
 
-   NetDeviceContainer enbLteDevs = lteHelper->InstallEnbDevice (enbNodes);
+   NetDeviceContainer enbLteDevs = lteHelper->InstallEnbDevice(enbNodes);
 
 Example with full source code of using automatic handover trigger can be found
 in the ``lena-x2-handover-measures`` example program.
@@ -1526,8 +1525,8 @@
 also affect all other data or control packets not related to handover, which may
 be an unwanted side effect. Otherwise, it can be done as follows::
 
-   Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false));
-   Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false));
+   Config::SetDefault("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue(false));
+   Config::SetDefault("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue(false));
 
 By using the above code, we disable the error model in both control and data
 channels and in both directions (downlink and uplink). This is necessary because
@@ -1550,13 +1549,13 @@
 in your simulation program you can declare the following methods::
 
    void
-   NotifyHandoverStartUe (std::string context,
-                          uint64_t imsi,
-                          uint16_t cellId,
-                          uint16_t rnti,
-                          uint16_t targetCellId)
+   NotifyHandoverStartUe(std::string context,
+                         uint64_t imsi,
+                         uint16_t cellId,
+                         uint16_t rnti,
+                         uint16_t targetCellId)
    {
-     std::cout << Simulator::Now ().GetSeconds () << " " << context
+     std::cout << Simulator::Now().GetSeconds() << " " << context
                << " UE IMSI " << imsi
                << ": previously connected to CellId " << cellId
                << " with RNTI " << rnti
@@ -1565,12 +1564,12 @@
    }
 
    void
-   NotifyHandoverEndOkUe (std::string context,
-                          uint64_t imsi,
-                          uint16_t cellId,
-                          uint16_t rnti)
+   NotifyHandoverEndOkUe(std::string context,
+                         uint64_t imsi,
+                         uint16_t cellId,
+                         uint16_t rnti)
    {
-     std::cout << Simulator::Now ().GetSeconds () << " " << context
+     std::cout << Simulator::Now().GetSeconds() << " " << context
                << " UE IMSI " << imsi
                << ": successful handover to CellId " << cellId
                << " with RNTI " << rnti
@@ -1578,13 +1577,13 @@
    }
 
    void
-   NotifyHandoverStartEnb (std::string context,
-                           uint64_t imsi,
-                           uint16_t cellId,
-                           uint16_t rnti,
-                           uint16_t targetCellId)
+   NotifyHandoverStartEnb(std::string context,
+                          uint64_t imsi,
+                          uint16_t cellId,
+                          uint16_t rnti,
+                          uint16_t targetCellId)
    {
-     std::cout << Simulator::Now ().GetSeconds () << " " << context
+     std::cout << Simulator::Now().GetSeconds() << " " << context
                << " eNB CellId " << cellId
                << ": start handover of UE with IMSI " << imsi
                << " RNTI " << rnti
@@ -1593,12 +1592,12 @@
    }
 
    void
-   NotifyHandoverEndOkEnb (std::string context,
-                           uint64_t imsi,
-                           uint16_t cellId,
-                           uint16_t rnti)
+   NotifyHandoverEndOkEnb(std::string context,
+                          uint64_t imsi,
+                          uint16_t cellId,
+                          uint16_t rnti)
    {
-     std::cout << Simulator::Now ().GetSeconds () << " " << context
+     std::cout << Simulator::Now().GetSeconds() << " " << context
                << " eNB CellId " << cellId
                << ": completed handover of UE with IMSI " << imsi
                << " RNTI " << rnti
@@ -1608,17 +1607,17 @@
 Then, you can hook up these methods to the corresponding trace sources
 like this::
 
-   Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverStart",
-                    MakeCallback (&NotifyHandoverStartEnb));
-   Config::Connect ("/NodeList/*/DeviceList/*/LteUeRrc/HandoverStart",
-                    MakeCallback (&NotifyHandoverStartUe));
-   Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverEndOk",
-                    MakeCallback (&NotifyHandoverEndOkEnb));
-   Config::Connect ("/NodeList/*/DeviceList/*/LteUeRrc/HandoverEndOk",
-                    MakeCallback (&NotifyHandoverEndOkUe));
+   Config::Connect("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverStart",
+                   MakeCallback(&NotifyHandoverStartEnb));
+   Config::Connect("/NodeList/*/DeviceList/*/LteUeRrc/HandoverStart",
+                   MakeCallback(&NotifyHandoverStartUe));
+   Config::Connect("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverEndOk",
+                   MakeCallback(&NotifyHandoverEndOkEnb));
+   Config::Connect("/NodeList/*/DeviceList/*/LteUeRrc/HandoverEndOk",
+                   MakeCallback(&NotifyHandoverEndOkUe));
 
 Handover failure events can also be traced by trace sink functions with
-a similar signature as above (including IMSI, cell ID, and RNTI). Four
+a similar signature as above(including IMSI, cell ID, and RNTI). Four
 different failure events are traced:
 
 1. HandoverFailureNoPreamble: Handover failure due to non allocation of
@@ -1630,14 +1629,14 @@
 Similarly, one can hook up methods to the corresponding trace sources
 like this::
 
-   Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureNoPreamble",
-                    MakeCallback (&NotifyHandoverFailureNoPreamble));
-   Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureMaxRach",
-                    MakeCallback (&NotifyHandoverFailureMaxRach));
-   Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureLeaving",
-                    MakeCallback (&NotifyHandoverFailureLeaving));
-   Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureJoining",
-                    MakeCallback (&NotifyHandoverFailureJoining));
+   Config::Connect("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureNoPreamble",
+                   MakeCallback(&NotifyHandoverFailureNoPreamble));
+   Config::Connect("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureMaxRach",
+                   MakeCallback(&NotifyHandoverFailureMaxRach));
+   Config::Connect("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureLeaving",
+                   MakeCallback(&NotifyHandoverFailureLeaving));
+   Config::Connect("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureJoining",
+                   MakeCallback(&NotifyHandoverFailureJoining));
 
 The example program ``src/lte/examples/lena-x2-handover.cc``
 illustrates how the above instructions can be integrated in a
@@ -1695,8 +1694,8 @@
 Selecting a FR algorithm is done via the ``LteHelper`` object and
 its ``SetFfrAlgorithmType`` method as shown below::
 
-   Ptr<LteHelper> lteHelper = CreateObject<LteHelper> ();
-   lteHelper->SetFfrAlgorithmType ("ns3::LteFrHardAlgorithm");
+   Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();
+   lteHelper->SetFfrAlgorithmType("ns3::LteFrHardAlgorithm");
 
 Each implemented FR algorithm provide several configurable attributes. Users do
 not have to care about UL and DL bandwidth configuration, because it is done
@@ -1704,8 +1703,8 @@
 configure required values for ``LteEnbNetDevice``::
 
    uint8_t bandwidth = 100;
-   lteHelper->SetEnbDeviceAttribute ("DlBandwidth", UintegerValue (bandwidth));
-   lteHelper->SetEnbDeviceAttribute ("UlBandwidth", UintegerValue (bandwidth));
+   lteHelper->SetEnbDeviceAttribute("DlBandwidth", UintegerValue(bandwidth));
+   lteHelper->SetEnbDeviceAttribute("UlBandwidth", UintegerValue(bandwidth));
 
 
 Now, each FR algorithms configuration will be described.
@@ -1726,12 +1725,12 @@
 
 Example configuration of LteFrHardAlgorithm can be done in following way::
 
-   lteHelper->SetFfrAlgorithmType ("ns3::LteFrHardAlgorithm");
-   lteHelper->SetFfrAlgorithmAttribute ("DlSubBandOffset", UintegerValue (8));
-   lteHelper->SetFfrAlgorithmAttribute ("DlSubBandwidth", UintegerValue (8));
-   lteHelper->SetFfrAlgorithmAttribute ("UlSubBandOffset", UintegerValue (8));
-   lteHelper->SetFfrAlgorithmAttribute ("UlSubBandwidth", UintegerValue (8));
-   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice (enbNodes.Get(0));
+   lteHelper->SetFfrAlgorithmType("ns3::LteFrHardAlgorithm");
+   lteHelper->SetFfrAlgorithmAttribute("DlSubBandOffset", UintegerValue(8));
+   lteHelper->SetFfrAlgorithmAttribute("DlSubBandwidth", UintegerValue(8));
+   lteHelper->SetFfrAlgorithmAttribute("UlSubBandOffset", UintegerValue(8));
+   lteHelper->SetFfrAlgorithmAttribute("UlSubBandwidth", UintegerValue(8));
+   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice(enbNodes.Get(0));
 
 Above example allow eNB to use only RBs from 8 to 16 in DL and UL, while entire cell
 bandwidth is 25.
@@ -1765,21 +1764,21 @@
 private sub-band in DL and UL, RSRQ threshold is 20 dB, power in center area equals
 ``LteEnbPhy::TxPower - 3dB``, power in edge area equals ``LteEnbPhy::TxPower + 3dB``::
 
-   lteHelper->SetFfrAlgorithmType ("ns3::LteFrStrictAlgorithm");
-   lteHelper->SetFfrAlgorithmAttribute ("DlCommonSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("UlCommonSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("DlEdgeSubBandOffset", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("DlEdgeSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("UlEdgeSubBandOffset", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("UlEdgeSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("RsrqThreshold", UintegerValue (20));
-   lteHelper->SetFfrAlgorithmAttribute ("CenterPowerOffset",
-                                        UintegerValue (LteRrcSap::PdschConfigDedicated::dB_3));
-   lteHelper->SetFfrAlgorithmAttribute ("EdgePowerOffset",
-                                        UintegerValue (LteRrcSap::PdschConfigDedicated::dB3));
-   lteHelper->SetFfrAlgorithmAttribute ("CenterAreaTpc", UintegerValue (1));
-   lteHelper->SetFfrAlgorithmAttribute ("EdgeAreaTpc", UintegerValue (2));
-   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice (enbNodes.Get(0));
+   lteHelper->SetFfrAlgorithmType("ns3::LteFrStrictAlgorithm");
+   lteHelper->SetFfrAlgorithmAttribute("DlCommonSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("UlCommonSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("DlEdgeSubBandOffset", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("DlEdgeSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("UlEdgeSubBandOffset", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("UlEdgeSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("RsrqThreshold", UintegerValue(20));
+   lteHelper->SetFfrAlgorithmAttribute("CenterPowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB_3));
+   lteHelper->SetFfrAlgorithmAttribute("EdgePowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB3));
+   lteHelper->SetFfrAlgorithmAttribute("CenterAreaTpc", UintegerValue(1));
+   lteHelper->SetFfrAlgorithmAttribute("EdgeAreaTpc", UintegerValue(2));
+   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice(enbNodes.Get(0));
 
 
 Soft Frequency Reuse Algorithm
@@ -1808,18 +1807,18 @@
 is not available for cell center users. RSRQ threshold is 20 dB, power in center area
 equals ``LteEnbPhy::TxPower``, power in edge area equals ``LteEnbPhy::TxPower + 3dB``::
 
-   lteHelper->SetFfrAlgorithmType ("ns3::LteFrSoftAlgorithm");
-   lteHelper->SetFfrAlgorithmAttribute ("DlEdgeSubBandOffset", UintegerValue (8));
-   lteHelper->SetFfrAlgorithmAttribute ("DlEdgeSubBandwidth", UintegerValue (8));
-   lteHelper->SetFfrAlgorithmAttribute ("UlEdgeSubBandOffset", UintegerValue (8));
-   lteHelper->SetFfrAlgorithmAttribute ("UlEdgeSubBandwidth", UintegerValue (8));
-   lteHelper->SetFfrAlgorithmAttribute ("AllowCenterUeUseEdgeSubBand", BooleanValue (false));
-   lteHelper->SetFfrAlgorithmAttribute ("RsrqThreshold", UintegerValue (20));
-   lteHelper->SetFfrAlgorithmAttribute ("CenterPowerOffset",
-                                        UintegerValue (LteRrcSap::PdschConfigDedicated::dB0));
-   lteHelper->SetFfrAlgorithmAttribute ("EdgePowerOffset",
-                                        UintegerValue (LteRrcSap::PdschConfigDedicated::dB3));
-   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice (enbNodes.Get(0));
+   lteHelper->SetFfrAlgorithmType("ns3::LteFrSoftAlgorithm");
+   lteHelper->SetFfrAlgorithmAttribute("DlEdgeSubBandOffset", UintegerValue(8));
+   lteHelper->SetFfrAlgorithmAttribute("DlEdgeSubBandwidth", UintegerValue(8));
+   lteHelper->SetFfrAlgorithmAttribute("UlEdgeSubBandOffset", UintegerValue(8));
+   lteHelper->SetFfrAlgorithmAttribute("UlEdgeSubBandwidth", UintegerValue(8));
+   lteHelper->SetFfrAlgorithmAttribute("AllowCenterUeUseEdgeSubBand", BooleanValue(false));
+   lteHelper->SetFfrAlgorithmAttribute("RsrqThreshold", UintegerValue(20));
+   lteHelper->SetFfrAlgorithmAttribute("CenterPowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB0));
+   lteHelper->SetFfrAlgorithmAttribute("EdgePowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB3));
+   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice(enbNodes.Get(0));
 
 
 Soft Fractional Frequency Reuse Algorithm
@@ -1860,22 +1859,22 @@
 Power in center area equals ``LteEnbPhy::TxPower - 3dB``, power in medium area equals
 ``LteEnbPhy::TxPower + 3dB``, power in edge area equals ``LteEnbPhy::TxPower + 3dB``::
 
-   lteHelper->SetFfrAlgorithmType ("ns3::LteFfrSoftAlgorithm");
-   lteHelper->SetFfrAlgorithmAttribute ("UlCommonSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("DlCommonSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("DlEdgeSubBandOffset", UintegerValue (0));
-   lteHelper->SetFfrAlgorithmAttribute ("DlEdgeSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("UlEdgeSubBandOffset", UintegerValue (0));
-   lteHelper->SetFfrAlgorithmAttribute ("UlEdgeSubBandwidth", UintegerValue (6));
-   lteHelper->SetFfrAlgorithmAttribute ("CenterRsrqThreshold", UintegerValue (28));
-   lteHelper->SetFfrAlgorithmAttribute ("EdgeRsrqThreshold", UintegerValue (18));
-   lteHelper->SetFfrAlgorithmAttribute ("CenterAreaPowerOffset",
-                                        UintegerValue (LteRrcSap::PdschConfigDedicated::dB_3));
-   lteHelper->SetFfrAlgorithmAttribute ("MediumAreaPowerOffset",
-                                        UintegerValue (LteRrcSap::PdschConfigDedicated::dB0));
-   lteHelper->SetFfrAlgorithmAttribute ("EdgeAreaPowerOffset",
-                                        UintegerValue (LteRrcSap::PdschConfigDedicated::dB3));
-   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice (enbNodes.Get(0));
+   lteHelper->SetFfrAlgorithmType("ns3::LteFfrSoftAlgorithm");
+   lteHelper->SetFfrAlgorithmAttribute("UlCommonSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("DlCommonSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("DlEdgeSubBandOffset", UintegerValue(0));
+   lteHelper->SetFfrAlgorithmAttribute("DlEdgeSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("UlEdgeSubBandOffset", UintegerValue(0));
+   lteHelper->SetFfrAlgorithmAttribute("UlEdgeSubBandwidth", UintegerValue(6));
+   lteHelper->SetFfrAlgorithmAttribute("CenterRsrqThreshold", UintegerValue(28));
+   lteHelper->SetFfrAlgorithmAttribute("EdgeRsrqThreshold", UintegerValue(18));
+   lteHelper->SetFfrAlgorithmAttribute("CenterAreaPowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB_3));
+   lteHelper->SetFfrAlgorithmAttribute("MediumAreaPowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB0));
+   lteHelper->SetFfrAlgorithmAttribute("EdgeAreaPowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB3));
+   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice(enbNodes.Get(0));
 
 
 Enhanced Fractional Frequency Reuse Algorithm
@@ -1921,19 +1920,19 @@
 power in edge area equals ``LteEnbPhy::TxPower + 0dB``::
 
    lteHelper->SetFfrAlgorithmType("ns3::LteFfrEnhancedAlgorithm");
-   lteHelper->SetFfrAlgorithmAttribute("RsrqThreshold", UintegerValue (25));
-   lteHelper->SetFfrAlgorithmAttribute("DlCqiThreshold", UintegerValue (10));
-   lteHelper->SetFfrAlgorithmAttribute("UlCqiThreshold", UintegerValue (10));
+   lteHelper->SetFfrAlgorithmAttribute("RsrqThreshold", UintegerValue(25));
+   lteHelper->SetFfrAlgorithmAttribute("DlCqiThreshold", UintegerValue(10));
+   lteHelper->SetFfrAlgorithmAttribute("UlCqiThreshold", UintegerValue(10));
    lteHelper->SetFfrAlgorithmAttribute("CenterAreaPowerOffset",
-                                       UintegerValue (LteRrcSap::PdschConfigDedicated::dB_6));
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB_6));
    lteHelper->SetFfrAlgorithmAttribute("EdgeAreaPowerOffset",
-                                       UintegerValue (LteRrcSap::PdschConfigDedicated::dB0));
-   lteHelper->SetFfrAlgorithmAttribute("UlSubBandOffset", UintegerValue (0));
-   lteHelper->SetFfrAlgorithmAttribute("UlReuse3SubBandwidth", UintegerValue (4));
-   lteHelper->SetFfrAlgorithmAttribute("UlReuse1SubBandwidth", UintegerValue (4));
-   lteHelper->SetFfrAlgorithmAttribute("DlSubBandOffset", UintegerValue (0));
-   lteHelper->SetFfrAlgorithmAttribute("DlReuse3SubBandwidth", UintegerValue (4));
-   lteHelper->SetFfrAlgorithmAttribute("DlReuse1SubBandwidth", UintegerValue (4));
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB0));
+   lteHelper->SetFfrAlgorithmAttribute("UlSubBandOffset", UintegerValue(0));
+   lteHelper->SetFfrAlgorithmAttribute("UlReuse3SubBandwidth", UintegerValue(4));
+   lteHelper->SetFfrAlgorithmAttribute("UlReuse1SubBandwidth", UintegerValue(4));
+   lteHelper->SetFfrAlgorithmAttribute("DlSubBandOffset", UintegerValue(0));
+   lteHelper->SetFfrAlgorithmAttribute("DlReuse3SubBandwidth", UintegerValue(4));
+   lteHelper->SetFfrAlgorithmAttribute("DlReuse1SubBandwidth", UintegerValue(4));
 
 
 Distributed Fractional Frequency Reuse Algorithm
@@ -1946,7 +1945,7 @@
 With Distributed Fractional Frequency Reuse  Algorithm, eNb uses entire cell bandwidth and there can
 be two sub-bands: center sub-band and edge sub-band . Within these sub-bands UEs can be served with
 different power level. Algorithm adaptively selects RBs for cell-edge sub-band on basis of
-coordination information (i.e. RNTP) from adjecent cells and notifies the base stations of the adjacent cells,
+coordination information (i.e. RNTP) from adjacent cells and notifies the base stations of the adjacent cells,
 which RBs it selected to use in edge sub-band. If there are no UE classified as edge UE in cell,
 eNB will not use any RBs as edge sub-band.
 
@@ -1971,13 +1970,13 @@
 
   lteHelper->SetFfrAlgorithmType("ns3::LteFfrDistributedAlgorithm");
   lteHelper->SetFfrAlgorithmAttribute("CalculationInterval", TimeValue(MilliSeconds(500)));
-  lteHelper->SetFfrAlgorithmAttribute ("RsrqThreshold", UintegerValue (25));
-  lteHelper->SetFfrAlgorithmAttribute ("RsrpDifferenceThreshold", UintegerValue (5));
-  lteHelper->SetFfrAlgorithmAttribute ("EdgeRbNum", UintegerValue (6));
-  lteHelper->SetFfrAlgorithmAttribute ("CenterPowerOffset",
-                                       UintegerValue (LteRrcSap::PdschConfigDedicated::dB0));
-  lteHelper->SetFfrAlgorithmAttribute ("EdgePowerOffset",
-                                       UintegerValue (LteRrcSap::PdschConfigDedicated::dB3));
+  lteHelper->SetFfrAlgorithmAttribute("RsrqThreshold", UintegerValue(25));
+  lteHelper->SetFfrAlgorithmAttribute("RsrpDifferenceThreshold", UintegerValue(5));
+  lteHelper->SetFfrAlgorithmAttribute("EdgeRbNum", UintegerValue(6));
+  lteHelper->SetFfrAlgorithmAttribute("CenterPowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB0));
+  lteHelper->SetFfrAlgorithmAttribute("EdgePowerOffset",
+                                       UintegerValue(LteRrcSap::PdschConfigDedicated::dB3));
 
 
 Automatic configuration
@@ -1999,8 +1998,8 @@
 Example below show automatic FR algorithm configuration::
 
    lteHelper->SetFfrAlgorithmType("ns3::LteFfrSoftAlgorithm");
-   lteHelper->SetFfrAlgorithmAttribute("FrCellTypeId", UintegerValue (1));
-   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice (enbNodes.Get(0));
+   lteHelper->SetFfrAlgorithmAttribute("FrCellTypeId", UintegerValue(1));
+   NetDeviceContainer enbDevs = lteHelper->InstallEnbDevice(enbNodes.Get(0));
 
 
 Uplink Power Control
@@ -2044,10 +2043,10 @@
 
 Example configuration is presented below::
 
-  Config::SetDefault ("ns3::LteUePhy::EnableUplinkPowerControl", BooleanValue (true));
-  Config::SetDefault ("ns3::LteEnbPhy::TxPower", DoubleValue (30));
-  Config::SetDefault ("ns3::LteUePowerControl::ClosedLoop", BooleanValue (true));
-  Config::SetDefault ("ns3::LteUePowerControl::AccumulationEnabled", BooleanValue (true));
+  Config::SetDefault("ns3::LteUePhy::EnableUplinkPowerControl", BooleanValue(true));
+  Config::SetDefault("ns3::LteEnbPhy::TxPower", DoubleValue(30));
+  Config::SetDefault("ns3::LteUePowerControl::ClosedLoop", BooleanValue(true));
+  Config::SetDefault("ns3::LteUePowerControl::AccumulationEnabled", BooleanValue(true));
 
 As an example, user can take a look and run the lena-uplink-power-control program.
 
@@ -2098,28 +2097,28 @@
 following sample code snippet shows one possible way to obtain the above::
 
    void
-   NotifyHandoverEndOkUe (std::string context, uint64_t imsi,
-                          uint16_t cellId, uint16_t rnti)
+   NotifyHandoverEndOkUe(std::string context, uint64_t imsi,
+                         uint16_t cellId, uint16_t rnti)
    {
      std::cout << "Handover IMSI " << imsi << std::endl;
    }
 
    int
-   main (int argc, char *argv[])
+   main(int argc, char *argv[])
    {
      /*** SNIP ***/
 
-     Config::Connect ("/NodeList/*/DeviceList/*/LteUeRrc/HandoverEndOk",
-                      MakeCallback (&NotifyHandoverEndOkUe));
+     Config::Connect("/NodeList/*/DeviceList/*/LteUeRrc/HandoverEndOk",
+                     MakeCallback(&NotifyHandoverEndOkUe));
 
-     lteHelper->EnablePhyTraces ();
-     lteHelper->EnableRlcTraces ();
-     Ptr<RadioBearerStatsCalculator> rlcStats = lteHelper->GetRlcStats ();
-     rlcStats->SetAttribute ("StartTime", TimeValue (Seconds (0)));
-     rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simTime)));
+     lteHelper->EnablePhyTraces();
+     lteHelper->EnableRlcTraces();
+     Ptr<RadioBearerStatsCalculator> rlcStats = lteHelper->GetRlcStats();
+     rlcStats->SetAttribute("StartTime", TimeValue(Seconds(0)));
+     rlcStats->SetAttribute("EpochDuration", TimeValue(Seconds(simTime)));
 
-     Simulator::Run ();
-     Simulator::Destroy ();
+     Simulator::Run();
+     Simulator::Destroy();
      return 0;
    }
 
@@ -2500,9 +2499,9 @@
 
 An example configuration is presented below::
 
-  Config::SetDefault ("ns3::LteHelper::UseCa", BooleanValue (useCa));
-  Config::SetDefault ("ns3::LteHelper::NumberOfComponentCarriers", UintegerValue (2));
-  Config::SetDefault ("ns3::LteHelper::EnbComponentCarrierManager", StringValue ("ns3::RrComponentCarrierManager"));
+  Config::SetDefault("ns3::LteHelper::UseCa", BooleanValue(useCa));
+  Config::SetDefault("ns3::LteHelper::NumberOfComponentCarriers", UintegerValue(2));
+  Config::SetDefault("ns3::LteHelper::EnbComponentCarrierManager", StringValue("ns3::RrComponentCarrierManager"));
 
 As an example, the user can take a look and run the ``lena-simple`` and ``lena-simple-epc`` programs and enable LTE traces
 to check the performance. A new column is added to PHY and MAC traces to indicate the component carrier.
@@ -2577,7 +2576,7 @@
 We note that, the RLF detection is enabled by default, which can be disabled by
 configuring the ``LteUePhy::EnableRlfDetection`` to false, e.g.,::
 
- Config::SetDefault ("ns3::LteUePhy::EnableRlfDetection", BooleanValue (false));
+ Config::SetDefault("ns3::LteUePhy::EnableRlfDetection", BooleanValue(false));
 
 In this example, to study the impact of a RLF on the user's quality of experience,
 we compute an instantaneous (i.e., every 200 ms) DL throughput of the UE, and
@@ -2710,5 +2709,3 @@
    EpcSgwApplication, EpcPgwApplication and EpcEnbApplication, then moving down the
    LTE radio stack (PDCP, RLC, MAC, and finally PHY). All this until
    you find where packets stop being processed / forwarded.
-
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/examples/lena-radio-link-failure.cc ns-3.38/src/lte/examples/lena-radio-link-failure.cc
--- ns-3.37/src/lte/examples/lena-radio-link-failure.cc	2022-11-01 17:37:24.543395300 -0700
+++ ns-3.38/src/lte/examples/lena-radio-link-failure.cc	2023-03-16 20:54:23.660868600 -0700
@@ -112,19 +112,21 @@
 }
 
 /// Map each of UE RRC states to its string representation.
-static const std::string g_ueRrcStateName[LteUeRrc::NUM_STATES] = {"IDLE_START",
-                                                                   "IDLE_CELL_SEARCH",
-                                                                   "IDLE_WAIT_MIB_SIB1",
-                                                                   "IDLE_WAIT_MIB",
-                                                                   "IDLE_WAIT_SIB1",
-                                                                   "IDLE_CAMPED_NORMALLY",
-                                                                   "IDLE_WAIT_SIB2",
-                                                                   "IDLE_RANDOM_ACCESS",
-                                                                   "IDLE_CONNECTING",
-                                                                   "CONNECTED_NORMALLY",
-                                                                   "CONNECTED_HANDOVER",
-                                                                   "CONNECTED_PHY_PROBLEM",
-                                                                   "CONNECTED_REESTABLISHING"};
+static const std::string g_ueRrcStateName[LteUeRrc::NUM_STATES] = {
+    "IDLE_START",
+    "IDLE_CELL_SEARCH",
+    "IDLE_WAIT_MIB_SIB1",
+    "IDLE_WAIT_MIB",
+    "IDLE_WAIT_SIB1",
+    "IDLE_CAMPED_NORMALLY",
+    "IDLE_WAIT_SIB2",
+    "IDLE_RANDOM_ACCESS",
+    "IDLE_CONNECTING",
+    "CONNECTED_NORMALLY",
+    "CONNECTED_HANDOVER",
+    "CONNECTED_PHY_PROBLEM",
+    "CONNECTED_REESTABLISHING",
+};
 
 /**
  * \param s The UE RRC state.
@@ -305,7 +307,7 @@
 }
 
 /**
- * Write the troughput to file.
+ * Write the throughput to file.
  *
  * \param firstWrite True if first time writing.
  * \param binSize Bin size.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/examples/lena-x2-handover.cc ns-3.38/src/lte/examples/lena-x2-handover.cc
--- ns-3.37/src/lte/examples/lena-x2-handover.cc	2022-11-01 17:37:24.543395300 -0700
+++ ns-3.38/src/lte/examples/lena-x2-handover.cc	2023-03-16 20:54:23.660868600 -0700
@@ -31,7 +31,7 @@
 NS_LOG_COMPONENT_DEFINE("LenaX2HandoverExample");
 
 /**
- * UE Connection established noticication.
+ * UE Connection established notification.
  *
  * \param context The context.
  * \param imsi The IMSI of the connected terminal.
@@ -82,7 +82,7 @@
 }
 
 /**
- * eNB Connection established noticication.
+ * eNB Connection established notification.
  *
  * \param context The context.
  * \param imsi The IMSI of the connected terminal.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/helper/cc-helper.cc ns-3.38/src/lte/helper/cc-helper.cc
--- ns-3.37/src/lte/helper/cc-helper.cc	2022-11-01 17:37:24.543395300 -0700
+++ ns-3.38/src/lte/helper/cc-helper.cc	2023-03-16 20:54:23.660868600 -0700
@@ -138,31 +138,31 @@
 }
 
 uint16_t
-CcHelper::GetNumberOfComponentCarriers()
+CcHelper::GetNumberOfComponentCarriers() const
 {
     return m_numberOfComponentCarriers;
 }
 
 uint32_t
-CcHelper::GetUlEarfcn()
+CcHelper::GetUlEarfcn() const
 {
     return m_ulEarfcn;
 }
 
 uint32_t
-CcHelper::GetDlEarfcn()
+CcHelper::GetDlEarfcn() const
 {
     return m_dlEarfcn;
 }
 
 uint16_t
-CcHelper::GetDlBandwidth()
+CcHelper::GetDlBandwidth() const
 {
     return m_dlBandwidth;
 }
 
 uint16_t
-CcHelper::GetUlBandwidth()
+CcHelper::GetUlBandwidth() const
 {
     return m_ulBandwidth;
 }
@@ -231,7 +231,7 @@
                          uint16_t dlBandwidth,
                          uint32_t ulEarfcn,
                          uint32_t dlEarfcn,
-                         bool isPrimary)
+                         bool isPrimary) const
 {
     ComponentCarrier cc;
     if (m_ulEarfcn != 0)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/helper/cc-helper.h ns-3.38/src/lte/helper/cc-helper.h
--- ns-3.37/src/lte/helper/cc-helper.h	2022-11-01 17:37:24.543395300 -0700
+++ ns-3.38/src/lte/helper/cc-helper.h	2023-03-16 20:54:23.660868600 -0700
@@ -74,7 +74,7 @@
      * \param dlBandwidth the DL bandwidth
      * \param ulEarfcn the UL EARFCN
      * \param dlEarfcn the DL EARFCN
-     * \param isPrimary ture if primary
+     * \param isPrimary true if primary
      * \returns the component carrier
      */
     ComponentCarrier DoCreateSingleCc(uint16_t ulBandwidth,
@@ -142,31 +142,31 @@
      *
      * \returns the number of component carriers
      */
-    uint16_t GetNumberOfComponentCarriers();
+    uint16_t GetNumberOfComponentCarriers() const;
     /**
      * Get UL EARFCN.
      *
      * \returns the UL EARFCN
      */
-    uint32_t GetUlEarfcn();
+    uint32_t GetUlEarfcn() const;
     /**
      * Get DL EARFCN.
      *
      * \returns the DL EARFCN
      */
-    uint32_t GetDlEarfcn();
+    uint32_t GetDlEarfcn() const;
     /**
      * Get DL bandwidth.
      *
      * \returns the DL bandwidth
      */
-    uint16_t GetDlBandwidth();
+    uint16_t GetDlBandwidth() const;
     /**
      * Get UL bandwidth.
      *
      * \returns the UL bandwidth
      */
-    uint16_t GetUlBandwidth();
+    uint16_t GetUlBandwidth() const;
 
   protected:
     // inherited from Object
@@ -188,7 +188,7 @@
                                     uint16_t dlBandwidth,
                                     uint32_t ulEarfcn,
                                     uint32_t dlEarfcn,
-                                    bool isPrimary);
+                                    bool isPrimary) const;
 
     /// Factory for each Carrier Component.
     ObjectFactory m_ccFactory;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/helper/lte-helper.cc ns-3.38/src/lte/helper/lte-helper.cc
--- ns-3.37/src/lte/helper/lte-helper.cc	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/helper/lte-helper.cc	2023-03-16 20:54:23.664868600 -0700
@@ -517,7 +517,7 @@
     Ptr<LteHandoverAlgorithm> handoverAlgorithm =
         m_handoverAlgorithmFactory.Create<LteHandoverAlgorithm>();
 
-    NS_ABORT_MSG_IF(m_componentCarrierPhyParams.size() != 0, "CC map is not clean");
+    NS_ABORT_MSG_IF(!m_componentCarrierPhyParams.empty(), "CC map is not clean");
     DoComponentCarrierConfigure(dev->GetUlEarfcn(),
                                 dev->GetDlEarfcn(),
                                 dev->GetUlBandwidth(),
@@ -837,7 +837,7 @@
     // of each component carrier. These values must be updated once the UE is attached to the
     // eNB and receives RRC Connection Reconfiguration message. In case of primary carrier or
     // a single carrier, these values will be updated once the UE will receive SIB2 and MIB.
-    NS_ABORT_MSG_IF(m_componentCarrierPhyParams.size() != 0, "CC map is not clean");
+    NS_ABORT_MSG_IF(!m_componentCarrierPhyParams.empty(), "CC map is not clean");
     DoComponentCarrierConfigure(dev->GetDlEarfcn() + 18000, dev->GetDlEarfcn(), 25, 25);
     NS_ABORT_MSG_IF(m_componentCarrierPhyParams.size() != m_noOfCcs,
                     "CC map size (" << m_componentCarrierPhyParams.size()
@@ -1426,7 +1426,7 @@
 {
     NS_LOG_FUNCTION(this << ulEarfcn << dlEarfcn << ulbw << dlbw);
 
-    NS_ABORT_MSG_IF(m_componentCarrierPhyParams.size() != 0, "CC map is not clean");
+    NS_ABORT_MSG_IF(!m_componentCarrierPhyParams.empty(), "CC map is not clean");
     Ptr<CcHelper> ccHelper = CreateObject<CcHelper>();
     ccHelper->SetNumberOfComponentCarriers(m_noOfCcs);
     ccHelper->SetUlEarfcn(ulEarfcn);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/component-carrier.cc ns-3.38/src/lte/model/component-carrier.cc
--- ns-3.37/src/lte/model/component-carrier.cc	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/component-carrier.cc	2023-03-16 20:54:23.664868600 -0700
@@ -253,7 +253,7 @@
 }
 
 uint16_t
-ComponentCarrierBaseStation::GetCellId()
+ComponentCarrierBaseStation::GetCellId() const
 {
     return m_cellId;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/component-carrier.h ns-3.38/src/lte/model/component-carrier.h
--- ns-3.37/src/lte/model/component-carrier.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/component-carrier.h	2023-03-16 20:54:23.664868600 -0700
@@ -188,7 +188,7 @@
      * Get cell identifier
      * \return cell identifier
      */
-    uint16_t GetCellId();
+    uint16_t GetCellId() const;
 
     /**
      * Set physical cell identifier
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/cqa-ff-mac-scheduler.cc ns-3.38/src/lte/model/cqa-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/cqa-ff-mac-scheduler.cc	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/cqa-ff-mac-scheduler.cc	2023-03-16 20:54:23.664868600 -0700
@@ -45,11 +45,11 @@
 
 /// CGA Type 0 Allocation
 static const int CqaType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(CqaFfMacScheduler);
 
@@ -549,7 +549,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 CqaFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -834,9 +834,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -846,7 +846,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -1074,7 +1074,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -1124,7 +1124,7 @@
     if (rbgAllocatedNum == numberOfRBGs)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -1144,12 +1144,12 @@
             // UE already allocated for HARQ or without HARQ process available -> drop it
             if (itRnti != rntiAllocated.end())
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ tx"
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx"
                                   << (uint16_t)(itLogicalChannels->first.m_rnti));
             }
             if (!HarqProcessAvailability(itLogicalChannels->first.m_rnti))
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ id"
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ id"
                                   << (uint16_t)(itLogicalChannels->first.m_rnti));
             }
             continue;
@@ -1337,9 +1337,9 @@
 
     // while there are more resources available, loop through the users that are grouped by HOL
     // value
-    while (availableRBGs.size() > 0)
+    while (!availableRBGs.empty())
     {
-        if (UeToAmountOfDataToTransfer.size() == 0)
+        if (UeToAmountOfDataToTransfer.empty())
         {
             NS_LOG_INFO("No UEs to be scheduled (no data or CQI==0),");
             break;
@@ -1366,7 +1366,7 @@
             break;
         }
 
-        while (availableRBGs.size() > 0 and itCurrentGroup->second.size() > 0)
+        while (!availableRBGs.empty() && !itCurrentGroup->second.empty())
         {
             bool currentRBchecked = false;
             int currentRB = *(availableRBGs.begin());
@@ -1379,7 +1379,7 @@
             UeToCoitaMetric.clear();
 
             // Iterate through the users and calculate which user will use the best of the current
-            // resource bloc.end()k and assign to that user.
+            // resource block.end() and assign to that user.
             for (std::set<LteFlowId_t>::iterator it = itCurrentGroup->second.begin();
                  it != itCurrentGroup->second.end();
                  it++)
@@ -2040,7 +2040,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -2089,7 +2089,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -2187,7 +2188,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -2469,7 +2470,7 @@
                           << " subframe no. " << (0xF & params.m_sfnSf));
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/cqa-ff-mac-scheduler.h ns-3.38/src/lte/model/cqa-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/cqa-ff-mac-scheduler.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/cqa-ff-mac-scheduler.h	2023-03-16 20:54:23.664868600 -0700
@@ -298,9 +298,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-enb-application.h ns-3.38/src/lte/model/epc-enb-application.h
--- ns-3.37/src/lte/model/epc-enb-application.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-enb-application.h	2023-03-16 20:54:23.664868600 -0700
@@ -245,7 +245,7 @@
      * Send a packet to the SGW via the S1-U interface
      *
      * \param packet packet to be sent
-     * \param teid the Tunnel Enpoint IDentifier
+     * \param teid the Tunnel Endpoint IDentifier
      */
     void SendToS1uSocket(Ptr<Packet> packet, uint32_t teid);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-enb-s1-sap.h ns-3.38/src/lte/model/epc-enb-s1-sap.h
--- ns-3.37/src/lte/model/epc-enb-s1-sap.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-enb-s1-sap.h	2023-03-16 20:54:23.664868600 -0700
@@ -163,6 +163,9 @@
      */
     MemberEpcEnbS1SapProvider(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberEpcEnbS1SapProvider() = delete;
+
     // inherited from EpcEnbS1SapProvider
     void InitialUeMessage(uint64_t imsi, uint16_t rnti) override;
     void DoSendReleaseIndication(uint64_t imsi, uint16_t rnti, uint8_t bearerId) override;
@@ -171,7 +174,6 @@
     void UeContextRelease(uint16_t rnti) override;
 
   private:
-    MemberEpcEnbS1SapProvider();
     C* m_owner; ///< owner class
 };
 
@@ -182,11 +184,6 @@
 }
 
 template <class C>
-MemberEpcEnbS1SapProvider<C>::MemberEpcEnbS1SapProvider()
-{
-}
-
-template <class C>
 void
 MemberEpcEnbS1SapProvider<C>::InitialUeMessage(uint64_t imsi, uint16_t rnti)
 {
@@ -231,13 +228,15 @@
      */
     MemberEpcEnbS1SapUser(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberEpcEnbS1SapUser() = delete;
+
     // inherited from EpcEnbS1SapUser
     void InitialContextSetupRequest(InitialContextSetupRequestParameters params) override;
     void DataRadioBearerSetupRequest(DataRadioBearerSetupRequestParameters params) override;
     void PathSwitchRequestAcknowledge(PathSwitchRequestAcknowledgeParameters params) override;
 
   private:
-    MemberEpcEnbS1SapUser();
     C* m_owner; ///< owner class
 };
 
@@ -247,11 +246,6 @@
 {
 }
 
-template <class C>
-MemberEpcEnbS1SapUser<C>::MemberEpcEnbS1SapUser()
-{
-}
-
 template <class C>
 void
 MemberEpcEnbS1SapUser<C>::InitialContextSetupRequest(InitialContextSetupRequestParameters params)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-gtpc-header.cc ns-3.38/src/lte/model/epc-gtpc-header.cc
--- ns-3.37/src/lte/model/epc-gtpc-header.cc	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-gtpc-header.cc	2023-03-16 20:54:23.664868600 -0700
@@ -209,7 +209,7 @@
 }
 
 uint32_t
-GtpcIes::DeserializeImsi(Buffer::Iterator& i, uint64_t& imsi)
+GtpcIes::DeserializeImsi(Buffer::Iterator& i, uint64_t& imsi) const
 {
     uint8_t type = i.ReadU8();
     NS_ASSERT_MSG(type == 1, "Wrong IMSI IE type = " << (uint16_t)type);
@@ -233,7 +233,7 @@
 }
 
 uint32_t
-GtpcIes::DeserializeCause(Buffer::Iterator& i, Cause_t& cause)
+GtpcIes::DeserializeCause(Buffer::Iterator& i, Cause_t& cause) const
 {
     uint8_t type = i.ReadU8();
     NS_ASSERT_MSG(type == 2, "Wrong Cause IE type = " << (uint16_t)type);
@@ -257,7 +257,7 @@
 }
 
 uint32_t
-GtpcIes::DeserializeEbi(Buffer::Iterator& i, uint8_t& epsBearerId)
+GtpcIes::DeserializeEbi(Buffer::Iterator& i, uint8_t& epsBearerId) const
 {
     uint8_t type = i.ReadU8();
     NS_ASSERT_MSG(type == 73, "Wrong EBI IE type = " << (uint16_t)type);
@@ -362,7 +362,7 @@
 }
 
 uint32_t
-GtpcIes::DeserializeBearerTft(Buffer::Iterator& i, Ptr<EpcTft> epcTft)
+GtpcIes::DeserializeBearerTft(Buffer::Iterator& i, Ptr<EpcTft> epcTft) const
 {
     uint8_t type = i.ReadU8();
     NS_ASSERT_MSG(type == 84, "Wrong Bearer TFT IE type = " << (uint16_t)type);
@@ -417,7 +417,7 @@
 }
 
 uint32_t
-GtpcIes::DeserializeUliEcgi(Buffer::Iterator& i, uint32_t& uliEcgi)
+GtpcIes::DeserializeUliEcgi(Buffer::Iterator& i, uint32_t& uliEcgi) const
 {
     uint8_t type = i.ReadU8();
     NS_ASSERT_MSG(type == 86, "Wrong ULI ECGI IE type = " << (uint16_t)type);
@@ -443,7 +443,7 @@
 }
 
 uint32_t
-GtpcIes::DeserializeFteid(Buffer::Iterator& i, GtpcHeader::Fteid_t& fteid)
+GtpcIes::DeserializeFteid(Buffer::Iterator& i, GtpcHeader::Fteid_t& fteid) const
 {
     uint8_t type = i.ReadU8();
     NS_ASSERT_MSG(type == 87, "Wrong FTEID IE type = " << (uint16_t)type);
@@ -468,7 +468,7 @@
 }
 
 uint32_t
-GtpcIes::DeserializeBearerContextHeader(Buffer::Iterator& i, uint16_t& length)
+GtpcIes::DeserializeBearerContextHeader(Buffer::Iterator& i, uint16_t& length) const
 {
     uint8_t type = i.ReadU8();
     NS_ASSERT_MSG(type == 93, "Wrong Bearer Context IE type = " << (uint16_t)type);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-gtpc-header.h ns-3.38/src/lte/model/epc-gtpc-header.h
--- ns-3.37/src/lte/model/epc-gtpc-header.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-gtpc-header.h	2023-03-16 20:54:23.664868600 -0700
@@ -188,7 +188,7 @@
 
 /**
  * \ingroup lte
- * GTP-C Information Elemements
+ * GTP-C Information Elements
  */
 class GtpcIes
 {
@@ -229,7 +229,7 @@
      * \param [out] imsi The IMSI
      * \return the number of deserialized bytes
      */
-    uint32_t DeserializeImsi(Buffer::Iterator& i, uint64_t& imsi);
+    uint32_t DeserializeImsi(Buffer::Iterator& i, uint64_t& imsi) const;
 
     /**
      * Serialize the Cause
@@ -243,7 +243,7 @@
      * \param [out] cause The cause
      * \return the number of deserialized bytes
      */
-    uint32_t DeserializeCause(Buffer::Iterator& i, Cause_t& cause);
+    uint32_t DeserializeCause(Buffer::Iterator& i, Cause_t& cause) const;
 
     /**
      * Serialize the eps Bearer Id
@@ -257,7 +257,7 @@
      * \param [out] epsBearerId The eps Bearer Id
      * \return the number of deserialized bytes
      */
-    uint32_t DeserializeEbi(Buffer::Iterator& i, uint8_t& epsBearerId);
+    uint32_t DeserializeEbi(Buffer::Iterator& i, uint8_t& epsBearerId) const;
 
     /**
      * \param i Buffer iterator
@@ -305,7 +305,7 @@
      * \param [out] epcTft The Bearer TFT
      * \return the number of deserialized bytes
      */
-    uint32_t DeserializeBearerTft(Buffer::Iterator& i, Ptr<EpcTft> epcTft);
+    uint32_t DeserializeBearerTft(Buffer::Iterator& i, Ptr<EpcTft> epcTft) const;
 
     /**
      * Serialize the UliEcgi
@@ -319,7 +319,7 @@
      * \param [out] uliEcgi UliEcgi
      * \return the number of deserialized bytes
      */
-    uint32_t DeserializeUliEcgi(Buffer::Iterator& i, uint32_t& uliEcgi);
+    uint32_t DeserializeUliEcgi(Buffer::Iterator& i, uint32_t& uliEcgi) const;
 
     /**
      * Serialize the Fteid_t
@@ -333,7 +333,7 @@
      * \param [out] fteid Fteid
      * \return the number of deserialized bytes
      */
-    uint32_t DeserializeFteid(Buffer::Iterator& i, GtpcHeader::Fteid_t& fteid);
+    uint32_t DeserializeFteid(Buffer::Iterator& i, GtpcHeader::Fteid_t& fteid) const;
 
     /**
      * Serialize the Bearer Context Header
@@ -347,7 +347,7 @@
      * \param [out] length length
      * \return the number of deserialized bytes
      */
-    uint32_t DeserializeBearerContextHeader(Buffer::Iterator& i, uint16_t& length);
+    uint32_t DeserializeBearerContextHeader(Buffer::Iterator& i, uint16_t& length) const;
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-pgw-application.h ns-3.38/src/lte/model/epc-pgw-application.h
--- ns-3.37/src/lte/model/epc-pgw-application.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-pgw-application.h	2023-03-16 20:54:23.664868600 -0700
@@ -114,7 +114,7 @@
      * Send a data packet to the internet via the SGi interface of the PGW
      *
      * \param packet packet to be sent
-     * \param teid the Tunnel Enpoint Identifier
+     * \param teid the Tunnel Endpoint Identifier
      */
     void SendToTunDevice(Ptr<Packet> packet, uint32_t teid);
 
@@ -123,7 +123,7 @@
      *
      * \param packet packet to be sent
      * \param sgwS5uAddress the address of the SGW
-     * \param teid the Tunnel Enpoint 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.37/src/lte/model/epc-s11-sap.h ns-3.38/src/lte/model/epc-s11-sap.h
--- ns-3.37/src/lte/model/epc-s11-sap.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-s11-sap.h	2023-03-16 20:54:23.664868600 -0700
@@ -135,7 +135,9 @@
             REQUEST_ACCEPTED_PARTIALLY,
             REQUEST_REJECTED,
             CONTEXT_NOT_FOUND
-        } cause; ///< the cause
+        };
+
+        Cause cause; ///< the cause
     };
 
     /**
@@ -257,21 +259,18 @@
      */
     MemberEpcS11SapMme(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberEpcS11SapMme() = delete;
+
     // inherited from EpcS11SapMme
     void CreateSessionResponse(CreateSessionResponseMessage msg) override;
     void ModifyBearerResponse(ModifyBearerResponseMessage msg) override;
     void DeleteBearerRequest(DeleteBearerRequestMessage msg) override;
 
   private:
-    MemberEpcS11SapMme();
     C* m_owner; ///< owner class
 };
 
-/**
- * Constructor
- *
- * \param owner the owner class
- */
 template <class C>
 MemberEpcS11SapMme<C>::MemberEpcS11SapMme(C* owner)
     : m_owner(owner)
@@ -279,11 +278,6 @@
 }
 
 template <class C>
-MemberEpcS11SapMme<C>::MemberEpcS11SapMme()
-{
-}
-
-template <class C>
 void
 MemberEpcS11SapMme<C>::CreateSessionResponse(CreateSessionResponseMessage msg)
 {
@@ -319,6 +313,9 @@
      */
     MemberEpcS11SapSgw(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberEpcS11SapSgw() = delete;
+
     // inherited from EpcS11SapSgw
     void CreateSessionRequest(CreateSessionRequestMessage msg) override;
     void ModifyBearerRequest(ModifyBearerRequestMessage msg) override;
@@ -326,26 +323,15 @@
     void DeleteBearerResponse(DeleteBearerResponseMessage msg) override;
 
   private:
-    MemberEpcS11SapSgw();
     C* m_owner; ///< owner class
 };
 
-/**
- * Constructor
- *
- * \param owner the owner class
- */
 template <class C>
 MemberEpcS11SapSgw<C>::MemberEpcS11SapSgw(C* owner)
     : m_owner(owner)
 {
 }
 
-template <class C>
-MemberEpcS11SapSgw<C>::MemberEpcS11SapSgw()
-{
-}
-
 template <class C>
 void
 MemberEpcS11SapSgw<C>::CreateSessionRequest(CreateSessionRequestMessage msg)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-s1ap-sap.h ns-3.38/src/lte/model/epc-s1ap-sap.h
--- ns-3.37/src/lte/model/epc-s1ap-sap.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-s1ap-sap.h	2023-03-16 20:54:23.664868600 -0700
@@ -202,6 +202,9 @@
      */
     MemberEpcS1apSapMme(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberEpcS1apSapMme() = delete;
+
     // inherited from EpcS1apSapMme
     /**
      * Initial UE Message function
@@ -248,7 +251,6 @@
         std::list<ErabSwitchedInDownlinkItem> erabToBeSwitchedInDownlinkList) override;
 
   private:
-    MemberEpcS1apSapMme();
     C* m_owner; ///< owner class
 };
 
@@ -259,11 +261,6 @@
 }
 
 template <class C>
-MemberEpcS1apSapMme<C>::MemberEpcS1apSapMme()
-{
-}
-
-template <class C>
 void
 MemberEpcS1apSapMme<C>::InitialUeMessage(uint64_t mmeUeS1Id,
                                          uint16_t enbUeS1Id,
@@ -318,6 +315,9 @@
      */
     MemberEpcS1apSapEnb(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberEpcS1apSapEnb() = delete;
+
     // inherited from EpcS1apSapEnb
     /**
      * Initial context setup request function
@@ -342,7 +342,6 @@
         std::list<ErabSwitchedInUplinkItem> erabToBeSwitchedInUplinkList) override;
 
   private:
-    MemberEpcS1apSapEnb();
     C* m_owner; ///< owner class
 };
 
@@ -352,11 +351,6 @@
 {
 }
 
-template <class C>
-MemberEpcS1apSapEnb<C>::MemberEpcS1apSapEnb()
-{
-}
-
 template <class C>
 void
 MemberEpcS1apSapEnb<C>::InitialContextSetupRequest(uint64_t mmeUeS1Id,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-sgw-application.h ns-3.38/src/lte/model/epc-sgw-application.h
--- ns-3.37/src/lte/model/epc-sgw-application.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-sgw-application.h	2023-03-16 20:54:23.664868600 -0700
@@ -135,7 +135,7 @@
      *
      * \param packet packet to be sent
      * \param pgwAddr the address of the PGW
-     * \param teid the Tunnel Enpoint Identifier
+     * \param teid the Tunnel Endpoint Identifier
      */
     void SendToS5uSocket(Ptr<Packet> packet, Ipv4Address pgwAddr, uint32_t teid);
 
@@ -144,7 +144,7 @@
      *
      * \param packet packet to be sent
      * \param enbS1uAddress the address of the eNB
-     * \param teid the Tunnel Enpoint 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.37/src/lte/model/epc-ue-nas.cc ns-3.38/src/lte/model/epc-ue-nas.cc
--- ns-3.37/src/lte/model/epc-ue-nas.cc	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-ue-nas.cc	2023-03-16 20:54:23.664868600 -0700
@@ -32,11 +32,13 @@
 NS_LOG_COMPONENT_DEFINE("EpcUeNas");
 
 /// Map each of UE NAS states to its string representation.
-static const std::string g_ueNasStateName[EpcUeNas::NUM_STATES] = {"OFF",
-                                                                   "ATTACHING",
-                                                                   "IDLE_REGISTERED",
-                                                                   "CONNECTING_TO_EPC",
-                                                                   "ACTIVE"};
+static const std::string g_ueNasStateName[EpcUeNas::NUM_STATES] = {
+    "OFF",
+    "ATTACHING",
+    "IDLE_REGISTERED",
+    "CONNECTING_TO_EPC",
+    "ACTIVE",
+};
 
 /**
  * \param s The UE NAS state.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-x2-header.h ns-3.38/src/lte/model/epc-x2-header.h
--- ns-3.37/src/lte/model/epc-x2-header.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-x2-header.h	2023-03-16 20:54:23.664868600 -0700
@@ -186,18 +186,18 @@
      */
     uint64_t GetUeAggregateMaxBitRateDownlink() const;
     /**
-     * Set UE Aggregrate Max Bit Rate Downlink function
+     * Set UE Aggregate Max Bit Rate Downlink function
      * \param bitRate the bit rate
      */
     void SetUeAggregateMaxBitRateDownlink(uint64_t bitRate);
 
     /**
-     * Get UE Aggregrate Max Bit Rate Uplik function
+     * Get UE Aggregate Max Bit Rate Uplik function
      * \returns the UE aggregate max bit rate uplink
      */
     uint64_t GetUeAggregateMaxBitRateUplink() const;
     /**
-     * Set UE Aggregrate Max Bit Rate Uplik function
+     * Set UE Aggregate Max Bit Rate Uplik function
      * \param bitRate the bit rate
      */
     void SetUeAggregateMaxBitRateUplink(uint64_t bitRate);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/epc-x2-sap.h ns-3.38/src/lte/model/epc-x2-sap.h
--- ns-3.37/src/lte/model/epc-x2-sap.h	2022-11-01 17:37:24.547395200 -0700
+++ ns-3.38/src/lte/model/epc-x2-sap.h	2023-03-16 20:54:23.664868600 -0700
@@ -232,8 +232,8 @@
         uint16_t sourceCellId;                  ///< source cell ID
         uint16_t targetCellId;                  ///< target cell ID
         uint32_t mmeUeS1apId;                   ///< MME UE S1 AP ID
-        uint64_t ueAggregateMaxBitRateDownlink; ///< UE aggregrate max bit rate downlink
-        uint64_t ueAggregateMaxBitRateUplink;   ///< UE aggregrate max bit rate uplink
+        uint64_t ueAggregateMaxBitRateDownlink; ///< UE aggregate max bit rate downlink
+        uint64_t ueAggregateMaxBitRateUplink;   ///< UE aggregate max bit rate uplink
         std::vector<ErabToBeSetupItem> bearers; ///< bearers
         Ptr<Packet> rrcContext;                 ///< RRC context
     };
@@ -503,6 +503,9 @@
      */
     EpcX2SpecificEpcX2SapProvider(C* x2);
 
+    // Delete default constructor to avoid misuse
+    EpcX2SpecificEpcX2SapProvider() = delete;
+
     //
     // Interface implemented from EpcX2SapProvider
     //
@@ -562,7 +565,6 @@
     void SendHandoverCancel(HandoverCancelParams params) override;
 
   private:
-    EpcX2SpecificEpcX2SapProvider();
     C* m_x2; ///< owner class
 };
 
@@ -573,11 +575,6 @@
 }
 
 template <class C>
-EpcX2SpecificEpcX2SapProvider<C>::EpcX2SpecificEpcX2SapProvider()
-{
-}
-
-template <class C>
 void
 EpcX2SpecificEpcX2SapProvider<C>::SendHandoverRequest(HandoverRequestParams params)
 {
@@ -655,6 +652,9 @@
      */
     EpcX2SpecificEpcX2SapUser(C* rrc);
 
+    // Delete default constructor to avoid misuse
+    EpcX2SpecificEpcX2SapUser() = delete;
+
     //
     // Interface implemented from EpcX2SapUser
     //
@@ -715,7 +715,6 @@
     void RecvHandoverCancel(HandoverCancelParams params) override;
 
   private:
-    EpcX2SpecificEpcX2SapUser();
     C* m_rrc; ///< owner class
 };
 
@@ -725,11 +724,6 @@
 {
 }
 
-template <class C>
-EpcX2SpecificEpcX2SapUser<C>::EpcX2SpecificEpcX2SapUser()
-{
-}
-
 template <class C>
 void
 EpcX2SpecificEpcX2SapUser<C>::RecvHandoverRequest(HandoverRequestParams params)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/eps-bearer.cc ns-3.38/src/lte/model/eps-bearer.cc
--- ns-3.37/src/lte/model/eps-bearer.cc	2022-11-01 17:37:24.551395200 -0700
+++ ns-3.38/src/lte/model/eps-bearer.cc	2023-03-16 20:54:23.664868600 -0700
@@ -81,7 +81,7 @@
     ObjectBase::ConstructSelf(AttributeConstructionList());
 }
 
-EpsBearer::EpsBearer(Qci x, struct GbrQosInformation y)
+EpsBearer::EpsBearer(Qci x, GbrQosInformation y)
     : ObjectBase(),
       qci(x),
       gbrQosInfo(y)
@@ -158,7 +158,7 @@
      */
     static EpsBearer::BearerRequirementsMap ret;
 
-    if (ret.size() == 0)
+    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)));
@@ -183,7 +183,7 @@
     // Needed to support GCC 4.9. Otherwise, use list constructors (see GetRequirementsRel10)
     static EpsBearer::BearerRequirementsMap ret;
 
-    if (ret.size() == 0)
+    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)));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/eps-bearer.h ns-3.38/src/lte/model/eps-bearer.h
--- ns-3.37/src/lte/model/eps-bearer.h	2022-11-01 17:37:24.551395200 -0700
+++ ns-3.38/src/lte/model/eps-bearer.h	2023-03-16 20:54:23.664868600 -0700
@@ -132,7 +132,9 @@
         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)
-    } qci;      ///< Qos class indicator
+    };
+
+    Qci qci; ///< Qos class indicator
 
     GbrQosInformation gbrQosInfo;    ///< GBR QOS information
     AllocationRetentionPriority arp; ///< allocation retention priority
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/fading-traces/fading_trace_generator.m ns-3.38/src/lte/model/fading-traces/fading_trace_generator.m
--- ns-3.37/src/lte/model/fading-traces/fading_trace_generator.m	2022-11-01 17:37:24.651395600 -0700
+++ ns-3.38/src/lte/model/fading-traces/fading_trace_generator.m	2023-03-16 20:54:23.768869000 -0700
@@ -8,7 +8,7 @@
 delays_urbanETU = [0 50e-9 120e-9 200e-9 230e-9 500e-9 1600e-9 2300e-9 5000e-9];
 Td = 100/c;
 
-% Realtive power of taps (according to 3GPP TS 36.104 Annex B.2)
+% Relative power of taps (according to 3GPP TS 36.104 Annex B.2)
 power_pedestrianEPA = [0.0 -1.0 -2.0 -3.0 -8.0 -17.2 -20.8];
 power_vehicularEVA = [0.0 -1.5 -1.4 -3.6 -0.6 -9.1 -7.0 -12.0 -16.9];
 power_urbanETU = [-1.0 -1.0 -1.0 0.0 0.0 0.0 -3.0 -5.0 -7.0];
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/fdbet-ff-mac-scheduler.cc ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/fdbet-ff-mac-scheduler.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.768869000 -0700
@@ -37,11 +37,11 @@
 
 /// FdBetType0AllocationRbg array
 static const int FdBetType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(FdBetFfMacScheduler);
 
@@ -369,7 +369,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 FdBetFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -598,9 +598,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -610,7 +610,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -838,7 +838,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -888,7 +888,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -909,11 +909,11 @@
             // UE already allocated for HARQ or without HARQ process available -> drop it
             if (itRnti != rntiAllocated.end())
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*itFlow).first);
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*itFlow).first);
             }
             if (!HarqProcessAvailability((*itFlow).first))
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*itFlow).first);
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*itFlow).first);
             }
             continue;
         }
@@ -952,7 +952,7 @@
         }
     }
 
-    if (estAveThr.size() != 0)
+    if (!estAveThr.empty())
     {
         // Find UE with largest priority metric
         for (it = estAveThr.begin(); it != estAveThr.end(); it++)
@@ -1460,7 +1460,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1506,7 +1506,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1577,7 +1578,7 @@
         {
             // unable to allocate new resource: finish scheduling
             m_nextRntiUl = (*it).first;
-            if (ret.m_dciList.size() > 0)
+            if (!ret.m_dciList.empty())
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
@@ -1596,7 +1597,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -1876,7 +1877,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/fdbet-ff-mac-scheduler.h ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/fdbet-ff-mac-scheduler.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.h	2023-03-16 20:54:23.768869000 -0700
@@ -290,9 +290,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/fdmt-ff-mac-scheduler.cc ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/fdmt-ff-mac-scheduler.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.768869000 -0700
@@ -37,11 +37,11 @@
 
 /// FdMtType0AllocationRbg size array
 static const int FdMtType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(FdMtFfMacScheduler);
 
@@ -358,7 +358,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 FdMtFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -587,9 +587,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -599,7 +599,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -827,7 +827,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -877,7 +877,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -900,11 +900,11 @@
                     // UE already allocated for HARQ or without HARQ process available -> drop it
                     if (itRnti != rntiAllocated.end())
                     {
-                        NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it));
+                        NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it));
                     }
                     if (!HarqProcessAvailability((*it)))
                     {
-                        NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it));
+                        NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it));
                     }
                     continue;
                 }
@@ -1420,7 +1420,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1465,7 +1465,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1536,7 +1537,7 @@
         {
             // unable to allocate new resource: finish scheduling
             m_nextRntiUl = (*it).first;
-            if (ret.m_dciList.size() > 0)
+            if (!ret.m_dciList.empty())
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
@@ -1555,7 +1556,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -1810,7 +1811,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/fdmt-ff-mac-scheduler.h ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/fdmt-ff-mac-scheduler.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.h	2023-03-16 20:54:23.768869000 -0700
@@ -269,7 +269,7 @@
      */
     void UpdateDlRlcBufferInfo(uint16_t rnti, uint8_t lcid, uint16_t size);
     /**
-     * Update UL RLC b uffer info function
+     * Update UL RLC buffer info function
      * \param rnti the RNTI
      * \param size the size
      */
@@ -287,9 +287,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/fdtbfq-ff-mac-scheduler.cc ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/fdtbfq-ff-mac-scheduler.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.768869000 -0700
@@ -38,11 +38,11 @@
 
 /// FdTbfqType0AllocationRbg value array
 static const int FdTbfqType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(FdTbfqFfMacScheduler);
 
@@ -423,7 +423,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 FdTbfqFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -700,9 +700,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -712,7 +712,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -940,7 +940,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -990,7 +990,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -1036,11 +1036,11 @@
                 // UE already allocated for HARQ or without HARQ process available -> drop it
                 if (itRnti != rntiAllocated.end())
                 {
-                    NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it).first);
+                    NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it).first);
                 }
                 if (!HarqProcessAvailability((*it).first))
                 {
-                    NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it).first);
+                    NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it).first);
                 }
                 continue;
             }
@@ -1132,7 +1132,7 @@
         }
         budget = budget + (*itMax).second.tokenPoolSize;
 
-        // calculate how much bytes this UE actally need
+        // calculate how much bytes this UE actually need
         if (budget == 0)
         {
             // there are no tokens for this UE
@@ -1368,7 +1368,7 @@
             rbgMap.at(rbgIndex) = false; // unmark this RBG
             // If all the RBGs are removed from the allocation
             // of this RNTI, we remove the UE from the allocation map
-            if ((*itMap).second.size() == 0)
+            if ((*itMap).second.empty())
             {
                 itMap = allocationMap.erase(itMap);
             }
@@ -1837,7 +1837,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1886,7 +1886,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1984,7 +1985,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -2239,7 +2240,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/fdtbfq-ff-mac-scheduler.h ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/fdtbfq-ff-mac-scheduler.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.h	2023-03-16 20:54:23.768869000 -0700
@@ -328,9 +328,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/ff-mac-common.h ns-3.38/src/lte/model/ff-mac-common.h
--- ns-3.37/src/lte/model/ff-mac-common.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/ff-mac-common.h	2023-03-16 20:54:23.768869000 -0700
@@ -182,7 +182,7 @@
 };
 
 /**
- * \brief See section 4.3.3 vendorSpecifiListElement
+ * \brief See section 4.3.3 vendorSpecificListElement
  * \struct VendorSpecificListElement_s
  */
 struct VendorSpecificListElement_s
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/ff-mac-csched-sap.h ns-3.38/src/lte/model/ff-mac-csched-sap.h
--- ns-3.37/src/lte/model/ff-mac-csched-sap.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/ff-mac-csched-sap.h	2023-03-16 20:54:23.768869000 -0700
@@ -470,6 +470,9 @@
      */
     MemberCschedSapProvider(C* scheduler);
 
+    // Delete default constructor to avoid misuse
+    MemberCschedSapProvider() = delete;
+
     // inherited from FfMacCschedSapProvider
     void CschedCellConfigReq(const struct CschedCellConfigReqParameters& params) override;
     void CschedUeConfigReq(const struct CschedUeConfigReqParameters& params) override;
@@ -478,16 +481,10 @@
     void CschedUeReleaseReq(const struct CschedUeReleaseReqParameters& params) override;
 
   private:
-    MemberCschedSapProvider();
     C* m_scheduler; ///< scheduler class
 };
 
 template <class C>
-MemberCschedSapProvider<C>::MemberCschedSapProvider()
-{
-}
-
-template <class C>
 MemberCschedSapProvider<C>::MemberCschedSapProvider(C* scheduler)
     : m_scheduler(scheduler)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/ff-mac-sched-sap.h ns-3.38/src/lte/model/ff-mac-sched-sap.h
--- ns-3.37/src/lte/model/ff-mac-sched-sap.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/ff-mac-sched-sap.h	2023-03-16 20:54:23.772869000 -0700
@@ -360,6 +360,9 @@
      */
     MemberSchedSapProvider(C* scheduler);
 
+    // Delete default constructor to avoid misuse
+    MemberSchedSapProvider() = delete;
+
     // inherited from FfMacSchedSapProvider
     void SchedDlRlcBufferReq(const struct SchedDlRlcBufferReqParameters& params) override;
     void SchedDlPagingBufferReq(const struct SchedDlPagingBufferReqParameters& params) override;
@@ -375,16 +378,10 @@
     void SchedUlCqiInfoReq(const struct SchedUlCqiInfoReqParameters& params) override;
 
   private:
-    MemberSchedSapProvider();
     C* m_scheduler; ///< the scheduler class
 };
 
 template <class C>
-MemberSchedSapProvider<C>::MemberSchedSapProvider()
-{
-}
-
-template <class C>
 MemberSchedSapProvider<C>::MemberSchedSapProvider(C* scheduler)
     : m_scheduler(scheduler)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-amc.cc ns-3.38/src/lte/model/lte-amc.cc
--- ns-3.37/src/lte/model/lte-amc.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-amc.cc	2023-03-16 20:54:23.772869000 -0700
@@ -44,22 +44,24 @@
  * file `TBS_support.xls` tab "MCS Table" (rounded to 2 decimal digits).
  * The index of the vector (range 0-15) identifies the CQI value.
  */
-static const double SpectralEfficiencyForCqi[16] = {0.0, // out of range
-                                                    0.15,
-                                                    0.23,
-                                                    0.38,
-                                                    0.6,
-                                                    0.88,
-                                                    1.18,
-                                                    1.48,
-                                                    1.91,
-                                                    2.41,
-                                                    2.73,
-                                                    3.32,
-                                                    3.9,
-                                                    4.52,
-                                                    5.12,
-                                                    5.55};
+static const double SpectralEfficiencyForCqi[16] = {
+    0.0, // out of range
+    0.15,
+    0.23,
+    0.38,
+    0.6,
+    0.88,
+    1.18,
+    1.48,
+    1.91,
+    2.41,
+    2.73,
+    3.32,
+    3.9,
+    4.52,
+    5.12,
+    5.55,
+};
 
 #if 0 // currently unused
 /**
@@ -91,23 +93,28 @@
  */
 static const double SpectralEfficiencyForMcs[32] = {
     0.15, 0.19, 0.23, 0.31, 0.38, 0.49, 0.6, 0.74, 0.88, 1.03, 1.18, 1.33, 1.48, 1.7, 1.91, 2.16,
-    2.41, 2.57, 2.73, 3.03, 3.32, 3.61, 3.9, 4.21, 4.52, 4.82, 5.12, 5.33, 5.55, 0,   0,    0};
+    2.41, 2.57, 2.73, 3.03, 3.32, 3.61, 3.9, 4.21, 4.52, 4.82, 5.12, 5.33, 5.55, 0,   0,    0,
+};
 
 /**
  * Table of MCS index (IMCS) and its TBS index (ITBS). Taken from 3GPP TS
  * 36.213 v8.8.0 Table 7.1.7.1-1: _Modulation and TBS index table for PDSCH_.
  * The index of the vector (range 0-28) identifies the MCS index.
  */
-static const int McsToItbsDl[29] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  9,  10, 11, 12, 13,
-                                    14, 15, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26};
+static const int McsToItbsDl[29] = {
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  9,  10, 11, 12, 13,
+    14, 15, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+};
 
 /**
  * Table of MCS index (IMCS) and its TBS index (ITBS). Taken from 3GPP TS
  * 36.213 v8.8.0 Table 8.6.1-1: _Modulation, TBS index and redundancy version table for PUSCH_.
  * The index of the vector (range 0-28) identifies the MCS index.
  */
-static const int McsToItbsUl[29] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 10, 11, 12, 13,
-                                    14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26};
+static const int McsToItbsUl[29] = {
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 10, 11, 12, 13,
+    14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26,
+};
 
 /**
  * Table of number of physical resource blocks (NPRB), TBS index (ITBS), and
@@ -444,8 +451,7 @@
                    43816, 46888, 51024, 55056, 59256, 61664, 66592, 68808, 75376},
     /* NPRB 110*/ {3112,  4008,  4968,  6456,  7992,  9528,  11448, 13536, 15264,
                    17568, 19080, 22152, 25456, 28336, 31704, 34008, 35160, 39232,
-                   43816, 46888, 51024, 55056, 59256, 63776, 66592, 71112, 75376}
-
+                   43816, 46888, 51024, 55056, 59256, 63776, 66592, 71112, 75376},
 };
 
 LteAmc::LteAmc()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-as-sap.h ns-3.38/src/lte/model/lte-as-sap.h
--- ns-3.37/src/lte/model/lte-as-sap.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-as-sap.h	2023-03-16 20:54:23.772869000 -0700
@@ -137,6 +137,9 @@
      */
     MemberLteAsSapProvider(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteAsSapProvider() = delete;
+
     // inherited from LteAsSapProvider
     void SetCsgWhiteList(uint32_t csgId) override;
     void StartCellSelection(uint32_t dlEarfcn) override;
@@ -146,7 +149,6 @@
     void Disconnect() override;
 
   private:
-    MemberLteAsSapProvider();
     C* m_owner; ///< the owner class
 };
 
@@ -157,11 +159,6 @@
 }
 
 template <class C>
-MemberLteAsSapProvider<C>::MemberLteAsSapProvider()
-{
-}
-
-template <class C>
 void
 MemberLteAsSapProvider<C>::SetCsgWhiteList(uint32_t csgId)
 {
@@ -218,6 +215,9 @@
      */
     MemberLteAsSapUser(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteAsSapUser() = delete;
+
     // inherited from LteAsSapUser
     void NotifyConnectionSuccessful() override;
     void NotifyConnectionFailed() override;
@@ -225,7 +225,6 @@
     void NotifyConnectionReleased() override;
 
   private:
-    MemberLteAsSapUser();
     C* m_owner; ///< the owner class
 };
 
@@ -235,11 +234,6 @@
 {
 }
 
-template <class C>
-MemberLteAsSapUser<C>::MemberLteAsSapUser()
-{
-}
-
 template <class C>
 void
 MemberLteAsSapUser<C>::NotifyConnectionSuccessful()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ccm-rrc-sap.h ns-3.38/src/lte/model/lte-ccm-rrc-sap.h
--- ns-3.37/src/lte/model/lte-ccm-rrc-sap.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-ccm-rrc-sap.h	2023-03-16 20:54:23.772869000 -0700
@@ -105,7 +105,7 @@
      * \param lcid the Logical Channel id
      * \param lcGroup the Logical Channel group
      * \param msu a pointer to the LteMacSapUser, the LteEnbComponentCarrierManager
-     *             has to store a LteMacSapUser for each Rlc istance, in order to
+     *             has to store a LteMacSapUser for each Rlc instance, in order to
      *             properly redirect the packet
      * \return vector of LcsConfig contains the lc configuration for each Mac
      *                the size of the vector is equal to the number of component
@@ -136,7 +136,7 @@
     /**
      * \brief Add the Signal Bearer for a specific Ue in LteEnbComponenCarrierManager
      * \param lcInfo this structure it is hard-coded in the LteEnbRrc
-     * \param rlcMacSapUser it is the MacSapUser of the Rlc istance
+     * \param rlcMacSapUser it is the MacSapUser of the Rlc instance
      * \return the LteMacSapUser of the ComponentCarrierManager
      *
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-common.cc ns-3.38/src/lte/model/lte-common.cc
--- ns-3.37/src/lte/model/lte-common.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-common.cc	2023-03-16 20:54:23.772869000 -0700
@@ -75,7 +75,7 @@
 }
 
 /**
- * Equaity operator
+ * Equality operator
  *
  * \param a lhs
  * \param b rhs
@@ -165,13 +165,11 @@
 
 /// Buffer size level BSR table
 static const uint32_t BufferSizeLevelBsrTable[64] = {
-
-    0,     10,    12,    14,    17,    19,    22,    26,    31,     36,     42,     49,    57,
-    67,    78,    91,    107,   125,   146,   171,   200,   234,    274,    321,    376,   440,
-    515,   603,   706,   826,   967,   1132,  1326,  1552,  1817,   2127,   2490,   2915,  3413,
-    3995,  4677,  5476,  6411,  7505,  8787,  10287, 12043, 14099,  16507,  19325,  22624, 26487,
-    31009, 36304, 42502, 49759, 58255, 68201, 79846, 93749, 109439, 128125, 150000, 150000
-
+    0,     10,    12,    14,    17,    19,    22,    26,    31,     36,     42,     49,     57,
+    67,    78,    91,    107,   125,   146,   171,   200,   234,    274,    321,    376,    440,
+    515,   603,   706,   826,   967,   1132,  1326,  1552,  1817,   2127,   2490,   2915,   3413,
+    3995,  4677,  5476,  6411,  7505,  8787,  10287, 12043, 14099,  16507,  19325,  22624,  26487,
+    31009, 36304, 42502, 49759, 58255, 68201, 79846, 93749, 109439, 128125, 150000, 150000,
 };
 
 uint32_t
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-control-messages.cc ns-3.38/src/lte/model/lte-control-messages.cc
--- ns-3.37/src/lte/model/lte-control-messages.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-control-messages.cc	2023-03-16 20:54:23.772869000 -0700
@@ -69,7 +69,7 @@
     m_dci = dci;
 }
 
-DlDciListElement_s
+const DlDciListElement_s&
 DlDciLteControlMessage::GetDci()
 {
     return m_dci;
@@ -92,7 +92,7 @@
     m_dci = dci;
 }
 
-UlDciListElement_s
+const UlDciListElement_s&
 UlDciLteControlMessage::GetDci()
 {
     return m_dci;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-control-messages.h ns-3.38/src/lte/model/lte-control-messages.h
--- ns-3.37/src/lte/model/lte-control-messages.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-control-messages.h	2023-03-16 20:54:23.772869000 -0700
@@ -106,7 +106,7 @@
      * \brief Get dic information
      * \return dci messages
      */
-    DlDciListElement_s GetDci();
+    const DlDciListElement_s& GetDci();
 
   private:
     DlDciListElement_s m_dci; ///< DCI
@@ -135,7 +135,7 @@
      * \brief Get dic information
      * \return dci messages
      */
-    UlDciListElement_s GetDci();
+    const UlDciListElement_s& GetDci();
 
   private:
     UlDciListElement_s m_dci; ///< DCI
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-enb-cphy-sap.h ns-3.38/src/lte/model/lte-enb-cphy-sap.h
--- ns-3.37/src/lte/model/lte-enb-cphy-sap.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-enb-cphy-sap.h	2023-03-16 20:54:23.772869000 -0700
@@ -158,6 +158,9 @@
      */
     MemberLteEnbCphySapProvider(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteEnbCphySapProvider() = delete;
+
     // inherited from LteEnbCphySapProvider
     void SetCellId(uint16_t cellId) override;
     void SetBandwidth(uint16_t ulBandwidth, uint16_t dlBandwidth) override;
@@ -172,7 +175,6 @@
     int8_t GetReferenceSignalPower() override;
 
   private:
-    MemberLteEnbCphySapProvider();
     C* m_owner; ///< the owner class
 };
 
@@ -183,11 +185,6 @@
 }
 
 template <class C>
-MemberLteEnbCphySapProvider<C>::MemberLteEnbCphySapProvider()
-{
-}
-
-template <class C>
 void
 MemberLteEnbCphySapProvider<C>::SetCellId(uint16_t cellId)
 {
@@ -280,10 +277,12 @@
      */
     MemberLteEnbCphySapUser(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteEnbCphySapUser() = delete;
+
     // methods inherited from LteEnbCphySapUser go here
 
   private:
-    MemberLteEnbCphySapUser();
     C* m_owner; ///< the owner class
 };
 
@@ -293,11 +292,6 @@
 {
 }
 
-template <class C>
-MemberLteEnbCphySapUser<C>::MemberLteEnbCphySapUser()
-{
-}
-
 } // namespace ns3
 
 #endif // LTE_ENB_CPHY_SAP_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-enb-mac.cc ns-3.38/src/lte/model/lte-enb-mac.cc
--- ns-3.37/src/lte/model/lte-enb-mac.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-enb-mac.cc	2023-03-16 20:54:23.772869000 -0700
@@ -490,7 +490,7 @@
 
     // --- DOWNLINK ---
     // Send Dl-CQI info to the scheduler
-    if (m_dlCqiReceived.size() > 0)
+    if (!m_dlCqiReceived.empty())
     {
         FfMacSchedSapProvider::SchedDlCqiInfoReqParameters dlcqiInfoReq;
         dlcqiInfoReq.m_sfnSf = ((0x3FF & frameNo) << 4) | (0xF & subframeNo);
@@ -564,7 +564,7 @@
     dlparams.m_sfnSf = ((0x3FF & dlSchedFrameNo) << 4) | (0xF & dlSchedSubframeNo);
 
     // Forward DL HARQ feebacks collected during last TTI
-    if (m_dlInfoListReceived.size() > 0)
+    if (!m_dlInfoListReceived.empty())
     {
         dlparams.m_dlInfoList = m_dlInfoListReceived;
         // empty local buffer
@@ -590,7 +590,7 @@
     m_ulCqiReceived.clear();
 
     // Send BSR reports to the scheduler
-    if (m_ulCeReceived.size() > 0)
+    if (!m_ulCeReceived.empty())
     {
         FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters ulMacReq;
         ulMacReq.m_sfnSf = ((0x3FF & frameNo) << 4) | (0xF & subframeNo);
@@ -618,7 +618,7 @@
     ulparams.m_sfnSf = ((0x3FF & ulSchedFrameNo) << 4) | (0xF & ulSchedSubframeNo);
 
     // Forward DL HARQ feebacks collected during last TTI
-    if (m_ulInfoListReceived.size() > 0)
+    if (!m_ulInfoListReceived.empty())
     {
         ulparams.m_ulInfoList = m_ulInfoListReceived;
         // empty local buffer
@@ -950,7 +950,7 @@
 }
 
 LteEnbCmacSapProvider::RachConfig
-LteEnbMac::DoGetRachConfig()
+LteEnbMac::DoGetRachConfig() const
 {
     struct LteEnbCmacSapProvider::RachConfig rc;
     rc.numberOfRaPreambles = m_numberOfRaPreambles;
@@ -1209,7 +1209,7 @@
         NS_LOG_INFO(this << " Send RAR message to RNTI " << ind.m_buildRarList.at(i).m_rnti
                          << " rapId " << itRapId->second);
     }
-    if (ind.m_buildRarList.size() > 0)
+    if (!ind.m_buildRarList.empty())
     {
         m_enbPhySapProvider->SendLteControlMessage(rarMsg);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-enb-mac.h ns-3.38/src/lte/model/lte-enb-mac.h
--- ns-3.37/src/lte/model/lte-enb-mac.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-enb-mac.h	2023-03-16 20:54:23.772869000 -0700
@@ -253,7 +253,7 @@
      * \brief Get RACH configuration function
      * \returns LteEnbCmacSapProvider::RachConfig
      */
-    LteEnbCmacSapProvider::RachConfig DoGetRachConfig();
+    LteEnbCmacSapProvider::RachConfig DoGetRachConfig() const;
     /**
      * \brief Allocate NC RA preamble function
      * \param rnti the RNTI
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-enb-phy.cc ns-3.38/src/lte/model/lte-enb-phy.cc
--- ns-3.37/src/lte/model/lte-enb-phy.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-enb-phy.cc	2023-03-16 20:54:23.772869000 -0700
@@ -677,7 +677,7 @@
     std::list<Ptr<LteControlMessage>> ctrlMsg = GetControlMessages();
     m_dlDataRbMap.clear();
     m_dlPowerAllocationMap.clear();
-    if (ctrlMsg.size() > 0)
+    if (!ctrlMsg.empty())
     {
         std::list<Ptr<LteControlMessage>>::iterator it;
         it = ctrlMsg.begin();
@@ -706,7 +706,7 @@
                     mask = (mask << 1);
                 }
                 // fire trace of DL Tx PHY stats
-                for (uint8_t i = 0; i < dci->GetDci().m_mcs.size(); i++)
+                for (std::size_t i = 0; i < dci->GetDci().m_mcs.size(); i++)
                 {
                     PhyTransmissionStatParameters params;
                     params.m_cellId = m_cellId;
@@ -897,11 +897,11 @@
     m_dlBandwidth = dlBandwidth;
 
     static const int Type0AllocationRbg[4] = {
-        10, // RGB size 1
-        26, // RGB size 2
-        63, // RGB size 3
-        110 // RGB size 4
-    };      // see table 7.1.6.1-1 of 36.213
+        10,  // RGB size 1
+        26,  // RGB size 2
+        63,  // RGB size 3
+        110, // RGB size 4
+    };       // see table 7.1.6.1-1 of 36.213
     for (int i = 0; i < 4; i++)
     {
         if (dlBandwidth < Type0AllocationRbg[i])
@@ -1086,7 +1086,7 @@
 LteEnbPhy::DequeueUlDci()
 {
     NS_LOG_FUNCTION(this);
-    if (m_ulDciQueue.at(0).size() > 0)
+    if (!m_ulDciQueue.at(0).empty())
     {
         std::list<UlDciLteControlMessage> ret = m_ulDciQueue.at(0);
         m_ulDciQueue.erase(m_ulDciQueue.begin());
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-enb-phy-sap.h ns-3.38/src/lte/model/lte-enb-phy-sap.h
--- ns-3.37/src/lte/model/lte-enb-phy-sap.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-enb-phy-sap.h	2023-03-16 20:54:23.772869000 -0700
@@ -105,14 +105,14 @@
     virtual void UlCqiReport(FfMacSchedSapProvider::SchedUlCqiInfoReqParameters ulcqi) = 0;
 
     /**
-     * Notify the HARQ on the UL tranmission status
+     * Notify the HARQ on the UL transmission status
      *
      * \param params
      */
     virtual void UlInfoListElementHarqFeeback(UlInfoListElement_s params) = 0;
 
     /**
-     * Notify the HARQ on the DL tranmission status
+     * Notify the HARQ on the DL transmission status
      *
      * \param params
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-enb-rrc.cc ns-3.38/src/lte/model/lte-enb-rrc.cc
--- ns-3.37/src/lte/model/lte-enb-rrc.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-enb-rrc.cc	2023-03-16 20:54:23.772869000 -0700
@@ -1392,7 +1392,7 @@
         m_rrc->m_anrSapProvider->ReportUeMeas(msg.measResults);
     }
 
-    if ((m_rrc->m_ffrRrcSapProvider.size() > 0) &&
+    if ((!m_rrc->m_ffrRrcSapProvider.empty()) &&
         (m_rrc->m_ffrMeasIds.find(measId) != m_rrc->m_ffrMeasIds.end()))
     {
         // this measurement was requested by the FFR function
@@ -2224,7 +2224,7 @@
 LteEnbRrc::SetLteFfrRrcSapProvider(LteFfrRrcSapProvider* s)
 {
     NS_LOG_FUNCTION(this << s);
-    if (m_ffrRrcSapProvider.size() > 0)
+    if (!m_ffrRrcSapProvider.empty())
     {
         m_ffrRrcSapProvider.at(0) = s;
     }
@@ -2306,7 +2306,7 @@
 LteEnbRrc::SetLteEnbCphySapProvider(LteEnbCphySapProvider* s)
 {
     NS_LOG_FUNCTION(this << s);
-    if (m_cphySapProvider.size() > 0)
+    if (!m_cphySapProvider.empty())
     {
         m_cphySapProvider.at(0) = s;
     }
@@ -2904,7 +2904,7 @@
         Ptr<UeManager> ueManager = GetUeManager(rnti);
         EpcX2Sap::HandoverPreparationFailureParams msg = ueManager->BuildHoPrepFailMsg();
         m_x2SapProvider->SendHandoverPreparationFailure(msg);
-        RemoveUe(rnti); // reomve the UE from the target eNB
+        RemoveUe(rnti); // remove the UE from the target eNB
         return;
     }
 
@@ -3052,7 +3052,7 @@
 
     NS_LOG_LOGIC("Number of cellInformationItems = " << params.cellInformationList.size());
 
-    NS_ABORT_IF(m_ffrRrcSapProvider.size() == 0);
+    NS_ABORT_IF(m_ffrRrcSapProvider.empty());
     m_ffrRrcSapProvider.at(0)->RecvLoadInformation(params);
 }
 
@@ -3476,7 +3476,7 @@
     NS_LOG_FUNCTION(this << srcCi);
     std::set<uint16_t>::iterator it = m_ueSrsConfigurationIndexSet.find(srcCi);
     NS_ASSERT_MSG(it != m_ueSrsConfigurationIndexSet.end(),
-                  "request to remove unkwown SRS CI " << srcCi);
+                  "request to remove unknown SRS CI " << srcCi);
     m_ueSrsConfigurationIndexSet.erase(it);
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-enb-rrc.h ns-3.38/src/lte/model/lte-enb-rrc.h
--- ns-3.37/src/lte/model/lte-enb-rrc.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-enb-rrc.h	2023-03-16 20:54:23.772869000 -0700
@@ -1498,7 +1498,7 @@
      * \note this method can have the side effect of updating the SRS
      * configuration index of all UEs
      *
-     * \param srcCi the indext to be removed
+     * \param srcCi the index to be removed
      */
     void RemoveSrsConfigurationIndex(uint16_t srcCi);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ffr-algorithm.cc ns-3.38/src/lte/model/lte-ffr-algorithm.cc
--- ns-3.37/src/lte/model/lte-ffr-algorithm.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-ffr-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
@@ -31,11 +31,11 @@
 
 /// Type 0 RGB allocation
 static const int Type0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 3GPP TS 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 3GPP TS 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(LteFfrAlgorithm);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ffr-distributed-algorithm.cc ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.cc
--- ns-3.37/src/lte/model/lte-ffr-distributed-algorithm.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
@@ -456,8 +456,8 @@
                 UpdateNeighbourMeasurements(rnti, it->physCellId, it->rsrpResult, it->rsrqResult);
 
                 bool found = false;
-                for (std::vector<uint16_t>::iterator ncIt = m_neigborCell.begin();
-                     ncIt != m_neigborCell.end();
+                for (std::vector<uint16_t>::iterator ncIt = m_neighborCell.begin();
+                     ncIt != m_neighborCell.end();
                      ncIt++)
                 {
                     if ((*ncIt) == it->physCellId)
@@ -467,7 +467,7 @@
                 }
                 if (found == false)
                 {
-                    m_neigborCell.push_back(it->physCellId);
+                    m_neighborCell.push_back(it->physCellId);
                 }
             }
         }
@@ -622,7 +622,8 @@
         }
     }
 
-    for (std::vector<uint16_t>::iterator ncIt = m_neigborCell.begin(); ncIt != m_neigborCell.end();
+    for (std::vector<uint16_t>::iterator ncIt = m_neighborCell.begin();
+         ncIt != m_neighborCell.end();
          ncIt++)
     {
         SendLoadInformation((*ncIt));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ffr-distributed-algorithm.h ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.h
--- ns-3.37/src/lte/model/lte-ffr-distributed-algorithm.h	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.h	2023-03-16 20:54:23.772869000 -0700
@@ -182,7 +182,7 @@
     typedef std::map<uint16_t, MeasurementRow_t> MeasurementTable_t;
     MeasurementTable_t m_ueMeasures; ///< UE measures
 
-    std::vector<uint16_t> m_neigborCell; ///< neighbor cell
+    std::vector<uint16_t> m_neighborCell; ///< neighbor cell
 
     uint8_t m_rsrpDifferenceThreshold; ///< RSRP difference threshold
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ffr-enhanced-algorithm.cc ns-3.38/src/lte/model/lte-ffr-enhanced-algorithm.cc
--- ns-3.37/src/lte/model/lte-ffr-enhanced-algorithm.cc	2022-11-01 17:37:24.655395500 -0700
+++ ns-3.38/src/lte/model/lte-ffr-enhanced-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
@@ -37,32 +37,37 @@
 NS_OBJECT_ENSURE_REGISTERED(LteFfrEnhancedAlgorithm);
 
 /// Spectral efficiency for CQI table
-static const double SpectralEfficiencyForCqi[16] = {0.0, // out of range
-                                                    0.15,
-                                                    0.23,
-                                                    0.38,
-                                                    0.6,
-                                                    0.88,
-                                                    1.18,
-                                                    1.48,
-                                                    1.91,
-                                                    2.41,
-                                                    2.73,
-                                                    3.32,
-                                                    3.9,
-                                                    4.52,
-                                                    5.12,
-                                                    5.55};
+static const double SpectralEfficiencyForCqi[16] = {
+    0.0, // out of range
+    0.15,
+    0.23,
+    0.38,
+    0.6,
+    0.88,
+    1.18,
+    1.48,
+    1.91,
+    2.41,
+    2.73,
+    3.32,
+    3.9,
+    4.52,
+    5.12,
+    5.55,
+};
 
 /// FfrEnhancedDownlinkDefaultConfiguration structure
-static const struct FfrEnhancedDownlinkDefaultConfiguration
+struct FfrEnhancedDownlinkDefaultConfiguration
 {
     uint8_t cellId;               ///< cell ID
     uint8_t dlBandwidth;          ///< DL bandwidth
     uint8_t dlSubBandOffset;      ///< DL subband offset
     uint8_t dlReuse3SubBandwidth; ///< reuse 3 subbandwidth
     uint8_t dlReuse1SubBandwidth; ///< reuse 1 subbandwidth
-} g_ffrEnhancedDownlinkDefaultConfiguration[] = {
+};
+
+/// The enhanced downlink default configuration
+static const FfrEnhancedDownlinkDefaultConfiguration g_ffrEnhancedDownlinkDefaultConfiguration[]{
     {1, 25, 0, 4, 4},
     {2, 25, 8, 4, 4},
     {3, 25, 16, 4, 4},
@@ -74,17 +79,21 @@
     {3, 75, 48, 8, 16},
     {1, 100, 0, 16, 16},
     {2, 100, 32, 16, 16},
-    {3, 100, 64, 16, 16}}; ///< the enhanced downlink default configation
+    {3, 100, 64, 16, 16},
+};
 
 /// FfrEnhancedUplinkDefaultConfiguration structure
-static const struct FfrEnhancedUplinkDefaultConfiguration
+struct FfrEnhancedUplinkDefaultConfiguration
 {
     uint8_t cellId;               ///< cell ID
     uint8_t ulBandwidth;          ///< UL bandwidth
     uint8_t ulSubBandOffset;      ///< UL subband offset
     uint8_t ulReuse3SubBandwidth; ///< UL reuse 3 subbandwidth
     uint8_t ulReuse1SubBandwidth; ///< UL reuse 1 subbandwidth
-} g_ffrEnhancedUplinkDefaultConfiguration[] = {
+};
+
+/// The enhanced uplink default configuration
+static const FfrEnhancedUplinkDefaultConfiguration g_ffrEnhancedUplinkDefaultConfiguration[]{
     {1, 25, 0, 4, 4},
     {2, 25, 8, 4, 4},
     {3, 25, 16, 4, 4},
@@ -96,7 +105,8 @@
     {3, 75, 48, 8, 16},
     {1, 100, 0, 16, 16},
     {2, 100, 32, 16, 16},
-    {3, 100, 64, 16, 16}}; ///< the enhanced uplink default configuration
+    {3, 100, 64, 16, 16},
+};
 
 /** \returns number of downlink configurations */
 const uint16_t NUM_DOWNLINK_CONFS(sizeof(g_ffrEnhancedDownlinkDefaultConfiguration) /
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ffr-soft-algorithm.cc ns-3.38/src/lte/model/lte-ffr-soft-algorithm.cc
--- ns-3.37/src/lte/model/lte-ffr-soft-algorithm.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-ffr-soft-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
@@ -31,14 +31,17 @@
 NS_OBJECT_ENSURE_REGISTERED(LteFfrSoftAlgorithm);
 
 /// FfrSoftDownlinkDefaultConfiguration structure
-static const struct FfrSoftDownlinkDefaultConfiguration
+struct FfrSoftDownlinkDefaultConfiguration
 {
     uint8_t cellId;               ///< cell ID
     uint8_t dlBandwidth;          ///< DL bandwidth
     uint8_t dlCommonSubBandwidth; ///< DL common subbandwidth
     uint8_t dlEdgeSubBandOffset;  ///< DL edge subband offset
     uint8_t dlEdgeSubBandwidth;   ///< DL edge subbandwidth
-} g_ffrSoftDownlinkDefaultConfiguration[] = {
+};
+
+/// The soft downlink default configuration
+static const FfrSoftDownlinkDefaultConfiguration g_ffrSoftDownlinkDefaultConfiguration[]{
     {1, 15, 2, 0, 4},
     {2, 15, 2, 4, 4},
     {3, 15, 2, 8, 4},
@@ -53,17 +56,21 @@
     {3, 75, 36, 24, 15},
     {1, 100, 28, 0, 24},
     {2, 100, 28, 24, 24},
-    {3, 100, 28, 48, 24}}; ///< the soft downlink default configuration
+    {3, 100, 28, 48, 24},
+};
 
 /// FfrSoftUplinkDefaultConfiguration structure
-static const struct FfrSoftUplinkDefaultConfiguration
+struct FfrSoftUplinkDefaultConfiguration
 {
     uint8_t cellId;               ///< cell ID
     uint8_t ulBandwidth;          ///< UL bandwidth
     uint8_t ulCommonSubBandwidth; ///< UL common subbandwidth
     uint8_t ulEdgeSubBandOffset;  ///< UL edge subband offset
     uint8_t ulEdgeSubBandwidth;   ///< edge subbandwidth
-} g_ffrSoftUplinkDefaultConfiguration[] = {
+};
+
+/// The soft uplink default configuration
+static const FfrSoftUplinkDefaultConfiguration g_ffrSoftUplinkDefaultConfiguration[]{
     {1, 15, 3, 0, 4},
     {2, 15, 3, 4, 4},
     {3, 15, 3, 8, 4},
@@ -78,7 +85,8 @@
     {3, 75, 36, 24, 15},
     {1, 100, 28, 0, 24},
     {2, 100, 28, 24, 24},
-    {3, 100, 28, 48, 24}}; ///< the soft uplink default configuration
+    {3, 100, 28, 48, 24},
+};
 
 /** \returns number of downlink configurations */
 const uint16_t NUM_DOWNLINK_CONFS(sizeof(g_ffrSoftDownlinkDefaultConfiguration) /
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-fr-hard-algorithm.cc ns-3.38/src/lte/model/lte-fr-hard-algorithm.cc
--- ns-3.37/src/lte/model/lte-fr-hard-algorithm.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-fr-hard-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
@@ -30,13 +30,16 @@
 NS_OBJECT_ENSURE_REGISTERED(LteFrHardAlgorithm);
 
 /// FrHardDownlinkDefaultConfiguration structure
-static const struct FrHardDownlinkDefaultConfiguration
+struct FrHardDownlinkDefaultConfiguration
 {
     uint8_t m_cellId;      ///< cell ID
     uint8_t m_dlBandwidth; ///< DL bandwidth
     uint8_t m_dlOffset;    ///< DL offset
     uint8_t m_dlSubBand;   ///< DL subband
-} g_frHardDownlinkDefaultConfiguration[] = {
+};
+
+/// The hard downlink default configuration
+static const FrHardDownlinkDefaultConfiguration g_frHardDownlinkDefaultConfiguration[]{
     {1, 15, 0, 4},
     {2, 15, 4, 4},
     {3, 15, 8, 6},
@@ -51,16 +54,20 @@
     {3, 75, 48, 27},
     {1, 100, 0, 32},
     {2, 100, 32, 32},
-    {3, 100, 64, 36}}; ///< the hard downlink default configuration
+    {3, 100, 64, 36},
+};
 
 /// FrHardUplinkDefaultConfiguration structure
-static const struct FrHardUplinkDefaultConfiguration
+struct FrHardUplinkDefaultConfiguration
 {
     uint8_t m_cellId;      ///< cell ID
     uint8_t m_ulBandwidth; ///< UL bandwidth
     uint8_t m_ulOffset;    ///< Ul offset
     uint8_t m_ulSubBand;   ///< UL subband
-} g_frHardUplinkDefaultConfiguration[] = {
+};
+
+/// The hard uplink default configuration
+static const FrHardUplinkDefaultConfiguration g_frHardUplinkDefaultConfiguration[]{
     {1, 15, 0, 5},
     {2, 15, 5, 5},
     {3, 15, 10, 5},
@@ -75,7 +82,8 @@
     {3, 75, 48, 27},
     {1, 100, 0, 32},
     {2, 100, 32, 32},
-    {3, 100, 64, 36}}; ///< the hard uplink default configuration
+    {3, 100, 64, 36},
+};
 
 /** \returns number of downlink configurations */
 const uint16_t NUM_DOWNLINK_CONFS(sizeof(g_frHardDownlinkDefaultConfiguration) /
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-fr-soft-algorithm.cc ns-3.38/src/lte/model/lte-fr-soft-algorithm.cc
--- ns-3.37/src/lte/model/lte-fr-soft-algorithm.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-fr-soft-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
@@ -31,13 +31,16 @@
 NS_OBJECT_ENSURE_REGISTERED(LteFrSoftAlgorithm);
 
 /// FrSoftDownlinkDefaultConfiguration structure
-static const struct FrSoftDownlinkDefaultConfiguration
+struct FrSoftDownlinkDefaultConfiguration
 {
     uint8_t cellId;              ///< cell ID
     uint8_t dlBandwidth;         ///< DL bandwidth
     uint8_t dlEdgeSubBandOffset; ///< DL edge subband offset
     uint8_t dlEdgeSubBandwidth;  ///< Dl edge subbandwidth
-} g_frSoftDownlinkDefaultConfiguration[] = {
+};
+
+/// Soft downlink default configuration
+static const FrSoftDownlinkDefaultConfiguration g_frSoftDownlinkDefaultConfiguration[]{
     {1, 15, 0, 4},
     {2, 15, 4, 4},
     {3, 15, 8, 6},
@@ -52,30 +55,36 @@
     {3, 75, 48, 27},
     {1, 100, 0, 32},
     {2, 100, 32, 32},
-    {3, 100, 64, 36}}; ///< soft downlink default configuration
+    {3, 100, 64, 36},
+};
 
 /// soft uplink default configuration
-static const struct FrSoftUplinkDefaultConfiguration
+struct FrSoftUplinkDefaultConfiguration
 {
     uint8_t cellId;              ///< cell ID
     uint8_t ulBandwidth;         ///< UL bandwidth
     uint8_t ulEdgeSubBandOffset; ///< UL edge subband offset
-    uint8_t ulEdgeSubBandwidth;  ///< UL endge subbandwidth
-} g_frSoftUplinkDefaultConfiguration[] = {{1, 15, 0, 5},
-                                          {2, 15, 5, 5},
-                                          {3, 15, 10, 5},
-                                          {1, 25, 0, 8},
-                                          {2, 25, 8, 8},
-                                          {3, 25, 16, 9},
-                                          {1, 50, 0, 16},
-                                          {2, 50, 16, 16},
-                                          {3, 50, 32, 18},
-                                          {1, 75, 0, 24},
-                                          {2, 75, 24, 24},
-                                          {3, 75, 48, 27},
-                                          {1, 100, 0, 32},
-                                          {2, 100, 32, 32},
-                                          {3, 100, 64, 36}}; ///< soft uplink default configuration
+    uint8_t ulEdgeSubBandwidth;  ///< UL edge subbandwidth
+};
+
+/// Soft uplink default configuration
+static const FrSoftUplinkDefaultConfiguration g_frSoftUplinkDefaultConfiguration[]{
+    {1, 15, 0, 5},
+    {2, 15, 5, 5},
+    {3, 15, 10, 5},
+    {1, 25, 0, 8},
+    {2, 25, 8, 8},
+    {3, 25, 16, 9},
+    {1, 50, 0, 16},
+    {2, 50, 16, 16},
+    {3, 50, 32, 18},
+    {1, 75, 0, 24},
+    {2, 75, 24, 24},
+    {3, 75, 48, 27},
+    {1, 100, 0, 32},
+    {2, 100, 32, 32},
+    {3, 100, 64, 36},
+};
 
 /** \returns number of downlink configurations */
 const uint16_t NUM_DOWNLINK_CONFS(sizeof(g_frSoftDownlinkDefaultConfiguration) /
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-fr-strict-algorithm.cc ns-3.38/src/lte/model/lte-fr-strict-algorithm.cc
--- ns-3.37/src/lte/model/lte-fr-strict-algorithm.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-fr-strict-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
@@ -31,14 +31,17 @@
 NS_OBJECT_ENSURE_REGISTERED(LteFrStrictAlgorithm);
 
 /// FrStrictDownlinkDefaultConfiguration structure
-static const struct FrStrictDownlinkDefaultConfiguration
+struct FrStrictDownlinkDefaultConfiguration
 {
     uint8_t cellId;               ///< cell ID
     uint8_t dlBandwidth;          ///< DL bandwidth
     uint8_t dlCommonSubBandwidth; ///< DL common subbandwidth
     uint8_t dlEdgeSubBandOffset;  ///< DL edge subband offset
     uint8_t dlEdgeSubBandwidth;   ///< DL edge subbandwidth
-} g_frStrictDownlinkDefaultConfiguration[] = {
+};
+
+/// The strict downlink default configuration
+static const FrStrictDownlinkDefaultConfiguration g_frStrictDownlinkDefaultConfiguration[]{
     {1, 15, 2, 0, 4},
     {2, 15, 2, 4, 4},
     {3, 15, 2, 8, 4},
@@ -53,17 +56,21 @@
     {3, 75, 36, 24, 15},
     {1, 100, 28, 0, 24},
     {2, 100, 28, 24, 24},
-    {3, 100, 28, 48, 24}}; ///< the strict downlink default configuration
+    {3, 100, 28, 48, 24},
+};
 
 /// FrStrictUplinkDefaultConfiguration structure
-static const struct FrStrictUplinkDefaultConfiguration
+struct FrStrictUplinkDefaultConfiguration
 {
     uint8_t cellId;               ///< cell ID
     uint8_t ulBandwidth;          ///< UL bandwidth
     uint8_t ulCommonSubBandwidth; ///< UL common subbandwidth
     uint8_t ulEdgeSubBandOffset;  ///< UL edge subband offset
     uint8_t ulEdgeSubBandwidth;   ///< UL edge subbandwidth
-} g_frStrictUplinkDefaultConfiguration[] = {
+};
+
+/// The strict uplink default configuration
+static const FrStrictUplinkDefaultConfiguration g_frStrictUplinkDefaultConfiguration[]{
     {1, 15, 3, 0, 4},
     {2, 15, 3, 4, 4},
     {3, 15, 3, 8, 4},
@@ -78,7 +85,8 @@
     {3, 75, 36, 24, 15},
     {1, 100, 28, 0, 24},
     {2, 100, 28, 24, 24},
-    {3, 100, 28, 48, 24}}; ///< the strict uplink default configuration
+    {3, 100, 28, 48, 24},
+};
 
 /** \returns number of downlink configurations */
 const uint16_t NUM_DOWNLINK_CONFS(sizeof(g_frStrictDownlinkDefaultConfiguration) /
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-mac-sap.h ns-3.38/src/lte/model/lte-mac-sap.h
--- ns-3.37/src/lte/model/lte-mac-sap.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-mac-sap.h	2023-03-16 20:54:23.772869000 -0700
@@ -51,7 +51,7 @@
         uint8_t harqProcessId; /**< the HARQ process id that was passed by the MAC in the call to
                                   NotifyTxOpportunity that generated this PDU */
         uint8_t componentCarrierId; /**< the component carrier id corresponding to the sending Mac
-                                       istance */
+                                       instance */
     };
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-mi-error-model.cc ns-3.38/src/lte/model/lte-mi-error-model.cc
--- ns-3.37/src/lte/model/lte-mi-error-model.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-mi-error-model.cc	2023-03-16 20:54:23.772869000 -0700
@@ -1138,7 +1138,7 @@
     double MI = 0.0;
     double Reff = 0.0;
     NS_ASSERT(mcs < 29);
-    if (miHistory.size() > 0)
+    if (!miHistory.empty())
     {
         // evaluate R_eff and MI_eff
         uint16_t codeBitsSum = 0;
@@ -1255,7 +1255,7 @@
 
     double errorRate = 1.0;
     uint8_t ecrId = 0;
-    if (miHistory.size() == 0)
+    if (miHistory.empty())
     {
         // first tx -> get ECR from MCS
         ecrId = McsEcrBlerTableMapping[mcs];
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-pdcp.cc ns-3.38/src/lte/model/lte-pdcp.cc
--- ns-3.37/src/lte/model/lte-pdcp.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-pdcp.cc	2023-03-16 20:54:23.776869000 -0700
@@ -154,7 +154,7 @@
 }
 
 LtePdcp::Status
-LtePdcp::GetStatus()
+LtePdcp::GetStatus() const
 {
     Status s;
     s.txSn = m_txSequenceNumber;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-pdcp.h ns-3.38/src/lte/model/lte-pdcp.h
--- ns-3.37/src/lte/model/lte-pdcp.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-pdcp.h	2023-03-16 20:54:23.776869000 -0700
@@ -107,7 +107,7 @@
      *
      * \return the current status of the PDCP
      */
-    Status GetStatus();
+    Status GetStatus() const;
 
     /**
      * Set the status of the PDCP
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-pdcp-sap.h ns-3.38/src/lte/model/lte-pdcp-sap.h
--- ns-3.37/src/lte/model/lte-pdcp-sap.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-pdcp-sap.h	2023-03-16 20:54:23.776869000 -0700
@@ -100,11 +100,13 @@
      */
     LtePdcpSpecificLtePdcpSapProvider(C* pdcp);
 
+    // Delete default constructor to avoid misuse
+    LtePdcpSpecificLtePdcpSapProvider() = delete;
+
     // Interface implemented from LtePdcpSapProvider
     void TransmitPdcpSdu(TransmitPdcpSduParameters params) override;
 
   private:
-    LtePdcpSpecificLtePdcpSapProvider();
     C* m_pdcp; ///< the PDCP
 };
 
@@ -115,11 +117,6 @@
 }
 
 template <class C>
-LtePdcpSpecificLtePdcpSapProvider<C>::LtePdcpSpecificLtePdcpSapProvider()
-{
-}
-
-template <class C>
 void
 LtePdcpSpecificLtePdcpSapProvider<C>::TransmitPdcpSdu(TransmitPdcpSduParameters params)
 {
@@ -138,11 +135,13 @@
      */
     LtePdcpSpecificLtePdcpSapUser(C* rrc);
 
+    // Delete default constructor to avoid misuse
+    LtePdcpSpecificLtePdcpSapUser() = delete;
+
     // Interface implemented from LtePdcpSapUser
     void ReceivePdcpSdu(ReceivePdcpSduParameters params) override;
 
   private:
-    LtePdcpSpecificLtePdcpSapUser();
     C* m_rrc; ///< RRC
 };
 
@@ -152,11 +151,6 @@
 {
 }
 
-template <class C>
-LtePdcpSpecificLtePdcpSapUser<C>::LtePdcpSpecificLtePdcpSapUser()
-{
-}
-
 template <class C>
 void
 LtePdcpSpecificLtePdcpSapUser<C>::ReceivePdcpSdu(ReceivePdcpSduParameters params)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-phy.cc ns-3.38/src/lte/model/lte-phy.cc
--- ns-3.37/src/lte/model/lte-phy.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-phy.cc	2023-03-16 20:54:23.776869000 -0700
@@ -217,7 +217,7 @@
 LtePhy::GetControlMessages()
 {
     NS_LOG_FUNCTION(this);
-    if (m_controlMessagesQueue.at(0).size() > 0)
+    if (!m_controlMessagesQueue.at(0).empty())
     {
         std::list<Ptr<LteControlMessage>> ret = m_controlMessagesQueue.at(0);
         m_controlMessagesQueue.erase(m_controlMessagesQueue.begin());
@@ -252,7 +252,7 @@
 }
 
 uint8_t
-LtePhy::GetComponentCarrierId()
+LtePhy::GetComponentCarrierId() const
 {
     return m_componentCarrierId;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-phy.h ns-3.38/src/lte/model/lte-phy.h
--- ns-3.37/src/lte/model/lte-phy.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-phy.h	2023-03-16 20:54:23.776869000 -0700
@@ -216,7 +216,7 @@
      *
      * \returns the component carrier ID index
      */
-    uint8_t GetComponentCarrierId();
+    uint8_t GetComponentCarrierId() const;
 
   protected:
     /// Pointer to the NetDevice where this PHY layer is attached.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-radio-bearer-info.h ns-3.38/src/lte/model/lte-radio-bearer-info.h
--- ns-3.37/src/lte/model/lte-radio-bearer-info.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-radio-bearer-info.h	2023-03-16 20:54:23.776869000 -0700
@@ -64,7 +64,7 @@
      */
     static TypeId GetTypeId();
 
-    uint8_t m_srbIdentity;                                  ///< SRB indentity
+    uint8_t m_srbIdentity;                                  ///< SRB identity
     LteRrcSap::LogicalChannelConfig m_logicalChannelConfig; ///< logical channel config
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-am.cc ns-3.38/src/lte/model/lte-rlc-am.cc
--- ns-3.37/src/lte/model/lte-rlc-am.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-am.cc	2023-03-16 20:54:23.776869000 -0700
@@ -469,7 +469,7 @@
 
     // Remove the first packet from the transmission buffer.
     // If only a segment of the packet is taken, then the remaining is given back later
-    if (m_txonBuffer.size() == 0)
+    if (m_txonBuffer.empty())
     {
         NS_LOG_LOGIC("No data pending");
         return;
@@ -578,7 +578,7 @@
             // (NO more segments) ? exit
             // break;
         }
-        else if ((nextSegmentSize - firstSegment->GetSize() <= 2) || (m_txonBuffer.size() == 0))
+        else if ((nextSegmentSize - firstSegment->GetSize() <= 2) || m_txonBuffer.empty())
         {
             NS_LOG_LOGIC(
                 "    IF nextSegmentSize - firstSegment->GetSize () <= 2 || txonBuffer.size == 0");
@@ -597,7 +597,7 @@
             nextSegmentId++;
 
             NS_LOG_LOGIC("        SDUs in TxBuffer  = " << m_txonBuffer.size());
-            if (m_txonBuffer.size() > 0)
+            if (!m_txonBuffer.empty())
             {
                 NS_LOG_LOGIC("        First SDU buffer  = " << m_txonBuffer.begin()->m_pdu);
                 NS_LOG_LOGIC(
@@ -627,7 +627,7 @@
             nextSegmentId++;
 
             NS_LOG_LOGIC("        SDUs in TxBuffer  = " << m_txonBuffer.size());
-            if (m_txonBuffer.size() > 0)
+            if (!m_txonBuffer.empty())
             {
                 NS_LOG_LOGIC("        First SDU buffer  = " << m_txonBuffer.begin()->m_pdu);
                 NS_LOG_LOGIC(
@@ -928,7 +928,7 @@
             std::map<uint16_t, PduBuffer>::iterator it = m_rxonBuffer.find(seqNumber.GetValue());
             if (it != m_rxonBuffer.end())
             {
-                NS_ASSERT(it->second.m_byteSegments.size() > 0);
+                NS_ASSERT(!it->second.m_byteSegments.empty());
                 NS_ASSERT_MSG(it->second.m_byteSegments.size() == 1,
                               "re-segmentation not supported");
                 NS_LOG_LOGIC("PDU segment already received, discarded");
@@ -1459,7 +1459,7 @@
                  */
                 m_sdusBuffer.pop_front();
 
-                if (m_sdusBuffer.size() > 0)
+                if (!m_sdusBuffer.empty())
                 {
                     /**
                      * Deliver zero, one or multiple PDUs
@@ -1577,7 +1577,7 @@
                  */
                 m_sdusBuffer.pop_front();
 
-                if (m_sdusBuffer.size() > 0)
+                if (!m_sdusBuffer.empty())
                 {
                     /**
                      * Deliver zero, one or multiple PDUs
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-am.h ns-3.38/src/lte/model/lte-rlc-am.h
--- ns-3.37/src/lte/model/lte-rlc-am.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-am.h	2023-03-16 20:54:23.776869000 -0700
@@ -222,12 +222,12 @@
     /**
      * SDU Reassembling state
      */
-    typedef enum
+    enum ReassemblingState_t
     {
         NONE = 0,
         WAITING_S0_FULL = 1,
         WAITING_SI_SF = 2
-    } ReassemblingState_t;
+    };
 
     ReassemblingState_t m_reassemblingState; ///< reassembling state
     Ptr<Packet> m_keepS0;                    ///< keep S0
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-am-header.h ns-3.38/src/lte/model/lte-rlc-am-header.h
--- ns-3.37/src/lte/model/lte-rlc-am-header.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-am-header.h	2023-03-16 20:54:23.776869000 -0700
@@ -67,17 +67,17 @@
     bool IsControlPdu() const;
 
     /// DataControlPdu_t enumeration
-    typedef enum
+    enum DataControlPdu_t
     {
         CONTROL_PDU = 0,
         DATA_PDU = 1
-    } DataControlPdu_t; ///< DataControlPdu_t typedef
+    };
 
     /// ControlPduType_t enumeration
-    typedef enum
+    enum ControPduType_t
     {
         STATUS_PDU = 000,
-    } ControPduType_t; ///< ControPduType_t typedef
+    };
 
     //
     // DATA PDU
@@ -110,18 +110,18 @@
     uint8_t GetFramingInfo() const;
 
     /// FramingInfoFirstByte_t enumeration
-    typedef enum
+    enum FramingInfoFirstByte_t
     {
         FIRST_BYTE = 0x00,
         NO_FIRST_BYTE = 0x02
-    } FramingInfoFirstByte_t; ///< FramingInfoFirstByte_t typedef
+    };
 
     /// FramingInfoLastByte_t enumeration
-    typedef enum
+    enum FramingInfoLastByte_t
     {
         LAST_BYTE = 0x00,
         NO_LAST_BYTE = 0x01
-    } FramingInfoLastByte_t; ///< FramingInfoLastByte_t typedef
+    };
 
     /**
      * Push extension bit function
@@ -150,11 +150,11 @@
     uint16_t PopLengthIndicator();
 
     /// ExtensionBit_t typedef
-    typedef enum
+    enum ExtensionBit_t
     {
         DATA_FIELD_FOLLOWS = 0,
         E_LI_FIELDS_FOLLOWS = 1
-    } ExtensionBit_t; ///< ExtensionBit_t typedef
+    };
 
     /**
      * Pop extension bit function
@@ -170,11 +170,11 @@
     uint8_t GetResegmentationFlag() const;
 
     /// ResegmentationFlag_t typedef
-    typedef enum
+    enum ResegmentationFlag_t
     {
         PDU = 0,
         SEGMENT = 1
-    } ResegmentationFlag_t; ///< ResegmentationFlag_t typedef
+    };
 
     /**
      * Set polling bit function
@@ -190,11 +190,11 @@
     uint8_t GetPollingBit() const;
 
     /// PollingBit_t enumeration
-    typedef enum
+    enum PollingBit_t
     {
         STATUS_REPORT_NOT_REQUESTED = 0,
         STATUS_REPORT_IS_REQUESTED = 1
-    } PollingBit_t; ///< PollingBit_t typedef
+    };
 
     /**
      * Set last segment flag function
@@ -210,11 +210,11 @@
     uint8_t GetLastSegmentFlag() const;
 
     /// LastSegmentFlag_t typedef
-    typedef enum
+    enum LastSegmentFlag_t
     {
         NO_LAST_PDU_SEGMENT = 0,
         LAST_PDU_SEGMENT = 1
-    } LastSegmentFlag_t; ///< LastSegmentFlag_t enumeration
+    };
 
     /**
      * Set segment offset function
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-header.h ns-3.38/src/lte/model/lte-rlc-header.h
--- ns-3.37/src/lte/model/lte-rlc-header.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-header.h	2023-03-16 20:54:23.776869000 -0700
@@ -100,25 +100,25 @@
     uint16_t PopLengthIndicator();
 
     /// ExtensionBit_t typedef
-    typedef enum
+    enum ExtensionBit_t
     {
         DATA_FIELD_FOLLOWS = 0,
         E_LI_FIELDS_FOLLOWS = 1
-    } ExtensionBit_t; ///< ExtensionBit_t enumeration
+    };
 
     /// FramingInfoFirstByte_t typedef
-    typedef enum
+    enum FramingInfoFirstByte_t
     {
         FIRST_BYTE = 0x00,
         NO_FIRST_BYTE = 0x02
-    } FramingInfoFirstByte_t; ///< FramingInfoFirstByte_t enumeration
+    };
 
     /// FramingInfoLastByte_t tyedef
-    typedef enum
+    enum FramingInfoLastByte_t
     {
         LAST_BYTE = 0x00,
         NO_LAST_BYTE = 0x01
-    } FramingInfoLastByte_t; ///< FramingInfoLastByte_t enumeration
+    };
 
     /**
      * \brief Get the type ID.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-sap.h ns-3.38/src/lte/model/lte-rlc-sap.h
--- ns-3.37/src/lte/model/lte-rlc-sap.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-sap.h	2023-03-16 20:54:23.776869000 -0700
@@ -88,6 +88,9 @@
      */
     LteRlcSpecificLteRlcSapProvider(C* rlc);
 
+    // Delete default constructor to avoid misuse
+    LteRlcSpecificLteRlcSapProvider() = delete;
+
     /**
      * Interface implemented from LteRlcSapProvider
      * \param params the TransmitPdcpPduParameters
@@ -95,7 +98,6 @@
     void TransmitPdcpPdu(TransmitPdcpPduParameters params) override;
 
   private:
-    LteRlcSpecificLteRlcSapProvider();
     C* m_rlc; ///< the RLC
 };
 
@@ -106,11 +108,6 @@
 }
 
 template <class C>
-LteRlcSpecificLteRlcSapProvider<C>::LteRlcSpecificLteRlcSapProvider()
-{
-}
-
-template <class C>
 void
 LteRlcSpecificLteRlcSapProvider<C>::TransmitPdcpPdu(TransmitPdcpPduParameters params)
 {
@@ -129,11 +126,13 @@
      */
     LteRlcSpecificLteRlcSapUser(C* pdcp);
 
+    // Delete default constructor to avoid misuse
+    LteRlcSpecificLteRlcSapUser() = delete;
+
     // Interface implemented from LteRlcSapUser
     void ReceivePdcpPdu(Ptr<Packet> p) override;
 
   private:
-    LteRlcSpecificLteRlcSapUser();
     C* m_pdcp; ///< the PDCP
 };
 
@@ -143,11 +142,6 @@
 {
 }
 
-template <class C>
-LteRlcSpecificLteRlcSapUser<C>::LteRlcSpecificLteRlcSapUser()
-{
-}
-
 template <class C>
 void
 LteRlcSpecificLteRlcSapUser<C>::ReceivePdcpPdu(Ptr<Packet> p)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-sdu-status-tag.h ns-3.38/src/lte/model/lte-rlc-sdu-status-tag.h
--- ns-3.37/src/lte/model/lte-rlc-sdu-status-tag.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-sdu-status-tag.h	2023-03-16 20:54:23.776869000 -0700
@@ -59,15 +59,15 @@
     void Deserialize(TagBuffer i) override;
     void Print(std::ostream& os) const override;
 
-    /// SduStatus_t typedef
-    typedef enum
+    /// SduStatus_t enumeration
+    enum SduStatus_t
     {
         FULL_SDU = 1,
         FIRST_SEGMENT = 2,
         MIDDLE_SEGMENT = 3,
         LAST_SEGMENT = 4,
         ANY_SEGMENT = 5
-    } SduStatus_t; ///< SduStatus_t enumeration
+    };
 
   private:
     uint8_t m_sduStatus; ///< SDU status
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-tm.cc ns-3.38/src/lte/model/lte-rlc-tm.cc
--- ns-3.37/src/lte/model/lte-rlc-tm.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-tm.cc	2023-03-16 20:54:23.776869000 -0700
@@ -113,7 +113,7 @@
     // When submitting a new TMD PDU to lower layer, the transmitting TM RLC entity shall:
     // - submit a RLC SDU without any modification to lower layer.
 
-    if (m_txBuffer.size() == 0)
+    if (m_txBuffer.empty())
     {
         NS_LOG_LOGIC("No data pending");
         return;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-um.cc ns-3.38/src/lte/model/lte-rlc-um.cc
--- ns-3.37/src/lte/model/lte-rlc-um.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-um.cc	2023-03-16 20:54:23.776869000 -0700
@@ -146,7 +146,7 @@
 
     // Remove the first packet from the transmission buffer.
     // If only a segment of the packet is taken, then the remaining is given back later
-    if (m_txBuffer.size() == 0)
+    if (m_txBuffer.empty())
     {
         NS_LOG_LOGIC("No data pending");
         return;
@@ -256,7 +256,7 @@
             // (NO more segments) → exit
             // break;
         }
-        else if ((nextSegmentSize - firstSegment->GetSize() <= 2) || (m_txBuffer.size() == 0))
+        else if ((nextSegmentSize - firstSegment->GetSize() <= 2) || m_txBuffer.empty())
         {
             NS_LOG_LOGIC(
                 "    IF nextSegmentSize - firstSegment->GetSize () <= 2 || txBuffer.size == 0");
@@ -274,7 +274,7 @@
             nextSegmentId++;
 
             NS_LOG_LOGIC("        SDUs in TxBuffer  = " << m_txBuffer.size());
-            if (m_txBuffer.size() > 0)
+            if (!m_txBuffer.empty())
             {
                 NS_LOG_LOGIC("        First SDU buffer  = " << m_txBuffer.begin()->m_pdu);
                 NS_LOG_LOGIC(
@@ -304,7 +304,7 @@
             nextSegmentId++;
 
             NS_LOG_LOGIC("        SDUs in TxBuffer  = " << m_txBuffer.size());
-            if (m_txBuffer.size() > 0)
+            if (!m_txBuffer.empty())
             {
                 NS_LOG_LOGIC("        First SDU buffer  = " << m_txBuffer.begin()->m_pdu);
                 NS_LOG_LOGIC(
@@ -741,7 +741,7 @@
                  */
                 m_sdusBuffer.pop_front();
 
-                if (m_sdusBuffer.size() > 0)
+                if (!m_sdusBuffer.empty())
                 {
                     /**
                      * Deliver zero, one or multiple PDUs
@@ -921,7 +921,7 @@
                  */
                 m_sdusBuffer.pop_front();
 
-                if (m_sdusBuffer.size() > 0)
+                if (!m_sdusBuffer.empty())
                 {
                     /**
                      * Deliver zero, one or multiple PDUs
@@ -1039,7 +1039,7 @@
                  */
                 m_sdusBuffer.pop_front();
 
-                if (m_sdusBuffer.size() > 0)
+                if (!m_sdusBuffer.empty())
                 {
                     /**
                      * Deliver zero, one or multiple PDUs
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rlc-um.h ns-3.38/src/lte/model/lte-rlc-um.h
--- ns-3.37/src/lte/model/lte-rlc-um.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rlc-um.h	2023-03-16 20:54:23.776869000 -0700
@@ -150,12 +150,12 @@
     /**
      * Reassembling state
      */
-    typedef enum
+    enum ReassemblingState_t
     {
         NONE = 0,
         WAITING_S0_FULL = 1,
         WAITING_SI_SF = 2
-    } ReassemblingState_t;
+    };
 
     ReassemblingState_t m_reassemblingState; ///< reassembling state
     Ptr<Packet> m_keepS0;                    ///< keep S0
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rrc-header.cc ns-3.38/src/lte/model/lte-rrc-header.cc
--- ns-3.37/src/lte/model/lte-rrc-header.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rrc-header.cc	2023-03-16 20:54:23.776869000 -0700
@@ -66,7 +66,7 @@
 }
 
 int
-RrcAsn1Header::GetMessageType()
+RrcAsn1Header::GetMessageType() const
 {
     return m_messageType;
 }
@@ -142,7 +142,7 @@
     for (; it != drbToAddModList.end(); it++)
     {
         // Serialize DRB-ToAddMod sequence
-        // 5 otional fields. Extension marker is present.
+        // 5 optional fields. Extension marker is present.
         std::bitset<5> drbToAddModListOptionalFieldsPresent = std::bitset<5>();
         drbToAddModListOptionalFieldsPresent.set(4, 1); // eps-BearerIdentity present
         drbToAddModListOptionalFieldsPresent.set(3, 0); // pdcp-Config not present
@@ -230,7 +230,7 @@
     for (; it != srbToAddModList.end(); it++)
     {
         // Serialize SRB-ToAddMod sequence
-        // 2 otional fields. Extension marker is present.
+        // 2 optional fields. Extension marker is present.
         std::bitset<2> srbToAddModListOptionalFieldsPresent = std::bitset<2>();
         srbToAddModListOptionalFieldsPresent.set(1, 0); // rlc-Config not present
         srbToAddModListOptionalFieldsPresent.set(0, 1); // logicalChannelConfig present
@@ -5912,7 +5912,7 @@
 }
 
 bool
-RrcConnectionReconfigurationHeader::GetHaveMeasConfig()
+RrcConnectionReconfigurationHeader::GetHaveMeasConfig() const
 {
     return m_haveMeasConfig;
 }
@@ -5924,7 +5924,7 @@
 }
 
 bool
-RrcConnectionReconfigurationHeader::GetHaveMobilityControlInfo()
+RrcConnectionReconfigurationHeader::GetHaveMobilityControlInfo() const
 {
     return m_haveMobilityControlInfo;
 }
@@ -5936,7 +5936,7 @@
 }
 
 bool
-RrcConnectionReconfigurationHeader::GetHaveRadioResourceConfigDedicated()
+RrcConnectionReconfigurationHeader::GetHaveRadioResourceConfigDedicated() const
 {
     return m_haveRadioResourceConfigDedicated;
 }
@@ -5948,7 +5948,7 @@
 }
 
 bool
-RrcConnectionReconfigurationHeader::GetHaveNonCriticalExtensionConfig()
+RrcConnectionReconfigurationHeader::GetHaveNonCriticalExtensionConfig() const
 {
     return m_haveNonCriticalExtension;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rrc-header.h ns-3.38/src/lte/model/lte-rrc-header.h
--- ns-3.37/src/lte/model/lte-rrc-header.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rrc-header.h	2023-03-16 20:54:23.776869000 -0700
@@ -50,7 +50,7 @@
      *
      * \returns the message type
      */
-    int GetMessageType();
+    int GetMessageType() const;
 
   protected:
     /**
@@ -776,7 +776,7 @@
      * Getter for m_haveMeasConfig
      * @return m_haveMeasConfig
      */
-    bool GetHaveMeasConfig();
+    bool GetHaveMeasConfig() const;
 
     /**
      * Getter for m_measConfig
@@ -788,7 +788,7 @@
      * Getter for m_haveMobilityControlInfo
      * @return m_haveMobilityControlInfo
      */
-    bool GetHaveMobilityControlInfo();
+    bool GetHaveMobilityControlInfo() const;
 
     /**
      * Getter for m_mobilityControlInfo
@@ -800,7 +800,7 @@
      * Getter for m_haveRadioResourceConfigDedicated
      * @return m_haveRadioResourceConfigDedicated
      */
-    bool GetHaveRadioResourceConfigDedicated();
+    bool GetHaveRadioResourceConfigDedicated() const;
 
     /**
      * Getter for m_radioResourceConfigDedicated
@@ -824,7 +824,7 @@
      * Getter for m_haveNonCriticalExtension
      * @return m_haveNonCriticalExtension
      */
-    bool GetHaveNonCriticalExtensionConfig();
+    bool GetHaveNonCriticalExtensionConfig() const;
 
     /**
      * Getter for m_nonCriticalExtension
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rrc-protocol-ideal.cc ns-3.38/src/lte/model/lte-rrc-protocol-ideal.cc
--- ns-3.37/src/lte/model/lte-rrc-protocol-ideal.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rrc-protocol-ideal.cc	2023-03-16 20:54:23.776869000 -0700
@@ -476,7 +476,7 @@
      *
      * \returns the message ID
      */
-    uint32_t GetMsgId();
+    uint32_t GetMsgId() const;
     /**
      * Set the message ID function
      *
@@ -499,7 +499,7 @@
 };
 
 uint32_t
-IdealHandoverPreparationInfoHeader::GetMsgId()
+IdealHandoverPreparationInfoHeader::GetMsgId() const
 {
     return m_msgId;
 }
@@ -601,7 +601,7 @@
      *
      * \returns the message ID
      */
-    uint32_t GetMsgId();
+    uint32_t GetMsgId() const;
     /**
      * Set the message ID function
      *
@@ -624,7 +624,7 @@
 };
 
 uint32_t
-IdealHandoverCommandHeader::GetMsgId()
+IdealHandoverCommandHeader::GetMsgId() const
 {
     return m_msgId;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rrc-protocol-real.cc ns-3.38/src/lte/model/lte-rrc-protocol-real.cc
--- ns-3.37/src/lte/model/lte-rrc-protocol-real.cc	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rrc-protocol-real.cc	2023-03-16 20:54:23.776869000 -0700
@@ -129,7 +129,8 @@
 }
 
 void
-LteUeRrcProtocolReal::DoSendRrcConnectionSetupCompleted(LteRrcSap::RrcConnectionSetupCompleted msg)
+LteUeRrcProtocolReal::DoSendRrcConnectionSetupCompleted(
+    LteRrcSap::RrcConnectionSetupCompleted msg) const
 {
     Ptr<Packet> packet = Create<Packet>();
 
@@ -220,7 +221,7 @@
 
 void
 LteUeRrcProtocolReal::DoSendRrcConnectionReestablishmentRequest(
-    LteRrcSap::RrcConnectionReestablishmentRequest msg)
+    LteRrcSap::RrcConnectionReestablishmentRequest msg) const
 {
     Ptr<Packet> packet = Create<Packet>();
 
@@ -239,7 +240,7 @@
 
 void
 LteUeRrcProtocolReal::DoSendRrcConnectionReestablishmentComplete(
-    LteRrcSap::RrcConnectionReestablishmentComplete msg)
+    LteRrcSap::RrcConnectionReestablishmentComplete msg) const
 {
     Ptr<Packet> packet = Create<Packet>();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rrc-protocol-real.h ns-3.38/src/lte/model/lte-rrc-protocol-real.h
--- ns-3.37/src/lte/model/lte-rrc-protocol-real.h	2022-11-01 17:37:24.659395700 -0700
+++ ns-3.38/src/lte/model/lte-rrc-protocol-real.h	2023-03-16 20:54:23.776869000 -0700
@@ -108,7 +108,7 @@
      *
      * \param msg LteRrcSap::RrcConnectionSetupCompleted
      */
-    void DoSendRrcConnectionSetupCompleted(LteRrcSap::RrcConnectionSetupCompleted msg);
+    void DoSendRrcConnectionSetupCompleted(LteRrcSap::RrcConnectionSetupCompleted msg) const;
     /**
      * Send RRC connection reconfiguration setup completed function
      *
@@ -122,14 +122,14 @@
      * \param msg LteRrcSap::RrcConnectionReestablishmentRequest
      */
     void DoSendRrcConnectionReestablishmentRequest(
-        LteRrcSap::RrcConnectionReestablishmentRequest msg);
+        LteRrcSap::RrcConnectionReestablishmentRequest msg) const;
     /**
      * Send RRC connection reestablishment complete function
      *
      * \param msg LteRrcSap::RrcConnectionReestablishmentComplete
      */
     void DoSendRrcConnectionReestablishmentComplete(
-        LteRrcSap::RrcConnectionReestablishmentComplete msg);
+        LteRrcSap::RrcConnectionReestablishmentComplete msg) const;
     /**
      * Send measurement report function
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-rrc-sap.h ns-3.38/src/lte/model/lte-rrc-sap.h
--- ns-3.37/src/lte/model/lte-rrc-sap.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-rrc-sap.h	2023-03-16 20:54:23.776869000 -0700
@@ -93,13 +93,15 @@
     struct RlcConfig
     {
         /// the direction choice
-        enum direction
+        enum Direction
         {
             AM,
             UM_BI_DIRECTIONAL,
             UM_UNI_DIRECTIONAL_UL,
             UM_UNI_DIRECTIONAL_DL
-        } choice; ///< direction choice
+        };
+
+        Direction choice; ///< direction choice
     };
 
     /// LogicalChannelConfig structure
@@ -115,11 +117,13 @@
     struct SoundingRsUlConfigCommon
     {
         /// the config action
-        enum action
+        enum Action
         {
             SETUP,
             RESET
-        } type; ///< action type
+        };
+
+        Action type; ///< action type
 
         uint16_t srsBandwidthConfig; ///< SRS bandwidth config
         uint8_t srsSubframeConfig;   ///< SRS subframe config
@@ -129,11 +133,13 @@
     struct SoundingRsUlConfigDedicated
     {
         /// the config action
-        enum action
+        enum Action
         {
             SETUP,
             RESET
-        } type; ///< action type
+        };
+
+        Action type; ///< action type
 
         uint16_t srsBandwidth;   ///< SRS bandwidth
         uint16_t srsConfigIndex; ///< SRS config index
@@ -159,7 +165,7 @@
          * P_A values, TS 36.331 6.3.2 PDSCH-Config
          * ENUMERATED { dB-6, dB-4dot77, dB-3, dB-1dot77, dB0, dB1, dB2, dB3 }
          */
-        enum db
+        enum Db
         {
             dB_6,
             dB_4dot77,
@@ -405,11 +411,13 @@
         uint16_t timeToTrigger;
 
         /// the report purpose
-        enum report
+        enum Report
         {
             REPORT_STRONGEST_CELLS,
             REPORT_CGI
-        } purpose; ///< purpose
+        };
+
+        Report purpose; ///< purpose
 
         /// Trigger type enumeration
         enum
@@ -487,18 +495,22 @@
     struct MeasGapConfig
     {
         /// the action type
-        enum action
+        enum Action
         {
             SETUP,
             RESET
-        } type; ///< action type
+        };
 
-        /// the gap offest
-        enum gap
+        Action type; ///< action type
+
+        /// the gap offset
+        enum Gap
         {
             GP0,
             GP1
-        } gapOffsetChoice; ///< gap offset
+        };
+
+        Gap gapOffsetChoice; ///< gap offset
 
         uint8_t gapOffsetValue; ///< gap offset value
     };
@@ -524,11 +536,13 @@
     struct SpeedStatePars
     {
         /// the action type
-        enum action
+        enum Action
         {
             SETUP,
             RESET
-        } type; ///< action type
+        };
+
+        Action type; ///< action type
 
         MobilityStateParameters mobilityStateParameters; ///< mobility state parameters
         SpeedStateScaleFactors timeToTriggerSf;          ///< time to trigger scale factors
@@ -1336,6 +1350,9 @@
      */
     MemberLteUeRrcSapUser(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteUeRrcSapUser() = delete;
+
     // inherited from LteUeRrcSapUser
     void Setup(SetupParameters params) override;
     void SendRrcConnectionRequest(RrcConnectionRequest msg) override;
@@ -1349,7 +1366,6 @@
     void SendIdealUeContextRemoveRequest(uint16_t rnti) override;
 
   private:
-    MemberLteUeRrcSapUser();
     C* m_owner; ///< the owner class
 };
 
@@ -1360,11 +1376,6 @@
 }
 
 template <class C>
-MemberLteUeRrcSapUser<C>::MemberLteUeRrcSapUser()
-{
-}
-
-template <class C>
 void
 MemberLteUeRrcSapUser<C>::Setup(SetupParameters params)
 {
@@ -1438,6 +1449,9 @@
      */
     MemberLteUeRrcSapProvider(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteUeRrcSapProvider() = delete;
+
     // methods inherited from LteUeRrcSapProvider go here
     void CompleteSetup(CompleteSetupParameters params) override;
     void RecvSystemInformation(SystemInformation msg) override;
@@ -1449,7 +1463,6 @@
     void RecvRrcConnectionReject(RrcConnectionReject msg) override;
 
   private:
-    MemberLteUeRrcSapProvider();
     C* m_owner; ///< the owner class
 };
 
@@ -1460,11 +1473,6 @@
 }
 
 template <class C>
-MemberLteUeRrcSapProvider<C>::MemberLteUeRrcSapProvider()
-{
-}
-
-template <class C>
 void
 MemberLteUeRrcSapProvider<C>::CompleteSetup(CompleteSetupParameters params)
 {
@@ -1537,8 +1545,10 @@
      */
     MemberLteEnbRrcSapUser(C* owner);
 
-    // inherited from LteEnbRrcSapUser
+    // Delete default constructor to avoid misuse
+    MemberLteEnbRrcSapUser() = delete;
 
+    // inherited from LteEnbRrcSapUser
     void SetupUe(uint16_t rnti, SetupUeParameters params) override;
     void RemoveUe(uint16_t rnti) override;
     void SendSystemInformation(uint16_t cellId, SystemInformation msg) override;
@@ -1555,7 +1565,6 @@
     RrcConnectionReconfiguration DecodeHandoverCommand(Ptr<Packet> p) override;
 
   private:
-    MemberLteEnbRrcSapUser();
     C* m_owner; ///< the owner class
 };
 
@@ -1566,11 +1575,6 @@
 }
 
 template <class C>
-MemberLteEnbRrcSapUser<C>::MemberLteEnbRrcSapUser()
-{
-}
-
-template <class C>
 void
 MemberLteEnbRrcSapUser<C>::SetupUe(uint16_t rnti, SetupUeParameters params)
 {
@@ -1680,8 +1684,10 @@
      */
     MemberLteEnbRrcSapProvider(C* owner);
 
-    // methods inherited from LteEnbRrcSapProvider go here
+    // Delete default constructor to avoid misuse
+    MemberLteEnbRrcSapProvider() = delete;
 
+    // methods inherited from LteEnbRrcSapProvider go here
     void CompleteSetupUe(uint16_t rnti, CompleteSetupUeParameters params) override;
     void RecvRrcConnectionRequest(uint16_t rnti, RrcConnectionRequest msg) override;
     void RecvRrcConnectionSetupCompleted(uint16_t rnti, RrcConnectionSetupCompleted msg) override;
@@ -1697,7 +1703,6 @@
     void RecvIdealUeContextRemoveRequest(uint16_t rnti) override;
 
   private:
-    MemberLteEnbRrcSapProvider();
     C* m_owner; ///< the owner class
 };
 
@@ -1707,11 +1712,6 @@
 {
 }
 
-template <class C>
-MemberLteEnbRrcSapProvider<C>::MemberLteEnbRrcSapProvider()
-{
-}
-
 template <class C>
 void
 MemberLteEnbRrcSapProvider<C>::CompleteSetupUe(uint16_t rnti, CompleteSetupUeParameters params)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-spectrum-phy.cc ns-3.38/src/lte/model/lte-spectrum-phy.cc
--- ns-3.37/src/lte/model/lte-spectrum-phy.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-spectrum-phy.cc	2023-03-16 20:54:23.776869000 -0700
@@ -56,7 +56,8 @@
 /// Effective coding rate
 static const double EffectiveCodingRate[29] = {
     0.08, 0.1, 0.11, 0.15, 0.19, 0.24, 0.3, 0.37, 0.44, 0.51, 0.3, 0.33, 0.37, 0.42, 0.48,
-    0.54, 0.6, 0.43, 0.45, 0.5,  0.55, 0.6, 0.65, 0.7,  0.75, 0.8, 0.85, 0.89, 0.92};
+    0.54, 0.6, 0.43, 0.45, 0.5,  0.55, 0.6, 0.65, 0.7,  0.75, 0.8, 0.85, 0.89, 0.92,
+};
 
 TbId_t::TbId_t()
 {
@@ -922,7 +923,7 @@
     it = m_expectedTbs.find(tbId);
     if (it != m_expectedTbs.end())
     {
-        // migth be a TB of an unreceived packet (due to high progpalosses)
+        // might be a TB of an unreceived packet (due to high progpalosses)
         m_expectedTbs.erase(it);
     }
     // insert new entry
@@ -972,9 +973,9 @@
 
     while (itTb != m_expectedTbs.end())
     {
-        if ((m_dataErrorModelEnabled) &&
-            (m_rxPacketBurstList.size() >
-             0)) // avoid to check for errors when there is no actual data transmitted
+        if (m_dataErrorModelEnabled &&
+            !m_rxPacketBurstList
+                 .empty()) // avoid to check for errors when there is no actual data transmitted
         {
             // retrieve HARQ info
             HarqProcessInfoList_t harqInfoList;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-spectrum-value-helper.cc ns-3.38/src/lte/model/lte-spectrum-value-helper.cc
--- ns-3.37/src/lte/model/lte-spectrum-value-helper.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-spectrum-value-helper.cc	2023-03-16 20:54:23.776869000 -0700
@@ -33,7 +33,7 @@
 /**
  * \brief Stream insertion operator.
  *
- * \note This function scope is stricly local, and can not be
+ * \note This function scope is strictly local, and can not be
  * used in other source files.
  *
  * \param [in] os The reference to the output stream.
@@ -66,7 +66,7 @@
  * filter: awk '{if ((NR % 7) == 1) printf("{"); printf ("%s",$0); if ((NR % 7) == 0)
  * printf("},\n"); else printf(", ");}' | sed 's/ – /, /g'
  */
-static const struct EutraChannelNumbers
+struct EutraChannelNumbers
 {
     uint8_t band;       ///< band
     double fDlLow;      ///<  DL low
@@ -77,7 +77,10 @@
     uint32_t nOffsUl;   ///< number offset UL
     uint32_t rangeNul1; ///< range UL 1
     uint32_t rangeNul2; ///< range UL 2
-} g_eutraChannelNumbers[] = {
+};
+
+/// Eutra channel numbers
+static const EutraChannelNumbers g_eutraChannelNumbers[]{
     {1, 2110, 0, 0, 599, 1920, 18000, 18000, 18599},
     {2, 1930, 600, 600, 1199, 1850, 18600, 18600, 19199},
     {3, 1805, 1200, 1200, 1949, 1710, 19200, 19200, 19949},
@@ -104,7 +107,8 @@
     {37, 1910, 37550, 37550, 37749, 1910, 37550, 37550, 37749},
     {38, 2570, 37750, 37750, 38249, 2570, 37750, 37750, 38249},
     {39, 1880, 38250, 38250, 38649, 1880, 38250, 38250, 38649},
-    {40, 2300, 38650, 38650, 39649, 2300, 38650, 38650, 39649}}; ///< eutra channel numbers
+    {40, 2300, 38650, 38650, 39649, 2300, 38650, 38650, 39649},
+};
 
 /// number of EUTRA bands
 #define NUM_EUTRA_BANDS (sizeof(g_eutraChannelNumbers) / sizeof(EutraChannelNumbers))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-spectrum-value-helper.h ns-3.38/src/lte/model/lte-spectrum-value-helper.h
--- ns-3.37/src/lte/model/lte-spectrum-value-helper.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-spectrum-value-helper.h	2023-03-16 20:54:23.776869000 -0700
@@ -90,7 +90,7 @@
     /**
      *
      *
-     * \param txBandwidthConf the tranmission bandwidth
+     * \param txBandwidthConf the transmission bandwidth
      * configuration in number of resource blocks
      *
      * \return the nominal channel bandwidth in Hz as per 3GPP TS 36.101
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-ccm-rrc-sap.h ns-3.38/src/lte/model/lte-ue-ccm-rrc-sap.h
--- ns-3.37/src/lte/model/lte-ue-ccm-rrc-sap.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-ccm-rrc-sap.h	2023-03-16 20:54:23.776869000 -0700
@@ -95,7 +95,7 @@
      * \brief Add the Signal Bearer for a specific Ue in LteUeComponenCarrierManager
      * \param lcid the Logical Channel Id
      * \param lcConfig this structure it is hard-coded in the LteEnbRrc
-     * \param msu it is the MacSapUser of the Rlc istance
+     * \param msu it is the MacSapUser of the Rlc instance
      * \return the LteMacSapUser of the ComponentCarrierManager
      *
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-cphy-sap.h ns-3.38/src/lte/model/lte-ue-cphy-sap.h
--- ns-3.37/src/lte/model/lte-ue-cphy-sap.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-cphy-sap.h	2023-03-16 20:54:23.776869000 -0700
@@ -307,6 +307,9 @@
      */
     MemberLteUeCphySapProvider(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteUeCphySapProvider() = delete;
+
     // inherited from LteUeCphySapProvider
     void Reset() override;
     void StartCellSearch(uint32_t dlEarfcn) override;
@@ -328,7 +331,6 @@
     void SetImsi(uint64_t imsi) override;
 
   private:
-    MemberLteUeCphySapProvider();
     C* m_owner; ///< the owner class
 };
 
@@ -339,11 +341,6 @@
 }
 
 template <class C>
-MemberLteUeCphySapProvider<C>::MemberLteUeCphySapProvider()
-{
-}
-
-template <class C>
 void
 MemberLteUeCphySapProvider<C>::Reset()
 {
@@ -484,6 +481,9 @@
      */
     MemberLteUeCphySapUser(C* owner);
 
+    // Delete default constructor to avoid misuse
+    MemberLteUeCphySapUser() = delete;
+
     // methods inherited from LteUeCphySapUser go here
     void RecvMasterInformationBlock(uint16_t cellId,
                                     LteRrcSap::MasterInformationBlock mib) override;
@@ -495,7 +495,6 @@
     void ResetSyncIndicationCounter() override;
 
   private:
-    MemberLteUeCphySapUser();
     C* m_owner; ///< the owner class
 };
 
@@ -505,11 +504,6 @@
 {
 }
 
-template <class C>
-MemberLteUeCphySapUser<C>::MemberLteUeCphySapUser()
-{
-}
-
 template <class C>
 void
 MemberLteUeCphySapUser<C>::RecvMasterInformationBlock(uint16_t cellId,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-mac.cc ns-3.38/src/lte/model/lte-ue-mac.cc
--- ns-3.37/src/lte/model/lte-ue-mac.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-mac.cc	2023-03-16 20:54:23.776869000 -0700
@@ -362,7 +362,7 @@
         return;
     }
 
-    if (m_ulBsrReceived.size() == 0)
+    if (m_ulBsrReceived.empty())
     {
         NS_LOG_INFO("No BSR report to transmit");
         return;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-phy.cc ns-3.38/src/lte/model/lte-ue-phy.cc
--- ns-3.37/src/lte/model/lte-ue-phy.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-phy.cc	2023-03-16 20:54:23.776869000 -0700
@@ -126,7 +126,10 @@
 ////////////////////////////////////////
 
 /// Map each of UE PHY states to its string representation.
-static const std::string g_uePhyStateName[LteUePhy::NUM_STATES] = {"CELL_SEARCH", "SYNCHRONIZED"};
+static const std::string g_uePhyStateName[LteUePhy::NUM_STATES] = {
+    "CELL_SEARCH",
+    "SYNCHRONIZED",
+};
 
 /**
  * \param s The UE PHY state.
@@ -867,7 +870,7 @@
         }
         dlcqi.m_rnti = m_rnti;
         dlcqi.m_ri = 1;                          // not yet used
-        dlcqi.m_cqiType = CqiListElement_s::P10; // Peridic CQI using PUCCH wideband
+        dlcqi.m_cqiType = CqiListElement_s::P10; // Periodic CQI using PUCCH wideband
         NS_ASSERT_MSG(nLayer > 0, " nLayer negative");
         NS_ASSERT_MSG(nLayer < 3, " nLayer limit is 2s");
         for (uint8_t i = 0; i < nLayer; i++)
@@ -1306,7 +1309,7 @@
         else
         {
             // send only PUCCH (ideal: fake null bandwidth signal)
-            if (ctrlMsg.size() > 0)
+            if (!ctrlMsg.empty())
             {
                 NS_LOG_LOGIC(this << " UE - start TX PUCCH (NO PUSCH)");
                 std::vector<int> dlRb;
@@ -1477,11 +1480,11 @@
         m_dlBandwidth = dlBandwidth;
 
         static const int Type0AllocationRbg[4] = {
-            10, // RGB size 1
-            26, // RGB size 2
-            63, // RGB size 3
-            110 // RGB size 4
-        };      // see table 7.1.6.1-1 of 36.213
+            10,  // RGB size 1
+            26,  // RGB size 2
+            63,  // RGB size 3
+            110, // RGB size 4
+        };       // see table 7.1.6.1-1 of 36.213
         for (int i = 0; i < 4; i++)
         {
             if (dlBandwidth < Type0AllocationRbg[i])
@@ -1650,7 +1653,7 @@
     if (m_downlinkInSync && (m_numOfFrames * 10) == m_numOfQoutEvalSf)
     {
         NS_LOG_LOGIC("At " << Simulator::Now().As(Time::MS)
-                           << " ms UE PHY sending out of snyc indication to UE RRC layer");
+                           << " ms UE PHY sending out of sync indication to UE RRC layer");
         m_ueCphySapUser->NotifyOutOfSync();
         m_numOfFrames = 0;
     }
@@ -1689,7 +1692,7 @@
     if (!m_downlinkInSync && (m_numOfFrames * 10) == m_numOfQinEvalSf)
     {
         NS_LOG_LOGIC("At " << Simulator::Now().As(Time::MS)
-                           << " ms UE PHY sending in snyc indication to UE RRC layer");
+                           << " ms UE PHY sending in sync indication to UE RRC layer");
         m_ueCphySapUser->NotifyInSync();
         m_numOfFrames = 0;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-phy.h ns-3.38/src/lte/model/lte-ue-phy.h
--- ns-3.37/src/lte/model/lte-ue-phy.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-phy.h	2023-03-16 20:54:23.776869000 -0700
@@ -572,7 +572,7 @@
     void DoResetRlfParams();
 
     /**
-     * \brief Start in Snyc detection function
+     * \brief Start in Sync detection function
      *
      * When T310 timer is started, it indicates that physical layer
      * problems are detected at the UE and the recovery process is
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-phy-sap.h ns-3.38/src/lte/model/lte-ue-phy-sap.h
--- ns-3.37/src/lte/model/lte-ue-phy-sap.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-phy-sap.h	2023-03-16 20:54:23.776869000 -0700
@@ -79,7 +79,7 @@
     virtual ~LteUePhySapUser();
 
     /**
-     * \brief Receive Phy Pdu funtion.
+     * \brief Receive Phy Pdu function.
      *
      * It is called by the Phy to notify the MAC of the reception of a new PHY-PDU
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-rrc.cc ns-3.38/src/lte/model/lte-ue-rrc.cc
--- ns-3.37/src/lte/model/lte-ue-rrc.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-rrc.cc	2023-03-16 20:54:23.776869000 -0700
@@ -91,19 +91,21 @@
 }
 
 /// Map each of UE RRC states to its string representation.
-static const std::string g_ueRrcStateName[LteUeRrc::NUM_STATES] = {"IDLE_START",
-                                                                   "IDLE_CELL_SEARCH",
-                                                                   "IDLE_WAIT_MIB_SIB1",
-                                                                   "IDLE_WAIT_MIB",
-                                                                   "IDLE_WAIT_SIB1",
-                                                                   "IDLE_CAMPED_NORMALLY",
-                                                                   "IDLE_WAIT_SIB2",
-                                                                   "IDLE_RANDOM_ACCESS",
-                                                                   "IDLE_CONNECTING",
-                                                                   "CONNECTED_NORMALLY",
-                                                                   "CONNECTED_HANDOVER",
-                                                                   "CONNECTED_PHY_PROBLEM",
-                                                                   "CONNECTED_REESTABLISHING"};
+static const std::string g_ueRrcStateName[LteUeRrc::NUM_STATES] = {
+    "IDLE_START",
+    "IDLE_CELL_SEARCH",
+    "IDLE_WAIT_MIB_SIB1",
+    "IDLE_WAIT_MIB",
+    "IDLE_WAIT_SIB1",
+    "IDLE_CAMPED_NORMALLY",
+    "IDLE_WAIT_SIB2",
+    "IDLE_RANDOM_ACCESS",
+    "IDLE_CONNECTING",
+    "CONNECTED_NORMALLY",
+    "CONNECTED_HANDOVER",
+    "CONNECTED_PHY_PROBLEM",
+    "CONNECTED_REESTABLISHING",
+};
 
 /////////////////////////////
 // ue RRC methods
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-ue-rrc.h ns-3.38/src/lte/model/lte-ue-rrc.h
--- ns-3.37/src/lte/model/lte-ue-rrc.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-ue-rrc.h	2023-03-16 20:54:23.780869000 -0700
@@ -734,7 +734,7 @@
     void StartConnection();
     /**
      * \brief Leave connected mode method
-     * Resets the UE back to an appropiate state depending
+     * Resets the UE back to an appropriate state depending
      * on the nature of cause. For example, the UE is move
      * to the IDLE_START state upon radio link failure. At
      * RRC, all radio bearers except SRB 0 are removed,
@@ -821,7 +821,7 @@
 
     uint8_t m_lastRrcTransactionIdentifier; ///< last RRC transaction identifier
 
-    LteRrcSap::PdschConfigDedicated m_pdschConfigDedicated; ///< the PDSCH condig dedicated
+    LteRrcSap::PdschConfigDedicated m_pdschConfigDedicated; ///< the PDSCH config dedicated
 
     uint16_t m_dlBandwidth; /**< Downlink bandwidth in RBs. */
     uint16_t m_ulBandwidth; /**< Uplink bandwidth in RBs. */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-vendor-specific-parameters.cc ns-3.38/src/lte/model/lte-vendor-specific-parameters.cc
--- ns-3.37/src/lte/model/lte-vendor-specific-parameters.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-vendor-specific-parameters.cc	2023-03-16 20:54:23.780869000 -0700
@@ -35,7 +35,7 @@
 }
 
 uint16_t
-SrsCqiRntiVsp::GetRnti()
+SrsCqiRntiVsp::GetRnti() const
 {
     return (m_rnti);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/lte-vendor-specific-parameters.h ns-3.38/src/lte/model/lte-vendor-specific-parameters.h
--- ns-3.37/src/lte/model/lte-vendor-specific-parameters.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/lte-vendor-specific-parameters.h	2023-03-16 20:54:23.780869000 -0700
@@ -46,7 +46,7 @@
      *
      * \returns the RNTI
      */
-    uint16_t GetRnti();
+    uint16_t GetRnti() const;
 
   private:
     uint16_t m_rnti; ///< the rnti
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/pf-ff-mac-scheduler.cc ns-3.38/src/lte/model/pf-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/pf-ff-mac-scheduler.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/pf-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -36,11 +36,11 @@
 
 /// PF type 0 allocation RBG
 static const int PfType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(PfFfMacScheduler);
 
@@ -371,7 +371,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 PfFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -648,9 +648,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -660,7 +660,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -888,7 +888,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -938,7 +938,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -966,11 +966,13 @@
                     // UE already allocated for HARQ or without HARQ process available -> drop it
                     if (itRnti != rntiAllocated.end())
                     {
-                        NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it).first);
+                        NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx"
+                                          << (uint16_t)(*it).first);
                     }
                     if (!HarqProcessAvailability((*it).first))
                     {
-                        NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it).first);
+                        NS_LOG_DEBUG(this << " RNTI discarded for HARQ id"
+                                          << (uint16_t)(*it).first);
                     }
                     continue;
                 }
@@ -1540,7 +1542,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1590,7 +1592,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1687,7 +1690,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -1972,7 +1975,7 @@
                           << " subframe no. " << (0xF & params.m_sfnSf));
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/pf-ff-mac-scheduler.h ns-3.38/src/lte/model/pf-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/pf-ff-mac-scheduler.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/pf-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -309,9 +309,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/pss-ff-mac-scheduler.cc ns-3.38/src/lte/model/pss-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/pss-ff-mac-scheduler.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/pss-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -39,11 +39,11 @@
 
 /// PSS type 0 allocation RBG
 static const int PssType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(PssFfMacScheduler);
 
@@ -403,7 +403,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 PssFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -680,9 +680,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -692,7 +692,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -920,7 +920,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -970,7 +970,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -990,7 +990,7 @@
         }
     }
 
-    if (ueSet.size() != 0)
+    if (!ueSet.empty())
     { // has data in RLC buffer
 
         // Time Domain scheduler
@@ -1004,11 +1004,11 @@
                 // UE already allocated for HARQ or without HARQ process available -> drop it
                 if (itRnti != rntiAllocated.end())
                 {
-                    NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it).first);
+                    NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it).first);
                 }
                 if (!HarqProcessAvailability((*it).first))
                 {
-                    NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it).first);
+                    NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it).first);
                 }
                 continue;
             }
@@ -1090,7 +1090,7 @@
             }
         } // end of ueSet
 
-        if (ueSet1.size() != 0 || ueSet2.size() != 0)
+        if (!ueSet1.empty() || !ueSet2.empty())
         {
             // sorting UE in ueSet1 and ueSet1 in descending order based on their metric value
             std::sort(ueSet1.rbegin(), ueSet1.rend());
@@ -1904,7 +1904,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1953,7 +1953,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -2051,7 +2052,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -2306,7 +2307,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/pss-ff-mac-scheduler.h ns-3.38/src/lte/model/pss-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/pss-ff-mac-scheduler.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/pss-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -226,7 +226,7 @@
         const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
-     * \brief Sched UL noise inteference request function
+     * \brief Sched UL noise interference request function
      *
      * \param params FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
@@ -314,9 +314,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/rem-spectrum-phy.cc ns-3.38/src/lte/model/rem-spectrum-phy.cc
--- ns-3.37/src/lte/model/rem-spectrum-phy.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/rem-spectrum-phy.cc	2023-03-16 20:54:23.780869000 -0700
@@ -182,7 +182,7 @@
 }
 
 double
-RemSpectrumPhy::GetSinr(double noisePower)
+RemSpectrumPhy::GetSinr(double noisePower) const
 {
     return m_referenceSignalPower / (m_sumPower - m_referenceSignalPower + noisePower);
 }
@@ -194,7 +194,7 @@
 }
 
 bool
-RemSpectrumPhy::IsActive()
+RemSpectrumPhy::IsActive() const
 {
     return m_active;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/rem-spectrum-phy.h ns-3.38/src/lte/model/rem-spectrum-phy.h
--- ns-3.37/src/lte/model/rem-spectrum-phy.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/rem-spectrum-phy.h	2023-03-16 20:54:23.780869000 -0700
@@ -83,7 +83,7 @@
      * \param noisePower the noise power
      * \return the Signal to Noise Ratio calculated
      */
-    double GetSinr(double noisePower);
+    double GetSinr(double noisePower) const;
 
     /**
      * make StartRx a no-op from now on, and mark instance as inactive
@@ -95,7 +95,7 @@
      *
      * \return true if active
      */
-    bool IsActive();
+    bool IsActive() const;
 
     /**
      * Reset the SINR calculator
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/rr-ff-mac-scheduler.cc ns-3.38/src/lte/model/rr-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/rr-ff-mac-scheduler.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/rr-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -38,11 +38,11 @@
 
 /// Type 0 allocation RBG
 static const int Type0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(RrFfMacScheduler);
 
@@ -349,7 +349,7 @@
     return (i.m_rnti < j.m_rnti);
 }
 
-uint8_t
+bool
 RrFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -573,9 +573,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -585,7 +585,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -814,7 +814,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -864,7 +864,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -923,7 +923,7 @@
 
     if (nflows == 0)
     {
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if ((!ret.m_buildDataList.empty()) || (!ret.m_buildRarList.empty()))
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -1311,7 +1311,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1427,7 +1427,7 @@
         {
             // unable to allocate new resource: finish scheduling
             m_nextRntiUl = (*it).first;
-            if (ret.m_dciList.size() > 0)
+            if (!ret.m_dciList.empty())
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
@@ -1446,7 +1446,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++)
@@ -1691,7 +1691,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/rr-ff-mac-scheduler.h ns-3.38/src/lte/model/rr-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/rr-ff-mac-scheduler.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/rr-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -268,9 +268,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/simple-ue-component-carrier-manager.cc ns-3.38/src/lte/model/simple-ue-component-carrier-manager.cc
--- ns-3.37/src/lte/model/simple-ue-component-carrier-manager.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/simple-ue-component-carrier-manager.cc	2023-03-16 20:54:23.780869000 -0700
@@ -277,7 +277,7 @@
         }
         it++;
     }
-    NS_ABORT_MSG_IF(res.size() == 0,
+    NS_ABORT_MSG_IF(res.empty(),
                     "LCID " << lcid << " not found in the ComponentCarrierManager map");
 
     return res;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tdbet-ff-mac-scheduler.cc ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/tdbet-ff-mac-scheduler.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -37,11 +37,11 @@
 
 /// TDBET type 0 allocation RBG
 static const int TdBetType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(TdBetFfMacScheduler);
 
@@ -369,7 +369,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 TdBetFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -598,9 +598,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -610,7 +610,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -838,7 +838,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -888,7 +888,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -935,11 +935,11 @@
             // UE already allocated for HARQ or without HARQ process available -> drop it
             if (itRnti != rntiAllocated.end())
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it).first);
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it).first);
             }
             if (!HarqProcessAvailability((*it).first))
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it).first);
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it).first);
             }
             continue;
         }
@@ -1376,7 +1376,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1422,7 +1422,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1493,7 +1494,7 @@
         {
             // unable to allocate new resource: finish scheduling
             m_nextRntiUl = (*it).first;
-            if (ret.m_dciList.size() > 0)
+            if (!ret.m_dciList.empty())
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
@@ -1512,7 +1513,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -1792,7 +1793,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tdbet-ff-mac-scheduler.h ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/tdbet-ff-mac-scheduler.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -288,9 +288,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tdmt-ff-mac-scheduler.cc ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/tdmt-ff-mac-scheduler.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -37,11 +37,11 @@
 
 /// TDMT type 0 allocation RBG
 static const int TdMtType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(TdMtFfMacScheduler);
 
@@ -358,7 +358,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 TdMtFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -587,9 +587,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -599,7 +599,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -827,7 +827,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -877,7 +877,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -895,11 +895,11 @@
             // UE already allocated for HARQ or without HARQ process available -> drop it
             if (itRnti != rntiAllocated.end())
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it));
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it));
             }
             if (!HarqProcessAvailability((*it)))
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it));
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it));
             }
 
             continue;
@@ -974,7 +974,7 @@
             } // end for RBG free
 
         } // end for RBGs
-        if (tempMap.size() > 0)
+        if (!tempMap.empty())
         {
             allocationMap.insert(std::pair<uint16_t, std::vector<uint16_t>>((*itMax), tempMap));
         }
@@ -1349,7 +1349,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1394,7 +1394,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1465,7 +1466,7 @@
         {
             // unable to allocate new resource: finish scheduling
             m_nextRntiUl = (*it).first;
-            if (ret.m_dciList.size() > 0)
+            if (!ret.m_dciList.empty())
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
@@ -1484,7 +1485,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -1739,7 +1740,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tdmt-ff-mac-scheduler.h ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/tdmt-ff-mac-scheduler.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -280,9 +280,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tdtbfq-ff-mac-scheduler.cc ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/tdtbfq-ff-mac-scheduler.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -38,11 +38,11 @@
 
 /// TDTBFQ type 0 allocation RBG
 static const int TdTbfqType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(TdTbfqFfMacScheduler);
 
@@ -421,7 +421,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 TdTbfqFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -698,9 +698,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -710,7 +710,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -938,7 +938,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -988,7 +988,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -1028,11 +1028,11 @@
             // UE already allocated for HARQ or without HARQ process available -> drop it
             if (itRnti != rntiAllocated.end())
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it).first);
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it).first);
             }
             if (!HarqProcessAvailability((*it).first))
             {
-                NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it).first);
+                NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it).first);
             }
             continue;
         }
@@ -1119,7 +1119,7 @@
             tempMap.push_back(i);
             rbgMap.at(i) = true;
         }
-        if (tempMap.size() > 0)
+        if (!tempMap.empty())
         {
             allocationMap.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>((*itMax).first, tempMap));
@@ -1573,7 +1573,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1622,7 +1622,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1720,7 +1721,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -1975,7 +1976,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tdtbfq-ff-mac-scheduler.h ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/tdtbfq-ff-mac-scheduler.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -297,9 +297,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tta-ff-mac-scheduler.cc ns-3.38/src/lte/model/tta-ff-mac-scheduler.cc
--- ns-3.37/src/lte/model/tta-ff-mac-scheduler.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tta-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -37,11 +37,11 @@
 
 /// TTA type 0 allocation RBG
 static const int TtaType0AllocationRbg[4] = {
-    10, // RGB size 1
-    26, // RGB size 2
-    63, // RGB size 3
-    110 // RGB size 4
-};      // see table 7.1.6.1-1 of 36.213
+    10,  // RGB size 1
+    26,  // RGB size 2
+    63,  // RGB size 3
+    110, // RGB size 4
+};       // see table 7.1.6.1-1 of 36.213
 
 NS_OBJECT_ENSURE_REGISTERED(TtaFfMacScheduler);
 
@@ -358,7 +358,7 @@
     return (lcActive);
 }
 
-uint8_t
+bool
 TtaFfMacScheduler::HarqProcessAvailability(uint16_t rnti)
 {
     NS_LOG_FUNCTION(this << rnti);
@@ -587,9 +587,9 @@
     // Process DL HARQ feedback
     RefreshHarqProcesses();
     // retrieve past HARQ retx buffered
-    if (m_dlInfoListBuffered.size() > 0)
+    if (!m_dlInfoListBuffered.empty())
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             NS_LOG_INFO(this << " Received DL-HARQ feedback");
             m_dlInfoListBuffered.insert(m_dlInfoListBuffered.end(),
@@ -599,7 +599,7 @@
     }
     else
     {
-        if (params.m_dlInfoList.size() > 0)
+        if (!params.m_dlInfoList.empty())
         {
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
@@ -827,7 +827,7 @@
                     }
                 }
 
-                if (rlcPduListPerLc.size() > 0)
+                if (!rlcPduListPerLc.empty())
                 {
                     newEl.m_rlcPduList.push_back(rlcPduListPerLc);
                 }
@@ -877,7 +877,7 @@
     if (rbgAllocatedNum == rbgNum)
     {
         // all the RBGs are already allocated -> exit
-        if ((ret.m_buildDataList.size() > 0) || (ret.m_buildRarList.size() > 0))
+        if (!ret.m_buildDataList.empty() || !ret.m_buildRarList.empty())
         {
             m_schedSapUser->SchedDlConfigInd(ret);
         }
@@ -900,11 +900,11 @@
                     // UE already allocated for HARQ or without HARQ process available -> drop it
                     if (itRnti != rntiAllocated.end())
                     {
-                        NS_LOG_DEBUG(this << " RNTI discared for HARQ tx" << (uint16_t)(*it));
+                        NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it));
                     }
                     if (!HarqProcessAvailability((*it)))
                     {
-                        NS_LOG_DEBUG(this << " RNTI discared for HARQ id" << (uint16_t)(*it));
+                        NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it));
                     }
                     continue;
                 }
@@ -1437,7 +1437,7 @@
 
     if (nflows == 0)
     {
-        if (ret.m_dciList.size() > 0)
+        if (!ret.m_dciList.empty())
         {
             m_allocationMaps.insert(
                 std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
@@ -1482,7 +1482,8 @@
         if ((itRnti != rntiAllocated.end()) || ((*it).second == 0))
         {
             // UE already allocated for UL-HARQ -> skip it
-            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discared, RNTI " << (*it).first);
+            NS_LOG_DEBUG(this << " UE already allocated in HARQ -> discarded, RNTI "
+                              << (*it).first);
             it++;
             if (it == m_ceBsrRxed.end())
             {
@@ -1553,7 +1554,7 @@
         {
             // unable to allocate new resource: finish scheduling
             m_nextRntiUl = (*it).first;
-            if (ret.m_dciList.size() > 0)
+            if (!ret.m_dciList.empty())
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
@@ -1572,7 +1573,7 @@
         else
         {
             // take the lowest CQI value (worst RB)
-            NS_ABORT_MSG_IF((*itCqi).second.size() == 0,
+            NS_ABORT_MSG_IF((*itCqi).second.empty(),
                             "CQI of RNTI = " << (*it).first << " has expired");
             double minSinr = (*itCqi).second.at(uldci.m_rbStart);
             if (minSinr == NO_SINR)
@@ -1827,7 +1828,7 @@
     case UlCqi_s::SRS: {
         // get the RNTI from vendor specific parameters
         uint16_t rnti = 0;
-        NS_ASSERT(params.m_vendorSpecificList.size() > 0);
+        NS_ASSERT(!params.m_vendorSpecificList.empty());
         for (std::size_t i = 0; i < params.m_vendorSpecificList.size(); i++)
         {
             if (params.m_vendorSpecificList.at(i).m_type == SRS_CQI_RNTI_VSP)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/model/tta-ff-mac-scheduler.h ns-3.38/src/lte/model/tta-ff-mac-scheduler.h
--- ns-3.37/src/lte/model/tta-ff-mac-scheduler.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/model/tta-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -280,9 +280,9 @@
      * \brief Return the availability of free process for the RNTI specified
      *
      * \param rnti the RNTI of the UE to be updated
-     * \return the process id  value
+     * \return the availability
      */
-    uint8_t HarqProcessAvailability(uint16_t rnti);
+    bool HarqProcessAvailability(uint16_t rnti);
 
     /**
      * \brief Refresh HARQ processes according to the timers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/epc-test-gtpu.h ns-3.38/src/lte/test/epc-test-gtpu.h
--- ns-3.37/src/lte/test/epc-test-gtpu.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/epc-test-gtpu.h	2023-03-16 20:54:23.780869000 -0700
@@ -27,12 +27,12 @@
 
 /**
  * \ingroup lte
+ * \ingroup tests
  * \defgroup lte-test lte module tests
  */
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for testing GPRS tunnelling protocol header coding and decoding.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/epc-test-s1u-downlink.cc ns-3.38/src/lte/test/epc-test-s1u-downlink.cc
--- ns-3.37/src/lte/test/epc-test-s1u-downlink.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/epc-test-s1u-downlink.cc	2023-03-16 20:54:23.780869000 -0700
@@ -45,7 +45,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Custom structure for testing UE downlink data
  */
@@ -74,7 +73,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Custom structure for testing eNodeB downlink data, contains
  * the list of data structures for UEs
@@ -86,7 +84,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief EpcS1uDlTestCase class
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/epc-test-s1u-uplink.cc ns-3.38/src/lte/test/epc-test-s1u-uplink.cc
--- ns-3.37/src/lte/test/epc-test-s1u-uplink.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/epc-test-s1u-uplink.cc	2023-03-16 20:54:23.780869000 -0700
@@ -52,7 +52,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * A Udp client. Sends UDP packet carrying sequence number and time
  * stamp but also including the EpsBearerTag. This tag is normally
@@ -125,11 +124,12 @@
         TypeId("ns3::EpsBearerTagUdpClient")
             .SetParent<Application>()
             .AddConstructor<EpsBearerTagUdpClient>()
-            .AddAttribute("MaxPackets",
-                          "The maximum number of packets the application will send",
-                          UintegerValue(100),
-                          MakeUintegerAccessor(&EpsBearerTagUdpClient::m_count),
-                          MakeUintegerChecker<uint32_t>())
+            .AddAttribute(
+                "MaxPackets",
+                "The maximum number of packets the application will send (zero means infinite)",
+                UintegerValue(100),
+                MakeUintegerAccessor(&EpsBearerTagUdpClient::m_count),
+                MakeUintegerChecker<uint32_t>())
             .AddAttribute("Interval",
                           "The time to wait between packets",
                           TimeValue(Seconds(1.0)),
@@ -241,7 +241,7 @@
         NS_LOG_INFO("Error while sending " << m_size << " bytes to " << m_peerAddress);
     }
 
-    if (m_sent < m_count)
+    if (m_sent < m_count || m_count == 0)
     {
         m_sendEvent = Simulator::Schedule(m_interval, &EpsBearerTagUdpClient::Send, this);
     }
@@ -249,7 +249,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Custom test structure to hold information of data transmitted in the uplink per UE
  */
@@ -284,7 +283,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Custom structure containing information about data sent in the uplink
  * of eNodeB. Includes the information of the data sent in the uplink per UE.
@@ -296,7 +294,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief EpcS1uUlTestCase class
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-ffr-simple.h ns-3.38/src/lte/test/lte-ffr-simple.h
--- ns-3.37/src/lte/test/lte-ffr-simple.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/lte-ffr-simple.h	2023-03-16 20:54:23.780869000 -0700
@@ -34,7 +34,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Simple Frequency Reuse algorithm implementation which uses only 1 sub-band.
  *                Used to test Downlink Power Allocation. When Simple FR receives UE measurements
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-simple-helper.h ns-3.38/src/lte/test/lte-simple-helper.h
--- ns-3.37/src/lte/test/lte-simple-helper.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/lte-simple-helper.h	2023-03-16 20:54:23.780869000 -0700
@@ -37,7 +37,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief A simplified version of LteHelper, that
  * is used for creation and configuration of LTE entities for testing purposes
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-aggregation-throughput-scale.cc ns-3.38/src/lte/test/lte-test-aggregation-throughput-scale.cc
--- ns-3.37/src/lte/test/lte-test-aggregation-throughput-scale.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/lte-test-aggregation-throughput-scale.cc	2023-03-16 20:54:23.780869000 -0700
@@ -54,6 +54,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteAggregationThroughputScaleTestSuite g_lteAggregationThroughputScaleTestSuite;
 
 LteAggregationThroughputScaleTestCase::LteAggregationThroughputScaleTestCase(std::string name)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-carrier-aggregation.cc ns-3.38/src/lte/test/lte-test-carrier-aggregation.cc
--- ns-3.37/src/lte/test/lte-test-carrier-aggregation.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/lte-test-carrier-aggregation.cc	2023-03-16 20:54:23.780869000 -0700
@@ -255,6 +255,10 @@
     }
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static TestCarrierAggregationSuite lenaTestRrFfMacSchedulerSuite;
 
 std::string
@@ -533,7 +537,7 @@
 }
 
 void
-CarrierAggregationTestCase::WriteResultToFile()
+CarrierAggregationTestCase::WriteResultToFile() const
 {
     std::ofstream dlOutFile;
     dlOutFile.open(dlResultsFileName, std::ofstream::out | std::ofstream::app);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-carrier-aggregation-configuration.cc ns-3.38/src/lte/test/lte-test-carrier-aggregation-configuration.cc
--- ns-3.37/src/lte/test/lte-test-carrier-aggregation-configuration.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/lte-test-carrier-aggregation-configuration.cc	2023-03-16 20:54:23.780869000 -0700
@@ -73,7 +73,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Carrier aggregation configuration test case.
  */
@@ -137,7 +136,7 @@
      */
     std::vector<std::map<uint16_t, ConfigToCheck>> EquallySpacedCcs();
 
-    uint32_t m_numberOfNodes;             ///< Numer of nodes
+    uint32_t m_numberOfNodes;             ///< Number of nodes
     uint16_t m_numberOfComponentCarriers; ///< Number of component carriers
     std::vector<ConfigToCheck>
         m_configToCheck;          ///< Vector containing all the configurations to check
@@ -388,7 +387,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Carrier aggregation configuration test suite.
  */
@@ -451,4 +449,8 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static CarrierAggregationConfigTestSuite g_carrierAggregationConfigTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-carrier-aggregation.h ns-3.38/src/lte/test/lte-test-carrier-aggregation.h
--- ns-3.37/src/lte/test/lte-test-carrier-aggregation.h	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/lte-test-carrier-aggregation.h	2023-03-16 20:54:23.780869000 -0700
@@ -30,8 +30,7 @@
 using namespace ns3;
 
 /**
- * \ingroup wifi-test
- * \ingroup tests wifi-test wifi module tests
+ * \ingroup lte-test
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -85,7 +84,7 @@
                       uint16_t sizeTb,
                       uint8_t componentCarrierId);
     /// Write result to file function
-    void WriteResultToFile();
+    void WriteResultToFile() const;
 
   private:
     void DoRun() override;
@@ -118,8 +117,7 @@
 };
 
 /**
- * \ingroup wifi-test
- * \ingroup tests
+ * \ingroup lte-test
  *
  * \brief Test Carrier Aggregation Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-cell-selection.cc ns-3.38/src/lte/test/lte-test-cell-selection.cc
--- ns-3.37/src/lte/test/lte-test-cell-selection.cc	2022-11-01 17:37:24.663395600 -0700
+++ ns-3.38/src/lte/test/lte-test-cell-selection.cc	2023-03-16 20:54:23.780869000 -0700
@@ -96,6 +96,10 @@
 
 } // end of LteCellSelectionTestSuite::LteCellSelectionTestSuite ()
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteCellSelectionTestSuite g_lteCellSelectionTestSuite;
 
 /*
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -83,7 +83,7 @@
     //    UDP traffic: payload size = 200 bytes, interval = 1 ms
     //    UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) *
     //    1000 byte/sec -> 232000 byte/rate
-    //  Totol bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
+    //  Total bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
     //  1 user -> 232000 * 1 = 232000 < 2196000 -> throughput = 232000 byte/sec
     //  3 user -> 232000 * 3 = 696000 < 2196000 -> througphut = 232000 byte/sec
     //  6 user -> 232000 * 6 = 139200 < 2196000 -> throughput = 232000 byte/sec
@@ -108,7 +108,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1383000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1383000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 139200 > 1383000 -> throughput = 1383000 / 6 = 230500 byte/sec
@@ -134,7 +134,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1191000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1191000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 1392000 > 1191000 -> throughput = 1191000 / 6 = 198500 byte/sec
@@ -161,7 +161,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 775000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 > 775000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 139200 > 775000 -> throughput = 775000 / 6 = 129166 byte/sec
@@ -260,6 +260,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestCqaFfMacSchedulerSuite lenaTestCqaFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E   # 1 ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-cqa-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-cqa-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-cqa-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-cqa-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -31,7 +31,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This is a system test program. The test is based on a scenario with single eNB and several
  * UEs. The goal of the test is validating if the obtained throughput performance is consistent with
@@ -82,7 +81,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This is a system test program. The test is based on a scenario with single eNB and several
  * UEs. The goal of the test is validating if the obtained throughput performance is consistent with
@@ -128,7 +126,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief The test suite for testing CQA scheduler functionality
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-cqi-generation.cc ns-3.38/src/lte/test/lte-test-cqi-generation.cc
--- ns-3.37/src/lte/test/lte-test-cqi-generation.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-cqi-generation.cc	2023-03-16 20:54:23.780869000 -0700
@@ -144,6 +144,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteCqiGenerationTestSuite lteCqiGenerationTestSuite;
 
 LteCqiGenerationTestCase::LteCqiGenerationTestCase(std::string name,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-cqi-generation.h ns-3.38/src/lte/test/lte-test-cqi-generation.h
--- ns-3.37/src/lte/test/lte-test-cqi-generation.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-cqi-generation.h	2023-03-16 20:54:23.780869000 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Cqi Generation Test Suite
  */
@@ -41,7 +40,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This is the test case for testing different configuration of CQI generation.
  *  The topology consists of the two UEs and two eNbs. UEs have the same position,
@@ -97,7 +95,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This test is very similar to LteCqiGenerationTestCase. The difference is that in this
  * test is enabled the downlink power control.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-deactivate-bearer.cc ns-3.38/src/lte/test/lte-test-deactivate-bearer.cc
--- ns-3.37/src/lte/test/lte-test-deactivate-bearer.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-deactivate-bearer.cc	2023-03-16 20:54:23.780869000 -0700
@@ -97,7 +97,11 @@
         TestCase::QUICK);
 }
 
-static LenaTestBearerDeactivateSuite lenaTestBearerDeactivateSuite; ///< the test suite
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
+static LenaTestBearerDeactivateSuite lenaTestBearerDeactivateSuite;
 
 std::string
 LenaDeactivateBearerTestCase::BuildNameString(uint16_t nUser, std::vector<uint16_t> dist)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-deactivate-bearer.h ns-3.38/src/lte/test/lte-test-deactivate-bearer.h
--- ns-3.37/src/lte/test/lte-test-deactivate-bearer.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-deactivate-bearer.h	2023-03-16 20:54:23.780869000 -0700
@@ -10,7 +10,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test case that for testing the deactivation of the bearer
  * functionality. The test consist in deactivating the bearer and checking
@@ -62,7 +61,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief The test suite class for the LenaDeactivateBearerTestCase.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-downlink-power-control.cc ns-3.38/src/lte/test/lte-test-downlink-power-control.cc
--- ns-3.37/src/lte/test/lte-test-downlink-power-control.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-downlink-power-control.cc	2023-03-16 20:54:23.780869000 -0700
@@ -386,6 +386,10 @@
     return rbTxpower;
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteDownlinkPowerControlTestSuite lteDownlinkPowerControlTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-downlink-power-control.h ns-3.38/src/lte/test/lte-test-downlink-power-control.h
--- ns-3.37/src/lte/test/lte-test-downlink-power-control.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-downlink-power-control.h	2023-03-16 20:54:23.780869000 -0700
@@ -31,7 +31,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for the LteDownlinkPowerControlSpectrumValueTestCase.
  *
@@ -52,7 +51,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test SINR calculation in the downlink when power control is used.
  * Test if the difference in power levels are corresponding to the estamated values.
@@ -88,7 +86,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test SINR calculation in the downlink when the power control is used.
  * Test the power control by comparing the downlink data and ctrl power
@@ -117,7 +114,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test if RRC connection reconfiguration messages are properly
  * generated upon the change in the downlink power.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-downlink-sinr.cc ns-3.38/src/lte/test/lte-test-downlink-sinr.cc
--- ns-3.37/src/lte/test/lte-test-downlink-sinr.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-downlink-sinr.cc	2023-03-16 20:54:23.780869000 -0700
@@ -103,6 +103,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteDownlinkSinrTestSuite lteDownlinkSinrTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-downlink-sinr.h ns-3.38/src/lte/test/lte-test-downlink-sinr.h
--- ns-3.37/src/lte/test/lte-test-downlink-sinr.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-downlink-sinr.h	2023-03-16 20:54:23.780869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 1.1 SINR calculation in downlink
  */
@@ -41,7 +40,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief  Test compares if the generated SINR is equal to estimated SINR
  *  value. Test generates several calls to LteSpectrumPhy::StartRx corresponding
@@ -73,7 +71,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief
  *  Test compares if the generated SINR is equal to estimated SINR value.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-earfcn.cc ns-3.38/src/lte/test/lte-test-earfcn.cc
--- ns-3.37/src/lte/test/lte-test-earfcn.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-earfcn.cc	2023-03-16 20:54:23.780869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test case that is testing if the frequency is properly generated
  * from provided EARFCN frequency.
@@ -75,7 +74,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief
  * Test case that is testing if the downlink frequency is properly
@@ -116,7 +114,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief  Test case that is testing if the uplink frequency is properly
  * converted from provided uplink EARFCN frequency value.
@@ -152,7 +149,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for testing correct conversion of frequencies in
  * the downlink and the uplink, and general EARFCN frequencies.
@@ -163,6 +159,10 @@
     LteEarfcnTestSuite();
 };
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteEarfcnTestSuite g_lteEarfcnTestSuite;
 
 LteEarfcnTestSuite::LteEarfcnTestSuite()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-entities.h ns-3.38/src/lte/test/lte-test-entities.h
--- ns-3.37/src/lte/test/lte-test-entities.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-entities.h	2023-03-16 20:54:23.780869000 -0700
@@ -33,7 +33,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This class implements a testing RRC entity
  */
@@ -166,7 +165,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This class implements a testing PDCP entity
  */
@@ -229,7 +227,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This class implements a testing loopback MAC layer
  */
@@ -311,11 +308,11 @@
     void SetRlcHeaderType(uint8_t rlcHeaderType);
 
     /// RCL Header Type enumeration
-    typedef enum
+    enum RlcHeaderType_t
     {
         UM_RLC_HEADER = 0,
         AM_RLC_HEADER = 1,
-    } RlcHeaderType_t; ///< the RLC header type
+    };
 
     /**
      * Set transmit opportunity mode
@@ -324,12 +321,12 @@
     void SetTxOpportunityMode(uint8_t mode);
 
     /// Transmit opportunity mode enumeration
-    typedef enum
+    enum TxOpportunityMode_t
     {
         MANUAL_MODE = 0,
         AUTOMATIC_MODE = 1,
         RANDOM_MODE = 2
-    } TxOpportunityMode_t; ///< transmit opportunity mode
+    };
 
     /**
      * Set transmit opportunity time
@@ -404,7 +401,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief RRC stub providing a testing S1 SAP user to be used with the EpcEnbApplication
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -194,6 +194,10 @@
         TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestFdBetFfMacSchedulerSuite lenaTestFdBetFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E   # 1 ------------------------------
@@ -519,7 +523,7 @@
      */
     for (int i = 0; i < m_nUser; i++)
     {
-        double thrRatio = (double)1 / m_nUser;
+        double thrRatio = 1.0 / m_nUser;
         double estThrRatio = (double)dlDataRxed.at(i) / totalData;
         NS_LOG_INFO("\tUser " << i << " thrRatio " << thrRatio << " estThrRatio " << estThrRatio);
         NS_TEST_ASSERT_MSG_EQ_TOL(estThrRatio, thrRatio, tolerance, " Unfair Throughput!");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -77,9 +76,8 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
- * \brief Test case is simillar to the one defined in
+ * \brief Test case is similar to the one defined in
  * LenaFdBetFfMacSchedulerTestCase1, with the difference that UEs are
  * placed in such a way to experience different SINRs from eNodeB.
  */
@@ -119,7 +117,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for FDBetFfMacScheduler test case.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -170,6 +170,10 @@
     AddTestCase(new LenaFdMtFfMacSchedulerTestCase(1, 100000, 0, 0, errorModel), TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestFdMtFfMacSchedulerSuite lenaTestFdMtFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -77,7 +76,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for LenaFdMtFfMacSchedulerTestCase test case.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
@@ -81,7 +81,7 @@
     //    UDP traffic: payload size = 200 bytes, interval = 1 ms
     //    UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) *
     //    1000 byte/sec -> 232000 byte/rate
-    //  Totol bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
+    //  Total bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
     //  1 user -> 232000 * 1 = 232000 < 2196000 -> throughput = 232000 byte/sec
     //  3 user -> 232000 * 3 = 696000 < 2196000 -> througphut = 232000 byte/sec
     //  6 user -> 232000 * 6 = 139200 < 2196000 -> throughput = 232000 byte/sec
@@ -105,7 +105,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1383000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1383000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 139200 > 1383000 -> throughput = 1383000 / 6 = 230500 byte/sec
@@ -131,7 +131,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1191000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1191000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 1392000 > 1191000 -> throughput = 1191000 / 6 = 198500 byte/sec
@@ -158,7 +158,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 775000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 775000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 139200 > 775000 -> throughput = 775000 / 6 = 129166 byte/sec
@@ -261,6 +261,10 @@
         TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestFdTbfqFfMacSchedulerSuite lenaTestFdTbfqFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E   # 1 ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -83,9 +82,8 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
- * \brief Test case is simillar to the one defined in
+ * \brief Test case is similar to the one defined in
  * LenaFdTbfqFfMacSchedulerTestCase1, with the difference that UEs are
  * placed in such a way to experience different SINRs from eNodeB.
  */
@@ -128,7 +126,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suit for FdTbfqFfMacScheduler test.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-frequency-reuse.cc ns-3.38/src/lte/test/lte-test-frequency-reuse.cc
--- ns-3.37/src/lte/test/lte-test-frequency-reuse.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-frequency-reuse.cc	2023-03-16 20:54:23.780869000 -0700
@@ -433,6 +433,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteFrequencyReuseTestSuite lteFrequencyReuseTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-frequency-reuse.h ns-3.38/src/lte/test/lte-test-frequency-reuse.h
--- ns-3.37/src/lte/test/lte-test-frequency-reuse.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-frequency-reuse.h	2023-03-16 20:54:23.780869000 -0700
@@ -37,7 +37,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test the fractional frequency reuse algorithms.
  */
@@ -49,7 +48,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test frequency reuse algorithm. Test fails if the muted frequencies
  *  are being used.
@@ -77,12 +75,12 @@
 
     /**
      * DL data receive start function
-     * \param spectrumValue the DL data reeive spectrum value
+     * \param spectrumValue the DL data receive spectrum value
      */
     void DlDataRxStart(Ptr<const SpectrumValue> spectrumValue);
     /**
      * UL data receive start function
-     * \param spectrumValue the UL data reeive spectrum value
+     * \param spectrumValue the UL data receive spectrum value
      */
     void UlDataRxStart(Ptr<const SpectrumValue> spectrumValue);
 
@@ -102,7 +100,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test hard frequency reuse algorithm. Test fails if the muted
  *  frequencies are being used.
@@ -123,7 +120,7 @@
      * \param ulSubBandOffset UL subband offset
      * \param ulSubBandwidth UL subbandwidth
      * \param availableDlRb the available DL per RB
-     * \param availableUlRb the avaialbel UL per RB
+     * \param availableUlRb the available UL per RB
      */
     LteHardFrTestCase(std::string name,
                       uint32_t userNum,
@@ -152,9 +149,8 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
- * \brief Test stric frequency reuse algorithm. Test fails if the muted frequencies
+ * \brief Test strict frequency reuse algorithm. Test fails if the muted frequencies
  *  are being used.
  */
 class LteStrictFrTestCase : public LteFrTestCase
@@ -175,7 +171,7 @@
      * \param ulEdgeSubBandOffset UL subband offset
      * \param ulEdgeSubBandwidth UL subbandwidth
      * \param availableDlRb the available DL per RB
-     * \param availableUlRb the avaialbel UL per RB
+     * \param availableUlRb the available UL per RB
      */
     LteStrictFrTestCase(std::string name,
                         uint32_t userNum,
@@ -208,9 +204,8 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
- * \brief Test frequency reuse algorithm by teleporing UEs to different
+ * \brief Test frequency reuse algorithm by teleporting UEs to different
  * parts of area and checking if the frequency is used according to the
  * frequency pattern for different parts of area. Test fails if the muted
  * frequencies for a given part of area are being used by UE.
@@ -229,12 +224,12 @@
 
     /**
      * DL data receive start function
-     * \param spectrumValue the DL receive specturm value
+     * \param spectrumValue the DL receive spectrum value
      */
     void DlDataRxStart(Ptr<const SpectrumValue> spectrumValue);
     /**
      * UL data receive start function
-     * \param spectrumValue the UL receive specturm value
+     * \param spectrumValue the UL receive spectrum value
      */
     void UlDataRxStart(Ptr<const SpectrumValue> spectrumValue);
 
@@ -287,7 +282,7 @@
     uint16_t m_dlBandwidth; ///< the DL bandwidth
     uint16_t m_ulBandwidth; ///< the UL bandwidth
 
-    Time m_teleportTime;             ///< the telport time
+    Time m_teleportTime;             ///< the teleport time
     Ptr<MobilityModel> m_ueMobility; ///< the UE mobility model
 
     double m_expectedDlPower;         ///< the expected DL power
@@ -303,7 +298,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Fr Area Test Case
  */
@@ -325,7 +319,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Soft Fr Area Test Case
  */
@@ -347,7 +340,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Soft Ffr Area Test Case
  */
@@ -369,7 +361,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Enhanced Ffr Area Test Case
  */
@@ -391,7 +382,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Distributed Ffr Area Test Case
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-harq.cc ns-3.38/src/lte/test/lte-test-harq.cc
--- ns-3.37/src/lte/test/lte-test-harq.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-harq.cc	2023-03-16 20:54:23.780869000 -0700
@@ -73,6 +73,10 @@
     AddTestCase(new LenaHarqTestCase(1, 770, 472, 0.06, 209964), TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestHarqSuite lenaTestHarqSuite;
 
 std::string
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-harq.h ns-3.38/src/lte/test/lte-test-harq.h
--- ns-3.37/src/lte/test/lte-test-harq.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-harq.h	2023-03-16 20:54:23.780869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -69,7 +68,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for harq test.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-interference.cc ns-3.38/src/lte/test/lte-test-interference.cc
--- ns-3.37/src/lte/test/lte-test-interference.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-interference.cc	2023-03-16 20:54:23.780869000 -0700
@@ -187,6 +187,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteInterferenceTestSuite lteLinkAdaptationWithInterferenceTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-interference-fr.cc ns-3.38/src/lte/test/lte-test-interference-fr.cc
--- ns-3.37/src/lte/test/lte-test-interference-fr.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-interference-fr.cc	2023-03-16 20:54:23.780869000 -0700
@@ -119,6 +119,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteInterferenceFrTestSuite LteInterferenceFrTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-interference-fr.h ns-3.38/src/lte/test/lte-test-interference-fr.h
--- ns-3.37/src/lte/test/lte-test-interference-fr.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-interference-fr.h	2023-03-16 20:54:23.780869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for the interference test when using different
  * frequency reuse algorithms.Check if the interfence values correspond to
@@ -43,7 +42,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte interference test when using hard frequency reuse algorithm. Check
  * if the interfence values correspond to theoretical values.
@@ -56,7 +54,7 @@
      *
      * \param name the reference name
      * \param d1 distance between ENB and UE
-     * \param d2 distnace between ENB and other UE
+     * \param d2 distance between ENB and other UE
      * \param dlSinr the DL SINR
      * \param ulSinr the UL SINR
      */
@@ -77,7 +75,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte interference test when using strict frequency reuse algorithm.
  */
@@ -89,7 +86,7 @@
      *
      * \param name the reference name
      * \param d1 distance between ENB and UE
-     * \param d2 distnace between ENB and other UE
+     * \param d2 distance between ENB and other UE
      * \param commonDlSinr the DL SINR
      * \param commonUlSinr the UL SINR
      * \param edgeDlSinr the DL SINR
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-interference.h ns-3.38/src/lte/test/lte-test-interference.h
--- ns-3.37/src/lte/test/lte-test-interference.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-interference.h	2023-03-16 20:54:23.780869000 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for interference test.
  */
@@ -40,7 +39,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test that SINR calculation and MCS selection works fine in a
  * multi-cell interference scenario.
@@ -53,7 +51,7 @@
      *
      * \param name the reference name
      * \param d1 distance between ENB and UE
-     * \param d2 distnace between ENB and other UE
+     * \param d2 distance between ENB and other UE
      * \param dlSinr the DL SINR
      * \param ulSinr the UL SINR
      * \param dlSe the DL se
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-ipv6-routing.cc ns-3.38/src/lte/test/lte-test-ipv6-routing.cc
--- ns-3.37/src/lte/test/lte-test-ipv6-routing.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-ipv6-routing.cc	2023-03-16 20:54:23.780869000 -0700
@@ -51,7 +51,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Ipv6 routing test case.
  */
@@ -396,4 +395,8 @@
     AddTestCase(new LteIpv6RoutingTestCase, TestCase::QUICK);
 }
 
-static LteIpv6RoutingTestSuite lteipv6testsuite;
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
+static LteIpv6RoutingTestSuite g_lteipv6testsuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-link-adaptation.cc ns-3.38/src/lte/test/lte-test-link-adaptation.cc
--- ns-3.37/src/lte/test/lte-test-link-adaptation.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-link-adaptation.cc	2023-03-16 20:54:23.780869000 -0700
@@ -105,6 +105,10 @@
     }
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteLinkAdaptationTestSuite lteLinkAdaptationTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-link-adaptation.h ns-3.38/src/lte/test/lte-test-link-adaptation.h
--- ns-3.37/src/lte/test/lte-test-link-adaptation.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-link-adaptation.h	2023-03-16 20:54:23.780869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 1.3 Link adaptation
  */
@@ -39,7 +38,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test that LTE link adaptation works according to the theoretical model.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-mimo.cc ns-3.38/src/lte/test/lte-test-mimo.cc
--- ns-3.37/src/lte/test/lte-test-mimo.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-mimo.cc	2023-03-16 20:54:23.780869000 -0700
@@ -81,6 +81,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestMimoSuite lenaTestMimoSuite;
 
 std::string
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-mimo.h ns-3.38/src/lte/test/lte-test-mimo.h
--- ns-3.37/src/lte/test/lte-test-mimo.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-mimo.h	2023-03-16 20:54:23.780869000 -0700
@@ -32,7 +32,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a
  * single eNB and single UE. The traffic is configured to be in saturation
@@ -85,7 +84,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena Test Mimo Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-pathloss-model.cc ns-3.38/src/lte/test/lte-test-pathloss-model.cc
--- ns-3.37/src/lte/test/lte-test-pathloss-model.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-pathloss-model.cc	2023-03-16 20:54:23.780869000 -0700
@@ -141,6 +141,10 @@
     }
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LtePathlossModelTestSuite ltePathlossModelTestSuite;
 
 LtePathlossModelSystemTestCase::LtePathlossModelSystemTestCase(std::string name,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-pathloss-model.h ns-3.38/src/lte/test/lte-test-pathloss-model.h
--- ns-3.37/src/lte/test/lte-test-pathloss-model.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-pathloss-model.h	2023-03-16 20:54:23.780869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 1.1 pathloss calculation
  */
@@ -41,7 +40,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief  Tests that the BuildingPathlossModel works according to
  * the expected theoretical values. Theoretical reference values
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-pf-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-pf-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-pf-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-pf-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
@@ -211,6 +211,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestPfFfMacSchedulerSuite lenaTestPfFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E   # 1 ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-pf-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-pf-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-pf-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-pf-ff-mac-scheduler.h	2023-03-16 20:54:23.784869000 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -61,7 +60,7 @@
     /**
      * Build name string
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, double dist);
@@ -75,7 +74,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena PfFf Mac Scheduler Test Case 2
  */
@@ -100,7 +98,7 @@
     /**
      * Builds the test name string based on provided parameter values
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, std::vector<double> dist);
@@ -114,7 +112,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief PfFfMacScheduler test suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-phy-error-model.cc ns-3.38/src/lte/test/lte-test-phy-error-model.cc
--- ns-3.37/src/lte/test/lte-test-phy-error-model.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-phy-error-model.cc	2023-03-16 20:54:23.784869000 -0700
@@ -114,6 +114,10 @@
     }
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestPhyErrorModelSuite lenaTestPhyErrorModelSuite;
 
 std::string
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-phy-error-model.h ns-3.38/src/lte/test/lte-test-phy-error-model.h
--- ns-3.37/src/lte/test/lte-test-phy-error-model.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-phy-error-model.h	2023-03-16 20:54:23.784869000 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -78,7 +77,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena Dl Ctrl Phy Error Model Test Case
  */
@@ -108,7 +106,7 @@
     /**
      * Build name string
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \param rngRun the rng run
      * \returns the name string
      */
@@ -124,7 +122,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena Test Phy Error Model Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-primary-cell-change.cc ns-3.38/src/lte/test/lte-test-primary-cell-change.cc
--- ns-3.37/src/lte/test/lte-test-primary-cell-change.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-primary-cell-change.cc	2023-03-16 20:54:23.784869000 -0700
@@ -96,6 +96,10 @@
                 TestCase::QUICK);
 } // end of LtePrimaryCellChangeTestSuite::LtePrimaryCellChangeTestSuite ()
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LtePrimaryCellChangeTestSuite g_ltePrimaryCellChangeTestSuite;
 
 /*
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-pss-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-pss-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-pss-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-pss-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
@@ -81,7 +81,7 @@
     //    UDP traffic: payload size = 200 bytes, interval = 1 ms
     //    UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) *
     //    1000 byte/sec -> 232000 byte/rate
-    //  Totol bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
+    //  Total bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
     //  1 user -> 232000 * 1 = 232000 < 2196000 -> throughput = 232000 byte/sec
     //  3 user -> 232000 * 3 = 696000 < 2196000 -> througphut = 232000 byte/sec
     //  6 user -> 232000 * 6 = 139200 < 2196000 -> throughput = 232000 byte/sec
@@ -106,7 +106,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1383000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1383000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 139200 > 1383000 -> throughput = 1383000 / 6 = 230500 byte/sec
@@ -132,7 +132,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1191000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1191000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 1392000 > 1191000 -> throughput = 1191000 / 6 = 198500 byte/sec
@@ -159,7 +159,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 775000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 > 775000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 139200 > 775000 -> throughput = 775000 / 6 = 129166 byte/sec
@@ -258,6 +258,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestPssFfMacSchedulerSuite lenaTestPssFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E   # 1 ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-pss-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-pss-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-pss-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-pss-ff-mac-scheduler.h	2023-03-16 20:54:23.784869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -66,7 +65,7 @@
     /**
      * Builds the test name string based on provided parameter values
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, double dist);
@@ -82,9 +81,8 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
- * \brief Simillar to the LenaPssFfMacSchedulerTestCase1 with the difference that
+ * \brief Similar to the LenaPssFfMacSchedulerTestCase1 with the difference that
  * UEs are places in such a way to experience different SINRs. Test checks if the
  * achieved throughput in such conditions has expected value.
  */
@@ -111,7 +109,7 @@
     /**
      * Builds the test name string based on provided parameter values
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, std::vector<double> dist);
@@ -126,7 +124,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena Pss Ff Mac Scheduler Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-radio-link-failure.cc ns-3.38/src/lte/test/lte-test-radio-link-failure.cc
--- ns-3.37/src/lte/test/lte-test-radio-link-failure.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-radio-link-failure.cc	2023-03-16 20:54:23.784869000 -0700
@@ -115,6 +115,10 @@
 
 } // end of LteRadioLinkFailureTestSuite::LteRadioLinkFailureTestSuite ()
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteRadioLinkFailureTestSuite g_lteRadioLinkFailureTestSuite;
 
 /*
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-am-e2e.cc ns-3.38/src/lte/test/lte-test-rlc-am-e2e.cc
--- ns-3.37/src/lte/test/lte-test-rlc-am-e2e.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-am-e2e.cc	2023-03-16 20:54:23.784869000 -0700
@@ -47,9 +47,11 @@
     // NS_LOG_INFO ("Creating LteRlcAmE2eTestSuite");
 
     double losses[] = {0.0, 0.05, 0.10, 0.15, 0.25, 0.50, 0.75, 0.90, 0.95};
-    uint32_t runs[] = {1111,  2222,  3333,  4444,  5555,  6666,  7777,  8888,  9999,  11110,
-                       12221, 13332, 14443, 15554, 16665, 17776, 18887, 19998, 21109, 22220,
-                       23331, 24442, 25553, 26664, 27775, 28886, 29997, 31108, 32219, 33330};
+    uint32_t runs[] = {
+        1111,  2222,  3333,  4444,  5555,  6666,  7777,  8888,  9999,  11110,
+        12221, 13332, 14443, 15554, 16665, 17776, 18887, 19998, 21109, 22220,
+        23331, 24442, 25553, 26664, 27775, 28886, 29997, 31108, 32219, 33330,
+    };
 
     for (uint32_t l = 0; l < (sizeof(losses) / sizeof(double)); l++)
     {
@@ -96,6 +98,10 @@
     }
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteRlcAmE2eTestSuite lteRlcAmE2eTestSuite;
 
 LteRlcAmE2eTestCase::LteRlcAmE2eTestCase(std::string name,
@@ -264,7 +270,7 @@
     //      retransmitted is much lower. This effect can be best noteed
     //      at very high loss rates, and can be adjusted by timers and
     //      params.
-    //   2) throuhgput is not meaningful, you need to evaluate the time
+    //   2) throughput is not meaningful, you need to evaluate the time
     //      it takes for all PDUs to be (re)transmitted successfully,
     //      i.e., how long it takes for the TX and reTX queues to deplete.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-am-e2e.h ns-3.38/src/lte/test/lte-test-rlc-am-e2e.h
--- ns-3.37/src/lte/test/lte-test-rlc-am-e2e.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-am-e2e.h	2023-03-16 20:54:23.784869000 -0700
@@ -33,7 +33,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for RlcAmE2e test case.
  */
@@ -45,7 +44,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * Test cases used for the test suite lte-rlc-am-e2e. See the testing section of
  * the LTE module documentation for details.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-am-transmitter.cc ns-3.38/src/lte/test/lte-test-rlc-am-transmitter.cc
--- ns-3.37/src/lte/test/lte-test-rlc-am-transmitter.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-am-transmitter.cc	2023-03-16 20:54:23.784869000 -0700
@@ -47,6 +47,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteRlcAmTransmitterTestSuite lteRlcAmTransmitterTestSuite;
 
 LteRlcAmTransmitterTestCase::LteRlcAmTransmitterTestCase(std::string name)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-am-transmitter.h ns-3.38/src/lte/test/lte-test-rlc-am-transmitter.h
--- ns-3.37/src/lte/test/lte-test-rlc-am-transmitter.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-am-transmitter.h	2023-03-16 20:54:23.784869000 -0700
@@ -38,7 +38,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief TestSuite 4.1.1 RLC AM: Only transmitter functionality.
  */
@@ -50,7 +49,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test case used by LteRlcAmTransmitterOneSduTestCase to create topology
  * and to implement functionalities and check if data received corresponds to
@@ -71,7 +69,7 @@
     /**
      * Check data received function
      * \param time the time to check
-     * \param shouldReceived shoul dhave received indicator
+     * \param shouldReceived should have received indicator
      * \param assertMsg the assert message
      */
     void CheckDataReceived(Time time, std::string shouldReceived, std::string assertMsg);
@@ -86,7 +84,7 @@
   private:
     /**
      * Check data received function
-     * \param shouldReceived shoul dhave received indicator
+     * \param shouldReceived should have received indicator
      * \param assertMsg the assert message
      */
     void DoCheckDataReceived(std::string shouldReceived, std::string assertMsg);
@@ -94,7 +92,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.1 Test that SDU transmitted at PDCP corresponds to PDU
  * received by MAC.
@@ -117,7 +114,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.2 Test the correct functionality of the Segmentation.
  * Test check that single SDU is properly segmented to n PDUs.
@@ -140,7 +136,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.3 Test that concatenation functionality works properly.
  * Test check if n SDUs are correctly contactenate to single PDU.
@@ -163,7 +158,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.4 Test checks functionality of Report Buffer Status by
  * testing primitive parameters.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-um-e2e.cc ns-3.38/src/lte/test/lte-test-rlc-um-e2e.cc
--- ns-3.37/src/lte/test/lte-test-rlc-um-e2e.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-um-e2e.cc	2023-03-16 20:54:23.784869000 -0700
@@ -75,6 +75,10 @@
     }
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteRlcUmE2eTestSuite lteRlcUmE2eTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-um-e2e.h ns-3.38/src/lte/test/lte-test-rlc-um-e2e.h
--- ns-3.37/src/lte/test/lte-test-rlc-um-e2e.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-um-e2e.h	2023-03-16 20:54:23.784869000 -0700
@@ -32,7 +32,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for RlcUmE2eTestCase
  */
@@ -44,7 +43,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test end-to-end flow when RLC UM is being used.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-um-transmitter.cc ns-3.38/src/lte/test/lte-test-rlc-um-transmitter.cc
--- ns-3.37/src/lte/test/lte-test-rlc-um-transmitter.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-um-transmitter.cc	2023-03-16 20:54:23.784869000 -0700
@@ -49,6 +49,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteRlcUmTransmitterTestSuite lteRlcUmTransmitterTestSuite;
 
 LteRlcUmTransmitterTestCase::LteRlcUmTransmitterTestCase(std::string name)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rlc-um-transmitter.h ns-3.38/src/lte/test/lte-test-rlc-um-transmitter.h
--- ns-3.37/src/lte/test/lte-test-rlc-um-transmitter.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rlc-um-transmitter.h	2023-03-16 20:54:23.784869000 -0700
@@ -38,7 +38,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief TestSuite 4.1.1 for RLC UM: Only transmitter part.
  */
@@ -50,7 +49,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test case used by LteRlcUmTransmitterOneSduTestCase to create topology
  * and to implement functionalities and check if data received corresponds to
@@ -71,7 +69,7 @@
     /**
      * Check data received function
      * \param time the time to check
-     * \param shouldReceived shoul dhave received indicator
+     * \param shouldReceived should have received indicator
      * \param assertMsg the assert message
      */
     void CheckDataReceived(Time time, std::string shouldReceived, std::string assertMsg);
@@ -94,7 +92,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.1 One SDU, One PDU
  */
@@ -116,7 +113,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.2 Segmentation (One SDU => n PDUs)
  */
@@ -138,7 +134,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.3 Concatenation (n SDUs => One PDU)
  */
@@ -160,7 +155,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 4.1.1.4 Report Buffer Status (test primitive parameters)
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rr-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-rr-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-rr-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rr-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
@@ -176,6 +176,10 @@
     AddTestCase(new LenaRrFfMacSchedulerTestCase(1, 100000, 0, 0, errorModel), TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestRrFfMacSchedulerSuite lenaTestRrFfMacSchedulerSuite;
 
 std::string
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-rr-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-rr-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-rr-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-rr-ff-mac-scheduler.h	2023-03-16 20:54:23.784869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * This system test program creates different test cases with a single eNB and
@@ -75,7 +74,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for RrFfMacScheduler test
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-secondary-cell-handover.cc ns-3.38/src/lte/test/lte-test-secondary-cell-handover.cc
--- ns-3.37/src/lte/test/lte-test-secondary-cell-handover.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-secondary-cell-handover.cc	2023-03-16 20:54:23.784869000 -0700
@@ -39,7 +39,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test measurement-based handover to secondary cell.
  */
@@ -196,7 +195,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief LTE measurement-based handover to secondary cell test suite.
  */
@@ -213,4 +211,8 @@
     AddTestCase(new LteSecondaryCellHandoverTestCase("Real RRC", false), TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteSecondaryCellHandoverTestSuite g_lteSecondaryCellHandoverTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-secondary-cell-selection.cc ns-3.38/src/lte/test/lte-test-secondary-cell-selection.cc
--- ns-3.37/src/lte/test/lte-test-secondary-cell-selection.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-secondary-cell-selection.cc	2023-03-16 20:54:23.784869000 -0700
@@ -67,6 +67,10 @@
 
 } // end of LteSecondaryCellSelectionTestSuite::LteSecondaryCellSelectionTestSuite ()
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteSecondaryCellSelectionTestSuite g_lteSecondaryCellSelectionTestSuite;
 
 /*
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-spectrum-value-helper.cc ns-3.38/src/lte/test/lte-test-spectrum-value-helper.cc
--- ns-3.37/src/lte/test/lte-test-spectrum-value-helper.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-spectrum-value-helper.cc	2023-03-16 20:54:23.784869000 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test checks if lte spectrum model is generated properly. Different
  * test cases are configured by defining different frequency and banwidth.
@@ -41,7 +40,7 @@
      *
      * \param str the test reference name
      * \param earfcn the central carrier frequency
-     * \param bw bandwidth tha bandwidth
+     * \param bw bandwidth the bandwidth
      * \param fcs the expected spectrum model
      */
     LteSpectrumModelTestCase(const char* str, uint16_t earfcn, uint8_t bw, std::vector<double> fcs);
@@ -82,7 +81,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test that the function for creation of LTE noise power spectral
  * density is working properly.
@@ -144,7 +142,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test that the funtcion for the creation of the Lte transmission power
  * spectral density is working as expected.
@@ -210,7 +207,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for LteSpectrumValueHelper. Test suite is
  * checking different functionalities of LteSpectrumValueHelper.
@@ -221,6 +217,10 @@
     LteSpectrumValueHelperTestSuite();
 };
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteSpectrumValueHelperTestSuite g_lteSpectrumValueHelperTestSuite;
 
 LteSpectrumValueHelperTestSuite::LteSpectrumValueHelperTestSuite()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
@@ -194,6 +194,10 @@
         TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestTdBetFfMacSchedulerSuite lenaTestTdBetFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E   # 1 ------------------------------
@@ -524,7 +528,7 @@
      */
     for (int i = 0; i < m_nUser; i++)
     {
-        double thrRatio = (double)1 / m_nUser;
+        double thrRatio = 1.0 / m_nUser;
         double estThrRatio = (double)dlDataRxed.at(i) / totalData;
         NS_LOG_INFO("\tUser " << i << " thrRatio " << thrRatio << " estThrRatio " << estThrRatio);
         NS_TEST_ASSERT_MSG_EQ_TOL(estThrRatio, thrRatio, tolerance, " Unfair Throughput!");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h	2023-03-16 20:54:23.784869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -62,7 +61,7 @@
     /**
      * Builds the test name string based on provided parameter values
      * \param nUser the number of UE nodes
-     * \param dist the distnace between UE nodes and eNodeB
+     * \param dist the distance between UE nodes and eNodeB
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, double dist);
@@ -76,7 +75,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena TdBet Ff Mac Scheduler Test Case
  */
@@ -115,7 +113,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for TdBetFfMacScheduler tests.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
@@ -169,6 +169,10 @@
     AddTestCase(new LenaTdMtFfMacSchedulerTestCase(1, 100000, 0, 0, errorModel), TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestTdMtFfMacSchedulerSuite lenaTestTdMtFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h	2023-03-16 20:54:23.784869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -62,7 +61,7 @@
     /**
      * Builds the test name string based on provided parameter values
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, double dist);
@@ -76,7 +75,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for TdMtFfMacScheduler tests.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
@@ -81,7 +81,7 @@
     //    UDP traffic: payload size = 200 bytes, interval = 1 ms
     //    UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) *
     //    1000 byte/sec -> 232000 byte/rate
-    //  Totol bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
+    //  Total bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec
     //  1 user -> 232000 * 1 = 232000 < 2196000 -> throughput = 232000 byte/sec
     //  3 user -> 232000 * 3 = 696000 < 2196000 -> througphut = 232000 byte/sec
     //  6 user -> 232000 * 6 = 139200 < 2196000 -> throughput = 232000 byte/sec
@@ -105,7 +105,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1383000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1383000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 1392000 > 1383000 -> throughput = 1383000 / 6 = 230500 byte/sec
@@ -131,7 +131,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 18 -> 1191 -> 1191000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 1191000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 1191000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 1392000 > 1191000 -> throughput = 1191000 / 6 = 198500 byte/sec
@@ -158,7 +158,7 @@
     //   UDP traffic: payload size = 200 bytes, interval = 1 ms
     //   UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000
     //   byte/sec -> 232000 byte/rate
-    // Totol bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
+    // Total bandwidth: 24 PRB at Itbs 13 -> 775 -> 775000 byte/sec
     // 1 user -> 903000 * 1 = 232000 < 775000 -> throughput = 232000 byte/sec
     // 3 user -> 232000 * 3 = 696000 < 775000 -> througphut = 232000 byte/sec
     // 6 user -> 232000 * 6 = 1392000 > 775000 -> throughput = 775000 / 6 = 129166 byte/sec
@@ -261,6 +261,10 @@
         TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestTdTbfqFfMacSchedulerSuite lenaTestTdTbfqFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E   # 1 ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h	2023-03-16 20:54:23.784869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -66,7 +65,7 @@
     /**
      * Builds the test name string based on provided parameter values
      * \param nUser the number of UE nodes
-     * \param dist the distnace between UE nodes and eNodeB
+     * \param dist the distance between UE nodes and eNodeB
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, double dist);
@@ -82,7 +81,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena TdTbfq Ff Mac Scheduler Test Case 2
  */
@@ -109,7 +107,7 @@
     /**
      * Builds the test name string based on provided parameter values
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, std::vector<double> dist);
@@ -124,7 +122,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for TdTbfqFfMacScheduler test.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tta-ff-mac-scheduler.cc ns-3.38/src/lte/test/lte-test-tta-ff-mac-scheduler.cc
--- ns-3.37/src/lte/test/lte-test-tta-ff-mac-scheduler.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tta-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
@@ -169,6 +169,10 @@
     AddTestCase(new LenaTtaFfMacSchedulerTestCase(1, 100000, 0, 0, errorModel), TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LenaTestTtaFfMacSchedulerSuite lenaTestTtaFfMacSchedulerSuite;
 
 // --------------- T E S T - C A S E ------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-tta-ff-mac-scheduler.h ns-3.38/src/lte/test/lte-test-tta-ff-mac-scheduler.h
--- ns-3.37/src/lte/test/lte-test-tta-ff-mac-scheduler.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-tta-ff-mac-scheduler.h	2023-03-16 20:54:23.784869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This system test program creates different test cases with a single eNB and
  * several UEs, all having the same Radio Bearer specification. In each test
@@ -62,7 +61,7 @@
     /**
      * Build name string
      * \param nUser the number of UE nodes
-     * \param dist the distnace between nodes
+     * \param dist the distance between nodes
      * \returns the name string
      */
     static std::string BuildNameString(uint16_t nUser, double dist);
@@ -76,7 +75,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lena Test Tta Ff Mac Scheduler Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-ue-measurements.cc ns-3.38/src/lte/test/lte-test-ue-measurements.cc
--- ns-3.37/src/lte/test/lte-test-ue-measurements.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-ue-measurements.cc	2023-03-16 20:54:23.784869000 -0700
@@ -232,6 +232,10 @@
                 TestCase::EXTENSIVE);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteUeMeasurementsTestSuite lteUeMeasurementsTestSuite;
 
 /*
@@ -693,6 +697,10 @@
 
 } // end of LteUeMeasurementsPiecewiseTestSuite1::LteUeMeasurementsPiecewiseTestSuite1
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteUeMeasurementsPiecewiseTestSuite1 lteUeMeasurementsPiecewiseTestSuite1;
 
 /*
@@ -1392,6 +1400,10 @@
 
 } // end of LteUeMeasurementsPiecewiseTestSuite2::LteUeMeasurementsPiecewiseTestSuite2
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteUeMeasurementsPiecewiseTestSuite2 lteUeMeasurementsPiecewiseTestSuite2;
 
 /*
@@ -1584,7 +1596,7 @@
                  << " dB)");
 
         // verifying reported best cells
-        if (measResults.measResultListEutra.size() == 0)
+        if (measResults.measResultListEutra.empty())
         {
             NS_TEST_ASSERT_MSG_EQ(measResults.haveMeasResultNeighCells,
                                   false,
@@ -1716,6 +1728,10 @@
                 TestCase::QUICK);
 } // end of LteUeMeasurementsPiecewiseTestSuite3::LteUeMeasurementsPiecewiseTestSuite3
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteUeMeasurementsPiecewiseTestSuite3 lteUeMeasurementsPiecewiseTestSuite3;
 
 /*
@@ -1873,7 +1889,7 @@
                  << " dB)");
 
         // verifying reported best cells
-        if (measResults.measResultListEutra.size() == 0)
+        if (measResults.measResultListEutra.empty())
         {
             NS_TEST_ASSERT_MSG_EQ(measResults.haveMeasResultNeighCells,
                                   false,
@@ -2264,6 +2280,10 @@
 
 } // end of LteUeMeasurementsHandoverTestSuite::LteUeMeasurementsHandoverTestSuite
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteUeMeasurementsHandoverTestSuite lteUeMeasurementsHandoverTestSuite;
 
 /*
@@ -2497,7 +2517,7 @@
                  << " dB)");
 
         // verifying reported best cells
-        if (measResults.measResultListEutra.size() == 0)
+        if (measResults.measResultListEutra.empty())
         {
             NS_TEST_ASSERT_MSG_EQ(measResults.haveMeasResultNeighCells,
                                   false,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-ue-measurements.h ns-3.38/src/lte/test/lte-test-ue-measurements.h
--- ns-3.37/src/lte/test/lte-test-ue-measurements.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-ue-measurements.h	2023-03-16 20:54:23.784869000 -0700
@@ -45,7 +45,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test that UE Measurements (see 36.214) calculation works fine in a
  * multi-cell interference scenario.
@@ -58,7 +57,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test that UE measurements calculation works properly in a scenario
  * with 2 eNodeBs and 2UEs. Test that RSRP and RSRQ of the serving cell and of
@@ -128,7 +126,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for generating calls to UE measurements test case
  *        ns3::LteUeMeasurementsPiecewiseTestCase1.
@@ -141,15 +138,14 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Testing UE measurements in LTE with simulation of 1 eNodeB and 1 UE in
  *        piecewise configuration and 120 ms report interval. During the simulation
  *        the placement of UE is being changed several times. Four different
  *        cases are considered: UE is very near to eNodeB, UE is near to eNodeB,
  *        UE is far from eNodeB and UE is very far from eNodeB. Test checks
- *        if the measurments correspond to the real conditions of the UE, i.e.
- *        wnen the signal from serving cell becomes weak, the measurements should
+ *        if the measurements correspond to the real conditions of the UE, i.e.
+ *        when the signal from serving cell becomes weak, the measurements should
  *        also start to correspond to the new channel conditions. Additionally, it
  *        is checked if UE detects some neighboring signal, if it does the test
  *        fails because there is no neighbor in this configuration. Also, test
@@ -265,7 +261,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for generating calls to UE measurements test case
  *        ns3::LteUeMeasurementsPiecewiseTestCase2.
@@ -278,7 +273,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Testing UE measurements in LTE with simulation of 2 eNodeB and 1 UE in
  *        piecewise configuration and 240 ms report interval.
@@ -402,7 +396,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for generating calls to UE measurements test case
  *        ns3::LteUeMeasurementsPiecewiseTestCase3.
@@ -415,7 +408,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Testing UE measurements in LTE with simulation of 3 eNodeB and 1 UE in
  *        piecewise configuration and 240 ms report interval.
@@ -537,7 +529,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test suite for generating calls to UE measurements test case
  *        ns3::LteUeMeasurementsHandoverTestCase.
@@ -550,7 +541,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Testing UE measurements in LTE with simulation of 2 eNodeB and 1 UE in
  *        a handover configuration.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-ue-phy.h ns-3.38/src/lte/test/lte-test-ue-phy.h
--- ns-3.37/src/lte/test/lte-test-ue-phy.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-ue-phy.h	2023-03-16 20:54:23.784869000 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Defines a simplified LtePhy class that is used for testing purposes
  * of downlink and uplink SINR generation. Used in LteDownlinkDataSinrTestCase
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-uplink-power-control.cc ns-3.38/src/lte/test/lte-test-uplink-power-control.cc
--- ns-3.37/src/lte/test/lte-test-uplink-power-control.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-uplink-power-control.cc	2023-03-16 20:54:23.784869000 -0700
@@ -68,6 +68,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteUplinkPowerControlTestSuite lteUplinkPowerControlTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-uplink-power-control.h ns-3.38/src/lte/test/lte-test-uplink-power-control.h
--- ns-3.37/src/lte/test/lte-test-uplink-power-control.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-uplink-power-control.h	2023-03-16 20:54:23.784869000 -0700
@@ -38,7 +38,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 1.1 Uplink Power Control
  */
@@ -50,7 +49,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Uplink Power Control Test Case
  */
@@ -138,7 +136,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Uplink Open Loop Power Control Test Case
  */
@@ -159,7 +156,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Uplink Closed Loop Power Control Absolute Mode Test Case
  */
@@ -180,7 +176,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Uplink Closed Loop Power Control Accumulated Mode Test Case
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-uplink-sinr.cc ns-3.38/src/lte/test/lte-test-uplink-sinr.cc
--- ns-3.37/src/lte/test/lte-test-uplink-sinr.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-uplink-sinr.cc	2023-03-16 20:54:23.784869000 -0700
@@ -122,6 +122,10 @@
                 TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteUplinkSinrTestSuite lteUplinkSinrTestSuite;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/lte-test-uplink-sinr.h ns-3.38/src/lte/test/lte-test-uplink-sinr.h
--- ns-3.37/src/lte/test/lte-test-uplink-sinr.h	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/lte-test-uplink-sinr.h	2023-03-16 20:54:23.784869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test 1.2 SINR calculation in uplink
  */
@@ -41,7 +40,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test generation of SINR in the uplink. Test schedules the signal and the interference
  * signals and it evaluates if the obtained value for SINR corresponds to the theoretical value
@@ -75,7 +73,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief  In this test one signal will be of interest, i.e., the
  *  SRS of the first signal will have the same CellId of the
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/reference/lte_cqi_generation.m ns-3.38/src/lte/test/reference/lte_cqi_generation.m
--- ns-3.37/src/lte/test/reference/lte_cqi_generation.m	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/reference/lte_cqi_generation.m	2023-03-16 20:54:23.784869000 -0700
@@ -9,11 +9,11 @@
 
 nrbsControl = 25; % tx bandwidth configuration in number of RBs
 bwControl = nrbsControl * 180000; % bandwidth in Hz, note that this is smaller than
-		    % the nominal Channel Bandwdith, see TS 36.101 fig 5.6-1
+		    % the nominal Channel Bandwidth, see TS 36.101 fig 5.6-1
 
 nrbsData = 12; % tx bandwidth configuration in number of RBs
 bwData = nrbsData * 180000; % bandwidth in Hz, note that this is smaller than
-		    % the nominal Channel Bandwdith, see TS 36.101 fig 5.6-1
+		    % the nominal Channel Bandwidth, see TS 36.101 fig 5.6-1
 
 kT = -174; % noise PSD in dBm / Hz
 ndBmData = kT + 10*log10(bwData);  % noise power dBm
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/reference/lte_frequency_reuse.m ns-3.38/src/lte/test/reference/lte_frequency_reuse.m
--- ns-3.37/src/lte/test/reference/lte_frequency_reuse.m	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/reference/lte_frequency_reuse.m	2023-03-16 20:54:23.784869000 -0700
@@ -11,7 +11,7 @@
 %%DOWNLINK
 P_A = 0;   %% << change this value to simulate Downlink Power Allocation
 PdschPower = 10.^((dlpdBm+ P_A - 30)/10);
-printf("Expexted Pdsch Power: %f \n", PdschPower)
+printf("Expected Pdsch Power: %f \n", PdschPower)
 
 
 %%UPLINK
@@ -69,5 +69,5 @@
 endif
 
 PuschPower = 10.^((PuschPowerdB - 30 )/10);
-printf("Expexted Pusch Power: %f \n", PuschPower)
+printf("Expected Pusch Power: %f \n", PuschPower)
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/reference/lte_link_budget_interference_fr.m ns-3.38/src/lte/test/reference/lte_link_budget_interference_fr.m
--- ns-3.37/src/lte/test/reference/lte_link_budget_interference_fr.m	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/reference/lte_link_budget_interference_fr.m	2023-03-16 20:54:23.784869000 -0700
@@ -8,7 +8,7 @@
 ful = 1930e6;  % UL carrier freq Hz, EARFCN = 18100
 nrbs = 25; % tx bandwidth configuration in number of RBs
 bw = nrbs * 180000; % bandwidth in Hz, note that this is smaller than
-		    % the nominal Channel Bandwdith, see TS 36.101 fig 5.6-1
+		    % the nominal Channel Bandwidth, see TS 36.101 fig 5.6-1
 kT = -174; % noise PSD in dBm / Hz
 ndBm = kT + 10*log10(bw);  % noise power dBm
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/reference/lte_link_budget_interference.m ns-3.38/src/lte/test/reference/lte_link_budget_interference.m
--- ns-3.37/src/lte/test/reference/lte_link_budget_interference.m	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/reference/lte_link_budget_interference.m	2023-03-16 20:54:23.784869000 -0700
@@ -8,7 +8,7 @@
 ful = 1930e6;  % UL carrier freq Hz, EARFCN = 18100
 nrbs = 25; % tx bandwidth configuration in number of RBs
 bw = nrbs * 180000; % bandwidth in Hz, note that this is smaller than
-		    % the nominal Channel Bandwdith, see TS 36.101 fig 5.6-1
+		    % the nominal Channel Bandwidth, see TS 36.101 fig 5.6-1
 kT = -174; % noise PSD in dBm / Hz
 ndBm = kT + 10*log10(bw);  % noise power dBm
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/reference/lte_link_budget.m ns-3.38/src/lte/test/reference/lte_link_budget.m
--- ns-3.37/src/lte/test/reference/lte_link_budget.m	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/reference/lte_link_budget.m	2023-03-16 20:54:23.784869000 -0700
@@ -7,7 +7,7 @@
 f = 2160e6;  % carrier freq Hz, EARFCN = 500 (downlink)
 nrbs = 25; % tx bandwidth configuration in number of RBs
 bw = nrbs * 180000; % bandwidth in Hz, note that this is smaller than
-		    % the nominal Channel Bandwdith, see TS 36.101 fig 5.6-1
+		    % the nominal Channel Bandwidth, see TS 36.101 fig 5.6-1
 kT = -174; % noise PSD in dBm / Hz
 n = kT + 10*log10(bw);  % noise power dBm
 p = 30;  % tx power dBm
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/reference/lte_ue_measurements.m ns-3.38/src/lte/test/reference/lte_ue_measurements.m
--- ns-3.37/src/lte/test/reference/lte_ue_measurements.m	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/reference/lte_ue_measurements.m	2023-03-16 20:54:23.784869000 -0700
@@ -6,10 +6,10 @@
 
 fdl = 2120e6;  % DL carrier freq Hz, EARFCN = 100
 ful = 1930e6;  % UL carrier freq Hz, EARFCN = 18100
-nrbs = 25; % tx bandwdith configuration in number of RBs
+nrbs = 25; % tx bandwidth configuration in number of RBs
 nres = nrbs * 12;
 bw = 180000 / 12; % bandwidth per RE in Hz
-%%bwtot = xxx %%, note that this is smaller than the nominal Channel Bandwdith, see TS 36.101 fig 5.6-1
+%%bwtot = xxx %%, note that this is smaller than the nominal Channel Bandwidth, see TS 36.101 fig 5.6-1
 kT = -174; % noise PSD in dBm / Hz
 ndBm = kT + 10*log10(bw);  % noise power dBm for a RE
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-asn1-encoding.cc ns-3.38/src/lte/test/test-asn1-encoding.cc
--- ns-3.37/src/lte/test/test-asn1-encoding.cc	2022-11-01 17:37:24.667395600 -0700
+++ ns-3.38/src/lte/test/test-asn1-encoding.cc	2023-03-16 20:54:23.784869000 -0700
@@ -28,13 +28,14 @@
 #include "ns3/string.h"
 #include "ns3/test.h"
 
+#include <iomanip>
+
 using namespace ns3;
 
 NS_LOG_COMPONENT_DEFINE("Asn1EncodingTest");
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Contains ASN encoding test utility functions.
  */
@@ -50,13 +51,13 @@
     {
         uint32_t psize = pkt->GetSize();
         uint8_t buffer[psize];
-        char sbuffer[psize * 3];
+        std::ostringstream oss(std::ostringstream::out);
         pkt->CopyData(buffer, psize);
         for (uint32_t i = 0; i < psize; i++)
         {
-            sprintf(&sbuffer[i * 3], "%02x ", buffer[i]);
+            oss << std::setfill('0') << std::setw(2) << std::hex << +(buffer[i]) << " ";
         }
-        return std::string(sbuffer);
+        return std::string(oss.str() + "\n");
     }
 
     /**
@@ -106,7 +107,6 @@
 // --------------------------- CLASS RrcHeaderTestCase -----------------------------
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief This class provides common functions to be inherited
  * by the children TestCases
@@ -324,7 +324,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Request Test Case
  */
@@ -377,7 +376,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Setup Test Case
  */
@@ -435,7 +433,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Setup Complete Test Case
  */
@@ -489,7 +486,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Reconfiguration Complete Test Case
  */
@@ -544,7 +540,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Reconfiguration Test Case
  */
@@ -783,7 +778,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Handover Preparation Info Test Case
  */
@@ -893,7 +887,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Reestablishment Request Test Case
  */
@@ -953,7 +946,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Reestablishment Test Case
  */
@@ -1010,7 +1002,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Reestablishment Complete Test Case
  */
@@ -1065,7 +1056,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rrc Connection Reject Test Case
  */
@@ -1119,7 +1109,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Measurement Report Test Case
  */
@@ -1257,7 +1246,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Asn1Encoding Test Suite
  */
@@ -1284,4 +1272,8 @@
     AddTestCase(new MeasurementReportTestCase(), TestCase::QUICK);
 }
 
-Asn1EncodingSuite asn1EncodingSuite;
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
+Asn1EncodingSuite g_asn1EncodingSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-epc-tft-classifier.cc ns-3.38/src/lte/test/test-epc-tft-classifier.cc
--- ns-3.37/src/lte/test/test-epc-tft-classifier.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-epc-tft-classifier.cc	2023-03-16 20:54:23.784869000 -0700
@@ -39,7 +39,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test case to check the functionality of the Tft Classifier. Test
  * consist of defining different TFT configurations, i.e. direction, ports,
@@ -206,7 +205,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Epc Tft Classifier Test Suite
  */
@@ -216,6 +214,10 @@
     EpcTftClassifierTestSuite();
 };
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static EpcTftClassifierTestSuite g_lteTftClassifierTestSuite;
 
 EpcTftClassifierTestSuite::EpcTftClassifierTestSuite()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-antenna.cc ns-3.38/src/lte/test/test-lte-antenna.cc
--- ns-3.37/src/lte/test/test-lte-antenna.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-antenna.cc	2023-03-16 20:54:23.784869000 -0700
@@ -41,7 +41,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Tests that the propagation model and the antenna parameters are
  * generate the correct values. Different test cases are created by specifying different
@@ -264,7 +263,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Enb Antenna Test Suite
  */
@@ -310,4 +308,8 @@
     AddTestCase(new LteEnbAntennaTestCase(-120.0, 120.0, 1, -0.1, -12.754), TestCase::QUICK);
 }
 
-static LteAntennaTestSuite lteAntennaTestSuite;
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
+static LteAntennaTestSuite g_lteAntennaTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-epc-e2e-data.cc ns-3.38/src/lte/test/test-lte-epc-e2e-data.cc
--- ns-3.37/src/lte/test/test-lte-epc-e2e-data.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-epc-e2e-data.cc	2023-03-16 20:54:23.784869000 -0700
@@ -44,7 +44,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  */
 
 /// BearerTestData structure
@@ -91,7 +90,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test that e2e packet flow is correct. Compares the data send and the
  * data received. Test uses mostly the PDCP stats to check the performance.
@@ -375,7 +373,7 @@
 }
 
 /**
- * \ingroup tests lte-test
+ * \ingroup lte-test
  *
  * \brief Test that the S1-U interface implementation works correctly
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-handover-delay.cc ns-3.38/src/lte/test/test-lte-handover-delay.cc
--- ns-3.37/src/lte/test/test-lte-handover-delay.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-handover-delay.cc	2023-03-16 20:54:23.784869000 -0700
@@ -44,7 +44,7 @@
 NS_LOG_COMPONENT_DEFINE("LteHandoverDelayTest");
 
 /**
- * \ingroup tests lte-test
+ * \ingroup lte-test
  *
  * \brief Verifying that the time needed for handover is under a
  * specified threshold.
@@ -286,7 +286,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Handover Delay Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-handover-failure.cc ns-3.38/src/lte/test/test-lte-handover-failure.cc
--- ns-3.37/src/lte/test/test-lte-handover-failure.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-handover-failure.cc	2023-03-16 20:54:23.784869000 -0700
@@ -46,7 +46,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Verifying that a handover failure occurs due to various causes
  *
@@ -124,7 +123,7 @@
 
     /**
      * \brief Called at the end of simulation and verifies that a handover
-     *        and a handover failure has occured in the simulation.
+     *        and a handover failure has occurred in the simulation.
      */
     void DoTeardown() override;
 
@@ -200,7 +199,7 @@
     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 occured in simulation
+    bool m_hasHandoverFailureOccured; ///< has handover failure occurred in simulation
 
 }; // end of class LteHandoverFailureTestCase
 
@@ -364,7 +363,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * The following log components can be used to debug this test's behavior:
  * LteHandoverFailureTest:LteEnbRrc:LteEnbMac:LteUeRrc:EpcX2
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-handover-target.cc ns-3.38/src/lte/test/test-lte-handover-target.cc
--- ns-3.37/src/lte/test/test-lte-handover-target.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-handover-target.cc	2023-03-16 20:54:23.784869000 -0700
@@ -49,7 +49,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Testing a handover algorithm, verifying that it selects the right
  *        target cell when more than one options available.
@@ -449,4 +448,8 @@
 
 } // end of LteHandoverTargetTestSuite ()
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteHandoverTargetTestSuite g_lteHandoverTargetTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-rlc-header.cc ns-3.38/src/lte/test/test-lte-rlc-header.cc
--- ns-3.37/src/lte/test/test-lte-rlc-header.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-rlc-header.cc	2023-03-16 20:54:23.784869000 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test Utils
  */
@@ -106,7 +105,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Rlc Am Status Pdu Test Case
  */
@@ -187,7 +185,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Rlc Header Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-rrc.cc ns-3.38/src/lte/test/test-lte-rrc.cc
--- ns-3.37/src/lte/test/test-lte-rrc.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-rrc.cc	2023-03-16 20:54:23.784869000 -0700
@@ -31,7 +31,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test rrc connection establishment.
  */
@@ -584,7 +583,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Rrc Connection Establishment Error Test Case
  */
@@ -765,7 +763,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte Rrc Test Suite
  */
@@ -895,4 +892,8 @@
         TestCase::QUICK);
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteRrcTestSuite g_lteRrcTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-x2-handover.cc ns-3.38/src/lte/test/test-lte-x2-handover.cc
--- ns-3.37/src/lte/test/test-lte-x2-handover.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-x2-handover.cc	2023-03-16 20:54:23.784869000 -0700
@@ -31,7 +31,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief HandoverEvent structure
  */
@@ -45,7 +44,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test X2 Handover. In this test is used NoOpHandoverAlgorithm and
  * the request for handover is generated manually, and it is not based on measurements.
@@ -123,7 +121,6 @@
 
     /**
      * \ingroup lte-test
-     * \ingroup tests
      *
      * \brief BearerData structure
      */
@@ -138,7 +135,6 @@
 
     /**
      * \ingroup lte-test
-     * \ingroup tests
      *
      * \brief UeData structure
      */
@@ -231,7 +227,7 @@
     uint32_t previousSeed = RngSeedManager::GetSeed();
     uint64_t previousRun = RngSeedManager::GetRun();
     Config::Reset();
-    // This test is sensitive to random variable stream assigments
+    // This test is sensitive to random variable stream assignments
     RngSeedManager::SetSeed(1);
     RngSeedManager::SetRun(3);
     Config::SetDefault("ns3::UdpClient::Interval", TimeValue(m_udpClientInterval));
@@ -657,7 +653,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief LTE X2 Handover Test Suite.
  *
@@ -770,8 +765,7 @@
     {
         for (int32_t useIdealRrc = 1; useIdealRrc >= 0; --useIdealRrc)
         {
-            //                                     nUes, nDBearers, helist, name, sched, admitHo,
-            //                                     idealRrc
+            // nUes, nDBearers, helist, name, sched, admitHo, idealRrc
             AddTestCase(
                 new LteX2HandoverTestCase(1, 0, hel0, hel0name, *schedIt, true, useIdealRrc),
                 TestCase::EXTENSIVE);
@@ -896,4 +890,8 @@
     }
 }
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteX2HandoverTestSuite g_lteX2HandoverTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/lte/test/test-lte-x2-handover-measures.cc ns-3.38/src/lte/test/test-lte-x2-handover-measures.cc
--- ns-3.37/src/lte/test/test-lte-x2-handover-measures.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/lte/test/test-lte-x2-handover-measures.cc	2023-03-16 20:54:23.784869000 -0700
@@ -32,7 +32,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief CheckPointEvent structure
  */
@@ -65,7 +64,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Test different X2 handover measures and algorithms, e.g. A2A4RsrqHandoverAlgorithm and
  * A3RsrpHandoverAlgorithm. Test defines different handover parameters and scenario configurations.
@@ -137,7 +135,7 @@
     std::list<CheckPointEvent> m_checkPointEventList; ///< check point event list
     std::string m_checkPointEventListName;            ///< check point event list name
     bool m_epc;                                       ///< whether to use EPC
-    bool m_useUdp;                                    ///<  whether to use UDP traffic
+    bool m_useUdp;                                    ///< whether to use UDP traffic
     std::string m_schedulerType;                      ///< scheduler type
     std::string m_handoverAlgorithmType;              ///< handover algorithm type
     bool m_admitHo;                                   ///< whether to configure to admit handover
@@ -147,7 +145,6 @@
 
     /**
      * \ingroup lte-test
-     * \ingroup tests
      *
      * \brief BearerData structure
      */
@@ -162,7 +159,6 @@
 
     /**
      * \ingroup lte-test
-     * \ingroup tests
      *
      * \brief UeData structure
      */
@@ -721,7 +717,7 @@
 
         NS_LOG_LOGIC("expBytes " << expectedBytes << " dlRx " << dlRx << " ulRx " << ulRx);
 
-        //                                tolerance
+        // tolerance
         if (it->dlSink)
         {
             NS_TEST_ASSERT_MSG_GT(dlRx,
@@ -740,7 +736,6 @@
 
 /**
  * \ingroup lte-test
- * \ingroup tests
  *
  * \brief Lte X2 Handover Measures Test Suite
  */
@@ -781,8 +776,7 @@
     std::string ho = "ns3::A2A4RsrqHandoverAlgorithm";
     for (useIdealRrc = 1; useIdealRrc >= 0; --useIdealRrc)
     {
-        //                                          nEnbs, nUes, nDBearers, celist, name, useUdp,
-        //                                          sched, ho, admitHo, idealRrc
+        // nEnbs, nUes, nDBearers, celist, name, useUdp, sched, ho, admitHo, idealRrc
         AddTestCase(new LteX2HandoverMeasuresTestCase(2,
                                                       1,
                                                       0,
@@ -887,8 +881,7 @@
     sched = "ns3::RrFfMacScheduler";
     for (useIdealRrc = 1; useIdealRrc >= 0; --useIdealRrc)
     {
-        //                                          nEnbs, nUes, nDBearers, celist, name, useUdp,
-        //                                          sched, admitHo, idealRrc
+        // nEnbs, nUes, nDBearers, celist, name, useUdp, sched, admitHo, idealRrc
         AddTestCase(new LteX2HandoverMeasuresTestCase(2,
                                                       1,
                                                       0,
@@ -928,8 +921,7 @@
     sched = "ns3::PfFfMacScheduler";
     for (useIdealRrc = 1; useIdealRrc >= 0; --useIdealRrc)
     {
-        //                                          nEnbs, nUes, nDBearers, celist, name, useUdp,
-        //                                          sched, admitHo, idealRrc
+        // nEnbs, nUes, nDBearers, celist, name, useUdp, sched, admitHo, idealRrc
         AddTestCase(new LteX2HandoverMeasuresTestCase(2,
                                                       1,
                                                       0,
@@ -968,8 +960,7 @@
     sched = "ns3::RrFfMacScheduler";
     for (useIdealRrc = 1; useIdealRrc >= 0; --useIdealRrc)
     {
-        //                                          nEnbs, nUes, nDBearers, celist, name, useUdp,
-        //                                          sched, admitHo, idealRrc
+        // nEnbs, nUes, nDBearers, celist, name, useUdp, sched, admitHo, idealRrc
         AddTestCase(new LteX2HandoverMeasuresTestCase(2,
                                                       1,
                                                       0,
@@ -1007,4 +998,8 @@
 
 } // end of LteX2HandoverMeasuresTestSuite ()
 
+/**
+ * \ingroup lte-test
+ * Static variable for test initialization
+ */
 static LteX2HandoverMeasuresTestSuite g_lteX2HandoverMeasuresTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/helper/mesh-helper.cc ns-3.38/src/mesh/helper/mesh-helper.cc
--- ns-3.37/src/mesh/helper/mesh-helper.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/helper/mesh-helper.cc	2023-03-16 20:54:23.788869100 -0700
@@ -48,7 +48,7 @@
 }
 
 void
-MeshHelper::SetSpreadInterfaceChannels(enum ChannelPolicy policy)
+MeshHelper::SetSpreadInterfaceChannels(ChannelPolicy policy)
 {
     m_spreadChannelPolicy = policy;
 }
@@ -105,7 +105,7 @@
 }
 
 void
-MeshHelper::SetStandard(enum WifiStandard standard)
+MeshHelper::SetStandard(WifiStandard standard)
 {
     m_standard = standard;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/helper/mesh-helper.h ns-3.38/src/mesh/helper/mesh-helper.h
--- ns-3.37/src/mesh/helper/mesh-helper.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/helper/mesh-helper.h	2023-03-16 20:54:23.788869100 -0700
@@ -86,7 +86,7 @@
      * Set standard
      * \param standard the wifi phy standard
      */
-    void SetStandard(enum WifiStandard standard);
+    void SetStandard(WifiStandard standard);
 
     /// \todo SetMeshId
     // void SetMeshId (std::string s);
@@ -182,7 +182,7 @@
     ObjectFactory m_mac;                  ///< the MAC
     ObjectFactory m_stationManager;       ///< the station manager
     ObjectFactory m_ackPolicySelector[4]; ///< ack policy selector for all ACs
-    enum WifiStandard m_standard;         ///< standard
+    WifiStandard m_standard;              ///< standard
 };
 
 /***************************************************************
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/hwmp-protocol.cc ns-3.38/src/mesh/model/dot11s/hwmp-protocol.cc
--- ns-3.37/src/mesh/model/dot11s/hwmp-protocol.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/hwmp-protocol.cc	2023-03-16 20:54:23.788869100 -0700
@@ -469,7 +469,7 @@
                                   MicroSeconds(preq.GetLifetime() * 1024),
                                   preq.GetOriginatorSeqNumber());
         // Notify trace source of routing change
-        struct RouteChange rChange;
+        RouteChange rChange;
         rChange.type = "Add Reactive";
         rChange.destination = preq.GetOriginatorAddress();
         rChange.retransmitter = from;
@@ -490,7 +490,7 @@
                                   MicroSeconds(preq.GetLifetime() * 1024),
                                   preq.GetOriginatorSeqNumber());
         // Notify trace source of routing change
-        struct RouteChange rChange;
+        RouteChange rChange;
         rChange.type = "Add Reactive";
         rChange.destination = fromMp;
         rChange.retransmitter = from;
@@ -525,7 +525,7 @@
                                            MicroSeconds(preq.GetLifetime() * 1024),
                                            preq.GetOriginatorSeqNumber());
                 // Notify trace source of routing change
-                struct RouteChange rChange;
+                RouteChange rChange;
                 rChange.type = "Add Proactive";
                 rChange.destination = preq.GetOriginatorAddress();
                 rChange.retransmitter = from;
@@ -658,7 +658,7 @@
                                   MicroSeconds(prep.GetLifetime() * 1024),
                                   sequence);
         // Notify trace source of routing change
-        struct RouteChange rChange;
+        RouteChange rChange;
         rChange.type = "Add Reactive";
         rChange.destination = prep.GetOriginatorAddress();
         rChange.retransmitter = from;
@@ -690,7 +690,7 @@
                                   MicroSeconds(prep.GetLifetime() * 1024),
                                   sequence);
         // Notify trace source of routing change
-        struct RouteChange rChange;
+        RouteChange rChange;
         rChange.type = "Add Reactive";
         rChange.destination = fromMp;
         rChange.retransmitter = from;
@@ -742,7 +742,7 @@
             retval.push_back(destinations[i]);
         }
     }
-    if (retval.size() == 0)
+    if (retval.empty())
     {
         return;
     }
@@ -866,7 +866,7 @@
     PathError retval;
     // HwmpRtable increments a sequence number as written in 11B.9.7.2
     retval.receivers = GetPerrReceivers(destinations);
-    if (retval.receivers.size() == 0)
+    if (retval.receivers.empty())
     {
         return retval;
     }
@@ -876,7 +876,7 @@
         retval.destinations.push_back(destinations[i]);
         m_rtable->DeleteReactivePath(destinations[i].destination);
         // Notify trace source of routing change
-        struct RouteChange rChange;
+        RouteChange rChange;
         rChange.type = "Delete Reactive";
         rChange.destination = destinations[i].destination;
         rChange.seqnum = destinations[i].seqnum;
@@ -938,14 +938,14 @@
         HwmpRtable::PrecursorList precursors = m_rtable->GetPrecursors(failedDest[i].destination);
         m_rtable->DeleteReactivePath(failedDest[i].destination);
         // Notify trace source of routing change
-        struct RouteChange rChange;
+        RouteChange rChange;
         rChange.type = "Delete Reactive";
         rChange.destination = failedDest[i].destination;
         rChange.seqnum = failedDest[i].seqnum;
         m_routeChangeTraceSource(rChange);
         m_rtable->DeleteProactivePath(failedDest[i].destination);
         // Notify trace source of routing change
-        struct RouteChange rChangePro;
+        RouteChange rChangePro;
         rChangePro.type = "Delete Proactive";
         rChangePro.destination = failedDest[i].destination;
         rChangePro.seqnum = failedDest[i].seqnum;
@@ -978,7 +978,7 @@
     {
         retval = m_neighboursCallback(interface);
     }
-    if ((retval.size() >= m_unicastPreqThreshold) || (retval.size() == 0))
+    if (retval.size() >= m_unicastPreqThreshold || retval.empty())
     {
         retval.clear();
         retval.push_back(Mac48Address::GetBroadcast());
@@ -995,7 +995,7 @@
     {
         retval = m_neighboursCallback(interface);
     }
-    if ((retval.size() >= m_unicastDataThreshold) || (retval.size() == 0))
+    if (retval.size() >= m_unicastDataThreshold || retval.empty())
     {
         retval.clear();
         retval.push_back(Mac48Address::GetBroadcast());
@@ -1039,7 +1039,7 @@
     NS_LOG_FUNCTION(this);
     QueuedPacket retval;
     retval.pkt = nullptr;
-    if (m_rqueue.size() != 0)
+    if (!m_rqueue.empty())
     {
         retval = m_rqueue[0];
         m_rqueue.erase(m_rqueue.begin());
@@ -1214,13 +1214,13 @@
 }
 
 bool
-HwmpProtocol::GetDoFlag()
+HwmpProtocol::GetDoFlag() const
 {
     return m_doFlag;
 }
 
 bool
-HwmpProtocol::GetRfFlag()
+HwmpProtocol::GetRfFlag() const
 {
     return m_rfFlag;
 }
@@ -1238,7 +1238,7 @@
 }
 
 uint8_t
-HwmpProtocol::GetMaxTtl()
+HwmpProtocol::GetMaxTtl() const
 {
     return m_maxTtl;
 }
@@ -1264,7 +1264,7 @@
 }
 
 uint8_t
-HwmpProtocol::GetUnicastPerrThreshold()
+HwmpProtocol::GetUnicastPerrThreshold() const
 {
     return m_unicastPerrThreshold;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/hwmp-protocol.h ns-3.38/src/mesh/model/dot11s/hwmp-protocol.h
--- ns-3.37/src/mesh/model/dot11s/hwmp-protocol.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/hwmp-protocol.h	2023-03-16 20:54:23.788869100 -0700
@@ -420,12 +420,12 @@
      * Get do flag function
      * \returns DO flag
      */
-    bool GetDoFlag();
+    bool GetDoFlag() const;
     /**
      * Get rf flag function
      * \returns the RF flag
      */
-    bool GetRfFlag();
+    bool GetRfFlag() const;
     /**
      * Get PREQ minimum interval function
      * \returns the PREQ
@@ -440,7 +440,7 @@
      * Get maximum TTL function
      * \returns the maximum TTL
      */
-    uint8_t GetMaxTtl();
+    uint8_t GetMaxTtl() const;
     /**
      * Get next period function
      * \returns the next period
@@ -460,7 +460,7 @@
      * Get unicast PERR threshold function
      * \returns the unicast PERR threshold
      */
-    uint8_t GetUnicastPerrThreshold();
+    uint8_t GetUnicastPerrThreshold() const;
     ///@}
 
   private:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/hwmp-protocol-mac.cc ns-3.38/src/mesh/model/dot11s/hwmp-protocol-mac.cc
--- ns-3.37/src/mesh/model/dot11s/hwmp-protocol-mac.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/hwmp-protocol-mac.cc	2023-03-16 20:54:23.788869100 -0700
@@ -178,7 +178,7 @@
             }
         }
     }
-    if (failedDestinations.size() > 0)
+    if (!failedDestinations.empty())
     {
         m_protocol->ReceivePerr(failedDestinations,
                                 header.GetAddr2(),
@@ -327,7 +327,7 @@
     {
         return;
     }
-    if (m_myPreq.size() == 0)
+    if (m_myPreq.empty())
     {
         return;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/hwmp-tag.cc ns-3.38/src/mesh/model/dot11s/hwmp-tag.cc
--- ns-3.37/src/mesh/model/dot11s/hwmp-tag.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/hwmp-tag.cc	2023-03-16 20:54:23.788869100 -0700
@@ -58,7 +58,7 @@
 }
 
 uint8_t
-HwmpTag::GetTtl()
+HwmpTag::GetTtl() const
 {
     return m_ttl;
 }
@@ -70,7 +70,7 @@
 }
 
 uint32_t
-HwmpTag::GetMetric()
+HwmpTag::GetMetric() const
 {
     return m_metric;
 }
@@ -82,7 +82,7 @@
 }
 
 uint32_t
-HwmpTag::GetSeqno()
+HwmpTag::GetSeqno() const
 {
     return m_seqno;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/hwmp-tag.h ns-3.38/src/mesh/model/dot11s/hwmp-tag.h
--- ns-3.37/src/mesh/model/dot11s/hwmp-tag.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/hwmp-tag.h	2023-03-16 20:54:23.788869100 -0700
@@ -71,7 +71,7 @@
      * Get the TTL value
      * \returns the TTL
      */
-    uint8_t GetTtl();
+    uint8_t GetTtl() const;
     /**
      * Set the metric value
      * \param metric the metric
@@ -81,7 +81,7 @@
      * Get the metric value
      * \returns the metric
      */
-    uint32_t GetMetric();
+    uint32_t GetMetric() const;
     /**
      * Set sequence number
      * \param seqno the sequence number
@@ -91,7 +91,7 @@
      * Get the sequence number
      * \returns the sequence number
      */
-    uint32_t GetSeqno();
+    uint32_t GetSeqno() const;
     /// Decrement TTL
     void DecrementTtl();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-configuration.cc ns-3.38/src/mesh/model/dot11s/ie-dot11s-configuration.cc
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-configuration.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-configuration.cc	2023-03-16 20:54:23.788869100 -0700
@@ -161,13 +161,13 @@
 {
     Buffer::Iterator start = i;
     // Active Path Selection Protocol ID:
-    m_APSPId = (dot11sPathSelectionProtocol)i.ReadU8();
+    m_APSPId = (Dot11sPathSelectionProtocol)i.ReadU8();
     // Active Path Metric ID:
-    m_APSMId = (dot11sPathSelectionMetric)i.ReadU8();
+    m_APSMId = (Dot11sPathSelectionMetric)i.ReadU8();
     // Congestion Control Mode ID:
-    m_CCMId = (dot11sCongestionControlMode)i.ReadU8();
-    m_SPId = (dot11sSynchronizationProtocolIdentifier)i.ReadU8();
-    m_APId = (dot11sAuthenticationProtocol)i.ReadU8();
+    m_CCMId = (Dot11sCongestionControlMode)i.ReadU8();
+    m_SPId = (Dot11sSynchronizationProtocolIdentifier)i.ReadU8();
+    m_APId = (Dot11sAuthenticationProtocol)i.ReadU8();
     m_neighbors = (i.ReadU8() >> 1) & 0xF;
     i = m_meshCap.Deserialize(i);
     return i.GetDistanceFrom(start);
@@ -187,13 +187,13 @@
 }
 
 void
-IeConfiguration::SetRouting(dot11sPathSelectionProtocol routingId)
+IeConfiguration::SetRouting(Dot11sPathSelectionProtocol routingId)
 {
     m_APSPId = routingId;
 }
 
 void
-IeConfiguration::SetMetric(dot11sPathSelectionMetric metricId)
+IeConfiguration::SetMetric(Dot11sPathSelectionMetric metricId)
 {
     m_APSMId = metricId;
 }
@@ -217,7 +217,7 @@
 }
 
 uint8_t
-IeConfiguration::GetNeighborCount()
+IeConfiguration::GetNeighborCount() const
 {
     return m_neighbors;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-configuration.h ns-3.38/src/mesh/model/dot11s/ie-dot11s-configuration.h
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-configuration.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-configuration.h	2023-03-16 20:54:23.788869100 -0700
@@ -31,33 +31,33 @@
 // according to IEEE 802.11 - 2012
 
 // in 7.3.2.98.2 Active Path Selection Protocol Identifier - 802.11s-2011
-enum dot11sPathSelectionProtocol
+enum Dot11sPathSelectionProtocol
 {
     PROTOCOL_HWMP = 0x01,
 };
 
 // in 7.3.2.98.3 Active Path Selection Metric Identifier - 802.11s-2011
-enum dot11sPathSelectionMetric
+enum Dot11sPathSelectionMetric
 {
     METRIC_AIRTIME = 0x01,
 };
 
 // in 7.3.2.98.4 Congestion Control Mode Identifier - 802.11s-2011
-enum dot11sCongestionControlMode
+enum Dot11sCongestionControlMode
 {
     CONGESTION_SIGNALING = 0x01,
     CONGESTION_NULL = 0x00,
 };
 
 // in 7.3.2.98.5 Synchronization Method Identifier - 802.11s-2011
-enum dot11sSynchronizationProtocolIdentifier
+enum Dot11sSynchronizationProtocolIdentifier
 {
     SYNC_NEIGHBOUR_OFFSET = 0x01, // Neighbor offset synchronization method
     SYNC_NULL = 0x00,             // Reserved
 };
 
 // in 7.3.2.98.6 Authentication Protocol Identifier - 802.11s-2011
-enum dot11sAuthenticationProtocol
+enum Dot11sAuthenticationProtocol
 {
     AUTH_NULL =
         0x00, // No authentication method is required to establish mesh peerings within the MBSS
@@ -134,12 +134,12 @@
      * Set routing value
      * \param routingId the routing ID
      */
-    void SetRouting(dot11sPathSelectionProtocol routingId);
+    void SetRouting(Dot11sPathSelectionProtocol routingId);
     /**
      * Set metric value
      * \param metricId the path selection metric
      */
-    void SetMetric(dot11sPathSelectionMetric metricId);
+    void SetMetric(Dot11sPathSelectionMetric metricId);
     /**
      * Is HWMP function
      * \returns true if Active Path Selection Protocol Identifier field set
@@ -161,7 +161,7 @@
      * Get neighbor count
      * \returns the the neighbor count
      */
-    uint8_t GetNeighborCount();
+    uint8_t GetNeighborCount() const;
     /**
      * Mesh capability
      * \returns the dot11s mesh capability
@@ -177,15 +177,15 @@
 
   private:
     /** Active Path Selection Protocol ID */
-    dot11sPathSelectionProtocol m_APSPId;
+    Dot11sPathSelectionProtocol m_APSPId;
     /** Active Path Metric ID */
-    dot11sPathSelectionMetric m_APSMId;
+    Dot11sPathSelectionMetric m_APSMId;
     /** Congestion Control Mode ID */
-    dot11sCongestionControlMode m_CCMId;
+    Dot11sCongestionControlMode m_CCMId;
     /** Sync protocol ID */
-    dot11sSynchronizationProtocolIdentifier m_SPId;
+    Dot11sSynchronizationProtocolIdentifier m_SPId;
     /** Auth protocol ID */
-    dot11sAuthenticationProtocol m_APId;
+    Dot11sAuthenticationProtocol m_APId;
     /** Mesh capability */
     Dot11sMeshCapability m_meshCap;
     uint8_t m_neighbors; ///< neighbors
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-metric-report.cc ns-3.38/src/mesh/model/dot11s/ie-dot11s-metric-report.cc
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-metric-report.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-metric-report.cc	2023-03-16 20:54:23.788869100 -0700
@@ -48,7 +48,7 @@
 }
 
 uint32_t
-IeLinkMetricReport::GetMetric()
+IeLinkMetricReport::GetMetric() const
 {
     return m_metric;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-metric-report.h ns-3.38/src/mesh/model/dot11s/ie-dot11s-metric-report.h
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-metric-report.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-metric-report.h	2023-03-16 20:54:23.788869100 -0700
@@ -52,7 +52,7 @@
      * Get metric value
      * \returns the metric
      */
-    uint32_t GetMetric();
+    uint32_t GetMetric() const;
 
     // Inherited from WifiInformationElement
     WifiInformationElementId ElementId() const override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-preq.cc ns-3.38/src/mesh/model/dot11s/ie-dot11s-preq.cc
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-preq.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-preq.cc	2023-03-16 20:54:23.788869100 -0700
@@ -64,19 +64,19 @@
 }
 
 bool
-DestinationAddressUnit::IsDo()
+DestinationAddressUnit::IsDo() const
 {
     return m_do;
 }
 
 bool
-DestinationAddressUnit::IsRf()
+DestinationAddressUnit::IsRf() const
 {
     return m_rf;
 }
 
 bool
-DestinationAddressUnit::IsUsn()
+DestinationAddressUnit::IsUsn() const
 {
     return m_usn;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-preq.h ns-3.38/src/mesh/model/dot11s/ie-dot11s-preq.h
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-preq.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-preq.h	2023-03-16 20:54:23.788869100 -0700
@@ -59,17 +59,17 @@
      * Is do function
      * \returns true if DO flag is set
      */
-    bool IsDo();
+    bool IsDo() const;
     /**
      * is RF function
      * \returns true if RF flag is set
      */
-    bool IsRf();
+    bool IsRf() const;
     /**
      * Is USN function
      * \returns true if USN flag set
      */
-    bool IsUsn();
+    bool IsUsn() const;
     /**
      * Get destination address function
      * \returns the MAC address
@@ -214,7 +214,7 @@
      */
     Mac48Address GetOriginatorAddress() const;
     /**
-     * Get originator sequence numnber value
+     * Get originator sequence number value
      * \returns the originator sequence number
      */
     uint32_t GetOriginatorSeqNumber() const;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-rann.cc ns-3.38/src/mesh/model/dot11s/ie-dot11s-rann.cc
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-rann.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-rann.cc	2023-03-16 20:54:23.788869100 -0700
@@ -85,31 +85,31 @@
 }
 
 uint8_t
-IeRann::GetFlags()
+IeRann::GetFlags() const
 {
     return m_flags;
 }
 
 uint8_t
-IeRann::GetHopcount()
+IeRann::GetHopcount() const
 {
     return m_hopcount;
 }
 
 uint8_t
-IeRann::GetTtl()
+IeRann::GetTtl() const
 {
     return m_ttl;
 }
 
 uint32_t
-IeRann::GetDestSeqNumber()
+IeRann::GetDestSeqNumber() const
 {
     return m_destSeqNumber;
 }
 
 uint32_t
-IeRann::GetMetric()
+IeRann::GetMetric() const
 {
     return m_metric;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/ie-dot11s-rann.h ns-3.38/src/mesh/model/dot11s/ie-dot11s-rann.h
--- ns-3.37/src/mesh/model/dot11s/ie-dot11s-rann.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/ie-dot11s-rann.h	2023-03-16 20:54:23.788869100 -0700
@@ -72,17 +72,17 @@
      * Get flags value
      * \returns the flags
      */
-    uint8_t GetFlags();
+    uint8_t GetFlags() const;
     /**
      * Get hop count value
      * \returns the hop count
      */
-    uint8_t GetHopcount();
+    uint8_t GetHopcount() const;
     /**
      * Get TTL value
      * \returns the TTL
      */
-    uint8_t GetTtl();
+    uint8_t GetTtl() const;
     /**
      * Get originator address value
      * \returns the MAC address of the originator
@@ -92,12 +92,12 @@
      * Get destination sequence number value
      * \returns the destination sequence number
      */
-    uint32_t GetDestSeqNumber();
+    uint32_t GetDestSeqNumber() const;
     /**
      * Get metric value
      * \returns the metric
      */
-    uint32_t GetMetric();
+    uint32_t GetMetric() const;
     /// Decrement TTL function
     void DecrementTtl();
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/peer-management-protocol.cc ns-3.38/src/mesh/model/dot11s/peer-management-protocol.cc
--- ns-3.37/src/mesh/model/dot11s/peer-management-protocol.cc	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/peer-management-protocol.cc	2023-03-16 20:54:23.788869100 -0700
@@ -401,7 +401,7 @@
 }
 
 bool
-PeerManagementProtocol::ShouldSendOpen(uint32_t interface, Mac48Address peerAddress)
+PeerManagementProtocol::ShouldSendOpen(uint32_t interface, Mac48Address peerAddress) const
 {
     return (m_stats.linksTotal < m_maxNumberOfPeerLinks);
 }
@@ -409,7 +409,7 @@
 bool
 PeerManagementProtocol::ShouldAcceptOpen(uint32_t interface,
                                          Mac48Address peerAddress,
-                                         PmpReasonCode& reasonCode)
+                                         PmpReasonCode& reasonCode) const
 {
     if (m_stats.linksTotal >= m_maxNumberOfPeerLinks)
     {
@@ -443,7 +443,7 @@
     NS_ASSERT_MSG(TuToTime(m_maxBeaconShift) <= m_beaconInterval[interface],
                   "Wrong beacon shift parameters");
 
-    if (iface->second.size() == 0)
+    if (iface->second.empty())
     {
         // I have no peers - may be our beacons are in collision
         ShiftOwnBeacon(interface);
@@ -574,7 +574,7 @@
 }
 
 uint8_t
-PeerManagementProtocol::GetNumberOfLinks()
+PeerManagementProtocol::GetNumberOfLinks() const
 {
     return m_stats.linksTotal;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/peer-management-protocol.h ns-3.38/src/mesh/model/dot11s/peer-management-protocol.h
--- ns-3.37/src/mesh/model/dot11s/peer-management-protocol.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/peer-management-protocol.h	2023-03-16 20:54:23.788869100 -0700
@@ -189,7 +189,7 @@
      * Get number of links
      * \returns the number of links
      */
-    uint8_t GetNumberOfLinks();
+    uint8_t GetNumberOfLinks() const;
     /**
      * Set mesh ID to a string value
      * \param s the mesh ID string value
@@ -285,7 +285,7 @@
      * \param peerAddress the peer address
      * \returns true is should send an open
      */
-    bool ShouldSendOpen(uint32_t interface, Mac48Address peerAddress);
+    bool ShouldSendOpen(uint32_t interface, Mac48Address peerAddress) const;
     /**
      * \brief External peer-chooser
      * \param interface the interface to use
@@ -293,7 +293,9 @@
      * \param reasonCode reason code
      * \returns true is should send an open
      */
-    bool ShouldAcceptOpen(uint32_t interface, Mac48Address peerAddress, PmpReasonCode& reasonCode);
+    bool ShouldAcceptOpen(uint32_t interface,
+                          Mac48Address peerAddress,
+                          PmpReasonCode& reasonCode) const;
     /**
      * \brief Indicates changes in peer links
      * \param interface the interface
@@ -409,7 +411,8 @@
          */
         void Print(std::ostream& os) const;
     };
-    struct Statistics m_stats; ///< statistics
+
+    Statistics m_stats; ///< statistics
 
     /// Add randomness to beacon shift
     Ptr<UniformRandomVariable> m_beaconShift;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/dot11s/peer-management-protocol-mac.h ns-3.38/src/mesh/model/dot11s/peer-management-protocol-mac.h
--- ns-3.37/src/mesh/model/dot11s/peer-management-protocol-mac.h	2022-11-01 17:37:24.671395500 -0700
+++ ns-3.38/src/mesh/model/dot11s/peer-management-protocol-mac.h	2023-03-16 20:54:23.788869100 -0700
@@ -233,7 +233,7 @@
     };
 
   private:
-    struct Statistics m_stats; ///< statistics
+    Statistics m_stats; ///< statistics
     ///\name Information about MAC and protocol:
     Ptr<MeshWifiInterfaceMac> m_parent;     ///< parent
     uint32_t m_ifIndex;                     ///< IF index
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/mesh-wifi-interface-mac.cc ns-3.38/src/mesh/model/mesh-wifi-interface-mac.cc
--- ns-3.37/src/mesh/model/mesh-wifi-interface-mac.cc	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/model/mesh-wifi-interface-mac.cc	2023-03-16 20:54:23.788869100 -0700
@@ -594,7 +594,7 @@
 }
 
 void
-MeshWifiInterfaceMac::ConfigureStandard(enum WifiStandard standard)
+MeshWifiInterfaceMac::ConfigureStandard(WifiStandard standard)
 {
     NS_ABORT_IF(!GetQosSupported());
     WifiMac::ConfigureStandard(standard);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/model/mesh-wifi-interface-mac.h ns-3.38/src/mesh/model/mesh-wifi-interface-mac.h
--- ns-3.37/src/mesh/model/mesh-wifi-interface-mac.h	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/model/mesh-wifi-interface-mac.h	2023-03-16 20:54:23.788869100 -0700
@@ -208,7 +208,7 @@
      *
      * \param standard the WifiStandard being configured
      */
-    void ConfigureStandard(enum WifiStandard standard) override;
+    void ConfigureStandard(WifiStandard standard) override;
     /**
      * \param cwMin the minimum contention window size
      * \param cwMax the maximum contention window size
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/dot11s/dot11s-test-suite.cc ns-3.38/src/mesh/test/dot11s/dot11s-test-suite.cc
--- ns-3.37/src/mesh/test/dot11s/dot11s-test-suite.cc	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/dot11s/dot11s-test-suite.cc	2023-03-16 20:54:23.788869100 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup dot11s-test
- * \ingroup tests
  *
  * \brief Built-in self test for MeshHeader
  */
@@ -95,7 +94,6 @@
 
 /**
  * \ingroup mesh-test
- * \ingroup tests
  *
  * \brief Unit test for HwmpRtable
  */
@@ -278,7 +276,6 @@
 
 /**
  * \ingroup mesh-test
- * \ingroup tests
  *
  * \brief Dot11s Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/dot11s/hwmp-proactive-regression.h ns-3.38/src/mesh/test/dot11s/hwmp-proactive-regression.h
--- ns-3.37/src/mesh/test/dot11s/hwmp-proactive-regression.h	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/dot11s/hwmp-proactive-regression.h	2023-03-16 20:54:23.788869100 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup dot11s-test
- * \ingroup tests
  *
  * \brief There are 5 stations set into a row, the center station is root.
  * Regression test indicates that traffic goes from the first to the
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/dot11s/hwmp-reactive-regression.h ns-3.38/src/mesh/test/dot11s/hwmp-reactive-regression.h
--- ns-3.37/src/mesh/test/dot11s/hwmp-reactive-regression.h	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/dot11s/hwmp-reactive-regression.h	2023-03-16 20:54:23.788869100 -0700
@@ -26,7 +26,6 @@
 
 /**
  * \ingroup dot11s-test
- * \ingroup tests
  *
  * \brief test for multihop path establishing and path error
  * procedures
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/dot11s/hwmp-simplest-regression.h ns-3.38/src/mesh/test/dot11s/hwmp-simplest-regression.h
--- ns-3.37/src/mesh/test/dot11s/hwmp-simplest-regression.h	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/dot11s/hwmp-simplest-regression.h	2023-03-16 20:54:23.792869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup dot11s-test
- * \ingroup tests
  *
  * \brief Peering Management & HWM Protocol regression test
  * Initiate scenario with 2 stations. Procedure of opening peer link
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/dot11s/hwmp-target-flags-regression.h ns-3.38/src/mesh/test/dot11s/hwmp-target-flags-regression.h
--- ns-3.37/src/mesh/test/dot11s/hwmp-target-flags-regression.h	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/dot11s/hwmp-target-flags-regression.h	2023-03-16 20:54:23.792869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup dot11s-test
- * \ingroup tests
  *
  * \brief This is a test for intermediate reply and saving routing
  * information about neighbour. 4 stations and 3 UDP ping streams are initiated.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/dot11s/pmp-regression.h ns-3.38/src/mesh/test/dot11s/pmp-regression.h
--- ns-3.37/src/mesh/test/dot11s/pmp-regression.h	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/dot11s/pmp-regression.h	2023-03-16 20:54:23.792869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup dot11s-test
- * \ingroup tests
  *
  * \brief Peering Management Protocol regression test
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/dot11s/regression.cc ns-3.38/src/mesh/test/dot11s/regression.cc
--- ns-3.37/src/mesh/test/dot11s/regression.cc	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/dot11s/regression.cc	2023-03-16 20:54:23.792869000 -0700
@@ -29,7 +29,6 @@
 
 /**
  * \ingroup dot11s-test
- * \ingroup tests
  *
  * \brief Dot11s Regression Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/flame/flame-regression.h ns-3.38/src/mesh/test/flame/flame-regression.h
--- ns-3.37/src/mesh/test/flame/flame-regression.h	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/flame/flame-regression.h	2023-03-16 20:54:23.792869000 -0700
@@ -32,7 +32,6 @@
 
 /**
  * \ingroup flame-test
- * \ingroup tests
  *
  * \brief FLAME protocol regression test of three stations:
  * \verbatim
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/flame/flame-test-suite.cc ns-3.38/src/mesh/test/flame/flame-test-suite.cc
--- ns-3.37/src/mesh/test/flame/flame-test-suite.cc	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/flame/flame-test-suite.cc	2023-03-16 20:54:23.792869000 -0700
@@ -28,7 +28,6 @@
 
 /**
  * \ingroup flame-test
- * \ingroup tests
  *
  * \brief Built-in self test for FlameHeader
  */
@@ -62,7 +61,6 @@
 
 /**
  * \ingroup flame-test
- * \ingroup tests
  *
  * \brief Unit test for FlameRtable
  */
@@ -143,7 +141,6 @@
 
 /**
  * \ingroup flame-test
- * \ingroup tests
  *
  * \brief Flame Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/flame/regression.cc ns-3.38/src/mesh/test/flame/regression.cc
--- ns-3.37/src/mesh/test/flame/regression.cc	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/flame/regression.cc	2023-03-16 20:54:23.792869000 -0700
@@ -25,7 +25,6 @@
 
 /**
  * \ingroup flame-test
- * \ingroup tests
  *
  * \brief Flame Regression Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mesh/test/mesh-information-element-vector-test-suite.cc ns-3.38/src/mesh/test/mesh-information-element-vector-test-suite.cc
--- ns-3.37/src/mesh/test/mesh-information-element-vector-test-suite.cc	2022-11-01 17:37:24.675395700 -0700
+++ ns-3.38/src/mesh/test/mesh-information-element-vector-test-suite.cc	2023-03-16 20:54:23.792869000 -0700
@@ -35,12 +35,12 @@
 
 /**
  * \ingroup mesh
+ * \ingroup tests
  * \defgroup mesh-test mesh module tests
  */
 
 /**
  * \ingroup mesh-test
- * \ingroup tests
  *
  * \brief Built-in self test for MeshInformationElementVector and all IE
  */
@@ -152,7 +152,6 @@
 
 /**
  * \ingroup mesh-test
- * \ingroup tests
  *
  * \brief Mesh Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/doc/mobility.rst ns-3.38/src/mobility/doc/mobility.rst
--- ns-3.37/src/mobility/doc/mobility.rst	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/doc/mobility.rst	2023-03-16 20:54:23.792869000 -0700
@@ -227,13 +227,13 @@
 
   MobilityHelper mobility;
 
-  mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
-    "MinX", DoubleValue (0.0),
-    "MinY", DoubleValue (0.0),
-    "DeltaX", DoubleValue (5.0),
-    "DeltaY", DoubleValue (10.0),
-    "GridWidth", UintegerValue (3),
-    "LayoutType", StringValue ("RowFirst"));
+  mobility.SetPositionAllocator("ns3::GridPositionAllocator",
+    "MinX", DoubleValue(0.0),
+    "MinY", DoubleValue(0.0),
+    "DeltaX", DoubleValue(5.0),
+    "DeltaY", DoubleValue(10.0),
+    "GridWidth", UintegerValue(3),
+    "LayoutType", StringValue("RowFirst"));
 
 This code tells the mobility helper to use a two-dimensional grid to initially
 place the nodes.  The first argument is an |ns3| TypeId specifying the
@@ -244,14 +244,14 @@
 
 .. sourcecode:: cpp
 
-  mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel",
-    "Bounds", RectangleValue (Rectangle (-50, 50, -50, 50)));
+  mobility.SetMobilityModel("ns3::RandomWalk2dMobilityModel",
+    "Bounds", RectangleValue(Rectangle(-50, 50, -50, 50)));
 
 Once the helper is configured, it is typically passed a container, such as:
 
 .. sourcecode:: cpp
 
-  mobility.Install (wifiStaNodes);
+  mobility.Install(wifiStaNodes);
 
 A MobilityHelper object may be reconfigured and reused for different
 NodeContainers during the configuration of an |ns3| scenario.
@@ -391,7 +391,7 @@
    * \param stream first stream index to use
    * \return the number of stream indices assigned by this model
    */
-  int64_t AssignStreams (int64_t stream);
+  int64_t AssignStreams(int64_t stream);
 
 The class ``MobilityHelper`` also provides this API.  The typical usage
 pattern when using the helper is:
@@ -401,8 +401,8 @@
   int64_t streamIndex = /*some positive integer */
   MobilityHelper mobility;
   ... (configure mobility)
-  mobility.Install (wifiStaNodes);
-  int64_t streamsUsed = mobility.AssignStreams (wifiStaNodes, streamIndex);
+  mobility.Install(wifiStaNodes);
+  int64_t streamsUsed = mobility.AssignStreams(wifiStaNodes, streamIndex);
 
 If AssignStreams is called before Install, it will not have any effect.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/examples/mobility-trace-example.cc ns-3.38/src/mobility/examples/mobility-trace-example.cc
--- ns-3.37/src/mobility/examples/mobility-trace-example.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/examples/mobility-trace-example.cc	2023-03-16 20:54:23.792869000 -0700
@@ -64,4 +64,6 @@
     Simulator::Stop(Seconds(5.0));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/helper/group-mobility-helper.h ns-3.38/src/mobility/helper/group-mobility-helper.h
--- ns-3.37/src/mobility/helper/group-mobility-helper.h	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/helper/group-mobility-helper.h	2023-03-16 20:54:23.792869000 -0700
@@ -181,7 +181,7 @@
     int64_t AssignStreams(NodeContainer c, int64_t stream);
 
   private:
-    // Enable loggin from template instantiations
+    // Enable logging from template instantiations
     NS_LOG_TEMPLATE_DECLARE; //!< the log component
 
     bool m_referencePositionSet{
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/helper/mobility-helper.cc ns-3.38/src/mobility/helper/mobility-helper.cc
--- ns-3.37/src/mobility/helper/mobility-helper.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/helper/mobility-helper.cc	2023-03-16 20:54:23.792869000 -0700
@@ -133,7 +133,7 @@
 }
 
 void
-MobilityHelper::InstallAll()
+MobilityHelper::InstallAll() const
 {
     Install(NodeContainer::GetGlobal());
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/helper/mobility-helper.h ns-3.38/src/mobility/helper/mobility-helper.h
--- ns-3.37/src/mobility/helper/mobility-helper.h	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/helper/mobility-helper.h	2023-03-16 20:54:23.792869000 -0700
@@ -171,7 +171,7 @@
      * Perform the work of MobilityHelper::Install on _all_ nodes which
      * exist in the simulation.
      */
-    void InstallAll();
+    void InstallAll() const;
 
     /**
      * \param stream an output stream wrapper
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/helper/ns2-mobility-helper.cc ns-3.38/src/mobility/helper/ns2-mobility-helper.cc
--- ns-3.37/src/mobility/helper/ns2-mobility-helper.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/helper/ns2-mobility-helper.cc	2023-03-16 20:54:23.792869000 -0700
@@ -178,7 +178,7 @@
 /**
  * Check if this corresponds to a line like this: $ns_ at 1 "$node_(0) setdest 2 3 4"
  * \param pr the ParseResult to analyze
- * \returns true if the ParseResult looks like a coordinate with a scheduled time and destionation
+ * \returns true if the ParseResult looks like a coordinate with a scheduled time and destination
  */
 static bool IsSchedSetPos(ParseResult pr);
 
@@ -591,12 +591,12 @@
 {
     std::string ret = s;
 
-    while (ret.size() > 0 && isblank(ret[0]))
+    while (!ret.empty() && isblank(ret[0]))
     {
         ret.erase(0, 1); // Removes blank spaces at the beginning of the line
     }
 
-    while (ret.size() > 0 && (isblank(ret[ret.size() - 1]) || (ret[ret.size() - 1] == ';')))
+    while (!ret.empty() && (isblank(ret[ret.size() - 1]) || (ret[ret.size() - 1] == ';')))
     {
         ret.erase(ret.size() - 1, 1); // Removes blank spaces from at end of line
     }
@@ -616,7 +616,7 @@
 bool
 IsVal(const std::string& str, T& ret)
 {
-    if (str.size() == 0)
+    if (str.empty())
     {
         return false;
     }
@@ -746,7 +746,7 @@
 IsSetInitialPos(ParseResult pr)
 {
     //        number of tokens         has $node_( ?                        has "set"           has
-    //        doble for position?
+    //        double for position?
     return pr.tokens.size() == 4 && HasNodeIdNumber(pr.tokens[0]) && pr.tokens[1] == NS2_SET &&
            pr.has_dval[3]
            // coord name is X_, Y_ or Z_ ?
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/model/position-allocator.cc ns-3.38/src/mobility/model/position-allocator.cc
--- ns-3.37/src/mobility/model/position-allocator.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/model/position-allocator.cc	2023-03-16 20:54:23.792869000 -0700
@@ -232,7 +232,7 @@
 }
 
 void
-GridPositionAllocator::SetLayoutType(enum LayoutType layoutType)
+GridPositionAllocator::SetLayoutType(LayoutType layoutType)
 {
     m_layoutType = layoutType;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/model/position-allocator.h ns-3.38/src/mobility/model/position-allocator.h
--- ns-3.37/src/mobility/model/position-allocator.h	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/model/position-allocator.h	2023-03-16 20:54:23.792869000 -0700
@@ -178,7 +178,7 @@
     /**
      * \param layoutType the type of layout to use (row first or column first).
      */
-    void SetLayoutType(enum LayoutType layoutType);
+    void SetLayoutType(LayoutType layoutType);
 
     /**
      * \return the x coordinate of the first allocated position.
@@ -203,20 +203,20 @@
     /**
      * \return the currently-selected layout type.
      */
-    enum LayoutType GetLayoutType() const;
+    LayoutType GetLayoutType() const;
 
     Vector GetNext() const override;
     int64_t AssignStreams(int64_t stream) override;
 
   private:
-    mutable uint32_t m_current;   //!< currently position
-    enum LayoutType m_layoutType; //!< currently selected layout type
-    double m_xMin;                //!< minimum boundary on x positions
-    double m_yMin;                //!< minimum boundary on y positions
-    double m_z;                   //!< z coordinate of all the positions generated
-    uint32_t m_n;                 //!< number of positions to allocate on each row or column
-    double m_deltaX;              //!< x interval between two consecutive x positions
-    double m_deltaY;              //!< y interval between two consecutive y positions
+    mutable uint32_t m_current; //!< currently position
+    LayoutType m_layoutType;    //!< currently selected layout type
+    double m_xMin;              //!< minimum boundary on x positions
+    double m_yMin;              //!< minimum boundary on y positions
+    double m_z;                 //!< z coordinate of all the positions generated
+    uint32_t m_n;               //!< number of positions to allocate on each row or column
+    double m_deltaX;            //!< x interval between two consecutive x positions
+    double m_deltaY;            //!< y interval between two consecutive y positions
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/model/random-walk-2d-mobility-model.h ns-3.38/src/mobility/model/random-walk-2d-mobility-model.h
--- ns-3.37/src/mobility/model/random-walk-2d-mobility-model.h	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/model/random-walk-2d-mobility-model.h	2023-03-16 20:54:23.792869000 -0700
@@ -84,7 +84,7 @@
 
     ConstantVelocityHelper m_helper;       //!< helper for this object
     EventId m_event;                       //!< stored event ID
-    enum Mode m_mode;                      //!< whether in time or distance mode
+    Mode m_mode;                           //!< whether in time or distance mode
     double m_modeDistance;                 //!< Change direction and speed after this distance
     Time m_modeTime;                       //!< Change current direction and speed after this delay
     Ptr<RandomVariableStream> m_speed;     //!< rv for picking speed
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/model/steady-state-random-waypoint-mobility-model.h ns-3.38/src/mobility/model/steady-state-random-waypoint-mobility-model.h
--- ns-3.37/src/mobility/model/steady-state-random-waypoint-mobility-model.h	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/model/steady-state-random-waypoint-mobility-model.h	2023-03-16 20:54:23.792869000 -0700
@@ -38,7 +38,7 @@
  * The difference is that the initial values of this parameters are not
  * from uniform distribution but from stationary distribution of RWM model.
  * The implementation of this model is 2D-specific and with nonzero nodes speeds.
- * In the 3D ns-3 coordinate system, the momement occurs on the
+ * In the 3D ns-3 coordinate system, the movement occurs on the
  * \f$ z=\overline{Z} \f$ plane, where \f$ \overline{Z} \f$ is a constant which
  * can be configured using the Z attribute.
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/test/geo-to-cartesian-test.cc ns-3.38/src/mobility/test/geo-to-cartesian-test.cc
--- ns-3.37/src/mobility/test/geo-to-cartesian-test.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/test/geo-to-cartesian-test.cc	2023-03-16 20:54:23.792869000 -0700
@@ -30,1483 +30,1468 @@
 const double TOLERANCE = 10;
 
 const double XSPHERE_MATLAB[216] = {
-    6371000,           1968747.27116279,  -5154247.27116279, -5154247.27116279,
-    1968747.27116279,  6371000,           1968747.27116279,  608376.364418605,
-    -1592750,          -1592750.00000000, 608376.364418605,  1968747.27116279,
-    -5154247.27116279, -1592750,          4169873.63558139,  4169873.63558139,
-    -1592750.00000000, -5154247.27116279, -5154247.27116279, -1592750.00000000,
-    4169873.63558139,  4169873.63558140,  -1592750.00000000, -5154247.27116279,
-    1968747.27116279,  608376.364418605,  -1592750.00000000, -1592750.00000000,
-    608376.364418604,  1968747.27116279,  6371000,           1968747.27116279,
-    -5154247.27116279, -5154247.27116279, 1968747.27116279,  6371000,
-    6371200,           1968809.07456167,  -5154409.07456167, -5154409.07456167,
-    1968809.07456166,  6371200,           1968809.07456167,  608395.462719168,
-    -1592800,          -1592800.00000000, 608395.462719167,  1968809.07456167,
-    -5154409.07456167, -1592800,          4170004.53728083,  4170004.53728083,
-    -1592800.00000000, -5154409.07456167, -5154409.07456167, -1592800,
-    4170004.53728083,  4170004.53728083,  -1592800.00000000, -5154409.07456167,
-    1968809.07456166,  608395.462719167,  -1592800.00000000, -1592800.00000000,
-    608395.462719167,  1968809.07456166,  6371200,           1968809.07456167,
-    -5154409.07456167, -5154409.07456167, 1968809.07456166,  6371200,
-    6371400,           1968870.87796054,  -5154570.87796054, -5154570.87796054,
-    1968870.87796054,  6371400,           1968870.87796054,  608414.561019730,
-    -1592850,          -1592850.00000000, 608414.561019730,  1968870.87796054,
-    -5154570.87796054, -1592850,          4170135.43898027,  4170135.43898027,
-    -1592850.00000000, -5154570.87796054, -5154570.87796054, -1592850.00000000,
-    4170135.43898027,  4170135.43898027,  -1592850.00000000, -5154570.87796054,
-    1968870.87796054,  608414.561019730,  -1592850.00000000, -1592850.00000000,
-    608414.561019729,  1968870.87796054,  6371400,           1968870.87796054,
-    -5154570.87796054, -5154570.87796054, 1968870.87796054,  6371400,
-    6371600,           1968932.68135942,  -5154732.68135941, -5154732.68135942,
-    1968932.68135941,  6371600,           1968932.68135942,  608433.659320293,
-    -1592900,          -1592900.00000000, 608433.659320292,  1968932.68135942,
-    -5154732.68135941, -1592900.00000000, 4170266.34067971,  4170266.34067971,
-    -1592900.00000000, -5154732.68135941, -5154732.68135942, -1592900.00000000,
-    4170266.34067971,  4170266.34067971,  -1592900.00000000, -5154732.68135942,
-    1968932.68135941,  608433.659320292,  -1592900.00000000, -1592900.00000000,
-    608433.659320292,  1968932.68135941,  6371600,           1968932.68135942,
-    -5154732.68135941, -5154732.68135942, 1968932.68135941,  6371600,
-    6371800,           1968994.48475829,  -5154894.48475829, -5154894.48475829,
-    1968994.48475829,  6371800,           1968994.48475829,  608452.757620855,
-    -1592950,          -1592950.00000000, 608452.757620855,  1968994.48475829,
-    -5154894.48475829, -1592950,          4170397.24237914,  4170397.24237914,
-    -1592950.00000000, -5154894.48475829, -5154894.48475829, -1592950.00000000,
-    4170397.24237914,  4170397.24237915,  -1592950.00000000, -5154894.48475829,
-    1968994.48475829,  608452.757620855,  -1592950.00000000, -1592950.00000000,
-    608452.757620854,  1968994.48475829,  6371800,           1968994.48475829,
-    -5154894.48475829, -5154894.48475829, 1968994.48475829,  6371800,
-    6372000,           1969056.28815717,  -5155056.28815716, -5155056.28815717,
-    1969056.28815716,  6372000,           1969056.28815717,  608471.855921418,
-    -1593000,          -1593000,          608471.855921417,  1969056.28815717,
-    -5155056.28815716, -1593000.00000000, 4170528.14407858,  4170528.14407858,
-    -1593000.00000000, -5155056.28815716, -5155056.28815717, -1593000.00000000,
-    4170528.14407858,  4170528.14407858,  -1593000.00000000, -5155056.28815717,
-    1969056.28815716,  608471.855921417,  -1593000.00000000, -1593000.00000000,
-    608471.855921417,  1969056.28815716,  6372000,           1969056.28815717,
-    -5155056.28815716, -5155056.28815717, 1969056.28815716,  6372000};
-
-const double YSPHERE_MATLAB[216] = {0,
-                                    6059181.06531642,
-                                    3744779.84235535,
-                                    -3744779.84235535,
-                                    -6059181.06531642,
-                                    -1.56044495147356e-09,
-                                    0,
-                                    1872389.92117767,
-                                    1157200.61148054,
-                                    -1157200.61148054,
-                                    -1872389.92117767,
-                                    -4.82204008791920e-10,
-                                    0,
-                                    -4901980.45383588,
-                                    -3029590.53265821,
-                                    3029590.53265821,
-                                    4901980.45383588,
-                                    1.26242648452870e-09,
-                                    0,
-                                    -4901980.45383589,
-                                    -3029590.53265821,
-                                    3029590.53265821,
-                                    4901980.45383589,
-                                    1.26242648452870e-09,
-                                    0,
-                                    1872389.92117767,
-                                    1157200.61148054,
-                                    -1157200.61148054,
-                                    -1872389.92117767,
-                                    -4.82204008791919e-10,
-                                    0,
-                                    6059181.06531642,
-                                    3744779.84235535,
-                                    -3744779.84235535,
-                                    -6059181.06531642,
-                                    -1.56044495147356e-09,
-                                    0,
-                                    6059371.27661968,
-                                    3744897.39940581,
-                                    -3744897.39940580,
-                                    -6059371.27661968,
-                                    -1.56049393734552e-09,
-                                    0,
-                                    1872448.69970290,
-                                    1157236.93860694,
-                                    -1157236.93860694,
-                                    -1872448.69970290,
-                                    -4.82219146258841e-10,
-                                    0,
-                                    -4902134.33801274,
-                                    -3029685.63830984,
-                                    3029685.63830984,
-                                    4902134.33801274,
-                                    1.26246611493160e-09,
-                                    0,
-                                    -4902134.33801274,
-                                    -3029685.63830984,
-                                    3029685.63830984,
-                                    4902134.33801274,
-                                    1.26246611493160e-09,
-                                    0,
-                                    1872448.69970290,
-                                    1157236.93860694,
-                                    -1157236.93860694,
-                                    -1872448.69970290,
-                                    -4.82219146258841e-10,
-                                    0,
-                                    6059371.27661968,
-                                    3744897.39940581,
-                                    -3744897.39940580,
-                                    -6059371.27661968,
-                                    -1.56049393734552e-09,
-                                    0,
-                                    6059561.48792294,
-                                    3745014.95645626,
-                                    -3745014.95645626,
-                                    -6059561.48792294,
-                                    -1.56054292321749e-09,
-                                    0,
-                                    1872507.47822813,
-                                    1157273.26573334,
-                                    -1157273.26573334,
-                                    -1872507.47822813,
-                                    -4.82234283725763e-10,
-                                    0,
-                                    -4902288.22218960,
-                                    -3029780.74396147,
-                                    3029780.74396147,
-                                    4902288.22218960,
-                                    1.26250574533451e-09,
-                                    0,
-                                    -4902288.22218960,
-                                    -3029780.74396147,
-                                    3029780.74396147,
-                                    4902288.22218960,
-                                    1.26250574533451e-09,
-                                    0,
-                                    1872507.47822813,
-                                    1157273.26573334,
-                                    -1157273.26573334,
-                                    -1872507.47822813,
-                                    -4.82234283725763e-10,
-                                    0,
-                                    6059561.48792294,
-                                    3745014.95645626,
-                                    -3745014.95645626,
-                                    -6059561.48792294,
-                                    -1.56054292321749e-09,
-                                    0,
-                                    6059751.69922620,
-                                    3745132.51350672,
-                                    -3745132.51350672,
-                                    -6059751.69922620,
-                                    -1.56059190908946e-09,
-                                    0,
-                                    1872566.25675336,
-                                    1157309.59285974,
-                                    -1157309.59285974,
-                                    -1872566.25675336,
-                                    -4.82249421192685e-10,
-                                    0,
-                                    -4902442.10636646,
-                                    -3029875.84961310,
-                                    3029875.84961310,
-                                    4902442.10636646,
-                                    1.26254537573741e-09,
-                                    0,
-                                    -4902442.10636646,
-                                    -3029875.84961310,
-                                    3029875.84961310,
-                                    4902442.10636646,
-                                    1.26254537573741e-09,
-                                    0,
-                                    1872566.25675336,
-                                    1157309.59285974,
-                                    -1157309.59285974,
-                                    -1872566.25675336,
-                                    -4.82249421192684e-10,
-                                    0,
-                                    6059751.69922620,
-                                    3745132.51350672,
-                                    -3745132.51350672,
-                                    -6059751.69922620,
-                                    -1.56059190908946e-09,
-                                    0,
-                                    6059941.91052946,
-                                    3745250.07055718,
-                                    -3745250.07055718,
-                                    -6059941.91052946,
-                                    -1.56064089496142e-09,
-                                    0,
-                                    1872625.03527859,
-                                    1157345.91998614,
-                                    -1157345.91998614,
-                                    -1872625.03527859,
-                                    -4.82264558659606e-10,
-                                    0,
-                                    -4902595.99054332,
-                                    -3029970.95526473,
-                                    3029970.95526473,
-                                    4902595.99054332,
-                                    1.26258500614032e-09,
-                                    0,
-                                    -4902595.99054332,
-                                    -3029970.95526473,
-                                    3029970.95526473,
-                                    4902595.99054332,
-                                    1.26258500614032e-09,
-                                    0,
-                                    1872625.03527859,
-                                    1157345.91998614,
-                                    -1157345.91998614,
-                                    -1872625.03527859,
-                                    -4.82264558659606e-10,
-                                    0,
-                                    6059941.91052946,
-                                    3745250.07055718,
-                                    -3745250.07055718,
-                                    -6059941.91052946,
-                                    -1.56064089496142e-09,
-                                    0,
-                                    6060132.12183272,
-                                    3745367.62760764,
-                                    -3745367.62760764,
-                                    -6060132.12183272,
-                                    -1.56068988083339e-09,
-                                    0,
-                                    1872683.81380382,
-                                    1157382.24711254,
-                                    -1157382.24711254,
-                                    -1872683.81380382,
-                                    -4.82279696126528e-10,
-                                    0,
-                                    -4902749.87472018,
-                                    -3030066.06091636,
-                                    3030066.06091636,
-                                    4902749.87472018,
-                                    1.26262463654322e-09,
-                                    0,
-                                    -4902749.87472018,
-                                    -3030066.06091636,
-                                    3030066.06091636,
-                                    4902749.87472018,
-                                    1.26262463654322e-09,
-                                    0,
-                                    1872683.81380382,
-                                    1157382.24711254,
-                                    -1157382.24711254,
-                                    -1872683.81380382,
-                                    -4.82279696126528e-10,
-                                    0,
-                                    6060132.12183272,
-                                    3745367.62760764,
-                                    -3745367.62760764,
-                                    -6060132.12183272,
-                                    -1.56068988083339e-09};
-
-const double ZSPHERE_MATLAB[216] = {0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    6059181.06531642,
-                                    6059181.06531642,
-                                    6059181.06531642,
-                                    6059181.06531642,
-                                    6059181.06531642,
-                                    6059181.06531642,
-                                    3744779.84235535,
-                                    3744779.84235535,
-                                    3744779.84235535,
-                                    3744779.84235535,
-                                    3744779.84235535,
-                                    3744779.84235535,
-                                    -3744779.84235535,
-                                    -3744779.84235535,
-                                    -3744779.84235535,
-                                    -3744779.84235535,
-                                    -3744779.84235535,
-                                    -3744779.84235535,
-                                    -6059181.06531642,
-                                    -6059181.06531642,
-                                    -6059181.06531642,
-                                    -6059181.06531642,
-                                    -6059181.06531642,
-                                    -6059181.06531642,
-                                    -1.56044495147356e-09,
-                                    -1.56044495147356e-09,
-                                    -1.56044495147356e-09,
-                                    -1.56044495147356e-09,
-                                    -1.56044495147356e-09,
-                                    -1.56044495147356e-09,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    6059371.27661968,
-                                    6059371.27661968,
-                                    6059371.27661968,
-                                    6059371.27661968,
-                                    6059371.27661968,
-                                    6059371.27661968,
-                                    3744897.39940581,
-                                    3744897.39940581,
-                                    3744897.39940581,
-                                    3744897.39940581,
-                                    3744897.39940581,
-                                    3744897.39940581,
-                                    -3744897.39940580,
-                                    -3744897.39940580,
-                                    -3744897.39940580,
-                                    -3744897.39940580,
-                                    -3744897.39940580,
-                                    -3744897.39940580,
-                                    -6059371.27661968,
-                                    -6059371.27661968,
-                                    -6059371.27661968,
-                                    -6059371.27661968,
-                                    -6059371.27661968,
-                                    -6059371.27661968,
-                                    -1.56049393734552e-09,
-                                    -1.56049393734552e-09,
-                                    -1.56049393734552e-09,
-                                    -1.56049393734552e-09,
-                                    -1.56049393734552e-09,
-                                    -1.56049393734552e-09,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    6059561.48792294,
-                                    6059561.48792294,
-                                    6059561.48792294,
-                                    6059561.48792294,
-                                    6059561.48792294,
-                                    6059561.48792294,
-                                    3745014.95645626,
-                                    3745014.95645626,
-                                    3745014.95645626,
-                                    3745014.95645626,
-                                    3745014.95645626,
-                                    3745014.95645626,
-                                    -3745014.95645626,
-                                    -3745014.95645626,
-                                    -3745014.95645626,
-                                    -3745014.95645626,
-                                    -3745014.95645626,
-                                    -3745014.95645626,
-                                    -6059561.48792294,
-                                    -6059561.48792294,
-                                    -6059561.48792294,
-                                    -6059561.48792294,
-                                    -6059561.48792294,
-                                    -6059561.48792294,
-                                    -1.56054292321749e-09,
-                                    -1.56054292321749e-09,
-                                    -1.56054292321749e-09,
-                                    -1.56054292321749e-09,
-                                    -1.56054292321749e-09,
-                                    -1.56054292321749e-09,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    6059751.69922620,
-                                    6059751.69922620,
-                                    6059751.69922620,
-                                    6059751.69922620,
-                                    6059751.69922620,
-                                    6059751.69922620,
-                                    3745132.51350672,
-                                    3745132.51350672,
-                                    3745132.51350672,
-                                    3745132.51350672,
-                                    3745132.51350672,
-                                    3745132.51350672,
-                                    -3745132.51350672,
-                                    -3745132.51350672,
-                                    -3745132.51350672,
-                                    -3745132.51350672,
-                                    -3745132.51350672,
-                                    -3745132.51350672,
-                                    -6059751.69922620,
-                                    -6059751.69922620,
-                                    -6059751.69922620,
-                                    -6059751.69922620,
-                                    -6059751.69922620,
-                                    -6059751.69922620,
-                                    -1.56059190908946e-09,
-                                    -1.56059190908946e-09,
-                                    -1.56059190908946e-09,
-                                    -1.56059190908946e-09,
-                                    -1.56059190908946e-09,
-                                    -1.56059190908946e-09,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    6059941.91052946,
-                                    6059941.91052946,
-                                    6059941.91052946,
-                                    6059941.91052946,
-                                    6059941.91052946,
-                                    6059941.91052946,
-                                    3745250.07055718,
-                                    3745250.07055718,
-                                    3745250.07055718,
-                                    3745250.07055718,
-                                    3745250.07055718,
-                                    3745250.07055718,
-                                    -3745250.07055718,
-                                    -3745250.07055718,
-                                    -3745250.07055718,
-                                    -3745250.07055718,
-                                    -3745250.07055718,
-                                    -3745250.07055718,
-                                    -6059941.91052946,
-                                    -6059941.91052946,
-                                    -6059941.91052946,
-                                    -6059941.91052946,
-                                    -6059941.91052946,
-                                    -6059941.91052946,
-                                    -1.56064089496142e-09,
-                                    -1.56064089496142e-09,
-                                    -1.56064089496142e-09,
-                                    -1.56064089496142e-09,
-                                    -1.56064089496142e-09,
-                                    -1.56064089496142e-09,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    0,
-                                    6060132.12183272,
-                                    6060132.12183272,
-                                    6060132.12183272,
-                                    6060132.12183272,
-                                    6060132.12183272,
-                                    6060132.12183272,
-                                    3745367.62760764,
-                                    3745367.62760764,
-                                    3745367.62760764,
-                                    3745367.62760764,
-                                    3745367.62760764,
-                                    3745367.62760764,
-                                    -3745367.62760764,
-                                    -3745367.62760764,
-                                    -3745367.62760764,
-                                    -3745367.62760764,
-                                    -3745367.62760764,
-                                    -3745367.62760764,
-                                    -6060132.12183272,
-                                    -6060132.12183272,
-                                    -6060132.12183272,
-                                    -6060132.12183272,
-                                    -6060132.12183272,
-                                    -6060132.12183272,
-                                    -1.56068988083339e-09,
-                                    -1.56068988083339e-09,
-                                    -1.56068988083339e-09,
-                                    -1.56068988083339e-09,
-                                    -1.56068988083339e-09,
-                                    -1.56068988083339e-09};
+    6371000,           1968747.27116279,  -5154247.27116279, -5154247.27116279, 1968747.27116279,
+    6371000,           1968747.27116279,  608376.364418605,  -1592750,          -1592750.00000000,
+    608376.364418605,  1968747.27116279,  -5154247.27116279, -1592750,          4169873.63558139,
+    4169873.63558139,  -1592750.00000000, -5154247.27116279, -5154247.27116279, -1592750.00000000,
+    4169873.63558139,  4169873.63558140,  -1592750.00000000, -5154247.27116279, 1968747.27116279,
+    608376.364418605,  -1592750.00000000, -1592750.00000000, 608376.364418604,  1968747.27116279,
+    6371000,           1968747.27116279,  -5154247.27116279, -5154247.27116279, 1968747.27116279,
+    6371000,           6371200,           1968809.07456167,  -5154409.07456167, -5154409.07456167,
+    1968809.07456166,  6371200,           1968809.07456167,  608395.462719168,  -1592800,
+    -1592800.00000000, 608395.462719167,  1968809.07456167,  -5154409.07456167, -1592800,
+    4170004.53728083,  4170004.53728083,  -1592800.00000000, -5154409.07456167, -5154409.07456167,
+    -1592800,          4170004.53728083,  4170004.53728083,  -1592800.00000000, -5154409.07456167,
+    1968809.07456166,  608395.462719167,  -1592800.00000000, -1592800.00000000, 608395.462719167,
+    1968809.07456166,  6371200,           1968809.07456167,  -5154409.07456167, -5154409.07456167,
+    1968809.07456166,  6371200,           6371400,           1968870.87796054,  -5154570.87796054,
+    -5154570.87796054, 1968870.87796054,  6371400,           1968870.87796054,  608414.561019730,
+    -1592850,          -1592850.00000000, 608414.561019730,  1968870.87796054,  -5154570.87796054,
+    -1592850,          4170135.43898027,  4170135.43898027,  -1592850.00000000, -5154570.87796054,
+    -5154570.87796054, -1592850.00000000, 4170135.43898027,  4170135.43898027,  -1592850.00000000,
+    -5154570.87796054, 1968870.87796054,  608414.561019730,  -1592850.00000000, -1592850.00000000,
+    608414.561019729,  1968870.87796054,  6371400,           1968870.87796054,  -5154570.87796054,
+    -5154570.87796054, 1968870.87796054,  6371400,           6371600,           1968932.68135942,
+    -5154732.68135941, -5154732.68135942, 1968932.68135941,  6371600,           1968932.68135942,
+    608433.659320293,  -1592900,          -1592900.00000000, 608433.659320292,  1968932.68135942,
+    -5154732.68135941, -1592900.00000000, 4170266.34067971,  4170266.34067971,  -1592900.00000000,
+    -5154732.68135941, -5154732.68135942, -1592900.00000000, 4170266.34067971,  4170266.34067971,
+    -1592900.00000000, -5154732.68135942, 1968932.68135941,  608433.659320292,  -1592900.00000000,
+    -1592900.00000000, 608433.659320292,  1968932.68135941,  6371600,           1968932.68135942,
+    -5154732.68135941, -5154732.68135942, 1968932.68135941,  6371600,           6371800,
+    1968994.48475829,  -5154894.48475829, -5154894.48475829, 1968994.48475829,  6371800,
+    1968994.48475829,  608452.757620855,  -1592950,          -1592950.00000000, 608452.757620855,
+    1968994.48475829,  -5154894.48475829, -1592950,          4170397.24237914,  4170397.24237914,
+    -1592950.00000000, -5154894.48475829, -5154894.48475829, -1592950.00000000, 4170397.24237914,
+    4170397.24237915,  -1592950.00000000, -5154894.48475829, 1968994.48475829,  608452.757620855,
+    -1592950.00000000, -1592950.00000000, 608452.757620854,  1968994.48475829,  6371800,
+    1968994.48475829,  -5154894.48475829, -5154894.48475829, 1968994.48475829,  6371800,
+    6372000,           1969056.28815717,  -5155056.28815716, -5155056.28815717, 1969056.28815716,
+    6372000,           1969056.28815717,  608471.855921418,  -1593000,          -1593000,
+    608471.855921417,  1969056.28815717,  -5155056.28815716, -1593000.00000000, 4170528.14407858,
+    4170528.14407858,  -1593000.00000000, -5155056.28815716, -5155056.28815717, -1593000.00000000,
+    4170528.14407858,  4170528.14407858,  -1593000.00000000, -5155056.28815717, 1969056.28815716,
+    608471.855921417,  -1593000.00000000, -1593000.00000000, 608471.855921417,  1969056.28815716,
+    6372000,           1969056.28815717,  -5155056.28815716, -5155056.28815717, 1969056.28815716,
+    6372000,
+};
+
+const double YSPHERE_MATLAB[216] = {
+    0,
+    6059181.06531642,
+    3744779.84235535,
+    -3744779.84235535,
+    -6059181.06531642,
+    -1.56044495147356e-09,
+    0,
+    1872389.92117767,
+    1157200.61148054,
+    -1157200.61148054,
+    -1872389.92117767,
+    -4.82204008791920e-10,
+    0,
+    -4901980.45383588,
+    -3029590.53265821,
+    3029590.53265821,
+    4901980.45383588,
+    1.26242648452870e-09,
+    0,
+    -4901980.45383589,
+    -3029590.53265821,
+    3029590.53265821,
+    4901980.45383589,
+    1.26242648452870e-09,
+    0,
+    1872389.92117767,
+    1157200.61148054,
+    -1157200.61148054,
+    -1872389.92117767,
+    -4.82204008791919e-10,
+    0,
+    6059181.06531642,
+    3744779.84235535,
+    -3744779.84235535,
+    -6059181.06531642,
+    -1.56044495147356e-09,
+    0,
+    6059371.27661968,
+    3744897.39940581,
+    -3744897.39940580,
+    -6059371.27661968,
+    -1.56049393734552e-09,
+    0,
+    1872448.69970290,
+    1157236.93860694,
+    -1157236.93860694,
+    -1872448.69970290,
+    -4.82219146258841e-10,
+    0,
+    -4902134.33801274,
+    -3029685.63830984,
+    3029685.63830984,
+    4902134.33801274,
+    1.26246611493160e-09,
+    0,
+    -4902134.33801274,
+    -3029685.63830984,
+    3029685.63830984,
+    4902134.33801274,
+    1.26246611493160e-09,
+    0,
+    1872448.69970290,
+    1157236.93860694,
+    -1157236.93860694,
+    -1872448.69970290,
+    -4.82219146258841e-10,
+    0,
+    6059371.27661968,
+    3744897.39940581,
+    -3744897.39940580,
+    -6059371.27661968,
+    -1.56049393734552e-09,
+    0,
+    6059561.48792294,
+    3745014.95645626,
+    -3745014.95645626,
+    -6059561.48792294,
+    -1.56054292321749e-09,
+    0,
+    1872507.47822813,
+    1157273.26573334,
+    -1157273.26573334,
+    -1872507.47822813,
+    -4.82234283725763e-10,
+    0,
+    -4902288.22218960,
+    -3029780.74396147,
+    3029780.74396147,
+    4902288.22218960,
+    1.26250574533451e-09,
+    0,
+    -4902288.22218960,
+    -3029780.74396147,
+    3029780.74396147,
+    4902288.22218960,
+    1.26250574533451e-09,
+    0,
+    1872507.47822813,
+    1157273.26573334,
+    -1157273.26573334,
+    -1872507.47822813,
+    -4.82234283725763e-10,
+    0,
+    6059561.48792294,
+    3745014.95645626,
+    -3745014.95645626,
+    -6059561.48792294,
+    -1.56054292321749e-09,
+    0,
+    6059751.69922620,
+    3745132.51350672,
+    -3745132.51350672,
+    -6059751.69922620,
+    -1.56059190908946e-09,
+    0,
+    1872566.25675336,
+    1157309.59285974,
+    -1157309.59285974,
+    -1872566.25675336,
+    -4.82249421192685e-10,
+    0,
+    -4902442.10636646,
+    -3029875.84961310,
+    3029875.84961310,
+    4902442.10636646,
+    1.26254537573741e-09,
+    0,
+    -4902442.10636646,
+    -3029875.84961310,
+    3029875.84961310,
+    4902442.10636646,
+    1.26254537573741e-09,
+    0,
+    1872566.25675336,
+    1157309.59285974,
+    -1157309.59285974,
+    -1872566.25675336,
+    -4.82249421192684e-10,
+    0,
+    6059751.69922620,
+    3745132.51350672,
+    -3745132.51350672,
+    -6059751.69922620,
+    -1.56059190908946e-09,
+    0,
+    6059941.91052946,
+    3745250.07055718,
+    -3745250.07055718,
+    -6059941.91052946,
+    -1.56064089496142e-09,
+    0,
+    1872625.03527859,
+    1157345.91998614,
+    -1157345.91998614,
+    -1872625.03527859,
+    -4.82264558659606e-10,
+    0,
+    -4902595.99054332,
+    -3029970.95526473,
+    3029970.95526473,
+    4902595.99054332,
+    1.26258500614032e-09,
+    0,
+    -4902595.99054332,
+    -3029970.95526473,
+    3029970.95526473,
+    4902595.99054332,
+    1.26258500614032e-09,
+    0,
+    1872625.03527859,
+    1157345.91998614,
+    -1157345.91998614,
+    -1872625.03527859,
+    -4.82264558659606e-10,
+    0,
+    6059941.91052946,
+    3745250.07055718,
+    -3745250.07055718,
+    -6059941.91052946,
+    -1.56064089496142e-09,
+    0,
+    6060132.12183272,
+    3745367.62760764,
+    -3745367.62760764,
+    -6060132.12183272,
+    -1.56068988083339e-09,
+    0,
+    1872683.81380382,
+    1157382.24711254,
+    -1157382.24711254,
+    -1872683.81380382,
+    -4.82279696126528e-10,
+    0,
+    -4902749.87472018,
+    -3030066.06091636,
+    3030066.06091636,
+    4902749.87472018,
+    1.26262463654322e-09,
+    0,
+    -4902749.87472018,
+    -3030066.06091636,
+    3030066.06091636,
+    4902749.87472018,
+    1.26262463654322e-09,
+    0,
+    1872683.81380382,
+    1157382.24711254,
+    -1157382.24711254,
+    -1872683.81380382,
+    -4.82279696126528e-10,
+    0,
+    6060132.12183272,
+    3745367.62760764,
+    -3745367.62760764,
+    -6060132.12183272,
+    -1.56068988083339e-09,
+};
+
+const double ZSPHERE_MATLAB[216] = {
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6059181.06531642,
+    6059181.06531642,
+    6059181.06531642,
+    6059181.06531642,
+    6059181.06531642,
+    6059181.06531642,
+    3744779.84235535,
+    3744779.84235535,
+    3744779.84235535,
+    3744779.84235535,
+    3744779.84235535,
+    3744779.84235535,
+    -3744779.84235535,
+    -3744779.84235535,
+    -3744779.84235535,
+    -3744779.84235535,
+    -3744779.84235535,
+    -3744779.84235535,
+    -6059181.06531642,
+    -6059181.06531642,
+    -6059181.06531642,
+    -6059181.06531642,
+    -6059181.06531642,
+    -6059181.06531642,
+    -1.56044495147356e-09,
+    -1.56044495147356e-09,
+    -1.56044495147356e-09,
+    -1.56044495147356e-09,
+    -1.56044495147356e-09,
+    -1.56044495147356e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6059371.27661968,
+    6059371.27661968,
+    6059371.27661968,
+    6059371.27661968,
+    6059371.27661968,
+    6059371.27661968,
+    3744897.39940581,
+    3744897.39940581,
+    3744897.39940581,
+    3744897.39940581,
+    3744897.39940581,
+    3744897.39940581,
+    -3744897.39940580,
+    -3744897.39940580,
+    -3744897.39940580,
+    -3744897.39940580,
+    -3744897.39940580,
+    -3744897.39940580,
+    -6059371.27661968,
+    -6059371.27661968,
+    -6059371.27661968,
+    -6059371.27661968,
+    -6059371.27661968,
+    -6059371.27661968,
+    -1.56049393734552e-09,
+    -1.56049393734552e-09,
+    -1.56049393734552e-09,
+    -1.56049393734552e-09,
+    -1.56049393734552e-09,
+    -1.56049393734552e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6059561.48792294,
+    6059561.48792294,
+    6059561.48792294,
+    6059561.48792294,
+    6059561.48792294,
+    6059561.48792294,
+    3745014.95645626,
+    3745014.95645626,
+    3745014.95645626,
+    3745014.95645626,
+    3745014.95645626,
+    3745014.95645626,
+    -3745014.95645626,
+    -3745014.95645626,
+    -3745014.95645626,
+    -3745014.95645626,
+    -3745014.95645626,
+    -3745014.95645626,
+    -6059561.48792294,
+    -6059561.48792294,
+    -6059561.48792294,
+    -6059561.48792294,
+    -6059561.48792294,
+    -6059561.48792294,
+    -1.56054292321749e-09,
+    -1.56054292321749e-09,
+    -1.56054292321749e-09,
+    -1.56054292321749e-09,
+    -1.56054292321749e-09,
+    -1.56054292321749e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6059751.69922620,
+    6059751.69922620,
+    6059751.69922620,
+    6059751.69922620,
+    6059751.69922620,
+    6059751.69922620,
+    3745132.51350672,
+    3745132.51350672,
+    3745132.51350672,
+    3745132.51350672,
+    3745132.51350672,
+    3745132.51350672,
+    -3745132.51350672,
+    -3745132.51350672,
+    -3745132.51350672,
+    -3745132.51350672,
+    -3745132.51350672,
+    -3745132.51350672,
+    -6059751.69922620,
+    -6059751.69922620,
+    -6059751.69922620,
+    -6059751.69922620,
+    -6059751.69922620,
+    -6059751.69922620,
+    -1.56059190908946e-09,
+    -1.56059190908946e-09,
+    -1.56059190908946e-09,
+    -1.56059190908946e-09,
+    -1.56059190908946e-09,
+    -1.56059190908946e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6059941.91052946,
+    6059941.91052946,
+    6059941.91052946,
+    6059941.91052946,
+    6059941.91052946,
+    6059941.91052946,
+    3745250.07055718,
+    3745250.07055718,
+    3745250.07055718,
+    3745250.07055718,
+    3745250.07055718,
+    3745250.07055718,
+    -3745250.07055718,
+    -3745250.07055718,
+    -3745250.07055718,
+    -3745250.07055718,
+    -3745250.07055718,
+    -3745250.07055718,
+    -6059941.91052946,
+    -6059941.91052946,
+    -6059941.91052946,
+    -6059941.91052946,
+    -6059941.91052946,
+    -6059941.91052946,
+    -1.56064089496142e-09,
+    -1.56064089496142e-09,
+    -1.56064089496142e-09,
+    -1.56064089496142e-09,
+    -1.56064089496142e-09,
+    -1.56064089496142e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6060132.12183272,
+    6060132.12183272,
+    6060132.12183272,
+    6060132.12183272,
+    6060132.12183272,
+    6060132.12183272,
+    3745367.62760764,
+    3745367.62760764,
+    3745367.62760764,
+    3745367.62760764,
+    3745367.62760764,
+    3745367.62760764,
+    -3745367.62760764,
+    -3745367.62760764,
+    -3745367.62760764,
+    -3745367.62760764,
+    -3745367.62760764,
+    -3745367.62760764,
+    -6060132.12183272,
+    -6060132.12183272,
+    -6060132.12183272,
+    -6060132.12183272,
+    -6060132.12183272,
+    -6060132.12183272,
+    -1.56068988083339e-09,
+    -1.56068988083339e-09,
+    -1.56068988083339e-09,
+    -1.56068988083339e-09,
+    -1.56068988083339e-09,
+    -1.56068988083339e-09,
+};
 
 const double XGRS80_MATLAB[216] = {
-    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164,
-    1970952.72545164,  6378137,           1976947.14312992,  610910.264208145,
-    -1599383.83577310, -1599383.83577310, 610910.264208145,  1976947.14312992,
-    -5165998.77753988, -1596381.41518003, 4179380.80394997,  4179380.80394997,
-    -1596381.41518003, -5165998.77753988, -5165998.77753988, -1596381.41518003,
-    4179380.80394997,  4179380.80394997,  -1596381.41518003, -5165998.77753988,
-    1976947.14312991,  610910.264208145,  -1599383.83577310, -1599383.83577310,
-    610910.264208145,  1976947.14312991,  6378137,           1970952.72545164,
-    -5160021.22545164, -5160021.22545164, 1970952.72545164,  6378137,
-    6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
-    1971014.52885052,  6378337,           1977008.94652879,  610929.362508708,
-    -1599433.83577310, -1599433.83577310, 610929.362508708,  1977008.94652879,
-    -5166160.58093876, -1596431.41518003, 4179511.70564940,  4179511.70564940,
-    -1596431.41518003, -5166160.58093876, -5166160.58093876, -1596431.41518003,
-    4179511.70564940,  4179511.70564940,  -1596431.41518003, -5166160.58093876,
-    1977008.94652879,  610929.362508708,  -1599433.83577310, -1599433.83577310,
-    610929.362508707,  1977008.94652879,  6378337,           1971014.52885052,
-    -5160183.02885052, -5160183.02885052, 1971014.52885052,  6378337,
-    6378537,           1971076.33224939,  -5160344.83224939, -5160344.83224940,
-    1971076.33224939,  6378537,           1977070.74992767,  610948.460809271,
-    -1599483.83577310, -1599483.83577310, 610948.460809270,  1977070.74992767,
-    -5166322.38433763, -1596481.41518003, 4179642.60734884,  4179642.60734884,
-    -1596481.41518003, -5166322.38433763, -5166322.38433763, -1596481.41518003,
-    4179642.60734884,  4179642.60734884,  -1596481.41518003, -5166322.38433763,
-    1977070.74992766,  610948.460809270,  -1599483.83577310, -1599483.83577310,
-    610948.460809270,  1977070.74992766,  6378537,           1971076.33224939,
-    -5160344.83224939, -5160344.83224940, 1971076.33224939,  6378537,
-    6378737,           1971138.13564827,  -5160506.63564827, -5160506.63564827,
-    1971138.13564827,  6378737,           1977132.55332654,  610967.559109833,
-    -1599533.83577310, -1599533.83577310, 610967.559109833,  1977132.55332654,
-    -5166484.18773651, -1596531.41518003, 4179773.50904828,  4179773.50904828,
-    -1596531.41518003, -5166484.18773651, -5166484.18773651, -1596531.41518003,
-    4179773.50904828,  4179773.50904828,  -1596531.41518003, -5166484.18773651,
-    1977132.55332654,  610967.559109833,  -1599533.83577310, -1599533.83577310,
-    610967.559109832,  1977132.55332654,  6378737,           1971138.13564827,
-    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,
-    6378937,           1971199.93904714,  -5160668.43904714, -5160668.43904714,
-    1971199.93904714,  6378937,           1977194.35672542,  610986.657410395,
-    -1599583.83577310, -1599583.83577310, 610986.657410395,  1977194.35672542,
-    -5166645.99113538, -1596581.41518003, 4179904.41074772,  4179904.41074772,
-    -1596581.41518003, -5166645.99113538, -5166645.99113538, -1596581.41518003,
-    4179904.41074772,  4179904.41074772,  -1596581.41518003, -5166645.99113538,
-    1977194.35672541,  610986.657410395,  -1599583.83577310, -1599583.83577310,
-    610986.657410395,  1977194.35672541,  6378937,           1971199.93904714,
-    -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
-    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602,
-    1971261.74244602,  6379137,           1977256.16012429,  611005.755710958,
-    -1599633.83577310, -1599633.83577310, 611005.755710958,  1977256.16012429,
-    -5166807.79453425, -1596631.41518003, 4180035.31244715,  4180035.31244715,
-    -1596631.41518003, -5166807.79453425, -5166807.79453426, -1596631.41518003,
-    4180035.31244715,  4180035.31244715,  -1596631.41518003, -5166807.79453426,
-    1977256.16012429,  611005.755710958,  -1599633.83577310, -1599633.83577310,
-    611005.755710957,  1977256.16012429,  6379137,           1971261.74244602,
-    -5160830.24244602, -5160830.24244602, 1971261.74244602,  6379137};
-
-const double YGRS80_MATLAB[216] = {0,
-                                   6065968.75567322,
-                                   3748974.86570096,
-                                   -3748974.86570096,
-                                   -6065968.75567322,
-                                   -1.56219301231466e-09,
-                                   0,
-                                   1880188.46284479,
-                                   1162020.37529350,
-                                   -1162020.37529350,
-                                   -1880188.46284479,
-                                   -4.84212398183511e-10,
-                                   0,
-                                   -4913156.80055210,
-                                   -3036497.89479889,
-                                   3036497.89479889,
-                                   4913156.80055210,
-                                   1.26530477346267e-09,
-                                   0,
-                                   -4913156.80055210,
-                                   -3036497.89479889,
-                                   3036497.89479889,
-                                   4913156.80055210,
-                                   1.26530477346267e-09,
-                                   0,
-                                   1880188.46284479,
-                                   1162020.37529350,
-                                   -1162020.37529350,
-                                   -1880188.46284479,
-                                   -4.84212398183511e-10,
-                                   0,
-                                   6065968.75567322,
-                                   3748974.86570096,
-                                   -3748974.86570096,
-                                   -6065968.75567322,
-                                   -1.56219301231466e-09,
-                                   0,
-                                   6066158.96697648,
-                                   3749092.42275142,
-                                   -3749092.42275142,
-                                   -6066158.96697648,
-                                   -1.56224199818663e-09,
-                                   0,
-                                   1880247.24137002,
-                                   1162056.70241990,
-                                   -1162056.70241990,
-                                   -1880247.24137002,
-                                   -4.84227535650433e-10,
-                                   0,
-                                   -4913310.68472896,
-                                   -3036593.00045052,
-                                   3036593.00045051,
-                                   4913310.68472896,
-                                   1.26534440386558e-09,
-                                   0,
-                                   -4913310.68472896,
-                                   -3036593.00045052,
-                                   3036593.00045051,
-                                   4913310.68472896,
-                                   1.26534440386558e-09,
-                                   0,
-                                   1880247.24137002,
-                                   1162056.70241990,
-                                   -1162056.70241990,
-                                   -1880247.24137002,
-                                   -4.84227535650433e-10,
-                                   0,
-                                   6066158.96697648,
-                                   3749092.42275142,
-                                   -3749092.42275142,
-                                   -6066158.96697648,
-                                   -1.56224199818663e-09,
-                                   0,
-                                   6066349.17827974,
-                                   3749209.97980188,
-                                   -3749209.97980187,
-                                   -6066349.17827974,
-                                   -1.56229098405859e-09,
-                                   0,
-                                   1880306.01989525,
-                                   1162093.02954630,
-                                   -1162093.02954630,
-                                   -1880306.01989525,
-                                   -4.84242673117355e-10,
-                                   0,
-                                   -4913464.56890582,
-                                   -3036688.10610215,
-                                   3036688.10610214,
-                                   4913464.56890582,
-                                   1.26538403426848e-09,
-                                   0,
-                                   -4913464.56890582,
-                                   -3036688.10610215,
-                                   3036688.10610215,
-                                   4913464.56890582,
-                                   1.26538403426848e-09,
-                                   0,
-                                   1880306.01989525,
-                                   1162093.02954630,
-                                   -1162093.02954630,
-                                   -1880306.01989525,
-                                   -4.84242673117354e-10,
-                                   0,
-                                   6066349.17827974,
-                                   3749209.97980188,
-                                   -3749209.97980187,
-                                   -6066349.17827974,
-                                   -1.56229098405859e-09,
-                                   0,
-                                   6066539.38958300,
-                                   3749327.53685233,
-                                   -3749327.53685233,
-                                   -6066539.38958300,
-                                   -1.56233996993056e-09,
-                                   0,
-                                   1880364.79842048,
-                                   1162129.35667270,
-                                   -1162129.35667270,
-                                   -1880364.79842048,
-                                   -4.84257810584276e-10,
-                                   0,
-                                   -4913618.45308268,
-                                   -3036783.21175378,
-                                   3036783.21175377,
-                                   4913618.45308268,
-                                   1.26542366467138e-09,
-                                   0,
-                                   -4913618.45308268,
-                                   -3036783.21175378,
-                                   3036783.21175377,
-                                   4913618.45308268,
-                                   1.26542366467138e-09,
-                                   0,
-                                   1880364.79842048,
-                                   1162129.35667270,
-                                   -1162129.35667270,
-                                   -1880364.79842048,
-                                   -4.84257810584276e-10,
-                                   0,
-                                   6066539.38958300,
-                                   3749327.53685233,
-                                   -3749327.53685233,
-                                   -6066539.38958300,
-                                   -1.56233996993056e-09,
-                                   0,
-                                   6066729.60088626,
-                                   3749445.09390279,
-                                   -3749445.09390279,
-                                   -6066729.60088626,
-                                   -1.56238895580252e-09,
-                                   0,
-                                   1880423.57694571,
-                                   1162165.68379910,
-                                   -1162165.68379910,
-                                   -1880423.57694571,
-                                   -4.84272948051198e-10,
-                                   0,
-                                   -4913772.33725954,
-                                   -3036878.31740540,
-                                   3036878.31740540,
-                                   4913772.33725954,
-                                   1.26546329507429e-09,
-                                   0,
-                                   -4913772.33725954,
-                                   -3036878.31740541,
-                                   3036878.31740540,
-                                   4913772.33725954,
-                                   1.26546329507429e-09,
-                                   0,
-                                   1880423.57694571,
-                                   1162165.68379910,
-                                   -1162165.68379910,
-                                   -1880423.57694571,
-                                   -4.84272948051198e-10,
-                                   0,
-                                   6066729.60088626,
-                                   3749445.09390279,
-                                   -3749445.09390279,
-                                   -6066729.60088626,
-                                   -1.56238895580252e-09,
-                                   0,
-                                   6066919.81218952,
-                                   3749562.65095325,
-                                   -3749562.65095325,
-                                   -6066919.81218952,
-                                   -1.56243794167449e-09,
-                                   0,
-                                   1880482.35547094,
-                                   1162202.01092550,
-                                   -1162202.01092550,
-                                   -1880482.35547094,
-                                   -4.84288085518120e-10,
-                                   0,
-                                   -4913926.22143639,
-                                   -3036973.42305703,
-                                   3036973.42305703,
-                                   4913926.22143639,
-                                   1.26550292547719e-09,
-                                   0,
-                                   -4913926.22143639,
-                                   -3036973.42305703,
-                                   3036973.42305703,
-                                   4913926.22143639,
-                                   1.26550292547719e-09,
-                                   0,
-                                   1880482.35547094,
-                                   1162202.01092550,
-                                   -1162202.01092550,
-                                   -1880482.35547094,
-                                   -4.84288085518119e-10,
-                                   0,
-                                   6066919.81218952,
-                                   3749562.65095325,
-                                   -3749562.65095325,
-                                   -6066919.81218952,
-                                   -1.56243794167449e-09};
-
-const double ZGRS80_MATLAB[216] = {0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6043686.27213354,
-                                   6043686.27213354,
-                                   6043686.27213354,
-                                   6043686.27213354,
-                                   6043686.27213354,
-                                   6043686.27213354,
-                                   3728191.67572948,
-                                   3728191.67572948,
-                                   3728191.67572948,
-                                   3728191.67572948,
-                                   3728191.67572948,
-                                   3728191.67572948,
-                                   -3728191.67572948,
-                                   -3728191.67572948,
-                                   -3728191.67572948,
-                                   -3728191.67572948,
-                                   -3728191.67572948,
-                                   -3728191.67572948,
-                                   -6043686.27213354,
-                                   -6043686.27213354,
-                                   -6043686.27213354,
-                                   -6043686.27213354,
-                                   -6043686.27213354,
-                                   -6043686.27213354,
-                                   -1.55173509862111e-09,
-                                   -1.55173509862111e-09,
-                                   -1.55173509862111e-09,
-                                   -1.55173509862111e-09,
-                                   -1.55173509862111e-09,
-                                   -1.55173509862111e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6043876.48343680,
-                                   6043876.48343680,
-                                   6043876.48343680,
-                                   6043876.48343680,
-                                   6043876.48343680,
-                                   6043876.48343680,
-                                   3728309.23277994,
-                                   3728309.23277994,
-                                   3728309.23277994,
-                                   3728309.23277994,
-                                   3728309.23277994,
-                                   3728309.23277994,
-                                   -3728309.23277994,
-                                   -3728309.23277994,
-                                   -3728309.23277994,
-                                   -3728309.23277994,
-                                   -3728309.23277994,
-                                   -3728309.23277994,
-                                   -6043876.48343680,
-                                   -6043876.48343680,
-                                   -6043876.48343680,
-                                   -6043876.48343680,
-                                   -6043876.48343680,
-                                   -6043876.48343680,
-                                   -1.55178408449307e-09,
-                                   -1.55178408449307e-09,
-                                   -1.55178408449307e-09,
-                                   -1.55178408449307e-09,
-                                   -1.55178408449307e-09,
-                                   -1.55178408449307e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044066.69474006,
-                                   6044066.69474006,
-                                   6044066.69474006,
-                                   6044066.69474006,
-                                   6044066.69474006,
-                                   6044066.69474006,
-                                   3728426.78983040,
-                                   3728426.78983040,
-                                   3728426.78983040,
-                                   3728426.78983040,
-                                   3728426.78983040,
-                                   3728426.78983040,
-                                   -3728426.78983040,
-                                   -3728426.78983040,
-                                   -3728426.78983040,
-                                   -3728426.78983040,
-                                   -3728426.78983040,
-                                   -3728426.78983040,
-                                   -6044066.69474006,
-                                   -6044066.69474006,
-                                   -6044066.69474006,
-                                   -6044066.69474006,
-                                   -6044066.69474006,
-                                   -6044066.69474006,
-                                   -1.55183307036504e-09,
-                                   -1.55183307036504e-09,
-                                   -1.55183307036504e-09,
-                                   -1.55183307036504e-09,
-                                   -1.55183307036504e-09,
-                                   -1.55183307036504e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044256.90604332,
-                                   6044256.90604332,
-                                   6044256.90604332,
-                                   6044256.90604332,
-                                   6044256.90604332,
-                                   6044256.90604332,
-                                   3728544.34688086,
-                                   3728544.34688086,
-                                   3728544.34688086,
-                                   3728544.34688086,
-                                   3728544.34688086,
-                                   3728544.34688086,
-                                   -3728544.34688086,
-                                   -3728544.34688086,
-                                   -3728544.34688086,
-                                   -3728544.34688086,
-                                   -3728544.34688086,
-                                   -3728544.34688086,
-                                   -6044256.90604332,
-                                   -6044256.90604332,
-                                   -6044256.90604332,
-                                   -6044256.90604332,
-                                   -6044256.90604332,
-                                   -6044256.90604332,
-                                   -1.55188205623700e-09,
-                                   -1.55188205623700e-09,
-                                   -1.55188205623700e-09,
-                                   -1.55188205623700e-09,
-                                   -1.55188205623700e-09,
-                                   -1.55188205623700e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044447.11734658,
-                                   6044447.11734658,
-                                   6044447.11734658,
-                                   6044447.11734658,
-                                   6044447.11734658,
-                                   6044447.11734658,
-                                   3728661.90393132,
-                                   3728661.90393132,
-                                   3728661.90393132,
-                                   3728661.90393132,
-                                   3728661.90393132,
-                                   3728661.90393132,
-                                   -3728661.90393132,
-                                   -3728661.90393132,
-                                   -3728661.90393132,
-                                   -3728661.90393132,
-                                   -3728661.90393132,
-                                   -3728661.90393132,
-                                   -6044447.11734658,
-                                   -6044447.11734658,
-                                   -6044447.11734658,
-                                   -6044447.11734658,
-                                   -6044447.11734658,
-                                   -6044447.11734658,
-                                   -1.55193104210897e-09,
-                                   -1.55193104210897e-09,
-                                   -1.55193104210897e-09,
-                                   -1.55193104210897e-09,
-                                   -1.55193104210897e-09,
-                                   -1.55193104210897e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044637.32864983,
-                                   6044637.32864983,
-                                   6044637.32864983,
-                                   6044637.32864983,
-                                   6044637.32864983,
-                                   6044637.32864983,
-                                   3728779.46098178,
-                                   3728779.46098178,
-                                   3728779.46098178,
-                                   3728779.46098178,
-                                   3728779.46098178,
-                                   3728779.46098178,
-                                   -3728779.46098177,
-                                   -3728779.46098177,
-                                   -3728779.46098177,
-                                   -3728779.46098177,
-                                   -3728779.46098177,
-                                   -3728779.46098177,
-                                   -6044637.32864983,
-                                   -6044637.32864983,
-                                   -6044637.32864983,
-                                   -6044637.32864983,
-                                   -6044637.32864983,
-                                   -6044637.32864983,
-                                   -1.55198002798094e-09,
-                                   -1.55198002798094e-09,
-                                   -1.55198002798094e-09,
-                                   -1.55198002798094e-09,
-                                   -1.55198002798094e-09,
-                                   -1.55198002798094e-09};
+    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
+    6378137,           1976947.14312992,  610910.264208145,  -1599383.83577310, -1599383.83577310,
+    610910.264208145,  1976947.14312992,  -5165998.77753988, -1596381.41518003, 4179380.80394997,
+    4179380.80394997,  -1596381.41518003, -5165998.77753988, -5165998.77753988, -1596381.41518003,
+    4179380.80394997,  4179380.80394997,  -1596381.41518003, -5165998.77753988, 1976947.14312991,
+    610910.264208145,  -1599383.83577310, -1599383.83577310, 610910.264208145,  1976947.14312991,
+    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
+    6378137,           6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
+    1971014.52885052,  6378337,           1977008.94652879,  610929.362508708,  -1599433.83577310,
+    -1599433.83577310, 610929.362508708,  1977008.94652879,  -5166160.58093876, -1596431.41518003,
+    4179511.70564940,  4179511.70564940,  -1596431.41518003, -5166160.58093876, -5166160.58093876,
+    -1596431.41518003, 4179511.70564940,  4179511.70564940,  -1596431.41518003, -5166160.58093876,
+    1977008.94652879,  610929.362508708,  -1599433.83577310, -1599433.83577310, 610929.362508707,
+    1977008.94652879,  6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
+    1971014.52885052,  6378337,           6378537,           1971076.33224939,  -5160344.83224939,
+    -5160344.83224940, 1971076.33224939,  6378537,           1977070.74992767,  610948.460809271,
+    -1599483.83577310, -1599483.83577310, 610948.460809270,  1977070.74992767,  -5166322.38433763,
+    -1596481.41518003, 4179642.60734884,  4179642.60734884,  -1596481.41518003, -5166322.38433763,
+    -5166322.38433763, -1596481.41518003, 4179642.60734884,  4179642.60734884,  -1596481.41518003,
+    -5166322.38433763, 1977070.74992766,  610948.460809270,  -1599483.83577310, -1599483.83577310,
+    610948.460809270,  1977070.74992766,  6378537,           1971076.33224939,  -5160344.83224939,
+    -5160344.83224940, 1971076.33224939,  6378537,           6378737,           1971138.13564827,
+    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           1977132.55332654,
+    610967.559109833,  -1599533.83577310, -1599533.83577310, 610967.559109833,  1977132.55332654,
+    -5166484.18773651, -1596531.41518003, 4179773.50904828,  4179773.50904828,  -1596531.41518003,
+    -5166484.18773651, -5166484.18773651, -1596531.41518003, 4179773.50904828,  4179773.50904828,
+    -1596531.41518003, -5166484.18773651, 1977132.55332654,  610967.559109833,  -1599533.83577310,
+    -1599533.83577310, 610967.559109832,  1977132.55332654,  6378737,           1971138.13564827,
+    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           6378937,
+    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
+    1977194.35672542,  610986.657410395,  -1599583.83577310, -1599583.83577310, 610986.657410395,
+    1977194.35672542,  -5166645.99113538, -1596581.41518003, 4179904.41074772,  4179904.41074772,
+    -1596581.41518003, -5166645.99113538, -5166645.99113538, -1596581.41518003, 4179904.41074772,
+    4179904.41074772,  -1596581.41518003, -5166645.99113538, 1977194.35672541,  610986.657410395,
+    -1599583.83577310, -1599583.83577310, 610986.657410395,  1977194.35672541,  6378937,
+    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
+    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
+    6379137,           1977256.16012429,  611005.755710958,  -1599633.83577310, -1599633.83577310,
+    611005.755710958,  1977256.16012429,  -5166807.79453425, -1596631.41518003, 4180035.31244715,
+    4180035.31244715,  -1596631.41518003, -5166807.79453425, -5166807.79453426, -1596631.41518003,
+    4180035.31244715,  4180035.31244715,  -1596631.41518003, -5166807.79453426, 1977256.16012429,
+    611005.755710958,  -1599633.83577310, -1599633.83577310, 611005.755710957,  1977256.16012429,
+    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
+    6379137,
+};
+
+const double YGRS80_MATLAB[216] = {
+    0,
+    6065968.75567322,
+    3748974.86570096,
+    -3748974.86570096,
+    -6065968.75567322,
+    -1.56219301231466e-09,
+    0,
+    1880188.46284479,
+    1162020.37529350,
+    -1162020.37529350,
+    -1880188.46284479,
+    -4.84212398183511e-10,
+    0,
+    -4913156.80055210,
+    -3036497.89479889,
+    3036497.89479889,
+    4913156.80055210,
+    1.26530477346267e-09,
+    0,
+    -4913156.80055210,
+    -3036497.89479889,
+    3036497.89479889,
+    4913156.80055210,
+    1.26530477346267e-09,
+    0,
+    1880188.46284479,
+    1162020.37529350,
+    -1162020.37529350,
+    -1880188.46284479,
+    -4.84212398183511e-10,
+    0,
+    6065968.75567322,
+    3748974.86570096,
+    -3748974.86570096,
+    -6065968.75567322,
+    -1.56219301231466e-09,
+    0,
+    6066158.96697648,
+    3749092.42275142,
+    -3749092.42275142,
+    -6066158.96697648,
+    -1.56224199818663e-09,
+    0,
+    1880247.24137002,
+    1162056.70241990,
+    -1162056.70241990,
+    -1880247.24137002,
+    -4.84227535650433e-10,
+    0,
+    -4913310.68472896,
+    -3036593.00045052,
+    3036593.00045051,
+    4913310.68472896,
+    1.26534440386558e-09,
+    0,
+    -4913310.68472896,
+    -3036593.00045052,
+    3036593.00045051,
+    4913310.68472896,
+    1.26534440386558e-09,
+    0,
+    1880247.24137002,
+    1162056.70241990,
+    -1162056.70241990,
+    -1880247.24137002,
+    -4.84227535650433e-10,
+    0,
+    6066158.96697648,
+    3749092.42275142,
+    -3749092.42275142,
+    -6066158.96697648,
+    -1.56224199818663e-09,
+    0,
+    6066349.17827974,
+    3749209.97980188,
+    -3749209.97980187,
+    -6066349.17827974,
+    -1.56229098405859e-09,
+    0,
+    1880306.01989525,
+    1162093.02954630,
+    -1162093.02954630,
+    -1880306.01989525,
+    -4.84242673117355e-10,
+    0,
+    -4913464.56890582,
+    -3036688.10610215,
+    3036688.10610214,
+    4913464.56890582,
+    1.26538403426848e-09,
+    0,
+    -4913464.56890582,
+    -3036688.10610215,
+    3036688.10610215,
+    4913464.56890582,
+    1.26538403426848e-09,
+    0,
+    1880306.01989525,
+    1162093.02954630,
+    -1162093.02954630,
+    -1880306.01989525,
+    -4.84242673117354e-10,
+    0,
+    6066349.17827974,
+    3749209.97980188,
+    -3749209.97980187,
+    -6066349.17827974,
+    -1.56229098405859e-09,
+    0,
+    6066539.38958300,
+    3749327.53685233,
+    -3749327.53685233,
+    -6066539.38958300,
+    -1.56233996993056e-09,
+    0,
+    1880364.79842048,
+    1162129.35667270,
+    -1162129.35667270,
+    -1880364.79842048,
+    -4.84257810584276e-10,
+    0,
+    -4913618.45308268,
+    -3036783.21175378,
+    3036783.21175377,
+    4913618.45308268,
+    1.26542366467138e-09,
+    0,
+    -4913618.45308268,
+    -3036783.21175378,
+    3036783.21175377,
+    4913618.45308268,
+    1.26542366467138e-09,
+    0,
+    1880364.79842048,
+    1162129.35667270,
+    -1162129.35667270,
+    -1880364.79842048,
+    -4.84257810584276e-10,
+    0,
+    6066539.38958300,
+    3749327.53685233,
+    -3749327.53685233,
+    -6066539.38958300,
+    -1.56233996993056e-09,
+    0,
+    6066729.60088626,
+    3749445.09390279,
+    -3749445.09390279,
+    -6066729.60088626,
+    -1.56238895580252e-09,
+    0,
+    1880423.57694571,
+    1162165.68379910,
+    -1162165.68379910,
+    -1880423.57694571,
+    -4.84272948051198e-10,
+    0,
+    -4913772.33725954,
+    -3036878.31740540,
+    3036878.31740540,
+    4913772.33725954,
+    1.26546329507429e-09,
+    0,
+    -4913772.33725954,
+    -3036878.31740541,
+    3036878.31740540,
+    4913772.33725954,
+    1.26546329507429e-09,
+    0,
+    1880423.57694571,
+    1162165.68379910,
+    -1162165.68379910,
+    -1880423.57694571,
+    -4.84272948051198e-10,
+    0,
+    6066729.60088626,
+    3749445.09390279,
+    -3749445.09390279,
+    -6066729.60088626,
+    -1.56238895580252e-09,
+    0,
+    6066919.81218952,
+    3749562.65095325,
+    -3749562.65095325,
+    -6066919.81218952,
+    -1.56243794167449e-09,
+    0,
+    1880482.35547094,
+    1162202.01092550,
+    -1162202.01092550,
+    -1880482.35547094,
+    -4.84288085518120e-10,
+    0,
+    -4913926.22143639,
+    -3036973.42305703,
+    3036973.42305703,
+    4913926.22143639,
+    1.26550292547719e-09,
+    0,
+    -4913926.22143639,
+    -3036973.42305703,
+    3036973.42305703,
+    4913926.22143639,
+    1.26550292547719e-09,
+    0,
+    1880482.35547094,
+    1162202.01092550,
+    -1162202.01092550,
+    -1880482.35547094,
+    -4.84288085518119e-10,
+    0,
+    6066919.81218952,
+    3749562.65095325,
+    -3749562.65095325,
+    -6066919.81218952,
+    -1.56243794167449e-09,
+};
+
+const double ZGRS80_MATLAB[216] = {
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6043686.27213354,
+    6043686.27213354,
+    6043686.27213354,
+    6043686.27213354,
+    6043686.27213354,
+    6043686.27213354,
+    3728191.67572948,
+    3728191.67572948,
+    3728191.67572948,
+    3728191.67572948,
+    3728191.67572948,
+    3728191.67572948,
+    -3728191.67572948,
+    -3728191.67572948,
+    -3728191.67572948,
+    -3728191.67572948,
+    -3728191.67572948,
+    -3728191.67572948,
+    -6043686.27213354,
+    -6043686.27213354,
+    -6043686.27213354,
+    -6043686.27213354,
+    -6043686.27213354,
+    -6043686.27213354,
+    -1.55173509862111e-09,
+    -1.55173509862111e-09,
+    -1.55173509862111e-09,
+    -1.55173509862111e-09,
+    -1.55173509862111e-09,
+    -1.55173509862111e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6043876.48343680,
+    6043876.48343680,
+    6043876.48343680,
+    6043876.48343680,
+    6043876.48343680,
+    6043876.48343680,
+    3728309.23277994,
+    3728309.23277994,
+    3728309.23277994,
+    3728309.23277994,
+    3728309.23277994,
+    3728309.23277994,
+    -3728309.23277994,
+    -3728309.23277994,
+    -3728309.23277994,
+    -3728309.23277994,
+    -3728309.23277994,
+    -3728309.23277994,
+    -6043876.48343680,
+    -6043876.48343680,
+    -6043876.48343680,
+    -6043876.48343680,
+    -6043876.48343680,
+    -6043876.48343680,
+    -1.55178408449307e-09,
+    -1.55178408449307e-09,
+    -1.55178408449307e-09,
+    -1.55178408449307e-09,
+    -1.55178408449307e-09,
+    -1.55178408449307e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044066.69474006,
+    6044066.69474006,
+    6044066.69474006,
+    6044066.69474006,
+    6044066.69474006,
+    6044066.69474006,
+    3728426.78983040,
+    3728426.78983040,
+    3728426.78983040,
+    3728426.78983040,
+    3728426.78983040,
+    3728426.78983040,
+    -3728426.78983040,
+    -3728426.78983040,
+    -3728426.78983040,
+    -3728426.78983040,
+    -3728426.78983040,
+    -3728426.78983040,
+    -6044066.69474006,
+    -6044066.69474006,
+    -6044066.69474006,
+    -6044066.69474006,
+    -6044066.69474006,
+    -6044066.69474006,
+    -1.55183307036504e-09,
+    -1.55183307036504e-09,
+    -1.55183307036504e-09,
+    -1.55183307036504e-09,
+    -1.55183307036504e-09,
+    -1.55183307036504e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044256.90604332,
+    6044256.90604332,
+    6044256.90604332,
+    6044256.90604332,
+    6044256.90604332,
+    6044256.90604332,
+    3728544.34688086,
+    3728544.34688086,
+    3728544.34688086,
+    3728544.34688086,
+    3728544.34688086,
+    3728544.34688086,
+    -3728544.34688086,
+    -3728544.34688086,
+    -3728544.34688086,
+    -3728544.34688086,
+    -3728544.34688086,
+    -3728544.34688086,
+    -6044256.90604332,
+    -6044256.90604332,
+    -6044256.90604332,
+    -6044256.90604332,
+    -6044256.90604332,
+    -6044256.90604332,
+    -1.55188205623700e-09,
+    -1.55188205623700e-09,
+    -1.55188205623700e-09,
+    -1.55188205623700e-09,
+    -1.55188205623700e-09,
+    -1.55188205623700e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044447.11734658,
+    6044447.11734658,
+    6044447.11734658,
+    6044447.11734658,
+    6044447.11734658,
+    6044447.11734658,
+    3728661.90393132,
+    3728661.90393132,
+    3728661.90393132,
+    3728661.90393132,
+    3728661.90393132,
+    3728661.90393132,
+    -3728661.90393132,
+    -3728661.90393132,
+    -3728661.90393132,
+    -3728661.90393132,
+    -3728661.90393132,
+    -3728661.90393132,
+    -6044447.11734658,
+    -6044447.11734658,
+    -6044447.11734658,
+    -6044447.11734658,
+    -6044447.11734658,
+    -6044447.11734658,
+    -1.55193104210897e-09,
+    -1.55193104210897e-09,
+    -1.55193104210897e-09,
+    -1.55193104210897e-09,
+    -1.55193104210897e-09,
+    -1.55193104210897e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044637.32864983,
+    6044637.32864983,
+    6044637.32864983,
+    6044637.32864983,
+    6044637.32864983,
+    6044637.32864983,
+    3728779.46098178,
+    3728779.46098178,
+    3728779.46098178,
+    3728779.46098178,
+    3728779.46098178,
+    3728779.46098178,
+    -3728779.46098177,
+    -3728779.46098177,
+    -3728779.46098177,
+    -3728779.46098177,
+    -3728779.46098177,
+    -3728779.46098177,
+    -6044637.32864983,
+    -6044637.32864983,
+    -6044637.32864983,
+    -6044637.32864983,
+    -6044637.32864983,
+    -6044637.32864983,
+    -1.55198002798094e-09,
+    -1.55198002798094e-09,
+    -1.55198002798094e-09,
+    -1.55198002798094e-09,
+    -1.55198002798094e-09,
+    -1.55198002798094e-09,
+};
 
 const double XWGS84_MATLAB[216] = {
-    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164,
-    1970952.72545164,  6378137,           1976947.14310045,  610910.264199039,
-    -1599383.83574926, -1599383.83574926, 610910.264199039,  1976947.14310045,
-    -5165998.77751058, -1596381.41517097, 4179380.80392626,  4179380.80392626,
-    -1596381.41517097, -5165998.77751058, -5165998.77751058, -1596381.41517097,
-    4179380.80392626,  4179380.80392626,  -1596381.41517097, -5165998.77751058,
-    1976947.14310045,  610910.264199039,  -1599383.83574926, -1599383.83574926,
-    610910.264199039,  1976947.14310045,  6378137,           1970952.72545164,
-    -5160021.22545164, -5160021.22545164, 1970952.72545164,  6378137,
-    6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
-    1971014.52885052,  6378337,           1977008.94649932,  610929.362499602,
-    -1599433.83574926, -1599433.83574926, 610929.362499601,  1977008.94649932,
-    -5166160.58090945, -1596431.41517097, 4179511.70562570,  4179511.70562570,
-    -1596431.41517097, -5166160.58090945, -5166160.58090945, -1596431.41517097,
-    4179511.70562570,  4179511.70562570,  -1596431.41517097, -5166160.58090945,
-    1977008.94649932,  610929.362499601,  -1599433.83574926, -1599433.83574926,
-    610929.362499601,  1977008.94649932,  6378337,           1971014.52885052,
-    -5160183.02885052, -5160183.02885052, 1971014.52885052,  6378337,
-    6378537,           1971076.33224939,  -5160344.83224939, -5160344.83224940,
-    1971076.33224939,  6378537,           1977070.74989820,  610948.460800164,
-    -1599483.83574926, -1599483.83574926, 610948.460800164,  1977070.74989820,
-    -5166322.38430833, -1596481.41517097, 4179642.60732513,  4179642.60732514,
-    -1596481.41517097, -5166322.38430833, -5166322.38430833, -1596481.41517097,
-    4179642.60732514,  4179642.60732514,  -1596481.41517097, -5166322.38430833,
-    1977070.74989820,  610948.460800164,  -1599483.83574926, -1599483.83574926,
-    610948.460800163,  1977070.74989820,  6378537,           1971076.33224939,
-    -5160344.83224939, -5160344.83224940, 1971076.33224939,  6378537,
-    6378737,           1971138.13564827,  -5160506.63564827, -5160506.63564827,
-    1971138.13564827,  6378737,           1977132.55329707,  610967.559100727,
-    -1599533.83574926, -1599533.83574926, 610967.559100726,  1977132.55329707,
-    -5166484.18770720, -1596531.41517097, 4179773.50902457,  4179773.50902457,
-    -1596531.41517097, -5166484.18770720, -5166484.18770720, -1596531.41517097,
-    4179773.50902457,  4179773.50902457,  -1596531.41517097, -5166484.18770720,
-    1977132.55329707,  610967.559100726,  -1599533.83574926, -1599533.83574926,
-    610967.559100726,  1977132.55329707,  6378737,           1971138.13564827,
-    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,
-    6378937,           1971199.93904714,  -5160668.43904714, -5160668.43904714,
-    1971199.93904714,  6378937,           1977194.35669595,  610986.657401289,
-    -1599583.83574926, -1599583.83574926, 610986.657401289,  1977194.35669595,
-    -5166645.99110608, -1596581.41517097, 4179904.41072401,  4179904.41072401,
-    -1596581.41517097, -5166645.99110608, -5166645.99110608, -1596581.41517097,
-    4179904.41072401,  4179904.41072401,  -1596581.41517097, -5166645.99110608,
-    1977194.35669595,  610986.657401289,  -1599583.83574926, -1599583.83574926,
-    610986.657401288,  1977194.35669595,  6378937,           1971199.93904714,
-    -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
-    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602,
-    1971261.74244602,  6379137,           1977256.16009482,  611005.755701852,
-    -1599633.83574926, -1599633.83574926, 611005.755701851,  1977256.16009482,
-    -5166807.79450495, -1596631.41517097, 4180035.31242345,  4180035.31242345,
-    -1596631.41517097, -5166807.79450495, -5166807.79450495, -1596631.41517097,
-    4180035.31242345,  4180035.31242345,  -1596631.41517097, -5166807.79450495,
-    1977256.16009482,  611005.755701851,  -1599633.83574926, -1599633.83574926,
-    611005.755701851,  1977256.16009482,  6379137,           1971261.74244602,
-    -5160830.24244602, -5160830.24244602, 1971261.74244602,  6379137};
-
-const double YWGS84_MATLAB[216] = {0,
-                                   6065968.75567322,
-                                   3748974.86570096,
-                                   -3748974.86570096,
-                                   -6065968.75567322,
-                                   -1.56219301231466e-09,
-                                   0,
-                                   1880188.46281677,
-                                   1162020.37527618,
-                                   -1162020.37527618,
-                                   -1880188.46281677,
-                                   -4.84212398176294e-10,
-                                   0,
-                                   -4913156.80052423,
-                                   -3036497.89478166,
-                                   3036497.89478166,
-                                   4913156.80052423,
-                                   1.26530477345549e-09,
-                                   0,
-                                   -4913156.80052423,
-                                   -3036497.89478166,
-                                   3036497.89478166,
-                                   4913156.80052423,
-                                   1.26530477345549e-09,
-                                   0,
-                                   1880188.46281677,
-                                   1162020.37527618,
-                                   -1162020.37527618,
-                                   -1880188.46281677,
-                                   -4.84212398176293e-10,
-                                   0,
-                                   6065968.75567322,
-                                   3748974.86570096,
-                                   -3748974.86570096,
-                                   -6065968.75567322,
-                                   -1.56219301231466e-09,
-                                   0,
-                                   6066158.96697648,
-                                   3749092.42275142,
-                                   -3749092.42275142,
-                                   -6066158.96697648,
-                                   -1.56224199818663e-09,
-                                   0,
-                                   1880247.24134200,
-                                   1162056.70240258,
-                                   -1162056.70240258,
-                                   -1880247.24134200,
-                                   -4.84227535643215e-10,
-                                   0,
-                                   -4913310.68470109,
-                                   -3036593.00043329,
-                                   3036593.00043329,
-                                   4913310.68470109,
-                                   1.26534440385840e-09,
-                                   0,
-                                   -4913310.68470109,
-                                   -3036593.00043329,
-                                   3036593.00043329,
-                                   4913310.68470109,
-                                   1.26534440385840e-09,
-                                   0,
-                                   1880247.24134200,
-                                   1162056.70240258,
-                                   -1162056.70240258,
-                                   -1880247.24134200,
-                                   -4.84227535643215e-10,
-                                   0,
-                                   6066158.96697648,
-                                   3749092.42275142,
-                                   -3749092.42275142,
-                                   -6066158.96697648,
-                                   -1.56224199818663e-09,
-                                   0,
-                                   6066349.17827974,
-                                   3749209.97980188,
-                                   -3749209.97980187,
-                                   -6066349.17827974,
-                                   -1.56229098405859e-09,
-                                   0,
-                                   1880306.01986723,
-                                   1162093.02952898,
-                                   -1162093.02952898,
-                                   -1880306.01986723,
-                                   -4.84242673110137e-10,
-                                   0,
-                                   -4913464.56887795,
-                                   -3036688.10608492,
-                                   3036688.10608492,
-                                   4913464.56887795,
-                                   1.26538403426130e-09,
-                                   0,
-                                   -4913464.56887795,
-                                   -3036688.10608492,
-                                   3036688.10608492,
-                                   4913464.56887795,
-                                   1.26538403426130e-09,
-                                   0,
-                                   1880306.01986722,
-                                   1162093.02952898,
-                                   -1162093.02952898,
-                                   -1880306.01986723,
-                                   -4.84242673110137e-10,
-                                   0,
-                                   6066349.17827974,
-                                   3749209.97980188,
-                                   -3749209.97980187,
-                                   -6066349.17827974,
-                                   -1.56229098405859e-09,
-                                   0,
-                                   6066539.38958300,
-                                   3749327.53685233,
-                                   -3749327.53685233,
-                                   -6066539.38958300,
-                                   -1.56233996993056e-09,
-                                   0,
-                                   1880364.79839246,
-                                   1162129.35665538,
-                                   -1162129.35665538,
-                                   -1880364.79839246,
-                                   -4.84257810577059e-10,
-                                   0,
-                                   -4913618.45305481,
-                                   -3036783.21173655,
-                                   3036783.21173655,
-                                   4913618.45305481,
-                                   1.26542366466421e-09,
-                                   0,
-                                   -4913618.45305481,
-                                   -3036783.21173655,
-                                   3036783.21173655,
-                                   4913618.45305481,
-                                   1.26542366466421e-09,
-                                   0,
-                                   1880364.79839245,
-                                   1162129.35665538,
-                                   -1162129.35665538,
-                                   -1880364.79839245,
-                                   -4.84257810577058e-10,
-                                   0,
-                                   6066539.38958300,
-                                   3749327.53685233,
-                                   -3749327.53685233,
-                                   -6066539.38958300,
-                                   -1.56233996993056e-09,
-                                   0,
-                                   6066729.60088626,
-                                   3749445.09390279,
-                                   -3749445.09390279,
-                                   -6066729.60088626,
-                                   -1.56238895580252e-09,
-                                   0,
-                                   1880423.57691768,
-                                   1162165.68378178,
-                                   -1162165.68378178,
-                                   -1880423.57691768,
-                                   -4.84272948043980e-10,
-                                   0,
-                                   -4913772.33723167,
-                                   -3036878.31738818,
-                                   3036878.31738818,
-                                   4913772.33723167,
-                                   1.26546329506711e-09,
-                                   0,
-                                   -4913772.33723167,
-                                   -3036878.31738818,
-                                   3036878.31738818,
-                                   4913772.33723167,
-                                   1.26546329506711e-09,
-                                   0,
-                                   1880423.57691768,
-                                   1162165.68378178,
-                                   -1162165.68378178,
-                                   -1880423.57691768,
-                                   -4.84272948043980e-10,
-                                   0,
-                                   6066729.60088626,
-                                   3749445.09390279,
-                                   -3749445.09390279,
-                                   -6066729.60088626,
-                                   -1.56238895580252e-09,
-                                   0,
-                                   6066919.81218952,
-                                   3749562.65095325,
-                                   -3749562.65095325,
-                                   -6066919.81218952,
-                                   -1.56243794167449e-09,
-                                   0,
-                                   1880482.35544291,
-                                   1162202.01090818,
-                                   -1162202.01090818,
-                                   -1880482.35544291,
-                                   -4.84288085510902e-10,
-                                   0,
-                                   -4913926.22140853,
-                                   -3036973.42303981,
-                                   3036973.42303981,
-                                   4913926.22140853,
-                                   1.26550292547002e-09,
-                                   0,
-                                   -4913926.22140853,
-                                   -3036973.42303981,
-                                   3036973.42303981,
-                                   4913926.22140853,
-                                   1.26550292547002e-09,
-                                   0,
-                                   1880482.35544291,
-                                   1162202.01090818,
-                                   -1162202.01090818,
-                                   -1880482.35544291,
-                                   -4.84288085510902e-10,
-                                   0,
-                                   6066919.81218952,
-                                   3749562.65095325,
-                                   -3749562.65095325,
-                                   -6066919.81218952,
-                                   -1.56243794167449e-09};
-
-const double ZWGS84_MATLAB[216] = {0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6043686.27224277,
-                                   6043686.27224277,
-                                   6043686.27224277,
-                                   6043686.27224277,
-                                   6043686.27224277,
-                                   6043686.27224277,
-                                   3728191.67583129,
-                                   3728191.67583129,
-                                   3728191.67583129,
-                                   3728191.67583129,
-                                   3728191.67583129,
-                                   3728191.67583129,
-                                   -3728191.67583129,
-                                   -3728191.67583129,
-                                   -3728191.67583129,
-                                   -3728191.67583129,
-                                   -3728191.67583129,
-                                   -3728191.67583129,
-                                   -6043686.27224277,
-                                   -6043686.27224277,
-                                   -6043686.27224277,
-                                   -6043686.27224277,
-                                   -6043686.27224277,
-                                   -6043686.27224277,
-                                   -1.55173509867228e-09,
-                                   -1.55173509867228e-09,
-                                   -1.55173509867228e-09,
-                                   -1.55173509867228e-09,
-                                   -1.55173509867228e-09,
-                                   -1.55173509867228e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6043876.48354603,
-                                   6043876.48354603,
-                                   6043876.48354603,
-                                   6043876.48354603,
-                                   6043876.48354603,
-                                   6043876.48354603,
-                                   3728309.23288175,
-                                   3728309.23288175,
-                                   3728309.23288175,
-                                   3728309.23288175,
-                                   3728309.23288175,
-                                   3728309.23288175,
-                                   -3728309.23288175,
-                                   -3728309.23288175,
-                                   -3728309.23288175,
-                                   -3728309.23288175,
-                                   -3728309.23288175,
-                                   -3728309.23288175,
-                                   -6043876.48354603,
-                                   -6043876.48354603,
-                                   -6043876.48354603,
-                                   -6043876.48354603,
-                                   -6043876.48354603,
-                                   -6043876.48354603,
-                                   -1.55178408454425e-09,
-                                   -1.55178408454425e-09,
-                                   -1.55178408454425e-09,
-                                   -1.55178408454425e-09,
-                                   -1.55178408454425e-09,
-                                   -1.55178408454425e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044066.69484929,
-                                   6044066.69484929,
-                                   6044066.69484929,
-                                   6044066.69484929,
-                                   6044066.69484929,
-                                   6044066.69484929,
-                                   3728426.78993221,
-                                   3728426.78993221,
-                                   3728426.78993221,
-                                   3728426.78993221,
-                                   3728426.78993221,
-                                   3728426.78993221,
-                                   -3728426.78993221,
-                                   -3728426.78993221,
-                                   -3728426.78993221,
-                                   -3728426.78993221,
-                                   -3728426.78993221,
-                                   -3728426.78993221,
-                                   -6044066.69484929,
-                                   -6044066.69484929,
-                                   -6044066.69484929,
-                                   -6044066.69484929,
-                                   -6044066.69484929,
-                                   -6044066.69484929,
-                                   -1.55183307041621e-09,
-                                   -1.55183307041621e-09,
-                                   -1.55183307041621e-09,
-                                   -1.55183307041621e-09,
-                                   -1.55183307041621e-09,
-                                   -1.55183307041621e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044256.90615255,
-                                   6044256.90615255,
-                                   6044256.90615255,
-                                   6044256.90615255,
-                                   6044256.90615255,
-                                   6044256.90615255,
-                                   3728544.34698267,
-                                   3728544.34698267,
-                                   3728544.34698267,
-                                   3728544.34698267,
-                                   3728544.34698267,
-                                   3728544.34698267,
-                                   -3728544.34698267,
-                                   -3728544.34698267,
-                                   -3728544.34698267,
-                                   -3728544.34698267,
-                                   -3728544.34698267,
-                                   -3728544.34698267,
-                                   -6044256.90615255,
-                                   -6044256.90615255,
-                                   -6044256.90615255,
-                                   -6044256.90615255,
-                                   -6044256.90615255,
-                                   -6044256.90615255,
-                                   -1.55188205628818e-09,
-                                   -1.55188205628818e-09,
-                                   -1.55188205628818e-09,
-                                   -1.55188205628818e-09,
-                                   -1.55188205628818e-09,
-                                   -1.55188205628818e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044447.11745581,
-                                   6044447.11745581,
-                                   6044447.11745581,
-                                   6044447.11745581,
-                                   6044447.11745581,
-                                   6044447.11745581,
-                                   3728661.90403313,
-                                   3728661.90403313,
-                                   3728661.90403313,
-                                   3728661.90403313,
-                                   3728661.90403313,
-                                   3728661.90403313,
-                                   -3728661.90403313,
-                                   -3728661.90403313,
-                                   -3728661.90403313,
-                                   -3728661.90403313,
-                                   -3728661.90403313,
-                                   -3728661.90403313,
-                                   -6044447.11745581,
-                                   -6044447.11745581,
-                                   -6044447.11745581,
-                                   -6044447.11745581,
-                                   -6044447.11745581,
-                                   -6044447.11745581,
-                                   -1.55193104216015e-09,
-                                   -1.55193104216015e-09,
-                                   -1.55193104216015e-09,
-                                   -1.55193104216015e-09,
-                                   -1.55193104216015e-09,
-                                   -1.55193104216015e-09,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   0,
-                                   6044637.32875907,
-                                   6044637.32875907,
-                                   6044637.32875907,
-                                   6044637.32875907,
-                                   6044637.32875907,
-                                   6044637.32875907,
-                                   3728779.46108359,
-                                   3728779.46108359,
-                                   3728779.46108359,
-                                   3728779.46108359,
-                                   3728779.46108359,
-                                   3728779.46108359,
-                                   -3728779.46108358,
-                                   -3728779.46108358,
-                                   -3728779.46108358,
-                                   -3728779.46108358,
-                                   -3728779.46108358,
-                                   -3728779.46108358,
-                                   -6044637.32875907,
-                                   -6044637.32875907,
-                                   -6044637.32875907,
-                                   -6044637.32875907,
-                                   -6044637.32875907,
-                                   -6044637.32875907,
-                                   -1.55198002803211e-09,
-                                   -1.55198002803211e-09,
-                                   -1.55198002803211e-09,
-                                   -1.55198002803211e-09,
-                                   -1.55198002803211e-09,
-                                   -1.55198002803211e-09};
+    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
+    6378137,           1976947.14310045,  610910.264199039,  -1599383.83574926, -1599383.83574926,
+    610910.264199039,  1976947.14310045,  -5165998.77751058, -1596381.41517097, 4179380.80392626,
+    4179380.80392626,  -1596381.41517097, -5165998.77751058, -5165998.77751058, -1596381.41517097,
+    4179380.80392626,  4179380.80392626,  -1596381.41517097, -5165998.77751058, 1976947.14310045,
+    610910.264199039,  -1599383.83574926, -1599383.83574926, 610910.264199039,  1976947.14310045,
+    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
+    6378137,           6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
+    1971014.52885052,  6378337,           1977008.94649932,  610929.362499602,  -1599433.83574926,
+    -1599433.83574926, 610929.362499601,  1977008.94649932,  -5166160.58090945, -1596431.41517097,
+    4179511.70562570,  4179511.70562570,  -1596431.41517097, -5166160.58090945, -5166160.58090945,
+    -1596431.41517097, 4179511.70562570,  4179511.70562570,  -1596431.41517097, -5166160.58090945,
+    1977008.94649932,  610929.362499601,  -1599433.83574926, -1599433.83574926, 610929.362499601,
+    1977008.94649932,  6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
+    1971014.52885052,  6378337,           6378537,           1971076.33224939,  -5160344.83224939,
+    -5160344.83224940, 1971076.33224939,  6378537,           1977070.74989820,  610948.460800164,
+    -1599483.83574926, -1599483.83574926, 610948.460800164,  1977070.74989820,  -5166322.38430833,
+    -1596481.41517097, 4179642.60732513,  4179642.60732514,  -1596481.41517097, -5166322.38430833,
+    -5166322.38430833, -1596481.41517097, 4179642.60732514,  4179642.60732514,  -1596481.41517097,
+    -5166322.38430833, 1977070.74989820,  610948.460800164,  -1599483.83574926, -1599483.83574926,
+    610948.460800163,  1977070.74989820,  6378537,           1971076.33224939,  -5160344.83224939,
+    -5160344.83224940, 1971076.33224939,  6378537,           6378737,           1971138.13564827,
+    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           1977132.55329707,
+    610967.559100727,  -1599533.83574926, -1599533.83574926, 610967.559100726,  1977132.55329707,
+    -5166484.18770720, -1596531.41517097, 4179773.50902457,  4179773.50902457,  -1596531.41517097,
+    -5166484.18770720, -5166484.18770720, -1596531.41517097, 4179773.50902457,  4179773.50902457,
+    -1596531.41517097, -5166484.18770720, 1977132.55329707,  610967.559100726,  -1599533.83574926,
+    -1599533.83574926, 610967.559100726,  1977132.55329707,  6378737,           1971138.13564827,
+    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           6378937,
+    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
+    1977194.35669595,  610986.657401289,  -1599583.83574926, -1599583.83574926, 610986.657401289,
+    1977194.35669595,  -5166645.99110608, -1596581.41517097, 4179904.41072401,  4179904.41072401,
+    -1596581.41517097, -5166645.99110608, -5166645.99110608, -1596581.41517097, 4179904.41072401,
+    4179904.41072401,  -1596581.41517097, -5166645.99110608, 1977194.35669595,  610986.657401289,
+    -1599583.83574926, -1599583.83574926, 610986.657401288,  1977194.35669595,  6378937,
+    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
+    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
+    6379137,           1977256.16009482,  611005.755701852,  -1599633.83574926, -1599633.83574926,
+    611005.755701851,  1977256.16009482,  -5166807.79450495, -1596631.41517097, 4180035.31242345,
+    4180035.31242345,  -1596631.41517097, -5166807.79450495, -5166807.79450495, -1596631.41517097,
+    4180035.31242345,  4180035.31242345,  -1596631.41517097, -5166807.79450495, 1977256.16009482,
+    611005.755701851,  -1599633.83574926, -1599633.83574926, 611005.755701851,  1977256.16009482,
+    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
+    6379137,
+};
+
+const double YWGS84_MATLAB[216] = {
+    0,
+    6065968.75567322,
+    3748974.86570096,
+    -3748974.86570096,
+    -6065968.75567322,
+    -1.56219301231466e-09,
+    0,
+    1880188.46281677,
+    1162020.37527618,
+    -1162020.37527618,
+    -1880188.46281677,
+    -4.84212398176294e-10,
+    0,
+    -4913156.80052423,
+    -3036497.89478166,
+    3036497.89478166,
+    4913156.80052423,
+    1.26530477345549e-09,
+    0,
+    -4913156.80052423,
+    -3036497.89478166,
+    3036497.89478166,
+    4913156.80052423,
+    1.26530477345549e-09,
+    0,
+    1880188.46281677,
+    1162020.37527618,
+    -1162020.37527618,
+    -1880188.46281677,
+    -4.84212398176293e-10,
+    0,
+    6065968.75567322,
+    3748974.86570096,
+    -3748974.86570096,
+    -6065968.75567322,
+    -1.56219301231466e-09,
+    0,
+    6066158.96697648,
+    3749092.42275142,
+    -3749092.42275142,
+    -6066158.96697648,
+    -1.56224199818663e-09,
+    0,
+    1880247.24134200,
+    1162056.70240258,
+    -1162056.70240258,
+    -1880247.24134200,
+    -4.84227535643215e-10,
+    0,
+    -4913310.68470109,
+    -3036593.00043329,
+    3036593.00043329,
+    4913310.68470109,
+    1.26534440385840e-09,
+    0,
+    -4913310.68470109,
+    -3036593.00043329,
+    3036593.00043329,
+    4913310.68470109,
+    1.26534440385840e-09,
+    0,
+    1880247.24134200,
+    1162056.70240258,
+    -1162056.70240258,
+    -1880247.24134200,
+    -4.84227535643215e-10,
+    0,
+    6066158.96697648,
+    3749092.42275142,
+    -3749092.42275142,
+    -6066158.96697648,
+    -1.56224199818663e-09,
+    0,
+    6066349.17827974,
+    3749209.97980188,
+    -3749209.97980187,
+    -6066349.17827974,
+    -1.56229098405859e-09,
+    0,
+    1880306.01986723,
+    1162093.02952898,
+    -1162093.02952898,
+    -1880306.01986723,
+    -4.84242673110137e-10,
+    0,
+    -4913464.56887795,
+    -3036688.10608492,
+    3036688.10608492,
+    4913464.56887795,
+    1.26538403426130e-09,
+    0,
+    -4913464.56887795,
+    -3036688.10608492,
+    3036688.10608492,
+    4913464.56887795,
+    1.26538403426130e-09,
+    0,
+    1880306.01986722,
+    1162093.02952898,
+    -1162093.02952898,
+    -1880306.01986723,
+    -4.84242673110137e-10,
+    0,
+    6066349.17827974,
+    3749209.97980188,
+    -3749209.97980187,
+    -6066349.17827974,
+    -1.56229098405859e-09,
+    0,
+    6066539.38958300,
+    3749327.53685233,
+    -3749327.53685233,
+    -6066539.38958300,
+    -1.56233996993056e-09,
+    0,
+    1880364.79839246,
+    1162129.35665538,
+    -1162129.35665538,
+    -1880364.79839246,
+    -4.84257810577059e-10,
+    0,
+    -4913618.45305481,
+    -3036783.21173655,
+    3036783.21173655,
+    4913618.45305481,
+    1.26542366466421e-09,
+    0,
+    -4913618.45305481,
+    -3036783.21173655,
+    3036783.21173655,
+    4913618.45305481,
+    1.26542366466421e-09,
+    0,
+    1880364.79839245,
+    1162129.35665538,
+    -1162129.35665538,
+    -1880364.79839245,
+    -4.84257810577058e-10,
+    0,
+    6066539.38958300,
+    3749327.53685233,
+    -3749327.53685233,
+    -6066539.38958300,
+    -1.56233996993056e-09,
+    0,
+    6066729.60088626,
+    3749445.09390279,
+    -3749445.09390279,
+    -6066729.60088626,
+    -1.56238895580252e-09,
+    0,
+    1880423.57691768,
+    1162165.68378178,
+    -1162165.68378178,
+    -1880423.57691768,
+    -4.84272948043980e-10,
+    0,
+    -4913772.33723167,
+    -3036878.31738818,
+    3036878.31738818,
+    4913772.33723167,
+    1.26546329506711e-09,
+    0,
+    -4913772.33723167,
+    -3036878.31738818,
+    3036878.31738818,
+    4913772.33723167,
+    1.26546329506711e-09,
+    0,
+    1880423.57691768,
+    1162165.68378178,
+    -1162165.68378178,
+    -1880423.57691768,
+    -4.84272948043980e-10,
+    0,
+    6066729.60088626,
+    3749445.09390279,
+    -3749445.09390279,
+    -6066729.60088626,
+    -1.56238895580252e-09,
+    0,
+    6066919.81218952,
+    3749562.65095325,
+    -3749562.65095325,
+    -6066919.81218952,
+    -1.56243794167449e-09,
+    0,
+    1880482.35544291,
+    1162202.01090818,
+    -1162202.01090818,
+    -1880482.35544291,
+    -4.84288085510902e-10,
+    0,
+    -4913926.22140853,
+    -3036973.42303981,
+    3036973.42303981,
+    4913926.22140853,
+    1.26550292547002e-09,
+    0,
+    -4913926.22140853,
+    -3036973.42303981,
+    3036973.42303981,
+    4913926.22140853,
+    1.26550292547002e-09,
+    0,
+    1880482.35544291,
+    1162202.01090818,
+    -1162202.01090818,
+    -1880482.35544291,
+    -4.84288085510902e-10,
+    0,
+    6066919.81218952,
+    3749562.65095325,
+    -3749562.65095325,
+    -6066919.81218952,
+    -1.56243794167449e-09,
+};
+
+const double ZWGS84_MATLAB[216] = {
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6043686.27224277,
+    6043686.27224277,
+    6043686.27224277,
+    6043686.27224277,
+    6043686.27224277,
+    6043686.27224277,
+    3728191.67583129,
+    3728191.67583129,
+    3728191.67583129,
+    3728191.67583129,
+    3728191.67583129,
+    3728191.67583129,
+    -3728191.67583129,
+    -3728191.67583129,
+    -3728191.67583129,
+    -3728191.67583129,
+    -3728191.67583129,
+    -3728191.67583129,
+    -6043686.27224277,
+    -6043686.27224277,
+    -6043686.27224277,
+    -6043686.27224277,
+    -6043686.27224277,
+    -6043686.27224277,
+    -1.55173509867228e-09,
+    -1.55173509867228e-09,
+    -1.55173509867228e-09,
+    -1.55173509867228e-09,
+    -1.55173509867228e-09,
+    -1.55173509867228e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6043876.48354603,
+    6043876.48354603,
+    6043876.48354603,
+    6043876.48354603,
+    6043876.48354603,
+    6043876.48354603,
+    3728309.23288175,
+    3728309.23288175,
+    3728309.23288175,
+    3728309.23288175,
+    3728309.23288175,
+    3728309.23288175,
+    -3728309.23288175,
+    -3728309.23288175,
+    -3728309.23288175,
+    -3728309.23288175,
+    -3728309.23288175,
+    -3728309.23288175,
+    -6043876.48354603,
+    -6043876.48354603,
+    -6043876.48354603,
+    -6043876.48354603,
+    -6043876.48354603,
+    -6043876.48354603,
+    -1.55178408454425e-09,
+    -1.55178408454425e-09,
+    -1.55178408454425e-09,
+    -1.55178408454425e-09,
+    -1.55178408454425e-09,
+    -1.55178408454425e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044066.69484929,
+    6044066.69484929,
+    6044066.69484929,
+    6044066.69484929,
+    6044066.69484929,
+    6044066.69484929,
+    3728426.78993221,
+    3728426.78993221,
+    3728426.78993221,
+    3728426.78993221,
+    3728426.78993221,
+    3728426.78993221,
+    -3728426.78993221,
+    -3728426.78993221,
+    -3728426.78993221,
+    -3728426.78993221,
+    -3728426.78993221,
+    -3728426.78993221,
+    -6044066.69484929,
+    -6044066.69484929,
+    -6044066.69484929,
+    -6044066.69484929,
+    -6044066.69484929,
+    -6044066.69484929,
+    -1.55183307041621e-09,
+    -1.55183307041621e-09,
+    -1.55183307041621e-09,
+    -1.55183307041621e-09,
+    -1.55183307041621e-09,
+    -1.55183307041621e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044256.90615255,
+    6044256.90615255,
+    6044256.90615255,
+    6044256.90615255,
+    6044256.90615255,
+    6044256.90615255,
+    3728544.34698267,
+    3728544.34698267,
+    3728544.34698267,
+    3728544.34698267,
+    3728544.34698267,
+    3728544.34698267,
+    -3728544.34698267,
+    -3728544.34698267,
+    -3728544.34698267,
+    -3728544.34698267,
+    -3728544.34698267,
+    -3728544.34698267,
+    -6044256.90615255,
+    -6044256.90615255,
+    -6044256.90615255,
+    -6044256.90615255,
+    -6044256.90615255,
+    -6044256.90615255,
+    -1.55188205628818e-09,
+    -1.55188205628818e-09,
+    -1.55188205628818e-09,
+    -1.55188205628818e-09,
+    -1.55188205628818e-09,
+    -1.55188205628818e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044447.11745581,
+    6044447.11745581,
+    6044447.11745581,
+    6044447.11745581,
+    6044447.11745581,
+    6044447.11745581,
+    3728661.90403313,
+    3728661.90403313,
+    3728661.90403313,
+    3728661.90403313,
+    3728661.90403313,
+    3728661.90403313,
+    -3728661.90403313,
+    -3728661.90403313,
+    -3728661.90403313,
+    -3728661.90403313,
+    -3728661.90403313,
+    -3728661.90403313,
+    -6044447.11745581,
+    -6044447.11745581,
+    -6044447.11745581,
+    -6044447.11745581,
+    -6044447.11745581,
+    -6044447.11745581,
+    -1.55193104216015e-09,
+    -1.55193104216015e-09,
+    -1.55193104216015e-09,
+    -1.55193104216015e-09,
+    -1.55193104216015e-09,
+    -1.55193104216015e-09,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    6044637.32875907,
+    6044637.32875907,
+    6044637.32875907,
+    6044637.32875907,
+    6044637.32875907,
+    6044637.32875907,
+    3728779.46108359,
+    3728779.46108359,
+    3728779.46108359,
+    3728779.46108359,
+    3728779.46108359,
+    3728779.46108359,
+    -3728779.46108358,
+    -3728779.46108358,
+    -3728779.46108358,
+    -3728779.46108358,
+    -3728779.46108358,
+    -3728779.46108358,
+    -6044637.32875907,
+    -6044637.32875907,
+    -6044637.32875907,
+    -6044637.32875907,
+    -6044637.32875907,
+    -6044637.32875907,
+    -1.55198002803211e-09,
+    -1.55198002803211e-09,
+    -1.55198002803211e-09,
+    -1.55198002803211e-09,
+    -1.55198002803211e-09,
+    -1.55198002803211e-09,
+};
 
 /**
  * \ingroup mobility
+ * \ingroup tests
  * \defgroup mobility-test mobility module tests
  */
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Geo To Cartesian Test Case
  *
@@ -1689,7 +1674,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Cartesian to Geo Test Case
  *
@@ -1806,7 +1790,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Geo To Cartesian Test Suite
  */
@@ -1891,4 +1874,8 @@
     }
 }
 
+/**
+ * \ingroup mobility-test
+ * Static variable for test initialization
+ */
 static GeoToCartesianTestSuite g_GeoToCartesianTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/test/mobility-test-suite.cc ns-3.38/src/mobility/test/mobility-test-suite.cc
--- ns-3.37/src/mobility/test/mobility-test-suite.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/test/mobility-test-suite.cc	2023-03-16 20:54:23.792869000 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Test whether course change notifications occur regardless of calls
  * to Update() position (which are triggered by calls to GetPosition())
@@ -113,7 +112,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Waypoint Lazy Notify True
  */
@@ -188,7 +186,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Waypoint Initial Position Is Waypoint Test
  */
@@ -382,7 +379,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Waypoint Mobility Model Via Helper Test
  */
@@ -478,7 +474,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Mobility Test Suite
  */
@@ -497,4 +492,8 @@
     AddTestCase(new WaypointMobilityModelViaHelper, TestCase::QUICK);
 }
 
-static MobilityTestSuite mobilityTestSuite; ///< the test suite
+/**
+ * \ingroup mobility-test
+ * Static variable for test initialization
+ */
+static MobilityTestSuite mobilityTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/test/mobility-trace-test-suite.cc ns-3.38/src/mobility/test/mobility-trace-test-suite.cc
--- ns-3.37/src/mobility/test/mobility-trace-test-suite.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/test/mobility-trace-test-suite.cc	2023-03-16 20:54:23.796869000 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Mobility Trace Test Case
  */
@@ -114,7 +113,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Mobility Trace Test Suite
  */
@@ -131,4 +129,8 @@
     AddTestCase(new MobilityTraceTestCase, TestCase::QUICK);
 }
 
-static MobilityTraceTestSuite mobilityTraceTestSuite; ///< the test suite
+/**
+ * \ingroup mobility-test
+ * Static variable for test initialization
+ */
+static MobilityTraceTestSuite mobilityTraceTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/test/ns2-mobility-helper-test-suite.cc ns-3.38/src/mobility/test/ns2-mobility-helper-test-suite.cc
--- ns-3.37/src/mobility/test/ns2-mobility-helper-test-suite.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/test/ns2-mobility-helper-test-suite.cc	2023-03-16 20:54:23.796869000 -0700
@@ -76,7 +76,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Every test case is supposed to:
  *  1. Generate short mobility trace file
@@ -115,7 +114,7 @@
         /**
          * Less-than operator - used to sort by timestamp
          * \param o object to compare to
-         * \returns true if the timestamp of the 1nd operand is less than the other one's
+         * \returns true if the timestamp of the 1st operand is less than the other one's
          */
         bool operator<(const ReferencePoint& o) const
         {
@@ -203,7 +202,7 @@
     }
 
     /// Create and name nodes
-    void CreateNodes()
+    void CreateNodes() const
     {
         NodeContainer nodes;
         nodes.Create(m_nodeCount);
@@ -322,7 +321,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief The test suite
  */
@@ -510,7 +508,7 @@
         t->AddReferencePoint("0", 3, Vector(0, -1, 0), Vector(0, 0, 0));
         AddTestCase(t, TestCase::QUICK);
 
-        // Sqare setdest with values in the form 1.0e+2
+        // Square setdest with values in the form 1.0e+2
         t = new Ns2MobilityHelperTest("Foalt numbers in 1.0e+2 format", Seconds(6));
         t->SetTrace("$node_(0) set X_ 0.0\n"
                     "$node_(0) set Y_ 0.0\n"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/test/rand-cart-around-geo-test.cc ns-3.38/src/mobility/test/rand-cart-around-geo-test.cc
--- ns-3.37/src/mobility/test/rand-cart-around-geo-test.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/test/rand-cart-around-geo-test.cc	2023-03-16 20:54:23.796869000 -0700
@@ -57,7 +57,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Rand Cart Around Geo Test Case
  */
@@ -174,7 +173,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Rand Cart Around Geo Test Suite
  */
@@ -209,4 +207,8 @@
     }
 }
 
-static RandCartAroundGeoTestSuite g_RandCartAroundGeoTestSuite; ///< the test suite
+/**
+ * \ingroup mobility-test
+ * Static variable for test initialization
+ */
+static RandCartAroundGeoTestSuite g_RandCartAroundGeoTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/test/steady-state-random-waypoint-mobility-model-test.cc ns-3.38/src/mobility/test/steady-state-random-waypoint-mobility-model-test.cc
--- ns-3.37/src/mobility/test/steady-state-random-waypoint-mobility-model-test.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/test/steady-state-random-waypoint-mobility-model-test.cc	2023-03-16 20:54:23.796869000 -0700
@@ -30,7 +30,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Steady State Random Waypoint Test
  */
@@ -153,7 +152,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Steady State Random Waypoint Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mobility/test/waypoint-mobility-model-test.cc ns-3.38/src/mobility/test/waypoint-mobility-model-test.cc
--- ns-3.37/src/mobility/test/waypoint-mobility-model-test.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mobility/test/waypoint-mobility-model-test.cc	2023-03-16 20:54:23.796869000 -0700
@@ -27,7 +27,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Waypoint Mobility Model Notify Test
  */
@@ -172,7 +171,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Waypoint Mobility Model Add Waypoint Test
  */
@@ -197,7 +195,7 @@
     void DoRun() override;
     void DoTeardown() override;
     /**
-     * Course change calback
+     * Course change callback
      * \param model the mobility model
      */
     void CourseChangeCallback(Ptr<const MobilityModel> model);
@@ -257,7 +255,6 @@
 
 /**
  * \ingroup mobility-test
- * \ingroup tests
  *
  * \brief Waypoint Mobility Model Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/doc/distributed.rst ns-3.38/src/mpi/doc/distributed.rst
--- ns-3.37/src/mpi/doc/distributed.rst	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mpi/doc/distributed.rst	2023-03-16 20:54:23.796869000 -0700
@@ -243,20 +243,20 @@
 showing how to add a command line argument to control the
 synchronization algorithm choice:::
 
-  cmd.AddValue ("nullmsg", "Enable the use of null-message synchronization", nullmsg);
-  if(nullmsg)
+  cmd.AddValue("nullmsg", "Enable the use of null-message synchronization", nullmsg);
+  if (nullmsg)
     {
-      GlobalValue::Bind ("SimulatorImplementationType",
-                         StringValue ("ns3::NullMessageSimulatorImpl"));
+      GlobalValue::Bind("SimulatorImplementationType",
+                         StringValue("ns3::NullMessageSimulatorImpl"));
     }
   else
     {
-      GlobalValue::Bind ("SimulatorImplementationType",
-                         StringValue ("ns3::DistributedSimulatorImpl"));
+      GlobalValue::Bind("SimulatorImplementationType",
+                         StringValue("ns3::DistributedSimulatorImpl"));
     }
 
   // Enable parallel simulator with the command line arguments
-  MpiInterface::Enable (&argc, &argv);
+  MpiInterface::Enable(&argc, &argv);
 
 
 
@@ -274,17 +274,17 @@
 First, a NodeContainer can be used to create the nodes and assign system ids::
 
     NodeContainer nodes;
-    nodes.Create (5, 1); // Creates 5 nodes with system id 1.
+    nodes.Create(5, 1); // Creates 5 nodes with system id 1.
 
 Alternatively, nodes can be created individually, assigned system ids, and added
 to a NodeContainer. This is useful if a NodeContainer holds nodes with different
 system ids::
 
     NodeContainer nodes;
-    Ptr<Node> node1 = CreateObject<Node> (0); // Create node1 with system id 0
-    Ptr<Node> node2 = CreateObject<Node> (1); // Create node2 with system id 1
-    nodes.Add (node1);
-    nodes.Add (node2);
+    Ptr<Node> node1 = CreateObject<Node>(0); // Create node1 with system id 0
+    Ptr<Node> node2 = CreateObject<Node>(1); // Create node2 with system id 1
+    nodes.Add(node1);
+    nodes.Add(node2);
 
 Next, where the simulation is divided is determined by the placement of
 point-to-point links. If a point-to-point link is created between two
@@ -308,15 +308,15 @@
 like this should work well, assuming all of these local variables were
 previously defined::
 
-    if (MpiInterface::GetSystemId () == 0)
+    if (MpiInterface::GetSystemId() == 0)
       {
-        pointToPoint.EnablePcapAll ("distributed-rank0");
-        phy.EnablePcap ("distributed-rank0", apDevices.Get (0));
-        csma.EnablePcap ("distributed-rank0", csmaDevices.Get (0), true);
+        pointToPoint.EnablePcapAll("distributed-rank0");
+        phy.EnablePcap("distributed-rank0", apDevices.Get(0));
+        csma.EnablePcap("distributed-rank0", csmaDevices.Get(0), true);
       }
-    else if (MpiInterface::GetSystemId () == 1)
+    else if (MpiInterface::GetSystemId() == 1)
       {
-        pointToPoint.EnablePcapAll ("distributed-rank1");
-        phy.EnablePcap ("distributed-rank1", apDevices.Get (0));
-        csma.EnablePcap ("distributed-rank1", csmaDevices.Get (0), true);
+        pointToPoint.EnablePcapAll("distributed-rank1");
+        phy.EnablePcap("distributed-rank1", apDevices.Get(0));
+        csma.EnablePcap("distributed-rank1", csmaDevices.Get(0), true);
       }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/examples/mpi-test-fixtures.cc ns-3.38/src/mpi/examples/mpi-test-fixtures.cc
--- ns-3.37/src/mpi/examples/mpi-test-fixtures.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mpi/examples/mpi-test-fixtures.cc	2023-03-16 20:54:23.796869000 -0700
@@ -19,8 +19,6 @@
 
 #include "mpi-test-fixtures.h"
 
-#include "mpi.h"
-
 #include "ns3/address.h"
 #include "ns3/inet-socket-address.h"
 #include "ns3/inet6-socket-address.h"
@@ -28,6 +26,8 @@
 #include "ns3/ptr.h"
 #include "ns3/simulator.h"
 
+#include <mpi.h>
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/examples/simple-distributed-empty-node.cc ns-3.38/src/mpi/examples/simple-distributed-empty-node.cc
--- ns-3.37/src/mpi/examples/simple-distributed-empty-node.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mpi/examples/simple-distributed-empty-node.cc	2023-03-16 20:54:23.796869000 -0700
@@ -64,7 +64,6 @@
  */
 
 #include "mpi-test-fixtures.h"
-#include "mpi.h"
 
 #include "ns3/core-module.h"
 #include "ns3/internet-stack-helper.h"
@@ -77,6 +76,8 @@
 #include "ns3/packet-sink-helper.h"
 #include "ns3/point-to-point-helper.h"
 
+#include <mpi.h>
+
 using namespace ns3;
 
 NS_LOG_COMPONENT_DEFINE("SimpleDistributedEmptyNode");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/examples/simple-distributed-mpi-comm.cc ns-3.38/src/mpi/examples/simple-distributed-mpi-comm.cc
--- ns-3.37/src/mpi/examples/simple-distributed-mpi-comm.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mpi/examples/simple-distributed-mpi-comm.cc	2023-03-16 20:54:23.796869000 -0700
@@ -55,7 +55,6 @@
  */
 
 #include "mpi-test-fixtures.h"
-#include "mpi.h"
 
 #include "ns3/core-module.h"
 #include "ns3/internet-stack-helper.h"
@@ -71,6 +70,8 @@
 #include "ns3/packet-sink.h"
 #include "ns3/point-to-point-helper.h"
 
+#include <mpi.h>
+
 using namespace ns3;
 
 NS_LOG_COMPONENT_DEFINE("SimpleDistributedMpiComm");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/model/distributed-simulator-impl.cc ns-3.38/src/mpi/model/distributed-simulator-impl.cc
--- ns-3.37/src/mpi/model/distributed-simulator-impl.cc	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mpi/model/distributed-simulator-impl.cc	2023-03-16 20:54:23.796869000 -0700
@@ -58,25 +58,25 @@
 }
 
 uint32_t
-LbtsMessage::GetTxCount()
+LbtsMessage::GetTxCount() const
 {
     return m_txCount;
 }
 
 uint32_t
-LbtsMessage::GetRxCount()
+LbtsMessage::GetRxCount() const
 {
     return m_rxCount;
 }
 
 uint32_t
-LbtsMessage::GetMyId()
+LbtsMessage::GetMyId() const
 {
     return m_myId;
 }
 
 bool
-LbtsMessage::IsFinished()
+LbtsMessage::IsFinished() const
 {
     return m_isFinished;
 }
@@ -164,7 +164,7 @@
 {
     NS_LOG_FUNCTION(this);
 
-    /* If runnning sequential simulation can ignore lookahead */
+    /* If running sequential simulation can ignore lookahead */
     if (MpiInterface::GetSize() <= 1)
     {
         m_lookAhead = Seconds(0);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/model/distributed-simulator-impl.h ns-3.38/src/mpi/model/distributed-simulator-impl.h
--- ns-3.37/src/mpi/model/distributed-simulator-impl.h	2022-11-01 17:37:24.679395700 -0700
+++ ns-3.38/src/mpi/model/distributed-simulator-impl.h	2023-03-16 20:54:23.796869000 -0700
@@ -76,19 +76,19 @@
     /**
      * \return transmitted count
      */
-    uint32_t GetTxCount();
+    uint32_t GetTxCount() const;
     /**
      * \return received count
      */
-    uint32_t GetRxCount();
+    uint32_t GetRxCount() const;
     /**
      * \return id which corresponds to mpi rank
      */
-    uint32_t GetMyId();
+    uint32_t GetMyId() const;
     /**
      * \return true if system is finished
      */
-    bool IsFinished();
+    bool IsFinished() const;
 
   private:
     uint32_t m_txCount;  /**< Count of transmitted messages. */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/model/granted-time-window-mpi-interface.h ns-3.38/src/mpi/model/granted-time-window-mpi-interface.h
--- ns-3.37/src/mpi/model/granted-time-window-mpi-interface.h	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/mpi/model/granted-time-window-mpi-interface.h	2023-03-16 20:54:23.796869000 -0700
@@ -27,13 +27,13 @@
 #ifndef NS3_GRANTED_TIME_WINDOW_MPI_INTERFACE_H
 #define NS3_GRANTED_TIME_WINDOW_MPI_INTERFACE_H
 
-#include "mpi.h"
 #include "parallel-communication-interface.h"
 
 #include "ns3/buffer.h"
 #include "ns3/nstime.h"
 
 #include <list>
+#include <mpi.h>
 #include <stdint.h>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/model/mpi-interface.h ns-3.38/src/mpi/model/mpi-interface.h
--- ns-3.37/src/mpi/model/mpi-interface.h	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/mpi/model/mpi-interface.h	2023-03-16 20:54:23.796869000 -0700
@@ -26,11 +26,11 @@
 #ifndef NS3_MPI_INTERFACE_H
 #define NS3_MPI_INTERFACE_H
 
-#include "mpi.h"
-
 #include <ns3/nstime.h>
 #include <ns3/packet.h>
 
+#include <mpi.h>
+
 namespace ns3
 {
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/model/mpi-receiver.cc ns-3.38/src/mpi/model/mpi-receiver.cc
--- ns-3.37/src/mpi/model/mpi-receiver.cc	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/mpi/model/mpi-receiver.cc	2023-03-16 20:54:23.796869000 -0700
@@ -18,7 +18,7 @@
 /**
  * \file
  * \ingroup mpi
- * ns3::MpiReciver implementation,
+ * ns3::MpiReceiver implementation,
  * provides an interface to aggregate to MPI-compatible NetDevices.
  */
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/model/null-message-mpi-interface.h ns-3.38/src/mpi/model/null-message-mpi-interface.h
--- ns-3.37/src/mpi/model/null-message-mpi-interface.h	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/mpi/model/null-message-mpi-interface.h	2023-03-16 20:54:23.796869000 -0700
@@ -26,13 +26,13 @@
 #ifndef NS3_NULLMESSAGE_MPI_INTERFACE_H
 #define NS3_NULLMESSAGE_MPI_INTERFACE_H
 
-#include "mpi.h"
 #include "parallel-communication-interface.h"
 
 #include <ns3/buffer.h>
 #include <ns3/nstime.h>
 
 #include <list>
+#include <mpi.h>
 
 namespace ns3
 {
@@ -72,7 +72,7 @@
     MPI_Comm GetCommunicator() override;
 
   private:
-    /**
+    /*
      * The null message implementation is a collaboration of several
      * classes.  Methods that should be invoked only by the
      * collaborators are private to restrict use.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/model/parallel-communication-interface.h ns-3.38/src/mpi/model/parallel-communication-interface.h
--- ns-3.37/src/mpi/model/parallel-communication-interface.h	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/mpi/model/parallel-communication-interface.h	2023-03-16 20:54:23.796869000 -0700
@@ -27,14 +27,13 @@
 #ifndef NS3_PARALLEL_COMMUNICATION_INTERFACE_H
 #define NS3_PARALLEL_COMMUNICATION_INTERFACE_H
 
-#include "mpi.h"
-
 #include <ns3/buffer.h>
 #include <ns3/nstime.h>
 #include <ns3/object.h>
 #include <ns3/packet.h>
 
 #include <list>
+#include <mpi.h>
 #include <stdint.h>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/mpi/test/mpi-test-suite.cc ns-3.38/src/mpi/test/mpi-test-suite.cc
--- ns-3.37/src/mpi/test/mpi-test-suite.cc	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/mpi/test/mpi-test-suite.cc	2023-03-16 20:54:23.796869000 -0700
@@ -43,7 +43,8 @@
                 const std::string program,
                 const std::string dataDir,
                 const int ranks,
-                const std::string args = "");
+                const std::string args = "",
+                const bool shouldNotErr = true);
 
     /** Destructor */
     ~MpiTestCase() override
@@ -75,8 +76,9 @@
                          const std::string program,
                          const std::string dataDir,
                          const int ranks,
-                         const std::string args /* = "" */)
-    : ExampleAsTestCase(name, program, dataDir, args),
+                         const std::string args /* = "" */,
+                         const bool shouldNotErr /* = true */)
+    : ExampleAsTestCase(name, program, dataDir, args, shouldNotErr),
       m_ranks(ranks)
 {
 }
@@ -114,10 +116,11 @@
                  const std::string dataDir,
                  const int ranks,
                  const std::string args = "",
-                 const TestDuration duration = QUICK)
+                 const TestDuration duration = QUICK,
+                 const bool shouldNotErr = true)
         : TestSuite(name, EXAMPLE)
     {
-        AddTestCase(new MpiTestCase(name, program, dataDir, ranks, args), duration);
+        AddTestCase(new MpiTestCase(name, program, dataDir, ranks, args, shouldNotErr), duration);
     }
 
 }; // class MpiTestSuite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/doc/animation.rst ns-3.38/src/netanim/doc/animation.rst
--- ns-3.37/src/netanim/doc/animation.rst	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/netanim/doc/animation.rst	2023-03-16 20:54:23.796869000 -0700
@@ -159,7 +159,7 @@
 
 .. sourcecode:: cpp
 
-  AnimationInterface anim ("animation.xml");  // where "animation.xml" is any arbitrary filename
+  AnimationInterface anim("animation.xml");  // where "animation.xml" is any arbitrary filename
 
 [for versions before ns-3.13 you also have to use the line "anim.SetXMLOutput() to set the XML mode and also use anim.StartAnimation();]
 
@@ -172,7 +172,7 @@
 The following are optional but useful steps::
 
   // Step 1
-  anim.SetMobilityPollInterval (Seconds (1));
+  anim.SetMobilityPollInterval(Seconds(1));
 
 AnimationInterface records the position of all nodes every 250 ms by default. The statement above sets
 the periodic interval at which AnimationInterface records the position of all nodes. If the nodes are
@@ -181,21 +181,21 @@
 ::
 
   // Step 2
-  anim.SetConstantPosition (Ptr< Node > n, double x, double y);
+  anim.SetConstantPosition(Ptr< Node > n, double x, double y);
 
 AnimationInterface requires that the position of all nodes be set. In |ns3| this is done by setting an associated MobilityModel. "SetConstantPosition" is a quick way to set the x-y coordinates of a node which is stationary.
 
 ::
 
   // Step 3
-  anim.SetStartTime (Seconds(150)); and anim.SetStopTime (Seconds(150));
+  anim.SetStartTime(Seconds(150)); and anim.SetStopTime(Seconds(150));
 
 AnimationInterface can generate large XML files. The above statements restricts the window between which AnimationInterface does tracing. Restricting the window serves to focus only on relevant portions of the simulation and creating manageably small XML files
 
 ::
 
   // Step 4
-  AnimationInterface anim ("animation.xml", 50000);
+  AnimationInterface anim("animation.xml", 50000);
 
 Using the above constructor ensures that each animation XML trace file has only 50000 packets. For example, if AnimationInterface captures 150000 packets, using the above constructor splits the capture into 3 files
 
@@ -208,7 +208,7 @@
 ::
 
   // Step 5
-  anim.EnablePacketMetadata (true);
+  anim.EnablePacketMetadata(true);
 
 With the above statement, AnimationInterface records the meta-data of each packet in the xml trace file. Metadata can be used by NetAnim to provide better statistics and filter, along with providing some brief information about the packet such as TCP sequence number or source & destination IP address during packet animation.
 
@@ -218,21 +218,21 @@
 ::
 
   // Step 6
-  anim.UpdateNodeDescription (5, "Access-point");
+  anim.UpdateNodeDescription(5, "Access-point");
 
 With the above statement, AnimationInterface assigns the text "Access-point" to node 5.
 
 ::
 
   // Step 7
-  anim.UpdateNodeSize (6, 1.5, 1.5);
+  anim.UpdateNodeSize(6, 1.5, 1.5);
 
 With the above statement, AnimationInterface sets the node size to scale by 1.5. NetAnim automatically scales the graphics view to fit the oboundaries of the topology. This means that NetAnim, can abnormally scale a node's size too high or too low. Using AnimationInterface::UpdateNodeSize allows you to overwrite the default scaling in NetAnim and use your own custom scale.
 
 ::
 
   // Step 8
-  anim.UpdateNodeCounter (89, 7, 3.4);
+  anim.UpdateNodeCounter(89, 7, 3.4);
 
 With the above statement, AnimationInterface sets the counter with Id == 89, associated with Node 7 with the value 3.4.
 The counter with Id 89 is obtained using AnimationInterface::AddNodeCounter. An example usage for this is in src/netanim/examples/resource-counters.cc.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/examples/colors-link-description.cc ns-3.38/src/netanim/examples/colors-link-description.cc
--- ns-3.37/src/netanim/examples/colors-link-description.cc	2022-11-01 17:37:24.687395600 -0700
+++ ns-3.38/src/netanim/examples/colors-link-description.cc	2023-03-16 20:54:23.800869200 -0700
@@ -30,17 +30,17 @@
 AnimationInterface* pAnim = nullptr;
 
 /// RGB structure
-struct rgb
+struct Rgb
 {
     uint8_t r; ///< red
     uint8_t g; ///< green
     uint8_t b; ///< blue
 };
 
-struct rgb colors[] = {
+Rgb colors[] = {
     {255, 0, 0}, // Red
     {0, 255, 0}, // Blue
-    {0, 0, 255}  // Green
+    {0, 0, 255}, // Green
 };
 
 void
@@ -72,7 +72,7 @@
     {
         index = 0;
     }
-    struct rgb color = colors[index];
+    Rgb color = colors[index];
     for (uint32_t nodeId = 4; nodeId < 12; ++nodeId)
     {
         pAnim->UpdateNodeColor(nodeId, color.r, color.g, color.b);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/examples/resources-counters.cc ns-3.38/src/netanim/examples/resources-counters.cc
--- ns-3.37/src/netanim/examples/resources-counters.cc	2022-11-01 17:37:24.687395600 -0700
+++ ns-3.38/src/netanim/examples/resources-counters.cc	2023-03-16 20:54:23.800869200 -0700
@@ -29,18 +29,18 @@
 
 AnimationInterface* pAnim = nullptr;
 
-/// RGB struture
-struct rgb
+/// RGB structure
+struct Rgb
 {
     uint8_t r; ///< red
     uint8_t g; ///< green
     uint8_t b; ///< blue
 };
 
-struct rgb colors[] = {
+Rgb colors[] = {
     {255, 0, 0}, // Red
     {0, 255, 0}, // Blue
-    {0, 0, 255}  // Green
+    {0, 0, 255}, // Green
 };
 
 uint32_t resourceId1;
@@ -96,7 +96,7 @@
     {
         index = 0;
     }
-    struct rgb color = colors[index];
+    Rgb color = colors[index];
     for (uint32_t nodeId = 4; nodeId < 12; ++nodeId)
     {
         pAnim->UpdateNodeColor(nodeId, color.r, color.g, color.b);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/examples/uan-animation.cc ns-3.38/src/netanim/examples/uan-animation.cc
--- ns-3.37/src/netanim/examples/uan-animation.cc	2022-11-01 17:37:24.687395600 -0700
+++ ns-3.38/src/netanim/examples/uan-animation.cc	2023-03-16 20:54:23.804869200 -0700
@@ -95,7 +95,7 @@
 }
 
 void
-NetAnimExperiment::UpdatePositions(NodeContainer& nodes)
+NetAnimExperiment::UpdatePositions(NodeContainer& nodes) const
 {
     NS_LOG_DEBUG(Simulator::Now().As(Time::S) << " Updating positions");
     NodeContainer::Iterator it = nodes.Begin();
@@ -304,4 +304,6 @@
 
     per = nullptr;
     sinr = nullptr;
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/examples/uan-animation.h ns-3.38/src/netanim/examples/uan-animation.h
--- ns-3.37/src/netanim/examples/uan-animation.h	2022-11-01 17:37:24.687395600 -0700
+++ ns-3.38/src/netanim/examples/uan-animation.h	2023-03-16 20:54:23.804869200 -0700
@@ -47,7 +47,7 @@
      * Update positions function
      * \param nodes the collection of nodes
      */
-    void UpdatePositions(NodeContainer& nodes);
+    void UpdatePositions(NodeContainer& nodes) const;
     /// Reset data function
     void ResetData();
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/model/animation-interface.cc ns-3.38/src/netanim/model/animation-interface.cc
--- ns-3.37/src/netanim/model/animation-interface.cc	2022-11-01 17:37:24.687395600 -0700
+++ ns-3.38/src/netanim/model/animation-interface.cc	2023-03-16 20:54:23.804869200 -0700
@@ -301,7 +301,7 @@
 }
 
 bool
-AnimationInterface::IsStarted()
+AnimationInterface::IsStarted() const
 {
     return m_started;
 }
@@ -377,6 +377,12 @@
 }
 
 void
+AnimationInterface::UpdateNodeSize(Ptr<Node> n, double width, double height)
+{
+    UpdateNodeSize(n->GetId(), width, height);
+}
+
+void
 AnimationInterface::UpdateNodeSize(uint32_t nodeId, double width, double height)
 {
     AnimationInterface::NodeSize s = {width, height};
@@ -605,13 +611,13 @@
     std::vector<std::string> elements;
     std::size_t pos1 = 0;
     std::size_t pos2;
-    while (pos1 != context.npos)
+    while (pos1 != std::string::npos)
     {
         pos1 = context.find('/', pos1);
         pos2 = context.find('/', pos1 + 1);
         elements.push_back(context.substr(pos1 + 1, pos2 - (pos1 + 1)));
         pos1 = pos2;
-        pos2 = context.npos;
+        pos2 = std::string::npos;
     }
     return elements;
 }
@@ -1498,7 +1504,7 @@
 }
 
 uint64_t
-AnimationInterface::GetTracePktCount()
+AnimationInterface::GetTracePktCount() const
 {
     return m_currentPktCount;
 }
@@ -2193,7 +2199,7 @@
 void
 AnimationInterface::CheckMaxPktsPerTraceFile()
 {
-    // Start a new trace file if the current packet count exceeded nax packets per file
+    // Start a new trace file if the current packet count exceeded max packets per file
     ++m_currentPktCount;
     if (m_currentPktCount <= m_maxPktsPerFile)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/model/animation-interface.h ns-3.38/src/netanim/model/animation-interface.h
--- ns-3.37/src/netanim/model/animation-interface.h	2022-11-01 17:37:24.687395600 -0700
+++ ns-3.38/src/netanim/model/animation-interface.h	2023-03-16 20:54:23.804869200 -0700
@@ -97,11 +97,11 @@
     /**
      * Counter Types
      */
-    typedef enum
+    enum CounterType
     {
         UINT32_COUNTER,
         DOUBLE_COUNTER
-    } CounterType;
+    };
 
     /**
      * \brief typedef for WriteCallBack used for listening to AnimationInterface
@@ -253,9 +253,9 @@
     /**
      * \brief Helper function to set Constant Position for a given node
      * \param n Ptr to the node
-     * \param x X co-ordinate of the node
-     * \param y Y co-ordinate of the node
-     * \param z Z co-ordinate of the node
+     * \param x X coordinate of the node
+     * \param y Y coordinate of the node
+     * \param z Z coordinate of the node
      *
      */
     static void SetConstantPosition(Ptr<Node> n, double x, double y, double z = 0);
@@ -286,6 +286,15 @@
 
     /**
      * \brief Helper function to update the size of a node
+     * \param n Ptr to the node
+     * \param width Width of the node
+     * \param height Height of the node
+     *
+     */
+    void UpdateNodeSize(Ptr<Node> n, double width, double height);
+
+    /**
+     * \brief Helper function to update the size of a node
      * \param nodeId Id of the node
      * \param width Width of the node
      * \param height Height of the node
@@ -325,8 +334,8 @@
     /**
      * \brief Helper function to set the background image
      * \param fileName File name of the background image
-     * \param x X co-ordinate of the image
-     * \param y Y co-ordinate of the image
+     * \param x X coordinate of the image
+     * \param y Y coordinate of the image
      * \param scaleX X scale of the image
      * \param scaleY Y scale of the image
      * \param opacity Opacity of the background: A value between 0.0 and 1.0. 0.0 is transparent,
@@ -373,7 +382,7 @@
      *
      * \returns true if AnimationInterface was started
      */
-    bool IsStarted();
+    bool IsStarted() const;
 
     /**
      * \brief Do not trace packets. This helps reduce the trace file size if AnimationInterface is
@@ -396,7 +405,7 @@
      *
      * \returns Number of packets recorded in the current trace file
      */
-    uint64_t GetTracePktCount();
+    uint64_t GetTracePktCount() const;
 
     /**
      *
@@ -465,27 +474,27 @@
     };
 
     /// RGB structure
-    typedef struct
+    struct Rgb
     {
         uint8_t r; ///< r
         uint8_t g; ///< g
         uint8_t b; ///< b
-    } Rgb;         ///< RGB structure
+    };             ///< RGB structure
 
     /// P2pLinkNodeIdPair structure
-    typedef struct
+    struct P2pLinkNodeIdPair
     {
         uint32_t fromNode; ///< from node
         uint32_t toNode;   ///< to node
-    } P2pLinkNodeIdPair;   ///< P2P link node id pair
+    };                     ///< P2P link node id pair
 
     /// LinkProperties structure
-    typedef struct
+    struct LinkProperties
     {
         std::string fromNodeDescription; ///< from node description
         std::string toNodeDescription;   ///< to node description
         std::string linkDescription;     ///< link description
-    } LinkProperties;                    ///< link properties
+    };                                   ///< link properties
 
     /// LinkPairCompare structure
     struct LinkPairCompare
@@ -514,21 +523,21 @@
     };
 
     /// Ipv4RouteTrackElement structure
-    typedef struct
+    struct Ipv4RouteTrackElement
     {
         std::string destination; ///< destination
         uint32_t fromNodeId;     ///< from node ID
-    } Ipv4RouteTrackElement;     ///< IPv4 route track element
+    };                           ///< IPv4 route track element
 
     /// Ipv4RoutePathElement structure
-    typedef struct
+    struct Ipv4RoutePathElement
     {
         uint32_t nodeId;     ///< node ID
         std::string nextHop; ///< next hop
-    } Ipv4RoutePathElement;  ///< IPv4 route path element
+    };                       ///< IPv4 route path element
 
     /// ProtocolType enumeration
-    typedef enum
+    enum ProtocolType
     {
         UAN,
         LTE,
@@ -537,14 +546,14 @@
         CSMA,
         LRWPAN,
         WAVE
-    } ProtocolType;
+    };
 
     /// NodeSize structure
-    typedef struct
+    struct NodeSize
     {
         double width;  ///< width
         double height; ///< height
-    } NodeSize;        ///< node size
+    };                 ///< node size
 
     typedef std::map<P2pLinkNodeIdPair, LinkProperties, LinkPairCompare>
         LinkPropertiesMap;                                       ///< LinkPropertiesMap typedef
@@ -618,7 +627,7 @@
     bool m_enablePacketMetadata;           ///< enable packet metadata
     Time m_startTime;                      ///< start time
     Time m_stopTime;                       ///< stop time
-    uint64_t m_maxPktsPerFile;             ///< maximum pakets per file
+    uint64_t m_maxPktsPerFile;             ///< maximum packets per file
     std::string m_originalFileName;        ///< original file name
     Time m_routingStopTime;                ///< routing stop time
     std::string m_routingFileName;         ///< routing file name
@@ -1039,7 +1048,7 @@
      * \param tx the transmit device
      * \param rx the receive device
      * \param txTime the transmit time
-     * \param rxTime the reeive time
+     * \param rxTime the receive time
      */
     void DevTxTrace(std::string context,
                     Ptr<const Packet> p,
@@ -1492,7 +1501,6 @@
  * When Anim receives a Tx Notification we tag the packet with a unique global uint64_t identifier
  * before recording Tx information
  * When Anim receives Rx notifications the tag is used to retrieve Tx information recorded earlier
- *
  */
 
 class AnimByteTag : public Tag
@@ -1501,56 +1509,48 @@
     /**
      * \brief Get Type Id
      * \returns Type Id
-     *
      */
     static TypeId GetTypeId();
 
     /**
      * \brief Get Instance Type Id
      * \returns Type Id
-     *
      */
     TypeId GetInstanceTypeId() const override;
 
     /**
      * \brief Get Serialized Size
      * \returns Serialized Size (i.e size of uint64_t)
-     *
      */
     uint32_t GetSerializedSize() const override;
 
     /**
      * \brief Serialize function
      * \param i Tag Buffer
-     *
      */
     void Serialize(TagBuffer i) const override;
 
     /**
      * \brief Deserialize function
      * \param i Tag Buffer
-     *
      */
     void Deserialize(TagBuffer i) override;
 
     /**
      * \brief Print tag info
      * \param os Reference of ostream object
-     *
      */
     void Print(std::ostream& os) const override;
 
     /**
      * \brief Set global Uid in tag
      * \param AnimUid global Uid
-     *
      */
     void Set(uint64_t AnimUid);
 
     /**
      * \brief Get Uid in tag
      * \returns Uid in tag
-     *
      */
     uint64_t Get() const;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/_required_netanim_version.py ns-3.38/src/netanim/_required_netanim_version.py
--- ns-3.37/src/netanim/_required_netanim_version.py	2022-11-01 17:37:24.683395600 -0700
+++ ns-3.38/src/netanim/_required_netanim_version.py	2023-03-16 20:54:23.796869000 -0700
@@ -6,4 +6,4 @@
 # on 'git describe --tags' command.  Example, if the latest release was 3.108,
 # and 'git describe --tags' reports "netanim-3.108-6-g8e7c0a9", then write the
 # version string below as 'netanim-3.108.post6+ng8e7c0a9'
-__required_netanim_version__ = 'netanim-3.108'
+__required_netanim_version__ = 'netanim-3.109'
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/netanim/test/netanim-test.cc ns-3.38/src/netanim/test/netanim-test.cc
--- ns-3.37/src/netanim/test/netanim-test.cc	2022-11-01 17:37:24.687395600 -0700
+++ ns-3.38/src/netanim/test/netanim-test.cc	2023-03-16 20:54:23.804869200 -0700
@@ -35,12 +35,12 @@
 
 /**
  * \ingroup netanim
+ * \ingroup tests
  * \defgroup netanim-test animation module tests
  */
 
 /**
  * \ingroup netanim-test
- * \ingroup tests
  *
  * \brief Abstract Animation Interface Test Case
  */
@@ -112,7 +112,6 @@
 
 /**
  * \ingroup netanim-test
- * \ingroup tests
  *
  * \brief Animation Interface Test Case
  */
@@ -181,7 +180,6 @@
 
 /**
  * \ingroup netanim-test
- * \ingroup tests
  *
  * \brief Animation Remaining Energy Test Case
  */
@@ -243,7 +241,6 @@
 
 /**
  * \ingroup netanim-test
- * \ingroup tests
  *
  * \brief Animation Interface Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/CMakeLists.txt ns-3.38/src/network/CMakeLists.txt
--- ns-3.37/src/network/CMakeLists.txt	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/CMakeLists.txt	2023-03-16 20:54:23.804869200 -0700
@@ -68,6 +68,7 @@
     utils/simple-channel.cc
     utils/simple-net-device.cc
     utils/sll-header.cc
+    utils/timestamp-tag.cc
 )
 
 set(header_files
@@ -146,6 +147,7 @@
     utils/simple-channel.h
     utils/simple-net-device.h
     utils/sll-header.h
+    utils/timestamp-tag.h
 )
 
 build_lib(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/doc/error-model.rst ns-3.38/src/network/doc/error-model.rst
--- ns-3.37/src/network/doc/error-model.rst	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/doc/error-model.rst	2023-03-16 20:54:23.804869200 -0700
@@ -75,11 +75,11 @@
 * ``bool ErrorModel::IsCorrupt (Ptr<Packet> pkt)``:  Evaluate the packet and
   return true or false whether the packet should be considered errored or not.
   Some models could potentially alter the contents of the packet bit buffer.
-* ``void ErrorModel::Reset (void)``:  Reset any state.
-* ``void ErrorModel::Enable (void)``:  Enable the model
-* ``void ErrorModel::Disble (void)``:  Disable the model; IsCorrupt() will
+* ``void ErrorModel::Reset ()``:  Reset any state.
+* ``void ErrorModel::Enable ()``:  Enable the model
+* ``void ErrorModel::Disable ()``:  Disable the model; IsCorrupt() will
   always return false.
-* ``bool ErrorModel::IsEnabled (void) const``:  Return the enabled state
+* ``bool ErrorModel::IsEnabled () const``:  Return the enabled state
 
 
 Many |ns3| NetDevices contain attributes holding pointers to error
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/doc/packets.rst ns-3.38/src/network/doc/packets.rst
--- ns-3.37/src/network/doc/packets.rst	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/doc/packets.rst	2023-03-16 20:54:23.804869200 -0700
@@ -159,12 +159,12 @@
 
 The following command will create a new packet with a new unique Id.::
 
-  Ptr<Packet> pkt = Create<Packet> ();
+  Ptr<Packet> pkt = Create<Packet>();
 
 What is the Uid (unique Id)?  It is an internal id that the system uses to
 identify packets.  It can be fetched via the following method::
 
-  uint32_t uid = pkt->GetUid ();
+  uint32_t uid = pkt->GetUid();
 
 But please note the following. This uid is an internal uid and cannot be counted
 on to provide an accurate counter of how many "simulated packets" of a
@@ -182,7 +182,7 @@
 on-demand when needed).  The command to do this is, when the packet is
 created::
 
-  Ptr<Packet> pkt = Create<Packet> (N);
+  Ptr<Packet> pkt = Create<Packet>(N);
 
 where N is a positive integer.
 
@@ -193,17 +193,17 @@
    * \returns the size in bytes of the packet (including the zero-filled
    *          initial payload)
    */
-  uint32_t GetSize (void) const;
+  uint32_t GetSize() const;
 
 You can also initialize a packet with a character buffer. The input
 data is copied and the input buffer is untouched. The constructor
 applied is::
 
-  Packet (uint8_t const *buffer, uint32_t size);
+  Packet(uint8_t const *buffer, uint32_t size);
 
 Here is an example::
 
-  Ptr<Packet> pkt1 = Create<Packet> (reinterpret_cast<const uint8_t*> ("hello"), 5);
+  Ptr<Packet> pkt1 = Create<Packet>(reinterpret_cast<const uint8_t*>("hello"), 5);
 
 Packets are freed when there are no more references to them, as with all |ns3|
 objects referenced by the Ptr class.
@@ -244,17 +244,17 @@
    *
    * \param header a reference to the header to add to this packet.
    */
-  void AddHeader (const Header & header);
+  void AddHeader(const Header & header);
   /**
    * Deserialize and remove the header from the internal buffer.
    *
-   * This method invokes Header::Deserialize (begin) and should be used for
+   * This method invokes Header::Deserialize(begin) and should be used for
    * fixed-length headers.
    *
    * \param header a reference to the header to remove from the internal buffer.
    * \returns the number of bytes removed from the packet.
    */
-  uint32_t RemoveHeader (Header &header);
+  uint32_t RemoveHeader(Header &header);
   /**
    * Deserialize but does _not_ remove the header from the internal buffer.
    * This method invokes Header::Deserialize.
@@ -262,20 +262,20 @@
    * \param header a reference to the header to read from the internal buffer.
    * \returns the number of bytes read from the packet.
    */
-  uint32_t PeekHeader (Header &header) const;
+  uint32_t PeekHeader(Header &header) const;
 
 For instance, here are the typical operations to add and remove a UDP header.::
 
- // add header
- Ptr<Packet> packet = Create<Packet> ();
- UdpHeader udpHeader;
- // Fill out udpHeader fields appropriately
- packet->AddHeader (udpHeader);
- ...
- // remove header
- UdpHeader udpHeader;
- packet->RemoveHeader (udpHeader);
- // Read udpHeader fields as needed
+  // add header
+  Ptr<Packet> packet = Create<Packet>();
+  UdpHeader udpHeader;
+  // Fill out udpHeader fields appropriately
+  packet->AddHeader(udpHeader);
+  ...
+  // remove header
+  UdpHeader udpHeader;
+  packet->RemoveHeader(udpHeader);
+  // Read udpHeader fields as needed
 
 If the header is variable-length, then another variant of RemoveHeader() is
 needed::
@@ -283,7 +283,7 @@
   /**
    * \brief Deserialize and remove the header from the internal buffer.
    *
-   * This method invokes Header::Deserialize (begin, end) and should be
+   * This method invokes Header::Deserialize(begin, end) and should be
    * used for variable-length headers (where the size is determined somehow
    * by the caller).
    *
@@ -291,7 +291,7 @@
    * \param size number of bytes to deserialize
    * \returns the number of bytes removed from the packet.
    */
-  uint32_t RemoveHeader (Header &header, uint32_t size);
+  uint32_t RemoveHeader(Header &header, uint32_t size);
 
 In this case, the caller must figure out and provide the right 'size' as
 an argument (the Deserialization routine may not know when to stop).  An
@@ -331,7 +331,7 @@
 * **Finding and Printing:** Both classes allow you to iterate over all of the
   tags and print them.
 * **Removal:** Users can add and remove the same packet tag multiple times on a
-  single packet (AddPacketTag () and RemovePacketTag ()). The packet However,
+  single packet (AddPacketTag() and RemovePacketTag()). The packet However,
   once a byte tag is added, it can only be removed by stripping all byte tags
   from the packet. Removing one of possibly multiple byte tags is not supported
   by the current API.
@@ -360,11 +360,11 @@
    * totally evil to allow a trace sink to modify the content of a
    * packet).
    */
-  void AddByteTag (const Tag &tag) const;
+  void AddByteTag(const Tag &tag) const;
   /**
    * \returns an iterator over the set of byte tags included in this packet.
    */
-  ByteTagIterator GetByteTagIterator (void) const;
+  ByteTagIterator GetByteTagIterator() const;
   /**
    * \param tag the tag to search in this packet
    * \returns true if the requested tag type was found, false otherwise.
@@ -372,12 +372,12 @@
    * If the requested tag type is found, it is copied in the user's
    * provided tag instance.
    */
-  bool FindFirstMatchingByteTag (Tag &tag) const;
+  bool FindFirstMatchingByteTag(Tag &tag) const;
 
   /**
    * Remove all the tags stored in this packet.
    */
-  void RemoveAllByteTags (void);
+  void RemoveAllByteTags();
 
   /**
    * \param os output stream in which the data should be printed.
@@ -385,7 +385,7 @@
    * Iterate over the tags present in this packet, and
    * invoke the Print method of each tag stored in the packet.
    */
-  void PrintByteTags (std::ostream &os) const;
+  void PrintByteTags(std::ostream &os) const;
 
 The Packet API for packet tags is given below.::
 
@@ -399,7 +399,7 @@
    * modify the state of this packet, which is fairly
    * un-intuitive.
    */
-  void AddPacketTag (const Tag &tag) const;
+  void AddPacketTag(const Tag &tag) const;
   /**
    * \param tag the tag to remove from this packet
    * \returns true if the requested tag is found, false
@@ -408,7 +408,7 @@
    * Remove a tag from this packet. This method calls
    * Tag::Deserialize if the tag is found.
    */
-  bool RemovePacketTag (Tag &tag);
+  bool RemovePacketTag(Tag &tag);
   /**
    * \param tag the tag to search in this packet
    * \returns true if the requested tag is found, false
@@ -416,11 +416,11 @@
    *
    * Search a matching tag and call Tag::Deserialize if it is found.
    */
-  bool PeekPacketTag (Tag &tag) const;
+  bool PeekPacketTag(Tag &tag) const;
   /**
    * Remove all packet tags.
    */
-  void RemoveAllPacketTags (void);
+  void RemoveAllPacketTags();
 
   /**
    * \param os the stream in which we want to print data.
@@ -430,30 +430,30 @@
    * \sa Packet::AddPacketTag, Packet::RemovePacketTag, Packet::PeekPacketTag,
    *  Packet::RemoveAllPacketTags
    */
-  void PrintPacketTags (std::ostream &os) const;
+  void PrintPacketTags(std::ostream &os) const;
 
   /**
    * \returns an object which can be used to iterate over the list of
    *  packet tags.
    */
-  PacketTagIterator GetPacketTagIterator (void) const;
+  PacketTagIterator GetPacketTagIterator() const;
 
 Here is a simple example illustrating the use of tags from the
 code in ``src/internet/model/udp-socket-impl.cc``::
 
   Ptr<Packet> p;  // pointer to a pre-existing packet
   SocketIpTtlTag tag
-  tag.SetTtl (m_ipMulticastTtl); // Convey the TTL from UDP layer to IP layer
-  p->AddPacketTag (tag);
+  tag.SetTtl(m_ipMulticastTtl); // Convey the TTL from UDP layer to IP layer
+  p->AddPacketTag(tag);
 
 This tag is read at the IP layer, then stripped (``src/internet/model/ipv4-l3-protocol.cc``)::
 
   uint8_t ttl = m_defaultTtl;
   SocketIpTtlTag tag;
-  bool found = packet->RemovePacketTag (tag);
+  bool found = packet->RemovePacketTag(tag);
   if (found)
     {
-      ttl = tag.GetTtl ();
+      ttl = tag.GetTtl();
     }
 
 Fragmentation and concatenation
@@ -463,15 +463,15 @@
 ``p`` of 90 bytes into two packets, one containing the first 10 bytes and the
 other containing the remaining 80, one may call the following code::
 
-  Ptr<Packet> frag0 = p->CreateFragment (0, 10);
-  Ptr<Packet> frag1 = p->CreateFragment (10, 90);
+  Ptr<Packet> frag0 = p->CreateFragment(0, 10);
+  Ptr<Packet> frag1 = p->CreateFragment(10, 90);
 
 As discussed above, the packet tags from ``p`` will follow to both packet
 fragments, and the byte tags will follow the byte ranges as needed.
 
 Now, to put them back together::
 
-  frag0->AddAtEnd (frag1);
+  frag0->AddAtEnd(frag1);
 
 Now frag0 should be equivalent to the original packet ``p``.  If, however, there
 were operations on the fragments before being reassembled (such as tag
@@ -500,8 +500,8 @@
 To enable this operation, users will typically insert one or both of these
 statements at the beginning of their programs::
 
-  Packet::EnablePrinting ();
-  Packet::EnableChecking ();
+  Packet::EnablePrinting();
+  Packet::EnableChecking();
 
 Sample programs
 ***************
@@ -675,4 +675,3 @@
 several orders of magnitude. However, even the dirty operations have been
 optimized for common use-cases which means that most of the time, these
 operations will not trigger data copies and will thus be still very fast.
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/doc/queue-limits.rst ns-3.38/src/network/doc/queue-limits.rst
--- ns-3.37/src/network/doc/queue-limits.rst	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/doc/queue-limits.rst	2023-03-16 20:54:23.804869200 -0700
@@ -88,9 +88,9 @@
 .. sourcecode:: cpp
 
   TrafficControlHelper tch;
-  uint32_t handle = tch.SetRootQueueDisc ("ns3::PfifoFastQueueDisc", "Limit", UintegerValue (1000));
+  uint32_t handle = tch.SetRootQueueDisc("ns3::PfifoFastQueueDisc", "Limit", UintegerValue(1000));
 
-  tch.SetQueueLimits ("ns3::DynamicQueueLimits", "HoldTime", StringValue ("4ms"));
+  tch.SetQueueLimits("ns3::DynamicQueueLimits", "HoldTime", StringValue("4ms"));
 
 then install the configuration on a NetDevices container
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/doc/queue.rst ns-3.38/src/network/doc/queue.rst
--- ns-3.37/src/network/doc/queue.rst	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/doc/queue.rst	2023-03-16 20:54:23.804869200 -0700
@@ -51,9 +51,9 @@
 drop policies. Subclasses need to define the following public methods:
 
 * ``bool Enqueue (Ptr<Item> item)``:  Enqueue a packet
-* ``Ptr<Item> Dequeue (void)``:  Dequeue a packet
-* ``Ptr<Item> Remove (void)``:  Remove a packet
-* ``Ptr<const Item> Peek (void)``:  Peek a packet
+* ``Ptr<Item> Dequeue ()``:  Dequeue a packet
+* ``Ptr<Item> Remove ()``:  Remove a packet
+* ``Ptr<const Item> Peek ()``:  Peek a packet
 
 The Enqueue method does not allow to store a packet if the queue capacity is exceeded.
 Subclasses may also define specialized public methods. For instance, the
@@ -96,21 +96,21 @@
 
   PointToPointHelper p2p;
 
-  p2p.SetQueue ("ns3::DropTailQueue");
-  p2p.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
-  p2p.SetChannelAttribute ("Delay", StringValue ("2ms"));
-  NetDeviceContainer devn0n2 = p2p.Install (n0n2);
-
-  p2p.SetQueue ("ns3::DropTailQueue");
-  p2p.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
-  p2p.SetChannelAttribute ("Delay", StringValue ("3ms"));
-  NetDeviceContainer devn1n2 = p2p.Install (n1n2);
-
-  p2p.SetQueue ("ns3::DropTailQueue",
-                "MaxSize", StringValue ("50p"));
-  p2p.SetDeviceAttribute ("DataRate", StringValue (linkDataRate));
-  p2p.SetChannelAttribute ("Delay", StringValue (linkDelay));
-  NetDeviceContainer devn2n3 = p2p.Install (n2n3);
+  p2p.SetQueue("ns3::DropTailQueue");
+  p2p.SetDeviceAttribute("DataRate", StringValue("10Mbps"));
+  p2p.SetChannelAttribute("Delay", StringValue("2ms"));
+  NetDeviceContainer devn0n2 = p2p.Install(n0n2);
+
+  p2p.SetQueue("ns3::DropTailQueue");
+  p2p.SetDeviceAttribute("DataRate", StringValue("10Mbps"));
+  p2p.SetChannelAttribute("Delay", StringValue("3ms"));
+  NetDeviceContainer devn1n2 = p2p.Install(n1n2);
+
+  p2p.SetQueue("ns3::DropTailQueue",
+               "MaxSize", StringValue("50p"));
+  p2p.SetDeviceAttribute("DataRate", StringValue(linkDataRate));
+  p2p.SetChannelAttribute("Delay", StringValue(linkDelay));
+  NetDeviceContainer devn2n3 = p2p.Install(n2n3);
 
 Please note that the SetQueue method of the PointToPointHelper class allows
 to specify "ns3::DropTailQueue" instead of "ns3::DropTailQueue<Packet>". The
@@ -144,4 +144,3 @@
 
 The drop-tail queue is used in several examples, such as
 ``examples/udp/udp-echo.cc``.
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/doc/sockets-api.rst ns-3.38/src/network/doc/sockets-api.rst
--- ns-3.37/src/network/doc/sockets-api.rst	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/doc/sockets-api.rst	2023-03-16 20:54:23.804869200 -0700
@@ -82,7 +82,7 @@
 be created on a given node, then a factory that can create such sockets
 must be aggregated to the Node::
 
-  static Ptr<Socket> CreateSocket (Ptr<Node> node, TypeId tid);
+  static Ptr<Socket> CreateSocket(Ptr<Node> node, TypeId tid);
 
 Examples of TypeIds to pass to this method are :cpp:class:`ns3::TcpSocketFactory`,
 :cpp:class:`ns3::PacketSocketFactory`, and :cpp:class:`ns3::UdpSocketFactory`.
@@ -93,7 +93,7 @@
   Ptr<Node> n0;
   // Do some stuff to build up the Node's internet stack
   Ptr<Socket> localSocket =
-     Socket::CreateSocket (n0, TcpSocketFactory::GetTypeId ());
+     Socket::CreateSocket(n0, TcpSocketFactory::GetTypeId());
 
 In some ns-3 code, sockets will not be explicitly created by user's
 main programs, if an ns-3 application does it.  For instance, for
@@ -130,14 +130,14 @@
 by calling :cpp:func:`ns3::Socket::GetTxAvailable`.  A typical sequence
 of events for sending data (ignoring connection setup) might be::
 
-    SetSendCallback (MakeCallback(&HandleSendCallback));
-    Send ();
-    Send ();
+    SetSendCallback(MakeCallback(&HandleSendCallback));
+    Send();
+    Send();
     ...
     // Send fails because buffer is full
     // Wait until HandleSendCallback is called
     // HandleSendCallback is called by socket, since space now available
-    Send (); // Start sending again
+    Send(); // Start sending again
 
 Similarly, on the receive side, the socket user does not block on
 a call to ``recv()``.  Instead, the application sets a callback
@@ -152,11 +152,11 @@
 
 There are two basic variants of ``Send()`` and ``Recv()`` supported::
 
-  virtual int Send (Ptr<Packet> p) = 0;
-  int Send (const uint8_t* buf, uint32_t size);
+  virtual int Send(Ptr<Packet> p) = 0;
+  int Send(const uint8_t* buf, uint32_t size);
 
-  Ptr<Packet> Recv (void);
-  int Recv (uint8_t* buf, uint32_t size);
+  Ptr<Packet> Recv();
+  int Recv(uint8_t* buf, uint32_t size);
 
 The non-Packet variants are provided for legacy API reasons.  When calling
 the raw buffer variant of :cpp:func:`ns3::Socket::Send`, the buffer is immediately
@@ -196,9 +196,9 @@
 
 There are two variants of methods used to send data to the socket::
 
-  virtual int Send (Ptr<Packet> p, uint32_t flags) = 0;
+  virtual int Send(Ptr<Packet> p, uint32_t flags) = 0;
 
-  virtual int SendTo (Ptr<Packet> p, uint32_t flags,
+  virtual int SendTo(Ptr<Packet> p, uint32_t flags,
                       const Address &toAddress) = 0;
 
 The first method is used if the socket has already been connected
@@ -222,8 +222,8 @@
 The native sockets API for ns-3 provides two public methods
 (of the Socket base class)::
 
-    void SetIpTos (uint8_t ipTos);
-    uint8_t GetIpTos (void) const;
+    void SetIpTos(uint8_t ipTos);
+    uint8_t GetIpTos() const;
 
 to set and get, respectively, the type of service associated with the socket.
 These methods are equivalent to using the IP_TOS option of BSD sockets.
@@ -234,9 +234,9 @@
 Instead, users can create an address of type :cpp:class:`ns3::InetSocketAddress`
 with the desired type of service value and pass it to the application helpers::
 
-    InetSocketAddress destAddress (ipv4Address, udpPort);
-    destAddress.SetTos (tos);
-    OnOffHelper onoff ("ns3::UdpSocketFactory", destAddress);
+    InetSocketAddress destAddress(ipv4Address, udpPort);
+    destAddress.SetTos(tos);
+    OnOffHelper onoff("ns3::UdpSocketFactory", destAddress);
 
 For this to work, the application must eventually call the
 :cpp:func:`ns3::Socket::Connect()` method to connect to the provided
@@ -277,8 +277,8 @@
 The native sockets API for ns-3 provides two public methods
 (of the Socket base class)::
 
-    void SetPriority (uint8_t priority);
-    uint8_t GetPriority (void) const;
+    void SetPriority(uint8_t priority);
+    uint8_t GetPriority() const;
 
 to set and get, respectively, the priority associated with the socket.
 These methods are equivalent to using the SO_PRIORITY option of BSD sockets.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/examples/lollipop-comparisions.cc ns-3.38/src/network/examples/lollipop-comparisions.cc
--- ns-3.37/src/network/examples/lollipop-comparisions.cc	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/examples/lollipop-comparisions.cc	2023-03-16 20:54:23.804869200 -0700
@@ -56,4 +56,6 @@
         }
         std::cout << std::endl;
     }
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/helper/application-container.cc ns-3.38/src/network/helper/application-container.cc
--- ns-3.37/src/network/helper/application-container.cc	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/helper/application-container.cc	2023-03-16 20:54:23.804869200 -0700
@@ -89,7 +89,7 @@
 }
 
 void
-ApplicationContainer::Start(Time start)
+ApplicationContainer::Start(Time start) const
 {
     for (Iterator i = Begin(); i != End(); ++i)
     {
@@ -99,7 +99,7 @@
 }
 
 void
-ApplicationContainer::StartWithJitter(Time start, Ptr<RandomVariableStream> rv)
+ApplicationContainer::StartWithJitter(Time start, Ptr<RandomVariableStream> rv) const
 {
     for (Iterator i = Begin(); i != End(); ++i)
     {
@@ -111,7 +111,7 @@
 }
 
 void
-ApplicationContainer::Stop(Time stop)
+ApplicationContainer::Stop(Time stop) const
 {
     for (Iterator i = Begin(); i != End(); ++i)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/helper/application-container.h ns-3.38/src/network/helper/application-container.h
--- ns-3.37/src/network/helper/application-container.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/helper/application-container.h	2023-03-16 20:54:23.804869200 -0700
@@ -140,7 +140,7 @@
      * Applications can be retrieved from the container in two ways.  First,
      * directly by an index into the container, and second, using an iterator.
      * This method is used in the direct method and is used to retrieve the
-     * indexed Ptr<Appliation>.
+     * indexed Ptr<Application>.
      *
      * \code
      *   uint32_t nApplications = container.GetN ();
@@ -180,8 +180,8 @@
     void Add(std::string name);
 
     /**
-     * \brief Arrange for all of the Applications in this container to Start()
-     * at the Time given as a parameter.
+     * \brief Start all of the Applications in this container at the start time
+     * given as a parameter.
      *
      * All Applications need to be provided with a starting simulation time and
      * a stopping simulation time.  The ApplicationContainer is a convenient
@@ -189,24 +189,24 @@
      * up and start doing their thing (Start) at a common time.
      *
      * This method simply iterates through the contained Applications and calls
-     * their Start() methods with the provided Time.
+     * their Application::SetStartTime() methods with the provided Time.
      *
      * \param start The Time at which each of the applications should start.
      */
-    void Start(Time start);
+    void Start(Time start) const;
 
     /**
      * \brief Start all of the Applications in this container at the start time
      * given as a parameter, plus some jitter.
      *
      * This method iterates through the contained Applications and calls
-     * their Start() methods with the provided start Time, plus a jitter value
-     * drawn from the provided random variable.
+     * their Application::SetStartTime() methods with the provided start Time, plus
+     * a jitter value drawn from the provided random variable.
      *
      * \param start The Time at which each of the applications should start.
      * \param rv The random variable that adds jitter (units of seconds)
      */
-    void StartWithJitter(Time start, Ptr<RandomVariableStream> rv);
+    void StartWithJitter(Time start, Ptr<RandomVariableStream> rv) const;
 
     /**
      * \brief Arrange for all of the Applications in this container to Stop()
@@ -218,11 +218,11 @@
      * down and stop doing their thing (Stop) at a common time.
      *
      * This method simply iterates through the contained Applications and calls
-     * their Stop() methods with the provided Time.
+     * their Application::SetStopTime() methods with the provided Time.
      *
      * \param stop The Time at which each of the applications should stop.
      */
-    void Stop(Time stop);
+    void Stop(Time stop) const;
 
   private:
     std::vector<Ptr<Application>> m_applications; //!< Applications smart pointers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/helper/delay-jitter-estimation.cc ns-3.38/src/network/helper/delay-jitter-estimation.cc
--- ns-3.37/src/network/helper/delay-jitter-estimation.cc	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/helper/delay-jitter-estimation.cc	2023-03-16 20:54:23.804869200 -0700
@@ -21,120 +21,28 @@
 
 #include "ns3/simulator.h"
 #include "ns3/string.h"
-#include "ns3/tag.h"
+#include "ns3/timestamp-tag.h"
 
 namespace ns3
 {
 
-/**
- * Tag to perform Delay and Jitter estimations
- *
- * The tag holds the packet's creation timestamp
- */
-class DelayJitterEstimationTimestampTag : public Tag
-{
-  public:
-    DelayJitterEstimationTimestampTag();
-
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    TypeId GetInstanceTypeId() const override;
-
-    uint32_t GetSerializedSize() const override;
-    void Serialize(TagBuffer i) const override;
-    void Deserialize(TagBuffer i) override;
-    void Print(std::ostream& os) const override;
-
-    /**
-     * \brief Get the Transmission time stored in the tag
-     * \return the transmission time
-     */
-    Time GetTxTime() const;
-
-  private:
-    Time m_creationTime; //!< The time stored in the tag
-};
-
-DelayJitterEstimationTimestampTag::DelayJitterEstimationTimestampTag()
-    : m_creationTime(Simulator::Now())
-{
-}
-
-TypeId
-DelayJitterEstimationTimestampTag::GetTypeId()
-{
-    static TypeId tid =
-        TypeId("anon::DelayJitterEstimationTimestampTag")
-            .SetParent<Tag>()
-            .SetGroupName("Network")
-            .AddConstructor<DelayJitterEstimationTimestampTag>()
-            .AddAttribute("CreationTime",
-                          "The time at which the timestamp was created",
-                          TimeValue(Time(0)),
-                          MakeTimeAccessor(&DelayJitterEstimationTimestampTag::m_creationTime),
-                          MakeTimeChecker());
-    return tid;
-}
-
-TypeId
-DelayJitterEstimationTimestampTag::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-uint32_t
-DelayJitterEstimationTimestampTag::GetSerializedSize() const
-{
-    return 8;
-}
-
-void
-DelayJitterEstimationTimestampTag::Serialize(TagBuffer i) const
-{
-    i.WriteU64(m_creationTime.GetTimeStep());
-}
-
-void
-DelayJitterEstimationTimestampTag::Deserialize(TagBuffer i)
-{
-    m_creationTime = TimeStep(i.ReadU64());
-}
-
-void
-DelayJitterEstimationTimestampTag::Print(std::ostream& os) const
-{
-    os << "CreationTime=" << m_creationTime;
-}
-
-Time
-DelayJitterEstimationTimestampTag::GetTxTime() const
-{
-    return m_creationTime;
-}
-
 DelayJitterEstimation::DelayJitterEstimation()
-    : m_jitter(Time(0)),
-      m_transit(Time(0))
 {
 }
 
 void
 DelayJitterEstimation::PrepareTx(Ptr<const Packet> packet)
 {
-    DelayJitterEstimationTimestampTag tag;
+    TimestampTag tag(Simulator::Now());
     packet->AddByteTag(tag);
 }
 
 void
 DelayJitterEstimation::RecordRx(Ptr<const Packet> packet)
 {
-    DelayJitterEstimationTimestampTag tag;
-    bool found;
-    found = packet->FindFirstMatchingByteTag(tag);
-    if (!found)
+    TimestampTag tag;
+
+    if (!packet->FindFirstMatchingByteTag(tag))
     {
         return;
     }
@@ -142,15 +50,14 @@
     // Variable names from
     // RFC 1889 Appendix A.8 ,p. 71,
     // RFC 3550 Appendix A.8, p. 94
-
-    Time r_ts = tag.GetTxTime();
+    Time r_ts = tag.GetTimestamp();
     Time arrival = Simulator::Now();
     Time transit = arrival - r_ts;
     Time delta = transit - m_transit;
     m_transit = transit;
 
     // floating jitter version
-    //  m_jitter += (Abs (delta) - m_jitter) / 16;
+    // m_jitter += (Abs (delta) - m_jitter) / 16;
 
     // int variant
     m_jitter += Abs(delta) - ((m_jitter + TimeStep(8)) / 16);
@@ -166,7 +73,7 @@
 DelayJitterEstimation::GetLastJitter() const
 {
     // floating jitter version
-    // return m_jitter.GetTimeStep ();
+    // return m_jitter.GetTimeStep();
 
     // int variant
     return (m_jitter / 16).GetTimeStep();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/helper/delay-jitter-estimation.h ns-3.38/src/network/helper/delay-jitter-estimation.h
--- ns-3.37/src/network/helper/delay-jitter-estimation.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/helper/delay-jitter-estimation.h	2023-03-16 20:54:23.804869200 -0700
@@ -42,40 +42,44 @@
     DelayJitterEstimation();
 
     /**
-     * \param packet the packet to send over a wire
-     *
      * This method should be invoked once on each packet to
      * record within the packet the tx time which is used upon
      * packet reception to calculate the delay and jitter. The
      * tx time is stored in the packet as an ns3::Tag which means
      * that it does not use any network resources and is not
      * taken into account in transmission delay calculations.
+     *
+     * \param packet the packet to send over a wire
      */
     static void PrepareTx(Ptr<const Packet> packet);
+
     /**
-     * \param packet the packet received
-     *
      * Invoke this method to update the delay and jitter calculations
      * After a call to this method, \ref GetLastDelay and \ref GetLastJitter
      * will return an updated delay and jitter.
+     *
+     * \param packet the packet received
      */
     void RecordRx(Ptr<const Packet> packet);
 
     /**
-     * \returns the updated delay.
+     * Get the Last Delay object.
+     *
+     * \return the updated delay.
      */
     Time GetLastDelay() const;
+
     /**
      * The jitter is calculated using the \RFC{1889} (RTP) jitter
      * definition.
      *
-     * \returns the updated jitter.
+     * \return the updated jitter.
      */
     uint64_t GetLastJitter() const;
 
   private:
-    Time m_jitter;  //!< Jitter estimation
-    Time m_transit; //!< Relative transit time for the previous packet
+    Time m_jitter{0};  //!< Jitter estimation
+    Time m_transit{0}; //!< Relative transit time for the previous packet
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/helper/trace-helper.cc ns-3.38/src/network/helper/trace-helper.cc
--- ns-3.37/src/network/helper/trace-helper.cc	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/helper/trace-helper.cc	2023-03-16 20:54:23.804869200 -0700
@@ -79,7 +79,7 @@
 PcapHelper::GetFilenameFromDevice(std::string prefix, Ptr<NetDevice> device, bool useObjectNames)
 {
     NS_LOG_FUNCTION(prefix << device << useObjectNames);
-    NS_ABORT_MSG_UNLESS(prefix.size(), "Empty prefix string");
+    NS_ABORT_MSG_UNLESS(!prefix.empty(), "Empty prefix string");
 
     std::ostringstream oss;
     oss << prefix << "-";
@@ -95,7 +95,7 @@
         devicename = Names::FindName(device);
     }
 
-    if (nodename.size())
+    if (!nodename.empty())
     {
         oss << nodename;
     }
@@ -106,7 +106,7 @@
 
     oss << "-";
 
-    if (devicename.size())
+    if (!devicename.empty())
     {
         oss << devicename;
     }
@@ -127,7 +127,7 @@
                                          bool useObjectNames)
 {
     NS_LOG_FUNCTION(prefix << object << interface << useObjectNames);
-    NS_ABORT_MSG_UNLESS(prefix.size(), "Empty prefix string");
+    NS_ABORT_MSG_UNLESS(!prefix.empty(), "Empty prefix string");
 
     std::ostringstream oss;
     oss << prefix << "-";
@@ -143,11 +143,11 @@
         nodename = Names::FindName(node);
     }
 
-    if (objname.size())
+    if (!objname.empty())
     {
         oss << objname;
     }
-    else if (nodename.size())
+    else if (!nodename.empty())
     {
         oss << nodename;
     }
@@ -216,7 +216,7 @@
                                         bool useObjectNames)
 {
     NS_LOG_FUNCTION(prefix << device << useObjectNames);
-    NS_ABORT_MSG_UNLESS(prefix.size(), "Empty prefix string");
+    NS_ABORT_MSG_UNLESS(!prefix.empty(), "Empty prefix string");
 
     std::ostringstream oss;
     oss << prefix << "-";
@@ -232,7 +232,7 @@
         devicename = Names::FindName(device);
     }
 
-    if (nodename.size())
+    if (!nodename.empty())
     {
         oss << nodename;
     }
@@ -243,7 +243,7 @@
 
     oss << "-";
 
-    if (devicename.size())
+    if (!devicename.empty())
     {
         oss << devicename;
     }
@@ -264,7 +264,7 @@
                                                bool useObjectNames)
 {
     NS_LOG_FUNCTION(prefix << object << interface << useObjectNames);
-    NS_ABORT_MSG_UNLESS(prefix.size(), "Empty prefix string");
+    NS_ABORT_MSG_UNLESS(!prefix.empty(), "Empty prefix string");
 
     std::ostringstream oss;
     oss << prefix << "-";
@@ -280,11 +280,11 @@
         nodename = Names::FindName(node);
     }
 
-    if (objname.size())
+    if (!objname.empty())
     {
         oss << objname;
     }
-    else if (nodename.size())
+    else if (!nodename.empty())
     {
         oss << nodename;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/model/address.h ns-3.38/src/network/model/address.h
--- ns-3.37/src/network/model/address.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/model/address.h	2023-03-16 20:54:23.804869200 -0700
@@ -86,6 +86,14 @@
  * }
  * \endcode
  *
+ * To convert a specific Address T (e.g., Ipv6Address) to and from an Address type,
+ * a class must implement three public functions:
+ * \code
+ * static T ConvertFrom(const Address& address);
+ * Address ConvertTo() const;
+ * operator Address() const;
+ * \endcode
+ *
  * \see attribute_Address
  */
 class Address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/model/application.h ns-3.38/src/network/model/application.h
--- ns-3.37/src/network/model/application.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/model/application.h	2023-03-16 20:54:23.804869200 -0700
@@ -74,7 +74,7 @@
      *        relative to the current simulation time.
      *
      * Applications start at various times in the simulation scenario.
-     * The Start method specifies when the application should be
+     * This method specifies when the application should be
      * started.  The application subclasses should override the
      * private "StartApplication" method defined below, which is called at the
      * time specified, to cause the application to begin.
@@ -87,7 +87,7 @@
      *        current simulation time.
      *
      * Once an application has started, it is sometimes useful
-     * to stop the application.  The Stop method specifies when an
+     * to stop the application.  This method specifies when an
      * application is to stop.  The application subclasses should override
      * the private StopApplication method, to be notified when that
      * time has come.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/model/buffer.h ns-3.38/src/network/model/buffer.h
--- ns-3.37/src/network/model/buffer.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/model/buffer.h	2023-03-16 20:54:23.804869200 -0700
@@ -802,9 +802,9 @@
         ~LocalStaticDestructor();
     };
 
-    static uint32_t g_maxSize;                                   //!< Max observed data size
-    static FreeList* g_freeList;                                 //!< Buffer data container
-    static struct LocalStaticDestructor g_localStaticDestructor; //!< Local static destructor
+    static uint32_t g_maxSize;                            //!< Max observed data size
+    static FreeList* g_freeList;                          //!< Buffer data container
+    static LocalStaticDestructor g_localStaticDestructor; //!< Local static destructor
 #endif
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/model/byte-tag-list.cc ns-3.38/src/network/model/byte-tag-list.cc
--- ns-3.37/src/network/model/byte-tag-list.cc	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/model/byte-tag-list.cc	2023-03-16 20:54:23.804869200 -0700
@@ -92,7 +92,7 @@
 ByteTagList::Iterator::Next()
 {
     NS_ASSERT(HasNext());
-    struct Item item = Item(TagBuffer(m_current + 16, m_end));
+    Item item = Item(TagBuffer(m_current + 16, m_end));
     item.tid.SetUid(m_nextTid);
     item.size = m_nextSize;
     item.start = std::max(m_nextStart, m_offsetStart);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/model/nix-vector.h ns-3.38/src/network/model/nix-vector.h
--- ns-3.37/src/network/model/nix-vector.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/model/nix-vector.h	2023-03-16 20:54:23.804869200 -0700
@@ -34,7 +34,7 @@
  *
  * This data structure holds a vector of "neighbor-indexes" for
  * a simulation specific routing protocol, nix-vector routing.
- * Theses neighbor-indexes correspond to the net-device which a
+ * These neighbor-indexes correspond to the net-device which a
  * node should use to route a packet.  A nix-vector is built
  * (or fetched from a cache) on-demand. The nix-vector is
  * transmitted with the packet, and along each hop of the
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/model/packet-metadata.h ns-3.38/src/network/model/packet-metadata.h
--- ns-3.37/src/network/model/packet-metadata.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/model/packet-metadata.h	2023-03-16 20:54:23.804869200 -0700
@@ -91,7 +91,12 @@
             PAYLOAD, //!< Payload
             HEADER,  //!< Header
             TRAILER  //!< Trailer
-        } type;      //!< metadata type
+        };
+
+        /**
+         * metadata type
+         */
+        ItemType type;
 
         /**
          * true: this is a fragmented header, trailer, or, payload.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/model/socket.h ns-3.38/src/network/model/socket.h
--- ns-3.37/src/network/model/socket.h	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/model/socket.h	2023-03-16 20:54:23.808869100 -0700
@@ -180,7 +180,7 @@
      *        completed. The callback is passed  back a pointer to
      *        the same socket object.
      * \param connectionFailed this callback is invoked when the
-     *        connection request initiated by the user is unsuccessfully
+     *        connection request initiated by the user is unsuccessfuly
      *        completed. The callback is passed back a pointer to the
      *        same socket object.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/test/buffer-test.cc ns-3.38/src/network/test/buffer-test.cc
--- ns-3.37/src/network/test/buffer-test.cc	2022-11-01 17:37:24.691395800 -0700
+++ ns-3.38/src/network/test/buffer-test.cc	2023-03-16 20:54:23.808869100 -0700
@@ -291,8 +291,7 @@
     i.Prev(100);
     i.WriteU8(1, 100);
 
-    /// \internal
-    /// See \bugid{54}
+    // See \bugid{54}
     {
         const uint32_t actualSize = 72602;
         const uint32_t chunkSize = 67624;
@@ -354,7 +353,7 @@
     i.Write(buffer.Begin(), buffer.End());
     ENSURE_WRITTEN_BYTES(other, 9, 0x1, 0x2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3, 0x4);
 
-    /// \internal See \bugid{1001}
+    // See \bugid{1001}
     std::string ct("This is the next content of the buffer.");
     buffer = Buffer();
     buffer.AddAtStart(ct.size());
@@ -376,7 +375,7 @@
     }
     free(cBuf);
 
-    /// \internal See \bugid{2044}  Will not pass without bug2044 fix.
+    // See \bugid{2044}  Will not pass without bug2044 fix.
     buffer = Buffer(1);
     buffer.AddAtEnd(2);
     i = buffer.Begin();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/test/packetbb-test-suite.cc ns-3.38/src/network/test/packetbb-test-suite.cc
--- ns-3.37/src/network/test/packetbb-test-suite.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/test/packetbb-test-suite.cc	2023-03-16 20:54:23.808869100 -0700
@@ -267,21 +267,23 @@
 
         packet->TlvPushBack(tlv2);
 
-        uint8_t buffer[] = {0x0c,
-                            0x00,
-                            0x06,
-                            0x00,
-                            0x0a,
-                            0x01,
-                            0x00,
-                            0x02,
-                            0x90,
-                            0x64,
-                            0x04,
-                            0x01,
-                            0x02,
-                            0x03,
-                            0x04};
+        uint8_t buffer[] = {
+            0x0c,
+            0x00,
+            0x06,
+            0x00,
+            0x0a,
+            0x01,
+            0x00,
+            0x02,
+            0x90,
+            0x64,
+            0x04,
+            0x01,
+            0x02,
+            0x03,
+            0x04,
+        };
         AddTestCase(new PbbTestCase("6", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -410,7 +412,8 @@
             0xfc, 0xfd, 0xfe, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
             0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
             0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
-            0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c};
+            0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+        };
         tlv2->SetValue(tlv2val, sizeof(tlv2val));
 
         packet->TlvPushBack(tlv2);
@@ -438,7 +441,8 @@
             0xfe, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
             0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
             0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
-            0x29, 0x2a, 0x2b, 0x2c};
+            0x29, 0x2a, 0x2b, 0x2c,
+        };
         AddTestCase(new PbbTestCase("7", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -474,8 +478,21 @@
         msg1->SetType(1);
         packet->MessagePushBack(msg1);
 
-        uint8_t buffer[] =
-            {0x0c, 0x00, 0x08, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x06, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x0c,
+            0x00,
+            0x08,
+            0x00,
+            0x02,
+            0x01,
+            0x00,
+            0x01,
+            0x03,
+            0x00,
+            0x06,
+            0x00,
+            0x00,
+        };
         AddTestCase(new PbbTestCase("8", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -524,9 +541,11 @@
         msg2->SetOriginatorAddress(Ipv4Address("10.0.0.1"));
         packet->MessagePushBack(msg2);
 
-        uint8_t buffer[] = {0x0c, 0x00, 0x09, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03,
-                            0x00, 0x06, 0x00, 0x00, 0x02, 0x83, 0x00, /* [14] used to be 0x80 */
-                            0x0a, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x0c, 0x00, 0x09, 0x00, 0x02, 0x01, 0x00, 0x01,
+            0x03, 0x00, 0x06, 0x00, 0x00, 0x02, 0x83, 0x00, /* [14] used to be 0x80 */
+            0x0a, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00,
+        };
         AddTestCase(new PbbTestCase("9", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -634,9 +653,11 @@
         msg2->SetHopCount(1);
         packet->MessagePushBack(msg2);
 
-        uint8_t buffer[] = {0x0c, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03,
-                            0x00, 0x06, 0x00, 0x00, 0x02, 0xe3, 0x00, /* [14] used to be 0xe0 */
-                            0x0c, 0x0a, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x0c, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03,
+            0x00, 0x06, 0x00, 0x00, 0x02, 0xe3, 0x00, /* [14] used to be 0xe0 */
+            0x0c, 0x0a, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00,
+        };
         AddTestCase(new PbbTestCase("11", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -691,9 +712,11 @@
         msg2->SetSequenceNumber(12345);
         packet->MessagePushBack(msg2);
 
-        uint8_t buffer[] = {0x0c, 0x00, 0x0c, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x06,
-                            0x00, 0x00, 0x02, 0xf3, 0x00, /* [14] - 0xf0 */
-                            0x0e, 0x0a, 0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x0c, 0x00, 0x0c, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x06,
+            0x00, 0x00, 0x02, 0xf3, 0x00, /* [14] - 0xf0 */
+            0x0e, 0x0a, 0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00,
+        };
         AddTestCase(new PbbTestCase("12", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -748,9 +771,11 @@
         msg2->SetSequenceNumber(12345);
         packet->MessagePushBack(msg2);
 
-        uint8_t buffer[] = {0x0c, 0x00, 0x0d, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x06,
-                            0x00, 0x00, 0x02, 0xf3, 0x00, /* [14] - 0xf0 */
-                            0x0e, 0x0a, 0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x0c, 0x00, 0x0d, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x06,
+            0x00, 0x00, 0x02, 0xf3, 0x00, /* [14] - 0xf0 */
+            0x0e, 0x0a, 0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00,
+        };
         AddTestCase(new PbbTestCase("13", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -814,9 +839,11 @@
         msg2->SetSequenceNumber(12345);
         packet->MessagePushBack(msg2);
 
-        uint8_t buffer[] = {0x0c, 0x00, 0x0e, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x08,
-                            0x00, 0x02, 0x01, 0x00, 0x02, 0xf3, 0x00, 0x0e, 0x0a, /* [16] - 0xf0 */
-                            0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x0c, 0x00, 0x0e, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00,
+            0x08, 0x00, 0x02, 0x01, 0x00, 0x02, 0xf3, 0x00, 0x0e, 0x0a, /* [16] - 0xf0 */
+            0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00,
+        };
         AddTestCase(new PbbTestCase("14", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -889,10 +916,12 @@
 
         packet->MessagePushBack(msg2);
 
-        uint8_t buffer[] = {0x0c, 0x00, 0x0f, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x08,
-                            0x00, 0x02, 0x01, 0x00, 0x02, 0xf3, 0x00, 0x16, 0x0a, /* [16] - 0xf0 */
-                            0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00, 0x01, 0x00,
-                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x0c, 0x00, 0x0f, 0x00, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00,
+            0x08, 0x00, 0x02, 0x01, 0x00, 0x02, 0xf3, 0x00, 0x16, 0x0a, /* [16] - 0xf0 */
+            0x00, 0x00, 0x01, 0xff, 0x01, 0x30, 0x39, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        };
         AddTestCase(new PbbTestCase("15", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -2243,7 +2272,8 @@
             0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
             0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
             0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
-            0x2c};
+            0x2c,
+        };
         AddTestCase(new PbbTestCase("28", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
@@ -2307,8 +2337,10 @@
 
         packet->MessagePushBack(m1);
 
-        uint8_t buffer[] = {0x00, 0x01, 0x8f, 0x00, 0x16, 0xab, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
-                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
+        uint8_t buffer[] = {
+            0x00, 0x01, 0x8f, 0x00, 0x16, 0xab, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+        };
         AddTestCase(new PbbTestCase("30", packet, buffer, sizeof(buffer)), TestCase::QUICK);
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/test/packet-test-suite.cc ns-3.38/src/network/test/packet-test-suite.cc
--- ns-3.37/src/network/test/packet-test-suite.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/test/packet-test-suite.cc	2023-03-16 20:54:23.808869100 -0700
@@ -565,7 +565,7 @@
     while (i.HasNext() && j < expected.size())
     {
         ByteTagIterator::Item item = i.Next();
-        struct Expected e = expected[j];
+        Expected e = expected[j];
         std::ostringstream oss;
         oss << "anon::ATestTag<" << e.n << ">";
         NS_TEST_EXPECT_MSG_EQ(item.GetTypeId().GetName(), oss.str(), "trivial");
@@ -603,7 +603,7 @@
     while (i.HasNext() && j < expected.size())
     {
         ByteTagIterator::Item item = i.Next();
-        struct Expected e = expected[j];
+        Expected e = expected[j];
         std::ostringstream oss;
         oss << "anon::ATestTag<" << e.n << ">";
         NS_TEST_EXPECT_MSG_EQ(item.GetTypeId().GetName(), oss.str(), "trivial");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/test/pcap-file-test-suite.cc ns-3.38/src/network/test/pcap-file-test-suite.cc
--- ns-3.37/src/network/test/pcap-file-test-suite.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/test/pcap-file-test-suite.cc	2023-03-16 20:54:23.808869100 -0700
@@ -1116,135 +1116,137 @@
 /**
  * PCAP Packet structure
  */
-typedef struct PACKET_ENTRY
+struct PacketEntry
 {
     uint32_t tsSec;                //!< Time (seconds part)
     uint32_t tsUsec;               //!< Time (micro seconds part)
     uint32_t inclLen;              //!< Length of the entry in the PCAP
     uint32_t origLen;              //!< length of the original packet
     uint16_t data[N_PACKET_BYTES]; //!< Packet data
-} PacketEntry;
+};
 
-static const PacketEntry knownPackets[] = {{2,
-                                            3696,
-                                            46,
-                                            46,
-                                            {0x0001,
-                                             0x0800,
-                                             0x0604,
-                                             0x0001,
-                                             0x0000,
-                                             0x0000,
-                                             0x0003,
-                                             0x0a01,
-                                             0x0201,
-                                             0xffff,
-                                             0xffff,
-                                             0xffff,
-                                             0x0a01,
-                                             0x0204,
-                                             0x0000,
-                                             0x0000}},
-                                           {2,
-                                            3707,
-                                            46,
-                                            46,
-                                            {0x0001,
-                                             0x0800,
-                                             0x0604,
-                                             0x0002,
-                                             0x0000,
-                                             0x0000,
-                                             0x0006,
-                                             0x0a01,
-                                             0x0204,
-                                             0x0000,
-                                             0x0000,
-                                             0x0003,
-                                             0x0a01,
-                                             0x0201,
-                                             0x0000,
-                                             0x0000}},
-                                           {2,
-                                            3801,
-                                            1070,
-                                            1070,
-                                            {0x4500,
-                                             0x041c,
-                                             0x0000,
-                                             0x0000,
-                                             0x3f11,
-                                             0x0000,
-                                             0x0a01,
-                                             0x0101,
-                                             0x0a01,
-                                             0x0204,
-                                             0xc001,
-                                             0x0009,
-                                             0x0408,
-                                             0x0000,
-                                             0x0000,
-                                             0x0000}},
-                                           {2,
-                                            3811,
-                                            46,
-                                            46,
-                                            {0x0001,
-                                             0x0800,
-                                             0x0604,
-                                             0x0001,
-                                             0x0000,
-                                             0x0000,
-                                             0x0006,
-                                             0x0a01,
-                                             0x0204,
-                                             0xffff,
-                                             0xffff,
-                                             0xffff,
-                                             0x0a01,
-                                             0x0201,
-                                             0x0000,
-                                             0x0000}},
-                                           {2,
-                                            3822,
-                                            46,
-                                            46,
-                                            {0x0001,
-                                             0x0800,
-                                             0x0604,
-                                             0x0002,
-                                             0x0000,
-                                             0x0000,
-                                             0x0003,
-                                             0x0a01,
-                                             0x0201,
-                                             0x0000,
-                                             0x0000,
-                                             0x0006,
-                                             0x0a01,
-                                             0x0204,
-                                             0x0000,
-                                             0x0000}},
-                                           {2,
-                                            3915,
-                                            1070,
-                                            1070,
-                                            {0x4500,
-                                             0x041c,
-                                             0x0000,
-                                             0x0000,
-                                             0x4011,
-                                             0x0000,
-                                             0x0a01,
-                                             0x0204,
-                                             0x0a01,
-                                             0x0101,
-                                             0x0009,
-                                             0xc001,
-                                             0x0408,
-                                             0x0000,
-                                             0x0000,
-                                             0x0000}}};
+static const PacketEntry knownPackets[] = {
+    {2,
+     3696,
+     46,
+     46,
+     {0x0001,
+      0x0800,
+      0x0604,
+      0x0001,
+      0x0000,
+      0x0000,
+      0x0003,
+      0x0a01,
+      0x0201,
+      0xffff,
+      0xffff,
+      0xffff,
+      0x0a01,
+      0x0204,
+      0x0000,
+      0x0000}},
+    {2,
+     3707,
+     46,
+     46,
+     {0x0001,
+      0x0800,
+      0x0604,
+      0x0002,
+      0x0000,
+      0x0000,
+      0x0006,
+      0x0a01,
+      0x0204,
+      0x0000,
+      0x0000,
+      0x0003,
+      0x0a01,
+      0x0201,
+      0x0000,
+      0x0000}},
+    {2,
+     3801,
+     1070,
+     1070,
+     {0x4500,
+      0x041c,
+      0x0000,
+      0x0000,
+      0x3f11,
+      0x0000,
+      0x0a01,
+      0x0101,
+      0x0a01,
+      0x0204,
+      0xc001,
+      0x0009,
+      0x0408,
+      0x0000,
+      0x0000,
+      0x0000}},
+    {2,
+     3811,
+     46,
+     46,
+     {0x0001,
+      0x0800,
+      0x0604,
+      0x0001,
+      0x0000,
+      0x0000,
+      0x0006,
+      0x0a01,
+      0x0204,
+      0xffff,
+      0xffff,
+      0xffff,
+      0x0a01,
+      0x0201,
+      0x0000,
+      0x0000}},
+    {2,
+     3822,
+     46,
+     46,
+     {0x0001,
+      0x0800,
+      0x0604,
+      0x0002,
+      0x0000,
+      0x0000,
+      0x0003,
+      0x0a01,
+      0x0201,
+      0x0000,
+      0x0000,
+      0x0006,
+      0x0a01,
+      0x0204,
+      0x0000,
+      0x0000}},
+    {2,
+     3915,
+     1070,
+     1070,
+     {0x4500,
+      0x041c,
+      0x0000,
+      0x0000,
+      0x4011,
+      0x0000,
+      0x0a01,
+      0x0204,
+      0x0a01,
+      0x0101,
+      0x0009,
+      0xc001,
+      0x0408,
+      0x0000,
+      0x0000,
+      0x0000}},
+};
 
 void
 ReadFileTestCase::DoRun()
@@ -1282,10 +1284,10 @@
         NS_TEST_ASSERT_MSG_EQ(f.Fail(), false, "Read() of known good pcap file returns error");
         NS_TEST_ASSERT_MSG_EQ(tsSec,
                               p.tsSec,
-                              "Incorrectly read seconds timestap from known good pcap file");
+                              "Incorrectly read seconds timestamp from known good pcap file");
         NS_TEST_ASSERT_MSG_EQ(tsUsec,
                               p.tsUsec,
-                              "Incorrectly read microseconds timestap from known good pcap file");
+                              "Incorrectly read microseconds timestamp from known good pcap file");
         NS_TEST_ASSERT_MSG_EQ(inclLen,
                               p.inclLen,
                               "Incorrectly read included length from known good packet");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/test/test-data-rate.cc ns-3.38/src/network/test/test-data-rate.cc
--- ns-3.37/src/network/test/test-data-rate.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/test/test-data-rate.cc	2023-03-16 20:54:23.808869100 -0700
@@ -198,7 +198,7 @@
 };
 
 DataRateTestCase2::DataRateTestCase2()
-    : DataRateTestCase("Test arithmatic on DateRate")
+    : DataRateTestCase("Test arithmetic on DateRate")
 {
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/crc32.cc ns-3.38/src/network/utils/crc32.cc
--- ns-3.37/src/network/utils/crc32.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/crc32.cc	2023-03-16 20:54:23.808869100 -0700
@@ -64,7 +64,8 @@
     0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
     0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
     0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
-    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D};
+    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
+};
 
 uint32_t
 CRC32Calculate(const uint8_t* data, int length)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/data-rate.cc ns-3.38/src/network/utils/data-rate.cc
--- ns-3.37/src/network/utils/data-rate.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/data-rate.cc	2023-03-16 20:54:23.808869100 -0700
@@ -198,7 +198,7 @@
 }
 
 DataRate
-DataRate::operator+(DataRate rhs)
+DataRate::operator+(DataRate rhs) const
 {
     return DataRate(m_bps + rhs.m_bps);
 }
@@ -211,7 +211,7 @@
 }
 
 DataRate
-DataRate::operator-(DataRate rhs)
+DataRate::operator-(DataRate rhs) const
 {
     NS_ASSERT_MSG(m_bps >= rhs.m_bps, "Data Rate cannot be negative.");
     return DataRate(m_bps - rhs.m_bps);
@@ -226,7 +226,7 @@
 }
 
 DataRate
-DataRate::operator*(double rhs)
+DataRate::operator*(double rhs) const
 {
     return DataRate(((uint64_t)(m_bps * rhs)));
 }
@@ -239,7 +239,7 @@
 }
 
 DataRate
-DataRate::operator*(uint64_t rhs)
+DataRate::operator*(uint64_t rhs) const
 {
     return DataRate(m_bps * rhs);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/data-rate.h ns-3.38/src/network/utils/data-rate.h
--- ns-3.37/src/network/utils/data-rate.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/data-rate.h	2023-03-16 20:54:51.628957000 -0700
@@ -57,7 +57,7 @@
  * * A unit.
  *
  * Whitespace is allowed but not required between the numeric value and
- * multipler or unit.
+ * multiplier or unit.
  *
  * Supported multiplier prefixes:
  *
@@ -124,7 +124,7 @@
      *
      * \param rhs the DataRate to add to this DataRate
      */
-    DataRate operator+(DataRate rhs);
+    DataRate operator+(DataRate rhs) const;
 
     /**
      * \return the DataRate representing the sum of this object with rhs
@@ -138,7 +138,7 @@
      *
      * \param rhs the DataRate to subtract from this DataRate
      */
-    DataRate operator-(DataRate rhs);
+    DataRate operator-(DataRate rhs) const;
 
     /**
      * \return the DataRate representing the difference of this object with rhs
@@ -156,7 +156,7 @@
      *
      * \param rhs the double to multiply to this datarate
      */
-    DataRate operator*(double rhs);
+    DataRate operator*(double rhs) const;
 
     /**
      * \brief Scales the DataRate
@@ -165,7 +165,7 @@
      *
      * \return DataRate object representing the product of this object with rhs
      *
-     * \param rhs the double to multipy to this datarate
+     * \param rhs the double to multiply to this datarate
      */
     DataRate& operator*=(double rhs);
 
@@ -174,16 +174,16 @@
      *
      * \return DataRate object representing the product of this object with rhs
      *
-     * \param rhs the uint64_t to multipy to this datarate
+     * \param rhs the uint64_t to multiply to this datarate
      */
-    DataRate operator*(uint64_t rhs);
+    DataRate operator*(uint64_t rhs) const;
 
     /**
      * \brief Scales the DataRate
      *
      * \return DataRate object representing the product of this object with rhs
      *
-     * \param rhs the uint64_t to multipy to this datarate
+     * \param rhs the uint64_t to multiply to this datarate
      */
     DataRate& operator*=(uint64_t rhs);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/error-model.cc ns-3.38/src/network/utils/error-model.cc
--- ns-3.37/src/network/utils/error-model.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/error-model.cc	2023-03-16 20:54:23.808869100 -0700
@@ -198,7 +198,7 @@
 }
 
 void
-RateErrorModel::SetUnit(enum ErrorUnit error_unit)
+RateErrorModel::SetUnit(ErrorUnit error_unit)
 {
     NS_LOG_FUNCTION(this << error_unit);
     m_unit = error_unit;
@@ -445,7 +445,7 @@
     NS_LOG_FUNCTION(this);
 }
 
-std::list<uint32_t>
+std::list<uint64_t>
 ListErrorModel::GetList() const
 {
     NS_LOG_FUNCTION(this);
@@ -453,7 +453,7 @@
 }
 
 void
-ListErrorModel::SetList(const std::list<uint32_t>& packetlist)
+ListErrorModel::SetList(const std::list<uint64_t>& packetlist)
 {
     NS_LOG_FUNCTION(this << &packetlist);
     m_packetList = packetlist;
@@ -470,7 +470,7 @@
     {
         return false;
     }
-    uint32_t uid = p->GetUid();
+    auto uid = p->GetUid();
     for (PacketListCI i = m_packetList.begin(); i != m_packetList.end(); i++)
     {
         if (uid == *i)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/error-model.h ns-3.38/src/network/utils/error-model.h
--- ns-3.37/src/network/utils/error-model.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/error-model.h	2023-03-16 20:54:23.808869100 -0700
@@ -209,7 +209,7 @@
     /**
      * \param error_unit the ErrorUnit to be used by the underlying model
      */
-    void SetUnit(enum ErrorUnit error_unit);
+    void SetUnit(ErrorUnit error_unit);
 
     /**
      * \returns the error rate being applied by the model
@@ -257,8 +257,8 @@
     virtual bool DoCorruptBit(Ptr<Packet> p);
     void DoReset() override;
 
-    enum ErrorUnit m_unit; //!< Error rate unit
-    double m_rate;         //!< Error rate
+    ErrorUnit m_unit; //!< Error rate unit
+    double m_rate;    //!< Error rate
 
     Ptr<RandomVariableStream> m_ranvar; //!< rng stream
 };
@@ -388,22 +388,22 @@
     /**
      * \return a copy of the underlying list
      */
-    std::list<uint32_t> GetList() const;
+    std::list<uint64_t> GetList() const;
     /**
      * \param packetlist The list of packet uids to error.
      *
      * This method overwrites any previously provided list.
      */
-    void SetList(const std::list<uint32_t>& packetlist);
+    void SetList(const std::list<uint64_t>& packetlist);
 
   private:
     bool DoCorrupt(Ptr<Packet> p) override;
     void DoReset() override;
 
     /// Typedef: packet Uid list
-    typedef std::list<uint32_t> PacketList;
+    typedef std::list<uint64_t> PacketList;
     /// Typedef: packet Uid list const iterator
-    typedef std::list<uint32_t>::const_iterator PacketListCI;
+    typedef std::list<uint64_t>::const_iterator PacketListCI;
 
     PacketList m_packetList; //!< container of Uid of packets to corrupt
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/inet6-socket-address.h ns-3.38/src/network/utils/inet6-socket-address.h
--- ns-3.37/src/network/utils/inet6-socket-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/inet6-socket-address.h	2023-03-16 20:54:23.808869100 -0700
@@ -113,13 +113,13 @@
      */
     static Inet6SocketAddress ConvertFrom(const Address& addr);
 
-  private:
     /**
      * \brief Convert to Address.
      * \return Address instance
      */
     Address ConvertTo() const;
 
+  private:
     /**
      * \brief Get the type.
      * \return the type of Inet6SocketAddress
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/inet-socket-address.h ns-3.38/src/network/utils/inet-socket-address.h
--- ns-3.37/src/network/utils/inet-socket-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/inet-socket-address.h	2023-03-16 20:54:23.808869100 -0700
@@ -116,13 +116,13 @@
      */
     static InetSocketAddress ConvertFrom(const Address& address);
 
-  private:
     /**
      * \brief Convert to an Address type
      * \return the Address corresponding to this object.
      */
     Address ConvertTo() const;
 
+  private:
     /**
      * \brief Get the underlying address type (automatically assigned).
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/ipv4-address.cc ns-3.38/src/network/utils/ipv4-address.cc
--- ns-3.37/src/network/utils/ipv4-address.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/ipv4-address.cc	2023-03-16 20:54:23.808869100 -0700
@@ -24,63 +24,17 @@
 
 #include <cstdlib>
 
+#ifdef __WIN32__
+#include <WS2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+
 namespace ns3
 {
 
 NS_LOG_COMPONENT_DEFINE("Ipv4Address");
 
-#define ASCII_DOT (0x2e)
-#define ASCII_ZERO (0x30)
-#define ASCII_SLASH (0x2f)
-
-/**
- * \brief Converts a string representing an IP address into the address
- * \param address the address string
- * \returns the address
- */
-static uint32_t
-AsciiToIpv4Host(const char* address)
-{
-    NS_LOG_FUNCTION(&address);
-    uint32_t host = 0;
-    uint8_t numberOfDots = 0;
-    const char* ptr = address;
-
-    NS_ASSERT_MSG(*ptr != ASCII_DOT,
-                  "Error, can not build an IPv4 address from an invalid string: " << address);
-    while (true)
-    {
-        uint8_t byte = 0;
-        while (*ptr != ASCII_DOT && *ptr != 0)
-        {
-            byte *= 10;
-            byte += *ptr - ASCII_ZERO;
-            ptr++;
-        }
-        host <<= 8;
-        host |= byte;
-        if (*ptr == 0)
-        {
-            break;
-        }
-        ptr++;
-        NS_ASSERT_MSG(*ptr != ASCII_DOT,
-                      "Error, can not build an IPv4 address from an invalid string: " << address);
-        numberOfDots++;
-    }
-    NS_ASSERT_MSG(*(ptr - 1) != ASCII_DOT,
-                  "Error, can not build an IPv4 address from an invalid string: " << address);
-    NS_ASSERT_MSG(numberOfDots == 3,
-                  "Error, can not build an IPv4 address from an invalid string: " << address);
-
-    return host;
-}
-
-} // namespace ns3
-
-namespace ns3
-{
-
 Ipv4Mask::Ipv4Mask()
     : m_mask(0x66666666)
 {
@@ -96,7 +50,7 @@
 Ipv4Mask::Ipv4Mask(const char* mask)
 {
     NS_LOG_FUNCTION(this << mask);
-    if (*mask == ASCII_SLASH)
+    if (*mask == '/')
     {
         uint32_t plen = static_cast<uint32_t>(std::atoi(++mask));
         NS_ASSERT(plen <= 32);
@@ -111,7 +65,11 @@
     }
     else
     {
-        m_mask = AsciiToIpv4Host(mask);
+        if (inet_pton(AF_INET, mask, &m_mask) <= 0)
+        {
+            NS_ABORT_MSG("Error, can not build an IPv4 mask from an invalid string: " << mask);
+        }
+        m_mask = ntohl(m_mask);
     }
 }
 
@@ -219,8 +177,16 @@
 Ipv4Address::Ipv4Address(const char* address)
 {
     NS_LOG_FUNCTION(this << address);
-    m_address = AsciiToIpv4Host(address);
+
+    if (inet_pton(AF_INET, address, &m_address) <= 0)
+    {
+        NS_LOG_LOGIC("Error, can not build an IPv4 address from an invalid string: " << address);
+        m_address = 0;
+        m_initialized = false;
+        return;
+    }
     m_initialized = true;
+    m_address = ntohl(m_address);
 }
 
 uint32_t
@@ -242,8 +208,15 @@
 Ipv4Address::Set(const char* address)
 {
     NS_LOG_FUNCTION(this << address);
-    m_address = AsciiToIpv4Host(address);
+    if (inet_pton(AF_INET, address, &m_address) <= 0)
+    {
+        NS_LOG_LOGIC("Error, can not build an IPv4 address from an invalid string: " << address);
+        m_address = 0;
+        m_initialized = false;
+        return;
+    }
     m_initialized = true;
+    m_address = ntohl(m_address);
 }
 
 Ipv4Address
@@ -275,7 +248,7 @@
         // broadcast for this address.
         return false;
     }
-    return ((Get() | mask.GetInverse()) == Get());
+    return ((Get() | mask.Get()) == Ipv4Address::GetBroadcast().Get());
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/ipv4-address.h ns-3.38/src/network/utils/ipv4-address.h
--- ns-3.37/src/network/utils/ipv4-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/ipv4-address.h	2023-03-16 20:54:51.628957000 -0700
@@ -183,6 +183,12 @@
      */
     static Ipv4Address ConvertFrom(const Address& address);
     /**
+     * \brief Convert to an Address type
+     * \return the Address corresponding to this object.
+     */
+    Address ConvertTo() const;
+
+    /**
      * \return the 0.0.0.0 address
      */
     static Ipv4Address GetZero();
@@ -201,12 +207,6 @@
 
   private:
     /**
-     * \brief Convert to an Address type
-     * \return the Address corresponding to this object.
-     */
-    Address ConvertTo() const;
-
-    /**
      * \brief Get the underlying address type (automatically assigned).
      *
      * \returns the address type
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/ipv6-address.cc ns-3.38/src/network/utils/ipv6-address.cc
--- ns-3.37/src/network/utils/ipv6-address.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/ipv6-address.cc	2023-03-16 20:54:23.808869100 -0700
@@ -17,28 +17,6 @@
  * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
  */
 
-// Part of the Ipv6Address::Print function has been adapted from inet_ntop6 Linux function.
-// See http://www.net-snmp.org/dev/agent/inet__ntop_8c_source.html
-// Author: Paul Vixie, 1996.
-// The inet_ntop6 function was under the copyright below, which is
-// compatible with GPLv2, see http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses.
-
-/* Copyright (c) 1996 by Internet Software Consortium.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- */
-
 #include "ipv6-address.h"
 
 #include "mac16-address.h"
@@ -49,7 +27,13 @@
 #include "ns3/log.h"
 
 #include <iomanip>
-#include <memory.h>
+#include <memory>
+
+#ifdef __WIN32__
+#include <WS2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
 
 namespace ns3
 {
@@ -165,151 +149,6 @@
 }
 #endif
 
-/**
- * \brief Convert an IPv6 C-string into a 128-bit representation.
- *
- * \param address pointer to the char buffer with the address ascii representation
- * \param addr the buffer to store the IPv6 address
- *
- * \return true if success, false otherwise (bad format, ...)
- *
- * \note This function is strongly inspired by inet_pton6() from Paul Vixie.
- * \todo Handle IPv6 address with decimal value for last four bytes.
- */
-static bool
-AsciiToIpv6Host(const char* address, uint8_t addr[16])
-{
-    NS_LOG_FUNCTION(address << &addr);
-    static const char xdigits_l[] = "0123456789abcdef";
-    static const char xdigits_u[] = "0123456789ABCDEF";
-    unsigned char tmp[16];
-    unsigned char* tp = tmp;
-    unsigned char* const endp = tp + 16;
-    unsigned char* colonp = nullptr;
-    const char* xdigits = nullptr;
-#if 0
-  const char* curtok = 0;
-#endif
-    int ch = 0;
-    int seen_xdigits = 0;
-    unsigned int val = 0;
-
-    memset(tp, 0x00, 16);
-
-    /* Leading :: requires some special handling. */
-    if (*address == ':')
-    {
-        if (*++address != ':')
-        {
-            return (0);
-        }
-    }
-#if 0
-  curtok = address;
-#endif
-    while ((ch = *address++) != '\0')
-    {
-        const char* pch = nullptr;
-
-        if ((pch = strchr((xdigits = xdigits_l), ch)) == nullptr)
-        {
-            pch = strchr((xdigits = xdigits_u), ch);
-        }
-
-        if (pch != nullptr)
-        {
-            val <<= 4;
-            val |= (pch - xdigits);
-
-            if (++seen_xdigits > 4)
-            {
-                return (0);
-            }
-            continue;
-        }
-        if (ch == ':')
-        {
-#if 0
-          curtok = address;
-#endif
-
-            if (!seen_xdigits)
-            {
-                if (colonp)
-                {
-                    return (0);
-                }
-                colonp = tp;
-                continue;
-            }
-
-            if (endp - tp < 2)
-            {
-                return (0);
-            }
-
-            *tp++ = (unsigned char)(val >> 8) & 0xff;
-            *tp++ = (unsigned char)val & 0xff;
-            seen_xdigits = 0;
-            val = 0;
-            continue;
-        }
-
-        /* \todo Handle IPv4 mapped address (2001::192.168.0.1) */
-#if 0
-      if (ch == '.' && (endp - tp > 3 /* NS_INADDRSZ - 1 */)) &&
-          inet_pton4 (curtok, tp) > 0)
-        {
-          tp += 4 /*NS_INADDRSZ*/;
-          seen_xdigits = 0;
-          break; /* '\0' was seen by inet_pton4(). */
-        }
-#endif
-        return (0);
-    }
-
-    if (seen_xdigits)
-    {
-        if (endp - tp < 2)
-        {
-            return (0);
-        }
-        *tp++ = (unsigned char)(val >> 8) & 0xff;
-        *tp++ = (unsigned char)val & 0xff;
-    }
-
-    if (colonp != nullptr)
-    {
-        /*
-         * Since some memmove ()'s erroneously fail to handle
-         * overlapping regions, we'll do the shift by hand.
-         */
-        const int n = tp - colonp;
-        int i = 0;
-
-        if (tp == endp)
-        {
-            return (0);
-        }
-
-        for (i = 1; i <= n; i++)
-        {
-            endp[-i] = colonp[n - i];
-            colonp[n - i] = 0;
-        }
-
-        tp = endp;
-    }
-
-    if (tp != endp)
-    {
-        return (0);
-    }
-
-    memcpy(addr, tmp, 16);
-    return (1);
-}
-
 Ipv6Address::Ipv6Address()
 {
     NS_LOG_FUNCTION(this);
@@ -334,7 +173,14 @@
 Ipv6Address::Ipv6Address(const char* address)
 {
     NS_LOG_FUNCTION(this << address);
-    AsciiToIpv6Host(address, m_address);
+
+    if (inet_pton(AF_INET6, address, m_address) <= 0)
+    {
+        memset(m_address, 0x00, 16);
+        NS_LOG_LOGIC("Error, can not build an IPv6 address from an invalid string: " << address);
+        m_initialized = false;
+        return;
+    }
     m_initialized = true;
 }
 
@@ -356,7 +202,13 @@
 Ipv6Address::Set(const char* address)
 {
     NS_LOG_FUNCTION(this << address);
-    AsciiToIpv6Host(address, m_address);
+    if (inet_pton(AF_INET6, address, m_address) <= 0)
+    {
+        memset(m_address, 0x00, 16);
+        NS_LOG_LOGIC("Error, can not build an IPv6 address from an invalid string: " << address);
+        m_initialized = false;
+        return;
+    }
     m_initialized = true;
 }
 
@@ -389,22 +241,24 @@
 Ipv6Address::MakeIpv4MappedAddress(Ipv4Address addr)
 {
     NS_LOG_FUNCTION(addr);
-    uint8_t buf[16] = {0x00,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0xff,
-                       0xff,
-                       0x00,
-                       0x00,
-                       0x00,
-                       0x00};
+    uint8_t buf[16] = {
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0xff,
+        0xff,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+    };
     addr.Serialize(&buf[12]);
     return (Ipv6Address(buf));
 }
@@ -678,93 +532,12 @@
 {
     NS_LOG_FUNCTION(this << &os);
 
-    // note: part of this function has been adapted from inet_ntop6 Linux function.
-    // See http://www.net-snmp.org/dev/agent/inet__ntop_8c_source.html
-    // Author: Paul Vixie, 1996.
-
-    if (IsIpv4MappedAddress())
-    {
-        os << "::ffff:" << (unsigned int)m_address[12] << "." << (unsigned int)m_address[13] << "."
-           << (unsigned int)m_address[14] << "." << (unsigned int)m_address[15];
-        return;
-    }
-
-    uint16_t address[8];
-    uint8_t i;
-
-    for (i = 0; i < 8; i++)
-    {
-        address[i] = (uint16_t(m_address[2 * i]) << 8) | uint16_t(m_address[2 * i + 1]);
-    }
-
-    int8_t bestBase = -1;
-    int8_t bestLen = 0;
-    int8_t curBase = -1;
-    int8_t curLen = 0;
-
-    for (i = 0; i < 8; i++)
-    {
-        if (address[i] == 0)
-        {
-            if (curBase == -1)
-            {
-                curBase = i;
-                curLen = 1;
-            }
-            else
-            {
-                curLen++;
-            }
-        }
-        else
-        {
-            if (curBase != -1)
-            {
-                if (bestBase == -1 || curLen > bestLen)
-                {
-                    bestBase = curBase;
-                    bestLen = curLen;
-                }
-                curBase = -1;
-            }
-        }
-    }
-    if (curBase != -1)
-    {
-        if (bestBase == -1 || curLen > bestLen)
-        {
-            bestBase = curBase;
-            bestLen = curLen;
-        }
-    }
-    if (bestBase != -1 && bestLen < 2)
-    {
-        bestBase = -1;
-    }
+    char str[INET6_ADDRSTRLEN];
 
-    for (i = 0; i < 8;)
+    if (inet_ntop(AF_INET6, m_address, str, INET6_ADDRSTRLEN))
     {
-        // Are we inside the best run of 0x00's?
-        if (i == bestBase)
-        {
-            os << ':';
-            i += bestLen;
-            continue;
-        }
-        // Are we following an initial run of 0x00s or any real hex?
-        if (i != 0)
-        {
-            os << ':';
-        }
-        os << std::hex << (unsigned int)address[i];
-        i++;
-    }
-    // Was it a trailing run of 0x00's?
-    if (bestBase != -1 && (bestBase + bestLen) == 8)
-    {
-        os << ':';
+        os << str;
     }
-    os << std::dec;
 }
 
 bool
@@ -1044,7 +817,10 @@
 Ipv6Prefix::Ipv6Prefix(const char* prefix)
 {
     NS_LOG_FUNCTION(this << prefix);
-    AsciiToIpv6Host(prefix, m_prefix);
+    if (inet_pton(AF_INET6, prefix, m_prefix) <= 0)
+    {
+        NS_ABORT_MSG("Error, can not build an IPv6 prefix from an invalid string: " << prefix);
+    }
     m_prefixLength = GetMinimumPrefixLength();
 }
 
@@ -1058,8 +834,10 @@
 Ipv6Prefix::Ipv6Prefix(const char* prefix, uint8_t prefixLength)
 {
     NS_LOG_FUNCTION(this << prefix);
-    AsciiToIpv6Host(prefix, m_prefix);
-
+    if (inet_pton(AF_INET6, prefix, m_prefix) <= 0)
+    {
+        NS_ABORT_MSG("Error, can not build an IPv6 prefix from an invalid string: " << prefix);
+    }
     uint8_t autoLength = GetMinimumPrefixLength();
     NS_ASSERT_MSG(autoLength <= prefixLength,
                   "Ipv6Prefix: address and prefix are not compatible: " << Ipv6Address(prefix)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/ipv6-address.h ns-3.38/src/network/utils/ipv6-address.h
--- ns-3.37/src/network/utils/ipv6-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/ipv6-address.h	2023-03-16 20:54:51.628957000 -0700
@@ -346,6 +346,12 @@
     static Ipv6Address ConvertFrom(const Address& address);
 
     /**
+     * \brief convert the IPv6Address object to an Address object.
+     * \return the Address object corresponding to this object.
+     */
+    Address ConvertTo() const;
+
+    /**
      * \return true if address is initialized (i.e., set to something), false otherwise
      */
     bool IsInitialized() const;
@@ -400,12 +406,6 @@
 
   private:
     /**
-     * \brief convert the IPv6Address object to an Address object.
-     * \return the Address object corresponding to this object.
-     */
-    Address ConvertTo() const;
-
-    /**
      * \brief Return the Type of address.
      * \return type of address
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/llc-snap-header.h ns-3.38/src/network/utils/llc-snap-header.h
--- ns-3.37/src/network/utils/llc-snap-header.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/llc-snap-header.h	2023-03-16 20:54:23.808869100 -0700
@@ -29,7 +29,7 @@
 {
 
 /**
- * The length in octects of the LLC/SNAP header
+ * The length in octets of the LLC/SNAP header
  */
 static const uint16_t LLC_SNAP_HEADER_LENGTH = 8;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/mac16-address.h ns-3.38/src/network/utils/mac16-address.h
--- ns-3.37/src/network/utils/mac16-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/mac16-address.h	2023-03-16 20:54:23.808869100 -0700
@@ -78,6 +78,13 @@
      */
     static Mac16Address ConvertFrom(const Address& address);
     /**
+     * \returns a new Address instance
+     *
+     * Convert an instance of this class to a polymorphic Address instance.
+     */
+    Address ConvertTo() const;
+
+    /**
      * \param address address to test
      * \returns true if the address matches, false otherwise.
      */
@@ -151,13 +158,6 @@
 
   private:
     /**
-     * \returns a new Address instance
-     *
-     * Convert an instance of this class to a polymorphic Address instance.
-     */
-    Address ConvertTo() const;
-
-    /**
      * \brief Return the Type of address.
      * \return type of address
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/mac48-address.h ns-3.38/src/network/utils/mac48-address.h
--- ns-3.37/src/network/utils/mac48-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/mac48-address.h	2023-03-16 20:54:23.808869100 -0700
@@ -82,6 +82,13 @@
      */
     static Mac48Address ConvertFrom(const Address& address);
     /**
+     * \returns a new Address instance
+     *
+     * Convert an instance of this class to a polymorphic Address instance.
+     */
+    Address ConvertTo() const;
+
+    /**
      * \param address address to test
      * \returns true if the address matches, false otherwise.
      */
@@ -155,13 +162,6 @@
 
   private:
     /**
-     * \returns a new Address instance
-     *
-     * Convert an instance of this class to a polymorphic Address instance.
-     */
-    Address ConvertTo() const;
-
-    /**
      * \brief Return the Type of address.
      * \return type of address
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/mac64-address.h ns-3.38/src/network/utils/mac64-address.h
--- ns-3.37/src/network/utils/mac64-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/mac64-address.h	2023-03-16 20:54:23.808869100 -0700
@@ -81,6 +81,13 @@
      */
     static Mac64Address ConvertFrom(const Address& address);
     /**
+     * \returns a new Address instance
+     *
+     * Convert an instance of this class to a polymorphic Address instance.
+     */
+    Address ConvertTo() const;
+
+    /**
      * \param address address to test
      * \returns true if the address matches, false otherwise.
      */
@@ -108,13 +115,6 @@
 
   private:
     /**
-     * \returns a new Address instance
-     *
-     * Convert an instance of this class to a polymorphic Address instance.
-     */
-    Address ConvertTo() const;
-
-    /**
      * \brief Return the Type of address.
      * \return type of address
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/mac8-address.h ns-3.38/src/network/utils/mac8-address.h
--- ns-3.37/src/network/utils/mac8-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/mac8-address.h	2023-03-16 20:54:23.808869100 -0700
@@ -63,6 +63,13 @@
     static Mac8Address ConvertFrom(const Address& address);
 
     /**
+     * Convert to a generic Address.
+     *
+     * \return The Address value.
+     */
+    Address ConvertTo() const;
+
+    /**
      * Check that a generic Address is compatible with Mac8Address.
      *
      * \param address  Address to test.
@@ -133,12 +140,6 @@
      * \return The type value.
      */
     static uint8_t GetType();
-    /**
-     * Convert to a generic Address.
-     *
-     * \return The Address value.
-     */
-    Address ConvertTo() const;
 
     friend bool operator<(const Mac8Address& a, const Mac8Address& b);
     friend bool operator==(const Mac8Address& a, const Mac8Address& b);
@@ -158,7 +159,7 @@
 bool operator<(const Mac8Address& a, const Mac8Address& b);
 
 /**
- * Address comparison, equalit.
+ * Address comparison, equality.
  *
  * \param a First address to compare.
  * \param b Second address to compare.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/net-device-queue-interface.cc ns-3.38/src/network/utils/net-device-queue-interface.cc
--- ns-3.37/src/network/utils/net-device-queue-interface.cc	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/net-device-queue-interface.cc	2023-03-16 20:54:23.808869100 -0700
@@ -22,7 +22,6 @@
 #include "ns3/abort.h"
 #include "ns3/queue-item.h"
 #include "ns3/queue-limits.h"
-#include "ns3/simulator.h"
 #include "ns3/uinteger.h"
 
 namespace ns3
@@ -89,7 +88,7 @@
     // Request the queue disc to dequeue a packet
     if (wasStoppedByDevice && !m_wakeCallback.IsNull())
     {
-        Simulator::ScheduleNow(&NetDeviceQueue::m_wakeCallback, this);
+        m_wakeCallback();
     }
 }
 
@@ -142,7 +141,7 @@
     // Request the queue disc to dequeue a packet
     if (wasStoppedByQueueLimits && !m_wakeCallback.IsNull())
     {
-        Simulator::ScheduleNow(&NetDeviceQueue::m_wakeCallback, this);
+        m_wakeCallback();
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/net-device-queue-interface.h ns-3.38/src/network/utils/net-device-queue-interface.h
--- ns-3.37/src/network/utils/net-device-queue-interface.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/net-device-queue-interface.h	2023-03-16 20:54:23.808869100 -0700
@@ -25,6 +25,7 @@
 #include "ns3/object-factory.h"
 #include "ns3/object.h"
 #include "ns3/ptr.h"
+#include "ns3/simulator.h"
 
 #include <functional>
 #include <vector>
@@ -361,19 +362,21 @@
 NetDeviceQueue::PacketDequeued(QueueType* queue, Ptr<const typename QueueType::ItemType> item)
 {
     NS_LOG_FUNCTION(this << queue << item);
-
-    // Inform BQL
-    NotifyTransmittedBytes(item->GetSize());
-
     NS_ASSERT_MSG(m_device, "Aggregated NetDevice not set");
-    // After dequeuing a packet, if there is room for another packet we
-    // call Wake () that ensures that the queue is not stopped and restarts
-    // the queue disc if the queue was stopped
 
-    if (!queue->WouldOverflow(1, m_device->GetMtu()))
-    {
-        Wake();
-    }
+    Simulator::ScheduleNow([=]() {
+        // Inform BQL
+        NotifyTransmittedBytes(item->GetSize());
+
+        // After dequeuing a packet, if there is room for another packet we
+        // call Wake () that ensures that the queue is not stopped and restarts
+        // the queue disc if the queue was stopped
+
+        if (!queue->WouldOverflow(1, m_device->GetMtu()))
+        {
+            Wake();
+        }
+    });
 }
 
 template <typename QueueType>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/packet-socket-address.h ns-3.38/src/network/utils/packet-socket-address.h
--- ns-3.37/src/network/utils/packet-socket-address.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/packet-socket-address.h	2023-03-16 20:54:23.808869100 -0700
@@ -104,6 +104,12 @@
     static PacketSocketAddress ConvertFrom(const Address& address);
 
     /**
+     * \brief Convert an instance of this class to a polymorphic Address instance.
+     * \returns a new Address instance
+     */
+    Address ConvertTo() const;
+
+    /**
      * \param address address to test
      * \returns true if the address matches, false otherwise.
      */
@@ -116,12 +122,6 @@
      */
     static uint8_t GetType();
 
-    /**
-     * \brief Convert an instance of this class to a polymorphic Address instance.
-     * \returns a new Address instance
-     */
-    Address ConvertTo() const;
-
     uint16_t m_protocol;   //!< Protocol
     bool m_isSingleDevice; //!< True if directed to a specific outgoing NetDevice
     uint32_t m_device;     //!< Outgoing NetDevice index
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/packet-socket.h ns-3.38/src/network/utils/packet-socket.h
--- ns-3.37/src/network/utils/packet-socket.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/packet-socket.h	2023-03-16 20:54:23.808869100 -0700
@@ -109,8 +109,8 @@
      */
     void SetNode(Ptr<Node> node);
 
-    enum SocketErrno GetErrno() const override;
-    enum SocketType GetSocketType() const override;
+    SocketErrno GetErrno() const override;
+    SocketType GetSocketType() const override;
     Ptr<Node> GetNode() const override;
     /**
      * \brief Bind the socket to the NetDevice and register the protocol handler.
@@ -196,15 +196,15 @@
         STATE_CLOSED
     };
 
-    Ptr<Node> m_node;                 //!< the associated node
-    mutable enum SocketErrno m_errno; //!< Socket error code
-    bool m_shutdownSend;              //!< Send no longer allowed
-    bool m_shutdownRecv;              //!< Receive no longer allowed
-    enum State m_state;               //!< Socket state
-    uint16_t m_protocol;              //!< Socket protocol
-    bool m_isSingleDevice;            //!< Is bound to a single netDevice
-    uint32_t m_device;                //!< index of the bound NetDevice
-    Address m_destAddr;               //!< Default destination address
+    Ptr<Node> m_node;            //!< the associated node
+    mutable SocketErrno m_errno; //!< Socket error code
+    bool m_shutdownSend;         //!< Send no longer allowed
+    bool m_shutdownRecv;         //!< Receive no longer allowed
+    State m_state;               //!< Socket state
+    uint16_t m_protocol;         //!< Socket protocol
+    bool m_isSingleDevice;       //!< Is bound to a single netDevice
+    uint32_t m_device;           //!< index of the bound NetDevice
+    Address m_destAddr;          //!< Default destination address
 
     std::queue<std::pair<Ptr<Packet>, Address>> m_deliveryQueue; //!< Rx queue
     uint32_t m_rxAvailable;                                      //!< Rx queue size [Bytes]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/pcap-file.h ns-3.38/src/network/utils/pcap-file.h
--- ns-3.37/src/network/utils/pcap-file.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/pcap-file.h	2023-03-16 20:54:23.808869100 -0700
@@ -296,7 +296,7 @@
     /**
      * \brief Pcap file header
      */
-    typedef struct
+    struct PcapFileHeader
     {
         uint32_t m_magicNumber; //!< Magic number identifying this as a pcap file
         uint16_t
@@ -307,18 +307,18 @@
         uint32_t m_sigFigs; //!< Unused by pretty much everybody
         uint32_t m_snapLen; //!< Maximum length of packet data stored in records
         uint32_t m_type;    //!< Data link type of packet data
-    } PcapFileHeader;
+    };
 
     /**
      * \brief Pcap record header
      */
-    typedef struct
+    struct PcapRecordHeader
     {
         uint32_t m_tsSec;   //!< seconds part of timestamp
         uint32_t m_tsUsec;  //!< microseconds part of timestamp (nsecs for PCAP_NSEC_MAGIC)
         uint32_t m_inclLen; //!< number of octets of packet saved in file
         uint32_t m_origLen; //!< actual length of original packet
-    } PcapRecordHeader;
+    };
 
     /**
      * \brief Swap a value byte order
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/queue-size.h ns-3.38/src/network/utils/queue-size.h
--- ns-3.37/src/network/utils/queue-size.h	2022-11-01 17:37:24.695395700 -0700
+++ ns-3.38/src/network/utils/queue-size.h	2023-03-16 20:54:23.808869100 -0700
@@ -66,7 +66,7 @@
  * * A unit.
  *
  * Whitespace is allowed but not required between the numeric value and
- * multipler or unit.
+ * multiplier or unit.
  *
  * Supported multiplier prefixes:
  *
@@ -221,7 +221,8 @@
 ATTRIBUTE_HELPER_HEADER(QueueSize);
 
 /**
- * \brief Increase the queue size by a packet size
+ * Increase the queue size by a packet size, if the queue size is in bytes,
+ * or by one, otherwise.
  *
  * \param lhs queue size
  * \param rhs packet
@@ -230,7 +231,8 @@
 template <typename Item>
 QueueSize operator+(const QueueSize& lhs, const Ptr<Item>& rhs);
 /**
- * \brief Increase the queue size by a packet size
+ * Increase the queue size by a packet size, if the queue size is in bytes,
+ * or by one, otherwise.
  *
  * \param lhs packet
  * \param rhs queue size
@@ -240,6 +242,27 @@
 QueueSize operator+(const Ptr<Item>& lhs, const QueueSize& rhs);
 
 /**
+ * Decrease the queue size by a packet size, if the queue size is in bytes,
+ * or by one, otherwise.
+ *
+ * \param lhs queue size
+ * \param rhs packet
+ * \return the queue size decreased by the packet size
+ */
+template <typename Item>
+QueueSize operator-(const QueueSize& lhs, const Ptr<Item>& rhs);
+/**
+ * Decrease the queue size by a packet size, if the queue size is in bytes,
+ * or by one, otherwise.
+ *
+ * \param lhs packet
+ * \param rhs queue size
+ * \return the queue size decreased by the packet size
+ */
+template <typename Item>
+QueueSize operator-(const Ptr<Item>& lhs, const QueueSize& rhs);
+
+/**
  * Implementation of the templates declared above.
  */
 
@@ -272,6 +295,40 @@
     }
     NS_FATAL_ERROR("Unknown queue size mode");
 }
+
+template <typename Item>
+QueueSize
+operator-(const QueueSize& lhs, const Ptr<Item>& rhs)
+{
+    if (lhs.GetUnit() == QueueSizeUnit::PACKETS)
+    {
+        NS_ABORT_IF(lhs.GetValue() < 1);
+        return QueueSize(lhs.GetUnit(), lhs.GetValue() - 1);
+    }
+    if (lhs.GetUnit() == QueueSizeUnit::BYTES)
+    {
+        NS_ABORT_IF(lhs.GetValue() < rhs->GetSize());
+        return QueueSize(lhs.GetUnit(), lhs.GetValue() - rhs->GetSize());
+    }
+    NS_FATAL_ERROR("Unknown queue size mode");
+}
+
+template <typename Item>
+QueueSize
+operator-(const Ptr<Item>& lhs, const QueueSize& rhs)
+{
+    if (rhs.GetUnit() == QueueSizeUnit::PACKETS)
+    {
+        NS_ABORT_IF(rhs.GetValue() < 1);
+        return QueueSize(rhs.GetUnit(), rhs.GetValue() - 1);
+    }
+    if (rhs.GetUnit() == QueueSizeUnit::BYTES)
+    {
+        NS_ABORT_IF(rhs.GetValue() < lhs->GetSize());
+        return QueueSize(rhs.GetUnit(), rhs.GetValue() - lhs->GetSize());
+    }
+    NS_FATAL_ERROR("Unknown queue size mode");
+}
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/sll-header.h ns-3.38/src/network/utils/sll-header.h
--- ns-3.37/src/network/utils/sll-header.h	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/network/utils/sll-header.h	2023-03-16 20:54:23.812869000 -0700
@@ -101,7 +101,7 @@
     /**
      * \return Packet type
      */
-    enum PacketType GetPacketType() const;
+    PacketType GetPacketType() const;
 
     /**
      * \param type Depends on source and address of the packet
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/timestamp-tag.cc ns-3.38/src/network/utils/timestamp-tag.cc
--- ns-3.37/src/network/utils/timestamp-tag.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/network/utils/timestamp-tag.cc	2023-03-16 20:54:23.812869000 -0700
@@ -0,0 +1,90 @@
+/*
+ * 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: Joe Kopena <tjkopena@cs.drexel.edu>
+ */
+
+#include "timestamp-tag.h"
+
+#include "ns3/nstime.h"
+#include "ns3/tag-buffer.h"
+#include "ns3/tag.h"
+#include "ns3/type-id.h"
+#include "ns3/uinteger.h"
+
+namespace ns3
+{
+
+NS_OBJECT_ENSURE_REGISTERED(TimestampTag);
+
+TimestampTag::TimestampTag() = default;
+
+TimestampTag::TimestampTag(Time timestamp)
+    : m_timestamp(timestamp)
+{
+}
+
+TypeId
+TimestampTag::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::TimestampTag")
+                            .SetParent<Tag>()
+                            .SetGroupName("Network")
+                            .AddConstructor<TimestampTag>();
+    return tid;
+}
+
+TypeId
+TimestampTag::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+uint32_t
+TimestampTag::GetSerializedSize() const
+{
+    return 8;
+}
+
+void
+TimestampTag::Serialize(TagBuffer i) const
+{
+    i.WriteU64(m_timestamp.GetTimeStep());
+}
+
+void
+TimestampTag::Deserialize(TagBuffer i)
+{
+    m_timestamp = TimeStep(i.ReadU64());
+}
+
+void
+TimestampTag::Print(std::ostream& os) const
+{
+    os << "timestamp=" << m_timestamp.As(Time::S);
+}
+
+Time
+TimestampTag::GetTimestamp() const
+{
+    return m_timestamp;
+}
+
+void
+TimestampTag::SetTimestamp(Time timestamp)
+{
+    m_timestamp = timestamp;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/network/utils/timestamp-tag.h ns-3.38/src/network/utils/timestamp-tag.h
--- ns-3.37/src/network/utils/timestamp-tag.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/network/utils/timestamp-tag.h	2023-03-16 20:54:23.812869000 -0700
@@ -0,0 +1,82 @@
+/*
+ * 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: Joe Kopena <tjkopena@cs.drexel.edu>
+ */
+
+#ifndef TIMESTAMP_TAG_H
+#define TIMESTAMP_TAG_H
+
+#include "ns3/nstime.h"
+#include "ns3/tag-buffer.h"
+#include "ns3/tag.h"
+#include "ns3/type-id.h"
+
+#include <iostream>
+
+namespace ns3
+{
+
+/**
+ * Timestamp tag for associating a timestamp with a packet.
+ *
+ * It would have been more realistic to include this info in
+ * a header. Here we show how to avoid the extra overhead in
+ * a simulation.
+ */
+class TimestampTag : public Tag
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+    TypeId GetInstanceTypeId() const override;
+
+    /**
+     * \brief Construct a new TimestampTag object
+     */
+    TimestampTag();
+
+    /**
+     * \brief Construct a new TimestampTag object with the given timestamp
+     * \param timestamp The timestamp
+     */
+    TimestampTag(Time timestamp);
+
+    void Serialize(TagBuffer i) const override;
+    void Deserialize(TagBuffer i) override;
+    uint32_t GetSerializedSize() const override;
+    void Print(std::ostream& os) const override;
+
+    /**
+     * \brief Get the Timestamp object
+     * \return Time for this tag
+     */
+    Time GetTimestamp() const;
+
+    /**
+     * \brief Set the Timestamp object
+     * \param timestamp Timestamp to assign to tag
+     */
+    void SetTimestamp(Time timestamp);
+
+  private:
+    Time m_timestamp{0}; //!< Timestamp
+};
+
+} // namespace ns3
+
+#endif // TIMESTAMP_TAG_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/CMakeLists.txt ns-3.38/src/nix-vector-routing/CMakeLists.txt
--- ns-3.37/src/nix-vector-routing/CMakeLists.txt	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/CMakeLists.txt	2023-03-16 20:54:23.812869000 -0700
@@ -4,8 +4,6 @@
                model/nix-vector-routing.cc
   HEADER_FILES helper/nix-vector-helper.h
                model/nix-vector-routing.h
-  DEPRECATED_HEADER_FILES helper/ipv4-nix-vector-helper.h
-                          model/ipv4-nix-vector-routing.h
   LIBRARIES_TO_LINK ${libinternet}
   TEST_SOURCES test/nix-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/doc/nix-vector-routing.rst ns-3.38/src/nix-vector-routing/doc/nix-vector-routing.rst
--- ns-3.37/src/nix-vector-routing/doc/nix-vector-routing.rst	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/doc/nix-vector-routing.rst	2023-03-16 20:54:23.812869000 -0700
@@ -112,8 +112,8 @@
 
    Ipv4NixVectorHelper nixRouting;
    InternetStackHelper stack;
-   stack.SetRoutingHelper (nixRouting);  // has effect on the next Install ()
-   stack.Install (allNodes);             // allNodes is the NodeContainer
+   stack.SetRoutingHelper(nixRouting);  // has effect on the next Install()
+   stack.Install(allNodes);             // allNodes is the NodeContainer
 
 *  Using IPv6 Nix-Vector Routing:
 
@@ -121,8 +121,8 @@
 
    Ipv6NixVectorHelper nixRouting;
    InternetStackHelper stack;
-   stack.SetRoutingHelper (nixRouting);  // has effect on the next Install ()
-   stack.Install (allNodes);             // allNodes is the NodeContainer
+   stack.SetRoutingHelper(nixRouting);  // has effect on the next Install()
+   stack.Install(allNodes);             // allNodes is the NodeContainer
 
 .. note::
    The NixVectorHelper helper class helps to use NixVectorRouting functionality.
@@ -166,7 +166,7 @@
    n2 has 3 neighbors i.e. n1, n3 and n0. The n2 net-device for n1 -- n2
    p2p link was created before the n2 net-device for n2 -- n3 p2p link
    which was before the n2 netdevice for n0 -- n2 p2p link. This, n3
-   has neighbor-index of 01 (n1 has 00 and n0 has 10) with repect to n2.
+   has neighbor-index of 01 (n1 has 00 and n0 has 10) with respect to n2.
 
    Thus, the nix-vector for the path from n0 to n3 is 101.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/examples/nix-simple.cc ns-3.38/src/nix-vector-routing/examples/nix-simple.cc
--- ns-3.37/src/nix-vector-routing/examples/nix-simple.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/examples/nix-simple.cc	2023-03-16 20:54:23.812869000 -0700
@@ -255,7 +255,7 @@
                                       interfaces01.GetAddress(1),
                                       routingStream);
         // Trace routing tables
-        nixRouting.PrintRoutingTableAllAt(Seconds(8), routingStream);
+        Ipv4NixVectorHelper::PrintRoutingTableAllAt(Seconds(8), routingStream);
     }
     else
     {
@@ -302,7 +302,7 @@
                                       interfaces01.GetAddress(1, 1),
                                       routingStream);
         // Trace routing tables
-        nixRouting.PrintRoutingTableAllAt(Seconds(8), routingStream);
+        Ipv6NixVectorHelper::PrintRoutingTableAllAt(Seconds(8), routingStream);
     }
 
     UdpEchoServerHelper echoServer(9);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/examples/nix-simple-multi-address.cc ns-3.38/src/nix-vector-routing/examples/nix-simple-multi-address.cc
--- ns-3.37/src/nix-vector-routing/examples/nix-simple-multi-address.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/examples/nix-simple-multi-address.cc	2023-03-16 20:54:23.812869000 -0700
@@ -214,7 +214,7 @@
                                   interfaces12.GetAddress(1, 1),
                                   routingStream);
     // Trace routing tables.
-    nixRouting.PrintRoutingTableAllAt(Seconds(4), routingStream);
+    Ipv4NixVectorHelper::PrintRoutingTableAllAt(Seconds(4), routingStream);
     // Assign address5 addresses to n2 and n3.
     // n2 gets 10.2.3.1 and n3 gets 10.2.3.2 on the same interface.
     Simulator::Schedule(Seconds(5), &Ipv4AddressHelper::Assign, &address5, devices34);
@@ -223,7 +223,7 @@
     // Notice that NixCache and Ipv4RouteCache becomes empty for each node.
     // This happens because new addresses are added at t = +5s, due to which
     // existing caches get flushed.
-    nixRouting.PrintRoutingTableAllAt(Seconds(6), routingStream);
+    Ipv4NixVectorHelper::PrintRoutingTableAllAt(Seconds(6), routingStream);
 
     // Check the path from n0 to n3 (10.2.3.2).
     nixRouting.PrintRoutingPathAt(Seconds(7),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/helper/ipv4-nix-vector-helper.h ns-3.38/src/nix-vector-routing/helper/ipv4-nix-vector-helper.h
--- ns-3.37/src/nix-vector-routing/helper/ipv4-nix-vector-helper.h	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/helper/ipv4-nix-vector-helper.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2021 NITK Surathkal
- *
- * 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
- *
- * The purpose of this file is to have backwards compatibility with previous
- * IPv4 Nix Vector Routing examples and usages.
- *
- * Authors: Ameya Deshpande <ameyanrd@outlook.com>
- */
-#pragma GCC warning "NS_DEPRECATED_3_35 - Deprecated header, please use nix-vector-helper.h instead"
-#include "nix-vector-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/model/ipv4-nix-vector-routing.h ns-3.38/src/nix-vector-routing/model/ipv4-nix-vector-routing.h
--- ns-3.37/src/nix-vector-routing/model/ipv4-nix-vector-routing.h	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/model/ipv4-nix-vector-routing.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2021 NITK Surathkal
- *
- * 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
- *
- * The purpose of this file is to have backwards compatibility with previous
- * IPv4 Nix Vector Routing examples and usages.
- *
- * Authors: Ameya Deshpande <ameyanrd@outlook.com>
- */
-#pragma GCC warning                                                                                \
-    "NS_DEPRECATED_3_35 - Deprecated header, please use nix-vector-routing.h instead"
-#include "nix-vector-routing.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/model/nix-vector-routing.cc ns-3.38/src/nix-vector-routing/model/nix-vector-routing.cc
--- ns-3.37/src/nix-vector-routing/model/nix-vector-routing.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/model/nix-vector-routing.cc	2023-03-16 20:54:23.812869000 -0700
@@ -1147,7 +1147,7 @@
     parentVector.at(source->GetId()) = source;
 
     // BFS loop
-    while (greyNodeList.size() != 0)
+    while (!greyNodeList.empty())
     {
         Ptr<Node> currNode = greyNodeList.front();
         Ptr<IpL3Protocol> ip = currNode->GetObject<IpL3Protocol>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/model/nix-vector-routing.h ns-3.38/src/nix-vector-routing/model/nix-vector-routing.h
--- ns-3.37/src/nix-vector-routing/model/nix-vector-routing.h	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/model/nix-vector-routing.h	2023-03-16 20:54:23.812869000 -0700
@@ -487,7 +487,7 @@
     static bool g_isCacheDirty;
 
     /**
-     * Nix Epoch, incremented each time a flush is perfomed.
+     * Nix Epoch, incremented each time a flush is performed.
      */
     static uint32_t g_epoch;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/nix-vector-routing/test/nix-test.cc ns-3.38/src/nix-vector-routing/test/nix-test.cc
--- ns-3.37/src/nix-vector-routing/test/nix-test.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/nix-vector-routing/test/nix-test.cc	2023-03-16 20:54:23.812869000 -0700
@@ -297,8 +297,8 @@
     ifIndex = ipv6->GetInterfaceForDevice(dAdC.Get(0));
     Simulator::Schedule(Seconds(5), &Ipv6::SetDown, ipv6, ifIndex);
 
-    ipv4NixRouting.PrintRoutingTableAllAt(Seconds(7), cacheStreamv4);
-    ipv6NixRouting.PrintRoutingTableAllAt(Seconds(7), cacheStreamv6);
+    Ipv4NixVectorHelper::PrintRoutingTableAllAt(Seconds(7), cacheStreamv4);
+    Ipv6NixVectorHelper::PrintRoutingTableAllAt(Seconds(7), cacheStreamv6);
 
     SendData(Seconds(8), txSocket, Ipv4Address("10.1.3.2"));
     SendData(Seconds(8), txSocket, Ipv6Address("2001:3::200:ff:fe00:8"));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/olsr/doc/olsr.rst ns-3.38/src/olsr/doc/olsr.rst
--- ns-3.37/src/olsr/doc/olsr.rst	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/olsr/doc/olsr.rst	2023-03-16 20:54:23.812869000 -0700
@@ -57,18 +57,18 @@
   NodeContainer c:
   ...
   // Enable OLSR
-  NS_LOG_INFO ("Enabling OLSR Routing.");
+  NS_LOG_INFO("Enabling OLSR Routing.");
   OlsrHelper olsr;
 
   Ipv4StaticRoutingHelper staticRouting;
 
   Ipv4ListRoutingHelper list;
-  list.Add (staticRouting, 0);
-  list.Add (olsr, 10);
+  list.Add(staticRouting, 0);
+  list.Add(olsr, 10);
 
   InternetStackHelper internet;
-  internet.SetRoutingHelper (list);
-  internet.Install (c);
+  internet.SetRoutingHelper(list);
+  internet.Install(c);
 
 Once installed,the OLSR "main interface" can be set with the SetMainInterface()
 command. If the user does not specify a main address, the protocol will select
@@ -85,7 +85,7 @@
 Examples
 ++++++++
 
-The examples are in the ``src/olsr/examples/`` directory. However, many other examples esists in the
+The examples are in the ``src/olsr/examples/`` directory. However, many other examples exists in the
 general examples directory, e.g., ``examples/routing/manet-routing-compare.cc``.
 
 For specific examples of the HNA feature, see the examples in ``src/olsr/examples/``.
@@ -98,7 +98,7 @@
 simulation script writer can call one of three overloaded functions
 with different scope to enable OLSR: ``ns3::OlsrHelper::Install
 (NodeContainer container)``; ``ns3::OlsrHelper::Install (Ptr<Node>
-node)``; or ``ns3::OlsrHelper::InstallAll (void)``
+node)``; or ``ns3::OlsrHelper::InstallAll ()``
 
 Attributes
 ++++++++++
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/olsr/examples/olsr-hna.cc ns-3.38/src/olsr/examples/olsr-hna.cc
--- ns-3.37/src/olsr/examples/olsr-hna.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/olsr/examples/olsr-hna.cc	2023-03-16 20:54:23.812869000 -0700
@@ -138,7 +138,7 @@
     WifiHelper wifi;
     if (verbose)
     {
-        wifi.EnableLogComponents(); // Turn on all Wifi logging
+        WifiHelper::EnableLogComponents(); // Turn on all Wifi logging
     }
     wifi.SetStandard(WIFI_STANDARD_80211b);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/olsr/model/olsr-header.cc ns-3.38/src/olsr/model/olsr-header.cc
--- ns-3.37/src/olsr/model/olsr-header.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/olsr/model/olsr-header.cc	2023-03-16 20:54:23.812869000 -0700
@@ -85,7 +85,7 @@
 ///
 /// \brief Converts a number of seconds in the mantissa/exponent format to a decimal number.
 ///
-/// \param olsr_format number of seconds in mantissa/exponent format.
+/// \param olsrFormat number of seconds in mantissa/exponent format.
 /// \return the decimal number of seconds.
 ///
 double
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/olsr/model/olsr-repositories.h ns-3.38/src/olsr/model/olsr-repositories.h
--- ns-3.37/src/olsr/model/olsr-repositories.h	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/olsr/model/olsr-repositories.h	2023-03-16 20:54:23.812869000 -0700
@@ -102,7 +102,10 @@
     {
         STATUS_NOT_SYM = 0, // "not symmetric"
         STATUS_SYM = 1,     // "symmetric"
-    } status;               //!< Status of the link.
+    };
+
+    /// Status of the link.
+    Status status;
 
     /// A value between 0 and 7 specifying the node's willingness to carry traffic on behalf of
     /// other nodes.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/olsr/model/olsr-routing-protocol.cc ns-3.38/src/olsr/model/olsr-routing-protocol.cc
--- ns-3.37/src/olsr/model/olsr-routing-protocol.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/olsr/model/olsr-routing-protocol.cc	2023-03-16 20:54:23.812869000 -0700
@@ -248,12 +248,6 @@
     m_hnaRoutingTable->SetIpv4(ipv4);
 }
 
-Ptr<Ipv4>
-RoutingProtocol::GetIpv4() const
-{
-    return m_ipv4;
-}
-
 void
 RoutingProtocol::DoDispose()
 {
@@ -305,7 +299,7 @@
         *os << std::setw(16) << iter->first;
         *os << std::setw(16) << iter->second.nextAddr;
         *os << std::setw(16);
-        if (Names::FindName(m_ipv4->GetNetDevice(iter->second.interface)) != "")
+        if (!Names::FindName(m_ipv4->GetNetDevice(iter->second.interface)).empty())
         {
             *os << Names::FindName(m_ipv4->GetNetDevice(iter->second.interface));
         }
@@ -1889,7 +1883,7 @@
             mid.interfaceAddresses.push_back(addr);
         }
     }
-    if (mid.interfaceAddresses.size() == 0)
+    if (mid.interfaceAddresses.empty())
     {
         return;
     }
@@ -1927,7 +1921,7 @@
         associations.push_back(assoc);
     }
     // If there is no HNA associations to send, return without queuing the message
-    if (associations.size() == 0)
+    if (associations.empty())
     {
         return;
     }
@@ -2671,7 +2665,7 @@
 void
 RoutingProtocol::TcTimerExpire()
 {
-    if (m_state.GetMprSelectors().size() > 0)
+    if (!m_state.GetMprSelectors().empty())
     {
         SendTc();
     }
@@ -2692,7 +2686,7 @@
 void
 RoutingProtocol::HnaTimerExpire()
 {
-    if (m_state.GetAssociations().size() > 0)
+    if (!m_state.GetAssociations().empty())
     {
         SendHna();
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/olsr/model/olsr-routing-protocol.h ns-3.38/src/olsr/model/olsr-routing-protocol.h
--- ns-3.37/src/olsr/model/olsr-routing-protocol.h	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/olsr/model/olsr-routing-protocol.h	2023-03-16 20:54:23.812869000 -0700
@@ -356,11 +356,6 @@
                     ErrorCallback ecb) override;
     void SetIpv4(Ptr<Ipv4> ipv4) override;
 
-    /**
-     * \returns the ipv4 object this routing protocol is associated with
-     */
-    NS_DEPRECATED_3_34
-    virtual Ptr<Ipv4> GetIpv4() const;
     void PrintRoutingTable(Ptr<OutputStreamWrapper> stream,
                            Time::Unit unit = Time::S) const override;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/olsr/test/olsr-routing-protocol-test-suite.cc ns-3.38/src/olsr/test/olsr-routing-protocol-test-suite.cc
--- ns-3.37/src/olsr/test/olsr-routing-protocol-test-suite.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/olsr/test/olsr-routing-protocol-test-suite.cc	2023-03-16 20:54:23.812869000 -0700
@@ -81,13 +81,13 @@
      *
      * Node 1 must select only one MPR (2 or 3, doesn't matter)
      */
-    NeighborTuple neigbor;
-    neigbor.status = NeighborTuple::STATUS_SYM;
-    neigbor.willingness = OLSR_WILL_DEFAULT;
-    neigbor.neighborMainAddr = Ipv4Address("10.0.0.2");
-    protocol->m_state.InsertNeighborTuple(neigbor);
-    neigbor.neighborMainAddr = Ipv4Address("10.0.0.3");
-    protocol->m_state.InsertNeighborTuple(neigbor);
+    NeighborTuple neighbor;
+    neighbor.status = NeighborTuple::STATUS_SYM;
+    neighbor.willingness = OLSR_WILL_DEFAULT;
+    neighbor.neighborMainAddr = Ipv4Address("10.0.0.2");
+    protocol->m_state.InsertNeighborTuple(neighbor);
+    neighbor.neighborMainAddr = Ipv4Address("10.0.0.3");
+    protocol->m_state.InsertNeighborTuple(neighbor);
     TwoHopNeighborTuple tuple;
     tuple.expirationTime = Seconds(3600);
     tuple.neighborMainAddr = Ipv4Address("10.0.0.2");
@@ -149,9 +149,9 @@
      *
      * Node 1 must select nodes 2, 3 and 7 (since it is WILL_ALWAYS) as MPRs.
      */
-    neigbor.willingness = OLSR_WILL_ALWAYS;
-    neigbor.neighborMainAddr = Ipv4Address("10.0.0.7");
-    protocol->m_state.InsertNeighborTuple(neigbor);
+    neighbor.willingness = OLSR_WILL_ALWAYS;
+    neighbor.neighborMainAddr = Ipv4Address("10.0.0.7");
+    protocol->m_state.InsertNeighborTuple(neighbor);
 
     protocol->MprComputation();
     mpr = state.GetMprSet();
@@ -171,9 +171,9 @@
      * 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
      */
-    neigbor.willingness = OLSR_WILL_NEVER;
-    neigbor.neighborMainAddr = Ipv4Address("10.0.0.8");
-    protocol->m_state.InsertNeighborTuple(neigbor);
+    neighbor.willingness = OLSR_WILL_NEVER;
+    neighbor.neighborMainAddr = Ipv4Address("10.0.0.8");
+    protocol->m_state.InsertNeighborTuple(neighbor);
     tuple.neighborMainAddr = Ipv4Address("10.0.0.8");
     tuple.twoHopNeighborAddr = Ipv4Address("10.0.0.9");
     protocol->m_state.InsertTwoHopNeighborTuple(tuple);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/openflow/CMakeLists.txt ns-3.38/src/openflow/CMakeLists.txt
--- ns-3.37/src/openflow/CMakeLists.txt	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/openflow/CMakeLists.txt	2023-03-16 20:54:23.812869000 -0700
@@ -20,6 +20,8 @@
     openflow_FOUND)
    AND (NOT
         ${openflow_FOUND})
+   AND (NOT
+        ${NS3_FETCH_OPTIONAL_COMPONENTS})
 )
   message(
     ${HIGHLIGHTED_STATUS}
@@ -28,6 +30,12 @@
   return()
 endif()
 
+if(${NS3_FETCH_OPTIONAL_COMPONENTS})
+  set(openflow_LIBRARIES
+      openflow
+  )
+endif()
+
 check_include_file_cxx(
   boost/static_assert.hpp
   BOOST_STATIC_ASSERT
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/openflow/examples/openflow-switch.cc ns-3.38/src/openflow/examples/openflow-switch.cc
--- ns-3.37/src/openflow/examples/openflow-switch.cc	2022-11-01 17:37:24.699395700 -0700
+++ ns-3.38/src/openflow/examples/openflow-switch.cc	2023-03-16 20:54:23.812869000 -0700
@@ -51,21 +51,21 @@
 ns3::Time timeout = ns3::Seconds(0);
 
 bool
-SetVerbose(std::string value)
+SetVerbose(const std::string& value)
 {
     verbose = true;
     return true;
 }
 
 bool
-SetDrop(std::string value)
+SetDrop(const std::string& value)
 {
     use_drop = true;
     return true;
 }
 
 bool
-SetTimeout(std::string value)
+SetTimeout(const std::string& value)
 {
     try
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/point-to-point/doc/point-to-point.rst ns-3.38/src/point-to-point/doc/point-to-point.rst
--- ns-3.37/src/point-to-point/doc/point-to-point.rst	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/point-to-point/doc/point-to-point.rst	2023-03-16 20:54:23.812869000 -0700
@@ -79,23 +79,23 @@
 ``Nodes``) as you need on your network::
 
   NodeContainer nodes;
-  nodes.Create (2);
+  nodes.Create(2);
 
 Once you have your nodes, you need to instantiate a ``PointToPointHelper`` and
 set any attributes you may want to change. Note that since this is a
-point-to-point (as compared to a point-to-multipoint) there may only be two
+point-to-point(as compared to a point-to-multipoint) there may only be two
 nodes with associated net devices connected by a PointToPointChannel.::
 
   PointToPointHelper pointToPoint;
-  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-  pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+  pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+  pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
 
 Once the attributes are set, all that remains is to create the devices and
 install them on the required nodes, and to connect the devices together using a
 PointToPoint channel. When we create the net devices, we add them to a container
 to allow you to use them in the future. This all takes just one line of code.::
 
-  NetDeviceContainer devices = pointToPoint.Install (nodes);
+  NetDeviceContainer devices = pointToPoint.Install(nodes);
 
 PointToPoint Tracing
 ********************
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/point-to-point/examples/main-attribute-value.cc ns-3.38/src/point-to-point/examples/main-attribute-value.cc
--- ns-3.37/src/point-to-point/examples/main-attribute-value.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/point-to-point/examples/main-attribute-value.cc	2023-03-16 20:54:23.812869000 -0700
@@ -153,4 +153,6 @@
     NS_LOG_INFO("5.  txQueue limit changed through wildcarded namespace: " << limit.Get());
 
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/point-to-point/model/ppp-header.cc ns-3.38/src/point-to-point/model/ppp-header.cc
--- ns-3.37/src/point-to-point/model/ppp-header.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/point-to-point/model/ppp-header.cc	2023-03-16 20:54:23.816869300 -0700
@@ -100,7 +100,7 @@
 }
 
 uint16_t
-PppHeader::GetProtocol()
+PppHeader::GetProtocol() const
 {
     return m_protocol;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/point-to-point/model/ppp-header.h ns-3.38/src/point-to-point/model/ppp-header.h
--- ns-3.37/src/point-to-point/model/ppp-header.h	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/point-to-point/model/ppp-header.h	2023-03-16 20:54:23.816869300 -0700
@@ -93,7 +93,7 @@
      *
      * \return the protocol type being carried
      */
-    uint16_t GetProtocol();
+    uint16_t GetProtocol() const;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/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
--- ns-3.37/src/point-to-point-layout/doc/point-to-point-dumbbell.rst	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/point-to-point-layout/doc/point-to-point-dumbbell.rst	2023-03-16 20:54:23.812869000 -0700
@@ -67,18 +67,18 @@
 router nodes as shown below::
 
   PointToPointHelper p2pLeaf;
-  p2pLeaf.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
-  p2pLeaf.SetChannelAttribute ("Delay", StringValue ("2ms"));
+  p2pLeaf.SetDeviceAttribute("DataRate", StringValue("10Mbps"));
+  p2pLeaf.SetChannelAttribute("Delay", StringValue("2ms"));
 
   PointToPointHelper p2pRouter;
-  p2pRouter.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-  p2pRouter.SetChannelAttribute ("Delay", StringValue ("10ms"));
+  p2pRouter.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+  p2pRouter.SetChannelAttribute("Delay", StringValue("10ms"));
 
 After creating the ``PointToPointHelper`` objects, we need to instantiate the
 ``PointToPointDumbbellHelper``::
 
-  PointToPointDumbbellHelper dumbbell (nLeftLeaf, p2pLeaf, nRightLeaf,
-                                       p2pLeaf, p2pRouter);
+  PointToPointDumbbellHelper dumbbell(nLeftLeaf, p2pLeaf, nRightLeaf,
+                                      p2pLeaf, p2pRouter);
 
 It is also possible to create two separate ``PointToPointHelper`` objects for
 left leaf nodes and right leaf nodes. In this case, a total of three
@@ -86,21 +86,21 @@
 one for right leaf nodes and one for the router nodes as shown below::
 
   PointToPointHelper p2pLeft;
-  p2pLeft.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
-  p2pLeft.SetChannelAttribute ("Delay", StringValue ("2ms"));
+  p2pLeft.SetDeviceAttribute("DataRate", StringValue("10Mbps"));
+  p2pLeft.SetChannelAttribute("Delay", StringValue("2ms"));
 
   PointToPointHelper p2pRight;
-  p2pRight.SetDeviceAttribute ("DataRate", StringValue ("15Mbps"));
-  p2pRight.SetChannelAttribute ("Delay", StringValue ("5ms"));
+  p2pRight.SetDeviceAttribute("DataRate", StringValue("15Mbps"));
+  p2pRight.SetChannelAttribute("Delay", StringValue("5ms"));
 
   PointToPointHelper p2pRouter;
-  p2pRouter.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
-  p2pRouter.SetChannelAttribute ("Delay", StringValue ("10ms"));
+  p2pRouter.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+  p2pRouter.SetChannelAttribute("Delay", StringValue("10ms"));
 
 In this case, we instantiate the ``PointToPointDumbbellHelper`` as shown below::
 
-  PointToPointDumbbellHelper dumbbell (nLeftLeaf, p2pLeft, nRightLeaf,
-                                       p2pRight, p2pRouter);
+  PointToPointDumbbellHelper dumbbell(nLeftLeaf, p2pLeft, nRightLeaf,
+                                      p2pRight, p2pRouter);
 
 After this, we can tell the helper to position the nodes in a dumbbell layout.
 This is done to improve the visualization of the topology and can be achieved
@@ -110,33 +110,33 @@
   // uly -> Minimum Upper Left Y coordinate
   // lrx -> Maximum Lower Right X coordinate
   // lry -> Maximum Lower Right Y coordinate
-  dumbbell.BoundingBox (ulx, uly, lrx, lry);
+  dumbbell.BoundingBox(ulx, uly, lrx, lry);
 
 Next, we need to install an Internet Stack on the nodes. We have to
 instantiate a ``InternetStackHelper`` object for this, and pass it as a
 parameter to ``InstallStack`` function::
 
   InternetStackHelper stack;
-  dumbbell.InstallStack (stack);
+  dumbbell.InstallStack(stack);
 
 Subsequently, we have to assign a IPv4/IPv6 address to the nodes as shown
 below::
 
   // IPv4
   Ipv4AddressHelper leftIp, rightIp, routerIp;
-  leftIp.SetBase ("10.1.1.0", "255.255.255.0");
-  rightIp.SetBase ("10.2.1.0", "255.255.255.0");
-  routerIp.SetBase ("10.3.1.0", "255.255.255.0");
+  leftIp.SetBase("10.1.1.0", "255.255.255.0");
+  rightIp.SetBase("10.2.1.0", "255.255.255.0");
+  routerIp.SetBase("10.3.1.0", "255.255.255.0");
 
-  dumbbell.AssignIpv4Addresses (leftIp, rightIp, routerIp);
+  dumbbell.AssignIpv4Addresses(leftIp, rightIp, routerIp);
 
 For IPv6 address::
 
   // IPv6
-  Ipv6Address addrBase ("2001:1::");
-  Ipv6Prefix prefix (64)
+  Ipv6Address addrBase("2001:1::");
+  Ipv6Prefix prefix(64)
 
-  dumbbell.AssignIpv6Addresses (addrBase, prefix);
+  dumbbell.AssignIpv6Addresses(addrBase, prefix);
 
 Example
 *******
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/point-to-point-layout/model/point-to-point-dumbbell.cc ns-3.38/src/point-to-point-layout/model/point-to-point-dumbbell.cc
--- ns-3.37/src/point-to-point-layout/model/point-to-point-dumbbell.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/point-to-point-layout/model/point-to-point-dumbbell.cc	2023-03-16 20:54:23.812869000 -0700
@@ -219,7 +219,7 @@
 PointToPointDumbbellHelper::BoundingBox(double ulx,
                                         double uly, // Upper left x/y
                                         double lrx,
-                                        double lry) // Lower right x/y
+                                        double lry) const // Lower right x/y
 {
     double xDist;
     double yDist;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/point-to-point-layout/model/point-to-point-dumbbell.h ns-3.38/src/point-to-point-layout/model/point-to-point-dumbbell.h
--- ns-3.37/src/point-to-point-layout/model/point-to-point-dumbbell.h	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/point-to-point-layout/model/point-to-point-dumbbell.h	2023-03-16 20:54:23.812869000 -0700
@@ -164,7 +164,7 @@
      * \param lrx lower right x value
      * \param lry lower right y value
      */
-    void BoundingBox(double ulx, double uly, double lrx, double lry);
+    void BoundingBox(double ulx, double uly, double lrx, double lry) const;
 
   private:
     NodeContainer m_leftLeaf;                        //!< Left Leaf nodes
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/examples/jakes-propagation-model-example.cc ns-3.38/src/propagation/examples/jakes-propagation-model-example.cc
--- ns-3.37/src/propagation/examples/jakes-propagation-model-example.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/examples/jakes-propagation-model-example.cc	2023-03-16 20:54:23.816869300 -0700
@@ -28,7 +28,7 @@
 /**
  * \ingroup propagation
  * \brief Constructs a JakesPropagationlossModel and print the loss value as a function of time into
- * std::cout. Distribution and correlation statistics is compared woth a theoretical ones using R
+ * std::cout. Distribution and correlation statistics is compared with a theoretical ones using R
  * package (http://www.r-project.org/). Scripts are presented within comments.
  */
 class JakesPropagationExample
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/model/channel-condition-model.h ns-3.38/src/propagation/model/channel-condition-model.h
--- ns-3.37/src/propagation/model/channel-condition-model.h	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/model/channel-condition-model.h	2023-03-16 20:54:23.816869300 -0700
@@ -103,7 +103,7 @@
     ~ChannelCondition() override;
 
     /**
-     * Get the LosConditionValue contaning the information about the LOS/NLOS
+     * Get the LosConditionValue containing the information about the LOS/NLOS
      * state of the channel
      *
      * \return the LosConditionValue
@@ -119,7 +119,7 @@
     void SetLosCondition(LosConditionValue losCondition);
 
     /**
-     * Get the O2iConditionValue contaning the information about the O2I
+     * Get the O2iConditionValue containing the information about the O2I
      * state of the channel
      *
      * \return the O2iConditionValue
@@ -127,7 +127,7 @@
     O2iConditionValue GetO2iCondition() const;
 
     /**
-     * Set the O2iConditionValue contaning the information about the O2I
+     * Set the O2iConditionValue containing the information about the O2I
      * state of the channel
      *
      * \param o2iCondition the O2iConditionValue
@@ -135,7 +135,7 @@
     void SetO2iCondition(O2iConditionValue o2iCondition);
 
     /**
-     * Get the O2iLowHighConditionValue contaning the information about the O2I
+     * Get the O2iLowHighConditionValue containing the information about the O2I
      * building penetration losses (low or high)
      *
      * \return the O2iLowHighConditionValue
@@ -143,7 +143,7 @@
     O2iLowHighConditionValue GetO2iLowHighCondition() const;
 
     /**
-     * Set the O2iLowHighConditionValue contaning the information about the O2I
+     * Set the O2iLowHighConditionValue containing the information about the O2I
      * building penetration losses (low or high)
      *
      * \param o2iLowHighCondition the O2iLowHighConditionValue
@@ -569,8 +569,8 @@
     Time m_updatePeriod;       //!< the update period for the channel condition
 
     double m_o2iThreshold{
-        0}; //!< the threshold for determing what is the ratio of channels with O2I
-    double m_o2iLowLossThreshold{0}; //!< the threshold for determing what is the ratio of low -
+        0}; //!< the threshold for determining what is the ratio of channels with O2I
+    double m_o2iLowLossThreshold{0}; //!< the threshold for determining what is the ratio of low -
                                      //!< high O2I building penetration losses
     double m_linkO2iConditionToAntennaHeight{
         false}; //!< the indicator that determines whether the O2I/O2O condition is determined based
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/model/cost231-propagation-loss-model.cc ns-3.38/src/propagation/model/cost231-propagation-loss-model.cc
--- ns-3.37/src/propagation/model/cost231-propagation-loss-model.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/model/cost231-propagation-loss-model.cc	2023-03-16 20:54:23.816869300 -0700
@@ -86,7 +86,7 @@
 }
 
 double
-Cost231PropagationLossModel::GetShadowing()
+Cost231PropagationLossModel::GetShadowing() const
 {
     return m_shadowing;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/model/cost231-propagation-loss-model.h ns-3.38/src/propagation/model/cost231-propagation-loss-model.h
--- ns-3.37/src/propagation/model/cost231-propagation-loss-model.h	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/model/cost231-propagation-loss-model.h	2023-03-16 20:54:23.816869300 -0700
@@ -120,7 +120,7 @@
      * Get the shadowing value
      * \returns the shadowing value
      */
-    double GetShadowing();
+    double GetShadowing() const;
     /**
      * Set the shadowing value
      * \param shadowing the shadowing value
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/model/probabilistic-v2v-channel-condition-model.h ns-3.38/src/propagation/model/probabilistic-v2v-channel-condition-model.h
--- ns-3.37/src/propagation/model/probabilistic-v2v-channel-condition-model.h	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/model/probabilistic-v2v-channel-condition-model.h	2023-03-16 20:54:23.816869300 -0700
@@ -75,10 +75,7 @@
      */
     double ComputePnlos(Ptr<const MobilityModel> a, Ptr<const MobilityModel> b) const override;
 
-    enum VehicleDensity m_densityUrban
-    {
-        VehicleDensity::INVALID
-    }; //!< vehicle density
+    VehicleDensity m_densityUrban{VehicleDensity::INVALID}; //!< vehicle density
 };
 
 /**
@@ -130,10 +127,7 @@
      */
     double ComputePnlos(Ptr<const MobilityModel> a, Ptr<const MobilityModel> b) const override;
 
-    enum VehicleDensity m_densityHighway
-    {
-        VehicleDensity::INVALID
-    }; //!< vehicle density
+    VehicleDensity m_densityHighway{VehicleDensity::INVALID}; //!< vehicle density
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/test/channel-condition-model-test-suite.cc ns-3.38/src/propagation/test/channel-condition-model-test-suite.cc
--- ns-3.37/src/propagation/test/channel-condition-model-test-suite.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/test/channel-condition-model-test-suite.cc	2023-03-16 20:54:23.816869300 -0700
@@ -69,13 +69,13 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         Vector m_positionA; //!< the position of the first node
         Vector m_positionB; //!< the position of the second node
         double m_pLos;      //!< LOS probability
         TypeId m_typeId;    //!< the type ID of the channel condition model to be used
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors;          //!< array containing all the test vectors
     Ptr<ThreeGppChannelConditionModel> m_condModel; //!< the channel condition model
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/test/propagation-loss-model-test-suite.cc ns-3.38/src/propagation/test/propagation-loss-model-test-suite.cc
--- ns-3.37/src/propagation/test/propagation-loss-model-test-suite.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/test/propagation-loss-model-test-suite.cc	2023-03-16 20:54:23.816869300 -0700
@@ -53,13 +53,13 @@
     void DoRun() override;
 
     /// Test vector
-    typedef struct
+    struct TestVector
     {
         Vector m_position;  //!< Test node position
         double m_pt;        //!< Tx power [dBm]
         double m_pr;        //!< Rx power [W]
         double m_tolerance; //!< Tolerance
-    } TestVector;
+    };
 
     /// Test vectors
     TestVectors<TestVector> m_testVectors;
@@ -164,13 +164,13 @@
     void DoRun() override;
 
     /// Test vector
-    typedef struct
+    struct TestVector
     {
         Vector m_position;  //!< Test node position
         double m_pt;        //!< Tx power [dBm]
         double m_pr;        //!< Rx power [W]
         double m_tolerance; //!< Tolerance
-    } TestVector;
+    };
 
     /// Test vectors
     TestVectors<TestVector> m_testVectors;
@@ -304,13 +304,13 @@
     void DoRun() override;
 
     /// Test vector
-    typedef struct
+    struct TestVector
     {
         Vector m_position;  //!< Test node position
         double m_pt;        //!< Tx power [dBm]
         double m_pr;        //!< Rx power [W]
         double m_tolerance; //!< Tolerance
-    } TestVector;
+    };
 
     /// Test vectors
     TestVectors<TestVector> m_testVectors;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/propagation/test/three-gpp-propagation-loss-model-test-suite.cc ns-3.38/src/propagation/test/three-gpp-propagation-loss-model-test-suite.cc
--- ns-3.37/src/propagation/test/three-gpp-propagation-loss-model-test-suite.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/propagation/test/three-gpp-propagation-loss-model-test-suite.cc	2023-03-16 20:54:23.816869300 -0700
@@ -63,14 +63,14 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         double m_distance;  //!< 2D distance between UT and BS in meters
         bool m_isLos;       //!< if true LOS, if false NLOS
         double m_frequency; //!< carrier frequency in Hz
         double m_pt;        //!< transmitted power in dBm
         double m_pr;        //!< received power in dBm
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     double m_tolerance;                    //!< tolerance
@@ -226,14 +226,14 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         double m_distance;  //!< 2D distance between UT and BS in meters
         bool m_isLos;       //!< if true LOS, if false NLOS
         double m_frequency; //!< carrier frequency in Hz
         double m_pt;        //!< transmitted power in dBm
         double m_pr;        //!< received power in dBm
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     double m_tolerance;                    //!< tolerance
@@ -389,14 +389,14 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         double m_distance;  //!< 2D distance between UT and BS in meters
         bool m_isLos;       //!< if true LOS, if false NLOS
         double m_frequency; //!< carrier frequency in Hz
         double m_pt;        //!< transmitted power in dBm
         double m_pr;        //!< received power in dBm
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     double m_tolerance;                    //!< tolerance
@@ -546,14 +546,14 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         double m_distance;  //!< 2D distance between UT and BS in meters
         bool m_isLos;       //!< if true LOS, if false NLOS
         double m_frequency; //!< carrier frequency in Hz
         double m_pt;        //!< transmitted power in dBm
         double m_pr;        //!< received power in dBm
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     double m_tolerance;                    //!< tolerance
@@ -720,14 +720,14 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         double m_distance;  //!< 2D distance between UT and BS in meters
         bool m_isLos;       //!< if true LOS/NLOSv, if false NLOS
         double m_frequency; //!< carrier frequency in Hz
         double m_pt;        //!< transmitted power in dBm
         double m_pr;        //!< received power in dBm
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     double m_tolerance;                    //!< tolerance
@@ -879,14 +879,14 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         double m_distance;  //!< 2D distance between UT and BS in meters
         bool m_isLos;       //!< if true LOS/NLOSv, if false NLOS
         double m_frequency; //!< carrier frequency in Hz
         double m_pt;        //!< transmitted power in dBm
         double m_pr;        //!< received power in dBm
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
     double m_tolerance;                    //!< tolerance
@@ -1020,7 +1020,7 @@
      *        to be used
      * \param hBs the BS height in meters
      * \param hUt the UT height in meters
-     * \param distance the intial distance between the BS and the UT
+     * \param distance the initial distance between the BS and the UT
      * \param shadowingEnabled true if shadowging must be enabled
      */
     void RunTest(uint16_t testNum,
@@ -1047,7 +1047,7 @@
     /**
      * Struct containing the parameters for each test
      */
-    typedef struct
+    struct TestVector
     {
         std::string m_propagationLossModelType; //!< the propagation loss model type id
         double m_hBs;                           //!< the BS height in meters
@@ -1057,7 +1057,7 @@
                                   //!< case in dB
         double m_shadowingStdNlos; //!< the standard deviation of the shadowing component in the
                                    //!< NLOS case in dB
-    } TestVector;
+    };
 
     TestVectors<TestVector> m_testVectors;         //!< array containing all the test vectors
     Ptr<ThreeGppPropagationLossModel> m_lossModel; //!< the propagation loss model
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/doc/sixlowpan.rst ns-3.38/src/sixlowpan/doc/sixlowpan.rst
--- ns-3.37/src/sixlowpan/doc/sixlowpan.rst	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/sixlowpan/doc/sixlowpan.rst	2023-03-16 20:54:23.816869300 -0700
@@ -144,7 +144,7 @@
 what the IPv6 address of a node is.
 
 Due to this, the nodes configured to use IPHC will drop the packets compressed with HC1
-and viceversa. The drop is logged in the drop trace as ``DROP_DISALLOWED_COMPRESSION``.
+and vice-versa. The drop is logged in the drop trace as ``DROP_DISALLOWED_COMPRESSION``.
 
 
 Using 6LoWPAN with IPv4 (or other L3 protocols)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc ns-3.38/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc
--- ns-3.37/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc	2022-11-01 17:37:24.703395600 -0700
+++ ns-3.38/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc	2023-03-16 20:54:23.816869300 -0700
@@ -31,14 +31,14 @@
 using namespace ns3;
 
 static void
-dataSentMacConfirm(McpsDataConfirmParams params)
+DataSentMacConfirm(Ptr<LrWpanNetDevice> device, McpsDataConfirmParams params)
 {
     // In the case of transmissions with the Ack flag activated, the transaction is only
     // successful if the Ack was received.
     if (params.m_status == LrWpanMcpsDataConfirmStatus::IEEE_802_15_4_SUCCESS)
     {
-        NS_LOG_UNCOND("**********" << Simulator::Now().As(Time::S)
-                                   << " | Transmission successfully sent");
+        std::cout << Simulator::Now().As(Time::S) << " | Node " << device->GetNode()->GetId()
+                  << " | Transmission successfully sent\n";
     }
 }
 
@@ -53,12 +53,11 @@
 
     if (verbose)
     {
-        LogComponentEnableAll(LOG_PREFIX_TIME);
-        LogComponentEnableAll(LOG_PREFIX_FUNC);
+        LogComponentEnableAll(LogLevel(LOG_PREFIX_TIME | LOG_PREFIX_FUNC | LOG_PREFIX_NODE));
         LogComponentEnable("LrWpanMac", LOG_LEVEL_INFO);
         LogComponentEnable("LrWpanCsmaCa", LOG_LEVEL_INFO);
         LogComponentEnable("LrWpanHelper", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_INFO);
+        LogComponentEnable("Ping", LOG_LEVEL_INFO);
     }
 
     NodeContainer nodes;
@@ -89,15 +88,13 @@
     Ptr<LrWpanNetDevice> dev1 = lrwpanDevices.Get(0)->GetObject<LrWpanNetDevice>();
     Ptr<LrWpanNetDevice> dev2 = lrwpanDevices.Get(1)->GetObject<LrWpanNetDevice>();
 
-    McpsDataConfirmCallback cb1;
-    cb1 = MakeCallback(&dataSentMacConfirm);
-    dev1->GetMac()->SetMcpsDataConfirmCallback(cb1);
-    dev2->GetMac()->SetMcpsDataConfirmCallback(cb1);
+    dev1->GetMac()->SetMcpsDataConfirmCallback(MakeBoundCallback(&DataSentMacConfirm, dev1));
 
-    // Fake PAN association, coordinator assignment, short address assignment and initialization
+    dev2->GetMac()->SetMcpsDataConfirmCallback(MakeBoundCallback(&DataSentMacConfirm, dev2));
+
+    // Manual PAN association, coordinator assignment, short address assignment and initialization
     // of beacon-enabled mode in 802.15.4-2011.
-    // This is needed because the lr-wpan module does not provide (yet)
-    // a full PAN association procedure.
+    // Association using the MAC functions can also be used instead of a manual association.
 
     // AssociateToBeaconPan (devices, PAN ID, Coordinator Address, Beacon Order, Superframe Order)
 
@@ -113,7 +110,22 @@
     //                            Beacon Interval = 251.65 secs
     //   |-----------------------------------------------------------------------------------------|
 
-    lrWpanHelper.AssociateToBeaconPan(lrwpanDevices, 0, Mac16Address("00:01"), 14, 13);
+    // Manually set an associated PAN, Pan id = 1 the first device (dev1) is used as coordinator
+    lrWpanHelper.CreateAssociatedPan(lrwpanDevices, 5);
+
+    // Start the beacon mode from the MAC layer of the coordinator (dev1)
+    MlmeStartRequestParams params;
+    params.m_panCoor = true;
+    params.m_PanId = 5;
+    params.m_bcnOrd = 14;
+    params.m_sfrmOrd = 13;
+    params.m_logCh = 11;
+
+    Simulator::ScheduleWithContext(dev1->GetNode()->GetId(),
+                                   Seconds(0),
+                                   &LrWpanMac::MlmeStartRequest,
+                                   dev1->GetMac(),
+                                   params);
 
     InternetStackHelper internetv6;
     internetv6.Install(nodes);
@@ -129,30 +141,29 @@
     // Send ping packets after the 2nd second of the simulation during the
     // first 8 seconds of the CAP in the incoming superframe
 
-    uint32_t packetSize = 10;
+    uint32_t packetSize = 16;
     uint32_t maxPacketCount = 5;
     Time interPacketInterval = Seconds(1);
-    Ping6Helper ping6;
+    PingHelper ping(deviceInterfaces.GetAddress(1, 1));
 
-    ping6.SetLocal(deviceInterfaces.GetAddress(0, 1));
-    ping6.SetRemote(deviceInterfaces.GetAddress(1, 1));
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(nodes.Get(0));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Interval", TimeValue(interPacketInterval));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(nodes.Get(0));
 
     apps.Start(Seconds(2.0));
-    apps.Stop(Seconds(10.0));
+    apps.Stop(Seconds(7.0));
 
     AsciiTraceHelper ascii;
     lrWpanHelper.EnableAsciiAll(ascii.CreateFileStream("Ping-6LoW-lr-wpan-beacon.tr"));
     lrWpanHelper.EnablePcapAll(std::string("Ping-6LoW-lr-wpan-beacon"), true);
 
-    Simulator::Stop(Seconds(600));
+    Simulator::Stop(Seconds(7));
 
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
 
 // BO/SO values to time equivalence
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/examples/example-ping-lr-wpan.cc ns-3.38/src/sixlowpan/examples/example-ping-lr-wpan.cc
--- ns-3.37/src/sixlowpan/examples/example-ping-lr-wpan.cc	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/sixlowpan/examples/example-ping-lr-wpan.cc	2023-03-16 20:54:23.816869300 -0700
@@ -49,7 +49,7 @@
 
     if (verbose)
     {
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
         LogComponentEnable("LrWpanMac", LOG_LEVEL_ALL);
         LogComponentEnable("LrWpanPhy", LOG_LEVEL_ALL);
         LogComponentEnable("LrWpanNetDevice", LOG_LEVEL_ALL);
@@ -81,10 +81,9 @@
     // Add and install the LrWpanNetDevice for each node
     NetDeviceContainer lrwpanDevices = lrWpanHelper.Install(nodes);
 
-    // Fake PAN association and short address assignment.
-    // This is needed because the lr-wpan module does not provide (yet)
-    // a full PAN association procedure.
-    lrWpanHelper.AssociateToPan(lrwpanDevices, 1);
+    // Manual PAN association and extended and short address assignment.
+    // Association using the MAC functions can also be used instead of this step.
+    lrWpanHelper.CreateAssociatedPan(lrwpanDevices, 1);
 
     InternetStackHelper internetv6;
     internetv6.Install(nodes);
@@ -107,21 +106,18 @@
                   << deviceInterfaces.GetAddress(1, 1) << std::endl;
     }
 
-    uint32_t packetSize = 10;
+    uint32_t packetSize = 16;
     uint32_t maxPacketCount = 5;
     Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
+    PingHelper ping(deviceInterfaces.GetAddress(1, 1));
 
-    ping6.SetLocal(deviceInterfaces.GetAddress(0, 1));
-    ping6.SetRemote(deviceInterfaces.GetAddress(1, 1));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Interval", TimeValue(interPacketInterval));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(nodes.Get(0));
 
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(nodes.Get(0));
-
-    apps.Start(Seconds(1.0));
-    apps.Stop(Seconds(10.0));
+    apps.Start(Seconds(2.0));
+    apps.Stop(Seconds(20.0));
 
     if (!disableAsciiTrace)
     {
@@ -138,8 +134,10 @@
         Ipv6RoutingHelper::PrintNeighborCacheAllAt(Seconds(9), routingStream);
     }
 
-    Simulator::Stop(Seconds(10));
+    Simulator::Stop(Seconds(20));
 
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/examples/example-ping-lr-wpan-mesh-under.cc ns-3.38/src/sixlowpan/examples/example-ping-lr-wpan-mesh-under.cc
--- ns-3.37/src/sixlowpan/examples/example-ping-lr-wpan-mesh-under.cc	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/sixlowpan/examples/example-ping-lr-wpan-mesh-under.cc	2023-03-16 20:54:23.816869300 -0700
@@ -44,7 +44,7 @@
 
     if (verbose)
     {
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
         LogComponentEnable("LrWpanMac", LOG_LEVEL_ALL);
         LogComponentEnable("LrWpanPhy", LOG_LEVEL_ALL);
         LogComponentEnable("LrWpanNetDevice", LOG_LEVEL_ALL);
@@ -81,10 +81,9 @@
     // Add and install the LrWpanNetDevice for each node
     NetDeviceContainer lrwpanDevices = lrWpanHelper.Install(wsnNodes);
 
-    // Fake PAN association and short address assignment.
-    // This is needed because the lr-wpan module does not provide (yet)
-    // a full PAN association procedure.
-    lrWpanHelper.AssociateToPan(lrwpanDevices, 0);
+    // Manual PAN association and extended and short address assignment.
+    // Association using the MAC functions can also be used instead of this step.
+    lrWpanHelper.CreateAssociatedPan(lrwpanDevices, 0);
 
     InternetStackHelper internetv6;
     internetv6.Install(wsnNodes);
@@ -116,21 +115,18 @@
         dev->SetAttribute("MeshUnderRadius", UintegerValue(10));
     }
 
-    uint32_t packetSize = 10;
+    uint32_t packetSize = 16;
     uint32_t maxPacketCount = 5;
     Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
+    PingHelper ping(wiredDeviceInterfaces.GetAddress(0, 1));
 
-    ping6.SetLocal(wsnDeviceInterfaces.GetAddress(nWsnNodes - 1, 1));
-    ping6.SetRemote(wiredDeviceInterfaces.GetAddress(0, 1));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Interval", TimeValue(interPacketInterval));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(wsnNodes.Get(nWsnNodes - 1));
 
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(wsnNodes.Get(nWsnNodes - 1));
-
-    apps.Start(Seconds(1.0));
-    apps.Stop(Seconds(10.0));
+    apps.Start(Seconds(2.0));
+    apps.Stop(Seconds(20.0));
 
     AsciiTraceHelper ascii;
     lrWpanHelper.EnableAsciiAll(ascii.CreateFileStream("Ping-6LoW-lr-wpan-meshunder-lr-wpan.tr"));
@@ -139,8 +135,10 @@
     csmaHelper.EnableAsciiAll(ascii.CreateFileStream("Ping-6LoW-lr-wpan-meshunder-csma.tr"));
     csmaHelper.EnablePcapAll(std::string("Ping-6LoW-lr-wpan-meshunder-csma"), true);
 
-    Simulator::Stop(Seconds(10));
+    Simulator::Stop(Seconds(20));
 
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/examples/example-sixlowpan.cc ns-3.38/src/sixlowpan/examples/example-sixlowpan.cc
--- ns-3.37/src/sixlowpan/examples/example-sixlowpan.cc	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/sixlowpan/examples/example-sixlowpan.cc	2023-03-16 20:54:23.816869300 -0700
@@ -60,7 +60,7 @@
     if (verbose)
     {
         LogComponentEnable("SixLowPanNetDevice", LOG_LEVEL_ALL);
-        LogComponentEnable("Ping6Application", LOG_LEVEL_ALL);
+        LogComponentEnable("Ping", LOG_LEVEL_ALL);
     }
 
     Packet::EnablePrinting();
@@ -107,15 +107,12 @@
     uint32_t packetSize = 200;
     uint32_t maxPacketCount = 50;
     Time interPacketInterval = Seconds(1.);
-    Ping6Helper ping6;
+    PingHelper ping(i2.GetAddress(1, 1));
 
-    ping6.SetLocal(i1.GetAddress(0, 1));
-    ping6.SetRemote(i2.GetAddress(1, 1));
-
-    ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount));
-    ping6.SetAttribute("Interval", TimeValue(interPacketInterval));
-    ping6.SetAttribute("PacketSize", UintegerValue(packetSize));
-    ApplicationContainer apps = ping6.Install(net1.Get(0));
+    ping.SetAttribute("Count", UintegerValue(maxPacketCount));
+    ping.SetAttribute("Interval", TimeValue(interPacketInterval));
+    ping.SetAttribute("Size", UintegerValue(packetSize));
+    ApplicationContainer apps = ping.Install(net1.Get(0));
 
     apps.Start(Seconds(5.0));
     apps.Stop(Seconds(15.0));
@@ -129,4 +126,6 @@
     Simulator::Run();
     Simulator::Destroy();
     NS_LOG_INFO("Done.");
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/model/sixlowpan-net-device.cc ns-3.38/src/sixlowpan/model/sixlowpan-net-device.cc
--- ns-3.37/src/sixlowpan/model/sixlowpan-net-device.cc	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/sixlowpan/model/sixlowpan-net-device.cc	2023-03-16 20:54:23.816869300 -0700
@@ -2356,7 +2356,7 @@
     NS_ASSERT_MSG(l2Mtu > frag1Hdr.GetSerializedSize(),
                   "6LoWPAN: can not fragment, 6LoWPAN headers are bigger than MTU");
 
-    // All the headers are substracted to get remaining units for data
+    // All the headers are subtracted to get remaining units for data
     size = l2Mtu - frag1Hdr.GetSerializedSize() - compressedHeaderSize - extraHdrSize;
     size -= size % 8;
     size += compressedHeaderSize;
@@ -2589,7 +2589,7 @@
 {
     NS_LOG_FUNCTION(this);
 
-    bool ret = m_fragments.size() > 0;
+    bool ret = !m_fragments.empty();
     uint16_t lastEndOffset = 0;
 
     if (ret)
@@ -2877,7 +2877,7 @@
         {
             if (address.HasPrefix(context.contextPrefix))
             {
-                NS_LOG_LOGIC("Fount context "
+                NS_LOG_LOGIC("Found context "
                              << +contextId << " "
                              << Ipv6Address::GetOnes().CombinePrefix(context.contextPrefix)
                              << context.contextPrefix << " matching");
@@ -2920,7 +2920,7 @@
                     addressBytes[9] == contextBytes[5] && addressBytes[10] == contextBytes[6] &&
                     addressBytes[11] == contextBytes[7])
                 {
-                    NS_LOG_LOGIC("Fount context "
+                    NS_LOG_LOGIC("Found context "
                                  << +contextId << " "
                                  << Ipv6Address::GetOnes().CombinePrefix(context.contextPrefix)
                                  << context.contextPrefix << " matching");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/model/sixlowpan-net-device.h ns-3.38/src/sixlowpan/model/sixlowpan-net-device.h
--- ns-3.37/src/sixlowpan/model/sixlowpan-net-device.h	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/sixlowpan/model/sixlowpan-net-device.h	2023-03-16 20:54:23.816869300 -0700
@@ -80,7 +80,7 @@
         DROP_FRAGMENT_TIMEOUT = 1,           //!< Fragment timeout exceeded
         DROP_FRAGMENT_BUFFER_FULL,           //!< Fragment buffer size exceeded
         DROP_UNKNOWN_EXTENSION,              //!< Unsupported compression kind
-        DROP_DISALLOWED_COMPRESSION,         //!< HC1 while in IPHC mode or viceversa
+        DROP_DISALLOWED_COMPRESSION,         //!< HC1 while in IPHC mode or vice-versa
         DROP_SATETFUL_DECOMPRESSION_PROBLEM, //!< Decompression failed due to missing or expired
                                              //!< context
     };
@@ -178,7 +178,7 @@
                                        uint32_t ifindex);
 
     /**
-     * TracedCallback signature fo packet drop events
+     * TracedCallback signature for packet drop events
      *
      * \param [in] reason The reason for the drop.
      * \param [in] packet The packet.
@@ -382,7 +382,7 @@
      * \param [in] packet The packet to be compressed.
      * \param [in] src The MAC source address.
      * \param [in] dst The MAC destination address.
-     * \return true if the packet can not be decompressed due to wrong context informations.
+     * \return true if the packet can not be decompressed due to wrong context information.
      */
     bool DecompressLowPanIphc(Ptr<Packet> packet, const Address& src, const Address& dst);
 
@@ -407,7 +407,7 @@
      * \param [in] srcAddress The IPv6 source address.
      * \param [in] dstAddress The IPv6 destination address.
      * \return A std::pair containing the decompressed header type and a flag - true if the packet
-     * can not be decompressed due to wrong context informations.
+     * can not be decompressed due to wrong context information.
      */
     std::pair<uint8_t, bool> DecompressLowPanNhc(Ptr<Packet> packet,
                                                  const Address& src,
@@ -641,7 +641,7 @@
     Ptr<UniformRandomVariable> m_rng; //!< Rng for the fragments tag.
 
     /**
-     * Structure holding the informations for a context (used in compression and decompression)
+     * Structure holding the information for a context (used in compression and decompression)
      */
     struct ContextEntry
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/test/example-ping-lr-wpan.reflog ns-3.38/src/sixlowpan/test/example-ping-lr-wpan.reflog
--- ns-3.37/src/sixlowpan/test/example-ping-lr-wpan.reflog	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/sixlowpan/test/example-ping-lr-wpan.reflog	2023-03-16 20:54:23.816869300 -0700
@@ -1,6 +1,16 @@
 Device 0: pseudo-Mac-48 02:00:00:00:00:01, IPv6 Address 2001:2::ff:fe00:1
 Device 1: pseudo-Mac-48 02:00:00:00:00:02, IPv6 Address 2001:2::ff:fe00:2
+PING 2001:2::ff:fe00:2 - 16 bytes of data; 64 bytes including ICMP and IPv6 headers.
+24 bytes from (2001:2::ff:fe00:2): icmp_seq=0 ttl=64 time=21.536 ms
+24 bytes from (2001:2::ff:fe00:2): icmp_seq=1 ttl=64 time=9.312 ms
+24 bytes from (2001:2::ff:fe00:2): icmp_seq=2 ttl=64 time=8.992 ms
+24 bytes from (2001:2::ff:fe00:2): icmp_seq=3 ttl=64 time=9.952 ms
+24 bytes from (2001:2::ff:fe00:2): icmp_seq=4 ttl=64 time=6.752 ms
+
+--- 2001:2::ff:fe00:2 ping statistics ---
+5 packets transmitted, 5 received, 0% packet loss, time 4006ms
+rtt min/avg/max/mdev = 6/10.6/21/5.941 ms
 NDISC Cache of node 0 at time +9s
 2001:2::ff:fe00:2 dev 2 lladdr 00-06-02:00:00:00:00:02 REACHABLE
 NDISC Cache of node 1 at time +9s
-2001:2::ff:fe00:1 dev 2 lladdr 00-06-02:00:00:00:00:01 REACHABLE
+2001:2::ff:fe00:1 dev 2 lladdr 00-06-02:00:00:00:00:01 REACHABLE
\ No newline at end of file
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc ns-3.38/src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc
--- ns-3.37/src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc	2023-03-16 20:54:23.820869200 -0700
@@ -50,12 +50,12 @@
     /**
      * \brief Structure to hold the Rx/Tx packets.
      */
-    typedef struct
+    struct Data
     {
         Ptr<Packet> packet; /**< Packet data */
         Address src;        /**< Source address */
         Address dst;        /**< Destination address */
-    } Data;
+    };
 
     std::vector<Data> m_txPackets; //!< Transmitted packets
     std::vector<Data> m_rxPackets; //!< Received packets
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/CMakeLists.txt ns-3.38/src/spectrum/CMakeLists.txt
--- ns-3.37/src/spectrum/CMakeLists.txt	2022-11-01 17:37:24.707395800 -0700
+++ ns-3.38/src/spectrum/CMakeLists.txt	2023-03-16 20:54:23.820869200 -0700
@@ -12,6 +12,7 @@
     model/half-duplex-ideal-phy.cc
     model/matrix-based-channel-model.cc
     model/microwave-oven-spectrum-value-helper.cc
+    model/two-ray-spectrum-propagation-loss-model.cc
     model/multi-model-spectrum-channel.cc
     model/non-communicating-net-device.cc
     model/single-model-spectrum-channel.cc
@@ -50,6 +51,7 @@
     model/half-duplex-ideal-phy.h
     model/matrix-based-channel-model.h
     model/microwave-oven-spectrum-value-helper.h
+    model/two-ray-spectrum-propagation-loss-model.h
     model/multi-model-spectrum-channel.h
     model/non-communicating-net-device.h
     model/single-model-spectrum-channel.h
@@ -82,6 +84,7 @@
   LIBRARIES_TO_LINK ${libpropagation}
                     ${libantenna}
   TEST_SOURCES
+    test/two-ray-splm-test-suite.cc
     test/spectrum-ideal-phy-test.cc
     test/spectrum-interference-test.cc
     test/spectrum-value-test.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/doc/spectrum.rst ns-3.38/src/spectrum/doc/spectrum.rst
--- ns-3.37/src/spectrum/doc/spectrum.rst	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/doc/spectrum.rst	2023-03-16 20:54:23.820869200 -0700
@@ -177,7 +177,7 @@
    interference model (implemented in ``SpectrumInterference``)
    together with an error model based on Shannon capacity (described
    in [Baldo2009Spectrum]_ and implemented in ``SpectrumErrorModel``. This PHY
-   uses the ``GenericPhy`` interface. Its addditional custom signal
+   uses the ``GenericPhy`` interface. Its additional custom signal
    parameters are defined in ``HalfDuplexIdealPhySignalParameters``.
 
  * ``WifiSpectrumValueHelper`` is an helper object that makes it easy
@@ -314,7 +314,7 @@
  * Both ``SingleModelSpectrumChannel`` and
    ``MultiModelSpectrumChannel`` provide a trace source called
    ``PathLoss`` which is fired whenever a new path loss value is
-   calclulated. **Note**: only single-frequency path loss is accounted
+   calculated. **Note**: only single-frequency path loss is accounted
    for, see the attribute description.
 
  * The example implementations described in :ref:`sec-example-model-implementations` also provide some trace sources.
@@ -832,3 +832,216 @@
    Sandra Lagen, Michele Zorzi. "Implementation of a Spatial Channel Model for
    ns-3". Submitted to the Workshop on ns-3 (WNS3 '20). 2020.
    Available: https://arxiv.org/abs/2002.09341
+
+
+Two-Ray fading model
+====================
+The model aims to provide a performance-oriented alternative to the 3GPP TR 38.901
+framework [TR38901]_ which is implemented in the ``ThreeGppSpectrumPropagationLossModel`` and
+``ThreeGppChannelModel`` classes and whose implementation is described in [Zugno2020]_.
+The overall design follows the general approach of [Polese2018]_, with aim of providing
+the means for computing a 3GPP TR 38.901-like end-to-end channel gain by combining
+several statistical terms. The frequency range of applicability is the same as
+that of [TR38901]_, i.e., 0.5 - 100 GHz.
+
+Use-cases
+#########
+The use-cases for this channel model comprise large-scale MIMO simulations involving a high
+number of nodes (100+), such as multi-cell LTE and 5G deployments in dense urban areas, for which
+the full 3GPP TR 38.901 does not represent a viable option.
+
+Implementation - ``TwoRaySpectrumPropagationLossModel``
+#######################################################
+The computation of the channel gain is taken care of by the ``TwoRaySpectrumPropagationLossModel``
+class. In particular, the latter samples a statistical term which combines:
+
+* The array and beamforming gain, computed as outlined in [Rebato2018]_ using the
+  ``CalcBeamformingGain`` function. This term supports the presence of multiple
+  antenna elements both at the transmitter and at the receiver and arbitrary antenna
+  radiation patterns. Specifically, the array gain is compute as:
+
+.. math::
+
+  G_{\mathrm{AA}}(\theta, \varphi)=\left|\boldsymbol{a}^{\mathrm{T}}(\theta, \varphi)
+  \boldsymbol{w}\left(\theta_0, \varphi_0\right)\right|^2=\left|\mathrm{AF}_{\mathrm{v}}(\theta,
+  \varphi)\right|^2\left|\mathrm{AF}_{\mathrm{h}}(\theta, \varphi)\right|^2 G(\theta, \varphi),
+..
+
+where:
+
+.. math::
+
+  \operatorname{AF}_{\mathrm{v}}(\theta, \varphi)=\frac{1}{\sqrt{N_{\mathrm{v}}}}
+  \sum_{m=0}^{N_{\mathrm{v}}-1} e^{j k d_{\mathrm{v}} m\left(\cos \theta-\cos \theta_0\right)}
+..
+
+and:
+
+.. math::
+
+  \operatorname{AF}_{\mathrm{h}}(\theta, \varphi)=\frac{1}{\sqrt{N_{\mathrm{h}}}}
+  \sum_{n=0}^{N_{\mathrm{h}}-1} e^{j k d_{\mathrm{h}} n\left(\sin \theta
+  \sin \varphi-\sin \theta_0 \sin \varphi_0\right)}
+..
+
+In turn, :math:`N_h`, :math:`N_v` are the number of horizontal and vertical antenna
+elements respectively, :math:`d_h`, :math:`d_v` are the element spacing in the
+horizontal and vertical direction respectively. The figures below depict the resulting
+array radiation pattern versus the relative azimuth of transmitter and receiver, for antenna
+arrays featuring 3GPP TR 38.901 (``ThreeGppAntennaModel``, top) and isotropic
+(``IsotropicAntennaModel``, bottom) antenna elements, respectively.
+These figures match the corresponding plots of [Asplund]_.
+
+.. _fig-two-ray-spectrum-loss-model-3gpp-radiation-pattern:
+
+.. figure:: figures/two-ray-spectrum-loss-model-3gpp-radiation-pattern.*
+   :width: 400
+   :align: center
+
+   Radiation pattern produced by the ``CalcBeamformingGain`` method when using
+   antenna arrays featuring ``ThreeGppAntennaModel`` antenna elements, for various
+   Uniform Planar Array (UPA) configurations.
+
+.. _fig-two-ray-spectrum-loss-model-iso-radiation-pattern:
+
+.. figure:: figures/two-ray-spectrum-loss-model-iso-radiation-pattern.*
+   :width: 400
+   :align: center
+
+   Radiation pattern produced by the ``CalcBeamformingGain`` method when using
+   antenna arrays featuring ``IsotropicAntennaModel`` antenna elements, for various
+   Uniform Planar Array (UPA) configurations.
+
+Whenever the link is in NLOS, a penalty factor is introduced, to account for beam
+misalignment due to the lack of a dominant multipath component [Kulkarni]_.
+
+
+* A fast fading term, sampled using the Fluctuating Two Ray (FTR) model distribution [Romero]_.
+  The latter is a fading model which is more general than typical ones, taking into account two
+  dominant specular components and a mixture of scattered paths. As, a consequence it has
+  been shown to provide a better fit to fading phenomena at mmWaves. The model parameters
+  are automatically picked once the simulation scenario is set, using a lookup table which
+  associates the simulation parameters (such as carrier frequency and LOS condition) to the
+  FTR parameters providing the best fit to the corresponding TR 38.901 channel statistics.
+  As a consequence, this channel model can be used for all the frequencies which are
+  supported by the 38.901 model, i.e., 0.5-100 GHz. The calibration has been done by
+  first obtaining the statistics of the channel gain due to the small-scale fading in
+  the 3GPP model, using an ad hoc simulation script
+  (``src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc``). Then, this information
+  has been used as a reference to estimate the FTR parameters yielding the closest
+  (in a goodness-of-fit sense) fading realizations, using a custom Python script
+  (``src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py``).
+
+**Note:**
+
+  * To then obtain a full channel model characterization, the model is intended to be
+    used in conjunction of the path loss and shadowing capability provided by the
+    ``ThreeGppPropagationLossModel`` class.
+    Indeed, the goal of this model is to provide channel realizations which are as close
+    as possible to ones of [TR38901]_, but at a fraction of the complexity. Since the
+    path loss and shadowing terms are not computationally demanding anyway, the ones of
+    [Zugno2020]_ have been kept;
+
+  * Currently, the value of NLoS beamforming factor penalty factor is taken from
+    the preliminary work of [Kulkarni]_ and it is scenario-independent; As future
+    work, the possibility of using scenario-dependent penalty factors will be
+    investigated.
+
+Calibration
+###########
+The purpose of the calibration procedures is to compute offline a look-up table which
+associates the FTR fading model parameters with the simulation parameters.
+In particular, the [TR38901]_ fading distributions depend on:
+
+* The scenario (RMa, UMa, UMi-StreetCanyon, InH-OfficeOpen, InH-OfficeMixed);
+
+* The LOS condition (LoS/NLoS); and
+
+* The carrier frequency.
+
+As a consequence, the calibration output is a map which associates LoS condition and
+scenario to a list of carrier frequency-FTR parameters values. The latter represent the
+FTR parameters yielding channel realizations which exhibit the closest statistics to [TR38901]_.
+
+The actual calibration is a two-step procedure which:
+
+1. First generates reference channel gain curves using the
+``src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc`` simulation script.
+Specifically, the script samples ``numRealizations`` channel realizations and computes for each of them
+the end-to-end channel gain by setting the speed of the TX and RX pair to :math:`0`, disabling the shadowing
+and fixing the LOS condition. In such a way, any variation around the mean is due to the small-scale fading only.
+The channel gain samples are produced, and returned on output conditioned on the value of
+``enableOutput``, for each combination of LoS condition, channel model scenario and carrier frequency. The
+latter cover the whole [TR38901]_ frequency range of 0.5 - 100 GHz with a relatively sparse resolution
+(500 MHz), since the dependency of the fading distribution with respect to the carrier frequency is actually
+relatively weak.
+
+.. _fig-three-gpp-gain-reference-gain-vs-fc:
+
+.. figure:: figures/three-gpp-gain-reference-gain-vs-fc.*
+   :width: 400
+   :align: center
+
+   Empirical CDF of the reference channel gains obtained using the  ``three-gpp-two-ray-channel-calibration``
+   simulation script when keeping a fixed LoS condition and channel scenario and varying the carrier
+   frequency only.
+
+2. Then, the output of the above script is parsed by the ``two-ray-to-three-gpp-ch-calibration.py``
+Python companion script. In particular, reference ECDFs are obtained from the channel gains sampled using the
+model of [TR38901]_. In turn, the reference ECDFs (one for each LoS condition, channel model scenario
+and carrier frequency combination) are compared to FTR distributions ECDFs obtained using different values
+of the parameters. Finally, the parameters which provide the best fit (in a goodness-of-fit sense) for
+the specific scenario, LOS condition and carrier frequency are found. The parameters to test are picked initially
+by performing an exhaustive search within a discrete grid of possible values, and then by iteratively refining
+the previous search runs by scanning the neighborhood of the most recent identified values.
+In such regard, the Anderson-Darling statistical test is used to rank the various FTR distributions
+and eventually pick the one providing the closest approximation to the reference statistics.
+
+Testing
+#######
+The test suite ``TwoRaySplmTestSuite`` includes three test cases:
+
+* ``FtrFadingModelAverageTest``, which checks that the average of the Fluctuating Two Ray (FTR)
+  fading model realizations is consistent with the theoretical value provided in [Romero]_.
+
+* ``ArrayResponseTest``, which checks that the overall array response at boresight computed by the ù
+  ``CalcBeamformingGain`` function coincides with the expected theoretical values.
+
+* ``OverallGainAverageTest``, which checks that the average overall channel gain obtained using the
+  ``DoCalcRxPowerSpectralDensity`` method of the ``TwoRaySpectrumPropagationLossModel`` class is close
+  (it is, after all, a simplified and performance-oriented model) to the one obtained using
+  the ``ThreeGppSpectrumPropagationLossModel`` and ``ThreeGppChannelModel`` classes.
+
+
+References
+##########
+
+.. [Zugno2020] Zugno, Tommaso, Michele Polese, Natale Patriciello, Biljana Bojović,
+   Sandra Lagen, Michele Zorzi. "Implementation of a spatial channel model for ns-3."
+   In Proceedings of the 2020 Workshop on ns-3, pp. 49-56. 2020.
+
+.. [Polese2018] Michele Polese, Michele Zorzi. “Impact of channel models on
+   the end-to-end performance of mmwave cellular networks”. In: 2018 IEEE 19th
+   International Workshop on Signal Processing Advances in Wireless Communications
+   (SPAWC).
+
+.. [Rebato2018] Rebato, Mattia, Laura Resteghini, Christian Mazzucco, Michele Zorzi.
+   “Study of realistic antenna patterns in 5G mmWave cellular scenarios”. In: 2018
+   IEEE International Conference on Communications (ICC).
+
+.. [Romero] Romero-Jerez, Juan M., F. Javier Lopez-Martinez, José F. Paris,
+   Andrea J. Goldsmith. “The fluctuating two-ray fading model: Statistical
+   characterization and performance analysis”. In: IEEE Transactions on Wireless
+   Communications 16.7 (2017).
+
+.. [Kulkarni] Kulkarni, Mandar N., Eugene Visotsky, Jeffrey G. Andrews. "Correction
+   factor for analysis of MIMO wireless networks with highly directional beamforming.",
+   IEEE Wireless Communications Letters, 2018
+
+.. [Asplund] Asplund, Henrik, David Astely, Peter von Butovitsch, Thomas Chapman,
+   Mattias Frenne, Farshid Ghasemzadeh, Måns Hagström et al. Advanced Antenna Systems
+   for 5G Network Deployments: Bridging the Gap Between Theory and Practice.
+   Academic Press, 2020.
+
+.. [TR38901] 3GPP. 2018. TR 38.901. Study on channel for frequencies from 0.5 to
+   100 GHz. V.15.0.0. (2018-06).
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/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
--- ns-3.37/src/spectrum/examples/adhoc-aloha-ideal-phy-with-microwave-oven.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/examples/adhoc-aloha-ideal-phy-with-microwave-oven.cc	2023-03-16 20:54:23.824869200 -0700
@@ -299,4 +299,6 @@
     Simulator::Run();
 
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/examples/CMakeLists.txt ns-3.38/src/spectrum/examples/CMakeLists.txt
--- ns-3.37/src/spectrum/examples/CMakeLists.txt	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/examples/CMakeLists.txt	2023-03-16 20:54:23.824869200 -0700
@@ -2,56 +2,66 @@
   NAME adhoc-aloha-ideal-phy
   SOURCE_FILES adhoc-aloha-ideal-phy.cc
   LIBRARIES_TO_LINK
-    ${libspectrum}
-    ${libmobility}
-    ${libinternet}
     ${libapplications}
+    ${libinternet}
+    ${libmobility}
+    ${libspectrum}
 )
 
 build_lib_example(
   NAME adhoc-aloha-ideal-phy-matrix-propagation-loss-model
   SOURCE_FILES adhoc-aloha-ideal-phy-matrix-propagation-loss-model.cc
   LIBRARIES_TO_LINK
-    ${libspectrum}
-    ${libmobility}
-    ${libinternet}
     ${libapplications}
+    ${libinternet}
+    ${libmobility}
+    ${libspectrum}
 )
 
 build_lib_example(
   NAME adhoc-aloha-ideal-phy-with-microwave-oven
   SOURCE_FILES adhoc-aloha-ideal-phy-with-microwave-oven.cc
   LIBRARIES_TO_LINK
-    ${libspectrum}
-    ${libmobility}
-    ${libinternet}
     ${libapplications}
+    ${libinternet}
+    ${libmobility}
+    ${libspectrum}
 )
 
 build_lib_example(
   NAME tv-trans-example
   SOURCE_FILES tv-trans-example.cc
   LIBRARIES_TO_LINK
-    ${libspectrum}
-    ${libmobility}
     ${libcore}
+    ${libmobility}
+    ${libspectrum}
 )
 
 build_lib_example(
   NAME tv-trans-regional-example
   SOURCE_FILES tv-trans-regional-example.cc
   LIBRARIES_TO_LINK
-    ${libspectrum}
-    ${libmobility}
     ${libcore}
+    ${libmobility}
+    ${libspectrum}
 )
 
 build_lib_example(
   NAME three-gpp-channel-example
   SOURCE_FILES three-gpp-channel-example.cc
   LIBRARIES_TO_LINK
-    ${libspectrum}
+    ${libcore}
+    ${liblte}
     ${libmobility}
+    ${libspectrum}
+)
+
+build_lib_example(
+  NAME three-gpp-two-ray-channel-calibration
+  SOURCE_FILES three-gpp-two-ray-channel-calibration.cc
+  LIBRARIES_TO_LINK
     ${libcore}
     ${liblte}
+    ${libmobility}
+    ${libspectrum}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/examples/three-gpp-channel-example.cc ns-3.38/src/spectrum/examples/three-gpp-channel-example.cc
--- ns-3.37/src/spectrum/examples/three-gpp-channel-example.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/examples/three-gpp-channel-example.cc	2023-03-16 20:54:23.824869200 -0700
@@ -79,8 +79,6 @@
               Ptr<PhasedArrayModel> thisAntenna,
               Ptr<NetDevice> otherDevice)
 {
-    PhasedArrayModel::ComplexVector antennaWeights;
-
     // retrieve the position of the two devices
     Vector aPos = thisDevice->GetNode()->GetObject<MobilityModel>()->GetPosition();
     Vector bPos = otherDevice->GetNode()->GetObject<MobilityModel>()->GetPosition();
@@ -91,20 +89,26 @@
 
     double vAngleRadian = completeAngle.GetInclination(); // the elevation angle
 
-    // retrieve the number of antenna elements
-    int totNoArrayElements = thisAntenna->GetNumberOfElements();
+    // retrieve the number of antenna elements and resize the vector
+    uint64_t totNoArrayElements = thisAntenna->GetNumberOfElements();
+    PhasedArrayModel::ComplexVector antennaWeights(totNoArrayElements);
 
     // the total power is divided equally among the antenna elements
-    double power = 1 / sqrt(totNoArrayElements);
+    double power = 1.0 / sqrt(totNoArrayElements);
 
     // compute the antenna weights
-    for (int ind = 0; ind < totNoArrayElements; ind++)
+    const double sinVAngleRadian = sin(vAngleRadian);
+    const double cosVAngleRadian = cos(vAngleRadian);
+    const double sinHAngleRadian = sin(hAngleRadian);
+    const double cosHAngleRadian = cos(hAngleRadian);
+
+    for (uint64_t ind = 0; ind < totNoArrayElements; ind++)
     {
         Vector loc = thisAntenna->GetElementLocation(ind);
         double phase = -2 * M_PI *
-                       (sin(vAngleRadian) * cos(hAngleRadian) * loc.x +
-                        sin(vAngleRadian) * sin(hAngleRadian) * loc.y + cos(vAngleRadian) * loc.z);
-        antennaWeights.push_back(exp(std::complex<double>(0, phase)) * power);
+                       (sinVAngleRadian * cosHAngleRadian * loc.x +
+                        sinVAngleRadian * sinHAngleRadian * loc.y + cosVAngleRadian * loc.z);
+        antennaWeights[ind] = exp(std::complex<double>(0, phase)) * power;
     }
 
     // store the antenna weights
@@ -173,7 +177,7 @@
     double txPow = 49.0;          // tx power in dBm
     double noiseFigure = 9.0;     // noise figure in dB
     double distance = 10.0;       // distance between tx and rx nodes in meters
-    uint32_t simTime = 10000;     // simulation time in milliseconds
+    uint32_t simTime = 1000;      // simulation time in milliseconds
     uint32_t timeRes = 10;        // time resolution in milliseconds
     std::string scenario = "UMa"; // 3GPP propagation scenario
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc ns-3.38/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc
--- ns-3.37/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc	2023-03-16 20:54:23.824869200 -0700
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2022 SIGNET Lab, Department of Information Engineering,
+ * University of Padova
+ *
+ * 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/command-line.h>
+#include <ns3/core-module.h>
+#include <ns3/double.h>
+#include <ns3/isotropic-antenna-model.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/node-container.h>
+#include <ns3/object-factory.h>
+#include <ns3/pointer.h>
+#include <ns3/string.h>
+#include <ns3/three-gpp-channel-model.h>
+#include <ns3/three-gpp-propagation-loss-model.h>
+#include <ns3/three-gpp-spectrum-propagation-loss-model.h>
+#include <ns3/two-ray-spectrum-propagation-loss-model.h>
+#include <ns3/uinteger.h>
+#include <ns3/uniform-planar-array.h>
+
+NS_LOG_COMPONENT_DEFINE("ThreeGppTwoRayChannelCalibration");
+
+using namespace ns3;
+
+// Calibration results actually show a weak dependence with respect to the carrier frequency
+constexpr double FC_STEP = 5e9;
+
+// 500 MHz, as to provide a fit for the whole frequency range supported by the TR 38.901 model
+constexpr double MIN_FC = 500e6;
+
+// 100 GHz, as to provide a fit for the whole frequency range supported by the TR 38.901 model
+constexpr double MAX_FC = 100e9;
+
+// Results are independent from this
+constexpr double BW = 200e6;
+
+// Results are independent from this, it dictate sonly the resolution of the PSD.
+// This value corresponds to numerology index 2 of the 5G NR specifications
+constexpr double RB_WIDTH = 60e3;
+
+const std::vector<std::string> LOS_CONDITIONS{
+    "LOS",
+    "NLOS",
+};
+
+const std::vector<std::string> THREE_GPP_SCENARIOS{
+    "RMa",
+    "UMa",
+    "UMi-StreetCanyon",
+    "InH-OfficeOpen",
+    "InH-OfficeMixed",
+};
+
+const Ptr<OutputStreamWrapper> g_outStream =
+    Create<OutputStreamWrapper>("two-ray-to-three-gpp-calibration.csv", std::ios::out);
+
+void
+LogEndToEndGain(std::string cond, std::string scen, double fc, long int seed, double gain)
+{
+    *g_outStream->GetStream() << cond << "\t" << scen << "\t" << fc << "\t" << seed << "\t" << gain
+                              << "\n";
+}
+
+double
+ComputePowerSpectralDensityOverallPower(Ptr<const SpectrumValue> psd)
+{
+    return Integral(*psd);
+}
+
+Ptr<SpectrumValue>
+CreateTxPowerSpectralDensity(double fc)
+{
+    uint32_t numRbs = std::floor(BW / RB_WIDTH);
+    double f = fc - (numRbs * RB_WIDTH / 2.0);
+    double powerTx = 0.0;
+
+    Bands rbs;              // A vector representing each resource block
+    std::vector<int> rbsId; // A vector representing the resource block IDs
+    rbsId.reserve(numRbs);
+
+    for (uint32_t numrb = 0; numrb < numRbs; ++numrb)
+    {
+        BandInfo rb;
+        rb.fl = f;
+        f += RB_WIDTH / 2;
+        rb.fc = f;
+        f += RB_WIDTH / 2;
+        rb.fh = f;
+
+        rbs.push_back(rb);
+        rbsId.push_back(numrb);
+    }
+    Ptr<SpectrumModel> model = Create<SpectrumModel>(rbs);
+    Ptr<SpectrumValue> txPsd = Create<SpectrumValue>(model);
+
+    double powerTxW = std::pow(10., (powerTx - 30) / 10);
+    double txPowerDensity = powerTxW / BW;
+
+    for (auto rbId : rbsId)
+    {
+        (*txPsd)[rbId] = txPowerDensity;
+    }
+
+    return txPsd;
+}
+
+double
+ComputeEndToEndGain(std::string cond,
+                    std::string scen,
+                    double fc,
+                    Ptr<Node> a,
+                    Ptr<Node> b,
+                    Ptr<PhasedArrayModel> aArray,
+                    Ptr<PhasedArrayModel> bArray)
+{
+    // Fix the LOS condition
+    Ptr<ChannelConditionModel> channelConditionModel;
+    if (cond == "LOS")
+    {
+        channelConditionModel = CreateObject<AlwaysLosChannelConditionModel>();
+    }
+    else if (cond == "NLOS")
+    {
+        channelConditionModel = CreateObject<NeverLosChannelConditionModel>();
+    }
+    else
+    {
+        NS_ABORT_MSG("Unsupported channel condition");
+    }
+
+    // Create the needed objects. These must be created anew each loop, otherwise the channel is
+    // stored and never re-computed.
+    Ptr<ThreeGppSpectrumPropagationLossModel> threeGppSpectrumLossModel =
+        CreateObject<ThreeGppSpectrumPropagationLossModel>();
+    Ptr<ThreeGppChannelModel> threeGppChannelModel = CreateObject<ThreeGppChannelModel>();
+
+    // Pass the needed pointers between the various spectrum instances
+    threeGppSpectrumLossModel->SetAttribute("ChannelModel", PointerValue(threeGppChannelModel));
+    threeGppChannelModel->SetAttribute("ChannelConditionModel",
+                                       PointerValue(channelConditionModel));
+
+    // Create the TX PSD
+    Ptr<SpectrumValue> txPsd = CreateTxPowerSpectralDensity(fc);
+    double txPower = ComputePowerSpectralDensityOverallPower(txPsd);
+
+    // Create TX signal parameters
+    Ptr<SpectrumSignalParameters> signalParams = Create<SpectrumSignalParameters>();
+    signalParams->psd = txPsd;
+
+    // Set the carrier frequency
+    threeGppChannelModel->SetAttribute("Frequency", DoubleValue(fc));
+
+    // Set the scenario
+    threeGppChannelModel->SetAttribute("Scenario", StringValue(scen));
+
+    // Disable all possible sources of variance apart from the multipath fading
+    threeGppChannelModel->SetAttribute("Blockage", BooleanValue(false));
+
+    // Retrieve the mobility models and the position of the TX and RX nodes
+    Ptr<MobilityModel> aMob = a->GetObject<MobilityModel>();
+    Ptr<MobilityModel> bMob = b->GetObject<MobilityModel>();
+    Vector aPos = aMob->GetPosition();
+    Vector bPos = bMob->GetPosition();
+
+    // Compute the relative azimuth and the elevation angles
+    Angles angleBtoA(bPos, aPos);
+    Angles angleAtoB(aPos, bPos);
+
+    // Create the BF vectors
+    aArray->SetBeamformingVector(aArray->GetBeamformingVector(angleBtoA));
+    bArray->SetBeamformingVector(bArray->GetBeamformingVector(angleAtoB));
+
+    // Compute the received power due to multipath fading
+    auto rxPsd = threeGppSpectrumLossModel->DoCalcRxPowerSpectralDensity(signalParams,
+                                                                         aMob,
+                                                                         bMob,
+                                                                         aArray,
+                                                                         bArray);
+    double rxPower = ComputePowerSpectralDensityOverallPower(rxPsd);
+
+    return rxPower / txPower;
+}
+
+int
+main(int argc, char* argv[])
+{
+    uint32_t numRealizations = 5000; // The number of different channel realizations
+    bool enableOutput = false;       // Whether to log the results of the example
+
+    CommandLine cmd(__FILE__);
+    cmd.AddValue("enableOutput", "Logs the results of the example", enableOutput);
+    cmd.AddValue("numRealizations", "The number of different realizations", numRealizations);
+    cmd.Parse(argc, argv);
+
+    // Log trace structure
+    if (enableOutput)
+    {
+        *g_outStream->GetStream() << "cond\tscen\tfc\tseed\tgain\n";
+    }
+
+    // Aggregate them to the corresponding nodes
+    NodeContainer nodes;
+    nodes.Create(2);
+
+    // Create the mobility models for the TX and RX nodes
+    MobilityHelper mobility;
+    Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
+    Vector aPos(0.0, 0.0, 0.0);
+    Vector bPos(10.0, 0.0, 0.0);
+    positionAlloc->Add(aPos);
+    positionAlloc->Add(bPos);
+    mobility.SetPositionAllocator(positionAlloc);
+    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+    mobility.Install(nodes);
+
+    // Create the TX and RX phased arrays
+    Ptr<PhasedArrayModel> aPhasedArray =
+        CreateObjectWithAttributes<UniformPlanarArray>("NumColumns",
+                                                       UintegerValue(1),
+                                                       "NumRows",
+                                                       UintegerValue(1));
+    aPhasedArray->SetAntennaElement(PointerValue(CreateObject<IsotropicAntennaModel>()));
+    Ptr<PhasedArrayModel> bPhasedArray =
+        CreateObjectWithAttributes<UniformPlanarArray>("NumColumns",
+                                                       UintegerValue(1),
+                                                       "NumRows",
+                                                       UintegerValue(1));
+    bPhasedArray->SetAntennaElement(PointerValue(CreateObject<IsotropicAntennaModel>()));
+
+    // Loop over predetermined set of scenarios, LOS conditions and frequencies
+    for (const auto& cond : LOS_CONDITIONS)
+    {
+        for (const auto& scen : THREE_GPP_SCENARIOS)
+        {
+            for (double fc = MIN_FC; fc < MAX_FC; fc += FC_STEP)
+            {
+                for (uint32_t runIdx = 0; runIdx < numRealizations; runIdx++)
+                {
+                    double gain = ComputeEndToEndGain(cond,
+                                                      scen,
+                                                      fc,
+                                                      nodes.Get(0),
+                                                      nodes.Get(1),
+                                                      aPhasedArray,
+                                                      bPhasedArray);
+                    if (enableOutput)
+                    {
+                        LogEndToEndGain(cond, scen, fc, runIdx, gain);
+                    }
+                }
+            }
+        }
+    }
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/helper/tv-spectrum-transmitter-helper.cc ns-3.38/src/spectrum/helper/tv-spectrum-transmitter-helper.cc
--- ns-3.37/src/spectrum/helper/tv-spectrum-transmitter-helper.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/helper/tv-spectrum-transmitter-helper.cc	2023-03-16 20:54:23.824869200 -0700
@@ -46,7 +46,8 @@
     602e6, 608e6, 614e6, 620e6, 626e6, 632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6,
     674e6, 680e6, 686e6, 692e6, 698e6, 704e6, 710e6, 716e6, 722e6, 728e6, 734e6, 740e6,
     746e6, 752e6, 758e6, 764e6, 770e6, 776e6, 782e6, 788e6, 794e6, 800e6, 806e6, 812e6,
-    818e6, 824e6, 830e6, 836e6, 842e6, 848e6, 854e6, 860e6, 866e6, 872e6, 878e6, 884e6};
+    818e6, 824e6, 830e6, 836e6, 842e6, 848e6, 854e6, 860e6, 866e6, 872e6, 878e6, 884e6,
+};
 /// NORTH AMERICA end frequencies
 const double northAmericaEndFrequencies[84] = {
     0,     0,     60e6,  66e6,  72e6,  82e6,  88e6,  180e6, 186e6, 192e6, 198e6, 204e6,
@@ -55,7 +56,8 @@
     608e6, 614e6, 620e6, 626e6, 632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6, 674e6,
     680e6, 686e6, 692e6, 698e6, 704e6, 710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6,
     752e6, 758e6, 764e6, 770e6, 776e6, 782e6, 788e6, 794e6, 800e6, 806e6, 812e6, 818e6,
-    824e6, 830e6, 836e6, 842e6, 848e6, 854e6, 860e6, 866e6, 872e6, 878e6, 884e6, 890e6};
+    824e6, 830e6, 836e6, 842e6, 848e6, 854e6, 860e6, 866e6, 872e6, 878e6, 884e6, 890e6,
+};
 
 /// EUROPE: 70 elements (index 0 - 69); valid channels = 5 - 12, 21 - 69
 const int europeArrayLength = 70;
@@ -66,7 +68,8 @@
     494e6, 502e6, 510e6, 518e6, 526e6, 534e6, 542e6, 550e6, 558e6, 566e6, 574e6, 582e6,
     590e6, 598e6, 606e6, 614e6, 622e6, 630e6, 638e6, 646e6, 654e6, 662e6, 670e6, 678e6,
     686e6, 694e6, 702e6, 710e6, 718e6, 726e6, 734e6, 742e6, 750e6, 758e6, 766e6, 774e6,
-    782e6, 790e6, 798e6, 806e6, 814e6, 822e6, 830e6, 838e6, 846e6, 854e6};
+    782e6, 790e6, 798e6, 806e6, 814e6, 822e6, 830e6, 838e6, 846e6, 854e6,
+};
 /// EUROPE end frequencies
 const double europeEndFrequencies[70] = {
     0,     0,     0,     0,     0,     181e6, 188e6, 195e6, 202e6, 209e6, 216e6, 223e6,
@@ -74,7 +77,8 @@
     502e6, 510e6, 518e6, 526e6, 534e6, 542e6, 550e6, 558e6, 566e6, 574e6, 582e6, 590e6,
     598e6, 606e6, 614e6, 622e6, 630e6, 638e6, 646e6, 654e6, 662e6, 670e6, 678e6, 686e6,
     694e6, 702e6, 710e6, 718e6, 726e6, 734e6, 742e6, 750e6, 758e6, 766e6, 774e6, 782e6,
-    790e6, 798e6, 806e6, 814e6, 822e6, 830e6, 838e6, 846e6, 854e6, 862e6};
+    790e6, 798e6, 806e6, 814e6, 822e6, 830e6, 838e6, 846e6, 854e6, 862e6,
+};
 
 /// JAPAN: 63 elements (index 0 - 62); valid channels = 1 - 62
 const int japanArrayLength = 63;
@@ -84,14 +88,16 @@
     470e6, 476e6, 482e6, 488e6, 494e6, 500e6, 506e6, 512e6, 518e6, 524e6, 530e6, 536e6, 542e6,
     548e6, 554e6, 560e6, 566e6, 572e6, 578e6, 584e6, 590e6, 596e6, 602e6, 608e6, 614e6, 620e6,
     626e6, 632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6, 674e6, 680e6, 686e6, 692e6, 698e6,
-    704e6, 710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6, 752e6, 758e6, 764e6};
+    704e6, 710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6, 752e6, 758e6, 764e6,
+};
 /// JAPAN end frequencies
 const double japanEndFrequencies[63] = {
     0,     96e6,  102e6, 108e6, 176e6, 182e6, 188e6, 194e6, 198e6, 204e6, 210e6, 216e6, 222e6,
     476e6, 482e6, 488e6, 494e6, 500e6, 506e6, 512e6, 518e6, 524e6, 530e6, 536e6, 542e6, 548e6,
     554e6, 560e6, 566e6, 572e6, 578e6, 584e6, 590e6, 596e6, 602e6, 608e6, 614e6, 620e6, 626e6,
     632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6, 674e6, 680e6, 686e6, 692e6, 698e6, 704e6,
-    710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6, 752e6, 758e6, 764e6, 770e6};
+    710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6, 752e6, 758e6, 764e6, 770e6,
+};
 
 TvSpectrumTransmitterHelper::TvSpectrumTransmitterHelper()
     : m_channel(nullptr),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/half-duplex-ideal-phy.h ns-3.38/src/spectrum/model/half-duplex-ideal-phy.h
--- ns-3.37/src/spectrum/model/half-duplex-ideal-phy.h	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/half-duplex-ideal-phy.h	2023-03-16 20:54:23.824869200 -0700
@@ -210,7 +210,7 @@
 
     Ptr<MobilityModel> m_mobility;  //!< Mobility model
     Ptr<AntennaModel> m_antenna;    //!< Antenna model
-    Ptr<NetDevice> m_netDevice;     //!< NetDevice connected to theis phy
+    Ptr<NetDevice> m_netDevice;     //!< NetDevice connected to this phy
     Ptr<SpectrumChannel> m_channel; //!< Channel
 
     Ptr<SpectrumValue> m_txPsd;       //!< Tx power spectral density
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/matrix-based-channel-model.h ns-3.38/src/spectrum/model/matrix-based-channel-model.h
--- ns-3.37/src/spectrum/model/matrix-based-channel-model.h	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/matrix-based-channel-model.h	2023-03-16 20:54:23.824869200 -0700
@@ -22,6 +22,7 @@
 #define MATRIX_BASED_CHANNEL_H
 
 // #include <complex>
+#include <ns3/matrix-array.h>
 #include <ns3/nstime.h>
 #include <ns3/object.h>
 #include <ns3/phased-array-model.h>
@@ -49,31 +50,47 @@
      */
     ~MatrixBasedChannelModel() override;
 
-    typedef std::vector<double> DoubleVector;         //!< type definition for vectors of doubles
-    typedef std::vector<DoubleVector> Double2DVector; //!< type definition for matrices of doubles
-    typedef std::vector<Double2DVector>
-        Double3DVector; //!< type definition for 3D matrices of doubles
-    typedef std::vector<PhasedArrayModel::ComplexVector>
-        Complex2DVector; //!< type definition for complex matrices
-    typedef std::vector<Complex2DVector>
-        Complex3DVector; //!< type definition for complex 3D matrices
+    /// Type definition for vectors of doubles
+    using DoubleVector = std::vector<double>;
+
+    /// Type definition for matrices of doubles
+    using Double2DVector = std::vector<DoubleVector>;
+
+    /// Type definition for 3D matrices of doubles
+    using Double3DVector = std::vector<Double2DVector>;
+
+    using Complex2DVector = ComplexMatrixArray; //!< Create an alias for 2D complex vectors
+    using Complex3DVector = ComplexMatrixArray; //!< Create an alias for 3D complex vectors
 
     /**
      * Data structure that stores a channel realization
      */
     struct ChannelMatrix : public SimpleRefCount<ChannelMatrix>
     {
-        Complex3DVector m_channel; //!< channel matrix H[u][s][n].
-        Time m_generatedTime;      //!< generation time
-        std::pair<uint32_t, uint32_t>
-            m_antennaPair; //!< the first element is the ID of the antenna of the s-node (the
-                           //!< antenna of the transmitter when the channel was generated), the
-                           //!< second element is ID of the antenna of the u-node antenna (the
-                           //!< antenna of the receiver when the channel was generated)
-        std::pair<uint32_t, uint32_t>
-            m_nodeIds; //!< the first element is the s-node ID (the transmitter when the channel was
-                       //!< generated), the second element is the u-node ID (the receiver when the
-                       //!< channel was generated)
+        /**
+         * Channel matrix H[u][s][n].
+         */
+        Complex3DVector m_channel;
+
+        /**
+         * Generation time.
+         */
+        Time m_generatedTime;
+
+        /**
+         * The first element is the ID of the antenna of the s-node (the
+         * antenna of the transmitter when the channel was generated), the
+         * second element is ID of the antenna of the u-node antenna (the
+         * antenna of the receiver when the channel was generated).
+         */
+        std::pair<uint32_t, uint32_t> m_antennaPair;
+
+        /**
+         * The first element is the s-node ID (the transmitter when the channel was
+         * generated), the second element is the u-node ID (the receiver when the
+         * channel was generated).
+         */
+        std::pair<uint32_t, uint32_t> m_nodeIds;
 
         /**
          * Destructor for ChannelMatrix
@@ -105,18 +122,41 @@
      */
     struct ChannelParams : public SimpleRefCount<ChannelParams>
     {
-        Time m_generatedTime;   //!< generation time
-        DoubleVector m_delay;   //!< cluster delay in nanoseconds.
-        Double2DVector m_angle; //!< cluster angle angle[direction][n], where direction = 0(AOA),
-                                //!< 1(ZOA), 2(AOD), 3(ZOD) in degree.
-        DoubleVector m_alpha;   //!< alpha term per cluster as described in 3GPP TR 37.885 v15.3.0,
-                                //!< Sec. 6.2.3 for calculating doppler
-        DoubleVector m_D; //!< D term per cluster as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
-                          //!< for calculating doppler
-        std::pair<uint32_t, uint32_t>
-            m_nodeIds; //!< the first element is the s-node ID (the transmitter when the channel
-                       //!< params were generated), the second element is the u-node ID (the
-                       //!< receiver when the channel params were generated generated)
+        /**
+         * Generation time.
+         */
+        Time m_generatedTime;
+
+        /**
+         * Cluster delay in nanoseconds.
+         */
+        DoubleVector m_delay;
+
+        /**
+         * Cluster angle angle[direction][n], where direction = 0(AOA), 1(ZOA), 2(AOD), 3(ZOD)
+         * in degree.
+         */
+        Double2DVector m_angle;
+
+        /**
+         * Alpha term per cluster as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
+         * for calculating doppler.
+         */
+        DoubleVector m_alpha;
+
+        /**
+         * D term per cluster as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
+         * for calculating doppler.
+         */
+        DoubleVector m_D;
+
+        /**
+         * The first element is the s-node ID (the transmitter when the channel params were
+         * generated), the second element is the u-node ID (the receiver when the channel params
+         * were generated generated).
+         */
+        std::pair<uint32_t, uint32_t> m_nodeIds;
+
         /**
          * Destructor for ChannelParams
          */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/multi-model-spectrum-channel.cc ns-3.38/src/spectrum/model/multi-model-spectrum-channel.cc
--- ns-3.37/src/spectrum/model/multi-model-spectrum-channel.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/multi-model-spectrum-channel.cc	2023-03-16 20:54:23.824869200 -0700
@@ -148,20 +148,16 @@
 
     ++m_numDevices;
 
-    RxSpectrumModelInfoMap_t::iterator rxInfoIterator =
-        m_rxSpectrumModelInfoMap.find(rxSpectrumModelUid);
+    auto [rxInfoIterator, inserted] =
+        m_rxSpectrumModelInfoMap.emplace(rxSpectrumModelUid, RxSpectrumModelInfo(rxSpectrumModel));
 
-    if (rxInfoIterator == m_rxSpectrumModelInfoMap.end())
-    {
-        // spectrum model unknown, add it to the list of RxSpectrumModels
-        std::pair<RxSpectrumModelInfoMap_t::iterator, bool> ret;
-        ret = m_rxSpectrumModelInfoMap.insert(
-            std::make_pair(rxSpectrumModelUid, RxSpectrumModelInfo(rxSpectrumModel)));
-        NS_ASSERT(ret.second);
-        // also add the phy to the newly created set of SpectrumPhy for this RxSpectrumModel
-        ret.first->second.m_rxPhys.push_back(phy);
+    // rxInfoIterator points either to the newly inserted element or to the element that
+    // prevented insertion. In both cases, add the phy to the element pointed to by rxInfoIterator
+    rxInfoIterator->second.m_rxPhys.push_back(phy);
 
-        // and create the necessary converters for all the TX spectrum models that we know of
+    if (inserted)
+    {
+        // create the necessary converters for all the TX spectrum models that we know of
         for (TxSpectrumModelInfoMap_t::iterator txInfoIterator = m_txSpectrumModelInfoMap.begin();
              txInfoIterator != m_txSpectrumModelInfoMap.end();
              ++txInfoIterator)
@@ -182,11 +178,6 @@
             }
         }
     }
-    else
-    {
-        // spectrum model is already known, just add the device to the corresponding list
-        rxInfoIterator->second.m_rxPhys.push_back(phy);
-    }
 }
 
 TxSpectrumModelInfoMap_t::const_iterator
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/single-model-spectrum-channel.cc ns-3.38/src/spectrum/model/single-model-spectrum-channel.cc
--- ns-3.37/src/spectrum/model/single-model-spectrum-channel.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/single-model-spectrum-channel.cc	2023-03-16 20:54:23.824869200 -0700
@@ -84,7 +84,10 @@
 SingleModelSpectrumChannel::AddRx(Ptr<SpectrumPhy> phy)
 {
     NS_LOG_FUNCTION(this << phy);
-    m_phyList.push_back(phy);
+    if (std::find(m_phyList.cbegin(), m_phyList.cend(), phy) == m_phyList.cend())
+    {
+        m_phyList.push_back(phy);
+    }
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/spectrum-converter.h ns-3.38/src/spectrum/model/spectrum-converter.h
--- ns-3.37/src/spectrum/model/spectrum-converter.h	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/spectrum-converter.h	2023-03-16 20:54:23.824869200 -0700
@@ -44,7 +44,7 @@
     /**
      * Create a SpectrumConverter class that will be able to convert ValueVsFreq
      * instances defined over one SpectrumModel to corresponding ValueVsFreq
-     * instances defined over a diffent SpectrumModel
+     * instances defined over a different SpectrumModel
      *
      * @param fromSpectrumModel the SpectrumModel to convert from
      * @param toSpectrumModel the SpectrumModel to convert to
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/three-gpp-channel-model.cc ns-3.38/src/spectrum/model/three-gpp-channel-model.cc
--- ns-3.37/src/spectrum/model/three-gpp-channel-model.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/three-gpp-channel-model.cc	2023-03-16 20:54:23.828869000 -0700
@@ -45,7 +45,8 @@
 /// (Table 7.5-3)
 static const double offSetAlpha[20] = {
     0.0447, -0.0447, 0.1413, -0.1413, 0.2492, -0.2492, 0.3715, -0.3715, 0.5129, -0.5129,
-    0.6797, -0.6797, 0.8844, -0.8844, 1.1481, -1.1481, 1.5195, -1.5195, 2.1551, -2.1551};
+    0.6797, -0.6797, 0.8844, -0.8844, 1.1481, -1.1481, 1.5195, -1.5195, 2.1551, -2.1551,
+};
 
 /**
  * The square root matrix for <em>RMa LOS</em>, which is generated using the
@@ -396,7 +397,7 @@
 {
     NS_LOG_FUNCTION(this);
 
-    double fcGHz = m_frequency / 1e9;
+    double fcGHz = m_frequency / 1.0e9;
     Ptr<ParamsTable> table3gpp = Create<ParamsTable>();
     // table3gpp includes the following parameters:
     // numOfCluster, raysPerCluster, uLgDS, sigLgDS, uLgASD, sigLgASD,
@@ -425,7 +426,7 @@
             table3gpp->m_sigLgZSA = 0.40;
             table3gpp->m_uLgZSD = 0.34;
             table3gpp->m_sigLgZSD =
-                std::max(-1.0, -0.17 * (distance2D / 1000) - 0.01 * (hUT - 1.5) + 0.22);
+                std::max(-1.0, -0.17 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.22);
             table3gpp->m_offsetZOD = 0;
             table3gpp->m_cDS = 3.91e-9;
             table3gpp->m_cASD = 2;
@@ -459,7 +460,7 @@
             table3gpp->m_uLgZSA = 0.58;
             table3gpp->m_sigLgZSA = 0.37;
             table3gpp->m_uLgZSD =
-                std::max(-1.0, -0.19 * (distance2D / 1000) - 0.01 * (hUT - 1.5) + 0.28);
+                std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
             table3gpp->m_sigLgZSD = 0.30;
             table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
             table3gpp->m_cDS = 3.91e-9;
@@ -494,7 +495,7 @@
             table3gpp->m_uLgZSA = 0.93;
             table3gpp->m_sigLgZSA = 0.22;
             table3gpp->m_uLgZSD =
-                std::max(-1.0, -0.19 * (distance2D / 1000) - 0.01 * (hUT - 1.5) + 0.28);
+                std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
             table3gpp->m_sigLgZSD = 0.30;
             table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
             table3gpp->m_cDS = 3.91e-9;
@@ -532,7 +533,7 @@
             table3gpp->m_uLgZSA = 0.95;
             table3gpp->m_sigLgZSA = 0.16;
             table3gpp->m_uLgZSD =
-                std::max(-0.5, -2.1 * distance2D / 1000 - 0.01 * (hUT - 1.5) + 0.75);
+                std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.75);
             table3gpp->m_sigLgZSD = 0.40;
             table3gpp->m_offsetZOD = 0;
             table3gpp->m_cDS = std::max(0.25, -3.4084 * log10(fcGHz) + 6.5622) * 1e-9;
@@ -556,7 +557,7 @@
         }
         else
         {
-            double uLgZSD = std::max(-0.5, -2.1 * distance2D / 1000 - 0.01 * (hUT - 1.5) + 0.9);
+            double uLgZSD = std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.9);
 
             double afc = 0.208 * log10(fcGHz) - 0.782;
             double bfc = 25;
@@ -650,7 +651,7 @@
             table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
             table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
             table3gpp->m_uLgZSD =
-                std::max(-0.21, -14.8 * distance2D / 1000 + 0.01 * std::abs(hUT - hBS) + 0.83);
+                std::max(-0.21, -14.8 * distance2D / 1000.0 + 0.01 * std::abs(hUT - hBS) + 0.83);
             table3gpp->m_sigLgZSD = 0.35;
             table3gpp->m_offsetZOD = 0;
             table3gpp->m_cDS = 5e-9;
@@ -675,7 +676,7 @@
         else
         {
             double uLgZSD =
-                std::max(-0.5, -3.1 * distance2D / 1000 + 0.01 * std::max(hUT - hBS, 0.0) + 0.2);
+                std::max(-0.5, -3.1 * distance2D / 1000.0 + 0.01 * std::max(hUT - hBS, 0.0) + 0.2);
             double offsetZOD = -1 * std::pow(10, -1.5 * log10(std::max(10.0, distance2D)) + 3.3);
             if (!los && !o2i)
             {
@@ -1045,7 +1046,7 @@
     }
     else
     {
-        NS_FATAL_ERROR("unkonw scenarios");
+        NS_FATAL_ERROR("unknown scenarios");
     }
 
     return table3gpp;
@@ -1314,7 +1315,7 @@
         double power =
             exp(-1 * clusterDelay[cIndex] * (table3gpp->m_rTau - 1) / table3gpp->m_rTau / DS) *
             pow(10,
-                -1 * m_normalRv->GetValue() * table3gpp->m_perClusterShadowingStd / 10); //(7.5-5)
+                -1 * m_normalRv->GetValue() * table3gpp->m_perClusterShadowingStd / 10.0); //(7.5-5)
         powerSum += power;
         clusterPower.push_back(power);
     }
@@ -1332,7 +1333,7 @@
                                         // for (7.5-22)
     if (channelParams->m_losCondition == ChannelCondition::LOS)
     {
-        double kLinear = pow(10, kFactor / 10);
+        double kLinear = pow(10, kFactor / 10.0);
 
         for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
         {
@@ -1502,21 +1503,21 @@
         {
             Xn = -1;
         }
-        clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue() * ASA / 7) +
+        clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue() * ASA / 7.0) +
                              RadiansToDegrees(uAngle.GetAzimuth()); //(7.5-11)
-        clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue() * ASD / 7) +
+        clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue() * ASD / 7.0) +
                              RadiansToDegrees(sAngle.GetAzimuth());
         if (channelCondition->IsO2i())
         {
             clusterZoa[cIndex] =
-                clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7) + 90; //(7.5-16)
+                clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) + 90; //(7.5-16)
         }
         else
         {
-            clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7) +
+            clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) +
                                  RadiansToDegrees(uAngle.GetInclination()); //(7.5-16)
         }
-        clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue() * ZSD / 7) +
+        clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue() * ZSD / 7.0) +
                              RadiansToDegrees(sAngle.GetInclination()) +
                              table3gpp->m_offsetZOD; //(7.5-19)
     }
@@ -1606,7 +1607,7 @@
         for (uint8_t cInd = 0; cInd < channelParams->m_reducedClusterNumber; cInd++)
         {
             channelParams->m_clusterPower[cInd] =
-                channelParams->m_clusterPower[cInd] / pow(10, attenuationDb[cInd] / 10);
+                channelParams->m_clusterPower[cInd] / pow(10, attenuationDb[cInd] / 10.0);
         }
     }
     else
@@ -1679,11 +1680,12 @@
             temp2; // used to store the PHI values for all the possible combination of polarization
         for (uint8_t mInd = 0; mInd < table3gpp->m_raysPerCluster; mInd++)
         {
-            double uXprLinear = pow(10, table3gpp->m_uXpr / 10);     // convert to linear
-            double sigXprLinear = pow(10, table3gpp->m_sigXpr / 10); // convert to linear
+            double uXprLinear = pow(10, table3gpp->m_uXpr / 10.0);     // convert to linear
+            double sigXprLinear = pow(10, table3gpp->m_sigXpr / 10.0); // convert to linear
 
-            temp.push_back(std::pow(10, (m_normalRv->GetValue() * sigXprLinear + uXprLinear) / 10));
-            DoubleVector temp3; // used to store the PHI valuse
+            temp.push_back(
+                std::pow(10, (m_normalRv->GetValue() * sigXprLinear + uXprLinear) / 10.0));
+            DoubleVector temp3; // used to store the PHI values
             for (uint8_t pInd = 0; pInd < 4; pInd++)
             {
                 temp3.push_back(m_uniformRv->GetValue(-1 * M_PI, M_PI));
@@ -1853,9 +1855,9 @@
     MatrixBasedChannelModel::Double2DVector rayZoaRadian;
 
     // if channel params is generated in the same direction in which we
-    // generate the channel matrix, angles and zenit od departure and arrival are ok,
+    // generate the channel matrix, angles and zenith od departure and arrival are ok,
     // just set them to corresponding variable that will be used for the generation
-    // of channel matrix, otherwise we need to flip angles and zenits of departure and arrival
+    // of channel matrix, otherwise we need to flip angles and zeniths of departure and arrival
     if (isSameDirection)
     {
         rayAodRadian = channelParams->m_rayAodRadian;
@@ -1873,23 +1875,18 @@
 
     // Step 11: Generate channel coefficients for each cluster n and each receiver
     //  and transmitter element pair u,s.
-    Complex3DVector hUsn; // channel coffecient hUsn[u][s][n];
-    // where u and s are receive and transmit antenna element, n is cluster index.
-    // NOTE Since each of the strongest 2 clusters are divided into 3 sub-clusters,
-    // the total cluster will be numReducedCLuster + 4.
-    uint64_t uSize = uAntenna->GetNumberOfElements();
-    uint64_t sSize = sAntenna->GetNumberOfElements();
-
-    hUsn.resize(uSize);
-    for (uint64_t uIndex = 0; uIndex < uSize; uIndex++)
-    {
-        hUsn[uIndex].resize(sSize);
-        for (uint64_t sIndex = 0; sIndex < sSize; sIndex++)
-        {
-            hUsn[uIndex][sIndex].resize(channelParams->m_reducedClusterNumber);
-        }
-    }
-
+    // where n is cluster index, u and s are receive and transmit antenna element.
+    size_t uSize = uAntenna->GetNumberOfElements();
+    size_t sSize = sAntenna->GetNumberOfElements();
+
+    // NOTE: Since each of the strongest 2 clusters are divided into 3 sub-clusters,
+    // the total cluster will generally be numReducedCLuster + 4.
+    // However, it might be that m_cluster1st = m_cluster2nd. In this case the
+    // total number of clusters will be numReducedCLuster + 2.
+    uint16_t numOverallCluster = (channelParams->m_cluster1st != channelParams->m_cluster2nd)
+                                     ? channelParams->m_reducedClusterNumber + 4
+                                     : channelParams->m_reducedClusterNumber + 2;
+    Complex3DVector hUsn(uSize, sSize, numOverallCluster); // channel coefficient hUsn (u, s, n);
     NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPhase.size());
     NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPower.size());
     NS_ASSERT(channelParams->m_reducedClusterNumber <=
@@ -1919,17 +1916,91 @@
     Angles sAngle(uMob->GetPosition(), sMob->GetPosition());
     Angles uAngle(sMob->GetPosition(), uMob->GetPosition());
 
+    Complex2DVector raysPreComp(channelParams->m_reducedClusterNumber,
+                                table3gpp->m_raysPerCluster); // stores part of the ray expression,
+    // cached as independent from the u- and s-indexes
+    Double2DVector sinCosA; // cached multiplications of sin and cos of the ZoA and AoA angles
+    Double2DVector sinSinA; // cached multiplications of sines of the ZoA and AoA angles
+    Double2DVector cosZoA;  // cached cos of the ZoA angle
+    Double2DVector sinCosD; // cached multiplications of sin and cos of the ZoD and AoD angles
+    Double2DVector sinSinD; // cached multiplications of the cosines of the ZoA and AoA angles
+    Double2DVector cosZoD;  // cached cos of the ZoD angle
+
+    // resize to appropriate dimensions
+    sinCosA.resize(channelParams->m_reducedClusterNumber);
+    sinSinA.resize(channelParams->m_reducedClusterNumber);
+    cosZoA.resize(channelParams->m_reducedClusterNumber);
+    sinCosD.resize(channelParams->m_reducedClusterNumber);
+    sinSinD.resize(channelParams->m_reducedClusterNumber);
+    cosZoD.resize(channelParams->m_reducedClusterNumber);
+    for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
+    {
+        sinCosA[nIndex].resize(table3gpp->m_raysPerCluster);
+        sinSinA[nIndex].resize(table3gpp->m_raysPerCluster);
+        cosZoA[nIndex].resize(table3gpp->m_raysPerCluster);
+        sinCosD[nIndex].resize(table3gpp->m_raysPerCluster);
+        sinSinD[nIndex].resize(table3gpp->m_raysPerCluster);
+        cosZoD[nIndex].resize(table3gpp->m_raysPerCluster);
+    }
+    // pre-compute the terms which are independent from uIndex and sIndex
+    for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
+    {
+        for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
+        {
+            DoubleVector initialPhase = channelParams->m_clusterPhase[nIndex][mIndex];
+            NS_ASSERT(4 <= initialPhase.size());
+            double k = channelParams->m_crossPolarizationPowerRatios[nIndex][mIndex];
+
+            // cache the component of the "rays" terms which depend on the random angle of arrivals
+            // and departures and initial phases only
+            auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
+                Angles(channelParams->m_rayAoaRadian[nIndex][mIndex],
+                       channelParams->m_rayZoaRadian[nIndex][mIndex]));
+            auto [txFieldPatternPhi, txFieldPatternTheta] = sAntenna->GetElementFieldPattern(
+                Angles(channelParams->m_rayAodRadian[nIndex][mIndex],
+                       channelParams->m_rayZodRadian[nIndex][mIndex]));
+            raysPreComp(nIndex, mIndex) =
+                std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
+                    rxFieldPatternTheta * txFieldPatternTheta +
+                std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
+                    std::sqrt(1.0 / k) * rxFieldPatternTheta * txFieldPatternPhi +
+                std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
+                    std::sqrt(1.0 / k) * rxFieldPatternPhi * txFieldPatternTheta +
+                std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
+                    rxFieldPatternPhi * txFieldPatternPhi;
+
+            // 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 cosRayAoa = cos(rayAoaRadian[nIndex][mIndex]);
+            sinCosA[nIndex][mIndex] = sinRayZoa * cosRayAoa;
+            sinSinA[nIndex][mIndex] = sinRayZoa * sinRayAoa;
+            cosZoA[nIndex][mIndex] = cos(rayZoaRadian[nIndex][mIndex]);
+
+            // 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 cosRayAod = cos(rayAodRadian[nIndex][mIndex]);
+            sinCosD[nIndex][mIndex] = sinRayZod * cosRayAod;
+            sinSinD[nIndex][mIndex] = sinRayZod * sinRayAod;
+            cosZoD[nIndex][mIndex] = cos(rayZodRadian[nIndex][mIndex]);
+        }
+    }
+
     // The following for loops computes the channel coefficients
-    for (uint64_t uIndex = 0; uIndex < uSize; uIndex++)
+    // Keeps track of how many sub-clusters have been added up to now
+    uint8_t numSubClustersAdded = 0;
+    for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
     {
-        Vector uLoc = uAntenna->GetElementLocation(uIndex);
-
-        for (uint64_t sIndex = 0; sIndex < sSize; sIndex++)
+        for (size_t uIndex = 0; uIndex < uSize; uIndex++)
         {
-            Vector sLoc = sAntenna->GetElementLocation(sIndex);
+            Vector uLoc = uAntenna->GetElementLocation(uIndex);
 
-            for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
+            for (size_t sIndex = 0; sIndex < sSize; sIndex++)
             {
+                Vector sLoc = sAntenna->GetElementLocation(sIndex);
                 // Compute the N-2 weakest cluster, assuming 0 slant angle and a
                 // polarization slant angle configured in the array (7.5-22)
                 if (nIndex != channelParams->m_cluster1st && nIndex != channelParams->m_cluster2nd)
@@ -1937,52 +2008,26 @@
                     std::complex<double> rays(0, 0);
                     for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
                     {
-                        DoubleVector initialPhase = channelParams->m_clusterPhase[nIndex][mIndex];
-                        double k = channelParams->m_crossPolarizationPowerRatios[nIndex][mIndex];
                         // lambda_0 is accounted in the antenna spacing uLoc and sLoc.
-                        double rxPhaseDiff = 2 * M_PI *
-                                             (sin(rayZoaRadian[nIndex][mIndex]) *
-                                                  cos(rayAoaRadian[nIndex][mIndex]) * uLoc.x +
-                                              sin(rayZoaRadian[nIndex][mIndex]) *
-                                                  sin(rayAoaRadian[nIndex][mIndex]) * uLoc.y +
-                                              cos(rayZoaRadian[nIndex][mIndex]) * uLoc.z);
-
-                        double txPhaseDiff = 2 * M_PI *
-                                             (sin(rayZodRadian[nIndex][mIndex]) *
-                                                  cos(rayAodRadian[nIndex][mIndex]) * sLoc.x +
-                                              sin(rayZodRadian[nIndex][mIndex]) *
-                                                  sin(rayAodRadian[nIndex][mIndex]) * sLoc.y +
-                                              cos(rayZodRadian[nIndex][mIndex]) * sLoc.z);
+                        double rxPhaseDiff =
+                            2 * M_PI *
+                            (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
+                             cosZoA[nIndex][mIndex] * uLoc.z);
+
+                        double txPhaseDiff =
+                            2 * M_PI *
+                            (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
+                             cosZoD[nIndex][mIndex] * sLoc.z);
+
                         // NOTE Doppler is computed in the CalcBeamformingGain function and is
                         // simplified to only account for the center angle of each cluster.
-
-                        double rxFieldPatternPhi;
-                        double rxFieldPatternTheta;
-                        double txFieldPatternPhi;
-                        double txFieldPatternTheta;
-                        std::tie(rxFieldPatternPhi, rxFieldPatternTheta) =
-                            uAntenna->GetElementFieldPattern(
-                                Angles(channelParams->m_rayAoaRadian[nIndex][mIndex],
-                                       channelParams->m_rayZoaRadian[nIndex][mIndex]));
-                        std::tie(txFieldPatternPhi, txFieldPatternTheta) =
-                            sAntenna->GetElementFieldPattern(
-                                Angles(channelParams->m_rayAodRadian[nIndex][mIndex],
-                                       channelParams->m_rayZodRadian[nIndex][mIndex]));
-                        NS_ASSERT(4 <= initialPhase.size());
-                        rays += (std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
-                                     rxFieldPatternTheta * txFieldPatternTheta +
-                                 std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
-                                     std::sqrt(1 / k) * rxFieldPatternTheta * txFieldPatternPhi +
-                                 std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
-                                     std::sqrt(1 / k) * rxFieldPatternPhi * txFieldPatternTheta +
-                                 std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
-                                     rxFieldPatternPhi * txFieldPatternPhi) *
+                        rays += raysPreComp(nIndex, mIndex) *
                                 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
                                 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
                     }
                     rays *=
                         sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
-                    hUsn[uIndex][sIndex][nIndex] = rays;
+                    hUsn(uIndex, sIndex, nIndex) = rays;
                 }
                 else //(7.5-28)
                 {
@@ -1992,46 +2037,20 @@
 
                     for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
                     {
-                        double k = channelParams->m_crossPolarizationPowerRatios[nIndex][mIndex];
-
                         // ZML:Just remind me that the angle offsets for the 3 subclusters were not
                         // generated correctly.
-                        DoubleVector initialPhase = channelParams->m_clusterPhase[nIndex][mIndex];
-                        NS_ASSERT(4 <= initialPhase.size());
-
-                        double rxPhaseDiff = 2 * M_PI *
-                                             (sin(rayZoaRadian[nIndex][mIndex]) *
-                                                  cos(rayAoaRadian[nIndex][mIndex]) * uLoc.x +
-                                              sin(rayZoaRadian[nIndex][mIndex]) *
-                                                  sin(rayAoaRadian[nIndex][mIndex]) * uLoc.y +
-                                              cos(rayZoaRadian[nIndex][mIndex]) * uLoc.z);
-                        double txPhaseDiff = 2 * M_PI *
-                                             (sin(rayZodRadian[nIndex][mIndex]) *
-                                                  cos(rayAodRadian[nIndex][mIndex]) * sLoc.x +
-                                              sin(rayZodRadian[nIndex][mIndex]) *
-                                                  sin(rayAodRadian[nIndex][mIndex]) * sLoc.y +
-                                              cos(rayZodRadian[nIndex][mIndex]) * sLoc.z);
-
-                        double rxFieldPatternPhi;
-                        double rxFieldPatternTheta;
-                        double txFieldPatternPhi;
-                        double txFieldPatternTheta;
-                        std::tie(rxFieldPatternPhi, rxFieldPatternTheta) =
-                            uAntenna->GetElementFieldPattern(
-                                Angles(rayAoaRadian[nIndex][mIndex], rayZoaRadian[nIndex][mIndex]));
-                        std::tie(txFieldPatternPhi, txFieldPatternTheta) =
-                            sAntenna->GetElementFieldPattern(
-                                Angles(rayAodRadian[nIndex][mIndex], rayZodRadian[nIndex][mIndex]));
+                        double rxPhaseDiff =
+                            2 * M_PI *
+                            (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
+                             cosZoA[nIndex][mIndex] * uLoc.z);
+
+                        double txPhaseDiff =
+                            2 * M_PI *
+                            (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
+                             cosZoD[nIndex][mIndex] * sLoc.z);
 
                         std::complex<double> raySub =
-                            (std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
-                                 rxFieldPatternTheta * txFieldPatternTheta +
-                             std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
-                                 sqrt(1 / k) * rxFieldPatternTheta * txFieldPatternPhi +
-                             std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
-                                 sqrt(1 / k) * rxFieldPatternPhi * txFieldPatternTheta +
-                             std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
-                                 rxFieldPatternPhi * txFieldPatternPhi) *
+                            raysPreComp(nIndex, mIndex) *
                             std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
                             std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
 
@@ -2062,73 +2081,96 @@
                         sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
                     raysSub3 *=
                         sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
-                    hUsn[uIndex][sIndex][nIndex] = raysSub1;
-                    hUsn[uIndex][sIndex].push_back(raysSub2);
-                    hUsn[uIndex][sIndex].push_back(raysSub3);
+                    hUsn(uIndex, sIndex, nIndex) = raysSub1;
+                    hUsn(uIndex,
+                         sIndex,
+                         channelParams->m_reducedClusterNumber + numSubClustersAdded) = raysSub2;
+                    hUsn(uIndex,
+                         sIndex,
+                         channelParams->m_reducedClusterNumber + numSubClustersAdded + 1) =
+                        raysSub3;
                 }
             }
+        }
+        if (nIndex == channelParams->m_cluster1st || nIndex == channelParams->m_cluster2nd)
+        {
+            numSubClustersAdded += 2;
+        }
+    }
+
+    if (channelParams->m_losCondition == ChannelCondition::LOS) //(7.5-29) && (7.5-30)
+    {
+        double lambda = 3.0e8 / m_frequency; // the wavelength of the carrier frequency
+        std::complex<double> phaseDiffDueToDistance(cos(-2 * M_PI * distance3D / lambda),
+                                                    sin(-2 * M_PI * distance3D / lambda));
 
-            if (channelParams->m_losCondition == ChannelCondition::LOS) //(7.5-29) && (7.5-30)
+        const double sinUAngleIncl = sin(uAngle.GetInclination());
+        const double cosUAngleIncl = cos(uAngle.GetInclination());
+        const double sinUAngleAz = sin(uAngle.GetAzimuth());
+        const double cosUAngleAz = cos(uAngle.GetAzimuth());
+        const double sinSAngleIncl = sin(sAngle.GetInclination());
+        const double cosSAngleIncl = cos(sAngle.GetInclination());
+        const double sinSAngleAz = sin(sAngle.GetAzimuth());
+        const double cosSAngleAz = cos(sAngle.GetAzimuth());
+
+        for (size_t uIndex = 0; uIndex < uSize; uIndex++)
+        {
+            Vector uLoc = uAntenna->GetElementLocation(uIndex);
+            double rxPhaseDiff = 2 * M_PI *
+                                 (sinUAngleIncl * cosUAngleAz * uLoc.x +
+                                  sinUAngleIncl * sinUAngleAz * uLoc.y + cosUAngleIncl * uLoc.z);
+
+            for (size_t sIndex = 0; sIndex < sSize; sIndex++)
             {
+                Vector sLoc = sAntenna->GetElementLocation(sIndex);
                 std::complex<double> ray(0, 0);
-                double rxPhaseDiff =
-                    2 * M_PI *
-                    (sin(uAngle.GetInclination()) * cos(uAngle.GetAzimuth()) * uLoc.x +
-                     sin(uAngle.GetInclination()) * sin(uAngle.GetAzimuth()) * uLoc.y +
-                     cos(uAngle.GetInclination()) * uLoc.z);
                 double txPhaseDiff =
                     2 * M_PI *
-                    (sin(sAngle.GetInclination()) * cos(sAngle.GetAzimuth()) * sLoc.x +
-                     sin(sAngle.GetInclination()) * sin(sAngle.GetAzimuth()) * sLoc.y +
-                     cos(sAngle.GetInclination()) * sLoc.z);
-
-                double rxFieldPatternPhi;
-                double rxFieldPatternTheta;
-                double txFieldPatternPhi;
-                double txFieldPatternTheta;
-                std::tie(rxFieldPatternPhi, rxFieldPatternTheta) = uAntenna->GetElementFieldPattern(
+                    (sinSAngleIncl * cosSAngleAz * sLoc.x + sinSAngleIncl * sinSAngleAz * sLoc.y +
+                     cosSAngleIncl * sLoc.z);
+
+                auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
                     Angles(uAngle.GetAzimuth(), uAngle.GetInclination()));
-                std::tie(txFieldPatternPhi, txFieldPatternTheta) = sAntenna->GetElementFieldPattern(
+                auto [txFieldPatternPhi, txFieldPatternTheta] = sAntenna->GetElementFieldPattern(
                     Angles(sAngle.GetAzimuth(), sAngle.GetInclination()));
 
-                double lambda = 3e8 / m_frequency; // the wavelength of the carrier frequency
-
                 ray = (rxFieldPatternTheta * txFieldPatternTheta -
                        rxFieldPatternPhi * txFieldPatternPhi) *
-                      std::complex<double>(cos(-2 * M_PI * distance3D / lambda),
-                                           sin(-2 * M_PI * distance3D / lambda)) *
+                      phaseDiffDueToDistance *
                       std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
                       std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
 
-                double kLinear = pow(10, channelParams->m_K_factor / 10);
+                double kLinear = pow(10, channelParams->m_K_factor / 10.0);
                 // the LOS path should be attenuated if blockage is enabled.
-                hUsn[uIndex][sIndex][0] =
-                    sqrt(1 / (kLinear + 1)) * hUsn[uIndex][sIndex][0] +
+                hUsn(uIndex, sIndex, 0) =
+                    sqrt(1.0 / (kLinear + 1)) * hUsn(uIndex, sIndex, 0) +
                     sqrt(kLinear / (1 + kLinear)) * ray /
-                        pow(10, channelParams->m_attenuation_dB[0] / 10); //(7.5-30) for tau = tau1
-                double tempSize = hUsn[uIndex][sIndex].size();
-                for (uint8_t nIndex = 1; nIndex < tempSize; nIndex++)
+                        pow(10,
+                            channelParams->m_attenuation_dB[0] / 10.0); //(7.5-30) for tau = tau1
+                for (uint16_t nIndex = 1; nIndex < hUsn.GetNumPages(); nIndex++)
                 {
-                    hUsn[uIndex][sIndex][nIndex] *=
-                        sqrt(1 / (kLinear + 1)); //(7.5-30) for tau = tau2...taunN
+                    hUsn(uIndex, sIndex, nIndex) *=
+                        sqrt(1.0 / (kLinear + 1)); //(7.5-30) for tau = tau2...tauN
                 }
             }
         }
     }
 
     NS_LOG_DEBUG("Husn (sAntenna, uAntenna):" << sAntenna->GetId() << ", " << uAntenna->GetId());
-    for (auto& i : hUsn)
+    for (uint16_t cIndex = 0; cIndex < hUsn.GetNumPages(); cIndex++)
     {
-        for (auto& j : i)
+        for (uint16_t rowIdx = 0; rowIdx < hUsn.GetNumRows(); rowIdx++)
         {
-            for (auto& k : j)
+            for (uint16_t colIdx = 0; colIdx < hUsn.GetNumCols(); colIdx++)
             {
-                NS_LOG_DEBUG(" " << k << ",");
+                NS_LOG_DEBUG(" " << hUsn(rowIdx, colIdx, cIndex) << ",");
             }
         }
     }
-    NS_LOG_INFO("size of coefficient matrix =[" << hUsn.size() << "][" << hUsn[0].size() << "]["
-                                                << hUsn[0][0].size() << "]");
+
+    NS_LOG_INFO("size of coefficient matrix (rows, columns, clusters) = ("
+                << hUsn.GetNumRows() << ", " << hUsn.GetNumCols() << ", " << hUsn.GetNumPages()
+                << ")");
     channelMatrix->m_channel = hUsn;
     return channelMatrix;
 }
@@ -2187,7 +2229,7 @@
     }
 
     // generate or update non-self blocking
-    if (channelParams->m_nonSelfBlocking.size() == 0) // generate new blocking regions
+    if (channelParams->m_nonSelfBlocking.empty()) // generate new blocking regions
     {
         for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
         {
@@ -2288,14 +2330,14 @@
                       "the ZOA should be the range of [0,180]");
 
         // check self blocking
-        NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiSb - xSb / 2 << ","
-                           << phiSb + xSb / 2 << "]");
-        NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaSb - ySb / 2 << ","
-                           << thetaSb + ySb / 2 << "]");
-        if (std::abs(clusterAOA[cInd] - phiSb) < (xSb / 2) &&
-            std::abs(clusterZOA[cInd] - thetaSb) < (ySb / 2))
+        NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiSb - xSb / 2.0 << ","
+                           << phiSb + xSb / 2.0 << "]");
+        NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaSb - ySb / 2.0 << ","
+                           << thetaSb + ySb / 2.0 << "]");
+        if (std::abs(clusterAOA[cInd] - phiSb) < (xSb / 2.0) &&
+            std::abs(clusterZOA[cInd] - thetaSb) < (ySb / 2.0))
         {
-            powerAttenuation[cInd] += 30; // anttenuate by 30 dB.
+            powerAttenuation[cInd] += 30; // attenuate by 30 dB.
             NS_LOG_INFO("Cluster[" << +cInd
                                    << "] is blocked by self blocking region and reduce 30 dB power,"
                                       "the attenuation is ["
@@ -2331,17 +2373,17 @@
             if (std::abs(clusterAOA[cInd] - phiK) < (xK) &&
                 std::abs(clusterZOA[cInd] - thetaK) < (yK))
             {
-                double A1 = clusterAOA[cInd] - (phiK + xK / 2);   //(7.6-24)
-                double A2 = clusterAOA[cInd] - (phiK - xK / 2);   //(7.6-25)
-                double Z1 = clusterZOA[cInd] - (thetaK + yK / 2); //(7.6-26)
-                double Z2 = clusterZOA[cInd] - (thetaK - yK / 2); //(7.6-27)
+                double A1 = clusterAOA[cInd] - (phiK + xK / 2.0);   //(7.6-24)
+                double A2 = clusterAOA[cInd] - (phiK - xK / 2.0);   //(7.6-25)
+                double Z1 = clusterZOA[cInd] - (thetaK + yK / 2.0); //(7.6-26)
+                double Z2 = clusterZOA[cInd] - (thetaK - yK / 2.0); //(7.6-27)
                 int signA1;
                 int signA2;
                 int signZ1;
                 int signZ2;
                 // draw sign for the above parameters according to table 7.6.4.1-3 Description of
                 // signs
-                if (xK / 2 < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= xK)
+                if (xK / 2.0 < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= xK)
                 {
                     signA1 = -1;
                 }
@@ -2349,7 +2391,7 @@
                 {
                     signA1 = 1;
                 }
-                if (-1 * xK < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= -1 * xK / 2)
+                if (-1 * xK < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= -1 * xK / 2.0)
                 {
                     signA2 = -1;
                 }
@@ -2358,7 +2400,7 @@
                     signA2 = 1;
                 }
 
-                if (yK / 2 < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= yK)
+                if (yK / 2.0 < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= yK)
                 {
                     signZ1 = -1;
                 }
@@ -2366,7 +2408,8 @@
                 {
                     signZ1 = 1;
                 }
-                if (-1 * yK < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= -1 * yK / 2)
+                if (-1 * yK < clusterZOA[cInd] - thetaK &&
+                    clusterZOA[cInd] - thetaK <= -1 * yK / 2.0)
                 {
                     signZ2 = -1;
                 }
@@ -2376,24 +2419,24 @@
                 }
                 double lambda = 3e8 / m_frequency;
                 double fA1 =
-                    atan(signA1 * M_PI / 2 *
+                    atan(signA1 * M_PI / 2.0 *
                          sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
-                              (1 / cos(DegreesToRadians(A1)) - 1))) /
+                              (1.0 / cos(DegreesToRadians(A1)) - 1))) /
                     M_PI; //(7.6-23)
                 double fA2 =
-                    atan(signA2 * M_PI / 2 *
+                    atan(signA2 * M_PI / 2.0 *
                          sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
-                              (1 / cos(DegreesToRadians(A2)) - 1))) /
+                              (1.0 / cos(DegreesToRadians(A2)) - 1))) /
                     M_PI;
                 double fZ1 =
-                    atan(signZ1 * M_PI / 2 *
+                    atan(signZ1 * M_PI / 2.0 *
                          sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
-                              (1 / cos(DegreesToRadians(Z1)) - 1))) /
+                              (1.0 / cos(DegreesToRadians(Z1)) - 1))) /
                     M_PI;
                 double fZ2 =
-                    atan(signZ2 * M_PI / 2 *
+                    atan(signZ2 * M_PI / 2.0 *
                          sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
-                              (1 / cos(DegreesToRadians(Z2)) - 1))) /
+                              (1.0 / cos(DegreesToRadians(Z2)) - 1))) /
                     M_PI;
                 double lDb = -20 * log10(1 - (fA1 + fA2) * (fZ1 + fZ2)); //(7.6-22)
                 powerAttenuation[cInd] += lDb;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/three-gpp-channel-model.h ns-3.38/src/spectrum/model/three-gpp-channel-model.h
--- ns-3.37/src/spectrum/model/three-gpp-channel-model.h	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/three-gpp-channel-model.h	2023-03-16 20:54:23.828869000 -0700
@@ -25,9 +25,6 @@
 #include <ns3/boolean.h>
 #include <ns3/channel-condition-model.h>
 #include <ns3/matrix-based-channel-model.h>
-#include <ns3/nstime.h>
-#include <ns3/object.h>
-#include <ns3/random-variable-stream.h>
 
 #include <complex.h>
 #include <unordered_map>
@@ -260,7 +257,7 @@
 
     /**
      * Prepare 3gpp channel parameters among the nodes a and b.
-     * The function does the followin steps described in 3GPP 38.901:
+     * The function does the following steps described in 3GPP 38.901:
      *
      * Step 4: Generate large scale parameters. All LSPS are uncorrelated.
      * Step 5: Generate Delays.
@@ -364,7 +361,7 @@
     // parameters for the blockage model
     bool m_blockage;               //!< enables the blockage model A
     uint16_t m_numNonSelfBlocking; //!< number of non-self-blocking regions
-    bool m_portraitMode;           //!< true if potrait mode, false if landscape
+    bool m_portraitMode;           //!< true if portrait mode, false if landscape
     double m_blockerSpeed;         //!< the blocker speed
 
     static const uint8_t PHI_INDEX = 0; //!< index of the PHI value in the m_nonSelfBlocking array
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc ns-3.38/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc
--- ns-3.37/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc	2023-03-16 20:54:23.828869000 -0700
@@ -119,37 +119,19 @@
 {
     NS_LOG_FUNCTION(this);
 
-    uint16_t sAntenna = static_cast<uint16_t>(sW.size());
-    uint16_t uAntenna = static_cast<uint16_t>(uW.size());
+    size_t uAntennaNum = uW.GetSize();
+    size_t sAntennaNum = sW.GetSize();
 
-    NS_ASSERT(uAntenna == params->m_channel.size());
-    NS_ASSERT(sAntenna == params->m_channel.at(0).size());
+    NS_ASSERT(uAntennaNum == params->m_channel.GetNumRows());
+    NS_ASSERT(sAntennaNum == params->m_channel.GetNumCols());
 
-    NS_LOG_DEBUG("CalcLongTerm with sAntenna " << sAntenna << " uAntenna " << uAntenna);
+    NS_LOG_DEBUG("CalcLongTerm with " << uAntennaNum << " u antenna elements and " << sAntennaNum
+                                      << " s antenna elements.");
     // store the long term part to reduce computation load
     // only the small scale fading needs to be updated if the large scale parameters and antenna
-    // weights remain unchanged.
-    PhasedArrayModel::ComplexVector longTerm;
-    uint8_t numCluster = static_cast<uint8_t>(params->m_channel[0][0].size());
-
-    NS_ASSERT(uAntenna == params->m_channel.size());
-    NS_ASSERT(sAntenna == params->m_channel.at(0).size());
-
-    for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
-    {
-        std::complex<double> txSum(0, 0);
-        for (uint16_t sIndex = 0; sIndex < sAntenna; sIndex++)
-        {
-            std::complex<double> rxSum(0, 0);
-            for (uint16_t uIndex = 0; uIndex < uAntenna; uIndex++)
-            {
-                rxSum = rxSum + uW[uIndex] * params->m_channel[uIndex][sIndex][cIndex];
-            }
-            txSum = txSum + sW[sIndex] * rxSum;
-        }
-        longTerm.push_back(txSum);
-    }
-    return longTerm;
+    // weights remain unchanged. here we calculate long term uW * Husn * sW, the result is an array
+    // of values per cluster
+    return params->m_channel.MultiplyByLeftAndRightMatrix(uW.Transpose(), sW);
 }
 
 Ptr<SpectrumValue>
@@ -165,15 +147,15 @@
 
     Ptr<SpectrumValue> tempPsd = Copy<SpectrumValue>(txPsd);
 
-    // channel[rx][tx][cluster]
-    uint8_t numCluster = static_cast<uint8_t>(channelMatrix->m_channel[0][0].size());
+    // channel[cluster][rx][tx]
+    uint16_t numCluster = channelMatrix->m_channel.GetNumPages();
 
     // compute the doppler term
     // NOTE the update of Doppler is simplified by only taking the center angle of
     // each cluster in to consideration.
     double slotTime = Simulator::Now().GetSeconds();
     double factor = 2 * M_PI * slotTime * GetFrequency() / 3e8;
-    PhasedArrayModel::ComplexVector doppler;
+    PhasedArrayModel::ComplexVector doppler(numCluster);
 
     // The following asserts might seem paranoic, but it is important to
     // make sure that all the structures that are passed to this function
@@ -186,7 +168,7 @@
     NS_ASSERT(numCluster <= channelParams->m_angle[MatrixBasedChannelModel::ZOD_INDEX].size());
     NS_ASSERT(numCluster <= channelParams->m_angle[MatrixBasedChannelModel::AOA_INDEX].size());
     NS_ASSERT(numCluster <= channelParams->m_angle[MatrixBasedChannelModel::AOD_INDEX].size());
-    NS_ASSERT(numCluster <= longTerm.size());
+    NS_ASSERT(numCluster <= longTerm.GetSize());
 
     // check if channelParams structure is generated in direction s-to-u or u-to-s
     bool isSameDirection = (channelParams->m_nodeIds == channelMatrix->m_nodeIds);
@@ -197,9 +179,9 @@
     MatrixBasedChannelModel::DoubleVector aod;
 
     // if channel params is generated in the same direction in which we
-    // generate the channel matrix, angles and zenit od departure and arrival are ok,
+    // generate the channel matrix, angles and zenith od departure and arrival are ok,
     // just set them to corresponding variable that will be used for the generation
-    // of channel matrix, otherwise we need to flip angles and zenits of departure and arrival
+    // of channel matrix, otherwise we need to flip angles and zeniths of departure and arrival
     if (isSameDirection)
     {
         zoa = channelParams->m_angle[MatrixBasedChannelModel::ZOA_INDEX];
@@ -215,11 +197,11 @@
         aoa = channelParams->m_angle[MatrixBasedChannelModel::AOD_INDEX];
     }
 
-    for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
+    for (uint16_t cIndex = 0; cIndex < numCluster; cIndex++)
     {
         // Compute alpha and D as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
         // These terms account for an additional Doppler contribution due to the
-        // presence of moving objects in the sorrounding environment, such as in
+        // presence of moving objects in the surrounding environment, such as in
         // vehicular scenarios.
         // This contribution is applied only to the delayed (reflected) paths and
         // must be properly configured by setting the value of
@@ -240,10 +222,10 @@
                        sin(zod[cIndex] * M_PI / 180) * sin(aod[cIndex] * M_PI / 180) * sSpeed.y +
                        cos(zod[cIndex] * M_PI / 180) * sSpeed.z) +
                       2 * alpha * D);
-        doppler.emplace_back(cos(tempDoppler), sin(tempDoppler));
+        doppler[cIndex] = std::complex<double>(cos(tempDoppler), sin(tempDoppler));
     }
 
-    NS_ASSERT(numCluster <= doppler.size());
+    NS_ASSERT(numCluster <= doppler.GetSize());
 
     // apply the doppler term and the propagation delay to the long term component
     // to obtain the beamforming gain
@@ -255,7 +237,7 @@
         {
             std::complex<double> subsbandGain(0.0, 0.0);
             double fsb = (*sbit).fc; // center frequency of the sub-band
-            for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
+            for (uint16_t cIndex = 0; cIndex < numCluster; cIndex++)
             {
                 double delay = -2 * M_PI * fsb * (channelParams->m_delay[cIndex]);
                 subsbandGain = subsbandGain + longTerm[cIndex] * doppler[cIndex] *
@@ -279,7 +261,7 @@
         longTerm; // vector containing the long term component for each cluster
 
     // check if the channel matrix was generated considering a as the s-node and
-    // b as the u-node or viceversa
+    // b as the u-node or vice-versa
     PhasedArrayModel::ComplexVector sW;
     PhasedArrayModel::ComplexVector uW;
     if (!channelMatrix->IsReverse(aPhasedArrayModel->GetId(), bPhasedArrayModel->GetId()))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/tv-spectrum-transmitter.h ns-3.38/src/spectrum/model/tv-spectrum-transmitter.h
--- ns-3.37/src/spectrum/model/tv-spectrum-transmitter.h	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/model/tv-spectrum-transmitter.h	2023-03-16 20:54:23.828869000 -0700
@@ -129,7 +129,7 @@
     /** Sets up signal to be transmitted */
     virtual void SetupTx();
 
-    enum TvType m_tvType;      //!< Type of TV transmitter
+    TvType m_tvType;           //!< Type of TV transmitter
     double m_startFrequency;   //!< Start frequency (in Hz) of TV transmitter's signal
     double m_channelBandwidth; //!< Bandwidth (in Hz) of TV transmitter's signal
     double m_basePsd; //!< Base power spectral density value (in dBm/Hz) of TV transmitter's signal
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc ns-3.38/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc
--- ns-3.37/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc	2023-03-16 20:54:23.828869000 -0700
@@ -0,0 +1,1178 @@
+/*
+ * Copyright (c) 2022 SIGNET Lab, Department of Information Engineering,
+ * University of Padova
+ *
+ * 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 "two-ray-spectrum-propagation-loss-model.h"
+
+#include <ns3/double.h>
+#include <ns3/log.h>
+#include <ns3/node.h>
+#include <ns3/object-factory.h>
+#include <ns3/pointer.h>
+#include <ns3/random-variable-stream.h>
+#include <ns3/string.h>
+
+#include <algorithm>
+
+namespace ns3
+{
+
+/**
+ * Lookup table associating the simulation parameters to the corresponding fitted FTR parameters.
+ * The table is implemented as a nested map.
+ */
+static const TwoRaySpectrumPropagationLossModel::FtrParamsLookupTable
+    SIM_PARAMS_TO_FTR_PARAMS_TABLE = {
+        {"InH-OfficeOpen",
+         {{ChannelCondition::LosConditionValue::NLOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(1.2695458989293662e+01,
+                                                           3.081006083786e-03,
+                                                           1.6128465196199136e+02,
+                                                           2.7586206896551727e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.253444859420023e+00,
+                                                           6.334753732969e-04,
+                                                           7.882966657847448e+02,
+                                                           3.4482758620689657e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.2440475852275e+00,
+                                                           8.688106642961e-04,
+                                                           5.744993815655636e+02,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.249919762622172e+00,
+                                                           4.2221674779498e-03,
+                                                           1.1742258806909902e+02,
+                                                           3.4482758620689657e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.25697129961412e+00,
+                                                           4.2211041740365e-03,
+                                                           1.174524189370726e+02,
+                                                           6.896551724137931e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.2440475852275e+00,
+                                                           5.7752786216822e-03,
+                                                           8.557590962327636e+01,
+                                                           1.724137931034e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.251094645653469e+00,
+                                                           5.7927052949143e-03,
+                                                           8.53154561719156e+01,
+                                                           1.36482758620689e-02),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.724107824633563e+00,
+                                                           1.295909468305e-04,
+                                                           3.8572942113521017e+03,
+                                                           6.9862068965517e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.737785902809381e+00,
+                                                           2.44090689375e-04,
+                                                           2.047418976079014e+03,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.733508517158048e+00,
+                                                           5.006171052321396e-05,
+                                                           9.98667310933466e+03,
+                                                           1.310344827586e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.742066005621902e+00,
+                                                           9.428291321439076e-05,
+                                                           5.302187851896829e+03,
+                                                           1.03448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.748062717676833e+00,
+                                                           2.446180232004e-04,
+                                                           2.0430031092487088e+03,
+                                                           6.8896551724137e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.737785902809381e+00,
+                                                           1.6399062365392e-03,
+                                                           3.0389548052160666e+02,
+                                                           8.965517241379312e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.735219145469659e+00,
+                                                           1.296074026206e-04,
+                                                           3.856804337484282e+03,
+                                                           1.172413793103e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.736930208361434e+00,
+                                                           1.294265037695e-04,
+                                                           3.862196373520788e+03,
+                                                           6.6965517241379e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.727524738792882e+00,
+                                                           3.354626304689e-04,
+                                                           1.4894789821180611e+03,
+                                                           1.51724137931e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.26520488726192e+00,
+                                                           1.07946501149867e-02,
+                                                           4.531924098269983e+01,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.736074622586126e+00,
+                                                           2.2451744578083e-03,
+                                                           2.2169984332891184e+02,
+                                                           7.0965517241379e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.733508517158048e+00,
+                                                           4.2274879823188e-03,
+                                                           1.172735473385635e+02,
+                                                           1.241379310344e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.729233846942953e+00,
+                                                           3.360165965941e-04,
+                                                           1.487021737818881e+03,
+                                                           6.896551724137932e-06)}}},
+          {ChannelCondition::LosConditionValue::LOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(6.221618627242562e+01,
+                                                           5.7818075439484e-03,
+                                                           8.547814653106583e+01,
+                                                           1.310344827586e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.222408869140862e+01,
+                                                           5.7868906372814e-03,
+                                                           8.540218579193503e+01,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.1712469578065404e+02,
+                                                           7.9044249538951e-03,
+                                                           6.225570840591133e+01,
+                                                           2.e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.174524189370726e+02,
+                                                           7.9192593798279e-03,
+                                                           6.213721725968569e+01,
+                                                           1.586206896551e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.212932587331828e+01,
+                                                           4.2317491714902e-03,
+                                                           1.1715445096995552e+02,
+                                                           1.03448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(5.74353468732457e+02,
+                                                           7.9252008170168e-03,
+                                                           6.208988397195024e+01,
+                                                           5.517241379310345e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.295678257898671e+01,
+                                                           1.6409445497067e-03,
+                                                           3.0370255688368803e+02,
+                                                           1.448275862068e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.1721398402813756e+02,
+                                                           7.9222295504019e-03,
+                                                           6.211354610705902e+01,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.395445815898452e+01,
+                                                           6.863706760298528e-05,
+                                                           7.283693496699635e+03,
+                                                           2.06896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.220038444513843e+01,
+                                                           5.7854378744067e-03,
+                                                           8.542388196956914e+01,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.293586044359225e+01,
+                                                           2.246026238725e-03,
+                                                           2.2161538684598275e+02,
+                                                           1.724137931034e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.534227035109218e+01,
+                                                           4.2328151340042e-03,
+                                                           1.1712469578065404e+02,
+                                                           2.7586206896551727e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.296096860066171e+01,
+                                                           1.1934793291051e-03,
+                                                           4.179431587180458e+02,
+                                                           9.655172413793105e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.395750074607037e+01,
+                                                           4.990302323746383e-05,
+                                                           1.0018433043580286e+04,
+                                                           1.51724137931e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.1831489711041263e+02,
+                                                           7.926191482854e-03,
+                                                           6.20819985968797e+01,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.290659176610291e+01,
+                                                           1.1937818051584e-03,
+                                                           4.1783700843777945e+02,
+                                                           1.448275862068e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.176778335658431e+02,
+                                                           7.926191482854e-03,
+                                                           6.20819985968797e+01,
+                                                           2.e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.292331353831016e+01,
+                                                           8.692513904904e-04,
+                                                           5.742075929586678e+02,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.21474697532092e+02,
+                                                           7.9212393717809e-03,
+                                                           6.212143548915179e+01,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.29902856434769e+01,
+                                                           2.2511436839814e-03,
+                                                           2.211093231666549e+02,
+                                                           1.586206896551e-04)}}}}},
+        {"UMi-StreetCanyon",
+         {{ChannelCondition::LosConditionValue::NLOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(1.2689010957474556e+01,
+                                                           3.36059247219e-04,
+                                                           1.486832887020803e+03,
+                                                           1.724137931034e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2734216047612705e+01,
+                                                           8.70685292542e-04,
+                                                           5.732603031000779e+02,
+                                                           7.0551724137931e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2690622658336885e+01,
+                                                           3.086065900037e-03,
+                                                           1.6101857516847045e+02,
+                                                           0.e+00),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2711593407712735e+01,
+                                                           3.0864554585433e-03,
+                                                           1.6099812591365952e+02,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.29902856434769e+01,
+                                                           1.47463951390083e-02,
+                                                           3.290659176610291e+01,
+                                                           1.310344827586e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2718052915001302e+01,
+                                                           3.0884039842316e-03,
+                                                           1.6089591858864094e+02,
+                                                           1.51724137931e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.748207473187881e+01,
+                                                           1.07973332444199e-02,
+                                                           4.530773068511169e+01,
+                                                           6.8068965517241e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.255795670251574e+00,
+                                                           5.00680684988557e-05,
+                                                           9.985404808314654e+03,
+                                                           6.9586206896551e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.254620190193195e+00,
+                                                           4.617679219256e-04,
+                                                           1.0817950064503143e+03,
+                                                           2.07655172413793e-02),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.7488737060551784e+01,
+                                                           1.08201661923657e-02,
+                                                           4.521001111357971e+01,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.264028212239072e+00,
+                                                           2.445869720667e-04,
+                                                           2.0432626022764905e+03,
+                                                           1.655172413793e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.251094645653469e+00,
+                                                           1.295909468305e-04,
+                                                           3.8572942113521017e+03,
+                                                           1.655172413793e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.261675310485298e+00,
+                                                           6.868066254013259e-05,
+                                                           7.27906954953051e+03,
+                                                           1.103448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.242873597168312e+00,
+                                                           2.443697244117e-04,
+                                                           2.0450799765751449e+03,
+                                                           1.241379310344e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.264028212239072e+00,
+                                                           6.329934316604e-04,
+                                                           7.888976118731497e+02,
+                                                           6.7241379310344e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.2440475852275e+00,
+                                                           5.0074427281897054e-05,
+                                                           9.984136668368055e+03,
+                                                           1.03448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.242873597168312e+00,
+                                                           1.7827515329e-04,
+                                                           2.80365331692424e+03,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.242873597168312e+00,
+                                                           2.448665741566e-04,
+                                                           2.040928351071028e+03,
+                                                           1.37862068965517e-02),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.25697129961412e+00,
+                                                           3.357181936422e-04,
+                                                           1.4883443652113867e+03,
+                                                           2.06896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.262851686653192e+00,
+                                                           2.2474465885176e-03,
+                                                           2.21474697532092e+02,
+                                                           1.379310344827e-04)}}},
+          {ChannelCondition::LosConditionValue::LOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(6.212932587331828e+01,
+                                                           3.0821730014407e-03,
+                                                           1.6122321062648754e+02,
+                                                           0.e+00),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.290659176610291e+01,
+                                                           1.297720754633e-04,
+                                                           3.8519090192542544e+03,
+                                                           7.586206896551724e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.297771800493267e+01,
+                                                           2.445559248725e-04,
+                                                           2.0435221282639047e+03,
+                                                           6.896551724137931e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.295259708893377e+01,
+                                                           1.77777895565e-04,
+                                                           2.81149813657053e+03,
+                                                           6.896551724137932e-06),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.5181310193778565e+01,
+                                                           1.6374169558852e-03,
+                                                           3.043589974153497e+02,
+                                                           1.51724137931e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.2965155154026256e+01,
+                                                           6.879413898912483e-05,
+                                                           7.267061020126167e+03,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.29902856434769e+01,
+                                                           6.85760811693159e-05,
+                                                           7.290171957835979e+03,
+                                                           6.896551724137932e-06),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.295259708893377e+01,
+                                                           1.77935963353e-04,
+                                                           2.8089996795357683e+03,
+                                                           8.965517241379312e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.531348546843247e+01,
+                                                           3.077119528339e-03,
+                                                           1.614896255719667e+02,
+                                                           6.896551724137932e-06),
+             TwoRaySpectrumPropagationLossModel::FtrParams(7.281843565459175e+03,
+                                                           7.9123332420076e-03,
+                                                           6.219248503657932e+01,
+                                                           2.06896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.8582741457603433e+03,
+                                                           7.9143115301227e-03,
+                                                           6.217668922899309e+01,
+                                                           1.586206896551e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.2948412130435365e+01,
+                                                           1.294922559454e-04,
+                                                           3.860234761488555e+03,
+                                                           1.51724137931e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(5.748643258446342e+02,
+                                                           7.9143115301227e-03,
+                                                           6.217668922899309e+01,
+                                                           1.241379310344e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.2965155154026256e+01,
+                                                           1.779585559315e-04,
+                                                           2.8086429383948757e+03,
+                                                           1.586206896551e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.215300303953259e+01,
+                                                           3.0845081545813e-03,
+                                                           1.6110039816473343e+02,
+                                                           1.862068965517e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(7.27814511240983e+03,
+                                                           7.9073896501251e-03,
+                                                           6.223199211412119e+01,
+                                                           7.586206896551724e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.299866672987281e+01,
+                                                           1.779585559315e-04,
+                                                           2.8086429383948757e+03,
+                                                           4.137931034482759e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.8553350891312034e+03,
+                                                           7.9054130644305e-03,
+                                                           6.224780197124505e+01,
+                                                           1.241379310344e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.292749530889876e+01,
+                                                           3.362299038257e-04,
+                                                           1.4860777236373208e+03,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.5284718859438e+01,
+                                                           2.2451744578083e-03,
+                                                           2.2169984332891184e+02,
+                                                           1.448275862068e-04)}}}}},
+        {"InH-OfficeMixed",
+         {{ChannelCondition::LosConditionValue::NLOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(1.2726131915259918e+01,
+                                                           4.2274879823188e-03,
+                                                           1.172735473385635e+02,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.251094645653469e+00,
+                                                           1.6390760575064e-03,
+                                                           3.040499076660591e+02,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.261675310485298e+00,
+                                                           8.70354182635e-04,
+                                                           5.734787696501294e+02,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.26520488726192e+00,
+                                                           4.2211041740365e-03,
+                                                           1.174524189370726e+02,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.254620190193195e+00,
+                                                           4.2179158550445e-03,
+                                                           1.1754195701936764e+02,
+                                                           1.586206896551e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.254620190193195e+00,
+                                                           4.2226992294898e-03,
+                                                           1.1740767547642784e+02,
+                                                           6.7379310344827e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.731798323316221e+00,
+                                                           9.44506754620425e-05,
+                                                           5.292768388146024e+03,
+                                                           1.03448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.245221722401345e+00,
+                                                           5.77890490064e-03,
+                                                           8.55215829982986e+01,
+                                                           1.51724137931e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.7258160647265015e+00,
+                                                           6.329131436638e-04,
+                                                           7.889978140847802e+02,
+                                                           6.8344827586206e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.724107824633563e+00,
+                                                           8.691411880231e-04,
+                                                           5.74280526213736e+02,
+                                                           6.896551724137931e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.736930208361434e+00,
+                                                           2.448976607693e-04,
+                                                           2.0406691544916384e+03,
+                                                           1.37310344827586e-02),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.731798323316221e+00,
+                                                           8.682600702285e-04,
+                                                           5.748643258446342e+02,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.737785902809381e+00,
+                                                           1.6407368346666e-03,
+                                                           3.03741131810801e+02,
+                                                           1.724137931034e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.736930208361434e+00,
+                                                           1.77935963353e-04,
+                                                           2.8089996795357683e+03,
+                                                           1.03448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.23935252748936e+00,
+                                                           7.910355440443e-03,
+                                                           6.22082848570447e+01,
+                                                           6.7379310344827e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.723253867292691e+00,
+                                                           4.610068300136e-04,
+                                                           1.0835826296873358e+03,
+                                                           1.172413793103e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.723253867292691e+00,
+                                                           6.344403562566e-04,
+                                                           7.870961465788879e+02,
+                                                           2.e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.744635372264776e+00,
+                                                           6.33636101933e-04,
+                                                           7.880964521665514e+02,
+                                                           1.379310344827e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.728379238600564e+00,
+                                                           4.603053960641e-04,
+                                                           1.0852353652060228e+03,
+                                                           4.827586206896552e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.740353638327108e+00,
+                                                           2.2468783413341e-03,
+                                                           2.2153096253672345e+02,
+                                                           2.e-04)}}},
+          {ChannelCondition::LosConditionValue::LOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(3.857539171618605e+03,
+                                                           1.08074007896676e-02,
+                                                           4.526459309976018e+01,
+                                                           0.e+00),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.296096860066171e+01,
+                                                           1.194841073721e-03,
+                                                           4.174656947244478e+02,
+                                                           1.310344827586e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.2150282824788948e+02,
+                                                           7.9271822705314e-03,
+                                                           6.207411422324672e+01,
+                                                           1.103448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.520426947143501e+01,
+                                                           4.2317491714902e-03,
+                                                           1.1715445096995552e+02,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(5.749373425149475e+02,
+                                                           7.9281731800636e-03,
+                                                           6.206623085092408e+01,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.221618627242562e+01,
+                                                           5.7796304264225e-03,
+                                                           8.551072181262026e+01,
+                                                           7.586206896551724e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.1719909792804452e+02,
+                                                           7.9014613513626e-03,
+                                                           6.227943373585816e+01,
+                                                           1.448275862068e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.289405601071394e+01,
+                                                           3.355052108261e-04,
+                                                           1.4892898192515456e+03,
+                                                           8.965517241379312e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.294004380783209e+01,
+                                                           1.6378315743006e-03,
+                                                           3.042816955330164e+02,
+                                                           2.7586206896551727e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.1724376190085948e+02,
+                                                           7.9162903049039e-03,
+                                                           6.2160897433266726e+01,
+                                                           0.e+00),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.2965155154026256e+01,
+                                                           1.1936305575734e-03,
+                                                           4.178900802074444e+02,
+                                                           6.896551724137932e-06),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.291495159031198e+01,
+                                                           2.247730765756e-03,
+                                                           2.214465703888772e+02,
+                                                           5.517241379310345e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.045523380497429e+02,
+                                                           7.9133223252391e-03,
+                                                           6.218458663124002e+01,
+                                                           1.862068965517e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.2150282824788948e+02,
+                                                           7.9133223252391e-03,
+                                                           6.218458663124002e+01,
+                                                           6.896551724137931e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.2948412130435365e+01,
+                                                           1.6409445497067e-03,
+                                                           3.0370255688368803e+02,
+                                                           2.e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.040112935246124e+02,
+                                                           7.9153008566728e-03,
+                                                           6.216879282971112e+01,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.526171872148543e+01,
+                                                           4.2232310474297e-03,
+                                                           1.1739276477764624e+02,
+                                                           7.586206896551724e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.6130513761004684e+02,
+                                                           7.9143115301227e-03,
+                                                           6.217668922899309e+01,
+                                                           1.862068965517e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.04436318936002e+02,
+                                                           7.9242102730054e-03,
+                                                           6.2097770348585485e+01,
+                                                           2.06896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.0417061383577973e+03,
+                                                           1.08121021183973e-02,
+                                                           4.524447628451687e+01,
+                                                           2.e-04)}}}}},
+        {"RMa",
+         {{ChannelCondition::LosConditionValue::NLOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(6.736074622586126e+00,
+                                                           3.353349217972e-04,
+                                                           1.4900466148894875e+03,
+                                                           7.0275862068965e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.745492045387843e+00,
+                                                           6.33073729829e-04,
+                                                           7.887974223871352e+02,
+                                                           6.9862068965517e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.26520488726192e+00,
+                                                           1.08228555237886e-02,
+                                                           4.5198528558475154e+01,
+                                                           8.275862068965519e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.724107824633563e+00,
+                                                           6.333146853796e-04,
+                                                           7.884969302666273e+02,
+                                                           4.827586206896552e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.727524738792882e+00,
+                                                           1.295580415165e-04,
+                                                           3.8582741457603433e+03,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.736930208361434e+00,
+                                                           1.297555987707e-04,
+                                                           3.8523982713408623e+03,
+                                                           8.275862068965519e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.731798323316221e+00,
+                                                           2.448665741566e-04,
+                                                           2.040928351071028e+03,
+                                                           1.37724137931034e-02),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2700297163351271e+01,
+                                                           1.47354930616003e-02,
+                                                           3.293167761063697e+01,
+                                                           3.4482758620689657e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.7258160647265015e+00,
+                                                           1.780941716339e-04,
+                                                           2.80650344277193e+03,
+                                                           1.37310344827586e-02),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.235832799160756e+00,
+                                                           7.910355440443e-03,
+                                                           6.22082848570447e+01,
+                                                           8.275862068965519e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.741209767603669e+00,
+                                                           9.430686099733985e-05,
+                                                           5.300841188565312e+03,
+                                                           2.7586206896551727e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.736930208361434e+00,
+                                                           4.613579473452e-04,
+                                                           1.0827572060417654e+03,
+                                                           2.7586206896551727e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.735219145469659e+00,
+                                                           3.363579531122e-04,
+                                                           1.4855116028137206e+03,
+                                                           6.8206896551724e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.741209767603669e+00,
+                                                           8.698026120933e-04,
+                                                           5.738430655969447e+02,
+                                                           2.06896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.742066005621902e+00,
+                                                           4.990302323746383e-05,
+                                                           1.0018433043580286e+04,
+                                                           6.896551724137931e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.241699758204852e+00,
+                                                           5.7934325329886e-03,
+                                                           8.530462116421079e+01,
+                                                           9.655172413793105e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.741209767603669e+00,
+                                                           3.355052108261e-04,
+                                                           1.4892898192515456e+03,
+                                                           1.379310344827e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2700297163351271e+01,
+                                                           1.47282293521982e-02,
+                                                           3.2948412130435365e+01,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.26520488726192e+00,
+                                                           5.7956147886341e-03,
+                                                           8.527212439663107e+01,
+                                                           6.9448275862068e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.728379238600564e+00,
+                                                           4.996643770687291e-05,
+                                                           1.0005716967361968e+04,
+                                                           6.896551724137931e-05)}}},
+          {ChannelCondition::LosConditionValue::LOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(2.804365576918811e+03,
+                                                           1.47100852650808e-02,
+                                                           3.29902856434769e+01,
+                                                           2.06896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.2153096253672345e+02,
+                                                           1.08134457224711e-02,
+                                                           4.523873026531778e+01,
+                                                           1.103448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.171693313991936e+02,
+                                                           1.08174775144271e-02,
+                                                           4.522149658579409e+01,
+                                                           8.275862068965519e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.400013756546019e+01,
+                                                           1.193328119751e-03,
+                                                           4.1799624397043993e+02,
+                                                           6.8620689655172e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.6144861002079188e+02,
+                                                           1.07973332444199e-02,
+                                                           4.530773068511169e+01,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.8125695840352355e+03,
+                                                           1.4702833700715e-02,
+                                                           3.300704994545942e+01,
+                                                           1.448275862068e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.520426947143501e+01,
+                                                           7.9162903049039e-03,
+                                                           6.2160897433266726e+01,
+                                                           6.8068965517241e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.0830322773699647e+03,
+                                                           1.47445775831495e-02,
+                                                           3.2910771412767524e+01,
+                                                           9.655172413793105e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.5296223310938565e+01,
+                                                           7.9014613513626e-03,
+                                                           6.227943373585816e+01,
+                                                           2.7586206896551727e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.859254329118079e+03,
+                                                           1.47245987995756e-02,
+                                                           3.295678257898671e+01,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.1719909792804452e+02,
+                                                           1.0795991598188e-02,
+                                                           4.531348546843247e+01,
+                                                           1.241379310344e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.2160897433266726e+01,
+                                                           7.9113442804138e-03,
+                                                           6.220038444513843e+01,
+                                                           4.827586206896552e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.400013756546019e+01,
+                                                           3.0778964494433e-03,
+                                                           1.6144861002079188e+02,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.7439938881994646e+01,
+                                                           6.334753732969e-04,
+                                                           7.882966657847448e+02,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.53307542044457e+01,
+                                                           7.9153008566728e-03,
+                                                           6.216879282971112e+01,
+                                                           7.586206896551724e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.2965155154026256e+01,
+                                                           5.7781794648689e-03,
+                                                           8.553244556351568e+01,
+                                                           1.3793103448275863e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(8.540218579193503e+01,
+                                                           1.07973332444199e-02,
+                                                           4.530773068511169e+01,
+                                                           1.172413793103e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.7439938881994646e+01,
+                                                           9.44506754620425e-05,
+                                                           5.292768388146024e+03,
+                                                           3.4482758620689657e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.74443694478918e+01,
+                                                           1.297391241696e-04,
+                                                           3.8528875855700608e+03,
+                                                           5.517241379310345e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(8.535880996662047e+01,
+                                                           1.07946501149867e-02,
+                                                           4.531924098269983e+01,
+                                                           1.379310344827e-04)}}}}},
+        {"UMa",
+         {{ChannelCondition::LosConditionValue::NLOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(1.7439938881994646e+01,
+                                                           2.2485835121112e-03,
+                                                           2.2136221039019213e+02,
+                                                           7.e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.399708956352588e+01,
+                                                           7.909366722081e-03,
+                                                           6.221618627242562e+01,
+                                                           3.4482758620689657e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.7490958400945924e+01,
+                                                           3.0778964494433e-03,
+                                                           1.6144861002079188e+02,
+                                                           2.e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2700297163351271e+01,
+                                                           8.689208249277e-04,
+                                                           5.744264205160073e+02,
+                                                           4.137931034482759e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2700297163351271e+01,
+                                                           4.993472040654454e-05,
+                                                           1.00120729866762e+04,
+                                                           1.241379310344e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.272451570474495e+01,
+                                                           2.447112002444e-04,
+                                                           2.0422248278813e+03,
+                                                           1.51724137931e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2716437730514135e+01,
+                                                           2.2511436839814e-03,
+                                                           2.211093231666549e+02,
+                                                           6.903448275862e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2701910297736497e+01,
+                                                           4.2253589833499e-03,
+                                                           1.1733314091660613e+02,
+                                                           1.103448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2687399461297195e+01,
+                                                           4.2205726216469e-03,
+                                                           1.1746733721285608e+02,
+                                                           7.0965517241379e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.253444859420023e+00,
+                                                           9.42948863457656e-05,
+                                                           5.301514477472012e+03,
+                                                           1.586206896551e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2693846674220437e+01,
+                                                           5.7767288633179e-03,
+                                                           8.555417483326706e+01,
+                                                           6.896551724137932e-06),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.2722899699487671e+01,
+                                                           5.7883437606822e-03,
+                                                           8.538049512475196e+01,
+                                                           8.965517241379312e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.2440475852275e+00,
+                                                           1.781620182218e-04,
+                                                           2.805434306201756e+03,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.237005892929576e+00,
+                                                           4.994740491021047e-05,
+                                                           1.0009530094583308e+04,
+                                                           1.586206896551e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.252269677913102e+00,
+                                                           1.29541591992e-04,
+                                                           3.858764206316572e+03,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.269913082102622e+00,
+                                                           1.295909468305e-04,
+                                                           3.8572942113521017e+03,
+                                                           6.9310344827586e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.24874502880026e+00,
+                                                           2.443387047809e-04,
+                                                           2.0453397333972343e+03,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.262851686653192e+00,
+                                                           9.421110633952792e-05,
+                                                           5.306229894934544e+03,
+                                                           1.379310344827e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.237005892929576e+00,
+                                                           1.298050351238e-04,
+                                                           3.8509307014772426e+03,
+                                                           1.38137931034482e-02),
+             TwoRaySpectrumPropagationLossModel::FtrParams(9.24757044416878e+00,
+                                                           1.297391241696e-04,
+                                                           3.8528875855700608e+03,
+                                                           1.103448275862e-04)}}},
+          {ChannelCondition::LosConditionValue::LOS,
+           {{500000000.0,   5500000000.0,  10500000000.0, 15500000000.0, 20500000000.0,
+             25500000000.0, 30500000000.0, 35500000000.0, 40500000000.0, 45500000000.0,
+             50500000000.0, 55500000000.0, 60500000000.0, 65500000000.0, 70500000000.0,
+             75500000000.0, 80500000000.0, 85500000000.0, 90500000000.0, 95500000000.0},
+            {TwoRaySpectrumPropagationLossModel::FtrParams(8.553244556351568e+01,
+                                                           3.0845081545813e-03,
+                                                           1.6110039816473343e+02,
+                                                           2.e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(8.556504153815182e+01,
+                                                           4.2290854294851e-03,
+                                                           1.1722887201899576e+02,
+                                                           6.896551724137932e-06),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.531348546843247e+01,
+                                                           1.1945383299169e-03,
+                                                           4.175717506735425e+02,
+                                                           8.965517241379312e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.534227035109218e+01,
+                                                           1.6382462973584e-03,
+                                                           3.042044132840143e+02,
+                                                           1.03448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.6142810615183308e+02,
+                                                           5.7847116281393e-03,
+                                                           8.543473212524282e+01,
+                                                           1.448275862068e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.0824822045768908e+03,
+                                                           7.9271822705314e-03,
+                                                           6.207411422324672e+01,
+                                                           1.03448275862e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(5.307577927251286e+03,
+                                                           7.9064012965024e-03,
+                                                           6.223989654069086e+01,
+                                                           6.7655172413793e-03),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.289823406514352e+01,
+                                                           1.1928746063501e-03,
+                                                           4.1815554018694206e+02,
+                                                           1.793103448275e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(5.742075929586678e+02,
+                                                           7.9143115301227e-03,
+                                                           6.217668922899309e+01,
+                                                           6.206896551724138e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.215300303953259e+01,
+                                                           5.7847116281393e-03,
+                                                           8.543473212524282e+01,
+                                                           6.896551724137931e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.290659176610291e+01,
+                                                           8.689208249277e-04,
+                                                           5.744264205160073e+02,
+                                                           1.724137931034e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.2175616553118e+02,
+                                                           7.909366722081e-03,
+                                                           6.221618627242562e+01,
+                                                           4.827586206896552e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.220038444513843e+01,
+                                                           5.7752786216822e-03,
+                                                           8.557590962327636e+01,
+                                                           4.137931034482759e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(4.5181310193778565e+01,
+                                                           4.2216357928014e-03,
+                                                           1.1743750255590038e+02,
+                                                           1.862068965517e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.299447592055982e+01,
+                                                           2.2485835121112e-03,
+                                                           2.2136221039019213e+02,
+                                                           1.448275862068e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(6.206623085092408e+01,
+                                                           5.7803560422275e-03,
+                                                           8.549986200630548e+01,
+                                                           1.931034482758e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(1.1713957243052236e+02,
+                                                           7.9153008566728e-03,
+                                                           6.216879282971112e+01,
+                                                           4.827586206896552e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.4024535522229847e+01,
+                                                           6.325118562159e-04,
+                                                           7.894990160837534e+02,
+                                                           1.448275862068e-04),
+             TwoRaySpectrumPropagationLossModel::FtrParams(2.394229167421632e+01,
+                                                           1.778907867973e-04,
+                                                           2.809713297758256e+03,
+                                                           8.275862068965519e-05),
+             TwoRaySpectrumPropagationLossModel::FtrParams(3.2910771412767524e+01,
+                                                           2.2474465885176e-03,
+                                                           2.21474697532092e+02,
+                                                           7.586206896551724e-05)}}}}},
+};
+
+NS_LOG_COMPONENT_DEFINE("TwoRaySpectrumPropagationLossModel");
+
+NS_OBJECT_ENSURE_REGISTERED(TwoRaySpectrumPropagationLossModel);
+
+TwoRaySpectrumPropagationLossModel::TwoRaySpectrumPropagationLossModel()
+{
+    NS_LOG_FUNCTION(this);
+
+    // Create the Random Number Generator (RNG) variables only once to speed-up the tests
+    m_uniformRv = CreateObject<UniformRandomVariable>();
+    m_uniformRv->SetAttribute("Min", DoubleValue(0));
+    m_uniformRv->SetAttribute("Max", DoubleValue(2 * M_PI));
+
+    m_normalRv = CreateObject<NormalRandomVariable>();
+    m_normalRv->SetAttribute("Mean", DoubleValue(0));
+
+    m_gammaRv = CreateObject<GammaRandomVariable>();
+}
+
+TwoRaySpectrumPropagationLossModel::~TwoRaySpectrumPropagationLossModel()
+{
+    NS_LOG_FUNCTION(this);
+
+    m_uniformRv = nullptr;
+    m_normalRv = nullptr;
+    m_gammaRv = nullptr;
+}
+
+void
+TwoRaySpectrumPropagationLossModel::DoDispose()
+{
+}
+
+TypeId
+TwoRaySpectrumPropagationLossModel::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::TwoRaySpectrumPropagationLossModel")
+            .SetParent<PhasedArraySpectrumPropagationLossModel>()
+            .SetGroupName("Spectrum")
+            .AddConstructor<TwoRaySpectrumPropagationLossModel>()
+            .AddAttribute(
+                "ChannelConditionModel",
+                "Pointer to the channel condition model.",
+                PointerValue(),
+                MakePointerAccessor(&TwoRaySpectrumPropagationLossModel::m_channelConditionModel),
+                MakePointerChecker<ChannelConditionModel>())
+            .AddAttribute(
+                "Scenario",
+                "The 3GPP scenario (RMa, UMa, UMi-StreetCanyon, InH-OfficeOpen, InH-OfficeMixed).",
+                StringValue("RMa"),
+                MakeStringAccessor(&TwoRaySpectrumPropagationLossModel::SetScenario),
+                MakeStringChecker())
+            .AddAttribute("Frequency",
+                          "The operating Frequency in Hz",
+                          DoubleValue(500.0e6),
+                          MakeDoubleAccessor(&TwoRaySpectrumPropagationLossModel::SetFrequency),
+                          MakeDoubleChecker<double>());
+    return tid;
+}
+
+void
+TwoRaySpectrumPropagationLossModel::SetScenario(const std::string& scenario)
+{
+    NS_LOG_FUNCTION(this);
+    if (scenario != "RMa" && scenario != "UMa" && scenario != "UMi-StreetCanyon" &&
+        scenario != "InH-OfficeOpen" && scenario != "InH-OfficeMixed" && scenario != "V2V-Urban" &&
+        scenario != "V2V-Highway")
+    {
+        NS_ABORT_MSG("Unknown scenario (" + scenario +
+                     "), choose between: RMa, UMa, UMi-StreetCanyon, "
+                     "InH-OfficeOpen, InH-OfficeMixed, V2V-Urban or V2V-Highway");
+    }
+
+    if (SIM_PARAMS_TO_FTR_PARAMS_TABLE.find(scenario) == SIM_PARAMS_TO_FTR_PARAMS_TABLE.end())
+    {
+        NS_ABORT_MSG("The specified scenario has not been calibrated yet.");
+    }
+
+    m_scenario = scenario;
+}
+
+void
+TwoRaySpectrumPropagationLossModel::SetFrequency(double f)
+{
+    NS_LOG_FUNCTION(this);
+    NS_ASSERT_MSG(f >= 500.0e6 && f <= 100.0e9,
+                  "Frequency should be between 0.5 and 100 GHz but is " << f);
+    m_frequency = f;
+}
+
+ChannelCondition::LosConditionValue
+TwoRaySpectrumPropagationLossModel::GetLosCondition(Ptr<const MobilityModel> a,
+                                                    Ptr<const MobilityModel> b) const
+{
+    NS_LOG_FUNCTION(this);
+
+    NS_ASSERT(m_channelConditionModel);
+    auto cond = m_channelConditionModel->GetChannelCondition(a, b);
+
+    return cond->GetLosCondition();
+}
+
+TwoRaySpectrumPropagationLossModel::FtrParams
+TwoRaySpectrumPropagationLossModel::GetFtrParameters(Ptr<const MobilityModel> a,
+                                                     Ptr<const MobilityModel> b) const
+{
+    NS_LOG_FUNCTION(this);
+
+    // Retrieve LOS condition
+    ChannelCondition::LosConditionValue cond = GetLosCondition(a, b);
+
+    // Retrieve the corresponding tuple and vectors
+    NS_ASSERT_MSG(SIM_PARAMS_TO_FTR_PARAMS_TABLE.find(m_scenario)->second.find(cond) !=
+                      SIM_PARAMS_TO_FTR_PARAMS_TABLE.find(m_scenario)->second.end(),
+                  "The specified scenario and channel condition are not supported");
+    auto scenAndCondTuple = SIM_PARAMS_TO_FTR_PARAMS_TABLE.find(m_scenario)->second.find(cond);
+
+    // Get references to the corresponding vectors
+    auto& fcVec = std::get<0>(scenAndCondTuple->second);
+    auto& ftrParamsVec = std::get<1>(scenAndCondTuple->second);
+
+    // Find closest carrier frequency which has been calibrated
+    auto idxOfClosestFc = SearchClosestFc(fcVec, m_frequency);
+
+    // Retrieve the corresponding FTR parameters
+    NS_ASSERT(ftrParamsVec.size() >= idxOfClosestFc && idxOfClosestFc >= 0);
+    FtrParams params = ftrParamsVec[idxOfClosestFc];
+
+    return params;
+}
+
+double
+TwoRaySpectrumPropagationLossModel::CalcBeamformingGain(
+    Ptr<const MobilityModel> a,
+    Ptr<const MobilityModel> b,
+    Ptr<const PhasedArrayModel> aPhasedArrayModel,
+    Ptr<const PhasedArrayModel> bPhasedArrayModel) const
+{
+    NS_LOG_FUNCTION(this);
+
+    // Get the relative angles between tx and rx phased arrays
+    Angles aAngle(b->GetPosition(), a->GetPosition());
+    Angles bAngle(a->GetPosition(), b->GetPosition());
+
+    // Compute the beamforming vectors and and array responses
+    auto aArrayResponse = aPhasedArrayModel->GetSteeringVector(aAngle);
+    auto aAntennaFields = aPhasedArrayModel->GetElementFieldPattern(aAngle);
+    auto aBfVector = aPhasedArrayModel->GetBeamformingVector();
+    auto bArrayResponse = bPhasedArrayModel->GetSteeringVector(bAngle);
+    auto bAntennaFields = bPhasedArrayModel->GetElementFieldPattern(bAngle);
+    auto bBfVector = bPhasedArrayModel->GetBeamformingVector();
+
+    std::complex<double> aArrayOverallResponse = 0;
+    std::complex<double> bArrayOverallResponse = 0;
+
+    // Compute the dot products between the array responses and the beamforming vectors
+    for (size_t i = 0; i < aPhasedArrayModel->GetNumberOfElements(); i++)
+    {
+        aArrayOverallResponse += aArrayResponse[i] * aBfVector[i];
+    }
+    for (size_t i = 0; i < bPhasedArrayModel->GetNumberOfElements(); i++)
+    {
+        bArrayOverallResponse += bArrayResponse[i] * bBfVector[i];
+    }
+
+    double gain = norm(aArrayOverallResponse) *
+                  (std::pow(aAntennaFields.first, 2) + std::pow(aAntennaFields.second, 2)) *
+                  norm(bArrayOverallResponse) *
+                  (std::pow(bAntennaFields.first, 2) + std::pow(bAntennaFields.second, 2));
+
+    // Retrieve LOS condition to check if a correction factor needs to be introduced
+    ChannelCondition::LosConditionValue cond = GetLosCondition(a, b);
+    if (cond == ChannelCondition::NLOS)
+    {
+        // The linear penalty factor to be multiplied to the beamforming gain whenever the link is
+        // in NLOS
+        constexpr double NLOS_BEAMFORMING_FACTOR = 1.0 / 19;
+        gain *= NLOS_BEAMFORMING_FACTOR;
+    }
+
+    return gain;
+}
+
+double
+TwoRaySpectrumPropagationLossModel::GetFtrFastFading(const FtrParams& params) const
+{
+    NS_LOG_FUNCTION(this);
+
+    // Set the RNG parameters
+    m_normalRv->SetAttribute("Variance", DoubleValue(params.m_sigma));
+    m_gammaRv->SetAttribute("Alpha", DoubleValue(params.m_m));
+    m_gammaRv->SetAttribute("Beta", DoubleValue(1.0 / params.m_m));
+
+    // Compute the specular components amplitudes from the FTR parameters
+    double cmnSqrtTerm = sqrt(1 - std::pow(params.m_delta, 2));
+    double v1 = sqrt(params.m_sigma) * sqrt(params.m_k * (1 - cmnSqrtTerm));
+    double v2 = sqrt(params.m_sigma) * sqrt(params.m_k * (1 + cmnSqrtTerm));
+    double sqrtGamma = sqrt(m_gammaRv->GetValue());
+
+    // Sample the random phases of the specular components, which are uniformly distributed in [0,
+    // 2*PI]
+    double phi1 = m_uniformRv->GetValue();
+    double phi2 = m_uniformRv->GetValue();
+
+    // Sample the normal-distributed real and imaginary parts of the diffuse components
+    double x = m_normalRv->GetValue();
+    double y = m_normalRv->GetValue();
+
+    // Compute the channel response by combining the above terms
+    std::complex<double> h = sqrtGamma * v1 * std::complex<double>(cos(phi1), sin(phi1)) +
+                             sqrtGamma * v2 * std::complex<double>(cos(phi2), sin(phi2)) +
+                             std::complex<double>(x, y);
+
+    return norm(h);
+}
+
+Ptr<SpectrumValue>
+TwoRaySpectrumPropagationLossModel::DoCalcRxPowerSpectralDensity(
+    Ptr<const SpectrumSignalParameters> params,
+    Ptr<const MobilityModel> a,
+    Ptr<const MobilityModel> b,
+    Ptr<const PhasedArrayModel> aPhasedArrayModel,
+    Ptr<const PhasedArrayModel> bPhasedArrayModel) const
+{
+    NS_LOG_FUNCTION(this);
+    uint32_t aId = a->GetObject<Node>()->GetId(); // Id of the node a
+    uint32_t bId = b->GetObject<Node>()->GetId(); // Id of the node b
+
+    NS_ASSERT_MSG(aId != bId, "The two nodes must be different from one another");
+    NS_ASSERT_MSG(a->GetDistanceFrom(b) > 0.0,
+                  "The position of a and b devices cannot be the same");
+
+    Ptr<SpectrumValue> rxPsd = Copy<SpectrumValue>(params->psd);
+
+    // Retrieve the antenna of device a
+    NS_ASSERT_MSG(aPhasedArrayModel, "Antenna not found for node " << aId);
+    NS_LOG_DEBUG("a node " << a->GetObject<Node>() << " antenna " << aPhasedArrayModel);
+
+    // Retrieve the antenna of the device b
+    NS_ASSERT_MSG(bPhasedArrayModel, "Antenna not found for device " << bId);
+    NS_LOG_DEBUG("b node " << bId << " antenna " << bPhasedArrayModel);
+
+    // Retrieve FTR params from table
+    FtrParams ftrParams = GetFtrParameters(a, b);
+
+    // Compute the FTR fading
+    double fading = GetFtrFastFading(ftrParams);
+
+    // Compute the beamforming gain
+    double bfGain = CalcBeamformingGain(a, b, aPhasedArrayModel, bPhasedArrayModel);
+
+    // Apply the above terms to the TX PSD
+    *rxPsd *= (fading * bfGain);
+
+    return rxPsd;
+}
+
+std::size_t
+TwoRaySpectrumPropagationLossModel::SearchClosestFc(const std::vector<double>& frequencies,
+                                                    double targetFc) const
+{
+    auto it = std::min_element(std::begin(frequencies),
+                               std::end(frequencies),
+                               [targetFc](double lhs, double rhs) {
+                                   return std::abs(lhs - targetFc) < std::abs(rhs - targetFc);
+                               });
+    return std::distance(std::begin(frequencies), it);
+}
+
+int64_t
+TwoRaySpectrumPropagationLossModel::AssignStreams(int64_t stream)
+{
+    NS_LOG_FUNCTION(this << stream);
+    m_normalRv->SetStream(stream);
+    m_uniformRv->SetStream(stream + 1);
+    m_gammaRv->SetStream(stream + 2);
+    return 3;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h ns-3.38/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h
--- ns-3.37/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h	2023-03-16 20:54:23.828869000 -0700
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2022 SIGNET Lab, Department of Information Engineering,
+ * University of Padova
+ *
+ * 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 TWO_RAY_SPECTRUM_PROPAGATION_LOSS_H
+#define TWO_RAY_SPECTRUM_PROPAGATION_LOSS_H
+
+#include "ns3/channel-condition-model.h"
+#include "ns3/phased-array-spectrum-propagation-loss-model.h"
+#include "ns3/spectrum-signal-parameters.h"
+
+class FtrFadingModelAverageTest;
+class ArrayResponseTest;
+class OverallGainAverageTest;
+
+namespace ns3
+{
+
+class NetDevice;
+
+/**
+ * \ingroup spectrum
+ * \brief Two Ray Spectrum Propagation Loss Model
+ *
+ * This class implements a performance-oriented channel and beamforming models alternative to the
+ * ThreeGppSpectrumPropagationLossModel and ThreeGppChannelModel. The main method is
+ * DoCalcRxPowerSpectralDensity, which takes as input the power spectral density (PSD) of the
+ * transmitted signal, the mobility models of the transmitting node and receiving node, and returns
+ * the PSD of the received signal.
+ *
+ * The fading model is calibrated with the ThreeGppChannelModel, aiming to provide an end-to-end
+ * channel gain which matches as much as possible the TR 38.901-based channel model. Therefore, this
+ * model is to be used for large-scale simulations of MIMO wireless networks, for which the
+ * ThreeGppChannelModel would prove too computationally intensive. The target applicability of this
+ * model, in terms of frequency range, is 0.5-100 GHz.
+ *
+ * \see PhasedArrayModel
+ * \see ChannelCondition
+ */
+class TwoRaySpectrumPropagationLossModel : public PhasedArraySpectrumPropagationLossModel
+{
+    // Test classes needs access to CalcBeamformingGain and GetFtrFastFading
+    friend class ::FtrFadingModelAverageTest;
+    friend class ::ArrayResponseTest;
+    friend class ::OverallGainAverageTest;
+
+  public:
+    /**
+     * Struct holding the Fluctuating Two Ray fast-fading model parameters
+     */
+    struct FtrParams
+    {
+        /**
+         * Default constructor, requiring the Fluctuating Two Ray fading model parameters as
+         * arguments.
+         *
+         * \param m the m parameter of the FTR fading model, which represents the "Alpha" and "Beta"
+         * parameters of the Gamma-distributed random variable used to sample the power of the
+         * reflected components \param sigma the sigma parameter of the FTR fading model, which
+         * expresses the power of the diffuse real and imaginary components \param k the k parameter
+         * of the FTR fading model, which represents the ratio of the average power of the dominant
+         * components to the power of the remaining diffuse multipath \param delta the delta
+         * parameter of the FTR fading model, which express expressing how similar to each other are
+         * the average received powers of the specular components
+         */
+        FtrParams(double m, double sigma, double k, double delta)
+        {
+            // Make sure the parameter values belong to the proper domains
+            NS_ASSERT(delta >= 0.0 && delta <= 1.0);
+
+            m_m = m;
+            m_sigma = sigma;
+            m_k = k;
+            m_delta = delta;
+        }
+
+        /**
+         * Delete no-arguments default constructor.
+         */
+        FtrParams() = delete;
+
+        /**
+         * Parameter m for the Gamma variable. Used both as the shape and rate parameters.
+         */
+        double m_m = 0;
+
+        /**
+         * Parameter sigma. Used as the variance of the amplitudes of the normal diffuse components.
+         */
+        double m_sigma = 0;
+
+        /**
+         * Parameter K. Expresses ratio between dominant specular components and diffuse components.
+         */
+        double m_k = 0;
+
+        /**
+         * Parameter delta [0, 1]. Expresses how similar the amplitudes of the two dominant specular
+         * components are.
+         */
+        double m_delta = 0;
+    };
+
+    /**
+     * Tuple collecting vectors of carrier frequencies and FTR fading model parameters, encoded as a
+     * FtrParams struct
+     */
+    using CarrierFrequencyFtrParamsTuple = std::tuple<std::vector<double>, std::vector<FtrParams>>;
+
+    /**
+     * Nested map associating 3GPP scenario and LosCondition to the corresponding tuple of carrier
+     * frequencies and corresponding fitted FTR parameters
+     */
+    using FtrParamsLookupTable =
+        std::map<std::string,
+                 std::map<ChannelCondition::LosConditionValue, CarrierFrequencyFtrParamsTuple>>;
+
+    /**
+     * Constructor
+     */
+    TwoRaySpectrumPropagationLossModel();
+
+    /**
+     * Destructor
+     */
+    ~TwoRaySpectrumPropagationLossModel() override;
+
+    void DoDispose() override;
+
+    /**
+     * Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    /**
+     * Sets the propagation scenario
+     * \param scenario the propagation scenario
+     */
+    void SetScenario(const std::string& scenario);
+
+    /**
+     * Sets the center frequency of the model
+     * \param f the center frequency in Hz
+     */
+    void SetFrequency(double f);
+
+    /**
+     * \brief Assign a fixed random variable stream number to the random variables
+     * used by this model.
+     *
+     * \param stream first stream index to use
+     * \return the number of stream indices assigned by this model
+     */
+    int64_t AssignStreams(int64_t stream);
+
+    /**
+     * \brief Compute the received PSD.
+     *
+     * This function computes the received PSD by applying the Fluctuating Two-Ray (FTR)
+     * fast fading model and the beamforming gain.
+     * In particular, the beamforming gain is computed as the combination of the single-element
+     * and the array gains.
+     * The path loss and shadowing are to be computed separately, using the
+     * ThreeGppPropagationLossModel class.
+     *
+     * \param txPsd the PSD of the transmitted signal
+     * \param a first node mobility model
+     * \param b second node mobility model
+     * \param aPhasedArrayModel the antenna array of the first node
+     * \param bPhasedArrayModel the antenna array of the second node
+     * \return the PSD of the received signal
+     */
+    Ptr<SpectrumValue> DoCalcRxPowerSpectralDensity(
+        Ptr<const SpectrumSignalParameters> txPsd,
+        Ptr<const MobilityModel> a,
+        Ptr<const MobilityModel> b,
+        Ptr<const PhasedArrayModel> aPhasedArrayModel,
+        Ptr<const PhasedArrayModel> bPhasedArrayModel) const override;
+
+  private:
+    /**
+     * Retrieves the LOS condition associated to the specified mobility models
+     *
+     * \param a the mobility model of the first node
+     * \param b the mobility model of the second node
+     * \return the LOS condition of the link between a and b
+     */
+    ChannelCondition::LosConditionValue GetLosCondition(Ptr<const MobilityModel> a,
+                                                        Ptr<const MobilityModel> b) const;
+
+    /**
+     * Retrieves the FTR fading model parameters related to the carrier frequency and LOS condition.
+     *
+     * The calibration has been undertaken using the 3GPP 38.901 as a reference. The latter's
+     * small-scale fading distributions depend on the scenario (UMa, UMi, RMa, InH), the LOS
+     * condition (LOS/NLOS) and the carrier frequency. Therefore, the output of the calibration is a
+     * map associating such simulation parameters to the specific FTR parameters. Specifically, such
+     * parameters represent the FTR distribution yielding channel realizations which exhibit the
+     * closest statistics to the small-scale fading obtained when using the 3GPP 38.901 model. The
+     * estimation relies on reference curves obtained by collecting multiple 38.901 channel
+     * realizations and computing the corresponding end-to-end channel gain by setting the speed of
+     * the TX and RX pair to 0, disabling the shadowing and fixing the LOS condition. In such a way,
+     * any variation of the received power around the mean is given by the small-scale fading only.
+     * Finally, the reference ECDF of the channel gains is compared to the ECDF obtained from the
+     * FTR distribution using different values of the parameters. The parameters which provide the
+     * best fit (in a goodness-of-fit sense) are kept for the specific scenario, LOS condition and
+     * carrier frequency. The goodness of the fit is measured using the well-known Anderson-Darling
+     * metric.
+     *
+     * For additional details, please refer to
+     * src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc and
+     * src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py, which show how to obtain the
+     * reference curves and then estimate the FTR parameters based on the reference data,
+     * respectively.
+     *
+     * \param a first node mobility model
+     * \param b second node mobility model
+     * \return the corresponding FTR model parameters
+     */
+    FtrParams GetFtrParameters(Ptr<const MobilityModel> a, Ptr<const MobilityModel> b) const;
+
+    /**
+     * Compute the stochastic power gain due to the fast fading, modeled according to the
+     * Fluctuating Two-Ray Model.
+     *
+     * Specifically, the amplitude of the random fading term is sampled as:
+     *
+     * TODO: Currently the equations are not rendered, but neither are other formulas. Fixed in the
+     * mainline, so this should also render properly after a rebase will be done
+     *
+     * \f$ V_r = \sqrt{\zeta} V_1 \exp(j \phi_1) + \sqrt{\zeta} V_2 \exp(j \phi_2) + X + jY \f$,
+     * where \f$ \zeta \f$ is a unit-mean Gamma distributed random variable of shape and rate \f$ m
+     * \f$, \f$ X \f$ and \f$ Y \f$ are zero-mean Gaussian random variables of variance \f$ \sigma^2
+     * \f$, \f$ \phi_n \f$ is a uniform random variable over \f$ \left[ 0, 2\pi \right] \f$, \f$ V_1
+     * \f$ and \f$ V_2 \f$ are the constant amplitudes of the reflected components.
+     *
+     * See J. M. Romero-Jerez, F. J. Lopez-Martinez, J. F. Paris and A. Goldsmith, "The Fluctuating
+     * Two-Ray Fading Model for mmWave Communications," 2016 IEEE Globecom Workshops (GC Wkshps) for
+     * further details on such model.
+     *
+     * \param params the FTR fading model parameters
+     * \return the stochastic power gain due to the fast fading
+     */
+    double GetFtrFastFading(const FtrParams& params) const;
+
+    /**
+     * Compute the beamforming gain by combining single-element and array gains.
+     *
+     * Computes the overall beamforming and array gain, assuming analog beamforming
+     * both at the transmitter and at the receiver and arbitrary single-element
+     * radiation patterns. The computation is performed following Rebato, Mattia, et al.
+     * "Study of realistic antenna patterns in 5G mmwave cellular scenarios.",
+     * 2018 IEEE International Conference on Communications (ICC). IEEE, 2018.
+     *
+     * Additionally, whenever the link is in NLOS a penalty factor is introduced, to take into
+     * account for the possible misalignment of the beamforming vectors due to the lack of a
+     * dominant multipath component. See Kulkarni, Mandar N., Eugene Visotsky, and Jeffrey G.
+     * Andrews. "Correction factor for analysis of MIMO wireless networks with highly directional
+     * beamforming." IEEE Wireless Communications Letters 7.5 (2018) for further details on this
+     * approach.
+     *
+     * \param a first node mobility model
+     * \param b second node mobility model
+     * \param aPhasedArrayModel the antenna array of the first node
+     * \param bPhasedArrayModel the antenna array of the second node
+     * \return the beamforming gain
+     */
+    double CalcBeamformingGain(Ptr<const MobilityModel> a,
+                               Ptr<const MobilityModel> b,
+                               Ptr<const PhasedArrayModel> aPhasedArrayModel,
+                               Ptr<const PhasedArrayModel> bPhasedArrayModel) const;
+
+    /**
+     * Get the index of the closest carrier frequency for which the FTR estimated parameters are
+     * available.
+     *
+     * \param frequencies the vector of carrier frequencies which have been calibrated
+     * \param targetFc  the carrier frequency of the current simulation
+     * \return the index of frequencies representing the argmin over frequencies of
+     * abs(frequencies[index] - targetFc)
+     */
+    std::size_t SearchClosestFc(const std::vector<double>& frequencies, double targetFc) const;
+
+    /**
+     * The operating frequency
+     */
+    double m_frequency;
+
+    /**
+     * Random variable used to sample the uniform distributed phases of the FTR specular components.
+     */
+    Ptr<UniformRandomVariable> m_uniformRv;
+
+    /**
+     * Random variable used to sample the normal distributed amplitudes of the FTR diffuse
+     * components.
+     */
+    Ptr<NormalRandomVariable> m_normalRv;
+
+    /**
+     * Random variable used to sample the Nakagami distributed amplitude of the FTR specular
+     * components.
+     */
+    Ptr<GammaRandomVariable> m_gammaRv;
+
+    std::string m_scenario; //!< the 3GPP scenario
+
+    /**
+     * Channel condition model used to retrieve the LOS/NLOS condition of the communicating
+     * endpoints
+     */
+    Ptr<ChannelConditionModel> m_channelConditionModel;
+};
+
+} // namespace ns3
+
+#endif /* TWO_RAY_SPECTRUM_PROPAGATION_LOSS_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/test/examples-to-run.py ns-3.38/src/spectrum/test/examples-to-run.py
--- ns-3.37/src/spectrum/test/examples-to-run.py	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/test/examples-to-run.py	2023-03-16 20:54:23.828869000 -0700
@@ -11,6 +11,7 @@
     ("adhoc-aloha-ideal-phy", "True", "True"),
     ("adhoc-aloha-ideal-phy-with-microwave-oven", "True", "True"),
     ("adhoc-aloha-ideal-phy-matrix-propagation-loss-model", "True", "True"),
+    ("three-gpp-channel-example", "True", "True")
 ]
 
 # A list of Python examples to run in order to ensure that they remain
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/test/spectrum-interference-test.cc ns-3.38/src/spectrum/test/spectrum-interference-test.cc
--- ns-3.37/src/spectrum/test/spectrum-interference-test.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/test/spectrum-interference-test.cc	2023-03-16 20:54:23.828869000 -0700
@@ -159,7 +159,7 @@
     Ptr<const SpectrumModel> m;
 
     Bands bands;
-    struct BandInfo bi;
+    BandInfo bi;
 
     bi.fl = 2.400e9;
     bi.fc = 2.410e9;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/test/spectrum-value-test.cc ns-3.38/src/spectrum/test/spectrum-value-test.cc
--- ns-3.37/src/spectrum/test/spectrum-value-test.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/test/spectrum-value-test.cc	2023-03-16 20:54:23.828869000 -0700
@@ -54,10 +54,10 @@
 
   private:
     /**
-     * Check that two SpectrumValue are equal withing a tolerance
+     * Check that two SpectrumValue are equal within a tolerance
      * \param x first SpectrumValue
      * \param y second SpectrumValue
-     * \return true if the two values are withing the tolerance
+     * \return true if the two values are within the tolerance
      */
     bool MoreOrLessEqual(SpectrumValue x, SpectrumValue y);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/test/three-gpp-channel-test-suite.cc ns-3.38/src/spectrum/test/three-gpp-channel-test-suite.cc
--- ns-3.37/src/spectrum/test/three-gpp-channel-test-suite.cc	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/spectrum/test/three-gpp-channel-test-suite.cc	2023-03-16 20:54:23.828869000 -0700
@@ -106,17 +106,16 @@
         channelModel->GetChannel(txMob, rxMob, txAntenna, rxAntenna);
 
     double channelNorm = 0;
-    uint8_t numTotClusters = channelMatrix->m_channel.at(0).at(0).size();
-    for (uint8_t cIndex = 0; cIndex < numTotClusters; cIndex++)
+    uint16_t numTotalClusters = channelMatrix->m_channel.GetNumPages();
+    for (uint16_t cIndex = 0; cIndex < numTotalClusters; cIndex++)
     {
         double clusterNorm = 0;
         for (uint64_t sIndex = 0; sIndex < txAntennaElements; sIndex++)
         {
-            for (uint32_t uIndex = 0; uIndex < rxAntennaElements; uIndex++)
+            for (uint64_t uIndex = 0; uIndex < rxAntennaElements; uIndex++)
             {
                 clusterNorm +=
-                    std::pow(std::abs(channelMatrix->m_channel.at(uIndex).at(sIndex).at(cIndex)),
-                             2);
+                    std::pow(std::abs(channelMatrix->m_channel(uIndex, sIndex, cIndex)), 2);
             }
         }
         channelNorm += clusterNorm;
@@ -187,15 +186,15 @@
     Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix =
         channelModel->GetChannel(txMob, rxMob, txAntenna, rxAntenna);
 
-    // check the channel matrix dimensions
+    // check the channel matrix dimensions, expected H[cluster][rx][tx]
     NS_TEST_ASSERT_MSG_EQ(
-        channelMatrix->m_channel.at(0).size(),
+        channelMatrix->m_channel.GetNumCols(),
         txAntennaElements[0] * txAntennaElements[1],
-        "The second dimension of H should be equal to the number of tx antenna elements");
+        "The third dimension of H should be equal to the number of tx antenna elements");
     NS_TEST_ASSERT_MSG_EQ(
-        channelMatrix->m_channel.size(),
+        channelMatrix->m_channel.GetNumRows(),
         rxAntennaElements[0] * rxAntennaElements[1],
-        "The first dimension of H should be equal to the number of rx antenna elements");
+        "The second dimension of H should be equal to the number of rx antenna elements");
 
     // test if the channel matrix is correctly generated
     uint16_t numIt = 1000;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/test/two-ray-splm-test-suite.cc ns-3.38/src/spectrum/test/two-ray-splm-test-suite.cc
--- ns-3.37/src/spectrum/test/two-ray-splm-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/spectrum/test/two-ray-splm-test-suite.cc	2023-03-16 20:54:23.828869000 -0700
@@ -0,0 +1,833 @@
+/*
+ * Copyright (c) 2022 SIGNET Lab, Department of Information Engineering,
+ *
+ * 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/config.h>
+#include <ns3/constant-position-mobility-model.h>
+#include <ns3/double.h>
+#include <ns3/isotropic-antenna-model.h>
+#include <ns3/log.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/node-container.h>
+#include <ns3/pointer.h>
+#include <ns3/simulator.h>
+#include <ns3/string.h>
+#include <ns3/test.h>
+#include <ns3/three-gpp-antenna-model.h>
+#include <ns3/three-gpp-channel-model.h>
+#include <ns3/three-gpp-spectrum-propagation-loss-model.h>
+#include <ns3/two-ray-spectrum-propagation-loss-model.h>
+#include <ns3/uinteger.h>
+#include <ns3/uniform-planar-array.h>
+
+#include <array>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("TwoRaySplmTestSuite");
+
+/**
+ * \ingroup spectrum-tests
+ *
+ * Test case for the TwoRaySpectrumPropagationLossModel class.
+ *
+ * Check that the average of the Fluctuating Two Ray (FTR) fading model
+ * is consistent with the theoretical value.
+ */
+class FtrFadingModelAverageTest : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     */
+    FtrFadingModelAverageTest();
+
+    /**
+     * Destructor
+     */
+    ~FtrFadingModelAverageTest() override;
+
+  private:
+    /**
+     * Build the test scenario
+     */
+    void DoRun() override;
+
+    /**
+     * Compute the average of the FTR squared norm.
+     * \param [in] ftrParams The FTR parameters.
+     * \returns The average of \c N_MEASUREMENTS FTR samples.
+     */
+    double FtrSquaredNormAverage(
+        const TwoRaySpectrumPropagationLossModel::FtrParams& ftrParams) const;
+
+    /**
+     * Compute the expected mean of the FTR squared norm.
+     *
+     * The expected mean \f$ \mathbb{E} \left[ h^2 \right] \f$ can be computed as
+     * \f$ \mathbb{E}\left[ |h|^2 \right] = V_1^2 + V_2^2 + 2*\sigma \f$, where:
+     * \f$ \sigma^2 \f$ is the variance of the Gaussian distributed random variables
+     * which model the real and complex component of the specular terms and
+     * \f$ V_1 \f$ and \f$ V_2 \f$ are the constant amplitudes of the reflected components.ù
+     * In turn, this equals to \f$ 2*\sigma^2 \left( 1 + K \right) \f$.
+     *
+     * See J. M. Romero-Jerez, F. J. Lopez-Martinez, J. F. Paris and A. Goldsmith, "The Fluctuating
+     * Two-Ray Fading Model for mmWave Communications," 2016 IEEE Globecom Workshops (GC Wkshps) for
+     * further details.
+     *
+     * \param [in] sigma The sigma parameter of the FTR model.
+     * \param [in] k The k parameter of the FTR model.
+     * \returns The expected mean.
+     */
+    constexpr double FtrSquaredNormExpectedMean(double sigma, double k) const;
+
+    /// Number of samples to draw when populating the distribution.
+    static constexpr uint32_t N_MEASUREMENTS{100000};
+
+    /**
+     * Tolerance for testing FTR's expectations against theoretical values,
+     * expressed as a fraction of the expected mean.
+     */
+    static constexpr double TOLERANCE{1e-2};
+
+    /// Number of different values for each FTR parameter.
+    static constexpr uint8_t NUM_VALUES{3};
+
+    /// Maximum value for the m parameter.
+    static constexpr uint16_t MAX_M_VALUE{1000};
+};
+
+FtrFadingModelAverageTest::FtrFadingModelAverageTest()
+    : TestCase("Check that the average of the Fluctuating Two Ray model is consistent with the "
+               "theoretical expectation")
+{
+}
+
+FtrFadingModelAverageTest::~FtrFadingModelAverageTest()
+{
+}
+
+double
+FtrFadingModelAverageTest::FtrSquaredNormAverage(
+    const TwoRaySpectrumPropagationLossModel::FtrParams& ftrParams) const
+{
+    NS_LOG_FUNCTION(this);
+    double sum = 0.0;
+    auto twoRaySplm = CreateObject<TwoRaySpectrumPropagationLossModel>();
+    twoRaySplm->AssignStreams(1);
+    for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
+    {
+        double value = twoRaySplm->GetFtrFastFading(ftrParams);
+        sum += value;
+    }
+    double valueMean = sum / N_MEASUREMENTS;
+    return valueMean;
+}
+
+double constexpr FtrFadingModelAverageTest::FtrSquaredNormExpectedMean(double sigma, double k) const
+{
+    return 2 * sigma * (1 + k);
+}
+
+void
+FtrFadingModelAverageTest::DoRun()
+{
+    std::array<double, NUM_VALUES> sigma;
+    std::array<double, NUM_VALUES> k;
+    std::array<double, NUM_VALUES> delta;
+
+    // Generate a set of values for the FTR model parameters
+    for (uint8_t j = 0; j < NUM_VALUES; j++)
+    {
+        sigma[j] = std::pow(2, j);
+        k[j] = std::pow(2, j);
+        delta[j] = double(j) / NUM_VALUES; // Delta must be in [0, 1]
+    }
+
+    auto unifRv = CreateObject<UniformRandomVariable>();
+
+    // Check the consistency of the empirical average for a set of values of the FTR model
+    // parameters
+    for (uint8_t l = 0; l < NUM_VALUES; l++)
+    {
+        for (uint8_t m = 0; m < NUM_VALUES; m++)
+        {
+            for (uint8_t n = 0; n < NUM_VALUES; n++)
+            {
+                auto ftrParams = TwoRaySpectrumPropagationLossModel::FtrParams(
+                    unifRv->GetInteger(1, MAX_M_VALUE),
+                    sigma[l], // Average should be independent from m
+                    k[m],
+                    delta[n]);
+                double valueMean = FtrSquaredNormAverage(ftrParams);
+                double expectedMean = FtrSquaredNormExpectedMean(ftrParams.m_sigma, ftrParams.m_k);
+
+                NS_TEST_ASSERT_MSG_EQ_TOL(valueMean,
+                                          expectedMean,
+                                          expectedMean * TOLERANCE,
+                                          "wrong mean value");
+            }
+        }
+    }
+}
+
+/**
+ * \ingroup spectrum-tests
+ *
+ * Test case for the TwoRaySpectrumPropagationLossModel class.
+ *
+ * Check that the overall array response at boresight coincides with the
+ * expected theoretical values
+ */
+class ArrayResponseTest : public TestCase
+{
+  public:
+    /**
+     * The constructor of the test case
+     * \param txAntElem the antenna element of the TX antenna panel
+     * \param rxAntElem the antenna element of the RX antenna panel
+     * \param txNumAntennas the number of antenna elements of the TX antenna panel
+     * \param rxNumAntennas the number of antenna elements of the RX antenna panel
+     * \param txPosVec the position of the TX
+     * \param rxPosVec the position of the RX
+     * \param txBearing the bearing angle of the TX antenna panel
+     * \param rxBearing the bearing angle of the RX antenna panel
+     * \param expectedGain the theoretically expected gain for the above parameters
+     */
+    ArrayResponseTest(Ptr<AntennaModel> txAntElem,
+                      Ptr<AntennaModel> rxAntElem,
+                      uint16_t txNumAntennas,
+                      uint16_t rxNumAntennas,
+                      Vector txPosVec,
+                      Vector rxPosVec,
+                      double txBearing,
+                      double rxBearing,
+                      double expectedGain);
+
+    /**
+     * Destructor
+     */
+    ~ArrayResponseTest() override;
+
+  private:
+    /**
+     * Build the test scenario
+     */
+    void DoRun() override;
+
+    /**
+     * Tolerance for testing value produced by the simulator against
+     * expected theoretical value, as a fraction of the expected value.
+     */
+    static constexpr double TOLERANCE{1e-8};
+
+    Ptr<AntennaModel> m_txAntElem; //!< the antenna element of the TX antenna panel
+    Ptr<AntennaModel> m_rxAntElem; //!< the antenna element of the RX antenna panel
+    uint16_t m_txNumAntennas;      //!< the number of antenna elements of the TX antenna panel
+    uint16_t m_rxNumAntennas;      //!< the number of antenna elements of the RX antenna panel
+    Vector m_txPosVec;             //!< the position of the TX
+    Vector m_rxPosVec;             //!< the position of the RX
+    double m_txBearing;            //!< the bearing angle of the TX antenna panel [rad]
+    double m_rxBearing;            //!< the bearing angle of the RX antenna panel [rad]
+    double m_expectedGain;         //!< the gain which is theoretically expected  [db]
+};
+
+ArrayResponseTest::ArrayResponseTest(Ptr<AntennaModel> txAntElem,
+                                     Ptr<AntennaModel> rxAntElem,
+                                     uint16_t txNumAntennas,
+                                     uint16_t rxNumAntennas,
+                                     Vector txPosVec,
+                                     Vector rxPosVec,
+                                     double txBearing,
+                                     double rxBearing,
+                                     double expectedGain)
+    // TODO: Create a string with the test parameters as the test case name like in
+    // test-uniform-planar-array ?
+    : TestCase("Check that the overall array response gain has the proper trend with respect to"
+               "the number of antennas and the type of single element antenna"),
+      m_txAntElem(txAntElem),
+      m_rxAntElem(rxAntElem),
+      m_txNumAntennas(txNumAntennas),
+      m_rxNumAntennas(rxNumAntennas),
+      m_txPosVec(txPosVec),
+      m_rxPosVec(rxPosVec),
+      m_txBearing(txBearing),
+      m_rxBearing(rxBearing),
+      m_expectedGain(expectedGain)
+{
+}
+
+ArrayResponseTest::~ArrayResponseTest()
+{
+}
+
+void
+ArrayResponseTest::DoRun()
+{
+    auto twoRaySplm = CreateObject<TwoRaySpectrumPropagationLossModel>();
+    twoRaySplm->AssignStreams(1);
+
+    // Create and assign the channel condition model
+    auto channelConditionModel = CreateObject<AlwaysLosChannelConditionModel>();
+    twoRaySplm->SetAttribute("ChannelConditionModel", PointerValue(channelConditionModel));
+
+    // Create the TX and RX antenna arrays
+    auto txArray = CreateObject<UniformPlanarArray>();
+    auto rxArray = CreateObject<UniformPlanarArray>();
+    txArray->SetAttribute("AntennaElement", PointerValue(m_txAntElem));
+    rxArray->SetAttribute("AntennaElement", PointerValue(m_rxAntElem));
+
+    // Create the corresponding mobility models
+    auto txPos = CreateObject<ConstantPositionMobilityModel>();
+    auto rxPos = CreateObject<ConstantPositionMobilityModel>();
+    txPos->SetAttribute("Position", VectorValue(m_txPosVec));
+    rxPos->SetAttribute("Position", VectorValue(m_rxPosVec));
+
+    // Rotate the arrays
+    txArray->SetAttribute("BearingAngle", DoubleValue(m_txBearing));
+    rxArray->SetAttribute("BearingAngle", DoubleValue(m_rxBearing));
+
+    // Set the antenna arrays dimensions. Arrays are assumed to be squared.
+    txArray->SetAttribute("NumRows", UintegerValue(std::sqrt(m_txNumAntennas)));
+    txArray->SetAttribute("NumColumns", UintegerValue(std::sqrt(m_txNumAntennas)));
+    rxArray->SetAttribute("NumRows", UintegerValue(std::sqrt(m_rxNumAntennas)));
+    rxArray->SetAttribute("NumColumns", UintegerValue(std::sqrt(m_rxNumAntennas)));
+
+    // Compute the beamforming vectors
+    auto txBfVec = txArray->GetBeamformingVector(Angles(m_rxPosVec, m_txPosVec));
+    auto rxBfVec = rxArray->GetBeamformingVector(Angles(m_txPosVec, m_rxPosVec));
+    txArray->SetBeamformingVector(txBfVec);
+    rxArray->SetBeamformingVector(rxBfVec);
+
+    // Compute the overall array response
+    double gainTxRx = twoRaySplm->CalcBeamformingGain(txPos, rxPos, txArray, rxArray);
+    double gainRxTx = twoRaySplm->CalcBeamformingGain(rxPos, txPos, rxArray, txArray);
+
+    NS_TEST_EXPECT_MSG_EQ_TOL(gainTxRx, gainRxTx, gainTxRx * TOLERANCE, "gain should be symmetric");
+    NS_TEST_EXPECT_MSG_EQ_TOL(10 * log10(gainTxRx),
+                              m_expectedGain,
+                              m_expectedGain * TOLERANCE,
+                              "gain different from the theoretically expected value");
+}
+
+/**
+ * \ingroup spectrum-tests
+ *
+ * Test case for the TwoRaySpectrumPropagationLossModel class.
+ *
+ * Check that the average overall channel gain obtained using the
+ * TwoRaySpectrumPropagationLossModel class is close (it is, after all,
+ * a simplified and performance-oriented model) to the one obtained using
+ * the ThreeGppSpectrumPropagationLossModel
+ */
+class OverallGainAverageTest : public TestCase
+{
+  public:
+    /**
+     * The constructor of the test case
+     * \param txAntElem the antenna element of the TX antenna panel
+     * \param rxAntElem the antenna element of the RX antenna panel
+     * \param txNumAntennas the number of antenna elements of the TX antenna panel
+     * \param rxNumAntennas the number of antenna elements of the RX antenna panel
+     * \param fc the carrier frequency
+     * \param threeGppScenario the 3GPP scenario
+     */
+    OverallGainAverageTest(Ptr<AntennaModel> txAntElem,
+                           Ptr<AntennaModel> rxAntElem,
+                           uint16_t txNumAntennas,
+                           uint16_t rxNumAntennas,
+                           double fc,
+                           std::string threeGppScenario);
+
+    /**
+     * Computes the overall power of a PSD
+     *
+     * \param psd the PSD
+     * \returns the overall power of the PSD, obtained as the integral of the
+     *          sub-bands power over the PSD domain
+     */
+    double ComputePowerSpectralDensityOverallPower(Ptr<const SpectrumValue> psd);
+
+    /**
+     * Creates a PSD centered at fc, of bandwidth bw and sub-bands of
+     * width rbWidth
+     *
+     * \param fc the central frequency of the PSD
+     * \returns a PSD centered at fc, of bandwidth bw and sub-bands of
+     *          width rbWidths
+     */
+    Ptr<SpectrumValue> CreateTxPowerSpectralDensity(double fc);
+
+    /**
+     * Destructor
+     */
+    ~OverallGainAverageTest() override;
+
+  private:
+    /**
+     * Build the test scenario
+     */
+    void DoRun() override;
+
+    /**
+     * Tolerance for testing average channel gain produced by the Two Ray model with
+     * respect to the 3GPP model, which serves as the reference.
+     */
+    static constexpr double TOLERANCE{0.02};
+
+    /// Number of samples to draw when estimating the average.
+    static constexpr uint32_t N_MEASUREMENTS{1000};
+
+    /// The simulation bandwidth. Results are independent from this parameter.
+    static constexpr double M_BW{200e6};
+
+    /**
+     * The width of a RB, which in turn specifies the resolution of the generated PSDs.
+     *  Results are independent from this parameter.
+     */
+    static constexpr double M_RB_WIDTH{1e6};
+
+    Ptr<AntennaModel> m_txAntElem;  //!< the antenna element of the TX antenna panel
+    Ptr<AntennaModel> m_rxAntElem;  //!< the antenna element of the RX antenna panel
+    uint16_t m_txNumAntennas;       //!< the number of antenna elements of the TX antenna panel
+    uint16_t m_rxNumAntennas;       //!< the number of antenna elements of the RX antenna panel
+    double m_fc;                    //!< the carrier frequency
+    std::string m_threeGppScenario; //!< the 3GPP scenario
+};
+
+OverallGainAverageTest::OverallGainAverageTest(Ptr<AntennaModel> txAntElem,
+                                               Ptr<AntennaModel> rxAntElem,
+                                               uint16_t txNumAntennas,
+                                               uint16_t rxNumAntennas,
+                                               double fc,
+                                               std::string threeGppScenario)
+    // TODO: Create a string with the test parameters as the test case name like in
+    // test-uniform-planar-array ?
+    : TestCase("Check that the overall array response gain has the proper trend with respect to"
+               "the number of antennas and the type of single element antenna"),
+      m_txAntElem(txAntElem),
+      m_rxAntElem(rxAntElem),
+      m_txNumAntennas(txNumAntennas),
+      m_rxNumAntennas(rxNumAntennas),
+      m_fc(fc),
+      m_threeGppScenario(threeGppScenario)
+{
+}
+
+OverallGainAverageTest::~OverallGainAverageTest()
+{
+}
+
+double
+OverallGainAverageTest::ComputePowerSpectralDensityOverallPower(Ptr<const SpectrumValue> psd)
+{
+    return Integral(*psd);
+}
+
+Ptr<SpectrumValue>
+OverallGainAverageTest::CreateTxPowerSpectralDensity(double fc)
+{
+    uint32_t numRbs = std::floor(M_BW / M_RB_WIDTH);
+    double f = fc - (numRbs * M_RB_WIDTH / 2.0);
+    double powerTx = 0.0;
+
+    Bands rbs;              // A vector representing each resource block
+    std::vector<int> rbsId; // A vector representing the resource block IDs
+    rbs.reserve(numRbs);
+    rbsId.reserve(numRbs);
+    for (uint32_t numrb = 0; numrb < numRbs; ++numrb)
+    {
+        BandInfo rb;
+        rb.fl = f;
+        f += M_RB_WIDTH / 2;
+        rb.fc = f;
+        f += M_RB_WIDTH / 2;
+        rb.fh = f;
+
+        rbs.push_back(rb);
+        rbsId.push_back(numrb);
+    }
+    Ptr<SpectrumModel> model = Create<SpectrumModel>(rbs);
+    Ptr<SpectrumValue> txPsd = Create<SpectrumValue>(model);
+
+    double powerTxW = std::pow(10., (powerTx - 30) / 10);
+    double txPowerDensity = powerTxW / M_BW;
+
+    for (const auto& rbId : rbsId)
+    {
+        (*txPsd)[rbId] = txPowerDensity;
+    }
+
+    return txPsd;
+}
+
+void
+OverallGainAverageTest::DoRun()
+{
+    // Create the Two Ray and 3GPP SPLMs
+    auto twoRaySplm = CreateObject<TwoRaySpectrumPropagationLossModel>();
+    auto threeGppSplm = CreateObject<ThreeGppSpectrumPropagationLossModel>();
+    auto threeGppChannelModel = CreateObject<ThreeGppChannelModel>();
+    auto channelConditionModel = CreateObject<AlwaysLosChannelConditionModel>();
+    twoRaySplm->AssignStreams(1);
+    threeGppChannelModel->AssignStreams(1);
+
+    // Pass the needed pointers between the various spectrum instances
+    threeGppSplm->SetAttribute("ChannelModel", PointerValue(threeGppChannelModel));
+    threeGppChannelModel->SetAttribute("ChannelConditionModel",
+                                       PointerValue(channelConditionModel));
+    twoRaySplm->SetAttribute("ChannelConditionModel", PointerValue(channelConditionModel));
+
+    // Create the TX and RX nodes and mobility models
+    NodeContainer nodes;
+    nodes.Create(2);
+    MobilityHelper mobility;
+    Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
+    Vector txPosVec(0.0, 0.0, 0.0);
+    Vector rxPosVec(5.0, 0.0, 0.0);
+    positionAlloc->Add(txPosVec);
+    positionAlloc->Add(rxPosVec);
+    mobility.SetPositionAllocator(positionAlloc);
+    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+    mobility.Install(nodes);
+
+    // Create the TX antenna array
+    auto txArray = CreateObject<UniformPlanarArray>();
+    txArray->SetAttribute("AntennaElement", PointerValue(m_txAntElem));
+
+    // Rotate the array
+    txArray->SetAttribute("BearingAngle", DoubleValue(0));
+
+    // Set the antenna array dimensions. Arrays are assumed to be squared.
+    txArray->SetAttribute("NumRows", UintegerValue(std::sqrt(m_txNumAntennas)));
+    txArray->SetAttribute("NumColumns", UintegerValue(std::sqrt(m_txNumAntennas)));
+
+    // Set the channel simulation parameters
+    threeGppChannelModel->SetAttribute("Frequency", DoubleValue(m_fc));
+    twoRaySplm->SetAttribute("Frequency", DoubleValue(m_fc));
+    threeGppChannelModel->SetAttribute("Scenario", StringValue(m_threeGppScenario));
+    twoRaySplm->SetAttribute("Scenario", StringValue(m_threeGppScenario));
+
+    // Disable blockage in order to prevent unwanted variations around the mean value
+    threeGppChannelModel->SetAttribute("Blockage", BooleanValue(false));
+
+    // Create the TX PSD
+    Ptr<SpectrumValue> txPsd = CreateTxPowerSpectralDensity(m_fc);
+    double txPower = ComputePowerSpectralDensityOverallPower(txPsd);
+
+    // Create TX signal parameters
+    Ptr<SpectrumSignalParameters> signalParams = Create<SpectrumSignalParameters>();
+    signalParams->psd = txPsd;
+
+    // Compute and set the TX beamforming vector
+    auto txBfVec = txArray->GetBeamformingVector(Angles(rxPosVec, txPosVec));
+    txArray->SetBeamformingVector(txBfVec);
+
+    Ptr<MobilityModel> txMob = nodes.Get(0)->GetObject<MobilityModel>();
+    Ptr<MobilityModel> rxMob = nodes.Get(1)->GetObject<MobilityModel>();
+
+    double threeGppGainMean = 0;
+    double twoRayGainMean = 0;
+
+    // Compute the overall array responses
+    for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
+    {
+        // Re-create the RX array at each iteration, to force resampling of the 3GPP channel
+        auto rxArray = CreateObject<UniformPlanarArray>();
+
+        // Rotate the RX antenna array and set its dimensions
+        rxArray->SetAttribute("AntennaElement", PointerValue(m_rxAntElem));
+        rxArray->SetAttribute("BearingAngle", DoubleValue(-M_PI));
+        rxArray->SetAttribute("NumRows", UintegerValue(std::sqrt(m_rxNumAntennas)));
+        rxArray->SetAttribute("NumColumns", UintegerValue(std::sqrt(m_rxNumAntennas)));
+
+        // Compute and set the RX beamforming vector
+        auto rxBfVec = rxArray->GetBeamformingVector(Angles(txPosVec, rxPosVec));
+        rxArray->SetBeamformingVector(rxBfVec);
+
+        auto twoRayRxPsd =
+            twoRaySplm->DoCalcRxPowerSpectralDensity(signalParams, txMob, rxMob, txArray, rxArray);
+        auto threeGppRayRxPsd = threeGppSplm->DoCalcRxPowerSpectralDensity(signalParams,
+                                                                           txMob,
+                                                                           rxMob,
+                                                                           txArray,
+                                                                           rxArray);
+        double twoRayRxPower = ComputePowerSpectralDensityOverallPower(twoRayRxPsd);
+        double threeGppRxPower = ComputePowerSpectralDensityOverallPower(threeGppRayRxPsd);
+
+        twoRayGainMean += (twoRayRxPower / txPower);
+        threeGppGainMean += (threeGppRxPower / txPower);
+    }
+
+    NS_TEST_EXPECT_MSG_EQ_TOL(
+        twoRayGainMean / N_MEASUREMENTS,
+        threeGppGainMean / N_MEASUREMENTS,
+        twoRayGainMean * TOLERANCE,
+        "The 3GPP and Two Ray models should provide similar average channel gains");
+}
+
+/**
+ * \ingroup spectrum-tests
+ *
+ * Test suite for the TwoRaySpectrumPropagationLossModel class
+ */
+class TwoRaySplmTestSuite : public TestSuite
+{
+  public:
+    /**
+     * Constructor
+     */
+    TwoRaySplmTestSuite();
+};
+
+TwoRaySplmTestSuite::TwoRaySplmTestSuite()
+    : TestSuite("two-ray-splm-suite", UNIT)
+{
+    // Test the GetFtrFastFading function of the TwoRaySpectrumPropagationLossModel class
+    AddTestCase(new FtrFadingModelAverageTest, TestCase::QUICK);
+
+    // Test the CalcBeamformingGain function of the TwoRaySpectrumPropagationLossModel class
+    auto iso = CreateObject<IsotropicAntennaModel>();
+    auto tgpp = CreateObject<ThreeGppAntennaModel>();
+    const double maxTgppGain = tgpp->GetGainDb(Angles(0.0, M_PI / 2));
+
+    // Deploy 2 squared antenna arrays which face each other. Steer their beam towards the boresight
+    // and check the resulting array gain. SE = single element radiation pattern, N = number of
+    // radiating elements, Phi = bearing angle
+    //                                 SE tx,   SE rx, N tx, N rx,             position tx, position
+    //                                 rx,  Phi tx,  Phi rx,                       expected gain
+    AddTestCase(new ArrayResponseTest(iso,
+                                      iso,
+                                      1,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      0.0),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(iso,
+                                      iso,
+                                      4,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(4)),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(iso,
+                                      iso,
+                                      16,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(16)),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(iso,
+                                      iso,
+                                      64,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(64)),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(iso,
+                                      iso,
+                                      4,
+                                      4,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      2 * 10 * log10(4)),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(iso,
+                                      iso,
+                                      16,
+                                      16,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      2 * 10 * log10(16)),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(iso,
+                                      iso,
+                                      64,
+                                      64,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      2 * 10 * log10(64)),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      iso,
+                                      1,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      iso,
+                                      4,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(4) + maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      iso,
+                                      16,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(16) + maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      iso,
+                                      64,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(64) + maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      tgpp,
+                                      1,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      2 * maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      tgpp,
+                                      4,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(4) + 2 * maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      tgpp,
+                                      16,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(16) + 2 * maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      tgpp,
+                                      64,
+                                      1,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      10 * log10(64) + 2 * maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      tgpp,
+                                      4,
+                                      4,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      2 * 10 * log10(4) + 2 * maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      tgpp,
+                                      16,
+                                      16,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      2 * 10 * log10(16) + 2 * maxTgppGain),
+                TestCase::QUICK);
+    AddTestCase(new ArrayResponseTest(tgpp,
+                                      tgpp,
+                                      64,
+                                      64,
+                                      Vector(0.0, 0.0, 0.0),
+                                      Vector(5.0, 0.0, 0.0),
+                                      0.0,
+                                      -M_PI,
+                                      2 * 10 * log10(64) + 2 * maxTgppGain),
+                TestCase::QUICK);
+
+    // Deploy 2 squared antenna arrays which face each other. Steer their beam towards the boresight
+    // and check the overall resulting channel gain. SE = single element radiation pattern, N =
+    // number of radiating elements, Fc = carrier frequency, Scen = 3GPP scenario
+    //                                     SE tx,   SE rx, N tx, N rx,    Fc,    Scen
+    AddTestCase(new OverallGainAverageTest(iso, iso, 1, 1, 10e9, "RMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 1, 1, 10e9, "RMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 4, 4, 10e9, "RMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 4, 4, 10e9, "RMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 1, 1, 10e9, "UMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 1, 1, 10e9, "UMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 4, 4, 10e9, "UMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 4, 4, 10e9, "UMa"), TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 1, 1, 60e9, "UMi-StreetCanyon"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 1, 1, 60e9, "UMi-StreetCanyon"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 4, 4, 60e9, "UMi-StreetCanyon"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 4, 4, 60e9, "UMi-StreetCanyon"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 1, 1, 60e9, "InH-OfficeOpen"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 1, 1, 60e9, "InH-OfficeOpen"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 4, 4, 60e9, "InH-OfficeOpen"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 4, 4, 60e9, "InH-OfficeOpen"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 1, 1, 100e9, "InH-OfficeMixed"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 1, 1, 100e9, "InH-OfficeMixed"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(iso, iso, 4, 4, 100e9, "InH-OfficeMixed"),
+                TestCase::EXTENSIVE);
+    AddTestCase(new OverallGainAverageTest(tgpp, tgpp, 4, 4, 100e9, "InH-OfficeMixed"),
+                TestCase::EXTENSIVE);
+}
+
+// Static variable for test initialization
+static TwoRaySplmTestSuite myTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py ns-3.38/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py
--- ns-3.37/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py	2023-03-16 20:54:23.828869000 -0700
@@ -0,0 +1,570 @@
+#!/usr/bin/env python
+
+"""
+Companion TwoRaySpectrumPropagationLossModel calibration script
+
+Copyright (c) 2022 SIGNET Lab, Department of Information Engineering,
+University of Padova
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+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, see <http://www.gnu.org/licenses/>.
+"""
+
+import pandas as pd
+from matplotlib import pyplot as plt
+import seaborn as sns
+import numpy as np
+from pathlib import Path
+from itertools import product
+from tqdm import tqdm
+import joblib
+import contextlib
+import argparse as argp
+
+
+# Command line arguments
+parser = argp.ArgumentParser(formatter_class=argp.ArgumentDefaultsHelpFormatter)
+parser.add_argument("--num_search_grid_params", default=30,
+                    help="Number of values for each parameter of the search grids")
+parser.add_argument("--num_refinements", default=1,
+                    help="Number of refinement local search runs to be carried out")
+parser.add_argument("--ref_data_fname", default="two-ray-to-three-gpp-splm-calibration.csv",
+                    help="Filename of the fit reference data, obtained from ns-3")
+parser.add_argument("--fit_out_fname", default="two-ray-splm-fitted-params.txt",
+                    help="Filename of the fit results")
+parser.add_argument("--c_plus_plus_out_fname", default="two-ray-cplusplus-fitted-params.txt",
+                    help="Filename of the fit results, encoded as a C++ data structure to be imported in ns-3")
+parser.add_argument("--figs_folder", default="FiguresTwoRayThreeGppChCalibration/",
+                    help="Output folder for the fit results figures")
+parser.add_argument("--epsilon", default=1e-7,
+                    help="Tolerance value for the preliminary tests")
+parser.add_argument("--preliminary_fit_test", default=True,
+                    help="Whether to run preliminary tests which check the correctness of the script functions")
+parser.add_argument("--fit_ftr_to_threegpp", default=True,
+                    help="Whether to run the calibration with respect to the 3GPP reference channel gains")
+parser.add_argument("--output_ns3_table", default=True,
+                    help="Whether to output the code for importing the calibration results in ns-3")
+parser.add_argument("--plot_fit_results", default=False,
+                    help="Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions")
+
+args = parser.parse_args()
+# Number of values for each parameter of the search grids
+num_search_grid_params = int(args.num_search_grid_params)
+# Number of refinement local search runs to be carried out
+num_refinements = int(args.num_refinements)
+# Filename of the fit reference data, obtained from ns-3
+ref_data_fname = args.ref_data_fname
+# Filename of the fit results
+fit_out_fname = args.fit_out_fname
+# Filename of the fit results, encoded as a C++ data structure to be imported in ns-3
+c_plus_plus_out_fname = args.c_plus_plus_out_fname
+# Output folder for the fit results figures
+figs_folder = args.figs_folder
+# Tolerance value for the preliminary tests
+epsilon = float(args.epsilon)
+# Whether to run preliminary tests which check the correctness of the script functions
+preliminary_fit_test = bool(args.preliminary_fit_test)
+# Whether to run the calibration with respect to the 3GPP reference channel gains
+fit_ftr_to_threegpp = bool(args.fit_ftr_to_threegpp)
+# Whether to output the code for importing the calibration results in ns-3
+output_ns3_table = bool(args.output_ns3_table)
+# Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions
+plot_fit_results = bool(args.plot_fit_results)
+
+
+@contextlib.contextmanager
+def tqdm_joblib(tqdm_object):
+    """
+      Context manager to patch joblib to report into tqdm progress bar given as argument.
+      Taken from: https://stackoverflow.com/questions/24983493/tracking-progress-of-joblib-parallel-execution
+
+    """
+    class TqdmBatchCompletionCallback(joblib.parallel.BatchCompletionCallBack):
+        def __call__(self, *args, **kwargs):
+            tqdm_object.update(n=self.batch_size)
+            return super().__call__(*args, **kwargs)
+
+    old_batch_callback = joblib.parallel.BatchCompletionCallBack
+    joblib.parallel.BatchCompletionCallBack = TqdmBatchCompletionCallback
+    try:
+        yield tqdm_object
+    finally:
+        joblib.parallel.BatchCompletionCallBack = old_batch_callback
+        tqdm_object.close()
+
+
+## FtrParams class
+class FtrParams:
+    ## @var m
+    #  Parameter m for the Gamma variable. Used both as the shape and rate parameters.
+    ## @var sigma
+    #  Parameter sigma. Used as the variance of the amplitudes of the normal diffuse components.
+    ## @var k
+    #  Parameter K. Expresses ratio between dominant specular components and diffuse components.
+    ## @var delta
+    #  Parameter delta [0, 1]. Expresses how similar the amplitudes of the two dominant specular components are.
+
+    def __init__(self, m: float, sigma: float, k: float, delta: float):
+        '''! The initializer.
+        @param self: the object pointer
+        @param m: Parameter m for the Gamma variable. Used both as the shape and rate parameters.
+        @param sigma: Parameter sigma. Used as the variance of the amplitudes of the normal diffuse components.
+        @param k: Parameter K. Expresses ratio between dominant specular components and diffuse components.
+        @param delta: Parameter delta [0, 1]. Expresses how similar the amplitudes of the two dominant specular components are.
+        '''
+
+        self.m = m
+        self.sigma = sigma
+        self.k = k
+        self.delta = delta
+
+    def __init__(self):
+        '''! The initializer with default values.
+        @param self: the object pointer
+        '''
+
+        self.m = 1
+        self.sigma = 1.0
+        self.k = 0.0
+        self.delta = 0.0
+
+    def __str__(self):
+        '''! The initializer with default values.
+        @param self: the object pointer
+        @returns A string reporting the value of each of the FTR fading model parameters
+        '''
+
+        return f'm: {self.m}, sigma: {self.sigma}, k: {self.k}, delta: {self.delta}'
+
+
+def get_ftr_ecdf(params: FtrParams, n_samples: int, db=False):
+    '''!  Returns the ECDF for the FTR fading model, for a given parameter grid.
+        @param params: The FTR parameters grid.
+        @param n_samples: The number of samples of the output ECDF
+        @param db: Whether to return the ECDF with the gain expressed in dB
+        @returns The ECDF for the FTR fading model
+    '''
+
+    assert (params.delta >= 0 and params.delta <= 1.0)
+
+    # Compute the specular components amplitudes from the FTR parameters
+    cmn_sqrt_term = np.sqrt(1 - params.delta**2)
+    v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
+    v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
+
+    assert (abs((v1**2 + v2**2)/(2*params.sigma) - params.k) < 1e-5)
+    if params.k > 0:
+        assert (abs((2*v1*v2)/(v1**2 + v2**2) - params.delta) < 1e-4)
+    else:
+        assert (v1 == v2 == params.k)
+
+    sqrt_gamma = np.sqrt(np.random.gamma(
+        shape=params.m, scale=1/params.m, size=n_samples))
+
+    # Sample the random phases of the specular components, which are uniformly distributed in [0, 2*PI]
+    phi1 = np.random.uniform(low=0, high=1.0, size=n_samples)
+    phi2 = np.random.uniform(low=0, high=1.0, size=n_samples)
+
+    # Sample the normal-distributed real and imaginary parts of the diffuse components
+    x = np.random.normal(scale=np.sqrt(params.sigma), size=n_samples)
+    y = np.random.normal(scale=np.sqrt(params.sigma), size=n_samples)
+
+    # Compute the channel response by combining the above terms
+    compl_phi1 = np.vectorize(complex)(np.cos(phi1), np.sin(phi1))
+    compl_phi2 = np.vectorize(complex)(np.cos(phi2), np.sin(phi2))
+    compl_xy = np.vectorize(complex)(x, y)
+    h = np.multiply(sqrt_gamma, compl_phi1) * v1 + \
+        np.multiply(sqrt_gamma, compl_phi2) * v2 + compl_xy
+
+    # Compute the squared norms
+    power = np.square(np.absolute(h))
+
+    if db:
+        power = 10*np.log10(power)
+
+    return np.sort(power)
+
+
+def compute_ftr_mean(params: FtrParams):
+    '''!  Computes the mean of the FTR fading model, given a specific set of parameters.
+        @param params: The FTR fading model parameters.
+    '''
+
+    cmn_sqrt_term = np.sqrt(1 - params.delta**2)
+    v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
+    v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
+
+    mean = v1**2 + v2**2 + 2*params.sigma
+
+    return mean
+
+
+def compute_ftr_th_mean(params: FtrParams):
+    '''!  Computes the mean of the FTR fading model using the formula reported in the corresponding paper,
+          given a specific set of parameters.
+        @param params: The FTR fading model parameters.
+    '''
+
+    return 2 * params.sigma * (1 + params.k)
+
+
+def compute_anderson_darling_measure(ref_ecdf: list, target_ecdf: list) -> float:
+    '''!  Computes the Anderson-Darling measure for the specified reference and targets distributions.
+          In particular, the Anderson-Darling measure is defined as:
+          A^2 = -N -S, where S = \sum_{i=1}^N \frac{2i - 1}{N} \left[ ln F(Y_i) + ln F(Y_{N + 1 - i}) \right].
+
+          See https://www.itl.nist.gov/div898/handbook/eda/section3/eda35e.htm for further details.
+
+        @param ref_ecdf: The reference ECDF.
+        @param target_ecdf: The target ECDF we wish to match the reference distribution to.
+        @returns The Anderson-Darling measure for the specified reference and targets distributions.
+    '''
+
+    assert (len(ref_ecdf) == len(target_ecdf))
+
+    n = len(ref_ecdf)
+    mult_factors = np.linspace(start=1, stop=n, num=n)*2 + 1
+    ecdf_values = compute_ecdf_value(ref_ecdf, target_ecdf)
+
+    # First and last elements of the ECDF may lead to NaNs
+    with np.errstate(divide='ignore'):
+        log_a_plus_b = np.log(ecdf_values) + np.log(1 - np.flip(ecdf_values))
+
+    valid_idxs = np.isfinite(log_a_plus_b)
+    A_sq = - np.dot(mult_factors[valid_idxs], log_a_plus_b[valid_idxs])
+
+    return A_sq
+
+
+def compute_ecdf_value(ecdf: list, data_points: float) -> np.ndarray:
+    '''!  Given an ECDF and data points belonging to its domain, returns their associated EDCF value.
+        @param ecdf: The ECDF, represented as a sorted list of samples.
+        @param data_point: A list of data points belonging to the same domain as the samples.
+        @returns The ECDF value of the domain points of the specified ECDF
+    '''
+
+    ecdf_values = []
+    for point in data_points:
+        idx = np.searchsorted(ecdf, point) / len(ecdf)
+        ecdf_values.append(idx)
+
+    return np.asarray(ecdf_values)
+
+
+def get_sigma_from_k(k: float) -> float:
+    '''!  Computes the value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
+        @param k: The K parameter of the FTR fading model, which represents the ratio of the average power
+                  of the dominant components to the power of the remaining diffuse multipath.
+        @returns The value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
+    '''
+
+    return 1 / (2 + 2 * k)
+
+
+def fit_ftr_to_reference(ref_data: pd.DataFrame, ref_params_combo: tuple,  num_params: int, num_refinements: int) -> str:
+    '''!  Estimate the FTR parameters yielding the closest ECDF to the reference one.
+
+          Uses a global search to estimate the FTR parameters yielding the best fit to the reference ECDF.
+          Then, the search is refined by repeating the procedure in the neighborhood of the parameters
+          identified with the global search. Such a neighborhood is determined as the interval whose center
+          is the previous iteration best value, and the lower and upper bounds are the first lower and upper
+          values which were previously considered, respectively.
+
+        @param ref_data: The reference data, represented as a DataFrame of samples.
+        @param ref_params_combo: The specific combination of simulation parameters corresponding
+                                 to the reference ECDF
+        @param num_params: The number of values of each parameter in the global and local search grids.
+        @param num_refinements: The number of local refinement search to be carried out after the global search.
+
+        @returns An estimate of the FTR parameters yielding the closest ECDF to the reference one.
+    '''
+
+    # Retrieve the reference ECDF
+    ref_ecdf = ref_data.query(
+        'scen == @ref_params_combo[0] and cond == @ref_params_combo[1] and fc == @ref_params_combo[2]')
+
+    # Perform the fit
+    n_samples = len(ref_ecdf)
+    best_params = FtrParams()
+    best_ad = np.inf
+
+    # The m and K parameters can range in ]0, +inf[
+    m_and_k_ub = 4
+    m_and_k_lb = 0.001
+    m_and_k_step = (m_and_k_ub - m_and_k_lb) / n_samples
+
+    # The delta parameter can range in [0, 1]
+    delta_step = 1/n_samples
+
+    # Define the coarse grid
+    coarse_search_grid = {
+        # m must be in [0, +inf]
+        'm': np.power(np.ones(num_params)*10, np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params)),
+        # k must be in [0, +inf]
+        'k': np.power(np.ones(num_params)*10, np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params)),
+        # delta must be in [0, 1]
+        'delta': np.linspace(start=0.0, stop=1.0, endpoint=True, num=num_params)
+        # sigma determined from k, due to the unit-mean constraint
+    }
+
+    for element in product(*coarse_search_grid.values()):
+
+        # Create FTR params object
+        params = FtrParams()
+        params.m = element[0]
+        params.k = element[1]
+        params.delta = element[2]
+        params.sigma = get_sigma_from_k(params.k)
+
+        # Retrieve the corresponding FTR ECDF
+        ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
+        ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
+
+        if (ad_meas < best_ad):
+            best_params = params
+            best_ad = ad_meas
+
+    for _ in range(num_refinements):
+        # Refine search in the neighborhood of the previously identified params
+        finer_search_grid = {
+            'm': np.power(np.ones(num_params)*10,
+                          np.linspace(start=max(0, np.log10(best_params.m) - m_and_k_step),
+                                      stop=np.log10(best_params.m) +
+                                      m_and_k_step,
+                                      endpoint=True, num=num_params)),
+            'k': np.power(np.ones(num_params)*10,
+                          np.linspace(start=max(0, np.log10(best_params.k) - m_and_k_step),
+                                      stop=np.log10(best_params.k) +
+                                      m_and_k_step,
+                                      endpoint=True, num=num_params)),
+            'delta': np.linspace(start=max(0, best_params.delta - delta_step),
+                                 stop=min(1, best_params.delta + delta_step),
+                                 endpoint=True, num=num_params)
+            # sigma determined from k, due to the unit-mean constraint
+        }
+
+        m_and_k_step = (np.log10(best_params.m) + m_and_k_step -
+                        max(0, np.log10(best_params.m) - m_and_k_step)) / n_samples
+        delta_step = (min(1, best_params.delta + 1/num_params) -
+                      max(0, best_params.delta - 1/num_params)) / n_samples
+
+        for element in product(*finer_search_grid.values()):
+
+            # Create FTR params object
+            params = FtrParams()
+            params.m = element[0]
+            params.k = element[1]
+            params.delta = element[2]
+            params.sigma = get_sigma_from_k(params.k)
+
+            # Retrieve the corresponding FTR ECDF
+            ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
+            ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
+
+            if (ad_meas < best_ad):
+                best_params = params
+                best_ad = ad_meas
+
+    out_str = f"{ref_params_combo[0]}\t{ref_params_combo[1]}\t{ref_params_combo[2]}" + \
+        f" \t{best_params.sigma}\t{best_params.k}\t{best_params.delta}\t{best_params.m}\n"
+
+    return out_str
+
+
+def append_ftr_params_to_cpp_string(text: str, params: FtrParams) -> str:
+    text += f'TwoRaySpectrumPropagationLossModel::FtrParams({np.format_float_scientific(params.m)}, {np.format_float_scientific(params.sigma)}, \
+                                                            {np.format_float_scientific(params.k)}, {np.format_float_scientific(params.delta)})'
+
+    return text
+
+
+def print_cplusplus_map_from_fit_results(fit: pd.DataFrame, out_fname: str):
+    """
+    Prints to a file the results of the FTR fit, as C++ code.
+
+    Args:
+      fit (pd.DataFrame): A Pandas Dataframe holding the results of the FTR fit.
+      out_fname (str): The name of the file to print the C++ code to.
+    """
+
+    out_str = '{'
+
+    for scen in set(fit['scen']):
+        out_str += f'{{\"{scen}\",\n{{'
+
+        for cond in set(fit['cond']):
+            out_str += f'{{ChannelCondition::LosConditionValue::{cond}, \n'
+
+            # Print vector of carrier frequencies
+            freqs = np.sort(list(set(fit['fc'])))
+            out_str += "{{"
+            for fc in freqs:
+                out_str += f'{float(fc)}, '
+            out_str = out_str[0:-2]
+            out_str += '},\n{'
+
+            # Load corresponding fit results
+            for fc in freqs:
+                fit_line = fit.query(
+                    'scen == @scen and cond == @cond and fc == @fc')
+                assert(fit_line.reset_index().shape[0] == 1)
+
+                params = FtrParams()
+                params.m = fit_line.iloc[0]['m']
+                params.k = fit_line.iloc[0]['k']
+                params.delta = fit_line.iloc[0]['delta']
+                params.sigma = fit_line.iloc[0]['sigma']
+
+                # Print vector of corresponding FTR parameters
+                out_str = append_ftr_params_to_cpp_string(out_str, params)
+                out_str += ', '
+
+            out_str = out_str[0:-2]
+            out_str += '}'
+            out_str += '}},\n'
+
+        out_str = out_str[0:-2]
+        out_str += '}},\n'
+
+    out_str = out_str[0:-2]
+    out_str += '}\n'
+
+    f = open(out_fname, "w")
+    f.write(out_str)
+    f.close()
+
+
+if __name__ == '__main__':
+
+    #########################
+    ## Data pre-processing ##
+    #########################
+
+    # Load reference data obtained from the ns-3 TR 38.901 implementation
+    df = pd.read_csv(ref_data_fname, sep='\t')
+    # Linear gain --> gain in dB
+    df['gain'] = 10*np.log10(df['gain'])
+
+    # Retrieve the possible parameters configurations
+    scenarios = set(df['scen'])
+    is_los = set(df['cond'])
+    frequencies = np.sort(list(set(df['fc'])))
+
+    ####################################################################################################
+    ## Fit Fluctuating Two Ray model to the 3GPP TR 38.901 using the Anderson-Darling goodness-of-fit ##
+    ####################################################################################################
+
+    if preliminary_fit_test:
+
+        params = FtrParams()
+        get_ftr_ecdf(params, 100)
+
+        # Make sure the mean is indeed independent from the delta parameter
+        mean_list = []
+        params = FtrParams()
+        for delta in np.linspace(start=0, stop=1, num=100):
+            params.delta = delta
+            mean_list.append(compute_ftr_mean(params))
+
+        avg_mean = np.mean(mean_list)
+        assert np.all(np.abs(mean_list - avg_mean) < epsilon)
+
+        # Make sure that we are indeed generating parameters yielding unit-mean
+        mean_list.clear()
+        mean_th_list = []
+        params = FtrParams()
+        for k in np.linspace(start=1, stop=500, num=50):
+            sigma = get_sigma_from_k(k)
+            params.sigma = sigma
+            params.k = k
+            mean_list.append(compute_ftr_mean(params))
+            mean_th_list.append(compute_ftr_th_mean(params))
+
+        assert np.all(np.abs(mean_list - np.float64(1.0)) < epsilon)
+        assert np.all(np.abs(mean_th_list - np.float64(1.0)) < epsilon)
+
+    if fit_ftr_to_threegpp:
+
+        # Parallel search for the different simulation parameters combination
+        with tqdm_joblib(tqdm(desc="Fitting FTR to the 3GPP fading model", total=(len(scenarios) * len(is_los) * len(frequencies)))) as progress_bar:
+            res = joblib.Parallel(n_jobs=10)(
+                joblib.delayed(fit_ftr_to_reference)(df, params_comb, num_search_grid_params, num_refinements) for params_comb in product(scenarios, is_los, frequencies))
+
+        f = open(fit_out_fname, "w")
+        f.write("scen\tcond\tfc\tsigma\tk\tdelta\tm\n")
+        for line in res:
+            f.write(line)
+
+        f.close()
+
+    if output_ns3_table:
+
+        # Load the fit results
+        fit = pd.read_csv(fit_out_fname, delimiter='\t')
+
+        # Output the C++ data structure
+        print_cplusplus_map_from_fit_results(fit, c_plus_plus_out_fname)
+
+    if plot_fit_results:
+
+        # Set Seaborn defaults and setup output folder
+        sns.set(rc={'figure.figsize': (7, 5)})
+        sns.set_theme()
+        sns.set_style('darkgrid')
+
+        fit = pd.read_csv(fit_out_fname, delimiter='\t')
+        # Create folder if it does not exist
+        Path(figs_folder).mkdir(parents=True, exist_ok=True)
+        ad_measures = []
+
+        for params_comb in product(scenarios, is_los, frequencies):
+
+            data_query = 'scen == @params_comb[0] and cond == @params_comb[1] and fc == @params_comb[2]'
+
+            # Load corresponding reference data
+            ref_data = df.query(data_query)
+
+            # Create FTR params object
+            fit_line = fit.query(data_query)
+            assert(fit_line.reset_index().shape[0] == 1)
+            params = FtrParams()
+            params.m = fit_line.iloc[0]['m']
+            params.k = fit_line.iloc[0]['k']
+            params.delta = fit_line.iloc[0]['delta']
+            params.sigma = fit_line.iloc[0]['sigma']
+
+            # Retrieve the corresponding FTR ECDF
+            ftr_ecdf = get_ftr_ecdf(params, len(ref_data), db=True)
+
+            # Compute the AD measure
+            ad_meas = compute_anderson_darling_measure(
+                np.sort(ref_data['gain']), ftr_ecdf)
+            ad_measures.append(np.sqrt(ad_meas))
+
+            sns.ecdfplot(data=ref_data, x='gain',
+                         label='38.901 reference model')
+            sns.ecdfplot(
+                ftr_ecdf, label=f'Fitted FTR, sqrt(AD)={round(np.sqrt(ad_meas), 2)}')
+            plt.xlabel(
+                'End-to-end channel gain due to small scale fading [dB]')
+            plt.legend()
+            plt.savefig(
+                f'{figs_folder}{params_comb[0]}_{params_comb[1]}_{params_comb[2]/1e9}GHz_fit.png', dpi=500, bbox_inches='tight')
+            plt.clf()
+
+        # Plot ECDF of the scaled and normalized AD measures
+        sns.ecdfplot(ad_measures, label='AD measures')
+        plt.xlabel('Anderson-Darling goodness-of-fit')
+        plt.legend()
+        plt.savefig(f'{figs_folder}AD_measures.png',
+                    dpi=500, bbox_inches='tight')
+        plt.clf()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/CMakeLists.txt ns-3.38/src/stats/CMakeLists.txt
--- ns-3.37/src/stats/CMakeLists.txt	2022-11-01 17:37:24.711395700 -0700
+++ ns-3.38/src/stats/CMakeLists.txt	2023-03-16 20:54:23.828869000 -0700
@@ -1,5 +1,6 @@
 set(sqlite_sources)
-set(sqlite_header)
+set(sqlite_headers)
+set(private_sqlite_header)
 set(sqlite_libraries)
 if(${ENABLE_SQLITE})
   set(sqlite_sources
@@ -8,6 +9,8 @@
   )
   set(sqlite_headers
       model/sqlite-data-output.h
+  )
+  set(private_sqlite_headers
       model/sqlite-output.h
   )
   set(sqlite_libraries
@@ -73,6 +76,7 @@
   LIBNAME stats
   SOURCE_FILES ${source_files}
   HEADER_FILES ${header_files}
+  PRIVATE_HEADER_FILES ${private_sqlite_headers}
   LIBRARIES_TO_LINK ${libcore}
                     ${sqlite_libraries}
   TEST_SOURCES
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/doc/aggregator.rst ns-3.38/src/stats/doc/aggregator.rst
--- ns-3.37/src/stats/doc/aggregator.rst	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/doc/aggregator.rst	2023-03-16 20:54:23.828869000 -0700
@@ -79,7 +79,7 @@
 
     // Create an aggregator.
     Ptr<GnuplotAggregator> aggregator =
-      CreateObject<GnuplotAggregator> (fileNameWithoutExtension);
+      CreateObject<GnuplotAggregator>(fileNameWithoutExtension);
 
 The first argument for the constructor, fileNameWithoutExtension, is
 the name of the gnuplot related files to write with no extension.
@@ -138,18 +138,18 @@
 
 ::
 
-  void Create2dPlot ()
+  void Create2dPlot()
   {
     std::string fileNameWithoutExtension = "gnuplot-aggregator";
     std::string plotTitle                = "Gnuplot Aggregator Plot";
-    std::string plotXAxisHeading         = "Time (seconds)";
+    std::string plotXAxisHeading         = "Time(seconds)";
     std::string plotYAxisHeading         = "Double Values";
     std::string plotDatasetLabel         = "Data Values";
     std::string datasetContext           = "Dataset/Context/String";
 
     // Create an aggregator.
     Ptr<GnuplotAggregator> aggregator =
-      CreateObject<GnuplotAggregator> (fileNameWithoutExtension);
+      CreateObject<GnuplotAggregator>(fileNameWithoutExtension);
 
 Various GnuplotAggregator attributes are set including the 2-D dataset
 that will be plotted.
@@ -157,15 +157,15 @@
 ::
 
     // Set the aggregator's properties.
-    aggregator->SetTerminal ("png");
-    aggregator->SetTitle (plotTitle);
-    aggregator->SetLegend (plotXAxisHeading, plotYAxisHeading);
+    aggregator->SetTerminal("png");
+    aggregator->SetTitle(plotTitle);
+    aggregator->SetLegend(plotXAxisHeading, plotYAxisHeading);
 
     // Add a data set to the aggregator.
-    aggregator->Add2dDataset (datasetContext, plotDatasetLabel);
+    aggregator->Add2dDataset(datasetContext, plotDatasetLabel);
 
     // aggregator must be turned on
-    aggregator->Enable ();
+    aggregator->Enable();
 
 Next, the 2-D values are calculated, and each one is individually
 written to the GnuplotAggregator using the ``Write2d()`` function.
@@ -186,11 +186,11 @@
         value = time * time;
 
         // Add this point to the plot.
-        aggregator->Write2d (datasetContext, time, value);
+        aggregator->Write2d(datasetContext, time, value);
       }
 
     // Disable logging of data for the aggregator.
-    aggregator->Disable ();
+    aggregator->Disable();
   }
 
 FileAggregator
@@ -227,7 +227,7 @@
 
     // Create an aggregator that will have formatted values.
     Ptr<FileAggregator> aggregator =
-      CreateObject<FileAggregator> (fileName, FileAggregator::FORMATTED);
+      CreateObject<FileAggregator>(fileName, FileAggregator::FORMATTED);
 
 The first argument for the constructor, filename, is the name of the
 file to write; the second argument, fileType, is type of file to
@@ -281,21 +281,21 @@
 
 ::
 
-  void CreateCommaSeparatedFile ()
+  void CreateCommaSeparatedFile()
   {
     std::string fileName       = "file-aggregator-comma-separated.txt";
     std::string datasetContext = "Dataset/Context/String";
 
     // Create an aggregator.
     Ptr<FileAggregator> aggregator =
-      CreateObject<FileAggregator> (fileName, FileAggregator::COMMA_SEPARATED);
+      CreateObject<FileAggregator>(fileName, FileAggregator::COMMA_SEPARATED);
 
 FileAggregator attributes are set.
 
 ::
 
     // aggregator must be turned on
-    aggregator->Enable ();
+    aggregator->Enable();
 
 Next, the 2-D values are calculated, and each one is individually
 written to the FileAggregator using the ``Write2d()`` function.
@@ -316,11 +316,11 @@
         value = time * time;
 
         // Add this point to the plot.
-        aggregator->Write2d (datasetContext, time, value);
+        aggregator->Write2d(datasetContext, time, value);
       }
 
     // Disable logging of data for the aggregator.
-    aggregator->Disable ();
+    aggregator->Disable();
   }
 
 The following text file with 2 columns of formatted values was also
@@ -345,14 +345,14 @@
 
 ::
 
-  void CreateFormattedFile ()
+  void CreateFormattedFile()
   {
     std::string fileName       = "file-aggregator-formatted-values.txt";
     std::string datasetContext = "Dataset/Context/String";
 
     // Create an aggregator that will have formatted values.
     Ptr<FileAggregator> aggregator =
-      CreateObject<FileAggregator> (fileName, FileAggregator::FORMATTED);
+      CreateObject<FileAggregator>(fileName, FileAggregator::FORMATTED);
 
 FileAggregator attributes are set, including the C-style format string
 to use.
@@ -360,10 +360,10 @@
 ::
 
     // Set the format for the values.
-    aggregator->Set2dFormat ("Time = %.3e\tValue = %.0f");
+    aggregator->Set2dFormat("Time = %.3e\tValue = %.0f");
 
     // aggregator must be turned on
-    aggregator->Enable ();
+    aggregator->Enable();
 
 Next, the 2-D values are calculated, and each one is individually
 written to the FileAggregator using the ``Write2d()`` function.
@@ -384,10 +384,9 @@
         value = time * time;
 
         // Add this point to the plot.
-        aggregator->Write2d (datasetContext, time, value);
+        aggregator->Write2d(datasetContext, time, value);
       }
 
     // Disable logging of data for the aggregator.
-    aggregator->Disable ();
+    aggregator->Disable();
   }
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/doc/data-collection-helpers.rst ns-3.38/src/stats/doc/data-collection-helpers.rst
--- ns-3.37/src/stats/doc/data-collection-helpers.rst	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/doc/data-collection-helpers.rst	2023-03-16 20:54:23.828869000 -0700
@@ -69,20 +69,20 @@
 
 ::
 
-   void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
-                       const std::string &title,
-                       const std::string &xLegend,
-                       const std::string &yLegend,
-                       const std::string &terminalType = ".png");
+   void ConfigurePlot(const std::string &outputFileNameWithoutExtension,
+                      const std::string &title,
+                      const std::string &xLegend,
+                      const std::string &yLegend,
+                      const std::string &terminalType = ".png");
 
 The second statement hooks the trace source of interest:
 
 ::
 
-   void PlotProbe (const std::string &typeId,
-                   const std::string &path,
-                   const std::string &probeTraceSource,
-                   const std::string &title);
+   void PlotProbe(const std::string &typeId,
+                  const std::string &path,
+                  const std::string &probeTraceSource,
+                  const std::string &title);
 
 The arguments are as follows:
 
@@ -105,21 +105,21 @@
    // Configure the plot.  The first argument is the file name prefix
    // for the output files generated.  The second, third, and fourth
    // arguments are, respectively, the plot title, x-axis, and y-axis labels
-   plotHelper.ConfigurePlot ("seventh-packet-byte-count",
-                             "Packet Byte Count vs. Time",
-                             "Time (Seconds)",
-                             "Packet Byte Count",
-                             "png");
+   plotHelper.ConfigurePlot("seventh-packet-byte-count",
+                            "Packet Byte Count vs. Time",
+                            "Time(Seconds)",
+                            "Packet Byte Count",
+                            "png");
 
    // Specify the probe type, trace source path (in configuration namespace), and
    // probe output trace source ("OutputBytes") to plot.  The fourth argument
    // specifies the name of the data series label on the plot.  The last
    // argument formats the plot by specifying where the key should be placed.
-   plotHelper.PlotProbe (probeType,
-                         tracePath,
-                         "OutputBytes",
-                         "Packet Byte Count",
-                         GnuplotAggregator::KEY_BELOW);
+   plotHelper.PlotProbe(probeType,
+                        tracePath,
+                        "OutputBytes",
+                        "Packet Byte Count",
+                        GnuplotAggregator::KEY_BELOW);
 
 In this example, the ``probeType`` and ``tracePath`` are as follows (for IPv4):
 
@@ -176,11 +176,11 @@
 
 ::
 
-  void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
-                      const std::string &title,
-                      const std::string &xLegend,
-                      const std::string &yLegend,
-                      const std::string &terminalType = ".png");
+  void ConfigurePlot(const std::string &outputFileNameWithoutExtension,
+                     const std::string &title,
+                     const std::string &xLegend,
+                     const std::string &yLegend,
+                     const std::string &terminalType = ".png");
 
 It has the following arguments:
 
@@ -217,11 +217,11 @@
 
 ::
 
-  plotHelper.ConfigurePlot ("seventh-packet-byte-count",
-                            "Packet Byte Count vs. Time",
-                            "Time (Seconds)",
-                            "Packet Byte Count",
-                            "png");
+  plotHelper.ConfigurePlot("seventh-packet-byte-count",
+                           "Packet Byte Count vs. Time",
+                           "Time(Seconds)",
+                           "Packet Byte Count",
+                           "png");
 
 GnuplotHelper PlotProbe
 #######################
@@ -233,11 +233,11 @@
 
 ::
 
-  void PlotProbe (const std::string &typeId,
-                  const std::string &path,
-                  const std::string &probeTraceSource,
-                  const std::string &title,
-                  enum GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);
+  void PlotProbe(const std::string &typeId,
+                 const std::string &path,
+                 const std::string &probeTraceSource,
+                 const std::string &title,
+                 enum GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);
 
 It has the following arguments:
 
@@ -283,11 +283,11 @@
 
 ::
 
-  plotHelper.PlotProbe ("ns3::Ipv4PacketProbe",
-                        "/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
-                        "OutputBytes",
-                        "Packet Byte Count",
-                        GnuplotAggregator::KEY_BELOW);
+  plotHelper.PlotProbe("ns3::Ipv4PacketProbe",
+                       "/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
+                       "OutputBytes",
+                       "Packet Byte Count",
+                       GnuplotAggregator::KEY_BELOW);
 
 Other Examples
 ##############
@@ -311,8 +311,8 @@
 
 ::
 
-  Ptr<Emitter> emitter = CreateObject<Emitter> ();
-  Names::Add ("/Names/Emitter", emitter);
+  Ptr<Emitter> emitter = CreateObject<Emitter>();
+  Names::Add("/Names/Emitter", emitter);
 
 Note that because there are no wildcards in the path
 used below, only 1 datastream was drawn in the plot.
@@ -326,19 +326,19 @@
   GnuplotHelper plotHelper;
 
   // Configure the plot.
-  plotHelper.ConfigurePlot ("gnuplot-helper-example",
-                            "Emitter Counts vs. Time",
-                            "Time (Seconds)",
-                            "Emitter Count",
-                            "png");
+  plotHelper.ConfigurePlot("gnuplot-helper-example",
+                           "Emitter Counts vs. Time",
+                           "Time(Seconds)",
+                           "Emitter Count",
+                           "png");
 
   // Plot the values generated by the probe.  The path that we provide
   // helps to disambiguate the source of the trace.
-  plotHelper.PlotProbe ("ns3::Uinteger32Probe",
-                        "/Names/Emitter/Counter",
-                        "Output",
-                        "Emitter Count",
-                        GnuplotAggregator::KEY_INSIDE);
+  plotHelper.PlotProbe("ns3::Uinteger32Probe",
+                       "/Names/Emitter/Counter",
+                       "Output",
+                       "Emitter Count",
+                       GnuplotAggregator::KEY_INSIDE);
 
 FileHelper
 ==========
@@ -425,16 +425,16 @@
    FileHelper fileHelper;
 
    // Configure the file to be written.
-   fileHelper.ConfigureFile ("seventh-packet-byte-count",
-                             FileAggregator::FORMATTED);
+   fileHelper.ConfigureFile("seventh-packet-byte-count",
+                            FileAggregator::FORMATTED);
 
    // Set the labels for this formatted output file.
-   fileHelper.Set2dFormat ("Time (Seconds) = %.3e\tPacket Byte Count = %.0f");
+   fileHelper.Set2dFormat("Time (Seconds) = %.3e\tPacket Byte Count = %.0f");
 
    // Write the values generated by the probe.
-   fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
-                          "/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
-                          "OutputBytes");
+   fileHelper.WriteProbe("ns3::Ipv4PacketProbe",
+                         "/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
+                         "OutputBytes");
 
 FileHelper ConfigureFile
 ########################
@@ -446,8 +446,8 @@
 
 ::
 
-  void ConfigureFile (const std::string &outputFileNameWithoutExtension,
-                      enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
+  void ConfigureFile(const std::string &outputFileNameWithoutExtension,
+                     enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
 
 It has the following arguments:
 
@@ -474,8 +474,8 @@
 
 ::
 
-  fileHelper.ConfigureFile ("seventh-packet-byte-count",
-                            FileAggregator::FORMATTED);
+  fileHelper.ConfigureFile("seventh-packet-byte-count",
+                           FileAggregator::FORMATTED);
 
 FileHelper WriteProbe
 #####################
@@ -487,9 +487,9 @@
 
 ::
 
-  void WriteProbe (const std::string &typeId,
-                   const std::string &path,
-                   const std::string &probeTraceSource);
+  void WriteProbe(const std::string &typeId,
+                  const std::string &path,
+                  const std::string &probeTraceSource);
 
 It has the following arguments:
 
@@ -530,9 +530,9 @@
 
 ::
 
-  fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
-                         "/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
-                         "OutputBytes");
+  fileHelper.WriteProbe("ns3::Ipv4PacketProbe",
+                        "/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
+                        "OutputBytes");
 
 Other Examples
 ##############
@@ -567,8 +567,8 @@
 
 ::
 
-  Ptr<Emitter> emitter = CreateObject<Emitter> ();
-  Names::Add ("/Names/Emitter", emitter);
+  Ptr<Emitter> emitter = CreateObject<Emitter>();
+  Names::Add("/Names/Emitter", emitter);
 
 Note that because there are no wildcards in the path
 used below, only 1 text file was created.
@@ -582,17 +582,17 @@
   FileHelper fileHelper;
 
   // Configure the file to be written.
-  fileHelper.ConfigureFile ("file-helper-example",
-                            FileAggregator::FORMATTED);
+  fileHelper.ConfigureFile("file-helper-example",
+                           FileAggregator::FORMATTED);
 
   // Set the labels for this formatted output file.
-  fileHelper.Set2dFormat ("Time (Seconds) = %.3e\tCount = %.0f");
+  fileHelper.Set2dFormat("Time (Seconds) = %.3e\tCount = %.0f");
 
   // Write the values generated by the probe.  The path that we
   // provide helps to disambiguate the source of the trace.
-  fileHelper.WriteProbe ("ns3::Uinteger32Probe",
-                         "/Names/Emitter/Counter",
-                         "Output");
+  fileHelper.WriteProbe("ns3::Uinteger32Probe",
+                        "/Names/Emitter/Counter",
+                        "Output");
 
 Scope and Limitations
 =====================
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/doc/probe.rst ns-3.38/src/stats/doc/probe.rst
--- ns-3.37/src/stats/doc/probe.rst	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/doc/probe.rst	2023-03-16 20:54:23.828869000 -0700
@@ -45,19 +45,19 @@
   complicated structures; for instance, outputting the packet size value
   from a received ns3::Packet.
 * Probes register a name in the ns3::Config namespace (using
-  ``Names::Add ()``) so that other objects may refer to them.
+  ``Names::Add()``) so that other objects may refer to them.
 * Probes provide a static method that allows one to manipulate a Probe
   by name, such as what is done in ns2measure [Cic06]_
 
   ::
 
-    Stat::put ("my_metric", ID, sample);
+    Stat::put("my_metric", ID, sample);
 
   The ns-3 equivalent of the above ns2measure code is, e.g.
 
   ::
 
-    DoubleProbe::SetValueByPath ("/path/to/probe", sample);
+    DoubleProbe::SetValueByPath("/path/to/probe", sample);
 
 Creation
 ########
@@ -74,7 +74,7 @@
 
 ::
 
-  Ptr<DoubleProbe> myprobe = CreateObject<DoubleProbe> ();
+  Ptr<DoubleProbe> myprobe = CreateObject<DoubleProbe>();
 
 The declaration above creates DoubleProbes using the default values for its
 attributes.  There are four attributes in the DoubleProbe class; two in the
@@ -90,11 +90,11 @@
 
 ::
 
-  Ptr<DoubleProbe> myprobe = CreateObjectWithAttributes<DoubleProbe> (
-      "Name", StringValue ("myprobe"),
-      "Enabled", BooleanValue (false),
-      "Start", TimeValue (Seconds (100.0)),
-      "Stop", TimeValue (Seconds (1000.0)));
+  Ptr<DoubleProbe> myprobe = CreateObjectWithAttributes<DoubleProbe>(
+      "Name", StringValue("myprobe"),
+      "Enabled", BooleanValue(false),
+      "Start", TimeValue(Seconds(100.0)),
+      "Stop", TimeValue(Seconds(1000.0)));
 
 Start and Stop are Time variables which determine the interval of action
 of the Probe. The Probe will only output data if the current time of the
@@ -128,14 +128,14 @@
 
 ::
 
-  Ptr<Emitter> emitter = CreateObject<Emitter> ();
-  Names::Add ("/Names/Emitter", emitter);
+  Ptr<Emitter> emitter = CreateObject<Emitter>();
+  Names::Add("/Names/Emitter", emitter);
   ...
 
-  Ptr<DoubleProbe> probe1 = CreateObject<DoubleProbe> ();
+  Ptr<DoubleProbe> probe1 = CreateObject<DoubleProbe>();
 
   // Connect the probe to the emitter's Counter
-  bool connected = probe1->ConnectByObject ("Counter", emitter);
+  bool connected = probe1->ConnectByObject("Counter", emitter);
 
 The following code is probing the same Counter exported by the same
 emitter object.  This DoubleProbe, however, is using a path in the
@@ -145,10 +145,10 @@
 
 ::
 
-  Ptr<DoubleProbe> probe2 = CreateObject<DoubleProbe> ();
+  Ptr<DoubleProbe> probe2 = CreateObject<DoubleProbe>();
 
   // Note, no return value is checked here
-  probe2->ConnectByPath ("/Names/Emitter/Counter");
+  probe2->ConnectByPath("/Names/Emitter/Counter");
 
 The next DoubleProbe shown that is shown below will have its value set using
 its path in the configuration namespace.  Note that this time the
@@ -157,22 +157,22 @@
 
 ::
 
-  Ptr<DoubleProbe> probe3 = CreateObject<DoubleProbe> ();
-  probe3->SetName ("StaticallyAccessedProbe");
+  Ptr<DoubleProbe> probe3 = CreateObject<DoubleProbe>();
+  probe3->SetName("StaticallyAccessedProbe");
 
   // We must add it to the config database
-  Names::Add ("/Names/Probes", probe3->GetName (), probe3);
+  Names::Add("/Names/Probes", probe3->GetName(), probe3);
 
 The emitter's Count() function is now able to set the value for this DoubleProbe as follows:
 
 ::
 
   void
-  Emitter::Count (void)
+  Emitter::Count()
   {
     ...
     m_counter += 1.0;
-    DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
+    DoubleProbe::SetValueByPath("/Names/StaticallyAccessedProbe", m_counter);
     ...
   }
 
@@ -197,7 +197,7 @@
 
 ::
 
-  connected = probe1->TraceConnect ("Output", probe1->GetName (), MakeCallback (&NotifyViaProbe));
+  connected = probe1->TraceConnect("Output", probe1->GetName(), MakeCallback(&NotifyViaProbe));
 
 
 Other probes
@@ -259,13 +259,13 @@
   ::
 
     void
-    Emitter::Count (void)
+    Emitter::Count()
     {
-      NS_LOG_FUNCTION (this);
-      NS_LOG_DEBUG ("Counting at " << Simulator::Now ().GetSeconds ());
+      NS_LOG_FUNCTION(this);
+      NS_LOG_DEBUG("Counting at " << Simulator::Now().GetSeconds());
       m_counter += 1.0;
-      DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
-      Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this);
+      DoubleProbe::SetValueByPath("/Names/StaticallyAccessedProbe", m_counter);
+      Simulator::Schedule(Seconds(m_var->GetValue()), &Emitter::Count, this);
     }
 
 Let's look at the Probe more carefully.  Probes can receive their values
@@ -287,21 +287,21 @@
 
   // This is a function to test hooking a raw function to the trace source
   void
-  NotifyViaTraceSource (std::string context, double oldVal, double newVal)
+  NotifyViaTraceSource(std::string context, double oldVal, double newVal)
   {
-    NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
+    NS_LOG_DEBUG("context: " << context << " old " << oldVal << " new " << newVal);
   }
 
 First, the emitter needs to be setup:
 
 ::
 
-    Ptr<Emitter> emitter = CreateObject<Emitter> ();
-    Names::Add ("/Names/Emitter", emitter);
+    Ptr<Emitter> emitter = CreateObject<Emitter>();
+    Names::Add("/Names/Emitter", emitter);
 
     // The Emitter object is not associated with an ns-3 node, so
     // it won't get started automatically, so we need to do this ourselves
-    Simulator::Schedule (Seconds (0.0), &Emitter::Start, emitter);
+    Simulator::Schedule(Seconds(0.0), &Emitter::Start, emitter);
 
 The various DoubleProbes interact with the emitter in the example as
 shown below.
@@ -313,8 +313,8 @@
     // The below shows typical functionality without a probe
     // (connect a sink function to a trace source)
     //
-    connected = emitter->TraceConnect ("Counter", "sample context", MakeCallback (&NotifyViaTraceSource));
-    NS_ASSERT_MSG (connected, "Trace source not connected");
+    connected = emitter->TraceConnect("Counter", "sample context", MakeCallback(&NotifyViaTraceSource));
+    NS_ASSERT_MSG(connected, "Trace source not connected");
 
 
 case 1):
@@ -326,13 +326,13 @@
     //
 
     // probe1 will be hooked to the Emitter trace source
-    Ptr<DoubleProbe> probe1 = CreateObject<DoubleProbe> ();
+    Ptr<DoubleProbe> probe1 = CreateObject<DoubleProbe>();
     // the probe's name can serve as its context in the tracing
-    probe1->SetName ("ObjectProbe");
+    probe1->SetName("ObjectProbe");
 
     // Connect the probe to the emitter's Counter
-    connected = probe1->ConnectByObject ("Counter", emitter);
-    NS_ASSERT_MSG (connected, "Trace source not connected to probe1");
+    connected = probe1->ConnectByObject("Counter", emitter);
+    NS_ASSERT_MSG(connected, "Trace source not connected to probe1");
 
 case 2):
 
@@ -344,13 +344,13 @@
     //
 
     // Create another similar probe; this will hook up via a Config path
-    Ptr<DoubleProbe> probe2 = CreateObject<DoubleProbe> ();
-    probe2->SetName ("PathProbe");
+    Ptr<DoubleProbe> probe2 = CreateObject<DoubleProbe>();
+    probe2->SetName("PathProbe");
 
     // Note, no return value is checked here
-    probe2->ConnectByPath ("/Names/Emitter/Counter");
+    probe2->ConnectByPath("/Names/Emitter/Counter");
 
-case 4) (case 3 is not shown in this example):
+case 4)(case 3 is not shown in this example):
 
   ::
 
@@ -358,10 +358,10 @@
     // Probe3 will be called by the emitter directly through the
     // static method SetValueByPath().
     //
-    Ptr<DoubleProbe> probe3 = CreateObject<DoubleProbe> ();
-    probe3->SetName ("StaticallyAccessedProbe");
+    Ptr<DoubleProbe> probe3 = CreateObject<DoubleProbe>();
+    probe3->SetName("StaticallyAccessedProbe");
     // We must add it to the config database
-    Names::Add ("/Names/Probes", probe3->GetName (), probe3);
+    Names::Add("/Names/Probes", probe3->GetName(), probe3);
 
 And finally, the example shows how the probes can be hooked to
 generate output:
@@ -371,10 +371,10 @@
     // The probe itself should generate output.  The context that we provide
     // to this probe (in this case, the probe name) will help to disambiguate
     // the source of the trace
-    connected = probe3->TraceConnect ("Output",
-                                      "/Names/Probes/StaticallyAccessedProbe/Output",
-                                      MakeCallback (&NotifyViaProbe));
-    NS_ASSERT_MSG (connected, "Trace source not .. connected to probe3 Output");
+    connected = probe3->TraceConnect("Output",
+                                     "/Names/Probes/StaticallyAccessedProbe/Output",
+                                     MakeCallback(&NotifyViaProbe));
+    NS_ASSERT_MSG(connected, "Trace source not .. connected to probe3 Output");
 
 The following callback is hooked to the Probe in this example for
 illustrative purposes; normally, the Probe would be hooked to a
@@ -384,9 +384,9 @@
 
   // This is a function to test hooking it to the probe output
   void
-  NotifyViaProbe (std::string context, double oldVal, double newVal)
+  NotifyViaProbe(std::string context, double oldVal, double newVal)
   {
-    NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
+    NS_LOG_DEBUG("context: " << context << " old " << oldVal << " new " << newVal);
   }
 
 
@@ -424,17 +424,17 @@
 ::
 
     TypeId
-    Ipv4PacketProbe::GetTypeId ()
+    Ipv4PacketProbe::GetTypeId()
     {
-      static TypeId tid = TypeId ("ns3::Ipv4PacketProbe")
-        .SetParent<Probe> ()
-        .AddConstructor<Ipv4PacketProbe> ()
-        .AddTraceSource ( "Output",
-                          "The packet plus its IPv4 object and interface that serve as the output for this probe",
-                          MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output))
-        .AddTraceSource ( "OutputBytes",
-                          "The number of bytes in the packet",
-                          MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes))
+      static TypeId tid = TypeId("ns3::Ipv4PacketProbe")
+        .SetParent<Probe>()
+        .AddConstructor<Ipv4PacketProbe>()
+        .AddTraceSource( "Output",
+                         "The packet plus its IPv4 object and interface that serve as the output for this probe",
+                         MakeTraceSourceAccessor(&Ipv4PacketProbe::m_output))
+        .AddTraceSource( "OutputBytes",
+                         "The number of bytes in the packet",
+                         MakeTraceSourceAccessor(&Ipv4PacketProbe::m_outputBytes))
       ;
       return tid;
     }
@@ -447,18 +447,18 @@
 ::
 
     void
-    Ipv4PacketProbe::TraceSink (Ptr<const Packet> packet, Ptr<Ipv4> ipv4, uint32_t interface)
+    Ipv4PacketProbe::TraceSink(Ptr<const Packet> packet, Ptr<Ipv4> ipv4, uint32_t interface)
     {
-      NS_LOG_FUNCTION (this << packet << ipv4 << interface);
-      if (IsEnabled ())
+      NS_LOG_FUNCTION(this << packet << ipv4 << interface);
+      if (IsEnabled())
         {
           m_packet    = packet;
           m_ipv4      = ipv4;
           m_interface = interface;
-          m_output (packet, ipv4, interface);
+          m_output(packet, ipv4, interface);
 
-          uint32_t packetSizeNew = packet->GetSize ();
-          m_outputBytes (m_packetSizeOld, packetSizeNew);
+          uint32_t packetSizeNew = packet->GetSize();
+          m_outputBytes(m_packetSizeOld, packetSizeNew);
           m_packetSizeOld = packetSizeNew;
         }
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/doc/statistics.rst ns-3.38/src/stats/doc/statistics.rst
--- ns-3.37/src/stats/doc/statistics.rst	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/doc/statistics.rst	2023-03-16 20:54:23.828869000 -0700
@@ -103,18 +103,18 @@
   ::
 
     double distance = 50.0;
-    string format ("OMNet++");
-    string experiment ("wifi-distance-test");
-    string strategy ("wifi-default");
+    string format("OMNet++");
+    string experiment("wifi-distance-test");
+    string strategy("wifi-default");
     string runID;
 
-    CommandLine cmd (__FILE__);
-    cmd.AddValue("distance",   "Distance apart to place nodes (in meters).", distance);
+    CommandLine cmd(__FILE__);
+    cmd.AddValue("distance",   "Distance apart to place nodes(in meters).", distance);
     cmd.AddValue("format",     "Format to use for data output.",             format);
     cmd.AddValue("experiment", "Identifier for experiment.",                 experiment);
     cmd.AddValue("strategy",   "Identifier for strategy.",                   strategy);
     cmd.AddValue("run",        "Identifier for run.",                        runID);
-    cmd.Parse (argc, argv);
+    cmd.Parse(argc, argv);
 
 * Creating nodes and network stacks using ``ns3::NodeContainer``, ``ns3::WiFiHelper``, and ``ns3::InternetStackHelper``.
 
@@ -192,7 +192,7 @@
 
   ::
 
-    Ptr<CounterCalculator<> > appRx = CreateObject<CounterCalculator<> >();
+    Ptr<CounterCalculator<>> appRx = CreateObject<CounterCalculator<>>();
     appRx->SetKey("receiver-rx-packets");
     receiver->SetCounter(appRx);
     data.AddDataCalculator(appRx);
@@ -357,5 +357,3 @@
 The resulting graph provides no evidence that the default WiFi model's performance is necessarily unreasonable and lends some confidence to an at least token faithfulness to reality.  More importantly, this simple investigation has been carried all the way through using the statistical framework.  Success!
 
 .. image:: figures/Wifi-default.png
-
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/average.h ns-3.38/src/stats/model/average.h
--- ns-3.37/src/stats/model/average.h	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/average.h	2023-03-16 20:54:23.828869000 -0700
@@ -200,7 +200,7 @@
  * Print avg (err) [min, max]
  * \param os The output stream
  * \param x The Average value to print
- * \return the ouput stream.
+ * \return the output stream.
  */
 template <typename T>
 std::ostream&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/file-aggregator.cc ns-3.38/src/stats/model/file-aggregator.cc
--- ns-3.37/src/stats/model/file-aggregator.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/file-aggregator.cc	2023-03-16 20:54:23.828869000 -0700
@@ -42,7 +42,7 @@
     return tid;
 }
 
-FileAggregator::FileAggregator(const std::string& outputFileName, enum FileType fileType)
+FileAggregator::FileAggregator(const std::string& outputFileName, FileType fileType)
     : m_outputFileName(outputFileName),
       m_fileType(fileType),
       m_hasHeadingBeenSet(false),
@@ -84,7 +84,7 @@
 }
 
 void
-FileAggregator::SetFileType(enum FileType fileType)
+FileAggregator::SetFileType(FileType fileType)
 {
     NS_LOG_FUNCTION(this << fileType);
     m_fileType = fileType;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/file-aggregator.h ns-3.38/src/stats/model/file-aggregator.h
--- ns-3.37/src/stats/model/file-aggregator.h	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/file-aggregator.h	2023-03-16 20:54:23.828869000 -0700
@@ -63,7 +63,7 @@
      * outputFileName with values printed as specified by fileType.  The
      * default file type is space-separated.
      */
-    FileAggregator(const std::string& outputFileName, enum FileType fileType = SPACE_SEPARATED);
+    FileAggregator(const std::string& outputFileName, FileType fileType = SPACE_SEPARATED);
 
     ~FileAggregator() override;
 
@@ -74,7 +74,7 @@
      * \brief Set the file type to create, which determines the
      * separator to use when printing values to the file.
      */
-    void SetFileType(enum FileType fileType);
+    void SetFileType(FileType fileType);
 
     /**
      * \param heading the heading string.
@@ -344,7 +344,7 @@
     std::ofstream m_file;
 
     /// Determines the kind of file written by the aggregator.
-    enum FileType m_fileType;
+    FileType m_fileType;
 
     /// Printed between values in the file.
     std::string m_separator;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/gnuplot-aggregator.h ns-3.38/src/stats/model/gnuplot-aggregator.h
--- ns-3.37/src/stats/model/gnuplot-aggregator.h	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/gnuplot-aggregator.h	2023-03-16 20:54:23.828869000 -0700
@@ -252,7 +252,7 @@
      *
      * \brief Set the location of the key in the plot.
      */
-    void SetKeyLocation(enum KeyLocation keyLocation);
+    void SetKeyLocation(KeyLocation keyLocation);
 
   private:
     /// The output file name without any extension.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/gnuplot.cc ns-3.38/src/stats/model/gnuplot.cc
--- ns-3.37/src/stats/model/gnuplot.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/gnuplot.cc	2023-03-16 20:54:23.828869000 -0700
@@ -173,8 +173,8 @@
 {
     // *** Data Variables ***
 
-    enum Style m_style;         //!< The plotting style to use for this dataset.
-    enum ErrorBars m_errorBars; //!< Whether errorbars should be used for this dataset.
+    Style m_style;         //!< The plotting style to use for this dataset.
+    ErrorBars m_errorBars; //!< Whether errorbars should be used for this dataset.
 
     PointSet m_pointset; //!< The set of points in this data set
 
@@ -223,7 +223,7 @@
         os << "\"" << dataFileName << "\" index " << dataFileDatasetIndex;
     }
 
-    if (m_title.size())
+    if (!m_title.empty())
     {
         os << " title \"" << m_title << "\"";
     }
@@ -284,7 +284,7 @@
         break;
     }
 
-    if (m_extra.size())
+    if (!m_extra.empty())
     {
         os << " " << m_extra;
     }
@@ -334,7 +334,7 @@
 bool
 Gnuplot2dDataset::Data2d::IsEmpty() const
 {
-    return (m_pointset.size() == 0);
+    return m_pointset.empty();
 }
 
 // --- Gnuplot2dDataset ---------------------------------------------------- //
@@ -350,25 +350,25 @@
 }
 
 void
-Gnuplot2dDataset::SetDefaultStyle(enum Style style)
+Gnuplot2dDataset::SetDefaultStyle(Style style)
 {
     m_defaultStyle = style;
 }
 
 void
-Gnuplot2dDataset::SetStyle(enum Style style)
+Gnuplot2dDataset::SetStyle(Style style)
 {
     reinterpret_cast<Data2d*>(m_data)->m_style = style;
 }
 
 void
-Gnuplot2dDataset::SetDefaultErrorBars(enum ErrorBars errorBars)
+Gnuplot2dDataset::SetDefaultErrorBars(ErrorBars errorBars)
 {
     m_defaultErrorBars = errorBars;
 }
 
 void
-Gnuplot2dDataset::SetErrorBars(enum ErrorBars errorBars)
+Gnuplot2dDataset::SetErrorBars(ErrorBars errorBars)
 {
     reinterpret_cast<Data2d*>(m_data)->m_errorBars = errorBars;
 }
@@ -378,7 +378,7 @@
 {
     NS_ASSERT(reinterpret_cast<Data2d*>(m_data)->m_errorBars == NONE);
 
-    struct Point data;
+    Point data;
     data.empty = false;
     data.x = x;
     data.y = y;
@@ -393,7 +393,7 @@
     NS_ASSERT(reinterpret_cast<Data2d*>(m_data)->m_errorBars == X ||
               reinterpret_cast<Data2d*>(m_data)->m_errorBars == Y);
 
-    struct Point data;
+    Point data;
     data.empty = false;
     data.x = x;
     data.y = y;
@@ -407,7 +407,7 @@
 {
     NS_ASSERT(reinterpret_cast<Data2d*>(m_data)->m_errorBars == XY);
 
-    struct Point data;
+    Point data;
     data.empty = false;
     data.x = x;
     data.y = y;
@@ -419,7 +419,7 @@
 void
 Gnuplot2dDataset::AddEmptyLine()
 {
-    struct Point data;
+    Point data;
     data.empty = true;
     reinterpret_cast<Data2d*>(m_data)->m_pointset.push_back(data);
 }
@@ -474,12 +474,12 @@
 {
     os << m_function;
 
-    if (m_title.size())
+    if (!m_title.empty())
     {
         os << " title \"" << m_title << "\"";
     }
 
-    if (m_extra.size())
+    if (!m_extra.empty())
     {
         os << " " << m_extra;
     }
@@ -559,17 +559,17 @@
 {
     os << "\"-\" ";
 
-    if (m_style.size())
+    if (!m_style.empty())
     {
         os << " " << m_style;
     }
 
-    if (m_title.size())
+    if (!m_title.empty())
     {
         os << " title \"" << m_title << "\"";
     }
 
-    if (m_extra.size())
+    if (!m_extra.empty())
     {
         os << " " << m_extra;
     }
@@ -594,7 +594,7 @@
 bool
 Gnuplot3dDataset::Data3d::IsEmpty() const
 {
-    return (m_pointset.size() == 0);
+    return m_pointset.empty();
 }
 
 // --- Gnuplot3dDataset ---------------------------------------------------- //
@@ -621,7 +621,7 @@
 void
 Gnuplot3dDataset::Add(double x, double y, double z)
 {
-    struct Point data;
+    Point data;
     data.empty = false;
     data.x = x;
     data.y = y;
@@ -632,7 +632,7 @@
 void
 Gnuplot3dDataset::AddEmptyLine()
 {
-    struct Point data;
+    Point data;
     data.empty = true;
     reinterpret_cast<Data3d*>(m_data)->m_pointset.push_back(data);
 }
@@ -687,12 +687,12 @@
 {
     os << m_function;
 
-    if (m_title.size())
+    if (!m_title.empty())
     {
         os << " title \"" << m_title << "\"";
     }
 
-    if (m_extra.size())
+    if (!m_extra.empty())
     {
         os << " " << m_extra;
     }
@@ -812,32 +812,32 @@
 void
 Gnuplot::GenerateOutput(std::ostream& osControl, std::ostream& osData, std::string dataFileName)
 {
-    if (m_terminal.size())
+    if (!m_terminal.empty())
     {
         osControl << "set terminal " << m_terminal << std::endl;
     }
 
-    if (m_outputFilename.size())
+    if (!m_outputFilename.empty())
     {
         osControl << "set output \"" << m_outputFilename << "\"" << std::endl;
     }
 
-    if (m_title.size())
+    if (!m_title.empty())
     {
         osControl << "set title \"" << m_title << "\"" << std::endl;
     }
 
-    if (m_xLegend.size())
+    if (!m_xLegend.empty())
     {
         osControl << "set xlabel \"" << m_xLegend << "\"" << std::endl;
     }
 
-    if (m_yLegend.size())
+    if (!m_yLegend.empty())
     {
         osControl << "set ylabel \"" << m_yLegend << "\"" << std::endl;
     }
 
-    if (m_extra.size())
+    if (!m_extra.empty())
     {
         osControl << m_extra << std::endl;
     }
@@ -940,12 +940,12 @@
     // If this version of this function is called, it is assumed that a
     // single output file is being generated.
 
-    if (m_terminal.size())
+    if (!m_terminal.empty())
     {
         os << "set terminal " << m_terminal << std::endl;
     }
 
-    if (m_outputFilename.size())
+    if (!m_outputFilename.empty())
     {
         os << "set output \"" << m_outputFilename << "\"" << std::endl;
     }
@@ -964,12 +964,12 @@
     // If this version of this function is called, it is assumed that
     // separate output and date files are being generated.
 
-    if (m_terminal.size())
+    if (!m_terminal.empty())
     {
         osControl << "set terminal " << m_terminal << std::endl;
     }
 
-    if (m_outputFilename.size())
+    if (!m_outputFilename.empty())
     {
         osControl << "set output \"" << m_outputFilename << "\"" << std::endl;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/gnuplot.h ns-3.38/src/stats/model/gnuplot.h
--- ns-3.37/src/stats/model/gnuplot.h	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/gnuplot.h	2023-03-16 20:54:23.828869000 -0700
@@ -153,18 +153,18 @@
      * Change default style for all newly created objects.
      * \param style the style of plotting to use for newly created datasets.
      */
-    static void SetDefaultStyle(enum Style style);
+    static void SetDefaultStyle(Style style);
 
     /**
      * \param style the style of plotting to use for this dataset.
      */
-    void SetStyle(enum Style style);
+    void SetStyle(Style style);
 
     /**
      * Change default errorbars style for all newly created objects.
      * \param errorBars the style of errorbars to use for newly created datasets.
      */
-    static void SetDefaultErrorBars(enum ErrorBars errorBars);
+    static void SetDefaultErrorBars(ErrorBars errorBars);
 
     /**
      * \param errorBars the style of errorbars to display.
@@ -174,7 +174,7 @@
      * this dataset with the right GnuplotDataset::Add
      * method.
      */
-    void SetErrorBars(enum ErrorBars errorBars);
+    void SetErrorBars(ErrorBars errorBars);
 
     /**
      * \param x x coord to new data point
@@ -225,8 +225,8 @@
     /// The set of points in the dataset
     typedef std::vector<struct Point> PointSet;
 
-    static enum Style m_defaultStyle;         //!< default plot style
-    static enum ErrorBars m_defaultErrorBars; //!< default error bars type
+    static Style m_defaultStyle;         //!< default plot style
+    static ErrorBars m_defaultErrorBars; //!< default error bars type
 
     /// Forward declaration of the internal data class.
     struct Data2d;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/histogram.cc ns-3.38/src/stats/model/histogram.cc
--- ns-3.37/src/stats/model/histogram.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/histogram.cc	2023-03-16 20:54:23.828869000 -0700
@@ -46,13 +46,13 @@
 }
 
 double
-Histogram::GetBinStart(uint32_t index)
+Histogram::GetBinStart(uint32_t index) const
 {
     return index * m_binWidth;
 }
 
 double
-Histogram::GetBinEnd(uint32_t index)
+Histogram::GetBinEnd(uint32_t index) const
 {
     return (index + 1) * m_binWidth;
 }
@@ -66,7 +66,7 @@
 void
 Histogram::SetDefaultBinWidth(double binWidth)
 {
-    NS_ASSERT(m_histogram.size() == 0); // we can only change the bin width if no values were added
+    NS_ASSERT(m_histogram.empty()); // we can only change the bin width if no values were added
     m_binWidth = binWidth;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/histogram.h ns-3.38/src/stats/model/histogram.h
--- ns-3.37/src/stats/model/histogram.h	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/histogram.h	2023-03-16 20:54:23.828869000 -0700
@@ -64,13 +64,13 @@
      * \param index the bin index
      * \return the bin start
      */
-    double GetBinStart(uint32_t index);
+    double GetBinStart(uint32_t index) const;
     /**
      * \brief Returns the bin end, i.e., (index+1)*binWidth
      * \param index the bin index
      * \return the bin start
      */
-    double GetBinEnd(uint32_t index);
+    double GetBinEnd(uint32_t index) const;
     /**
      * \brief Returns the bin width.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/model/omnet-data-output.cc ns-3.38/src/stats/model/omnet-data-output.cc
--- ns-3.37/src/stats/model/omnet-data-output.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/model/omnet-data-output.cc	2023-03-16 20:54:23.828869000 -0700
@@ -167,11 +167,11 @@
 {
     NS_LOG_FUNCTION(this << context << name << statSum);
 
-    if (context == "")
+    if (context.empty())
     {
         context = ".";
     }
-    if (name == "")
+    if (name.empty())
     {
         name = "\"\"";
     }
@@ -213,11 +213,11 @@
 {
     NS_LOG_FUNCTION(this << context << name << val);
 
-    if (context == "")
+    if (context.empty())
     {
         context = ".";
     }
-    if (name == "")
+    if (name.empty())
     {
         name = "\"\"";
     }
@@ -232,11 +232,11 @@
 {
     NS_LOG_FUNCTION(this << context << name << val);
 
-    if (context == "")
+    if (context.empty())
     {
         context = ".";
     }
-    if (name == "")
+    if (name.empty())
     {
         name = "\"\"";
     }
@@ -251,11 +251,11 @@
 {
     NS_LOG_FUNCTION(this << context << name << val);
 
-    if (context == "")
+    if (context.empty())
     {
         context = ".";
     }
-    if (name == "")
+    if (name.empty())
     {
         name = "\"\"";
     }
@@ -270,11 +270,11 @@
 {
     NS_LOG_FUNCTION(this << context << name << val);
 
-    if (context == "")
+    if (context.empty())
     {
         context = ".";
     }
-    if (name == "")
+    if (name.empty())
     {
         name = "\"\"";
     }
@@ -289,11 +289,11 @@
 {
     NS_LOG_FUNCTION(this << context << name << val);
 
-    if (context == "")
+    if (context.empty())
     {
         context = ".";
     }
-    if (name == "")
+    if (name.empty())
     {
         name = "\"\"";
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/stats/test/double-probe-test-suite.cc ns-3.38/src/stats/test/double-probe-test-suite.cc
--- ns-3.37/src/stats/test/double-probe-test-suite.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/stats/test/double-probe-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -51,13 +51,13 @@
     }
 
     /// \return the time delta of the next report.
-    double GetTime()
+    double GetTime() const
     {
         return m_time;
     }
 
     /// \return a random variable, different for each reschedule.
-    double GetValue()
+    double GetValue() const
     {
         return aux;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/doc/tap.rst ns-3.38/src/tap-bridge/doc/tap.rst
--- ns-3.37/src/tap-bridge/doc/tap.rst	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/doc/tap.rst	2023-03-16 20:54:23.832869300 -0700
@@ -233,7 +233,7 @@
 not be changeable to suit ns-3.  For example, a particular VM scheme may create
 virtual "vethx" or "vmnetx" devices that appear local to virtual hosts.  In
 order to connect to such systems, one would need to manually create TAP devices
-on the host system and brigde these TAP devices to the existing (VM) virtual
+on the host system and bridge these TAP devices to the existing (VM) virtual
 devices.  The job of the Tap Bridge in this case is to extend the bridge to
 join a ns-3 net device.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/lxc-left.conf ns-3.38/src/tap-bridge/examples/lxc-left.conf
--- ns-3.37/src/tap-bridge/examples/lxc-left.conf	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/lxc-left.conf	2023-03-16 20:54:23.832869300 -0700
@@ -1,7 +1,7 @@
 # Container with network virtualized using a pre-configured bridge named br-left and
 # veth pair virtual network devices
-lxc.utsname = left
-lxc.network.type = veth
-lxc.network.flags = up
-lxc.network.link = br-left
-lxc.network.ipv4 = 10.0.0.1/24
+lxc.uts.name = left
+lxc.net.0.type = veth
+lxc.net.0.flags = up
+lxc.net.0.link = br-left
+lxc.net.0.ipv4.address = 10.0.0.1/24
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/lxc-right.conf ns-3.38/src/tap-bridge/examples/lxc-right.conf
--- ns-3.37/src/tap-bridge/examples/lxc-right.conf	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/lxc-right.conf	2023-03-16 20:54:23.832869300 -0700
@@ -1,7 +1,7 @@
 # Container with network virtualized using a pre-configured bridge named br-right and
 # veth pair virtual network devices
-lxc.utsname = right
-lxc.network.type = veth
-lxc.network.flags = up
-lxc.network.link = br-right
-lxc.network.ipv4 = 10.0.0.2/24
+lxc.uts.name = right
+lxc.net.0.type = veth
+lxc.net.0.flags = up
+lxc.net.0.link = br-right
+lxc.net.0.ipv4.address = 10.0.0.2/24
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/tap-csma.cc ns-3.38/src/tap-bridge/examples/tap-csma.cc
--- ns-3.37/src/tap-bridge/examples/tap-csma.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/tap-csma.cc	2023-03-16 20:54:23.832869300 -0700
@@ -109,4 +109,6 @@
     Simulator::Stop(Seconds(60.));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/tap-csma-virtual-machine.cc ns-3.38/src/tap-bridge/examples/tap-csma-virtual-machine.cc
--- ns-3.37/src/tap-bridge/examples/tap-csma-virtual-machine.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/tap-csma-virtual-machine.cc	2023-03-16 20:54:23.832869300 -0700
@@ -21,7 +21,7 @@
 // The actual steps required to configure the virtual machines can be rather
 // involved, so we don't go into that here.  Please have a look at one of
 // our HOWTOs on the nsnam wiki for more details about how to get the
-// system confgured.  For an example, have a look at "HOWTO Use Linux
+// system configured.  For an example, have a look at "HOWTO Use Linux
 // Containers to set up virtual networks" which uses this code as an
 // example.
 //
@@ -126,4 +126,6 @@
     Simulator::Stop(Seconds(600.));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/tap-wifi-dumbbell.cc ns-3.38/src/tap-bridge/examples/tap-wifi-dumbbell.cc
--- ns-3.37/src/tap-bridge/examples/tap-wifi-dumbbell.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/tap-wifi-dumbbell.cc	2023-03-16 20:54:23.832869300 -0700
@@ -228,4 +228,6 @@
     Simulator::Stop(Seconds(60.));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/tap-wifi-virtual-machine.cc ns-3.38/src/tap-bridge/examples/tap-wifi-virtual-machine.cc
--- ns-3.37/src/tap-bridge/examples/tap-wifi-virtual-machine.cc	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/tap-wifi-virtual-machine.cc	2023-03-16 20:54:23.832869300 -0700
@@ -21,7 +21,7 @@
 // The actual steps required to configure the virtual machines can be rather
 // involved, so we don't go into that here.  Please have a look at one of
 // our HOWTOs on the nsnam wiki for more details about how to get the
-// system confgured.  For an example, have a look at "HOWTO Use Linux
+// system configured.  For an example, have a look at "HOWTO Use Linux
 // Containers to set up virtual networks" which uses this code as an
 // example.
 //
@@ -165,4 +165,6 @@
     Simulator::Stop(Seconds(600.));
     Simulator::Run();
     Simulator::Destroy();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/virtual-network-setup.sh ns-3.38/src/tap-bridge/examples/virtual-network-setup.sh
--- ns-3.37/src/tap-bridge/examples/virtual-network-setup.sh	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/virtual-network-setup.sh	2023-03-16 20:54:23.832869300 -0700
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 brctl addbr br-left
 brctl addbr br-right
 tunctl -t tap-left
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/examples/virtual-network-teardown.sh ns-3.38/src/tap-bridge/examples/virtual-network-teardown.sh
--- ns-3.37/src/tap-bridge/examples/virtual-network-teardown.sh	2022-11-01 17:37:24.715395700 -0700
+++ ns-3.38/src/tap-bridge/examples/virtual-network-teardown.sh	2023-03-16 20:54:23.832869300 -0700
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 lxc-stop -n left
 lxc-stop -n right
 lxc-destroy -n left
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/model/tap-bridge.cc ns-3.38/src/tap-bridge/model/tap-bridge.cc
--- ns-3.37/src/tap-bridge/model/tap-bridge.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/tap-bridge/model/tap-bridge.cc	2023-03-16 20:54:23.832869300 -0700
@@ -292,7 +292,7 @@
     // In ConfigureLocal mode, the user is asking the TapBridge to do the
     // configuration and create a TAP with the provided "DeviceName" with which
     // the user can later do what she wants.  We need to extract values for the
-    // MAC address, IP address, net mask, etc, from the simualtion itself and
+    // MAC address, IP address, net mask, etc, from the simulation itself and
     // use them to initialize corresponding values on the created tap device.
     //
     // In UseBridge mode, the user is asking us to use an existing tap device
@@ -1092,7 +1092,7 @@
 }
 
 void
-TapBridge::SetMode(enum Mode mode)
+TapBridge::SetMode(Mode mode)
 {
     NS_LOG_FUNCTION(this << mode);
     m_mode = mode;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/tap-bridge/model/tap-creator.cc ns-3.38/src/tap-bridge/model/tap-creator.cc
--- ns-3.37/src/tap-bridge/model/tap-creator.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/tap-bridge/model/tap-creator.cc	2023-03-16 20:54:23.832869300 -0700
@@ -38,7 +38,7 @@
 
 #define TAP_MAGIC 95549
 
-static int gVerbose = 0; // Set to true to turn on logging messages.
+static bool gVerbose = false; // Set to true to turn on logging messages.
 
 #define LOG(msg)                                                                                   \
     if (gVerbose)                                                                                  \
@@ -474,7 +474,7 @@
     // in order to keep the main simulation program from having to be run with
     // root privileges.  We need root privileges to be able to futz with the
     // Tap device underlying all of this.  So all of these hoops are to allow
-    // us to exeucte the following code:
+    // us to execute the following code:
     //
     LOG("Creating Tap");
     int sock = CreateTap(dev, gw, ip, mac, operatingMode, netmask);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/csma-system-test-suite.cc ns-3.38/src/test/csma-system-test-suite.cc
--- ns-3.37/src/test/csma-system-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/csma-system-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -38,13 +38,13 @@
 #include "ns3/packet-socket-address.h"
 #include "ns3/packet-socket-helper.h"
 #include "ns3/packet.h"
+#include "ns3/ping-helper.h"
 #include "ns3/pointer.h"
 #include "ns3/simple-channel.h"
 #include "ns3/simulator.h"
 #include "ns3/string.h"
 #include "ns3/test.h"
 #include "ns3/uinteger.h"
-#include "ns3/v4ping-helper.h"
 
 #include <string>
 
@@ -784,23 +784,18 @@
     void DoRun() override;
     /**
      * Sink called when a packet is received by a node.
-     * \param p Received packet (unused).
-     * \param ad Sender's address (uused).
      */
-    void SinkRx(Ptr<const Packet> p, const Address& ad);
+    void SinkRx(Ptr<const Packet>, const Address&);
 
     /**
-     * Sink called when a PING ois received.
-     * \param context Context path (unused).
-     * \param rtt Round Trip Time (unused).
+     * Sink called when a PING is received.
      */
-    void PingRtt(std::string context, Time rtt);
+    void PingRtt(std::string, uint16_t, Time);
 
     /**
      * Sink called when a packet is dropped.
-     * \param p Received packet (unused).
      */
-    void DropEvent(Ptr<const Packet> p);
+    void DropEvent(Ptr<const Packet>);
 
     uint32_t m_countSinkRx;  //!< Counter of received packets.
     uint32_t m_countPingRtt; //!< Counter of PING received.
@@ -821,19 +816,19 @@
 }
 
 void
-CsmaPingTestCase::SinkRx(Ptr<const Packet> p, const Address& ad)
+CsmaPingTestCase::SinkRx(Ptr<const Packet>, const Address&)
 {
     m_countSinkRx++;
 }
 
 void
-CsmaPingTestCase::PingRtt(std::string context, Time rtt)
+CsmaPingTestCase::PingRtt(std::string, uint16_t, Time)
 {
     m_countPingRtt++;
 }
 
 void
-CsmaPingTestCase::DropEvent(Ptr<const Packet> p)
+CsmaPingTestCase::DropEvent(Ptr<const Packet>)
 {
     m_drops++;
 }
@@ -888,7 +883,7 @@
     apps.Start(Seconds(0.0));
     apps.Stop(Seconds(11.0));
 
-    V4PingHelper ping = V4PingHelper(addresses.GetAddress(2));
+    PingHelper ping(addresses.GetAddress(2));
     NodeContainer pingers;
     pingers.Add(c.Get(0));
     pingers.Add(c.Get(1));
@@ -902,7 +897,7 @@
                                   MakeCallback(&CsmaPingTestCase::SinkRx, this));
 
     // Trace pings
-    Config::Connect("/NodeList/*/ApplicationList/*/$ns3::V4Ping/Rtt",
+    Config::Connect("/NodeList/*/ApplicationList/*/$ns3::Ping/Rtt",
                     MakeCallback(&CsmaPingTestCase::PingRtt, this));
 
     Simulator::Run();
@@ -1151,7 +1146,7 @@
     // assign addresses to them as well.  We put all of the fill devices into
     // a single device container, so the first nFill devices are associated
     // with the channel connected to spokeDevices.Get (0), the second nFill
-    // devices afe associated with the channel connected to spokeDevices.Get (1)
+    // devices are associated with the channel connected to spokeDevices.Get (1)
     // etc.
     //
     Ipv4AddressHelper address;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/doc/tests.h ns-3.38/src/test/doc/tests.h
--- ns-3.37/src/test/doc/tests.h	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/doc/tests.h	2023-03-16 20:54:23.832869300 -0700
@@ -61,7 +61,7 @@
  * \brief Performance System Tests
  *
  * Note: these tests are (for real) scripts to be run.
- * The ouput is the time (in nanoseconds) that the test
+ * The output is the time (in nanoseconds) that the test
  * required to perform the requested write operations.
  */
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc ns-3.38/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc
--- ns-3.37/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -1626,7 +1626,7 @@
                           "Cobalt keeps drop_next as now and the next packet is marked. With "
                           "second dequeue count increases to 2, drop_next becomes now plus around"
                           "70ms which is less than the running time(140), and as the queue delay "
-                          "is persistantly higher than 5ms, second packet is marked.");
+                          "is persistently higher than 5ms, second packet is marked.");
     NS_TEST_EXPECT_MSG_EQ(q1->GetStats().GetNDroppedPackets(CobaltQueueDisc::TARGET_EXCEEDED_DROP),
                           0,
                           "There should not be any dropped packets");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/ns3tcp/ns3tcp-loss-test-suite.cc ns-3.38/src/test/ns3tcp/ns3tcp-loss-test-suite.cc
--- ns-3.37/src/test/ns3tcp/ns3tcp-loss-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/ns3tcp/ns3tcp-loss-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -35,7 +35,7 @@
 #include "ns3/string.h"
 #include "ns3/tcp-header.h"
 #include "ns3/tcp-socket-factory.h"
-#include "ns3/tcp-westwood.h"
+#include "ns3/tcp-westwood-plus.h"
 #include "ns3/test.h"
 #include "ns3/uinteger.h"
 
@@ -101,7 +101,7 @@
     std::string m_tcpModel;         //!< The TCP model name.
 
     /**
-     * Check that the transmitted packets are consitent with the trace.
+     * Check that the transmitted packets are consistent with the trace.
      * This callback is hooked to ns3::Ipv4L3Protocol/Tx.
      *
      * \param context The callback context (unused).
@@ -146,7 +146,7 @@
       m_writeResults(WRITE_PCAP),
       m_writeLogging(WRITE_LOGGING),
       m_needToClose(true),
-      m_tcpModel("ns3::TcpWestwood")
+      m_tcpModel("ns3::TcpWestwoodPlus")
 {
 }
 
@@ -351,15 +351,7 @@
 
     std::ostringstream tcpModel;
     tcpModel << "ns3::Tcp" << m_tcpModel;
-    if (m_tcpModel == "WestwoodPlus")
-    {
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpWestwood::GetTypeId()));
-        Config::SetDefault("ns3::TcpWestwood::ProtocolType", EnumValue(TcpWestwood::WESTWOODPLUS));
-    }
-    else
-    {
-        Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue(tcpModel.str()));
-    }
+    Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue(tcpModel.str()));
 
     Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(1000));
     Config::SetDefault("ns3::TcpSocket::DelAckCount", UintegerValue(1));
@@ -370,7 +362,7 @@
         LogComponentEnableAll(LOG_PREFIX_FUNC);
         LogComponentEnable("Ns3TcpLossTest", LOG_LEVEL_ALL);
         LogComponentEnable("ErrorModel", LOG_LEVEL_DEBUG);
-        LogComponentEnable("TcpWestwood", LOG_LEVEL_ALL);
+        LogComponentEnable("TcpWestwoodPlus", LOG_LEVEL_ALL);
         LogComponentEnable("TcpCongestionOps", LOG_LEVEL_INFO);
         LogComponentEnable("TcpSocketBase", LOG_LEVEL_INFO);
     }
@@ -530,12 +522,6 @@
     AddTestCase(new Ns3TcpLossTestCase("NewReno", 3), TestCase::QUICK);
     AddTestCase(new Ns3TcpLossTestCase("NewReno", 4), TestCase::QUICK);
 
-    AddTestCase(new Ns3TcpLossTestCase("Westwood", 0), TestCase::QUICK);
-    AddTestCase(new Ns3TcpLossTestCase("Westwood", 1), TestCase::QUICK);
-    AddTestCase(new Ns3TcpLossTestCase("Westwood", 2), TestCase::QUICK);
-    AddTestCase(new Ns3TcpLossTestCase("Westwood", 3), TestCase::QUICK);
-    AddTestCase(new Ns3TcpLossTestCase("Westwood", 4), TestCase::QUICK);
-
     AddTestCase(new Ns3TcpLossTestCase("WestwoodPlus", 0), TestCase::QUICK);
     AddTestCase(new Ns3TcpLossTestCase("WestwoodPlus", 1), TestCase::QUICK);
     AddTestCase(new Ns3TcpLossTestCase("WestwoodPlus", 2), TestCase::QUICK);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/ns3tcp/ns3tcp-socket-writer.h ns-3.38/src/test/ns3tcp/ns3tcp-socket-writer.h
--- ns-3.37/src/test/ns3tcp/ns3tcp-socket-writer.h	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/ns3tcp/ns3tcp-socket-writer.h	2023-03-16 20:54:23.832869300 -0700
@@ -43,7 +43,7 @@
     /**
      * Setup the socket.
      * \param node The node owning the socket.
-     * \param peer The destinaiton address.
+     * \param peer The destination address.
      */
     void Setup(Ptr<Node> node, Address peer);
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/ns3tcp/ns3tcp-state-test-suite.cc ns-3.38/src/test/ns3tcp/ns3tcp-state-test-suite.cc
--- ns-3.37/src/test/ns3tcp/ns3tcp-state-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/ns3tcp/ns3tcp-state-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -98,7 +98,7 @@
     bool m_needToClose;         //!< Check if the sending socket need to be closed.
 
     /**
-     * Check that the transmitted packets are consitent with the trace.
+     * Check that the transmitted packets are consistent with the trace.
      * This callback is hooked to ns3::Ipv4L3Protocol/Tx.
      *
      * \param context The callback context (unused).
@@ -111,7 +111,7 @@
                   Ptr<Ipv4> ipv4,
                   uint32_t interface);
     /**
-     * Check that the received packets are consitent with the trace.
+     * Check that the received packets are consistent with the trace.
      * This callback is hooked to ns3::Ipv4L3Protocol/Tx.
      *
      * \param context The callback context (unused).
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc ns-3.38/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc
--- ns-3.37/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -227,9 +227,12 @@
     // Get the four child queue discs and connect their Enqueue trace to the
     // PacketEnqueuedInQueueDisc method, which counts how many packets with the given ToS value have
     // been enqueued
+    // NOTE the purpose of the unary + operation in +m_QueueDiscCount is to decay the array type
+    // to a pointer type, so that the type of that argument matches the type of the second
+    // parameter of the PacketEnqueuedInQueueDisc function
     root->GetQueueDiscClass(0)->GetQueueDisc()->TraceConnectWithoutContext(
         "Enqueue",
-        MakeBoundCallback(&WifiAcMappingTest::PacketEnqueuedInQueueDisc, m_tos, m_QueueDiscCount));
+        MakeBoundCallback(&WifiAcMappingTest::PacketEnqueuedInQueueDisc, m_tos, +m_QueueDiscCount));
 
     root->GetQueueDiscClass(1)->GetQueueDisc()->TraceConnectWithoutContext(
         "Enqueue",
@@ -254,12 +257,15 @@
     // Get the four wifi mac queues and connect their Enqueue trace to the
     // PacketEnqueuedInWifiMacQueue method, which counts how many packets with the given ToS value
     // have been enqueued
+    // NOTE the purpose of the unary + operation in +m_WifiMacQueueCount is to decay the array type
+    // to a pointer type, so that the type of that argument matches the type of the second
+    // parameter of the PacketEnqueuedInWifiMacQueue function
     apMac->GetAttribute("BE_Txop", ptr);
     ptr.Get<QosTxop>()->GetWifiMacQueue()->TraceConnectWithoutContext(
         "Enqueue",
         MakeBoundCallback(&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue,
                           m_tos,
-                          m_WifiMacQueueCount));
+                          +m_WifiMacQueueCount));
 
     apMac->GetAttribute("BK_Txop", ptr);
     ptr.Get<QosTxop>()->GetWifiMacQueue()->TraceConnectWithoutContext(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/traced/traced-callback-typedef-test-suite.cc ns-3.38/src/test/traced/traced-callback-typedef-test-suite.cc
--- ns-3.37/src/test/traced/traced-callback-typedef-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/traced/traced-callback-typedef-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -143,7 +143,7 @@
 /**
  * \ingroup system-tests-traced
  *
- * Returns a sting representing the type of a class.
+ * Returns a string representing the type of a class.
  */
 #define TYPENAME(T)                                                                                \
     template <>                                                                                    \
@@ -249,7 +249,7 @@
   public:
     /**
      * \brief Sink function, called by a TracedCallback.
-     * \tparam Ts prameters of the TracedCallback.
+     * \tparam Ts parameters of the TracedCallback.
      */
     static void Sink(Ts...)
     {
@@ -325,7 +325,7 @@
 /**
  * \ingroup system-tests-traced
  *
- * Check the TracedCallback duplicate by checking if it maches the TracedCallback
+ * Check the TracedCallback duplicate by checking if it matches the TracedCallback
  * it is supposed to be equal to.
  */
 #define DUPE(U, T1)                                                                                \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/test/traced/traced-value-callback-typedef-test-suite.cc ns-3.38/src/test/traced/traced-value-callback-typedef-test-suite.cc
--- ns-3.37/src/test/traced/traced-value-callback-typedef-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/test/traced/traced-value-callback-typedef-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
@@ -81,7 +81,7 @@
 
     if (newValue != 1)
     {
-        g_Result += std::string(g_Result == "" ? "" : " | ") + "newValue should be 1";
+        g_Result += std::string(g_Result.empty() ? "" : " | ") + "newValue should be 1";
     }
 } // TracedValueCbSink<>()
 
@@ -217,7 +217,7 @@
         U sink = TracedValueCbSink<T>;
         CreateObject<CheckTvCb<T>>()->Invoke(sink);
 
-        NS_TEST_ASSERT_MSG_EQ(g_Result, "", g_Result);
+        NS_TEST_ASSERT_MSG_EQ(g_Result.empty(), true, g_Result);
         g_Result = "";
 
     } // CheckType<>()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/topology-read/doc/topology.rst ns-3.38/src/topology-read/doc/topology.rst
--- ns-3.37/src/topology-read/doc/topology.rst	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/topology-read/doc/topology.rst	2023-03-16 20:54:23.832869300 -0700
@@ -20,6 +20,22 @@
 
 An helper ``ns3::TopologyReaderHelper`` is provided to assist on trivial tasks.
 
+In some cases it might not be simple to identify which |ns3| node corresponds to a given node in the
+topology file. To simplify this task, each node created by ``ns3::TopologyReaderHelper`` has a name.
+The name format is "<TopologyReader>/NodeName/<label>" where "<TopologyReader>" is either
+"InetTopology", "OrbisTopology", or "RocketFuelTopology", and "<label>" is the identifier of the
+node in the topology file (can be either a number or a string, depending on the file type).
+Assuming that there are 10 nodes, labeled with number starting from 0, the code could be::
+
+    for (uint32_t nodeNumber = 0; nodeNumber < 10; nodeNumber++)
+    {
+        Ptr<Node> node = Names::Find<Node>("InetTopology/NodeName/" + std::to_string(nodeNumber));
+        if (node)
+        {
+            // Do something
+        }
+    }
+
 A good source for topology data is also Archipelago_.
 
 The current Archipelago Measurements_, monthly updated, are stored in the CAIDA website using
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/topology-read/examples/topology-example-sim.cc ns-3.38/src/topology-read/examples/topology-example-sim.cc
--- ns-3.37/src/topology-read/examples/topology-example-sim.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/topology-read/examples/topology-example-sim.cc	2023-03-16 20:54:23.836869200 -0700
@@ -34,31 +34,17 @@
 #include <sstream>
 
 /**
- * \file
  * \ingroup topology
- * Example of TopologyReader: .read in a topology in a specificed format.
- */
-
-//  Document the available input files
-/**
- * \file RocketFuel_toposample_1239_weights.txt
- * Example TopologyReader input file in RocketFuel format;
- * to read this with topology-example-sim.cc use \c --format=Rocket
- */
-/**
- * \file Inet_toposample.txt
- * Example TopologyReader input file in Inet format;
- * to read this with topology-example-sim.cc use \c --format=Inet
- */
-/**
- * \file Inet_small_toposample.txt
- * Example TopologyReader input file in Inet format;
- * to read this with topology-example-sim.cc use \c --format=Inet
- */
-/**
- * \file Orbis_toposample.txt
- * Example TopologyReader input file in Orbis format;
- * to read this with topology-example-sim.cc use \c --format=Orbis
+ * Example of TopologyReader: read in a topology in a specified format.
+ *
+ * This example can be used with the following parameters:
+ *   - <tt>--format=Inet --input=src/topology-read/examples/Inet_small_toposample.txt</tt>
+ *   - <tt>--format=Inet --input=src/topology-read/examples/Inet_toposample.txt</tt>
+ *   - <tt>--format=Orbis --input=src/topology-read/examples/Orbis_toposample.txt</tt>
+ *   - <tt>--format=Rocket
+ *     --input=src/topology-read/examples/RocketFuel_sample_4755.r0.cch_maps.txt</tt>
+ *   - <tt>--format=Rocket
+ *     --input=src/topology-read/examples/RocketFuel_toposample_1239_weights.txt</tt>
  */
 
 using namespace ns3;
@@ -87,6 +73,8 @@
     std::string format("Inet");
     std::string input("src/topology-read/examples/Inet_small_toposample.txt");
 
+    LogComponentEnable("TopologyCreationExperiment", LOG_LEVEL_INFO);
+
     // Set up command line parameters used to control the experiment.
     CommandLine cmd(__FILE__);
     cmd.AddValue("format", "Format to use for data input [Orbis|Inet|Rocketfuel].", format);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/topology-read/model/inet-topology-reader.cc ns-3.38/src/topology-read/model/inet-topology-reader.cc
--- ns-3.37/src/topology-read/model/inet-topology-reader.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/topology-read/model/inet-topology-reader.cc	2023-03-16 20:54:23.836869200 -0700
@@ -21,6 +21,7 @@
 #include "inet-topology-reader.h"
 
 #include "ns3/log.h"
+#include "ns3/names.h"
 #include "ns3/node-container.h"
 
 #include <cstdlib>
@@ -35,7 +36,6 @@
 
 namespace ns3
 {
-
 NS_LOG_COMPONENT_DEFINE("InetTopologyReader");
 
 NS_OBJECT_ENSURE_REGISTERED(InetTopologyReader);
@@ -117,6 +117,8 @@
             {
                 NS_LOG_INFO("Node " << nodesNumber << " name: " << from);
                 Ptr<Node> tmpNode = CreateObject<Node>();
+                std::string nodeName = "InetTopology/NodeName/" + from;
+                Names::Add(from, tmpNode);
                 nodeMap[from] = tmpNode;
                 nodes.Add(tmpNode);
                 nodesNumber++;
@@ -126,6 +128,8 @@
             {
                 NS_LOG_INFO("Node " << nodesNumber << " name: " << to);
                 Ptr<Node> tmpNode = CreateObject<Node>();
+                std::string nodename = "InetTopology/NodeName/" + to;
+                Names::Add(nodename, tmpNode);
                 nodeMap[to] = tmpNode;
                 nodes.Add(tmpNode);
                 nodesNumber++;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/topology-read/model/orbis-topology-reader.cc ns-3.38/src/topology-read/model/orbis-topology-reader.cc
--- ns-3.37/src/topology-read/model/orbis-topology-reader.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/topology-read/model/orbis-topology-reader.cc	2023-03-16 20:54:23.836869200 -0700
@@ -21,6 +21,7 @@
 #include "orbis-topology-reader.h"
 
 #include "ns3/log.h"
+#include "ns3/names.h"
 #include "ns3/node-container.h"
 
 #include <cstdlib>
@@ -99,6 +100,8 @@
             if (!nodeMap[from])
             {
                 Ptr<Node> tmpNode = CreateObject<Node>();
+                std::string nodename = "OrbisTopology/NodeName/" + from;
+                Names::Add(nodename, tmpNode);
                 nodeMap[from] = tmpNode;
                 nodes.Add(tmpNode);
                 nodesNumber++;
@@ -107,6 +110,8 @@
             if (!nodeMap[to])
             {
                 Ptr<Node> tmpNode = CreateObject<Node>();
+                std::string nodename = "OrbisTopology/NodeName/" + to;
+                Names::Add(nodename, tmpNode);
                 nodeMap[to] = tmpNode;
                 nodes.Add(tmpNode);
                 nodesNumber++;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/topology-read/model/rocketfuel-topology-reader.cc ns-3.38/src/topology-read/model/rocketfuel-topology-reader.cc
--- ns-3.37/src/topology-read/model/rocketfuel-topology-reader.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/topology-read/model/rocketfuel-topology-reader.cc	2023-03-16 20:54:23.836869200 -0700
@@ -20,6 +20,7 @@
 #include "rocketfuel-topology-reader.h"
 
 #include "ns3/log.h"
+#include "ns3/names.h"
 #include "ns3/node-container.h"
 
 #include <cstdlib>
@@ -87,13 +88,13 @@
  * Build a Regex object for RocketFuel topology maps file type
  * \return a static regex object for maps file type
  */
-static const std::regex rocketfuel_maps_regex(ROCKETFUEL_MAPS_LINE, std::regex::extended);
+static const std::regex rocketfuel_maps_regex(ROCKETFUEL_MAPS_LINE);
 
 /**
  * Build a Regex object for RocketFuel topology weights file type
  * \return a static regex object for weights file type
  */
-static const std::regex rocketfuel_weights_regex(ROCKETFUEL_WEIGHTS_LINE, std::regex::extended);
+static const std::regex rocketfuel_weights_regex(ROCKETFUEL_WEIGHTS_LINE);
 
 /**
  * \brief Print node info
@@ -212,6 +213,8 @@
         if (!m_nodeMap[uid])
         {
             Ptr<Node> tmpNode = CreateObject<Node>();
+            std::string nodename = "RocketFuelTopology/NodeName/" + uid;
+            Names::Add(nodename, tmpNode);
             m_nodeMap[uid] = tmpNode;
             nodes.Add(tmpNode);
             m_nodesNumber++;
@@ -227,6 +230,8 @@
             if (!m_nodeMap[nuid])
             {
                 Ptr<Node> tmpNode = CreateObject<Node>();
+                std::string nodename = "RocketFuelTopology/NodeName/" + nuid;
+                Names::Add(nodename, tmpNode);
                 m_nodeMap[nuid] = tmpNode;
                 nodes.Add(tmpNode);
                 m_nodesNumber++;
@@ -270,6 +275,8 @@
         if (!m_nodeMap[sname])
         {
             Ptr<Node> tmpNode = CreateObject<Node>();
+            std::string nodename = "RocketFuelTopology/NodeName/" + sname;
+            Names::Add(nodename, tmpNode);
             m_nodeMap[sname] = tmpNode;
             nodes.Add(tmpNode);
             m_nodesNumber++;
@@ -278,6 +285,8 @@
         if (!m_nodeMap[tname])
         {
             Ptr<Node> tmpNode = CreateObject<Node>();
+            std::string nodename = "RocketFuelTopology/NodeName/" + tname;
+            Names::Add(nodename, tmpNode);
             m_nodeMap[tname] = tmpNode;
             nodes.Add(tmpNode);
             m_nodesNumber++;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/topology-read/test/rocketfuel-topology-reader-test-suite.cc ns-3.38/src/topology-read/test/rocketfuel-topology-reader-test-suite.cc
--- ns-3.37/src/topology-read/test/rocketfuel-topology-reader-test-suite.cc	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/topology-read/test/rocketfuel-topology-reader-test-suite.cc	2023-03-16 20:54:23.836869200 -0700
@@ -33,8 +33,9 @@
 using namespace ns3;
 
 /**
- * \ingroup topology
  * \defgroup topology-test Topology module tests
+ * \ingroup topology
+ * \ingroup tests
  */
 
 /**
@@ -45,7 +46,6 @@
 
 /**
  * \ingroup topology-test
- * \ingroup tests
 
  * \brief Rocketfuel Topology Reader Weights Test
  */
@@ -90,8 +90,6 @@
 
 /**
  * \ingroup topology-test
- * \ingroup tests
-
  * \brief Rocketfuel Topology Reader Maps Test
  */
 class RocketfuelTopologyReaderMapsTest : public TestCase
@@ -135,7 +133,6 @@
 
 /**
  * \ingroup topology-test
- * \ingroup tests
  *
  * \brief Rocketfuel Topology Reader TestSuite
  */
@@ -154,5 +151,8 @@
     AddTestCase(new RocketfuelTopologyReaderMapsTest(), TestCase::QUICK);
 }
 
-static RocketfuelTopologyReaderTestSuite
-    g_rocketfuelTopologyReaderTestSuite; //!< Static variable for test initialization
+/**
+ * \ingroup topology-test
+ * Static variable for test initialization
+ */
+static RocketfuelTopologyReaderTestSuite g_rocketfuelTopologyReaderTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/cobalt.rst ns-3.38/src/traffic-control/doc/cobalt.rst
--- ns-3.37/src/traffic-control/doc/cobalt.rst	2022-11-01 17:37:24.719395600 -0700
+++ ns-3.38/src/traffic-control/doc/cobalt.rst	2023-03-16 20:54:23.836869200 -0700
@@ -51,7 +51,7 @@
   parameters (Pdrop of BLUE and dropping state of CoDel). If either of them
   decide to drop the packet, the packet is dropped.
 * ``CobaltQueueDisc::DoDequeue ()``: This routine performs the actual packet
-  ``drop based on ``CobaltQueueDisc::ShouldDrop ()``'s return value and
+  ``drop based on ``CobaltQueueDisc::ShouldDrop()``'s return value and
   schedules the next drop. Cobalt will decrease BLUE's drop probability
   if the queue is empty. This will ensure that the queue does not underflow.
   Otherwise Cobalt will take the next packet from the queue and calculate
@@ -122,7 +122,7 @@
 * Test 1: Simple enqueue/dequeue with no drops.
 * Test 2: Change of BLUE's drop probability upon queue full
   (Activation of Blue).
-* Test 3: This test verfies ECN marking.
+* Test 3: This test verifies ECN marking.
 * Test 4: CE threshold marking test.
 
 The test suite can be run using the following commands:
@@ -138,4 +138,3 @@
 ::
 
   $ NS_LOG="CobaltQueueDisc" ./ns3 run "test-runner --suite=cobalt-queue-disc"
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/codel.rst ns-3.38/src/traffic-control/doc/codel.rst
--- ns-3.37/src/traffic-control/doc/codel.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/codel.rst	2023-03-16 20:54:23.836869200 -0700
@@ -28,22 +28,22 @@
 
 * class :cpp:class:`CoDelQueueDisc`: This class implements the main CoDel algorithm:
 
-  * ``CoDelQueueDisc::DoEnqueue ()``: This routine tags a packet with the current time before pushing it into the queue.  The timestamp tag is used by ``CoDelQueue::DoDequeue()`` to compute the packet's sojourn time.  If the queue is full upon the packet arrival, this routine will drop the packet and record the number of drops due to queue overflow, which is stored in `m_dropOverLimit`.
+  * ``CoDelQueueDisc::DoEnqueue()``: This routine tags a packet with the current time before pushing it into the queue.  The timestamp tag is used by ``CoDelQueue::DoDequeue()`` to compute the packet's sojourn time.  If the queue is full upon the packet arrival, this routine will drop the packet and record the number of drops due to queue overflow, which is stored in `m_dropOverLimit`.
 
-  * ``CoDelQueueDisc::ShouldDrop ()``: This routine is ``CoDelQueueDisc::DoDequeue()``'s helper routine that determines whether a packet should be dropped or not based on its sojourn time.  If the sojourn time goes above `m_target` and remains above continuously for at least `m_interval`, the routine returns ``true`` indicating that it is OK to drop the packet. Otherwise, it returns ``false``.
+  * ``CoDelQueueDisc::ShouldDrop()``: This routine is ``CoDelQueueDisc::DoDequeue()``'s helper routine that determines whether a packet should be dropped or not based on its sojourn time.  If the sojourn time goes above `m_target` and remains above continuously for at least `m_interval`, the routine returns ``true`` indicating that it is OK to drop the packet. Otherwise, it returns ``false``.
 
-  * ``CoDelQueueDisc::DoDequeue ()``: This routine performs the actual packet drop based on ``CoDelQueueDisc::ShouldDrop ()``'s return value and schedules the next drop/mark.
+  * ``CoDelQueueDisc::DoDequeue()``: This routine performs the actual packet drop based on ``CoDelQueueDisc::ShouldDrop()``'s return value and schedules the next drop/mark.
 * class :cpp:class:`CoDelTimestampTag`: This class implements the timestamp tagging for a packet.  This tag is used to compute the packet's sojourn time (the difference between the time the packet is dequeued and the time it is pushed into the queue).
 
-There are 2 branches to ``CoDelQueueDisc::DoDequeue ()``:
+There are 2 branches to ``CoDelQueueDisc::DoDequeue()``:
 
-1. If the queue is currently in the dropping state, which means the sojourn time has remained above `m_target` for more than `m_interval`, the routine determines if it's OK to leave the dropping state or it's time for the next drop/mark. When ``CoDelQueueDisc::ShouldDrop ()`` returns ``false``, the queue can move out of the dropping state (set `m_dropping` to ``false``).  Otherwise, the queue continuously drops/marks packets and updates the time for next drop (`m_dropNext`) until one of the following conditions is met:
+1. If the queue is currently in the dropping state, which means the sojourn time has remained above `m_target` for more than `m_interval`, the routine determines if it's OK to leave the dropping state or it's time for the next drop/mark. When ``CoDelQueueDisc::ShouldDrop()`` returns ``false``, the queue can move out of the dropping state (set `m_dropping` to ``false``).  Otherwise, the queue continuously drops/marks packets and updates the time for next drop (`m_dropNext`) until one of the following conditions is met:
 
-    1. The queue is empty, upon which the queue leaves the dropping state and exits ``CoDelQueueDisc::ShouldDrop ()`` routine;
-    2. ``CoDelQueueDisc::ShouldDrop ()`` returns ``false`` (meaning the sojourn time goes below `m_target`) upon which the queue leaves the dropping state;
+    1. The queue is empty, upon which the queue leaves the dropping state and exits ``CoDelQueueDisc::ShouldDrop()`` routine;
+    2. ``CoDelQueueDisc::ShouldDrop()`` returns ``false`` (meaning the sojourn time goes below `m_target`) upon which the queue leaves the dropping state;
     3. It is not yet time for next drop/mark (`m_dropNext` is less than current time) upon which the queue waits for the next packet dequeue to check the condition again.
 
-2. If the queue is not in the dropping state, the routine enters the dropping state and drop/mark the first packet if ``CoDelQueueDisc::ShouldDrop ()`` returns ``true`` (meaning the sojourn time has gone above `m_target` for at least `m_interval` for the first time or it has gone above again after the queue leaves the dropping state).
+2. If the queue is not in the dropping state, the routine enters the dropping state and drop/mark the first packet if ``CoDelQueueDisc::ShouldDrop()`` returns ``true`` (meaning the sojourn time has gone above `m_target` for at least `m_interval` for the first time or it has gone above again after the queue leaves the dropping state).
 
 The CoDel queue disc does not require packet filters, does not admit
 child queue discs and uses a single internal queue. If not provided by
@@ -146,4 +146,3 @@
 .. sourcecode:: bash
 
   $ NS_LOG="CoDelQueueDisc" ./ns3 run "test-runner --suite=codel-queue-disc"
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/fq-cobalt.rst ns-3.38/src/traffic-control/doc/fq-cobalt.rst
--- ns-3.37/src/traffic-control/doc/fq-cobalt.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/fq-cobalt.rst	2023-03-16 20:54:23.836869200 -0700
@@ -62,9 +62,10 @@
 .. sourcecode:: cpp
 
   TrafficControlHelper tch;
-  tch.SetRootQueueDisc ("ns3::FqCobaltQueueDisc", "DropBatchSize", UintegerValue (1)
-                                                 "Perturbation", UintegerValue (256));
-  QueueDiscContainer qdiscs = tch.Install (devices);
+  tch.SetRootQueueDisc("ns3::FqCobaltQueueDisc",
+                        "DropBatchSize", UintegerValue(1),
+                       "Perturbation", UintegerValue(256));
+  QueueDiscContainer qdiscs = tch.Install(devices);
 
 Validation
 **********
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/fq-codel.rst ns-3.38/src/traffic-control/doc/fq-codel.rst
--- ns-3.37/src/traffic-control/doc/fq-codel.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/fq-codel.rst	2023-03-16 20:54:23.836869200 -0700
@@ -46,13 +46,13 @@
 
 * class :cpp:class:`FqCoDelQueueDisc`: This class implements the main FqCoDel algorithm:
 
-  * ``FqCoDelQueueDisc::DoEnqueue ()``: If no packet filter has been configured, this routine calls the QueueDiscItem::Hash() method to classify the given packet into an appropriate queue. Otherwise, the configured filters are used to classify the packet. If the filters are unable to classify the packet, the packet is dropped. Otherwise, an option is provided if set associative hashing is to be used.The packet is now handed over to the CoDel algorithm for timestamping. Then, if the queue is not currently active (i.e., if it is not in either the list of new or the list of old queues), it is added to the end of the list of new queues, and its deficit is initiated to the configured quantum. Otherwise,  the queue is left in its current queue list. Finally, the total number of enqueued packets is compared with the configured limit, and if it is above this value (which can happen since a packet was just enqueued), packets are dropped from the head of the queue with the largest current byte count until the number of dropped packets reaches the configured drop batch size or the backlog of the queue has been halved. Note that this in most cases means that the packet that was just enqueued is not among the packets that get dropped, which may even be from a different queue.
+  * ``FqCoDelQueueDisc::DoEnqueue()``: If no packet filter has been configured, this routine calls the QueueDiscItem::Hash() method to classify the given packet into an appropriate queue. Otherwise, the configured filters are used to classify the packet. If the filters are unable to classify the packet, the packet is dropped. Otherwise, an option is provided if set associative hashing is to be used.The packet is now handed over to the CoDel algorithm for timestamping. Then, if the queue is not currently active (i.e., if it is not in either the list of new or the list of old queues), it is added to the end of the list of new queues, and its deficit is initiated to the configured quantum. Otherwise,  the queue is left in its current queue list. Finally, the total number of enqueued packets is compared with the configured limit, and if it is above this value (which can happen since a packet was just enqueued), packets are dropped from the head of the queue with the largest current byte count until the number of dropped packets reaches the configured drop batch size or the backlog of the queue has been halved. Note that this in most cases means that the packet that was just enqueued is not among the packets that get dropped, which may even be from a different queue.
 
   * ``FqCoDelQueueDisc::SetAssociativeHash()``: An outer hash is identified for the given packet. This corresponds to the set into which the packet is to be enqueued. A set consists of a group of queues. The set determined by outer hash is enumerated; if a queue corresponding to this packet's flow is found (we use per-queue tags to achieve this), or in case of an inactive queue, or if a new queue can be created for this set without exceeding the maximum limit, the index of this queue is returned. Otherwise, all queues of this full set are active and correspond to flows different from the current packet's flow. In such cases, the index of first queue of this set is returned. We don’t consider creating new queues for the packet in these cases, since this approach may waste resources in the long run. The situation highlighted is a guaranteed collision and cannot be avoided without increasing the overall number of queues.
 
-  * ``FqCoDelQueueDisc::DoDequeue ()``: The first task performed by this routine is selecting a queue from which to dequeue a packet. To this end, the scheduler first looks at the list of new queues; for the queue at the head of that list, if that queue has a negative deficit (i.e., it has already dequeued at least a quantum of bytes), it is given an additional amount of deficit, the queue is put onto the end of the list of old queues, and the routine selects the next queue and starts again. Otherwise, that queue is selected for dequeue. If the list of new queues is empty, the scheduler proceeds down the list of old queues in the same fashion (checking the deficit, and either selecting the queue for dequeuing, or increasing deficit and putting the queue back at the end of the list). After having selected a queue from which to dequeue a packet, the CoDel algorithm is invoked on that queue. As a result of this, one or more packets may be discarded from the head of the selected queue, before the packet that should be dequeued is returned (or nothing is returned if the queue is or becomes empty while being handled by the CoDel algorithm). Finally, if the CoDel algorithm does not return a packet, then the queue must be empty, and the scheduler does one of two things: if the queue selected for dequeue came from the list of new queues, it is moved to the end of the list of old queues.  If instead it came from the list of old queues, that queue is removed from the list, to be added back (as a new queue) the next time a packet for that queue arrives. Then (since no packet was available for dequeue), the whole dequeue process is restarted from the beginning. If, instead, the scheduler did get a packet back from the CoDel algorithm, it subtracts the size of the packet from the byte deficit for the selected queue and returns the packet as the result of the dequeue operation.
+  * ``FqCoDelQueueDisc::DoDequeue()``: The first task performed by this routine is selecting a queue from which to dequeue a packet. To this end, the scheduler first looks at the list of new queues; for the queue at the head of that list, if that queue has a negative deficit (i.e., it has already dequeued at least a quantum of bytes), it is given an additional amount of deficit, the queue is put onto the end of the list of old queues, and the routine selects the next queue and starts again. Otherwise, that queue is selected for dequeue. If the list of new queues is empty, the scheduler proceeds down the list of old queues in the same fashion (checking the deficit, and either selecting the queue for dequeuing, or increasing deficit and putting the queue back at the end of the list). After having selected a queue from which to dequeue a packet, the CoDel algorithm is invoked on that queue. As a result of this, one or more packets may be discarded from the head of the selected queue, before the packet that should be dequeued is returned (or nothing is returned if the queue is or becomes empty while being handled by the CoDel algorithm). Finally, if the CoDel algorithm does not return a packet, then the queue must be empty, and the scheduler does one of two things: if the queue selected for dequeue came from the list of new queues, it is moved to the end of the list of old queues.  If instead it came from the list of old queues, that queue is removed from the list, to be added back (as a new queue) the next time a packet for that queue arrives. Then (since no packet was available for dequeue), the whole dequeue process is restarted from the beginning. If, instead, the scheduler did get a packet back from the CoDel algorithm, it subtracts the size of the packet from the byte deficit for the selected queue and returns the packet as the result of the dequeue operation.
 
-  * ``FqCoDelQueueDisc::FqCoDelDrop ()``: This routine is invoked by ``FqCoDelQueueDisc::DoEnqueue()`` to drop packets from the head of the queue with the largest current byte count. This routine keeps dropping packets until the number of dropped packets reaches the configured drop batch size or the backlog of the queue has been halved.
+  * ``FqCoDelQueueDisc::FqCoDelDrop()``: This routine is invoked by ``FqCoDelQueueDisc::DoEnqueue()`` to drop packets from the head of the queue with the largest current byte count. This routine keeps dropping packets until the number of dropped packets reaches the configured drop batch size or the backlog of the queue has been halved.
 
 * class :cpp:class:`FqCoDelFlow`: This class implements a flow queue, by keeping its current status (whether it is in the list of new queues, in the list of old queues or inactive) and its current deficit.
 
@@ -122,9 +122,9 @@
 .. sourcecode:: cpp
 
    TrafficControlHelper tch;
-   tch.SetRootQueueDisc ("ns3::FqCoDelQueueDisc", "DropBatchSize", UintegerValue (1)
-                                                 "Perturbation", UintegerValue (256));
-   QueueDiscContainer qdiscs = tch.Install (devices);
+   tch.SetRootQueueDisc("ns3::FqCoDelQueueDisc", "DropBatchSize", UintegerValue(1)
+                                                "Perturbation", UintegerValue(256));
+   QueueDiscContainer qdiscs = tch.Install(devices);
 
 The example for FqCoDel's L4S mode is `FqCoDel-L4S-example.cc` located in ``src/traffic-control/examples``.  To run the file (the first invocation below shows the available
 command-line options):
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/fq-pie.rst ns-3.38/src/traffic-control/doc/fq-pie.rst
--- ns-3.37/src/traffic-control/doc/fq-pie.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/fq-pie.rst	2023-03-16 20:54:23.836869200 -0700
@@ -77,9 +77,10 @@
 .. sourcecode:: cpp
 
   TrafficControlHelper tch;
-  tch.SetRootQueueDisc ("ns3::FqPieQueueDisc", "DropBatchSize", UintegerValue (1)
-                                                 "Perturbation", UintegerValue (256));
-  QueueDiscContainer qdiscs = tch.Install (devices);
+  tch.SetRootQueueDisc("ns3::FqPieQueueDisc",
+                       "DropBatchSize", UintegerValue(1)
+                       "Perturbation", UintegerValue(256));
+  QueueDiscContainer qdiscs = tch.Install(devices);
 
 Validation
 **********
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/mq.rst ns-3.38/src/traffic-control/doc/mq.rst
--- ns-3.37/src/traffic-control/doc/mq.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/mq.rst	2023-03-16 20:54:23.836869200 -0700
@@ -47,10 +47,10 @@
 .. sourcecode:: cpp
 
   TrafficControlHelper tch;
-  uint16_t handle = tch.SetRootQueueDisc ("ns3::MqQueueDisc");
-  TrafficControlHelper::ClassIdList cls = tch.AddQueueDiscClasses (handle, numTxQueues, "ns3::QueueDiscClass");
-  tch.AddChildQueueDiscs (handle, cls, "ns3::FqCoDelQueueDisc");
-  QueueDiscContainer qdiscs = tch.Install (devices);
+  uint16_t handle = tch.SetRootQueueDisc("ns3::MqQueueDisc");
+  TrafficControlHelper::ClassIdList cls = tch.AddQueueDiscClasses(handle, numTxQueues, "ns3::QueueDiscClass");
+  tch.AddChildQueueDiscs(handle, cls, "ns3::FqCoDelQueueDisc");
+  QueueDiscContainer qdiscs = tch.Install(devices);
 
 Note that the child queue discs attached to the classes do not necessarily have to be of the same type.
 
@@ -79,4 +79,3 @@
 ::
 
   $ NS_LOG="WifiAcMappingTest" ./ns3 run "test-runner --suite=ns3-wifi-ac-mapping"
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/pie.rst ns-3.38/src/traffic-control/doc/pie.rst
--- ns-3.37/src/traffic-control/doc/pie.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/pie.rst	2023-03-16 20:54:23.836869200 -0700
@@ -24,13 +24,13 @@
 
 * class :cpp:class:`PieQueueDisc`: This class implements the main PIE algorithm:
 
-  * ``PieQueueDisc::DoEnqueue ()``: This routine checks whether the queue is full, and if so, drops the packets and records the number of drops due to queue overflow. If queue is not full then if ActiveThreshold is set then it checks if queue delay is higher than ActiveThreshold and if it is then, this routine calls ``PieQueueDisc::DropEarly()``, and depending on the value returned, the incoming packet is either enqueued or dropped.
+  * ``PieQueueDisc::DoEnqueue()``: This routine checks whether the queue is full, and if so, drops the packets and records the number of drops due to queue overflow. If queue is not full then if ActiveThreshold is set then it checks if queue delay is higher than ActiveThreshold and if it is then, this routine calls ``PieQueueDisc::DropEarly()``, and depending on the value returned, the incoming packet is either enqueued or dropped.
 
-  * ``PieQueueDisc::DropEarly ()``: The decision to enqueue or drop the packet is taken by invoking this routine, which returns a boolean value; false indicates enqueue and true indicates drop.
+  * ``PieQueueDisc::DropEarly()``: The decision to enqueue or drop the packet is taken by invoking this routine, which returns a boolean value; false indicates enqueue and true indicates drop.
 
-  * ``PieQueueDisc::CalculateP ()``: This routine is called at a regular interval of `m_tUpdate` and updates the drop probability, which is required by ``PieQueueDisc::DropEarly()``
+  * ``PieQueueDisc::CalculateP()``: This routine is called at a regular interval of `m_tUpdate` and updates the drop probability, which is required by ``PieQueueDisc::DropEarly()``
 
-  * ``PieQueueDisc::DoDequeue ()``: This routine calculates queue delay using timestamps (by default) or, optionally with the `UseDequeRateEstimator` attribute enabled, calculates the average departure rate to estimate queue delay. A queue delay estimate required for updating the drop probability in ``PieQueueDisc::CalculateP ()``. Starting with the ns-3.32 release, the default approach to calculate queue delay has been changed to use timestamps.
+  * ``PieQueueDisc::DoDequeue()``: This routine calculates queue delay using timestamps (by default) or, optionally with the `UseDequeRateEstimator` attribute enabled, calculates the average departure rate to estimate queue delay. A queue delay estimate required for updating the drop probability in ``PieQueueDisc::CalculateP()``. Starting with the ns-3.32 release, the default approach to calculate queue delay has been changed to use timestamps.
 
 References
 ==========
@@ -112,4 +112,3 @@
 .. sourcecode:: bash
 
   $ NS_LOG="PieQueueDisc" ./ns3 run "test-runner --suite=pie-queue-disc"
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/prio.rst ns-3.38/src/traffic-control/doc/prio.rst
--- ns-3.37/src/traffic-control/doc/prio.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/prio.rst	2023-03-16 20:54:23.836869200 -0700
@@ -40,10 +40,10 @@
 is provided by `queue-discs-benchmark.cc` located in ``examples/traffic-control``::
 
   TrafficControlHelper tch;
-  uint16_t handle = tch.SetRootQueueDisc ("ns3::PrioQueueDisc", "Priomap", StringValue ("0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1"));
-  TrafficControlHelper::ClassIdList cid = tch.AddQueueDiscClasses (handle, 2, "ns3::QueueDiscClass");
-  tch.AddChildQueueDisc (handle, cid[0], "ns3::FifoQueueDisc");
-  tch.AddChildQueueDisc (handle, cid[1], "ns3::RedQueueDisc");
+  uint16_t handle = tch.SetRootQueueDisc("ns3::PrioQueueDisc", "Priomap", StringValue("0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1"));
+  TrafficControlHelper::ClassIdList cid = tch.AddQueueDiscClasses(handle, 2, "ns3::QueueDiscClass");
+  tch.AddChildQueueDisc(handle, cid[0], "ns3::FifoQueueDisc");
+  tch.AddChildQueueDisc(handle, cid[1], "ns3::RedQueueDisc");
 
 The code above adds two classes (bands) to a PrioQueueDisc. The highest priority one
 is a FifoQueueDisc, the other one is a RedQueueDisc. The attribute Priomap is set to
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/queue-discs.rst ns-3.38/src/traffic-control/doc/queue-discs.rst
--- ns-3.37/src/traffic-control/doc/queue-discs.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/queue-discs.rst	2023-03-16 20:54:23.836869200 -0700
@@ -92,13 +92,13 @@
 queue disc. A subclass is required to implement the following methods:
 
 * ``bool DoEnqueue (Ptr<QueueDiscItem> item)``:  Enqueue a packet
-* ``Ptr<QueueDiscItem> DoDequeue (void)``:  Dequeue a packet
-* ``bool CheckConfig (void) const``: Check if the configuration is correct
-* ``void InitializeParams (void)``: Initialize queue disc parameters
+* ``Ptr<QueueDiscItem> DoDequeue ()``:  Dequeue a packet
+* ``bool CheckConfig () const``: Check if the configuration is correct
+* ``void InitializeParams ()``: Initialize queue disc parameters
 
 and may optionally override the default implementation of the following method:
 
-* ``Ptr<const QueueDiscItem> DoPeek (void) const``: Peek the next packet to extract
+* ``Ptr<const QueueDiscItem> DoPeek () const``: Peek the next packet to extract
 
 The default implementation of the ``DoPeek`` method is based on the qdisc_peek_dequeued
 function of the Linux kernel, which dequeues a packet and retains it in the
@@ -211,9 +211,9 @@
 .. sourcecode:: cpp
 
   TrafficControlHelper tch;
-  uint16_t handle = tch.SetRootQueueDisc ("ns3::PfifoFastQueueDisc");
-  tch.AddInternalQueues (handle, 3, "ns3::DropTailQueue", "MaxSize", StringValue ("1000p"));
-  QueueDiscContainer qdiscs = tch.Install (devices);
+  uint16_t handle = tch.SetRootQueueDisc("ns3::PfifoFastQueueDisc");
+  tch.AddInternalQueues(handle, 3, "ns3::DropTailQueue", "MaxSize", StringValue("1000p"));
+  QueueDiscContainer qdiscs = tch.Install(devices);
 
 The above code adds three internal queues to the root queue disc of type PfifoFast.
 With the above configuration, the config path of the root queue disc installed on the j-th
@@ -235,7 +235,7 @@
 .. sourcecode:: cpp
 
   TrafficControlHelper tch;
-  tch.Uninstall (device);
+  tch.Uninstall(device);
 
 Note that the Uninstall method must be called after ``InternetStackHelper::Install()`` is called
 and after that IP addresses are configured using ``Ipv{4,6}AddressHelper``. For an example program,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/red.rst ns-3.38/src/traffic-control/doc/red.rst
--- ns-3.37/src/traffic-control/doc/red.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/red.rst	2023-03-16 20:54:23.836869200 -0700
@@ -138,7 +138,7 @@
 
 .. sourcecode:: cpp
 
-  Config::SetDefault ("ns3::RedQueueDisc::ARED", BooleanValue (true));
+  Config::SetDefault("ns3::RedQueueDisc::ARED", BooleanValue(true));
 
 Setting ARED to true implicitly configures both: (i) automatic setting
 of Queue weight, MinTh and MaxTh and (ii) adapting m_curMaxP.
@@ -151,16 +151,16 @@
 
 .. sourcecode:: cpp
 
-  Config::SetDefault ("ns3::RedQueueDisc::QW", DoubleValue (0.0));
-  Config::SetDefault ("ns3::RedQueueDisc::MinTh", DoubleValue (0));
-  Config::SetDefault ("ns3::RedQueueDisc::MaxTh", DoubleValue (0));
+  Config::SetDefault("ns3::RedQueueDisc::QW", DoubleValue(0.0));
+  Config::SetDefault("ns3::RedQueueDisc::MinTh", DoubleValue(0));
+  Config::SetDefault("ns3::RedQueueDisc::MaxTh", DoubleValue(0));
 
 To configure (ii); AdaptMaxP must be set to true, as done in
 ``src/traffic-control/examples/adaptive-red-tests.cc``:
 
 .. sourcecode:: cpp
 
-  Config::SetDefault ("ns3::RedQueueDisc::AdaptMaxP", BooleanValue (true));
+  Config::SetDefault("ns3::RedQueueDisc::AdaptMaxP", BooleanValue(true));
 
 Simulating Feng's Adaptive RED
 ==============================
@@ -170,7 +170,7 @@
 
 .. sourcecode:: cpp
 
-  Config::SetDefault ("ns3::RedQueueDisc::FengAdaptive", BooleanValue (true));
+  Config::SetDefault("ns3::RedQueueDisc::FengAdaptive", BooleanValue(true));
 
 Simulating NLRED
 ================
@@ -180,7 +180,7 @@
 
 .. sourcecode:: cpp
 
-  Config::SetDefault ("ns3::RedQueueDisc::NLRED", BooleanValue (true));
+  Config::SetDefault("ns3::RedQueueDisc::NLRED", BooleanValue(true));
 
 Examples
 ========
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/doc/tbf.rst ns-3.38/src/traffic-control/doc/tbf.rst
--- ns-3.37/src/traffic-control/doc/tbf.rst	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/doc/tbf.rst	2023-03-16 20:54:23.836869200 -0700
@@ -61,20 +61,20 @@
 
 * class :cpp:class:`TbfQueueDisc`: This class implements the main TBF algorithm:
 
-  * ``TbfQueueDisc::DoEnqueue ()``: This routine enqueue's the incoming packet if the queue is not full and drops the packet otherwise.
+  * ``TbfQueueDisc::DoEnqueue()``: This routine enqueue's the incoming packet if the queue is not full and drops the packet otherwise.
 
-  * ``TbfQueueDisc::DoPeek ()``: This routine peeks for the top item in the queue and if the queue is not empty, it returns the topmost item.
+  * ``TbfQueueDisc::DoPeek()``: This routine peeks for the top item in the queue and if the queue is not empty, it returns the topmost item.
 
-  * ``TbfQueueDisc::DoDequeue ()``: This routine performs the dequeuing of packets according to the following logic:
+  * ``TbfQueueDisc::DoDequeue()``: This routine performs the dequeuing of packets according to the following logic:
 
-    * It calls ``TbfQueueDisc::Peek ()`` and calculates the size of the packet to be dequeued in bytes.
+    * It calls ``TbfQueueDisc::Peek()`` and calculates the size of the packet to be dequeued in bytes.
     * Then it calculates the time difference 'delta', which is the time elapsed since the last update of tokens in the buckets.
     * If the second bucket exists, the number of tokens are updated according to the 'PeakRate' and 'delta'.
     * From this second bucket a number of tokens equal to the size of the packet to be dequeued is subtracted.
     * Now the number of tokens in the first bucket are updated according to 'Rate' and 'delta'.
     * From this first bucket a number of tokens equal to the size of the packet to be dequeued is subtracted.
     * If after this, both the first and second buckets have tokens greater than zero, then the packet is dequeued.
-    * Else, an event to ``QueueDisc::Run ()`` is scheduled after a time period when enough tokens will be present for the dequeue operation.
+    * Else, an event to ``QueueDisc::Run()`` is scheduled after a time period when enough tokens will be present for the dequeue operation.
 
 References
 ==========
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc ns-3.38/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc
--- ns-3.37/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc	2023-03-16 20:54:23.836869200 -0700
@@ -71,7 +71,7 @@
 /**
  * TCP Congestion window tracker.
  *
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param oldval Old value.
  * \param newval New value.
  */
@@ -90,7 +90,7 @@
 TraceCwnd(std::string cwndTrFileName)
 {
     AsciiTraceHelper ascii;
-    if (cwndTrFileName == "")
+    if (cwndTrFileName.empty())
     {
         NS_LOG_DEBUG("No trace file for cwnd provided");
         return;
@@ -107,7 +107,7 @@
 /**
  * Traffic Control Sojourn tracker.
  *
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param newval New value.
  */
 static void
@@ -125,7 +125,7 @@
 TraceSojourn(std::string sojournTrFileName)
 {
     AsciiTraceHelper ascii;
-    if (sojournTrFileName == "")
+    if (sojournTrFileName.empty())
     {
         NS_LOG_DEBUG("No trace file for sojourn provided");
         return;
@@ -142,7 +142,7 @@
 /**
  * Traffic Control Queue length tracker.
  *
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param oldval Old value.
  * \param newval New value.
  */
@@ -161,7 +161,7 @@
 TraceQueueLength(std::string queueLengthTrFileName)
 {
     AsciiTraceHelper ascii;
-    if (queueLengthTrFileName == "")
+    if (queueLengthTrFileName.empty())
     {
         NS_LOG_DEBUG("No trace file for queue length provided");
         return;
@@ -178,7 +178,7 @@
 /**
  * Traffic control drop trace.
  *
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param item The dropped item.
  */
 static void
@@ -196,7 +196,7 @@
 TraceEveryDrop(std::string everyDropTrFileName)
 {
     AsciiTraceHelper ascii;
-    if (everyDropTrFileName == "")
+    if (everyDropTrFileName.empty())
     {
         NS_LOG_DEBUG("No trace file for every drop event provided");
         return;
@@ -213,7 +213,7 @@
 /**
  * Traffic Control Dropping state trace.
  *
- * \param stream The ouput stream.
+ * \param stream The output stream.
  * \param oldVal Old value.
  * \param newVal New value.
  */
@@ -241,7 +241,7 @@
 TraceDroppingState(std::string dropStateTrFileName)
 {
     AsciiTraceHelper ascii;
-    if (dropStateTrFileName == "")
+    if (dropStateTrFileName.empty())
     {
         NS_LOG_DEBUG("No trace file for dropping state provided");
         return;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/examples/codel-vs-pfifo-basic-test.cc ns-3.38/src/traffic-control/examples/codel-vs-pfifo-basic-test.cc
--- ns-3.37/src/traffic-control/examples/codel-vs-pfifo-basic-test.cc	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/examples/codel-vs-pfifo-basic-test.cc	2023-03-16 20:54:23.836869200 -0700
@@ -78,7 +78,7 @@
 TraceCwnd(std::string cwndTrFileName)
 {
     AsciiTraceHelper ascii;
-    if (cwndTrFileName == "")
+    if (cwndTrFileName.empty())
     {
         NS_LOG_DEBUG("No trace file for cwnd provided");
         return;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/examples/fqcodel-l4s-example.cc ns-3.38/src/traffic-control/examples/fqcodel-l4s-example.cc
--- ns-3.37/src/traffic-control/examples/fqcodel-l4s-example.cc	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/examples/fqcodel-l4s-example.cc	2023-03-16 20:54:23.836869200 -0700
@@ -257,7 +257,7 @@
  * ---------------
  *    --n0TcpType:           First TCP type (bic, dctcp, or reno) [bic]
  *    --n1TcpType:           Second TCP type (cubic, dctcp, or reno) []
- *    --scenarioNum:         Scenario number from the scenarios avalaible in the file (1-9) [0]
+ *    --scenarioNum:         Scenario number from the scenarios available in the file (1-9) [0]
  *    --bottleneckQueueType: n2 queue type (fq or codel) [fq]
  *    --baseRtt:             base RTT [80ms]
  *    --useCeThreshold:      use CE threshold [false]
@@ -322,9 +322,10 @@
 }
 
 void
-TracePingRtt(std::ofstream* ofStream, Time rtt)
+TracePingRtt(std::ofstream* ofStream, uint16_t seqNo, Time rtt)
 {
-    *ofStream << Simulator::Now().GetSeconds() << " " << rtt.GetSeconds() * 1000 << std::endl;
+    *ofStream << Simulator::Now().GetSeconds() << " " << seqNo << " " << rtt.GetSeconds() * 1000
+              << std::endl;
 }
 
 void
@@ -531,7 +532,7 @@
     cmd.AddValue("n0TcpType", "n0 TCP type (bic, dctcp, or reno)", n0TcpType);
     cmd.AddValue("n1TcpType", "n1 TCP type (bic, dctcp, or reno)", n1TcpType);
     cmd.AddValue("scenarioNum",
-                 "Scenario number from the scenarios avalaible in the file (1-9)",
+                 "Scenario number from the scenarios available in the file (1-9)",
                  scenarioNum);
     cmd.AddValue("bottleneckQueueType", "n2 queue type (fq or codel)", queueType);
     cmd.AddValue("baseRtt", "base RTT", baseRtt);
@@ -612,7 +613,7 @@
             enableN1Tcp = true;
             n1TcpTypeId = TypeId::LookupByName("ns3::TcpDctcp");
         }
-        else if (n1TcpType == "")
+        else if (n1TcpType.empty())
         {
             NS_LOG_DEBUG("No N1 TCP selected");
         }
@@ -816,12 +817,12 @@
     // application setup                                      //
     ////////////////////////////////////////////////////////////
 
-    V4PingHelper pingHelper("192.168.1.2");
+    PingHelper pingHelper(Ipv4Address("192.168.1.2"));
     pingHelper.SetAttribute("Interval", TimeValue(pingInterval));
     pingHelper.SetAttribute("Size", UintegerValue(pingSize));
     ApplicationContainer pingContainer = pingHelper.Install(pingServer);
-    Ptr<V4Ping> v4Ping = pingContainer.Get(0)->GetObject<V4Ping>();
-    v4Ping->TraceConnectWithoutContext("Rtt", MakeBoundCallback(&TracePingRtt, &pingOfStream));
+    Ptr<Ping> ping = pingContainer.Get(0)->GetObject<Ping>();
+    ping->TraceConnectWithoutContext("Rtt", MakeBoundCallback(&TracePingRtt, &pingOfStream));
     pingContainer.Start(Seconds(1));
     pingContainer.Stop(stopTime - Seconds(1));
 
@@ -935,4 +936,6 @@
     lengthOfStream.close();
     queueDelayN0OfStream.close();
     queueDelayN1OfStream.close();
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/model/cobalt-queue-disc.cc ns-3.38/src/traffic-control/model/cobalt-queue-disc.cc
--- ns-3.37/src/traffic-control/model/cobalt-queue-disc.cc	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/model/cobalt-queue-disc.cc	2023-03-16 20:54:23.836869200 -0700
@@ -403,7 +403,7 @@
     if (CoDelTimeAfter((now - m_lastUpdateTimeBlue), Time2CoDel(m_target)))
     {
         NS_LOG_LOGIC("inside IF block");
-        m_pDrop = std::min(m_pDrop + m_increment, (double)1.0);
+        m_pDrop = std::min(m_pDrop + m_increment, 1.0);
         m_lastUpdateTimeBlue = now;
     }
     m_dropping = true;
@@ -421,7 +421,7 @@
     NS_LOG_FUNCTION(this);
     if (m_pDrop && CoDelTimeAfter((now - m_lastUpdateTimeBlue), Time2CoDel(m_target)))
     {
-        m_pDrop = std::max(m_pDrop - m_decrement, (double)0.0);
+        m_pDrop = std::max(m_pDrop - m_decrement, 0.0);
         m_lastUpdateTimeBlue = now;
     }
     m_dropping = false;
@@ -452,7 +452,7 @@
 
     // If L4S mode is enabled then check if the packet is ECT1 or CE and
     // if sojourn time is greater than CE threshold then the packet is marked.
-    // If packet is marked succesfully then the CoDel steps can be skipped.
+    // If packet is marked successfully then the CoDel steps can be skipped.
     if (item && m_useL4s)
     {
         uint8_t tosByte = 0;
@@ -534,7 +534,7 @@
     if (CoDelTimeAfter(sojournTime, Time2CoDel(m_blueThreshold)) &&
         CoDelTimeAfter((now - m_lastUpdateTimeBlue), Time2CoDel(m_target)))
     {
-        m_pDrop = std::min(m_pDrop + m_increment, (double)1.0);
+        m_pDrop = std::min(m_pDrop + m_increment, 1.0);
         m_lastUpdateTimeBlue = now;
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/model/cobalt-queue-disc.h ns-3.38/src/traffic-control/model/cobalt-queue-disc.h
--- ns-3.37/src/traffic-control/model/cobalt-queue-disc.h	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/model/cobalt-queue-disc.h	2023-03-16 20:54:23.836869200 -0700
@@ -216,7 +216,7 @@
 
     /**
      * Called to decide whether the current packet should be dropped based on decisions taken by
-     * Blue and Codel working parallely
+     * Blue and Codel working parallelly
      *
      * \return true if the packet should be dropped, false otherwise
      * \param item current packet
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/model/traffic-control-layer.cc ns-3.38/src/traffic-control/model/traffic-control-layer.cc
--- ns-3.37/src/traffic-control/model/traffic-control-layer.cc	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/model/traffic-control-layer.cc	2023-03-16 20:54:23.840869200 -0700
@@ -111,7 +111,7 @@
 {
     NS_LOG_FUNCTION(this << protocolType << device);
 
-    struct ProtocolHandlerEntry entry;
+    ProtocolHandlerEntry entry;
     entry.handler = handler;
     entry.protocol = protocolType;
     entry.device = device;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/model/traffic-control-layer.h ns-3.38/src/traffic-control/model/traffic-control-layer.h
--- ns-3.37/src/traffic-control/model/traffic-control-layer.h	2022-11-01 17:37:24.723395800 -0700
+++ ns-3.38/src/traffic-control/model/traffic-control-layer.h	2023-03-16 20:54:23.840869200 -0700
@@ -36,8 +36,10 @@
 class NetDeviceQueueInterface;
 
 /**
- * \defgroup traffic-control
- *
+ * \defgroup traffic-control Traffic Control model
+ */
+
+/**
  * The Traffic Control layer aims at introducing an equivalent of the Linux Traffic
  * Control infrastructure into ns-3. The Traffic Control layer sits in between
  * the NetDevices (L2) and any network protocol (e.g., IP). It is in charge of
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/adaptive-red-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/adaptive-red-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/adaptive-red-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/adaptive-red-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -31,12 +31,12 @@
 
 /**
  * \ingroup traffic-control
+ * \ingroup tests
  * \defgroup traffic-control-test traffic-control module tests
  */
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Ared Queue Disc Test Item
  */
@@ -83,7 +83,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Ared Queue Disc Test Case
  */
@@ -413,7 +412,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Ared Queue Disc Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/cobalt-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/cobalt-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/cobalt-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/cobalt-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -34,7 +34,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Cobalt Queue Disc Test Item
  */
@@ -46,10 +45,9 @@
      *
      * \param p packet
      * \param addr address
-     * \param protocol protocol
      * \param ecnCapable ECN capable
      */
-    CobaltQueueDiscTestItem(Ptr<Packet> p, const Address& addr, uint16_t protocol, bool ecnCapable);
+    CobaltQueueDiscTestItem(Ptr<Packet> p, const Address& addr, bool ecnCapable);
     ~CobaltQueueDiscTestItem() override;
 
     // Delete default constructor, copy constructor and assignment operator to avoid misuse
@@ -66,9 +64,8 @@
 
 CobaltQueueDiscTestItem::CobaltQueueDiscTestItem(Ptr<Packet> p,
                                                  const Address& addr,
-                                                 uint16_t protocol,
                                                  bool ecnCapable)
-    : QueueDiscItem(p, addr, ecnCapable),
+    : QueueDiscItem(p, addr, 0),
       m_ecnCapablePacket(ecnCapable)
 {
 }
@@ -94,7 +91,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 1: simple enqueue/dequeue with no drops
  */
@@ -177,27 +173,27 @@
     NS_TEST_ASSERT_MSG_EQ(queue->GetCurrentSize().GetValue(),
                           0 * modeSize,
                           "There should be no packets in queue");
-    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p1, dest, 0, false));
+    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p1, dest, false));
     NS_TEST_ASSERT_MSG_EQ(queue->GetCurrentSize().GetValue(),
                           1 * modeSize,
                           "There should be one packet in queue");
-    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p2, dest, 0, false));
+    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p2, dest, false));
     NS_TEST_ASSERT_MSG_EQ(queue->GetCurrentSize().GetValue(),
                           2 * modeSize,
                           "There should be two packets in queue");
-    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p3, dest, 0, false));
+    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p3, dest, false));
     NS_TEST_ASSERT_MSG_EQ(queue->GetCurrentSize().GetValue(),
                           3 * modeSize,
                           "There should be three packets in queue");
-    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p4, dest, 0, false));
+    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p4, dest, false));
     NS_TEST_ASSERT_MSG_EQ(queue->GetCurrentSize().GetValue(),
                           4 * modeSize,
                           "There should be four packets in queue");
-    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p5, dest, 0, false));
+    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p5, dest, false));
     NS_TEST_ASSERT_MSG_EQ(queue->GetCurrentSize().GetValue(),
                           5 * modeSize,
                           "There should be five packets in queue");
-    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p6, dest, 0, false));
+    queue->Enqueue(Create<CobaltQueueDiscTestItem>(p6, dest, false));
     NS_TEST_ASSERT_MSG_EQ(queue->GetCurrentSize().GetValue(),
                           6 * modeSize,
                           "There should be six packets in queue");
@@ -265,7 +261,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 2: Cobalt Queue Disc Drop Test Item
  */
@@ -372,7 +367,7 @@
     Address dest;
     for (uint32_t i = 0; i < nPkt; i++)
     {
-        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, 0, true));
+        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, true));
     }
 }
 
@@ -386,7 +381,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 3: Cobalt Queue Disc ECN marking Test Item
  */
@@ -632,7 +626,7 @@
     Address dest;
     for (uint32_t i = 0; i < nPkt; i++)
     {
-        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, 0, ecnCapable));
+        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, ecnCapable));
     }
 }
 
@@ -840,7 +834,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 4: Cobalt Queue Disc CE Threshold marking Test Item
  */
@@ -905,7 +898,7 @@
     Address dest;
     for (uint32_t i = 0; i < nPkt; i++)
     {
-        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, 0, true));
+        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, true));
     }
 }
 
@@ -1005,7 +998,7 @@
     Time dequeueInterval = MicroSeconds(1100);
     DequeueWithDelay(queue, modeSize, 11, dequeueInterval);
 
-    // First mark occured for the packet enqueued at 11ms, ideally TCP would decrease sending rate
+    // First mark occurred for the packet enqueued at 11ms, ideally TCP would decrease sending rate
     // which can be simulated by increasing interval between subsequent enqueues, so packets are now
     // enqueued with a delay 1.2ms.
     Time waitUntilFirstMark = MilliSeconds(11);
@@ -1052,7 +1045,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 5: Cobalt Queue Disc Enhanced Blue Test Item
  * This test checks that the Blue Enhancement is working correctly. This test checks that Pdrop
@@ -1176,7 +1168,7 @@
     Address dest;
     for (uint32_t i = 0; i < nPkt; i++)
     {
-        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, 0, true));
+        queue->Enqueue(Create<CobaltQueueDiscTestItem>(Create<Packet>(size), dest, true));
     }
 }
 
@@ -1201,6 +1193,7 @@
 }
 
 /**
+ * \ingroup traffic-control-test
  * The COBALT queue disc test suite.
  */
 static class CobaltQueueDiscTestSuite : public TestSuite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/codel-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/codel-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/codel-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/codel-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -58,7 +58,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Codel Queue Disc Test Item
  */
@@ -88,7 +87,7 @@
 };
 
 CodelQueueDiscTestItem::CodelQueueDiscTestItem(Ptr<Packet> p, const Address& addr, bool ecnCapable)
-    : QueueDiscItem(p, addr, ecnCapable),
+    : QueueDiscItem(p, addr, 0),
       m_ecnCapablePacket(ecnCapable)
 {
 }
@@ -114,7 +113,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 1: simple enqueue/dequeue with no drops
  */
@@ -277,7 +275,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 2: enqueue with drops due to queue overflow
  */
@@ -369,7 +366,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 3: NewtonStep unit test - test against explicit port of Linux implementation
  */
@@ -408,7 +404,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 4: ControlLaw unit test - test against explicit port of Linux implementation
  */
@@ -440,7 +435,7 @@
     return t + _reciprocal_scale(interval, recInvSqrt << REC_INV_SQRT_SHIFT_ns3);
 }
 
-// End Linux borrrow
+// End Linux borrow
 
 void
 CoDelQueueDiscControlLawTest::DoRun()
@@ -467,7 +462,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 5: enqueue/dequeue with drops according to CoDel algorithm
  */
@@ -680,7 +674,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test 6: enqueue/dequeue with marks according to CoDel algorithm
  */
@@ -1132,7 +1125,7 @@
                         nPacketsBeforeFirstMark,
                         "Number of packets in the queue before drop should be equal"
                         "to number of packets in the queue before first mark as the behavior "
-                        "untill packet N should be the same.");
+                        "until packet N should be the same.");
                     NS_TEST_ASSERT_MSG_EQ(
                         currentCeThreshMarkCount,
                         0,
@@ -1333,7 +1326,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief CoDel Queue Disc Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/fifo-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/fifo-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/fifo-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/fifo-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Fifo Queue Disc Test Item
  */
@@ -82,7 +81,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Fifo Queue Disc Test Case
  */
@@ -269,7 +267,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Fifo Queue Disc Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/pie-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/pie-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/pie-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/pie-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -33,7 +33,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Pie Queue Disc Test Item
  */
@@ -103,7 +102,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Pie Queue Disc Test Case
  */
@@ -849,7 +847,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Pie Queue Disc Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/prio-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/prio-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/prio-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/prio-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -35,7 +35,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Prio Queue Disc Test Item
  */
@@ -75,7 +74,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Prio Queue Disc Test Packet Filter
  */
@@ -134,7 +132,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Prio Queue Disc Test Case
  */
@@ -318,7 +315,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Prio Queue Disc Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/queue-disc-traces-test-suite.cc ns-3.38/src/traffic-control/test/queue-disc-traces-test-suite.cc
--- ns-3.37/src/traffic-control/test/queue-disc-traces-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/queue-disc-traces-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -30,11 +30,10 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Queue Disc Test Item
  */
-class qdTestItem : public QueueDiscItem
+class QdTestItem : public QueueDiscItem
 {
   public:
     /**
@@ -43,35 +42,34 @@
      * \param p the packet
      * \param addr the address
      */
-    qdTestItem(Ptr<Packet> p, const Address& addr);
-    ~qdTestItem() override;
+    QdTestItem(Ptr<Packet> p, const Address& addr);
+    ~QdTestItem() override;
     void AddHeader() override;
     bool Mark() override;
 };
 
-qdTestItem::qdTestItem(Ptr<Packet> p, const Address& addr)
+QdTestItem::QdTestItem(Ptr<Packet> p, const Address& addr)
     : QueueDiscItem(p, addr, 0)
 {
 }
 
-qdTestItem::~qdTestItem()
+QdTestItem::~QdTestItem()
 {
 }
 
 void
-qdTestItem::AddHeader()
+QdTestItem::AddHeader()
 {
 }
 
 bool
-qdTestItem::Mark()
+QdTestItem::Mark()
 {
     return false;
 }
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test Child Queue Disc that may drop packets before enqueue or after dequeue
  */
@@ -142,7 +140,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Test Parent Queue Disc having a child of type TestChildQueueDisc
  */
@@ -197,7 +194,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Keep statistics based on traces
  */
@@ -304,7 +300,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Queue Disc Traces Test Case
  *
@@ -450,7 +445,7 @@
     // Enqueue 4 packets. They must all be enqueued
     for (uint16_t i = 1; i <= 4; i++)
     {
-        root->Enqueue(Create<qdTestItem>(Create<Packet>(pktSizeUnit * i), dest));
+        root->Enqueue(Create<QdTestItem>(Create<Packet>(pktSizeUnit * i), dest));
 
         CheckQueued(root, i, pktSizeUnit * i * (i + 1) / 2);
         CheckDroppedBeforeEnqueue(root, 0, 0);
@@ -463,7 +458,7 @@
 
     // The fifth packet is dropped before enqueue by the child queue disc.
     // The packet drop is notified to the root queue disc.
-    root->Enqueue(Create<qdTestItem>(Create<Packet>(pktSizeUnit * 5), dest));
+    root->Enqueue(Create<QdTestItem>(Create<Packet>(pktSizeUnit * 5), dest));
 
     CheckQueued(root, 4, pktSizeUnit * 10);
     CheckDroppedBeforeEnqueue(root, 1, pktSizeUnit * 5);
@@ -559,7 +554,7 @@
     CheckDroppedAfterDequeue(child, 2, pktSizeUnit * 3);
 
     // Enqueue one packet.
-    root->Enqueue(Create<qdTestItem>(Create<Packet>(pktSizeUnit), dest));
+    root->Enqueue(Create<QdTestItem>(Create<Packet>(pktSizeUnit), dest));
 
     CheckQueued(root, 1, pktSizeUnit);
     CheckDroppedBeforeEnqueue(root, 1, pktSizeUnit * 5);
@@ -590,7 +585,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Queue Disc Traces Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/red-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/red-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/red-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/red-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -32,7 +32,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Red Queue Disc Test Item
  */
@@ -83,7 +82,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Red Queue Disc Test Case
  */
@@ -621,7 +619,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Red Queue Disc Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/tbf-queue-disc-test-suite.cc ns-3.38/src/traffic-control/test/tbf-queue-disc-test-suite.cc
--- ns-3.37/src/traffic-control/test/tbf-queue-disc-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/tbf-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -37,7 +37,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Tbf Queue Disc Test Item
  */
@@ -84,7 +83,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Tbf Queue Disc Test Case
  */
@@ -479,7 +477,7 @@
     }
 
     // The pattern being checked is a pattern of dequeue followed by blocked.  The delay between
-    // enqueues is not sufficient to allow ptokens to refill befor the next dequeue.  The first
+    // enqueues is not sufficient to allow ptokens to refill before the next dequeue.  The first
     // enqueue is at 1.08s in the future, and the attempted dequeue is at 1.10s in the future.  The
     // first dequeue will always succeed.  The second enqueue is 1.12s and attempted dequeue is
     // at 1.14s in the future, but the last dequeue was 0.04s prior; only 800 tokens can be refilled
@@ -541,7 +539,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Tbf Queue Disc Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/traffic-control/test/tc-flow-control-test-suite.cc ns-3.38/src/traffic-control/test/tc-flow-control-test-suite.cc
--- ns-3.37/src/traffic-control/test/tc-flow-control-test-suite.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/traffic-control/test/tc-flow-control-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
@@ -41,7 +41,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Queue Disc Test Item
  */
@@ -87,7 +86,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Traffic Control Flow Control Test Case
  */
@@ -496,7 +494,6 @@
 
 /**
  * \ingroup traffic-control-test
- * \ingroup tests
  *
  * \brief Traffic Control Flow Control Test Suite
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/doc/uan.rst ns-3.38/src/uan/doc/uan.rst
--- ns-3.37/src/uan/doc/uan.rst	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/doc/uan.rst	2023-03-16 20:54:23.840869200 -0700
@@ -294,7 +294,7 @@
 
 .. note::
 
-    should I insert the li-ion model deatils here? I think it is better to put them into an Energy-related chapter..
+    should I insert the li-ion model details here? I think it is better to put them into an Energy-related chapter..
 
 Scope and Limitations
 =====================
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/examples/uan-cw-example.cc ns-3.38/src/uan/examples/uan-cw-example.cc
--- ns-3.37/src/uan/examples/uan-cw-example.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/examples/uan-cw-example.cc	2023-03-16 20:54:23.840869200 -0700
@@ -103,7 +103,7 @@
 }
 
 void
-Experiment::UpdatePositions(NodeContainer& nodes)
+Experiment::UpdatePositions(NodeContainer& nodes) const
 {
     NS_LOG_DEBUG(Now().As(Time::S) << " Updating positions");
     NodeContainer::Iterator it = nodes.Begin();
@@ -227,7 +227,7 @@
         {
             NS_FATAL_ERROR("Could not open ascii trace file: " << m_asciitracefile);
         }
-        uan.EnableAsciiAll(ascii);
+        UanHelper::EnableAsciiAll(ascii);
 
         Simulator::Run();
         sinkNode = nullptr;
@@ -330,4 +330,6 @@
 
     per = nullptr;
     sinr = nullptr;
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/examples/uan-cw-example.h ns-3.38/src/uan/examples/uan-cw-example.h
--- ns-3.37/src/uan/examples/uan-cw-example.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/examples/uan-cw-example.h	2023-03-16 20:54:23.840869200 -0700
@@ -56,7 +56,7 @@
      *
      * \param nodes The nodes to reposition.
      */
-    void UpdatePositions(NodeContainer& nodes);
+    void UpdatePositions(NodeContainer& nodes) const;
     /** Save the throughput from a single run. */
     void ResetData();
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/examples/uan-rc-example.cc ns-3.38/src/uan/examples/uan-rc-example.cc
--- ns-3.37/src/uan/examples/uan-rc-example.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/examples/uan-rc-example.cc	2023-03-16 20:54:23.840869200 -0700
@@ -95,7 +95,7 @@
 }
 
 UanTxMode
-Experiment::CreateMode(uint32_t kass, uint32_t fc, bool upperblock, std::string name)
+Experiment::CreateMode(uint32_t kass, uint32_t fc, bool upperblock, std::string name) const
 {
     std::ostringstream buf;
     buf << name << " " << kass;
@@ -307,4 +307,6 @@
         NS_FATAL_ERROR("Can not open GNU Plot outfile: " << exp.m_gnuplotfile);
     }
     gp.GenerateOutput(of);
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/examples/uan-rc-example.h ns-3.38/src/uan/examples/uan-rc-example.h
--- ns-3.37/src/uan/examples/uan-rc-example.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/examples/uan-rc-example.h	2023-03-16 20:54:23.840869200 -0700
@@ -87,7 +87,7 @@
      * \param name Mode name.
      * \return The new mode.
      */
-    UanTxMode CreateMode(uint32_t kass, uint32_t fc, bool upperblock, std::string name);
+    UanTxMode CreateMode(uint32_t kass, uint32_t fc, bool upperblock, std::string name) const;
     /**
      * Create m_numRates matching control and data modes.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-header-common.cc ns-3.38/src/uan/model/uan-header-common.cc
--- ns-3.37/src/uan/model/uan-header-common.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-header-common.cc	2023-03-16 20:54:23.840869200 -0700
@@ -145,7 +145,7 @@
     return 0;
 }
 
-// Inherrited methods
+// Inherited methods
 
 uint32_t
 UanHeaderCommon::GetSerializedSize() const
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-header-rc.h ns-3.38/src/uan/model/uan-header-rc.h
--- ns-3.37/src/uan/model/uan-header-rc.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-header-rc.h	2023-03-16 20:54:23.840869200 -0700
@@ -93,7 +93,7 @@
      */
     void Print(std::ostream& os, Time::Unit unit) const;
 
-    // Inherrited methods
+    // Inherited methods
     uint32_t GetSerializedSize() const override;
     void Serialize(Buffer::Iterator start) const override;
     uint32_t Deserialize(Buffer::Iterator start) override;
@@ -211,7 +211,7 @@
      */
     void Print(std::ostream& os, Time::Unit unit) const;
 
-    // Inherrited methods
+    // Inherited methods
     uint32_t GetSerializedSize() const override;
     void Serialize(Buffer::Iterator start) const override;
     uint32_t Deserialize(Buffer::Iterator start) override;
@@ -315,7 +315,7 @@
      */
     void Print(std::ostream& os, Time::Unit unit) const;
 
-    // Inherrited methods
+    // Inherited methods
     uint32_t GetSerializedSize() const override;
     void Serialize(Buffer::Iterator start) const override;
     uint32_t Deserialize(Buffer::Iterator start) override;
@@ -433,7 +433,7 @@
      */
     void Print(std::ostream& os, Time::Unit unit) const;
 
-    // Inherrited methods
+    // Inherited methods
     uint32_t GetSerializedSize() const override;
     void Serialize(Buffer::Iterator start) const override;
     uint32_t Deserialize(Buffer::Iterator start) override;
@@ -500,7 +500,7 @@
      */
     uint8_t GetNoNacks() const;
 
-    // Inherrited methods
+    // Inherited methods
     uint32_t GetSerializedSize() const override;
     void Serialize(Buffer::Iterator start) const override;
     uint32_t Deserialize(Buffer::Iterator start) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-mac.cc ns-3.38/src/uan/model/uan-mac.cc
--- ns-3.37/src/uan/model/uan-mac.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-mac.cc	2023-03-16 20:54:23.840869200 -0700
@@ -43,7 +43,7 @@
 }
 
 uint32_t
-UanMac::GetTxModeIndex()
+UanMac::GetTxModeIndex() const
 {
     return m_txModeIndex;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-mac-cw.cc ns-3.38/src/uan/model/uan-mac-cw.cc
--- ns-3.37/src/uan/model/uan-mac-cw.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-mac-cw.cc	2023-03-16 20:54:23.840869200 -0700
@@ -126,11 +126,11 @@
             NS_LOG_DEBUG("State is not TX");
         }
 
-        NS_ASSERT(m_phy->GetTransducer()->GetArrivalList().size() >= 1 || m_phy->IsStateTx());
+        NS_ASSERT(!m_phy->GetTransducer()->GetArrivalList().empty() || m_phy->IsStateTx());
         return false;
     case RUNNING:
         NS_LOG_DEBUG("MAC " << GetAddress() << " Starting enqueue RUNNING");
-        NS_ASSERT(m_phy->GetTransducer()->GetArrivalList().size() == 0 && !m_phy->IsStateTx());
+        NS_ASSERT(m_phy->GetTransducer()->GetArrivalList().empty() && !m_phy->IsStateTx());
         return false;
     case TX:
     case IDLE: {
@@ -157,14 +157,14 @@
                                  << ": Enqueuing new packet while busy:  (Chose CW " << cw
                                  << ", Sending at " << m_sendTime.As(Time::S)
                                  << " Packet size: " << packet->GetSize());
-            NS_ASSERT(m_phy->GetTransducer()->GetArrivalList().size() >= 1 || m_phy->IsStateTx());
+            NS_ASSERT(!m_phy->GetTransducer()->GetArrivalList().empty() || m_phy->IsStateTx());
         }
         else
         {
             NS_ASSERT(m_state != TX);
             NS_LOG_DEBUG("Time " << Now().As(Time::S) << ": Addr " << GetAddress()
                                  << ": Enqueuing new packet while idle (sending)");
-            NS_ASSERT(m_phy->GetTransducer()->GetArrivalList().size() == 0 && !m_phy->IsStateTx());
+            NS_ASSERT(m_phy->GetTransducer()->GetArrivalList().empty() && !m_phy->IsStateTx());
             m_state = TX;
             m_phy->SendPacket(packet, GetTxModeIndex());
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-mac-cw.h ns-3.38/src/uan/model/uan-mac-cw.h
--- ns-3.37/src/uan/model/uan-mac-cw.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-mac-cw.h	2023-03-16 20:54:23.840869200 -0700
@@ -107,13 +107,13 @@
 
   private:
     /** Enum defining possible Phy states. */
-    typedef enum
+    enum State
     {
         IDLE,    //!< Idle state.
         CCABUSY, //!< Channel busy.
         RUNNING, //!< Delay timer running.
         TX       //!< Transmitting.
-    } State;
+    };
 
     /** Forwarding up callback. */
     Callback<void, Ptr<Packet>, uint16_t, const Mac8Address&> m_forwardUpCb;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-mac.h ns-3.38/src/uan/model/uan-mac.h
--- ns-3.37/src/uan/model/uan-mac.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-mac.h	2023-03-16 20:54:23.840869200 -0700
@@ -127,7 +127,7 @@
      * Get the Tx mode index (Modulation type).
      * \return the Tx mode index
      */
-    uint32_t GetTxModeIndex();
+    uint32_t GetTxModeIndex() const;
 
     /**
      * Set the Tx mode index (Modulation type).
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-phy-dual.cc ns-3.38/src/uan/model/uan-phy-dual.cc
--- ns-3.37/src/uan/model/uan-phy-dual.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-phy-dual.cc	2023-03-16 20:54:23.840869200 -0700
@@ -237,7 +237,7 @@
                             MakeTraceSourceAccessor(&UanPhyDual::m_rxOkLogger),
                             "ns3::UanPhy::TracedCallback")
             .AddTraceSource("RxError",
-                            "A packet was received unsuccessfully.",
+                            "A packet was received unsuccessfuly.",
                             MakeTraceSourceAccessor(&UanPhyDual::m_rxErrLogger),
                             "ns3::UanPhy::TracedCallback")
             .AddTraceSource("Tx",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-phy-dual.h ns-3.38/src/uan/model/uan-phy-dual.h
--- ns-3.37/src/uan/model/uan-phy-dual.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-phy-dual.h	2023-03-16 20:54:23.840869200 -0700
@@ -240,7 +240,7 @@
 
     /** A packet was received successfully. */
     ns3::TracedCallback<Ptr<const Packet>, double, UanTxMode> m_rxOkLogger;
-    /** A packet was received unsuccessfully. */
+    /** A packet was received unsuccessfuly. */
     ns3::TracedCallback<Ptr<const Packet>, double, UanTxMode> m_rxErrLogger;
     /** A packet was sent from this Phy. */
     ns3::TracedCallback<Ptr<const Packet>, double, UanTxMode> m_txLogger;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-phy-gen.cc ns-3.38/src/uan/model/uan-phy-gen.cc
--- ns-3.37/src/uan/model/uan-phy-gen.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-phy-gen.cc	2023-03-16 20:54:23.840869200 -0700
@@ -620,7 +620,7 @@
                             MakeTraceSourceAccessor(&UanPhyGen::m_rxOkLogger),
                             "ns3::UanPhy::TracedCallback")
             .AddTraceSource("RxError",
-                            "A packet was received unsuccessfully.",
+                            "A packet was received unsuccessfuly.",
                             MakeTraceSourceAccessor(&UanPhyGen::m_rxErrLogger),
                             "ns3::UanPhy::TracedCallback")
             .AddTraceSource("Tx",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-phy.h ns-3.38/src/uan/model/uan-phy.h
--- ns-3.37/src/uan/model/uan-phy.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-phy.h	2023-03-16 20:54:23.840869200 -0700
@@ -85,7 +85,7 @@
      * Convert kilopascals to dB re 1 uPa.
      *
      * \param kp Value in kilopascals.
-     * \return Valeu in dB re 1 uPa
+     * \return Value in dB re 1 uPa
      */
     inline double KpToDb(double kp) const
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/uan/model/uan-tx-mode.h ns-3.38/src/uan/model/uan-tx-mode.h
--- ns-3.37/src/uan/model/uan-tx-mode.h	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/uan/model/uan-tx-mode.h	2023-03-16 20:54:23.840869200 -0700
@@ -48,13 +48,13 @@
     /**
      * Modulation type.
      */
-    typedef enum
+    enum ModulationType
     {
         PSK,  //!< Phase shift keying.
         QAM,  //!< Quadrature amplitude modulation.
         FSK,  //!< Frequency shift keying.
         OTHER //!< Unspecified/undefined.
-    } ModulationType;
+    };
 
     /**
      * Get the modulation type of the mode.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/virtual-net-device/examples/CMakeLists.txt ns-3.38/src/virtual-net-device/examples/CMakeLists.txt
--- ns-3.37/src/virtual-net-device/examples/CMakeLists.txt	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/virtual-net-device/examples/CMakeLists.txt	2023-03-16 20:54:23.844869400 -0700
@@ -1,6 +1,6 @@
 build_lib_example(
-  NAME virtual-net-device
-  SOURCE_FILES virtual-net-device.cc
+  NAME virtual-net-device-example
+  SOURCE_FILES virtual-net-device-example.cc
   LIBRARIES_TO_LINK
     ${libvirtual-net-device}
     ${libpoint-to-point}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/virtual-net-device/examples/virtual-net-device.cc ns-3.38/src/virtual-net-device/examples/virtual-net-device.cc
--- ns-3.37/src/virtual-net-device/examples/virtual-net-device.cc	2022-11-01 17:37:24.727395800 -0700
+++ ns-3.38/src/virtual-net-device/examples/virtual-net-device.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,360 +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
- *
- * Based on simple-global-routing.cc
- * The Tunnel class adds two tunnels, n0<=>n3 and n1<=>n3
- */
-
-// Network topology
-//
-//  n0
-//     \ 5 Mb/s, 2ms
-//      \          1.5Mb/s, 10ms
-//       n2 -------------------------n3
-//      /
-//     / 5 Mb/s, 2ms
-//   n1
-//
-// - all links are point-to-point links with indicated one-way BW/delay
-// - Tracing of queues and packet receptions to file "virtual-net-device.tr"
-
-// Tunneling changes (relative to the simple-global-routing example):
-// - n0 will receive an extra virtual interface with hardcoded inner-tunnel address 11.0.0.1
-// - n1 will also receive an extra virtual interface with the same inner-tunnel address 11.0.0.1
-// - n3 will receive an extra virtual interface with inner-tunnel address 11.0.0.254
-// - The flows will be between 11.0.0.x (inner-tunnel) addresses instead of 10.1.x.y ones
-// - n3 will decide, on a per-packet basis, via random number, whether to
-//   send the packet to n0 or to n1.
-//
-// Note: here we create a tunnel where IP packets are tunneled over
-// UDP/IP, but tunneling directly IP-over-IP would also be possible;
-// see src/node/ipv4-raw-socket-factory.h.
-
-#include "ns3/virtual-net-device.h"
-
-#include "ns3/applications-module.h"
-#include "ns3/core-module.h"
-#include "ns3/internet-module.h"
-#include "ns3/network-module.h"
-#include "ns3/point-to-point-module.h"
-
-#include <cassert>
-#include <fstream>
-#include <iostream>
-#include <string>
-
-using namespace ns3;
-
-NS_LOG_COMPONENT_DEFINE("VirtualNetDeviceExample");
-
-/**
- * \ingroup virtual-net-device
- *
- * Tunnel class - its goal is to create and manage the tunnels between endpoints.
- */
-class Tunnel
-{
-    Ptr<Socket> m_n3Socket;           //!< Socket on the N3 node
-    Ptr<Socket> m_n0Socket;           //!< Socket on the N0 node
-    Ptr<Socket> m_n1Socket;           //!< Socket on the N1 node
-    Ipv4Address m_n3Address;          //!< Address of the N3 node
-    Ipv4Address m_n0Address;          //!< Address of the N0 node
-    Ipv4Address m_n1Address;          //!< Address of the N1 node
-    Ptr<UniformRandomVariable> m_rng; //!< Random number generator
-    Ptr<VirtualNetDevice> m_n0Tap;    //!< VirtualNetDevice on the N0 node
-    Ptr<VirtualNetDevice> m_n1Tap;    //!< VirtualNetDevice on the N1 node
-    Ptr<VirtualNetDevice> m_n3Tap;    //!< VirtualNetDevice on the N3 node
-
-    /**
-     * Send a packet through the N0 VirtualNetDevice
-     * \param packet Packet to send
-     * \param source Source IPv4 address
-     * \param dest Destination IPv4 address
-     * \param protocolNumber Protocol number
-     * \return true (always)
-     */
-    bool N0VirtualSend(Ptr<Packet> packet,
-                       const Address& source,
-                       const Address& dest,
-                       uint16_t protocolNumber)
-    {
-        NS_LOG_DEBUG("Send to " << m_n3Address << ": " << *packet);
-        m_n0Socket->SendTo(packet, 0, InetSocketAddress(m_n3Address, 667));
-        return true;
-    }
-
-    /**
-     * Send a packet through the N1 VirtualNetDevice
-     * \param packet Packet to send
-     * \param source Source IPv4 address
-     * \param dest Destination IPv4 address
-     * \param protocolNumber Protocol number
-     * \return true (always)
-     */
-    bool N1VirtualSend(Ptr<Packet> packet,
-                       const Address& source,
-                       const Address& dest,
-                       uint16_t protocolNumber)
-    {
-        NS_LOG_DEBUG("Send to " << m_n3Address << ": " << *packet);
-        m_n1Socket->SendTo(packet, 0, InetSocketAddress(m_n3Address, 667));
-        return true;
-    }
-
-    /**
-     * Send a packet through the N3 VirtualNetDevice
-     * \param packet Packet to send
-     * \param source Source IPv4 address
-     * \param dest Destination IPv4 address
-     * \param protocolNumber Protocol number
-     * \return true (always)
-     */
-    bool N3VirtualSend(Ptr<Packet> packet,
-                       const Address& source,
-                       const Address& dest,
-                       uint16_t protocolNumber)
-    {
-        if (m_rng->GetValue() < 0.25)
-        {
-            NS_LOG_DEBUG("Send to " << m_n0Address << ": " << *packet);
-            m_n3Socket->SendTo(packet, 0, InetSocketAddress(m_n0Address, 667));
-        }
-        else
-        {
-            NS_LOG_DEBUG("Send to " << m_n1Address << ": " << *packet);
-            m_n3Socket->SendTo(packet, 0, InetSocketAddress(m_n1Address, 667));
-        }
-        return true;
-    }
-
-    /**
-     * Receive a packet on the N3 VirtualNetDevice
-     * \param socket Receiving socket
-     */
-    void N3SocketRecv(Ptr<Socket> socket)
-    {
-        Ptr<Packet> packet = socket->Recv(65535, 0);
-        NS_LOG_DEBUG("N3SocketRecv: " << *packet);
-        m_n3Tap->Receive(packet,
-                         0x0800,
-                         m_n3Tap->GetAddress(),
-                         m_n3Tap->GetAddress(),
-                         NetDevice::PACKET_HOST);
-    }
-
-    /**
-     * Receive a packet on the N0 VirtualNetDevice
-     * \param socket Receiving socket
-     */
-    void N0SocketRecv(Ptr<Socket> socket)
-    {
-        Ptr<Packet> packet = socket->Recv(65535, 0);
-        NS_LOG_DEBUG("N0SocketRecv: " << *packet);
-        m_n0Tap->Receive(packet,
-                         0x0800,
-                         m_n0Tap->GetAddress(),
-                         m_n0Tap->GetAddress(),
-                         NetDevice::PACKET_HOST);
-    }
-
-    /**
-     * Receive a packet on the N1 VirtualNetDevice
-     * \param socket Receiving socket
-     */
-    void N1SocketRecv(Ptr<Socket> socket)
-    {
-        Ptr<Packet> packet = socket->Recv(65535, 0);
-        NS_LOG_DEBUG("N1SocketRecv: " << *packet);
-        m_n1Tap->Receive(packet,
-                         0x0800,
-                         m_n1Tap->GetAddress(),
-                         m_n1Tap->GetAddress(),
-                         NetDevice::PACKET_HOST);
-    }
-
-  public:
-    /**
-     * Constructor
-     * \param n3 Pointer of Node 3
-     * \param n0 Pointer of Node 0
-     * \param n1 Pointer of Node 1
-     * \param n3Addr IPv4 address of Node 3
-     * \param n0Addr IPv4 address of Node 0
-     * \param n1Addr IPv4 address of Node 1
-     */
-    Tunnel(Ptr<Node> n3,
-           Ptr<Node> n0,
-           Ptr<Node> n1,
-           Ipv4Address n3Addr,
-           Ipv4Address n0Addr,
-           Ipv4Address n1Addr)
-        : m_n3Address(n3Addr),
-          m_n0Address(n0Addr),
-          m_n1Address(n1Addr)
-    {
-        m_rng = CreateObject<UniformRandomVariable>();
-        m_n3Socket = Socket::CreateSocket(n3, TypeId::LookupByName("ns3::UdpSocketFactory"));
-        m_n3Socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), 667));
-        m_n3Socket->SetRecvCallback(MakeCallback(&Tunnel::N3SocketRecv, this));
-
-        m_n0Socket = Socket::CreateSocket(n0, TypeId::LookupByName("ns3::UdpSocketFactory"));
-        m_n0Socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), 667));
-        m_n0Socket->SetRecvCallback(MakeCallback(&Tunnel::N0SocketRecv, this));
-
-        m_n1Socket = Socket::CreateSocket(n1, TypeId::LookupByName("ns3::UdpSocketFactory"));
-        m_n1Socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), 667));
-        m_n1Socket->SetRecvCallback(MakeCallback(&Tunnel::N1SocketRecv, this));
-
-        // n0 tap device
-        m_n0Tap = CreateObject<VirtualNetDevice>();
-        m_n0Tap->SetAddress(Mac48Address("11:00:01:02:03:01"));
-        m_n0Tap->SetSendCallback(MakeCallback(&Tunnel::N0VirtualSend, this));
-        n0->AddDevice(m_n0Tap);
-        Ptr<Ipv4> ipv4 = n0->GetObject<Ipv4>();
-        uint32_t i = ipv4->AddInterface(m_n0Tap);
-        ipv4->AddAddress(i,
-                         Ipv4InterfaceAddress(Ipv4Address("11.0.0.1"), Ipv4Mask("255.255.255.0")));
-        ipv4->SetUp(i);
-
-        // n1 tap device
-        m_n1Tap = CreateObject<VirtualNetDevice>();
-        m_n1Tap->SetAddress(Mac48Address("11:00:01:02:03:02"));
-        m_n1Tap->SetSendCallback(MakeCallback(&Tunnel::N1VirtualSend, this));
-        n1->AddDevice(m_n1Tap);
-        ipv4 = n1->GetObject<Ipv4>();
-        i = ipv4->AddInterface(m_n1Tap);
-        ipv4->AddAddress(i,
-                         Ipv4InterfaceAddress(Ipv4Address("11.0.0.1"), Ipv4Mask("255.255.255.0")));
-        ipv4->SetUp(i);
-
-        // n3 tap device
-        m_n3Tap = CreateObject<VirtualNetDevice>();
-        m_n3Tap->SetAddress(Mac48Address("11:00:01:02:03:04"));
-        m_n3Tap->SetSendCallback(MakeCallback(&Tunnel::N3VirtualSend, this));
-        n3->AddDevice(m_n3Tap);
-        ipv4 = n3->GetObject<Ipv4>();
-        i = ipv4->AddInterface(m_n3Tap);
-        ipv4->AddAddress(
-            i,
-            Ipv4InterfaceAddress(Ipv4Address("11.0.0.254"), Ipv4Mask("255.255.255.0")));
-        ipv4->SetUp(i);
-    }
-};
-
-int
-main(int argc, char* argv[])
-{
-    // Users may find it convenient to turn on explicit logging
-    // for selected modules; the below lines suggest how to do this
-#if 0
-  LogComponentEnable ("VirtualNetDeviceExample", LOG_LEVEL_INFO);
-#endif
-    Packet::EnablePrinting();
-
-    // Set up some default values for the simulation.  Use the
-    Config::SetDefault("ns3::OnOffApplication::PacketSize", UintegerValue(210));
-    Config::SetDefault("ns3::OnOffApplication::DataRate", StringValue("448kb/s"));
-
-    // Allow the user to override any of the defaults and the above
-    // Config::SetDefault ()s at run-time, via command-line arguments
-    CommandLine cmd(__FILE__);
-    cmd.Parse(argc, argv);
-
-    NS_LOG_INFO("Create nodes.");
-    NodeContainer c;
-    c.Create(4);
-    NodeContainer n0n2 = NodeContainer(c.Get(0), c.Get(2));
-    NodeContainer n1n2 = NodeContainer(c.Get(1), c.Get(2));
-    NodeContainer n3n2 = NodeContainer(c.Get(3), c.Get(2));
-
-    InternetStackHelper internet;
-    internet.Install(c);
-
-    // We create the channels first without any IP addressing information
-    NS_LOG_INFO("Create channels.");
-    PointToPointHelper p2p;
-    p2p.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
-    p2p.SetChannelAttribute("Delay", StringValue("2ms"));
-    NetDeviceContainer d0d2 = p2p.Install(n0n2);
-
-    NetDeviceContainer d1d2 = p2p.Install(n1n2);
-
-    p2p.SetDeviceAttribute("DataRate", StringValue("1500kbps"));
-    p2p.SetChannelAttribute("Delay", StringValue("10ms"));
-    NetDeviceContainer d3d2 = p2p.Install(n3n2);
-
-    // Later, we add IP addresses.
-    NS_LOG_INFO("Assign IP Addresses.");
-    Ipv4AddressHelper ipv4;
-    ipv4.SetBase("10.1.1.0", "255.255.255.0");
-    Ipv4InterfaceContainer i0i2 = ipv4.Assign(d0d2);
-
-    ipv4.SetBase("10.1.2.0", "255.255.255.0");
-    Ipv4InterfaceContainer i1i2 = ipv4.Assign(d1d2);
-
-    ipv4.SetBase("10.1.3.0", "255.255.255.0");
-    Ipv4InterfaceContainer i3i2 = ipv4.Assign(d3d2);
-
-    // Create router nodes, initialize routing database and set up the routing
-    // tables in the nodes.
-    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
-
-    // Add the tunnels n0<=>n3 and n1<=>n3
-    Tunnel tunnel(c.Get(3),
-                  c.Get(0),
-                  c.Get(1),
-                  i3i2.GetAddress(0),
-                  i0i2.GetAddress(0),
-                  i1i2.GetAddress(0));
-
-    // Create the OnOff application to send UDP datagrams of size
-    // 210 bytes at a rate of 448 Kb/s
-    NS_LOG_INFO("Create Applications.");
-    uint16_t port = 9; // Discard port (RFC 863)
-    OnOffHelper onoff("ns3::UdpSocketFactory",
-                      Address(InetSocketAddress(Ipv4Address("11.0.0.254"), port)));
-    onoff.SetConstantRate(DataRate("448kb/s"));
-    ApplicationContainer apps = onoff.Install(c.Get(0));
-    apps.Start(Seconds(1.0));
-    apps.Stop(Seconds(10.0));
-
-    // Create a packet sink to receive these packets
-    PacketSinkHelper sink("ns3::UdpSocketFactory",
-                          Address(InetSocketAddress(Ipv4Address::GetAny(), port)));
-    apps = sink.Install(c.Get(3));
-    apps.Start(Seconds(1.0));
-    // apps.Stop (Seconds (10.0));
-
-    // Create a similar flow from n3 to n1, starting at time 1.1 seconds
-    onoff.SetAttribute("Remote", AddressValue(InetSocketAddress(Ipv4Address("11.0.0.1"), port)));
-    apps = onoff.Install(c.Get(3));
-    apps.Start(Seconds(1.1));
-    apps.Stop(Seconds(10.0));
-
-    // Create a packet sink to receive these packets
-    apps = sink.Install(c.Get(1));
-    apps.Start(Seconds(1.1));
-    // apps.Stop (Seconds (10.0));
-
-    AsciiTraceHelper ascii;
-    p2p.EnableAsciiAll(ascii.CreateFileStream("virtual-net-device.tr"));
-    p2p.EnablePcapAll("virtual-net-device");
-
-    NS_LOG_INFO("Run Simulation.");
-    Simulator::Run();
-    Simulator::Destroy();
-    NS_LOG_INFO("Done.");
-
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/virtual-net-device/examples/virtual-net-device-example.cc ns-3.38/src/virtual-net-device/examples/virtual-net-device-example.cc
--- ns-3.37/src/virtual-net-device/examples/virtual-net-device-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/virtual-net-device/examples/virtual-net-device-example.cc	2023-03-16 20:54:23.844869400 -0700
@@ -0,0 +1,359 @@
+/*
+ * 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
+ *
+ * Based on simple-global-routing.cc
+ * The Tunnel class adds two tunnels, n0<=>n3 and n1<=>n3
+ */
+
+// Network topology
+//
+//  n0
+//     \ 5 Mb/s, 2ms
+//      \          1.5Mb/s, 10ms
+//       n2 -------------------------n3
+//      /
+//     / 5 Mb/s, 2ms
+//   n1
+//
+// - all links are point-to-point links with indicated one-way BW/delay
+// - Tracing of queues and packet receptions to file "virtual-net-device.tr"
+
+// Tunneling changes (relative to the simple-global-routing example):
+// - n0 will receive an extra virtual interface with hardcoded inner-tunnel address 11.0.0.1
+// - n1 will also receive an extra virtual interface with the same inner-tunnel address 11.0.0.1
+// - n3 will receive an extra virtual interface with inner-tunnel address 11.0.0.254
+// - The flows will be between 11.0.0.x (inner-tunnel) addresses instead of 10.1.x.y ones
+// - n3 will decide, on a per-packet basis, via random number, whether to
+//   send the packet to n0 or to n1.
+//
+// Note: here we create a tunnel where IP packets are tunneled over
+// UDP/IP, but tunneling directly IP-over-IP would also be possible;
+// see src/node/ipv4-raw-socket-factory.h.
+
+#include "ns3/applications-module.h"
+#include "ns3/core-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/network-module.h"
+#include "ns3/point-to-point-module.h"
+#include "ns3/virtual-net-device.h"
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("VirtualNetDeviceExample");
+
+/**
+ * \ingroup virtual-net-device
+ *
+ * Tunnel class - its goal is to create and manage the tunnels between endpoints.
+ */
+class Tunnel
+{
+    Ptr<Socket> m_n3Socket;           //!< Socket on the N3 node
+    Ptr<Socket> m_n0Socket;           //!< Socket on the N0 node
+    Ptr<Socket> m_n1Socket;           //!< Socket on the N1 node
+    Ipv4Address m_n3Address;          //!< Address of the N3 node
+    Ipv4Address m_n0Address;          //!< Address of the N0 node
+    Ipv4Address m_n1Address;          //!< Address of the N1 node
+    Ptr<UniformRandomVariable> m_rng; //!< Random number generator
+    Ptr<VirtualNetDevice> m_n0Tap;    //!< VirtualNetDevice on the N0 node
+    Ptr<VirtualNetDevice> m_n1Tap;    //!< VirtualNetDevice on the N1 node
+    Ptr<VirtualNetDevice> m_n3Tap;    //!< VirtualNetDevice on the N3 node
+
+    /**
+     * Send a packet through the N0 VirtualNetDevice
+     * \param packet Packet to send
+     * \param source Source IPv4 address
+     * \param dest Destination IPv4 address
+     * \param protocolNumber Protocol number
+     * \return true (always)
+     */
+    bool N0VirtualSend(Ptr<Packet> packet,
+                       const Address& source,
+                       const Address& dest,
+                       uint16_t protocolNumber)
+    {
+        NS_LOG_DEBUG("Send to " << m_n3Address << ": " << *packet);
+        m_n0Socket->SendTo(packet, 0, InetSocketAddress(m_n3Address, 667));
+        return true;
+    }
+
+    /**
+     * Send a packet through the N1 VirtualNetDevice
+     * \param packet Packet to send
+     * \param source Source IPv4 address
+     * \param dest Destination IPv4 address
+     * \param protocolNumber Protocol number
+     * \return true (always)
+     */
+    bool N1VirtualSend(Ptr<Packet> packet,
+                       const Address& source,
+                       const Address& dest,
+                       uint16_t protocolNumber)
+    {
+        NS_LOG_DEBUG("Send to " << m_n3Address << ": " << *packet);
+        m_n1Socket->SendTo(packet, 0, InetSocketAddress(m_n3Address, 667));
+        return true;
+    }
+
+    /**
+     * Send a packet through the N3 VirtualNetDevice
+     * \param packet Packet to send
+     * \param source Source IPv4 address
+     * \param dest Destination IPv4 address
+     * \param protocolNumber Protocol number
+     * \return true (always)
+     */
+    bool N3VirtualSend(Ptr<Packet> packet,
+                       const Address& source,
+                       const Address& dest,
+                       uint16_t protocolNumber)
+    {
+        if (m_rng->GetValue() < 0.25)
+        {
+            NS_LOG_DEBUG("Send to " << m_n0Address << ": " << *packet);
+            m_n3Socket->SendTo(packet, 0, InetSocketAddress(m_n0Address, 667));
+        }
+        else
+        {
+            NS_LOG_DEBUG("Send to " << m_n1Address << ": " << *packet);
+            m_n3Socket->SendTo(packet, 0, InetSocketAddress(m_n1Address, 667));
+        }
+        return true;
+    }
+
+    /**
+     * Receive a packet on the N3 VirtualNetDevice
+     * \param socket Receiving socket
+     */
+    void N3SocketRecv(Ptr<Socket> socket)
+    {
+        Ptr<Packet> packet = socket->Recv(65535, 0);
+        NS_LOG_DEBUG("N3SocketRecv: " << *packet);
+        m_n3Tap->Receive(packet,
+                         0x0800,
+                         m_n3Tap->GetAddress(),
+                         m_n3Tap->GetAddress(),
+                         NetDevice::PACKET_HOST);
+    }
+
+    /**
+     * Receive a packet on the N0 VirtualNetDevice
+     * \param socket Receiving socket
+     */
+    void N0SocketRecv(Ptr<Socket> socket)
+    {
+        Ptr<Packet> packet = socket->Recv(65535, 0);
+        NS_LOG_DEBUG("N0SocketRecv: " << *packet);
+        m_n0Tap->Receive(packet,
+                         0x0800,
+                         m_n0Tap->GetAddress(),
+                         m_n0Tap->GetAddress(),
+                         NetDevice::PACKET_HOST);
+    }
+
+    /**
+     * Receive a packet on the N1 VirtualNetDevice
+     * \param socket Receiving socket
+     */
+    void N1SocketRecv(Ptr<Socket> socket)
+    {
+        Ptr<Packet> packet = socket->Recv(65535, 0);
+        NS_LOG_DEBUG("N1SocketRecv: " << *packet);
+        m_n1Tap->Receive(packet,
+                         0x0800,
+                         m_n1Tap->GetAddress(),
+                         m_n1Tap->GetAddress(),
+                         NetDevice::PACKET_HOST);
+    }
+
+  public:
+    /**
+     * Constructor
+     * \param n3 Pointer of Node 3
+     * \param n0 Pointer of Node 0
+     * \param n1 Pointer of Node 1
+     * \param n3Addr IPv4 address of Node 3
+     * \param n0Addr IPv4 address of Node 0
+     * \param n1Addr IPv4 address of Node 1
+     */
+    Tunnel(Ptr<Node> n3,
+           Ptr<Node> n0,
+           Ptr<Node> n1,
+           Ipv4Address n3Addr,
+           Ipv4Address n0Addr,
+           Ipv4Address n1Addr)
+        : m_n3Address(n3Addr),
+          m_n0Address(n0Addr),
+          m_n1Address(n1Addr)
+    {
+        m_rng = CreateObject<UniformRandomVariable>();
+        m_n3Socket = Socket::CreateSocket(n3, TypeId::LookupByName("ns3::UdpSocketFactory"));
+        m_n3Socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), 667));
+        m_n3Socket->SetRecvCallback(MakeCallback(&Tunnel::N3SocketRecv, this));
+
+        m_n0Socket = Socket::CreateSocket(n0, TypeId::LookupByName("ns3::UdpSocketFactory"));
+        m_n0Socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), 667));
+        m_n0Socket->SetRecvCallback(MakeCallback(&Tunnel::N0SocketRecv, this));
+
+        m_n1Socket = Socket::CreateSocket(n1, TypeId::LookupByName("ns3::UdpSocketFactory"));
+        m_n1Socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), 667));
+        m_n1Socket->SetRecvCallback(MakeCallback(&Tunnel::N1SocketRecv, this));
+
+        // n0 tap device
+        m_n0Tap = CreateObject<VirtualNetDevice>();
+        m_n0Tap->SetAddress(Mac48Address("11:00:01:02:03:01"));
+        m_n0Tap->SetSendCallback(MakeCallback(&Tunnel::N0VirtualSend, this));
+        n0->AddDevice(m_n0Tap);
+        Ptr<Ipv4> ipv4 = n0->GetObject<Ipv4>();
+        uint32_t i = ipv4->AddInterface(m_n0Tap);
+        ipv4->AddAddress(i,
+                         Ipv4InterfaceAddress(Ipv4Address("11.0.0.1"), Ipv4Mask("255.255.255.0")));
+        ipv4->SetUp(i);
+
+        // n1 tap device
+        m_n1Tap = CreateObject<VirtualNetDevice>();
+        m_n1Tap->SetAddress(Mac48Address("11:00:01:02:03:02"));
+        m_n1Tap->SetSendCallback(MakeCallback(&Tunnel::N1VirtualSend, this));
+        n1->AddDevice(m_n1Tap);
+        ipv4 = n1->GetObject<Ipv4>();
+        i = ipv4->AddInterface(m_n1Tap);
+        ipv4->AddAddress(i,
+                         Ipv4InterfaceAddress(Ipv4Address("11.0.0.1"), Ipv4Mask("255.255.255.0")));
+        ipv4->SetUp(i);
+
+        // n3 tap device
+        m_n3Tap = CreateObject<VirtualNetDevice>();
+        m_n3Tap->SetAddress(Mac48Address("11:00:01:02:03:04"));
+        m_n3Tap->SetSendCallback(MakeCallback(&Tunnel::N3VirtualSend, this));
+        n3->AddDevice(m_n3Tap);
+        ipv4 = n3->GetObject<Ipv4>();
+        i = ipv4->AddInterface(m_n3Tap);
+        ipv4->AddAddress(
+            i,
+            Ipv4InterfaceAddress(Ipv4Address("11.0.0.254"), Ipv4Mask("255.255.255.0")));
+        ipv4->SetUp(i);
+    }
+};
+
+int
+main(int argc, char* argv[])
+{
+    // Users may find it convenient to turn on explicit logging
+    // for selected modules; the below lines suggest how to do this
+#if 0
+  LogComponentEnable ("VirtualNetDeviceExample", LOG_LEVEL_INFO);
+#endif
+    Packet::EnablePrinting();
+
+    // Set up some default values for the simulation.  Use the
+    Config::SetDefault("ns3::OnOffApplication::PacketSize", UintegerValue(210));
+    Config::SetDefault("ns3::OnOffApplication::DataRate", StringValue("448kb/s"));
+
+    // Allow the user to override any of the defaults and the above
+    // Config::SetDefault ()s at run-time, via command-line arguments
+    CommandLine cmd(__FILE__);
+    cmd.Parse(argc, argv);
+
+    NS_LOG_INFO("Create nodes.");
+    NodeContainer c;
+    c.Create(4);
+    NodeContainer n0n2 = NodeContainer(c.Get(0), c.Get(2));
+    NodeContainer n1n2 = NodeContainer(c.Get(1), c.Get(2));
+    NodeContainer n3n2 = NodeContainer(c.Get(3), c.Get(2));
+
+    InternetStackHelper internet;
+    internet.Install(c);
+
+    // We create the channels first without any IP addressing information
+    NS_LOG_INFO("Create channels.");
+    PointToPointHelper p2p;
+    p2p.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
+    p2p.SetChannelAttribute("Delay", StringValue("2ms"));
+    NetDeviceContainer d0d2 = p2p.Install(n0n2);
+
+    NetDeviceContainer d1d2 = p2p.Install(n1n2);
+
+    p2p.SetDeviceAttribute("DataRate", StringValue("1500kbps"));
+    p2p.SetChannelAttribute("Delay", StringValue("10ms"));
+    NetDeviceContainer d3d2 = p2p.Install(n3n2);
+
+    // Later, we add IP addresses.
+    NS_LOG_INFO("Assign IP Addresses.");
+    Ipv4AddressHelper ipv4;
+    ipv4.SetBase("10.1.1.0", "255.255.255.0");
+    Ipv4InterfaceContainer i0i2 = ipv4.Assign(d0d2);
+
+    ipv4.SetBase("10.1.2.0", "255.255.255.0");
+    Ipv4InterfaceContainer i1i2 = ipv4.Assign(d1d2);
+
+    ipv4.SetBase("10.1.3.0", "255.255.255.0");
+    Ipv4InterfaceContainer i3i2 = ipv4.Assign(d3d2);
+
+    // Create router nodes, initialize routing database and set up the routing
+    // tables in the nodes.
+    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
+
+    // Add the tunnels n0<=>n3 and n1<=>n3
+    Tunnel tunnel(c.Get(3),
+                  c.Get(0),
+                  c.Get(1),
+                  i3i2.GetAddress(0),
+                  i0i2.GetAddress(0),
+                  i1i2.GetAddress(0));
+
+    // Create the OnOff application to send UDP datagrams of size
+    // 210 bytes at a rate of 448 Kb/s
+    NS_LOG_INFO("Create Applications.");
+    uint16_t port = 9; // Discard port (RFC 863)
+    OnOffHelper onoff("ns3::UdpSocketFactory",
+                      Address(InetSocketAddress(Ipv4Address("11.0.0.254"), port)));
+    onoff.SetConstantRate(DataRate("448kb/s"));
+    ApplicationContainer apps = onoff.Install(c.Get(0));
+    apps.Start(Seconds(1.0));
+    apps.Stop(Seconds(10.0));
+
+    // Create a packet sink to receive these packets
+    PacketSinkHelper sink("ns3::UdpSocketFactory",
+                          Address(InetSocketAddress(Ipv4Address::GetAny(), port)));
+    apps = sink.Install(c.Get(3));
+    apps.Start(Seconds(1.0));
+    // apps.Stop (Seconds (10.0));
+
+    // Create a similar flow from n3 to n1, starting at time 1.1 seconds
+    onoff.SetAttribute("Remote", AddressValue(InetSocketAddress(Ipv4Address("11.0.0.1"), port)));
+    apps = onoff.Install(c.Get(3));
+    apps.Start(Seconds(1.1));
+    apps.Stop(Seconds(10.0));
+
+    // Create a packet sink to receive these packets
+    apps = sink.Install(c.Get(1));
+    apps.Start(Seconds(1.1));
+    // apps.Stop (Seconds (10.0));
+
+    AsciiTraceHelper ascii;
+    p2p.EnableAsciiAll(ascii.CreateFileStream("virtual-net-device.tr"));
+    p2p.EnablePcapAll("virtual-net-device");
+
+    NS_LOG_INFO("Run Simulation.");
+    Simulator::Run();
+    Simulator::Destroy();
+    NS_LOG_INFO("Done.");
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/visualizer/model/pyviz.cc ns-3.38/src/visualizer/model/pyviz.cc
--- ns-3.37/src/visualizer/model/pyviz.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/visualizer/model/pyviz.cc	2023-03-16 20:54:23.844869400 -0700
@@ -43,7 +43,7 @@
 {
     std::vector<std::string> results;
     size_t cutAt;
-    while ((cutAt = str.find_first_of('/')) != str.npos)
+    while ((cutAt = str.find_first_of('/')) != std::string::npos)
     {
         if (cutAt > 0)
         {
@@ -457,7 +457,7 @@
                 missingHeaders.erase(missingIter);
             }
         }
-        if (missingHeaders.size() == 0)
+        if (missingHeaders.empty())
         {
             return true;
         }
@@ -986,7 +986,7 @@
      * Clip start top function
      * \param line the clip line
      */
-    void ClipStartTop(Line& line)
+    void ClipStartTop(Line& line) const
     {
         line.start.x += line.dx * (m_clipMin.y - line.start.y) / line.dy;
         line.start.y = m_clipMin.y;
@@ -996,7 +996,7 @@
      * Clip start bottom function
      * \param line the clip line
      */
-    void ClipStartBottom(Line& line)
+    void ClipStartBottom(Line& line) const
     {
         line.start.x += line.dx * (m_clipMax.y - line.start.y) / line.dy;
         line.start.y = m_clipMax.y;
@@ -1006,7 +1006,7 @@
      * Clip start right function
      * \param line the clip line
      */
-    void ClipStartRight(Line& line)
+    void ClipStartRight(Line& line) const
     {
         line.start.y += line.dy * (m_clipMax.x - line.start.x) / line.dx;
         line.start.x = m_clipMax.x;
@@ -1016,7 +1016,7 @@
      * Clip start left function
      * \param line the clip line
      */
-    void ClipStartLeft(Line& line)
+    void ClipStartLeft(Line& line) const
     {
         line.start.y += line.dy * (m_clipMin.x - line.start.x) / line.dx;
         line.start.x = m_clipMin.x;
@@ -1026,7 +1026,7 @@
      * Clip end top function
      * \param line the clip line
      */
-    void ClipEndTop(Line& line)
+    void ClipEndTop(Line& line) const
     {
         line.end.x += line.dx * (m_clipMin.y - line.end.y) / line.dy;
         line.end.y = m_clipMin.y;
@@ -1036,7 +1036,7 @@
      * Clip end bottom function
      * \param line the clip line
      */
-    void ClipEndBottom(Line& line)
+    void ClipEndBottom(Line& line) const
     {
         line.end.x += line.dx * (m_clipMax.y - line.end.y) / line.dy;
         line.end.y = m_clipMax.y;
@@ -1046,7 +1046,7 @@
      * Clip end right function
      * \param line the clip line
      */
-    void ClipEndRight(Line& line)
+    void ClipEndRight(Line& line) const
     {
         line.end.y += line.dy * (m_clipMax.x - line.end.x) / line.dx;
         line.end.x = m_clipMax.x;
@@ -1056,7 +1056,7 @@
      * Clip end left function
      * \param line the clip line
      */
-    void ClipEndLeft(Line& line)
+    void ClipEndLeft(Line& line) const
     {
         line.end.y += line.dy * (m_clipMin.x - line.end.x) / line.dx;
         line.end.x = m_clipMin.x;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/visualizer/model/visual-simulator-impl.h ns-3.38/src/visualizer/model/visual-simulator-impl.h
--- ns-3.37/src/visualizer/model/visual-simulator-impl.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/visualizer/model/visual-simulator-impl.h	2023-03-16 20:54:23.844869400 -0700
@@ -50,7 +50,7 @@
     static TypeId GetTypeId();
 
     VisualSimulatorImpl();
-    ~VisualSimulatorImpl();
+    ~VisualSimulatorImpl() override;
 
     // Inherited
     void Destroy() override;
@@ -77,8 +77,8 @@
     void RunRealSimulator();
 
   protected:
-    void DoDispose();
-    void NotifyConstructionCompleted();
+    void DoDispose() override;
+    void NotifyConstructionCompleted() override;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/visualizer/visualizer/core.py ns-3.38/src/visualizer/visualizer/core.py
--- ns-3.37/src/visualizer/visualizer/core.py	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/visualizer/visualizer/core.py	2023-03-16 20:54:23.844869400 -0700
@@ -113,10 +113,10 @@
     #  label
     ## @var _label_canvas_item
     #  label canvas
-    ## @var selected
-    #  selected property
     ## @var highlighted
     #  highlighted property
+    ## @var selected
+    #  selected property
 
     ## signal emitted whenever a tooltip is about to be shown for the node
     ## the first signal parameter is a python list of strings, to which
@@ -301,7 +301,10 @@
         @param event: event
         @return none
         """
+
+        ## highlighted property
         self.highlighted = True
+
     def on_leave_notify_event(self, view, target, event):
         """!
         On Leave event handle.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/visualizer/visualizer/ipython_view.py ns-3.38/src/visualizer/visualizer/ipython_view.py
--- ns-3.37/src/visualizer/visualizer/ipython_view.py	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/visualizer/visualizer/ipython_view.py	2023-03-16 20:54:23.844869400 -0700
@@ -23,6 +23,7 @@
 
 from pkg_resources import parse_version
 
+## Try to import IPython
 try:
   import IPython
 except ImportError:
@@ -46,30 +47,10 @@
   #  header
   ## @var config
   #  config
-  ## @var user_ns
-  #  user_ns
-  ## @var old_stdout
-  #  saved stdout
-  ## @var old_stderr
-  #  saved stderr
-  ## @var system
-  #  system
-  ## @var cfg
-  # configuration
   ## @var colors
   #  colors
-  ## @var raw_input_original
-  #  original raw input
-  ## @var stdin
-  #  cin
-  ## @var stdout
-  #  cout
-  ## @var stderr
-  #  cerr
   ## @var raw_input
   #  raw input
-  ## @var excepthook
-  #  exception hook
   ## Constructor
   def __init__(self,argv=None,user_ns=None,user_global_ns=None,
                cin=None, cout=None,cerr=None, input_func=None):
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/doc/wave.rst ns-3.38/src/wave/doc/wave.rst
--- ns-3.37/src/wave/doc/wave.rst	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/doc/wave.rst	2023-03-16 20:54:23.844869400 -0700
@@ -110,7 +110,7 @@
 5. ConfigureEdca
 
    This method will allow the user to set EDCA parameters of WAVE channels
-   including CCH ans SCHs. And the OcbWifiMac itself also uses this method
+   including CCH and SCHs. And the OcbWifiMac itself also uses this method
    to configure default 802.11p EDCA parameters.
 
 6. Wildcard BSSID
@@ -140,7 +140,7 @@
 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 inherting
+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``
@@ -225,7 +225,7 @@
 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
+  (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
@@ -237,7 +237,7 @@
 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
+  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
@@ -453,13 +453,13 @@
 
     NodeContainer nodes;
     NetDeviceContainer devices;
-    nodes.Create (2);
-    YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default ();
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
-    wifiPhy.SetChannel (wifiChannel.Create ());
+    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);
+    Wifi80211pHelper 80211pHelper = Wifi80211pHelper::Default();
+    devices = 80211pHelper.Install(wifiPhy, wifi80211pMac, nodes);
 
 The relation of  ``ns3::YansWavePhyHelper``, ``ns3::QosWaveMacHelper`` and ``ns3::WaveHelper``
 is described as below:
@@ -482,13 +482,13 @@
 
     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);
+    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``.
 
@@ -527,43 +527,43 @@
     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;
+    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);
+    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);
+    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);
+    double bsm_pdr1 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(1);
 
     // total WAVE BSM bytes sent
-    uint32_t cumulativeWaveBsmBytes = m_waveBsmHelper.GetWaveBsmStats ()->GetTxByteCount ();
+    uint32_t cumulativeWaveBsmBytes = m_waveBsmHelper.GetWaveBsmStats()->GetTxByteCount();
 
     // get number of WAVE BSM packets sent
-    int wavePktsSent = m_waveBsmHelper.GetWaveBsmStats ()->GetTxPktCount ();
+    int wavePktsSent = m_waveBsmHelper.GetWaveBsmStats()->GetTxPktCount();
 
     // get number of WAVE BSM packets received
-    int wavePktsReceived = m_waveBsmHelper.GetWaveBsmStats ()->GetRxPktCount ();
+    int wavePktsReceived = m_waveBsmHelper.GetWaveBsmStats()->GetRxPktCount();
 
     // reset count of WAVE BSM packets received
-    m_waveBsmHelper.GetWaveBsmStats ()->SetRxPktCount (0);
+    m_waveBsmHelper.GetWaveBsmStats()->SetRxPktCount(0);
 
     // reset count of WAVE BSM packets sent
-    m_waveBsmHelper.GetWaveBsmStats ()->SetTxPktCount (0);
+    m_waveBsmHelper.GetWaveBsmStats()->SetTxPktCount(0);
 
     // indicate that a node (nodeId) is moving.  (set to 0 to "stop" node)
     WaveBsmHelper::GetNodesMoving()[nodeId] = 1;
@@ -590,22 +590,22 @@
 
   ::
 
-    VscCallback vsccall = MakeCallback (&VsaExample::GetWsaAndOi, this);
+    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);
+      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);
+      Ptr<Packet> vsc = Create<Packet>();
+      ocb2->SendVsc(vsc, Mac48Address::GetBroadcast(), m_16093oi);
 
 6. then registered callbacks in the receiver will be called.
 
@@ -623,118 +623,118 @@
   ::
 
     // the class ``ns3::WaveNetDeviceExample``here will has a receive method "Receive" to be registered.
-    receiver->SetReceiveCallback (MakeCallback (&WaveNetDeviceExample::Receive, this));
+    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));
+    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);
+    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)
+    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)
+    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);
+    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);
+    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);
+    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);
+    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);
+    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);
+    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);
+    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);
+    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);
+    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);
+    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);
+    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.
@@ -817,7 +817,7 @@
 
 ::
 
-  Wifi80211pHelper::EnableLogComponents ();
+  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
@@ -825,7 +825,7 @@
 
 ::
 
-  WaveHelper::EnableLogComponents ();
+  WaveHelper::EnableLogComponents();
 
 
 Advanced Usage
@@ -841,10 +841,10 @@
 ::
 
     // in this case, the MAC entities for SCH2 to SCH6 will not be created
-    WaveHelper helper = WaveHelper::Default ();
+    WaveHelper helper = WaveHelper::Default();
     uint32_t channels[] = {CCH, SCH1};
-    std::vector<uint32_t> channelsVector (channels, channels + 2);
-    helper.CreateMacForChannel (channelsVector);
+    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
@@ -853,11 +853,11 @@
 
 ::
 
-    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
+    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
 ========
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/examples/vanet-routing-compare.cc ns-3.38/src/wave/examples/vanet-routing-compare.cc
--- ns-3.37/src/wave/examples/vanet-routing-compare.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/examples/vanet-routing-compare.cc	2023-03-16 20:54:23.844869400 -0700
@@ -144,25 +144,25 @@
      * \brief Returns the number of bytes received
      * \return the number of bytes received
      */
-    uint32_t GetRxBytes();
+    uint32_t GetRxBytes() const;
 
     /**
      * \brief Returns the cumulative number of bytes received
      * \return the cumulative number of bytes received
      */
-    uint32_t GetCumulativeRxBytes();
+    uint32_t GetCumulativeRxBytes() const;
 
     /**
      * \brief Returns the count of packets received
      * \return the count of packets received
      */
-    uint32_t GetRxPkts();
+    uint32_t GetRxPkts() const;
 
     /**
      * \brief Returns the cumulative count of packets received
      * \return the cumulative count of packets received
      */
-    uint32_t GetCumulativeRxPkts();
+    uint32_t GetCumulativeRxPkts() const;
 
     /**
      * \brief Increments the number of (application-data)
@@ -192,25 +192,25 @@
      * \brief Returns the number of bytes transmitted
      * \return the number of bytes transmitted
      */
-    uint32_t GetTxBytes();
+    uint32_t GetTxBytes() const;
 
     /**
      * \brief Returns the cumulative number of bytes transmitted
      * \return the cumulative number of bytes transmitted
      */
-    uint32_t GetCumulativeTxBytes();
+    uint32_t GetCumulativeTxBytes() const;
 
     /**
      * \brief Returns the number of packets transmitted
      * \return the number of packets transmitted
      */
-    uint32_t GetTxPkts();
+    uint32_t GetTxPkts() const;
 
     /**
      * \brief Returns the cumulative number of packets transmitted
      * \return the cumulative number of packets transmitted
      */
-    uint32_t GetCumulativeTxPkts();
+    uint32_t GetCumulativeTxPkts() const;
 
     /**
      * \brief Increment the number of bytes transmitted
@@ -236,7 +236,7 @@
     void SetTxPkts(uint32_t txPkts);
 
   private:
-    uint32_t m_RxBytes;           ///< reeive bytes
+    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
@@ -259,25 +259,25 @@
 }
 
 uint32_t
-RoutingStats::GetRxBytes()
+RoutingStats::GetRxBytes() const
 {
     return m_RxBytes;
 }
 
 uint32_t
-RoutingStats::GetCumulativeRxBytes()
+RoutingStats::GetCumulativeRxBytes() const
 {
     return m_cumulativeRxBytes;
 }
 
 uint32_t
-RoutingStats::GetRxPkts()
+RoutingStats::GetRxPkts() const
 {
     return m_RxPkts;
 }
 
 uint32_t
-RoutingStats::GetCumulativeRxPkts()
+RoutingStats::GetCumulativeRxPkts() const
 {
     return m_cumulativeRxPkts;
 }
@@ -309,25 +309,25 @@
 }
 
 uint32_t
-RoutingStats::GetTxBytes()
+RoutingStats::GetTxBytes() const
 {
     return m_TxBytes;
 }
 
 uint32_t
-RoutingStats::GetCumulativeTxBytes()
+RoutingStats::GetCumulativeTxBytes() const
 {
     return m_cumulativeTxBytes;
 }
 
 uint32_t
-RoutingStats::GetTxPkts()
+RoutingStats::GetTxPkts() const
 {
     return m_TxPkts;
 }
 
 uint32_t
-RoutingStats::GetCumulativeTxPkts()
+RoutingStats::GetCumulativeTxPkts() const
 {
     return m_cumulativeTxPkts;
 }
@@ -397,7 +397,7 @@
      * 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 dump routing tables at t=5 seconds (0=no;1=yes)
+     * \param routingTables whether to dump routing tables at t=5 seconds
      */
     void Install(NodeContainer& c,
                  NetDeviceContainer& d,
@@ -405,7 +405,7 @@
                  double totalTime,
                  int protocol,
                  uint32_t nSinks,
-                 int routingTables);
+                 bool routingTables);
 
     /**
      * \brief Trace the receipt of an on-off-application generated packet
@@ -422,9 +422,9 @@
 
     /**
      * \brief Enable/disable logging
-     * \param log non-zero to enable logging
+     * \param log whether to enable logging
      */
-    void SetLogging(int log);
+    void SetLogging(bool log);
 
   private:
     /**
@@ -448,7 +448,7 @@
     void SetupRoutingMessages(NodeContainer& c, Ipv4InterfaceContainer& adhocTxInterfaces);
 
     /**
-     * \brief Sets up a routing packet for tranmission
+     * \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
@@ -465,10 +465,10 @@
     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)
-    int m_routingTables;        ///< dump routing table (at t=5 sec).  0=No, 1=Yes
+    bool m_routingTables;       ///< dump routing table (at t=5 sec).  0=No, 1=Yes
     RoutingStats routingStats;  ///< routing statistics
     std::string m_protocolName; ///< protocol name
-    int m_log;                  ///< log
+    bool m_log;                 ///< log
 };
 
 NS_OBJECT_ENSURE_REGISTERED(RoutingHelper);
@@ -486,8 +486,8 @@
       m_protocol(0),
       m_port(9),
       m_nSinks(0),
-      m_routingTables(0),
-      m_log(0)
+      m_routingTables(false),
+      m_log(false)
 {
 }
 
@@ -502,7 +502,7 @@
                        double totalTime,
                        int protocol,
                        uint32_t nSinks,
-                       int routingTables)
+                       bool routingTables)
 {
     m_TotalSimTime = totalTime;
     m_protocol = protocol;
@@ -547,25 +547,25 @@
         m_protocolName = "NONE";
         break;
     case 1:
-        if (m_routingTables != 0)
+        if (m_routingTables)
         {
-            olsr.PrintRoutingTableAllAt(rtt, rtw);
+            Ipv4RoutingHelper::PrintRoutingTableAllAt(rtt, rtw);
         }
         list.Add(olsr, 100);
         m_protocolName = "OLSR";
         break;
     case 2:
-        if (m_routingTables != 0)
+        if (m_routingTables)
         {
-            aodv.PrintRoutingTableAllAt(rtt, rtw);
+            Ipv4RoutingHelper::PrintRoutingTableAllAt(rtt, rtw);
         }
         list.Add(aodv, 100);
         m_protocolName = "AODV";
         break;
     case 3:
-        if (m_routingTables != 0)
+        if (m_routingTables)
         {
-            dsdv.PrintRoutingTableAllAt(rtt, rtw);
+            ns3::DsdvHelper::PrintRoutingTableAllAt(rtt, rtw);
         }
         list.Add(dsdv, 100);
         m_protocolName = "DSDV";
@@ -590,7 +590,7 @@
         dsrMain.Install(dsr, c);
     }
 
-    if (m_log != 0)
+    if (m_log)
     {
         NS_LOG_UNCOND("Routing Setup for " << m_protocolName);
     }
@@ -673,7 +673,7 @@
         uint32_t RxRoutingBytes = packet->GetSize();
         GetRoutingStats().IncRxBytes(RxRoutingBytes);
         GetRoutingStats().IncRxPkts();
-        if (m_log != 0)
+        if (m_log)
         {
             NS_LOG_UNCOND(m_protocolName + " " +
                           PrintReceivedRoutingPacket(socket, packet, srcAddress));
@@ -695,7 +695,7 @@
 }
 
 void
-RoutingHelper::SetLogging(int log)
+RoutingHelper::SetLogging(bool log)
 {
     m_log = log;
 }
@@ -728,10 +728,10 @@
      * (this includes MAC/PHY overhead)
      * \return the number of bytes transmitted
      */
-    uint32_t GetTxBytes();
+    uint32_t GetTxBytes() const;
 
     /**
-     * \brief Callback signiture for Phy/Tx trace
+     * \brief Callback signature for Phy/Tx trace
      * \param context this object
      * \param packet packet transmitted
      * \param mode wifi mode
@@ -745,14 +745,14 @@
                     uint8_t txPower);
 
     /**
-     * \brief Callback signiture for Phy/TxDrop
+     * \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 signiture for Phy/RxDrop
+     * \brief Callback signature for Phy/RxDrop
      * \param context this object
      * \param packet the rx packet being dropped
      * \param reason the reason for the drop
@@ -814,7 +814,7 @@
 }
 
 uint32_t
-WifiPhyStats::GetTxBytes()
+WifiPhyStats::GetTxBytes() const
 {
     return m_phyTxBytes;
 }
@@ -1033,7 +1033,7 @@
 ConfigStoreHelper::SaveConfig(std::string configFilename)
 {
     // only save if a non-empty filename has been specified
-    if (configFilename != "")
+    if (!configFilename.empty())
     {
         // Output config store to txt format
         Config::SetDefault("ns3::ConfigStore::Filename", StringValue(configFilename));
@@ -1227,36 +1227,35 @@
     int m_nodePause;                            ///< in s
     uint32_t m_wavePacketSize;                  ///< bytes
     double m_waveInterval;                      ///< seconds
-    int m_verbose;                              ///< verbose
+    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
-    int m_routingTables;                        ///< routing tables
-    int m_asciiTrace;                           ///< ascii trace
-    int m_pcap;                                 ///< PCAP
+    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 configi 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
-    int m_log;                          ///< log
-    /// used to get consistent random numbers across scenarios
-    int64_t m_streamIndex;
-    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
+    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
@@ -1291,16 +1290,16 @@
       m_nodePause(0),
       m_wavePacketSize(200),
       m_waveInterval(0.1),
-      m_verbose(0),
+      m_verbose(false),
       m_scenario(1),
       m_gpsAccuracyNs(40),
       m_txMaxDelayMs(10),
-      m_routingTables(0),
-      m_asciiTrace(0),
-      m_pcap(0),
+      m_routingTables(false),
+      m_asciiTrace(false),
+      m_pcap(false),
       m_loadConfigFilename("load-config.txt"),
       m_saveConfigFilename(""),
-      m_log(0),
+      m_log(false),
       m_streamIndex(0),
       m_adhocTxNodes(),
       m_txSafetyRange1(50.0),
@@ -1320,9 +1319,8 @@
     m_wifiPhyStats = CreateObject<WifiPhyStats>();
     m_routingHelper = CreateObject<RoutingHelper>();
 
-    // set to non-zero value to enable
     // simply uncond logging during simulation run
-    m_log = 1;
+    m_log = true;
 }
 
 void
@@ -1347,9 +1345,9 @@
 
 /// Trace mobility 1=yes;0=no
 static ns3::GlobalValue g_traceMobility("VRCtraceMobility",
-                                        "Trace mobility 1=yes;0=no",
-                                        ns3::UintegerValue(0),
-                                        ns3::MakeUintegerChecker<uint32_t>());
+                                        "Enable trace mobility",
+                                        ns3::BooleanValue(false),
+                                        ns3::MakeBooleanChecker());
 
 /// Routing protocol
 static ns3::GlobalValue g_protocol("VRCprotocol",
@@ -1407,9 +1405,9 @@
 
 /// Verbose 0=no;1=yes
 static ns3::GlobalValue g_verbose("VRCverbose",
-                                  "Verbose 0=no;1=yes",
-                                  ns3::UintegerValue(0),
-                                  ns3::MakeUintegerChecker<uint32_t>());
+                                  "Enable verbose",
+                                  ns3::BooleanValue(false),
+                                  ns3::MakeBooleanChecker());
 
 /// Scenario
 static ns3::GlobalValue g_scenario("VRCscenario",
@@ -1419,21 +1417,21 @@
 
 /// Dump routing tables at t=5 seconds 0=no;1=yes
 static ns3::GlobalValue g_routingTables("VRCroutingTables",
-                                        "Dump routing tables at t=5 seconds 0=no;1=yes",
-                                        ns3::UintegerValue(0),
-                                        ns3::MakeUintegerChecker<uint32_t>());
+                                        "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 0=no;1=yes",
-                                     ns3::UintegerValue(0),
-                                     ns3::MakeUintegerChecker<uint32_t>());
+                                     "Dump ASCII trace",
+                                     ns3::BooleanValue(false),
+                                     ns3::MakeBooleanChecker());
 
 /// Generate PCAP files 0=no;1=yes
 static ns3::GlobalValue g_pcap("VRCpcap",
-                               "Generate PCAP files 0=no;1=yes",
-                               ns3::UintegerValue(0),
-                               ns3::MakeUintegerChecker<uint32_t>());
+                               "Generate PCAP files",
+                               ns3::BooleanValue(false),
+                               ns3::MakeBooleanChecker());
 
 /// Simulation start time for capturing cumulative BSM
 static ns3::GlobalValue g_cumulativeBsmCaptureStart(
@@ -1732,7 +1730,7 @@
         mac_phy_oh = (double)(totalPhyBytes - totalAppBytes) / (double)totalPhyBytes;
     }
 
-    if (m_log != 0)
+    if (m_log)
     {
         NS_LOG_UNCOND("BSM_PDR1=" << bsm_pdr1 << " BSM_PDR2=" << bsm_pdr2
                                   << " BSM_PDR3=" << bsm_pdr3 << " BSM_PDR4=" << bsm_pdr4
@@ -1834,7 +1832,7 @@
 
     std::ofstream out(m_CSVfileName, std::ios::app);
 
-    if (m_log != 0)
+    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
@@ -1885,6 +1883,7 @@
     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
@@ -1895,8 +1894,8 @@
     m_port = uintegerValue.Get();
     GlobalValue::GetValueByName("VRCnSinks", uintegerValue);
     m_nSinks = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCtraceMobility", uintegerValue);
-    m_traceMobility = uintegerValue.Get();
+    GlobalValue::GetValueByName("VRCtraceMobility", booleanValue);
+    m_traceMobility = booleanValue.Get();
     GlobalValue::GetValueByName("VRCprotocol", uintegerValue);
     m_protocol = uintegerValue.Get();
     GlobalValue::GetValueByName("VRClossModel", uintegerValue);
@@ -1915,16 +1914,16 @@
     m_nodePause = uintegerValue.Get();
     GlobalValue::GetValueByName("VRCwavePacketSize", uintegerValue);
     m_wavePacketSize = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCverbose", uintegerValue);
-    m_verbose = uintegerValue.Get();
+    GlobalValue::GetValueByName("VRCverbose", booleanValue);
+    m_verbose = booleanValue.Get();
     GlobalValue::GetValueByName("VRCscenario", uintegerValue);
     m_scenario = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCroutingTables", uintegerValue);
-    m_routingTables = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCasciiTrace", uintegerValue);
-    m_asciiTrace = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCpcap", uintegerValue);
-    m_pcap = 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();
 
@@ -1987,7 +1986,7 @@
 
     g_port.SetValue(UintegerValue(m_port));
     g_nSinks.SetValue(UintegerValue(m_nSinks));
-    g_traceMobility.SetValue(UintegerValue(m_traceMobility));
+    g_traceMobility.SetValue(BooleanValue(m_traceMobility));
     g_protocol.SetValue(UintegerValue(m_protocol));
     g_lossModel.SetValue(UintegerValue(m_lossModel));
     g_fading.SetValue(UintegerValue(m_fading));
@@ -1997,11 +1996,11 @@
     g_nodeSpeed.SetValue(UintegerValue(m_nodeSpeed));
     g_nodePause.SetValue(UintegerValue(m_nodePause));
     g_wavePacketSize.SetValue(UintegerValue(m_wavePacketSize));
-    g_verbose.SetValue(UintegerValue(m_verbose));
+    g_verbose.SetValue(BooleanValue(m_verbose));
     g_scenario.SetValue(UintegerValue(m_scenario));
-    g_routingTables.SetValue(UintegerValue(m_routingTables));
-    g_asciiTrace.SetValue(UintegerValue(m_asciiTrace));
-    g_pcap.SetValue(UintegerValue(m_pcap));
+    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));
@@ -2067,7 +2066,7 @@
     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", "0=quiet;1=verbose", m_verbose);
+    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);
@@ -2086,9 +2085,9 @@
     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", "Dump routing tables at t=5 seconds", m_routingTables);
-    cmd.AddValue("asciiTrace", "Dump ASCII Trace data", m_asciiTrace);
-    cmd.AddValue("pcap", "Create PCAP files for all nodes", m_pcap);
+    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);
@@ -2256,7 +2255,7 @@
     wifiChannel.SetPropagationDelay("ns3::ConstantSpeedPropagationDelayModel");
     if (m_lossModel == 3)
     {
-        // two-ray requires antenna height (else defaults to Friss)
+        // two-ray requires antenna height (else defaults to Friis)
         wifiChannel.AddPropagationLoss(m_lossModelName,
                                        "Frequency",
                                        DoubleValue(freq),
@@ -2294,9 +2293,9 @@
     Wifi80211pHelper wifi80211p = Wifi80211pHelper::Default();
     if (m_verbose)
     {
-        wifi80211p.EnableLogComponents(); // Turn on all Wifi 802.11p logging
+        Wifi80211pHelper::EnableLogComponents(); // Turn on all Wifi 802.11p logging
         // likewise, turn on WAVE PHY logging
-        waveHelper.EnableLogComponents();
+        WaveHelper::EnableLogComponents();
     }
 
     WifiHelper wifi;
@@ -2350,14 +2349,14 @@
         m_adhocTxDevices = wifi.Install(wifiPhy, wifiMac, m_adhocTxNodes);
     }
 
-    if (m_asciiTrace != 0)
+    if (m_asciiTrace)
     {
         AsciiTraceHelper ascii;
         Ptr<OutputStreamWrapper> osw = ascii.CreateFileStream(m_trName + ".tr");
         wifiPhy.EnableAsciiAll(osw);
         wavePhy.EnableAsciiAll(osw);
     }
-    if (m_pcap != 0)
+    if (m_pcap)
     {
         wifiPhy.EnablePcapAll("vanet-routing-compare-pcap");
         wavePhy.EnablePcapAll("vanet-routing-compare-pcap");
@@ -2496,4 +2495,6 @@
 {
     VanetRoutingExperiment experiment;
     experiment.Simulate(argc, argv);
+
+    return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/examples/wave-simple-80211p.cc ns-3.38/src/wave/examples/wave-simple-80211p.cc
--- ns-3.37/src/wave/examples/wave-simple-80211p.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/examples/wave-simple-80211p.cc	2023-03-16 20:54:23.844869400 -0700
@@ -146,7 +146,7 @@
     Wifi80211pHelper wifi80211p = Wifi80211pHelper::Default();
     if (verbose)
     {
-        wifi80211p.EnableLogComponents(); // Turn on all Wifi 802.11p logging
+        Wifi80211pHelper::EnableLogComponents(); // Turn on all Wifi 802.11p logging
     }
 
     wifi80211p.SetRemoteStationManager("ns3::ConstantRateWifiManager",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/helper/wave-bsm-stats.cc ns-3.38/src/wave/helper/wave-bsm-stats.cc
--- ns-3.37/src/wave/helper/wave-bsm-stats.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/helper/wave-bsm-stats.cc	2023-03-16 20:54:23.844869400 -0700
@@ -32,7 +32,7 @@
     : m_wavePktSendCount(0),
       m_waveByteSendCount(0),
       m_wavePktReceiveCount(0),
-      m_log(0)
+      m_log(false)
 {
     m_wavePktExpectedReceiveCounts.resize(10, 0);
     m_wavePktInCoverageReceiveCounts.resize(10, 0);
@@ -58,7 +58,7 @@
 }
 
 int
-WaveBsmStats::GetTxPktCount()
+WaveBsmStats::GetTxPktCount() const
 {
     return m_wavePktSendCount;
 }
@@ -84,7 +84,7 @@
 }
 
 int
-WaveBsmStats::GetRxPktCount()
+WaveBsmStats::GetRxPktCount() const
 {
     return m_wavePktReceiveCount;
 }
@@ -120,7 +120,7 @@
 }
 
 int
-WaveBsmStats::GetTxByteCount()
+WaveBsmStats::GetTxByteCount() const
 {
     return m_waveByteSendCount;
 }
@@ -170,13 +170,13 @@
 }
 
 void
-WaveBsmStats::SetLogging(int log)
+WaveBsmStats::SetLogging(bool log)
 {
     m_log = log;
 }
 
-int
-WaveBsmStats::GetLogging()
+bool
+WaveBsmStats::GetLogging() const
 {
     return m_log;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/helper/wave-bsm-stats.h ns-3.38/src/wave/helper/wave-bsm-stats.h
--- ns-3.37/src/wave/helper/wave-bsm-stats.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/helper/wave-bsm-stats.h	2023-03-16 20:54:23.844869400 -0700
@@ -70,7 +70,7 @@
      * \brief Returns the count of transmitted packets
      * \return count of packets transmitted
      */
-    int GetTxPktCount();
+    int GetTxPktCount() const;
 
     /*
      * Note:
@@ -129,7 +129,7 @@
      * \brief Returns the count of packets received
      * \return the count of packets received
      */
-    int GetRxPktCount();
+    int GetRxPktCount() const;
 
     /**
      * \brief Returns the count of expected packets received within range(index)
@@ -190,7 +190,7 @@
      * not include MAC/PHY overhead
      * \return number of bytes of application-data transmitted
      */
-    int GetTxByteCount();
+    int GetTxByteCount() const;
 
     /**
      * \brief Returns the BSM Packet Delivery Ratio (PDR)
@@ -212,26 +212,26 @@
 
     /**
      * \brief Enables/disables logging
-     * \param log
+     * \param log logging state
      */
-    void SetLogging(int log);
+    void SetLogging(bool log);
 
     /**
      * \brief Gets logging state
      * \return logging state
      */
-    int GetLogging();
+    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 ceoverage receive counts
+    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
-    int m_log;                                            ///< log
+    bool m_log;                                           ///< logging state
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/helper/wave-helper.cc ns-3.38/src/wave/helper/wave-helper.cc
--- ns-3.37/src/wave/helper/wave-helper.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/helper/wave-helper.cc	2023-03-16 20:54:23.844869400 -0700
@@ -91,7 +91,7 @@
                                Ptr<const Packet> p,
                                double snr,
                                WifiMode mode,
-                               enum WifiPreamble preamble)
+                               WifiPreamble preamble)
 {
     NS_LOG_FUNCTION(stream << context << p << snr << mode << preamble);
     *stream->GetStream() << "r " << Simulator::Now().GetSeconds() << " " << context << " " << *p
@@ -111,7 +111,7 @@
                                   Ptr<const Packet> p,
                                   double snr,
                                   WifiMode mode,
-                                  enum WifiPreamble preamble)
+                                  WifiPreamble preamble)
 {
     NS_LOG_FUNCTION(stream << p << snr << mode << preamble);
     *stream->GetStream() << "r " << Simulator::Now().GetSeconds() << " " << *p << std::endl;
@@ -146,8 +146,7 @@
     }
 
     std::vector<Ptr<WifiPhy>> phys = device->GetPhys();
-    NS_ABORT_MSG_IF(phys.size() == 0,
-                    "EnablePcapInternal(): Phy layer in WaveNetDevice must be set");
+    NS_ABORT_MSG_IF(phys.empty(), "EnablePcapInternal(): Phy layer in WaveNetDevice must be set");
 
     PcapHelper pcapHelper;
 
@@ -303,7 +302,7 @@
 void
 WaveHelper::CreateMacForChannel(std::vector<uint32_t> channelNumbers)
 {
-    if (channelNumbers.size() == 0)
+    if (channelNumbers.empty())
     {
         NS_FATAL_ERROR("the WAVE MAC entities is at least one");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/helper/wifi-80211p-helper.cc ns-3.38/src/wave/helper/wifi-80211p-helper.cc
--- ns-3.37/src/wave/helper/wifi-80211p-helper.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/helper/wifi-80211p-helper.cc	2023-03-16 20:54:23.844869400 -0700
@@ -56,7 +56,7 @@
 }
 
 void
-Wifi80211pHelper::SetStandard(enum WifiStandard standard)
+Wifi80211pHelper::SetStandard(WifiStandard standard)
 {
     if (standard == WIFI_STANDARD_80211p)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/helper/wifi-80211p-helper.h ns-3.38/src/wave/helper/wifi-80211p-helper.h
--- ns-3.37/src/wave/helper/wifi-80211p-helper.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/helper/wifi-80211p-helper.h	2023-03-16 20:54:51.628957000 -0700
@@ -58,7 +58,7 @@
      * Users can only configure 802.11p with 10MHz or 5 MHz channel bandwidth.
      * The default 802.11p standard uses 10MHz.
      */
-    void SetStandard(enum WifiStandard standard) override;
+    void SetStandard(WifiStandard standard) override;
 
     /**
      * \param phy the PHY helper to create PHY objects
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/channel-coordinator.h ns-3.38/src/wave/model/channel-coordinator.h
--- ns-3.37/src/wave/model/channel-coordinator.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/channel-coordinator.h	2023-03-16 20:54:23.844869400 -0700
@@ -242,7 +242,7 @@
     Time m_schi; ///< SchInterval
     Time m_gi;   ///< GuardInterval
 
-    /// Listeners typdef
+    /// Listeners typedef
     typedef std::vector<Ptr<ChannelCoordinationListener>> Listeners;
     /// Listeners iterator typedef
     typedef std::vector<Ptr<ChannelCoordinationListener>>::iterator ListenersI;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/channel-scheduler.h ns-3.38/src/wave/model/channel-scheduler.h
--- ns-3.37/src/wave/model/channel-scheduler.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/channel-scheduler.h	2023-03-16 20:54:23.844869400 -0700
@@ -182,7 +182,7 @@
      * \param channelNumber the specified channel number
      * \return  the type of current assigned channel access for the specific channel.
      */
-    virtual enum ChannelAccess GetAssignedAccessType(uint32_t channelNumber) const = 0;
+    virtual ChannelAccess GetAssignedAccessType(uint32_t channelNumber) const = 0;
 
     /**
      * \param schInfo the request information for assigning SCH access.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/default-channel-scheduler.h ns-3.38/src/wave/model/default-channel-scheduler.h
--- ns-3.37/src/wave/model/default-channel-scheduler.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/default-channel-scheduler.h	2023-03-16 20:54:23.844869400 -0700
@@ -49,7 +49,7 @@
      * \param channelNumber the specified channel number
      * \return  the type of current assigned channel access for the specific channel.
      */
-    enum ChannelAccess GetAssignedAccessType(uint32_t channelNumber) const override;
+    ChannelAccess GetAssignedAccessType(uint32_t channelNumber) const override;
 
     /**
      * Notify CCH slot start
@@ -126,10 +126,10 @@
      *   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
-    enum ChannelAccess m_channelAccess; ///< channel access
+    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
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/higher-tx-tag.cc ns-3.38/src/wave/model/higher-tx-tag.cc
--- ns-3.37/src/wave/model/higher-tx-tag.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/higher-tx-tag.cc	2023-03-16 20:54:23.844869400 -0700
@@ -55,6 +55,9 @@
 
 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);
@@ -80,14 +83,16 @@
 HigherLayerTxVectorTag::GetSerializedSize() const
 {
     NS_LOG_FUNCTION(this);
-    return (sizeof(WifiTxVector) + 1);
+    return (sizeof(WifiMode) + 1 + 2 + 1);
 }
 
 void
 HigherLayerTxVectorTag::Serialize(TagBuffer i) const
 {
     NS_LOG_FUNCTION(this << &i);
-    i.Write((uint8_t*)&m_txVector, sizeof(WifiTxVector));
+    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));
 }
 
@@ -95,8 +100,13 @@
 HigherLayerTxVectorTag::Deserialize(TagBuffer i)
 {
     NS_LOG_FUNCTION(this << &i);
-    i.Read((uint8_t*)&m_txVector, sizeof(WifiTxVector));
+    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
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/higher-tx-tag.h ns-3.38/src/wave/model/higher-tx-tag.h
--- ns-3.37/src/wave/model/higher-tx-tag.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/higher-tx-tag.h	2023-03-16 20:54:23.844869400 -0700
@@ -78,6 +78,9 @@
 
   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
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/ocb-wifi-mac.cc ns-3.38/src/wave/model/ocb-wifi-mac.cc
--- ns-3.37/src/wave/model/ocb-wifi-mac.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/ocb-wifi-mac.cc	2023-03-16 20:54:23.844869400 -0700
@@ -342,7 +342,7 @@
 }
 
 void
-OcbWifiMac::ConfigureEdca(uint32_t cwmin, uint32_t cwmax, uint32_t aifsn, enum AcIndex ac)
+OcbWifiMac::ConfigureEdca(uint32_t cwmin, uint32_t cwmax, uint32_t aifsn, AcIndex ac)
 {
     NS_LOG_FUNCTION(this << cwmin << cwmax << aifsn << ac);
     Ptr<Txop> dcf;
@@ -413,7 +413,7 @@
 }
 
 void
-OcbWifiMac::ConfigureStandard(enum WifiStandard standard)
+OcbWifiMac::ConfigureStandard(WifiStandard standard)
 {
     NS_LOG_FUNCTION(this << standard);
     NS_ASSERT(standard == WIFI_STANDARD_80211p);
@@ -488,7 +488,7 @@
 }
 
 void
-OcbWifiMac::CancleTx(enum AcIndex ac)
+OcbWifiMac::CancleTx(AcIndex ac)
 {
     NS_LOG_FUNCTION(this << ac);
     Ptr<QosTxop> queue = GetQosTxop(ac);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/ocb-wifi-mac.h ns-3.38/src/wave/model/ocb-wifi-mac.h
--- ns-3.37/src/wave/model/ocb-wifi-mac.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/ocb-wifi-mac.h	2023-03-16 20:54:23.844869400 -0700
@@ -63,13 +63,13 @@
      * \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 vender specific action frame.
+     * 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 vender specific action frame has been received and
+     * \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.
      */
@@ -136,7 +136,7 @@
      *
      * configure EDCA queue parameters
      */
-    void ConfigureEdca(uint32_t cwmin, uint32_t cwmax, uint32_t aifsn, enum AcIndex ac);
+    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
     /**
@@ -169,7 +169,7 @@
      * To support MAC extension for multiple channel operation,
      * Cancel transmit operation for internal queue associated with a specified Access Category.
      */
-    void CancleTx(enum AcIndex ac);
+    void CancleTx(AcIndex ac);
     /**
      * To support MAC extension for multiple channel operation,
      * Reset current MAC entity and flush its internal queues.
@@ -183,7 +183,7 @@
     void SetWifiPhy(Ptr<WifiPhy> phy);
 
     // Inherited from base class
-    void ConfigureStandard(enum WifiStandard standard) override;
+    void ConfigureStandard(WifiStandard standard) override;
 
   protected:
     void DoDispose() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/vendor-specific-action.cc ns-3.38/src/wave/model/vendor-specific-action.cc
--- ns-3.37/src/wave/model/vendor-specific-action.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/vendor-specific-action.cc	2023-03-16 20:54:23.844869400 -0700
@@ -107,7 +107,7 @@
 }
 
 void
-OrganizationIdentifier::SetType(enum OrganizationIdentifierType type)
+OrganizationIdentifier::SetType(OrganizationIdentifierType type)
 {
     NS_LOG_FUNCTION(this);
     m_type = type;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/vendor-specific-action.h ns-3.38/src/wave/model/vendor-specific-action.h
--- ns-3.37/src/wave/model/vendor-specific-action.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/vendor-specific-action.h	2023-03-16 20:54:23.844869400 -0700
@@ -89,11 +89,11 @@
     /**
      * \param type set the type of current OrganizationIdentifier
      */
-    void SetType(enum OrganizationIdentifierType type);
+    void SetType(OrganizationIdentifierType type);
     /**
      * \returns whether this OrganizationIdentifier is OUI24 or OUI36.
      */
-    enum OrganizationIdentifierType GetType() const;
+    OrganizationIdentifierType GetType() const;
 
     // below methods will be called by VendorSpecificActionHeader
     /**
@@ -120,8 +120,8 @@
     friend std::ostream& operator<<(std::ostream& os, const OrganizationIdentifier& oi);
     friend std::istream& operator>>(std::istream& is, const OrganizationIdentifier& oi);
 
-    enum OrganizationIdentifierType m_type; ///< OI type
-    uint8_t m_oi[5];                        ///< organization identifier
+    OrganizationIdentifierType m_type; ///< OI type
+    uint8_t m_oi[5];                   ///< organization identifier
 };
 
 ATTRIBUTE_HELPER_HEADER(OrganizationIdentifier);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/vsa-manager.cc ns-3.38/src/wave/model/vsa-manager.cc
--- ns-3.37/src/wave/model/vsa-manager.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/vsa-manager.cc	2023-03-16 20:54:23.844869400 -0700
@@ -124,7 +124,7 @@
 }
 
 void
-VsaManager::DoSendVsa(enum VsaTransmitInterval interval,
+VsaManager::DoSendVsa(VsaTransmitInterval interval,
                       uint32_t channel,
                       Ptr<Packet> vsc,
                       OrganizationIdentifier oi,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/vsa-manager.h ns-3.38/src/wave/model/vsa-manager.h
--- ns-3.37/src/wave/model/vsa-manager.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/vsa-manager.h	2023-03-16 20:54:23.844869400 -0700
@@ -62,13 +62,13 @@
  */
 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
-    enum VsaTransmitInterval sendInterval; ///< send interval
+    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
@@ -86,7 +86,7 @@
             Ptr<Packet> vscPacket,
             uint32_t channel,
             uint8_t repeat,
-            enum VsaTransmitInterval interval)
+            VsaTransmitInterval interval)
         : peer(peer),
           oi(identifier),
           managementId(manageId),
@@ -183,13 +183,13 @@
     /// VsaWork structure
     struct VsaWork
     {
-        Mac48Address peer;                     ///< peer
-        OrganizationIdentifier oi;             ///< OI
-        Ptr<Packet> vsc;                       ///< VSC
-        uint32_t channelNumber;                ///< channel number
-        enum VsaTransmitInterval sentInterval; ///< VSA transmit interval
-        Time repeatPeriod;                     ///< repeat period
-        EventId repeat;                        ///< repeat ID
+        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
     };
 
     /**
@@ -205,7 +205,7 @@
      * \param oi the Organization Identifier for VSA frame
      * \param peer the destination address
      */
-    void DoSendVsa(enum VsaTransmitInterval interval,
+    void DoSendVsa(VsaTransmitInterval interval,
                    uint32_t channel,
                    Ptr<Packet> vsc,
                    OrganizationIdentifier oi,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/wave-net-device.cc ns-3.38/src/wave/model/wave-net-device.cc
--- ns-3.37/src/wave/model/wave-net-device.cc	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/wave-net-device.cc	2023-03-16 20:54:23.844869400 -0700
@@ -144,7 +144,7 @@
 WaveNetDevice::DoInitialize()
 {
     NS_LOG_FUNCTION(this);
-    if (m_phyEntities.size() == 0)
+    if (m_phyEntities.empty())
     {
         NS_FATAL_ERROR("there is no PHY entity in this WAVE device");
     }
@@ -153,7 +153,7 @@
         Ptr<WifiPhy> phy = (*i);
         phy->Initialize();
     }
-    if (m_macEntities.size() == 0)
+    if (m_macEntities.empty())
     {
         NS_FATAL_ERROR("there is no MAC entity in this WAVE device");
     }
@@ -469,7 +469,7 @@
 }
 
 void
-WaveNetDevice::CancelTx(uint32_t channelNumber, enum AcIndex ac)
+WaveNetDevice::CancelTx(uint32_t channelNumber, AcIndex ac)
 {
     if (IsAvailableChannel(channelNumber))
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/model/wave-net-device.h ns-3.38/src/wave/model/wave-net-device.h
--- ns-3.37/src/wave/model/wave-net-device.h	2022-11-01 17:37:24.731395700 -0700
+++ ns-3.38/src/wave/model/wave-net-device.h	2023-03-16 20:54:23.844869400 -0700
@@ -319,7 +319,7 @@
      *
      * Cancel all transmissions with the particular category and channel number.
      */
-    void CancelTx(uint32_t channelNumber, enum AcIndex ac);
+    void CancelTx(uint32_t channelNumber, AcIndex ac);
 
     /**
      * \param packet packet sent from above down to Network Device
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wave/test/ocb-test-suite.cc ns-3.38/src/wave/test/ocb-test-suite.cc
--- ns-3.37/src/wave/test/ocb-test-suite.cc	2022-11-01 17:37:24.735396000 -0700
+++ ns-3.38/src/wave/test/ocb-test-suite.cc	2023-03-16 20:54:23.848869300 -0700
@@ -108,7 +108,7 @@
                       Ptr<const Packet> packet,
                       double snr,
                       WifiMode mode,
-                      enum WifiPreamble preamble);
+                      WifiPreamble preamble);
     /**
      * Phy transmit trace function
      * \param context the context
@@ -233,7 +233,7 @@
                                  Ptr<const Packet> packet,
                                  double snr,
                                  WifiMode mode,
-                                 enum WifiPreamble preamble)
+                                 WifiPreamble preamble)
 {
     if (phyrx_time == Time(0))
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/CMakeLists.txt ns-3.38/src/wifi/CMakeLists.txt
--- ns-3.37/src/wifi/CMakeLists.txt	2022-11-01 17:37:24.735396000 -0700
+++ ns-3.38/src/wifi/CMakeLists.txt	2023-03-16 20:54:23.848869300 -0700
@@ -27,6 +27,9 @@
     model/edca-parameter-set.cc
     model/eht/eht-capabilities.cc
     model/eht/eht-configuration.cc
+    model/eht/eht-frame-exchange-manager.cc
+    model/eht/eht-operation.cc
+    model/eht/tid-to-link-mapping-element.cc
     model/eht/eht-phy.cc
     model/eht/eht-ppdu.cc
     model/eht/multi-link-element.cc
@@ -173,6 +176,9 @@
     model/edca-parameter-set.h
     model/eht/eht-capabilities.h
     model/eht/eht-configuration.h
+    model/eht/eht-frame-exchange-manager.h
+    model/eht/eht-operation.h
+    model/eht/tid-to-link-mapping-element.h
     model/eht/eht-phy.h
     model/eht/eht-ppdu.h
     model/eht/multi-link-element.h
@@ -335,9 +341,11 @@
     test/wifi-phy-reception-test.cc
     test/wifi-phy-thresholds-test.cc
     test/wifi-primary-channels-test.cc
+    test/wifi-ru-allocation-test.cc
     test/wifi-channel-switching-test.cc
     test/wifi-test.cc
     test/wifi-transmit-mask-test.cc
     test/wifi-txop-test.cc
     test/wifi-phy-cca-test.cc
+    test/wifi-non-ht-dup-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/doc/source/wifi-changelog.rst ns-3.38/src/wifi/doc/source/wifi-changelog.rst
--- ns-3.37/src/wifi/doc/source/wifi-changelog.rst	2022-11-01 17:37:24.735396000 -0700
+++ ns-3.38/src/wifi/doc/source/wifi-changelog.rst	2023-03-16 20:54:23.848869300 -0700
@@ -22,12 +22,12 @@
 For instance, where pre-ns-3.36 code may have said::
 
     WifiPhyHelper phy;
-    phy.Set("ChannelNumber", UintegerValue (36));
+    phy.Set("ChannelNumber", UintegerValue(36));
 
 the equivalent new code is::
 
     WifiPhyHelper phy;
-    phy.Set ("ChannelSettings", StringValue ("{36, 20, BAND_5GHZ, 0}"));
+    phy.Set("ChannelSettings", StringValue("{36, 20, BAND_5GHZ, 0}"));
 
 which denotes that channel 36 is used on a 20 MHz channel in the 5GHz band,
 and because a larger channel width greater than 20 MHz is not being used, there
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/doc/source/wifi-design.rst ns-3.38/src/wifi/doc/source/wifi-design.rst
--- ns-3.37/src/wifi/doc/source/wifi-design.rst	2022-11-01 17:37:24.735396000 -0700
+++ ns-3.38/src/wifi/doc/source/wifi-design.rst	2023-03-16 20:54:23.848869300 -0700
@@ -1262,7 +1262,7 @@
 restrictions and informs the PHY object.
 
 The PHY keeps tracks of incoming requests from the MAC to get access to the channel.
-If a request is received and if PHY reset(s) indicating TX power limitations occured
+If a request is received and if PHY reset(s) indicating TX power limitations occurred
 before a packet was transmitted, the next packet to be transmitted will be sent with
 a reduced power. Otherwise, no TX power restrictions will be applied.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/doc/source/wifi-testing.rst ns-3.38/src/wifi/doc/source/wifi-testing.rst
--- ns-3.37/src/wifi/doc/source/wifi-testing.rst	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/doc/source/wifi-testing.rst	2023-03-16 20:54:23.852869300 -0700
@@ -262,7 +262,7 @@
 simulates an 802.11a adhoc ring scenario, with a PHY rate set to
 54 Mbit/s, and loop from 5 stations to 50 stations, by a step of
 5 stations. It generates a plt file, which allows user to quickly
-generate an eps file using gnuplot and vizualize the graph.
+generate an eps file using gnuplot and visualize the graph.
 
 ::
 
@@ -279,7 +279,7 @@
 11a, 11b or 11g currently supported), to select the PHY rate (in Mbit/s),
 as well as to choose between an adhoc or an infrastructure configuration.
 
-When run for 802.11g 6 Mbit/s in infrastucture mode, the output is:
+When run for 802.11g 6 Mbit/s in infrastructure mode, the output is:
 
 ::
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/doc/source/wifi-user.rst ns-3.38/src/wifi/doc/source/wifi-user.rst
--- ns-3.37/src/wifi/doc/source/wifi-user.rst	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/doc/source/wifi-user.rst	2023-03-16 20:54:23.852869300 -0700
@@ -58,14 +58,14 @@
 default values in the simulator, and infrastructure mode::
 
   NodeContainer wifiStaNode;
-  wifiStaNode.Create (10);   // Create 10 station node objects
+  wifiStaNode.Create(10);   // Create 10 station node objects
   NodeContainer wifiApNode;
-  wifiApNode.Create (1);   // Create 1 access point node object
+  wifiApNode.Create(1);   // Create 1 access point node object
 
   // Create a channel helper and phy helper, and then create the channel
-  YansWifiChannelHelper channel = YansWifiChannelHelper::Default ();
-  YansWifiPhyHelper phy = YansWifiPhyHelper::Default ();
-  phy.SetChannel (channel.Create ());
+  YansWifiChannelHelper channel = YansWifiChannelHelper::Default();
+  YansWifiPhyHelper phy = YansWifiPhyHelper::Default();
+  phy.SetChannel(channel.Create());
 
   // Create a WifiMacHelper, which is reused across STA and AP configurations
   WifiMacHelper mac;
@@ -74,16 +74,16 @@
   // and install Wifi devices.  Configure a Wifi standard to use, which
   // will align various parameters in the Phy and Mac to standard defaults.
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211n);
+  wifi.SetStandard(WIFI_STANDARD_80211n);
   // Declare NetDeviceContainers to hold the container returned by the helper
   NetDeviceContainer wifiStaDevices;
   NetDeviceContainer wifiApDevice;
 
   // Perform the installation
-  mac.SetType ("ns3::StaWifiMac");
-  wifiStaDevices = wifi.Install (phy, mac, wifiStaNodes);
-  mac.SetType ("ns3::ApWifiMac");
-  wifiApDevice = wifi.Install (phy, mac, wifiApNode);
+  mac.SetType("ns3::StaWifiMac");
+  wifiStaDevices = wifi.Install(phy, mac, wifiStaNodes);
+  mac.SetType("ns3::ApWifiMac");
+  wifiApDevice = wifi.Install(phy, mac, wifiApNode);
 
 At this point, the 11 nodes have Wi-Fi devices configured, attached to a
 common channel.  The rest of this section describes how additional
@@ -100,8 +100,8 @@
 
 Users will typically type code such as::
 
-  YansWifiChannelHelper wifiChannelHelper = YansWifiChannelHelper::Default ();
-  Ptr<Channel> wifiChannel = wifiChannelHelper.Create ();
+  YansWifiChannelHelper wifiChannelHelper = YansWifiChannelHelper::Default();
+  Ptr<Channel> wifiChannel = wifiChannelHelper.Create();
 
 to get the defaults.  Specifically, the default is a channel model with a
 propagation delay equal to a constant, the speed of light (``ns3::ConstantSpeedPropagationDelayModel``),
@@ -136,7 +136,7 @@
 typically::
 
   YansWifiPhyHelper wifiPhyHelper;
-  wifiPhyHelper.SetChannel (wifiChannel);
+  wifiPhyHelper.SetChannel(wifiChannel);
 
 The default YansWifiPhyHelper is configured with TableBasedErrorRateModel
 (``ns3::TableBasedErrorRateModel``). You can change the error rate model by
@@ -145,7 +145,7 @@
 Optionally, if pcap tracing is needed, a user may use the following
 command to enable pcap tracing::
 
-  YansWifiPhyHelper::SetPcapDataLinkType (enum SupportedPcapDataLinkTypes dlt)
+  YansWifiPhyHelper::SetPcapDataLinkType(enum SupportedPcapDataLinkTypes dlt)
 
 |ns3| supports RadioTap and Prism tracing extensions for 802.11.
 
@@ -156,43 +156,43 @@
 In order to enable 802.11n/ac/ax MIMO, the number of antennas as well as the number of supported spatial streams need to be configured.
 For example, this code enables MIMO with 2 antennas and 2 spatial streams::
 
- wifiPhyHelper.Set ("Antennas", UintegerValue (2));
- wifiPhyHelper.Set ("MaxSupportedTxSpatialStreams", UintegerValue (2));
- wifiPhyHelper.Set ("MaxSupportedRxSpatialStreams", UintegerValue (2));
+ wifiPhyHelper.Set("Antennas", UintegerValue(2));
+ wifiPhyHelper.Set("MaxSupportedTxSpatialStreams", UintegerValue(2));
+ wifiPhyHelper.Set("MaxSupportedRxSpatialStreams", UintegerValue(2));
 
 It is also possible to configure less streams than the number of antennas in order to benefit from diversity gain, and to define different MIMO capabilities for downlink and uplink.
 For example, this code configures a node with 3 antennas that supports 2 spatial streams in downstream and 1 spatial stream in upstream::
 
- wifiPhyHelper.Set ("Antennas", UintegerValue (3));
- wifiPhyHelper.Set ("MaxSupportedTxSpatialStreams", UintegerValue (2));
- wifiPhyHelper.Set ("MaxSupportedRxSpatialStreams", UintegerValue (1));
+ wifiPhyHelper.Set("Antennas", UintegerValue(3));
+ wifiPhyHelper.Set("MaxSupportedTxSpatialStreams", UintegerValue(2));
+ wifiPhyHelper.Set("MaxSupportedRxSpatialStreams", UintegerValue(1));
 
 802.11n PHY layer can support both 20 (default) or 40 MHz channel width, and 802.11ac/ax PHY layer can use either 20, 40, 80 (default) or 160 MHz channel width.  See below for further documentation on setting the frequency, channel width, and channel number.
 
 ::
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211ac);
-  wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager",
-                                "DataMode", StringValue ("VhtMcs9"),
-                                "ControlMode", StringValue ("VhtMcs0"));
+  wifi.SetStandard(WIFI_STANDARD_80211ac);
+  wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
+                               "DataMode", StringValue("VhtMcs9"),
+                               "ControlMode", StringValue("VhtMcs0"));
 
   //Install PHY and MAC
-  Ssid ssid = Ssid ("ns3-wifi");
+  Ssid ssid = Ssid("ns3-wifi");
   WifiMacHelper mac;
 
-  mac.SetType ("ns3::StaWifiMac",
-               "Ssid", SsidValue (ssid),
-               "ActiveProbing", BooleanValue (false));
+  mac.SetType("ns3::StaWifiMac",
+               "Ssid", SsidValue(ssid),
+               "ActiveProbing", BooleanValue(false));
 
   NetDeviceContainer staDevice;
-  staDevice = wifi.Install (phy, mac, wifiStaNode);
+  staDevice = wifi.Install(phy, mac, wifiStaNode);
 
-  mac.SetType ("ns3::ApWifiMac",
-               "Ssid", SsidValue (ssid));
+  mac.SetType("ns3::ApWifiMac",
+               "Ssid", SsidValue(ssid));
 
   NetDeviceContainer apDevice;
-  apDevice = wifi.Install (phy, mac, wifiApNode);
+  apDevice = wifi.Install(phy, mac, wifiApNode);
 
 .. _Channel-settings:
 
@@ -209,14 +209,14 @@
 
 ::
 
-  StringValue value ("{38, 40, BAND_5GHZ, 0}"));
+  StringValue value("{38, 40, BAND_5GHZ, 0}"));
 
 * Defining a TupleValue object to set the ``ChannelSettings`` attribute
 
 ::
 
   TupleValue<UintegerValue, UintegerValue, EnumValue, UintegerValue> value;
-  value.Set (WifiPhy::ChannelTuple {38, 40, WIFI_PHY_BAND_5GHZ, 0});
+  value.Set(WifiPhy::ChannelTuple {38, 40, WIFI_PHY_BAND_5GHZ, 0});
 
 In both cases, the operating channel will be channel 38 in the 5 GHz band, which
 has a width of 40 MHz, and the primary20 channel will be the 20 MHz subchannel
@@ -228,26 +228,26 @@
 
 ::
 
-  Config::SetDefault ("ns3::WifiPhy::ChannelSettings", StringValue ("{38, 40, BAND_5GHZ, 0}"));
+  Config::SetDefault("ns3::WifiPhy::ChannelSettings", StringValue("{38, 40, BAND_5GHZ, 0}"));
 
 * by setting an attribute value in the helper; e.g.
 
 ::
 
   TupleValue<UintegerValue, UintegerValue, EnumValue, UintegerValue> value;
-  value.Set (WifiPhy::ChannelTuple {38, 40, WIFI_PHY_BAND_5GHZ, 0});
-  YansWifiPhyHelper wifiPhyHelper = YansWifiPhyHelper::Default ();
-  wifiPhyHelper.Set ("ChannelSettings", value);
+  value.Set(WifiPhy::ChannelTuple {38, 40, WIFI_PHY_BAND_5GHZ, 0});
+  YansWifiPhyHelper wifiPhyHelper = YansWifiPhyHelper::Default();
+  wifiPhyHelper.Set("ChannelSettings", value);
 
-* by setting the WifiHelper::SetStandard (enum WifiStandard) method; and
+* by setting the WifiHelper::SetStandard(enum WifiStandard) method; and
 
 * by performing post-installation configuration of the option, either
   via a Ptr to the WifiPhy object, or through the Config namespace; e.g.:
 
 ::
 
-  Config::Set ("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phy/$ns3::WifiPhy/ChannelSettings",
-               StringValue ("{38, 40, BAND_5GHZ, 0}"));
+  Config::Set("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phy/$ns3::WifiPhy/ChannelSettings",
+              StringValue("{38, 40, BAND_5GHZ, 0}"));
 
 This section provides guidance on how to properly configure these settings.
 
@@ -311,27 +311,27 @@
 ::
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211ac);
+  wifi.SetStandard(WIFI_STANDARD_80211ac);
   YansWifiPhyHelper phyHelper;
-  phyHelper.Set ("ChannelSettings", StringValue ("{58, 0, BAND_5GHZ, 0}"));
+  phyHelper.Set("ChannelSettings", StringValue("{58, 0, BAND_5GHZ, 0}"));
   // channel width unspecified
   // -> it is set to 80 MHz (width of channel 58)
 
 ::
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211n);
+  wifi.SetStandard(WIFI_STANDARD_80211n);
   YansWifiPhyHelper phyHelper;
-  phyHelper.Set ("ChannelSettings", StringValue ("{0, 40, BAND_5GHZ, 0}"));
+  phyHelper.Set("ChannelSettings", StringValue("{0, 40, BAND_5GHZ, 0}"));
   // channel number unspecified
   // -> it is set to channel 38 (first 40 MHz channel in the 5GHz band)
 
 ::
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211ax);
+  wifi.SetStandard(WIFI_STANDARD_80211ax);
   YansWifiPhyHelper phyHelper;
-  phyHelper.Set ("ChannelSettings", StringValue ("{0, 0, BAND_2_4GHZ, 0}"));
+  phyHelper.Set("ChannelSettings", StringValue("{0, 0, BAND_2_4GHZ, 0}"));
   // both channel number and width unspecified
   // -> width set to 20 MHz (default width for 802.11ax in the 2.4 GHZ band)
   // -> channel number set to 1 (first 20 MHz channel in the 2.4 GHz band)
@@ -339,9 +339,9 @@
 ::
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211a);
+  wifi.SetStandard(WIFI_STANDARD_80211a);
   YansWifiPhyHelper phyHelper;
-  phyHelper.Set ("ChannelSettings", StringValue ("{0, 0, BAND_UNSPECIFIED, 0}"));
+  phyHelper.Set("ChannelSettings", StringValue("{0, 0, BAND_UNSPECIFIED, 0}"));
   // band, channel number and width unspecified
   // -> band is set to WIFI_PHY_BAND_5GHZ (default band for 802.11a)
   // -> width set to 20 MHz (default width for 802.11a in the 5 GHZ band)
@@ -458,7 +458,7 @@
 ::
 
   Ptr<WifiPhy> wifiPhy = ...;
-  wifiPhy->SetAttribute ("ChannelSettings", StringValue ("{1321, 20, BAND_5GHZ, 0}"));
+  wifiPhy->SetAttribute("ChannelSettings", StringValue("{1321, 20, BAND_5GHZ, 0}"));
 
 The known channel numbers are defined in the implementation file
 ``src/wifi/model/wifi-phy-operating-channel.cc``. Of course, this file may be edited
@@ -471,10 +471,10 @@
 ::
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211n);
+  wifi.SetStandard(WIFI_STANDARD_80211n);
   ...
   Ptr<WifiPhy> wifiPhy = ...;
-  wifiPhy->SetAttribute ("ChannelSettings", StringValue ("{14, 20, BAND_5GHZ, 0}"));
+  wifiPhy->SetAttribute("ChannelSettings", StringValue("{14, 20, BAND_5GHZ, 0}"));
 
 In the above, while channel number 14 is well-defined in practice for 802.11b
 only, it is for 2.4 GHz band, not 5 GHz band.
@@ -520,19 +520,19 @@
 will be a non-AP STA in an infrastructure network where the AP has SSID ``ns-3-ssid``::
 
     WifiMacHelper wifiMacHelper;
-    Ssid ssid = Ssid ("ns-3-ssid");
-    wifiMacHelper.SetType ("ns3::StaWifiMac",
-                          "Ssid", SsidValue (ssid),
-                          "ActiveProbing", BooleanValue (false));
+    Ssid ssid = Ssid("ns-3-ssid");
+    wifiMacHelper.SetType("ns3::StaWifiMac",
+                         "Ssid", SsidValue(ssid),
+                         "ActiveProbing", BooleanValue(false));
 
 The following code shows how to create an AP with QoS enabled::
 
   WifiMacHelper wifiMacHelper;
-  wifiMacHelper.SetType ("ns3::ApWifiMac",
-                         "Ssid", SsidValue (ssid),
-                         "QosSupported", BooleanValue (true),
-                         "BeaconGeneration", BooleanValue (true),
-                         "BeaconInterval", TimeValue (Seconds (2.5)));
+  wifiMacHelper.SetType("ns3::ApWifiMac",
+                        "Ssid", SsidValue(ssid),
+                        "QosSupported", BooleanValue(true),
+                        "BeaconGeneration", BooleanValue(true),
+                        "BeaconInterval", TimeValue(Seconds(2.5)));
 
 To create ad-hoc MAC instances, simply use ``ns3::AdhocWifiMac`` instead of ``ns3::StaWifiMac`` or ``ns3::ApWifiMac``.
 
@@ -555,12 +555,12 @@
 in an infrastructure network where the AP has SSID ``ns-3-ssid``::
 
     WifiMacHelper wifiMacHelper;
-    Ssid ssid = Ssid ("ns-3-ssid");
-    wifiMacHelper.SetType ("ns3::StaWifiMac",
-                          "Ssid", SsidValue (ssid),
-                          "QosSupported", BooleanValue (true),
-                          "BE_BlockAckThreshold", UintegerValue (2),
-                          "ActiveProbing", BooleanValue (false));
+    Ssid ssid = Ssid("ns-3-ssid");
+    wifiMacHelper.SetType("ns3::StaWifiMac",
+                         "Ssid", SsidValue(ssid),
+                         "QosSupported", BooleanValue(true),
+                         "BE_BlockAckThreshold", UintegerValue(2),
+                         "ActiveProbing", BooleanValue(false));
 
 For MAC instances that have 802.11n-style High Throughput (HT) and/or 802.11ac-style Very High Throughput (VHT) and/or 802.11ax-style High Efficiency (HE) support enabled,
 the ``ns3::WifiMacHelper`` can be also used to set:
@@ -574,31 +574,31 @@
 in an infrastructure network where the AP has SSID ``ns-3-ssid``::
 
     WifiHelper wifi;
-    wifi.SetStandard (WIFI_STANDARD_80211n);
+    wifi.SetStandard(WIFI_STANDARD_80211n);
 
     WifiMacHelper wifiMacHelper;
-    Ssid ssid = Ssid ("ns-3-ssid");
-    wifiMacHelper.SetType ("ns3::StaWifiMac",
-                          "Ssid", SsidValue (ssid),
-                          "VO_MaxAmpduSize", UintegerValue (65535),
-                          "BE_MaxAmsduSize", UintegerValue (7935),
-                          "ActiveProbing", BooleanValue (false));
+    Ssid ssid = Ssid("ns-3-ssid");
+    wifiMacHelper.SetType("ns3::StaWifiMac",
+                          "Ssid", SsidValue(ssid),
+                          "VO_MaxAmpduSize", UintegerValue(65535),
+                          "BE_MaxAmsduSize", UintegerValue(7935),
+                          "ActiveProbing", BooleanValue(false));
 
 802.11ax APs support sending multi-user frames via DL OFDMA and UL OFDMA if a Multi-User Scheduler is
-aggregated to the wifi MAC (by default no scheduler is aggregated). WifiMacHelper enables to aggregate
+aggregated to the wifi MAC(by default no scheduler is aggregated). WifiMacHelper enables to aggregate
 a Multi-User Scheduler to an AP and set its parameters::
 
     WifiMacHelper wifiMacHelper;
-    wifiMacHelper.SetMultiUserScheduler ("ns3::RrMultiUserScheduler",
-                                        "EnableUlOfdma", BooleanValue (true),
-                                        "EnableBsrp", BooleanValue (false));
+    wifiMacHelper.SetMultiUserScheduler("ns3::RrMultiUserScheduler",
+                                        "EnableUlOfdma", BooleanValue(true),
+                                        "EnableBsrp", BooleanValue(false));
 
 The Ack Manager is in charge of selecting the acknowledgment method among the three
-available methods (see section :ref:`wifi-mu-ack-sequences` ). The default ack manager
+available methods(see section :ref:`wifi-mu-ack-sequences` ). The default ack manager
 enables to select the acknowledgment method, e.g.::
 
-    Config::SetDefault ("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
-                       EnumValue (WifiAcknowledgment::DL_MU_AGGREGATE_TF));
+    Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
+                       EnumValue(WifiAcknowledgment::DL_MU_AGGREGATE_TF));
 
 Selection of the Access Category (AC)
 +++++++++++++++++++++++++++++++++++++
@@ -611,9 +611,9 @@
 summary, users can create an address of type :cpp:class:`ns3::InetSocketAddress`
 with the desired type of service value and pass it to the application helpers::
 
-    InetSocketAddress destAddress (ipv4Address, udpPort);
-    destAddress.SetTos (tos);
-    OnOffHelper onoff ("ns3::UdpSocketFactory", destAddress);
+    InetSocketAddress destAddress(ipv4Address, udpPort);
+    destAddress.SetTos(tos);
+    OnOffHelper onoff("ns3::UdpSocketFactory", destAddress);
 
 Mapping the values of the DS field onto user priorities is performed similarly to the
 Linux mac80211 subsystem. Basically, the :cpp:func:`ns3::WifiNetDevice::SelectQueue()`
@@ -665,7 +665,7 @@
 
 So, for example,::
 
-    destAddress.SetTos (0xc0);
+    destAddress.SetTos(0xc0);
 
 will map to CS6, User Priority 6, and Access Category AC_VO.
 Also, the ns3-wifi-ac-mapping test suite (defined in
@@ -697,7 +697,7 @@
 Now, let's use the wifiPhyHelper and wifiMacHelper created above to install WifiNetDevices
 on a set of nodes in a NodeContainer "c"::
 
-  NetDeviceContainer wifiContainer = WifiHelper::Install (wifiPhyHelper, wifiMacHelper, c);
+  NetDeviceContainer wifiContainer = WifiHelper::Install(wifiPhyHelper, wifiMacHelper, c);
 
 This creates the WifiNetDevice which includes also a WifiRemoteStationManager, a
 WifiMac, and a WifiPhy (connected to the matching Channel).
@@ -706,30 +706,30 @@
 In order to change parameters that are overwritten by ``WifiHelper::SetStandard``, this should be done post-install using ``Config::Set``::
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211n);
-  wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", "DataMode", StringValue("HtMcs7"), "ControlMode", StringValue("HtMcs0"));
+  wifi.SetStandard(WIFI_STANDARD_80211n);
+  wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager", "DataMode", StringValue("HtMcs7"), "ControlMode", StringValue("HtMcs0"));
 
   //Install PHY and MAC
-  Ssid ssid = Ssid ("ns3-wifi");
+  Ssid ssid = Ssid("ns3-wifi");
 
   WifiMacHelper mac;
-  mac.SetType ("ns3::StaWifiMac",
-  "Ssid", SsidValue (ssid),
-  "ActiveProbing", BooleanValue (false));
+  mac.SetType("ns3::StaWifiMac",
+  "Ssid", SsidValue(ssid),
+  "ActiveProbing", BooleanValue(false));
 
   NetDeviceContainer staDevice;
-  staDevice = wifi.Install (phy, mac, wifiStaNode);
+  staDevice = wifi.Install(phy, mac, wifiStaNode);
 
-  mac.SetType ("ns3::ApWifiMac",
-  "Ssid", SsidValue (ssid));
+  mac.SetType("ns3::ApWifiMac",
+  "Ssid", SsidValue(ssid));
 
   NetDeviceContainer apDevice;
-  apDevice = wifi.Install (phy, mac, wifiApNode);
+  apDevice = wifi.Install(phy, mac, wifiApNode);
 
   //Once install is done, we overwrite the standard timing values
-  Config::Set ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/Slot", TimeValue (MicroSeconds (slot)));
-  Config::Set ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/Sifs", TimeValue (MicroSeconds (sifs)));
-  Config::Set ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/Pifs", TimeValue (MicroSeconds (pifs)));
+  Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/Slot", TimeValue(MicroSeconds(slot)));
+  Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/Sifs", TimeValue(MicroSeconds(sifs)));
+  Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/Pifs", TimeValue(MicroSeconds(pifs)));
 
 The WifiHelper can be used to set the attributes of the default ack policy selector
 (``ConstantWifiAckPolicySelector``) or to select a different (user provided) ack
@@ -738,16 +738,16 @@
 policy selector associated with BE AC to 0.5::
 
   WifiHelper wifi;
-  wifi.SetAckPolicySelectorForAc (AC_BE, "ns3::ConstantWifiAckPolicySelector",
-                                  "BaThreshold", DoubleValue (0.5));
+  wifi.SetAckPolicySelectorForAc(AC_BE, "ns3::ConstantWifiAckPolicySelector",
+                                 "BaThreshold", DoubleValue(0.5));
 
 The WifiHelper is also used to configure OBSS PD spatial reuse for 802.11ax.
 The following lines configure a WifiHelper to support OBSS PD spatial reuse
 using the ``ConstantObssPdAlgorithm`` with a threshold set to -72 dBm::
 
   WifiHelper wifi;
-  wifi.SetObssPdAlgorithm ("ns3::ConstantObssPdAlgorithm",
-                           "ObssPdLevel", DoubleValue (-72.0));
+  wifi.SetObssPdAlgorithm("ns3::ConstantObssPdAlgorithm",
+                          "ObssPdLevel", DoubleValue(-72.0));
 
 There are many other |ns3| attributes that can be set on the above helpers to
 deviate from the default behavior; the example scripts show how to do some of
@@ -765,15 +765,15 @@
 
 802.11n/ac PHY layer can use either long (800 ns) or short (400 ns) OFDM guard intervals. To configure this parameter for a given device, the following lines of code could be used (in this example, it enables the support of a short guard interval for the first station)::
 
- Ptr<NetDevice> nd = wifiStaDevices.Get (0);
- Ptr<WifiNetDevice> wnd = nd->GetObject<WifiNetDevice> ();
- Ptr<HtConfiguration> htConfiguration = wnd->GetHtConfiguration ();
- htConfiguration->SetShortGuardIntervalSupported (true);
+ Ptr<NetDevice> nd = wifiStaDevices.Get(0);
+ Ptr<WifiNetDevice> wnd = nd->GetObject<WifiNetDevice>();
+ Ptr<HtConfiguration> htConfiguration = wnd->GetHtConfiguration();
+ htConfiguration->SetShortGuardIntervalSupported(true);
 
 It is also possible to configure HT-specific attributes using ``Config::Set``.
-The following line of code enables the support of a short guard interval for all stations:
+The following line of code enables the support of a short guard interval for all stations::
 
- Config::Set ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HtConfiguration/ShortGuardIntervalSupported", BooleanValue (true));
+ Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HtConfiguration/ShortGuardIntervalSupported", BooleanValue(true));
 
 VHT configuration
 =================
@@ -794,17 +794,17 @@
 
 802.11ax PHY layer can use either 3200 ns, 1600 ns or 800 ns OFDM guard intervals. To configure this parameter, the following lines of code could be used (in this example, it enables the support of 1600 ns guard interval), such as in this example code snippet::
 
- Ptr<NetDevice> nd = wifiStaDevices.Get (0);
- Ptr<WifiNetDevice> wnd = nd->GetObject<WifiNetDevice> ();
- Ptr<HeConfiguration> heConfiguration = wnd->GetHeConfiguration ();
- heConfiguration->SetGuardInterval (NanoSeconds (1600));
+ Ptr<NetDevice> nd = wifiStaDevices.Get(0);
+ Ptr<WifiNetDevice> wnd = nd->GetObject<WifiNetDevice>();
+ Ptr<HeConfiguration> heConfiguration = wnd->GetHeConfiguration();
+ heConfiguration->SetGuardInterval(NanoSeconds(1600));
 
 802.11ax allows extended compressed Block ACKs containing a 256-bits bitmap, making
 possible transmissions of A-MPDUs containing up to 256 MPDUs, depending on the
 negotiated buffer size. In order to configure the buffer size of an 802.11ax device,
 the following line of code could be used::
 
- heConfiguration->SetMpduBufferSize (256);
+ heConfiguration->SetMpduBufferSize(256);
 
 For transmitting large MPDUs, it might also be needed to increase the maximum
 aggregation size (see above).
@@ -844,41 +844,41 @@
 Both devices are configured with ``ConstantRateWifiManager`` at the fixed rate of 12Mbps.
 Finally, we manually place them by using the ``ns3::ListPositionAllocator``::
 
-  std::string phyMode ("OfdmRate12Mbps");
+  std::string phyMode("OfdmRate12Mbps");
 
   NodeContainer c;
-  c.Create (2);
+  c.Create(2);
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211a);
+  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);
+  wifiPhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
 
   YansWifiChannelHelper wifiChannel;
-  wifiChannel.SetPropagationDelay ("ns3::ConstantSpeedPropagationDelayModel");
-  wifiChannel.AddPropagationLoss ("ns3::LogDistancePropagationLossModel",
-                                  "Exponent", DoubleValue (3.0));
-  wifiPhy.SetChannel (wifiChannel.Create ());
+  wifiChannel.SetPropagationDelay("ns3::ConstantSpeedPropagationDelayModel");
+  wifiChannel.AddPropagationLoss("ns3::LogDistancePropagationLossModel",
+                                 "Exponent", DoubleValue(3.0));
+  wifiPhy.SetChannel(wifiChannel.Create());
 
   // Add a non-QoS upper mac, and disable rate control (i.e. ConstantRateWifiManager)
   WifiMacHelper wifiMac;
-  wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager",
-                                "DataMode",StringValue (phyMode),
-                                "ControlMode",StringValue (phyMode));
+  wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
+                               "DataMode",StringValue(phyMode),
+                               "ControlMode",StringValue(phyMode));
   // Set it to adhoc mode
-  wifiMac.SetType ("ns3::AdhocWifiMac");
-  NetDeviceContainer devices = wifi.Install (wifiPhy, wifiMac, c);
+  wifiMac.SetType("ns3::AdhocWifiMac");
+  NetDeviceContainer devices = wifi.Install(wifiPhy, wifiMac, c);
 
   // 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);
+  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)
 
@@ -889,58 +889,58 @@
 In this example, we create one access point and two clients.
 Each node is equipped with 802.11b Wi-Fi device::
 
-  std::string phyMode ("DsssRate1Mbps");
+  std::string phyMode("DsssRate1Mbps");
 
   NodeContainer ap;
-  ap.Create (1);
+  ap.Create(1);
   NodeContainer sta;
-  sta.Create (2);
+  sta.Create(2);
 
   WifiHelper wifi;
-  wifi.SetStandard (WIFI_STANDARD_80211b);
+  wifi.SetStandard(WIFI_STANDARD_80211b);
 
-  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);
+  wifiPhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
 
   YansWifiChannelHelper wifiChannel;
   // reference loss must be changed since 802.11b is operating at 2.4GHz
-  wifiChannel.SetPropagationDelay ("ns3::ConstantSpeedPropagationDelayModel");
-  wifiChannel.AddPropagationLoss ("ns3::LogDistancePropagationLossModel",
-                                  "Exponent", DoubleValue (3.0),
-                                  "ReferenceLoss", DoubleValue (40.0459));
-  wifiPhy.SetChannel (wifiChannel.Create ());
+  wifiChannel.SetPropagationDelay("ns3::ConstantSpeedPropagationDelayModel");
+  wifiChannel.AddPropagationLoss("ns3::LogDistancePropagationLossModel",
+                                 "Exponent", DoubleValue(3.0),
+                                 "ReferenceLoss", DoubleValue(40.0459));
+  wifiPhy.SetChannel(wifiChannel.Create());
 
   // Add a non-QoS upper mac, and disable rate control
   WifiMacHelper wifiMac;
-  wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager",
-                                "DataMode",StringValue (phyMode),
-                                "ControlMode",StringValue (phyMode));
+  wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
+                               "DataMode",StringValue(phyMode),
+                               "ControlMode",StringValue(phyMode));
 
   // Setup the rest of the upper mac
-  Ssid ssid = Ssid ("wifi-default");
+  Ssid ssid = Ssid("wifi-default");
   // setup ap.
-  wifiMac.SetType ("ns3::ApWifiMac",
-                   "Ssid", SsidValue (ssid));
-  NetDeviceContainer apDevice = wifi.Install (wifiPhy, wifiMac, 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);
+  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));
-  positionAlloc->Add (Vector (0.0, 5.0, 0.0));
-  mobility.SetPositionAllocator (positionAlloc);
-  mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
-  mobility.Install (ap);
-  mobility.Install (sta);
+  Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
+  positionAlloc->Add(Vector(0.0, 0.0, 0.0));
+  positionAlloc->Add(Vector(5.0, 0.0, 0.0));
+  positionAlloc->Add(Vector(0.0, 5.0, 0.0));
+  mobility.SetPositionAllocator(positionAlloc);
+  mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+  mobility.Install(ap);
+  mobility.Install(sta);
 
   // other set up (e.g. InternetStack, Application)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/reference/Bianchi11a.m ns-3.38/src/wifi/examples/reference/Bianchi11a.m
--- ns-3.37/src/wifi/examples/reference/Bianchi11a.m	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/reference/Bianchi11a.m	2023-03-16 20:54:23.852869300 -0700
@@ -40,7 +40,7 @@
 T_SYMBOL = 3.2e-6 + T_GI; % symbol duration in seconds
 T_LPHY = 20e-6; % PHY preamble & header duration in seconds
 L_SERVICE = 16; % service field length in bits
-L_TAIL = 6; % tail lengthh in bits
+L_TAIL = 6; % tail length in bits
 L_MAC = (24 + 4) * 8; % MAC header size in bits
 L_APP_HDR = 8 * 6; % bits added by the upper layer(s)
 T_SIFS = 16e-6;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/reference/bianchi11ax.py ns-3.38/src/wifi/examples/reference/bianchi11ax.py
--- ns-3.37/src/wifi/examples/reference/bianchi11ax.py	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/reference/bianchi11ax.py	2023-03-16 20:54:23.852869300 -0700
@@ -36,7 +36,7 @@
     T_PHY_ACK = 20e-6              # PHY preamble & header duration in seconds (for ACK)
     T_PHY_DATA = 44e-6             # PHY preamble & header duration in seconds (for DATA)
     L_SERVICE = 16                 # service field length in bits
-    L_TAIL = 6                     # tail lengthh in bits
+    L_TAIL = 6                     # tail length in bits
     L_MAC = (30) * 8               # MAC header size in bits
     L_APP_HDR = 8 * 8              # bits added by the upper layer(s)
     T_SIFS = 16e-6
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/reference/Bianchi11g.m ns-3.38/src/wifi/examples/reference/Bianchi11g.m
--- ns-3.37/src/wifi/examples/reference/Bianchi11g.m	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/reference/Bianchi11g.m	2023-03-16 20:54:23.852869300 -0700
@@ -41,7 +41,7 @@
 T_LPHY = 20e-6; % PHY preamble & header duration in seconds
 T_SIG_EXT = 6e-6; % signal extension duration in seconds
 L_SERVICE = 16; % service field length in bits
-L_TAIL = 6; % tail lengthh in bits
+L_TAIL = 6; % tail length in bits
 L_MAC = (24 + 4) * 8; % MAC header size in bits
 L_APP_HDR = 8 * 6; % bits added by the upper layer(s)
 T_SIFS = 10e-6;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/wifi-bianchi.cc ns-3.38/src/wifi/examples/wifi-bianchi.cc
--- ns-3.37/src/wifi/examples/wifi-bianchi.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/wifi-bianchi.cc	2023-03-16 20:54:23.852869300 -0700
@@ -76,14 +76,14 @@
 std::map<Mac48Address, uint64_t>
     packetsTransmitted; ///< Map that stores the total packets transmitted per STA
 std::map<Mac48Address, uint64_t>
-    psduFailed; ///< Map that stores the total number of unsuccessfully received PSDUS (for which
+    psduFailed; ///< Map that stores the total number of unsuccessfuly received PSDUS (for which
                 ///< the PHY header was successfully received)  per STA (including PSDUs not
                 ///< addressed to that STA)
 std::map<Mac48Address, uint64_t>
     psduSucceeded; ///< Map that stores the total number of successfully received PSDUs per STA
                    ///< (including PSDUs not addressed to that STA)
 std::map<Mac48Address, uint64_t> phyHeaderFailed; ///< Map that stores the total number of
-                                                  ///< unsuccessfully received PHY headers per STA
+                                                  ///< unsuccessfuly received PHY headers per STA
 std::map<Mac48Address, uint64_t>
     rxEventWhileTxing; ///< Map that stores the number of reception events per STA that occurred
                        ///< while PHY was already transmitting a PPDU
@@ -2532,10 +2532,10 @@
     MobilityHelper mobility;
     Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
     mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
-    // Set postion for AP
+    // Set position for AP
     positionAlloc->Add(Vector(1.0, 1.0, 0.0));
 
-    // Set postion for STAs
+    // Set position for STAs
     double angle = (static_cast<double>(360) / (nNodes - 1));
     for (uint32_t i = 0; i < (nNodes - 1); ++i)
     {
@@ -2593,13 +2593,18 @@
                         MakeCallback(&DisassociationLog));
     }
 
+    std::string txop =
+        StaticCast<WifiNetDevice>(wifiNodes.Get(0)->GetDevice(0))->GetMac()->GetQosSupported()
+            ? "BE_Txop"
+            : "Txop";
     // Trace CW evolution
-    Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/$ns3::WifiMac/Txop/CwTrace",
+    Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/$ns3::WifiMac/" + txop +
+                        "/CwTrace",
                     MakeCallback(&CwTrace));
     // Trace backoff evolution
-    Config::Connect(
-        "/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/$ns3::WifiMac/Txop/BackoffTrace",
-        MakeCallback(&BackoffTrace));
+    Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/$ns3::WifiMac/" + txop +
+                        "/BackoffTrace",
+                    MakeCallback(&BackoffTrace));
     // Trace PHY Tx start events
     Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/$ns3::WifiPhy/PhyTxBegin",
                     MakeCallback(&PhyTxTrace));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/wifi-manager-example.cc ns-3.38/src/wifi/examples/wifi-manager-example.cc
--- ns-3.37/src/wifi/examples/wifi-manager-example.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/wifi-manager-example.cc	2023-03-16 20:54:23.852869300 -0700
@@ -25,6 +25,14 @@
 // The test consists of a device acting as server and a device as client generating traffic.
 //
 // The output consists of a plot of the rate observed and selected at the client device.
+// A special FixedRss propagation loss model is used to set a specific receive
+// power on the receiver.  The noise power is exclusively the thermal noise
+// for the channel bandwidth (no noise figure is configured).  Furthermore,
+// the CCA sensitivity attribute in WifiPhy can prevent signals from being
+// received even though the error model would permit it.  Therefore, for
+// the purpose of this example, the CCA sensitivity is lowered to a value
+// that disables it, and furthermore, the preamble detection model (which
+// also contains a similar threshold) is disabled.
 //
 // By default, the 802.11a standard using IdealWifiManager is plotted. Several command line
 // arguments can change the following options:
@@ -593,9 +601,24 @@
     Config::SetDefault("ns3::MinstrelWifiManager::PrintSamples", BooleanValue(true));
     Config::SetDefault("ns3::MinstrelHtWifiManager::PrintStats", BooleanValue(true));
 
+    // Disable the default noise figure of 7 dBm in WifiPhy; the calculations
+    // of SNR below assume that the only noise is thermal noise
+    Config::SetDefault("ns3::WifiPhy::RxNoiseFigure", DoubleValue(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 probe the error model down to low SNR values,
+    // and we have disabled the noise figure, so the noise level in 20 MHz
+    // will be about -101 dBm.  Therefore, lower the CCA sensitivity to a
+    // value that disables it (e.g. -110 dBm)
+    Config::SetDefault("ns3::WifiPhy::CcaSensitivity", DoubleValue(-110));
+
     WifiHelper wifi;
     wifi.SetStandard(serverSelectedStandard.m_standard);
     YansWifiPhyHelper wifiPhy;
+    // Disable the preamble detection model for the same reason that we
+    // disabled CCA sensitivity above-- we want to enable reception at low SNR
+    wifiPhy.DisablePreambleDetectionModel();
 
     Ptr<YansWifiChannel> wifiChannel = CreateObject<YansWifiChannel>();
     Ptr<ConstantSpeedPropagationDelayModel> delayModel =
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/wifi-phy-configuration.cc ns-3.38/src/wifi/examples/wifi-phy-configuration.cc
--- ns-3.37/src/wifi/examples/wifi-phy-configuration.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/wifi-phy-configuration.cc	2023-03-16 20:54:23.852869300 -0700
@@ -225,10 +225,10 @@
         staDevice = wifi.Install(phy, macSta, wifiStaNode.Get(0));
         apDevice = wifi.Install(phy, macAp, wifiApNode.Get(0));
         phySta = GetYansWifiPhyPtr(staDevice);
-        // We expect channel 7, width 80, frequency 5975
+        // We expect channel 7, width 80, frequency 5985
         NS_ASSERT(phySta->GetChannelNumber() == 7);
         NS_ASSERT(phySta->GetChannelWidth() == 80);
-        NS_ASSERT(phySta->GetFrequency() == 5975);
+        NS_ASSERT(phySta->GetFrequency() == 5985);
         PrintAttributesIfEnabled(printAttributes);
         break;
     case 10:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/wifi-phy-test.cc ns-3.38/src/wifi/examples/wifi-phy-test.cc
--- ns-3.37/src/wifi/examples/wifi-phy-test.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/wifi-phy-test.cc	2023-03-16 20:54:23.852869300 -0700
@@ -75,9 +75,9 @@
                  RxSignalInfo rxSignalInfo,
                  WifiTxVector txVector,
                  std::vector<bool> statusPerMpdu);
-    Ptr<WifiPhy> m_tx;      ///< transmit
-    struct Input m_input;   ///< input
-    struct Output m_output; ///< output
+    Ptr<WifiPhy> m_tx; ///< transmit
+    Input m_input;     ///< input
+    Output m_output;   ///< output
 };
 
 void
@@ -175,7 +175,7 @@
         uint32_t nPackets;     ///< number of packets
     };
 
-    /// Output struture
+    /// Output structure
     struct Output
     {
         uint32_t receivedA; ///< received A
@@ -207,12 +207,12 @@
                  RxSignalInfo rxSignalInfo,
                  WifiTxVector txVector,
                  std::vector<bool> statusPerMpdu);
-    Ptr<WifiPhy> m_txA;     ///< transmit A
-    Ptr<WifiPhy> m_txB;     ///< transmit B
-    uint32_t m_flowIdA;     ///< flow ID A
-    uint32_t m_flowIdB;     ///< flow ID B
-    struct Input m_input;   ///< input
-    struct Output m_output; ///< output
+    Ptr<WifiPhy> m_txA; ///< transmit A
+    Ptr<WifiPhy> m_txB; ///< transmit B
+    uint32_t m_flowIdA; ///< flow ID A
+    uint32_t m_flowIdB; ///< flow ID B
+    Input m_input;      ///< input
+    Output m_output;    ///< output
 };
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/examples/wifi-test-interference-helper.cc ns-3.38/src/wifi/examples/wifi-test-interference-helper.cc
--- ns-3.37/src/wifi/examples/wifi-test-interference-helper.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/examples/wifi-test-interference-helper.cc	2023-03-16 20:54:23.852869300 -0700
@@ -71,9 +71,9 @@
 
 NS_LOG_COMPONENT_DEFINE("test-interference-helper");
 
-bool checkResults = false;         //!< True if results have to be checked.
-bool expectRxASuccessfull = false; //!< True if Rx from A is expected to be successful.
-bool expectRxBSuccessfull = false; //!< True if Rx from B is expected to be successful.
+bool checkResults = false;        //!< True if results have to be checked.
+bool expectRxASuccessful = false; //!< True if Rx from A is expected to be successful.
+bool expectRxBSuccessful = false; //!< True if Rx from B is expected to be successful.
 
 /// InterferenceExperiment
 class InterferenceExperiment
@@ -123,7 +123,7 @@
     void SendB() const;
     Ptr<SpectrumWifiPhy> m_txA; ///< transmit A function
     Ptr<SpectrumWifiPhy> m_txB; ///< transmit B function
-    struct Input m_input;       ///< input
+    Input m_input;              ///< input
     bool m_droppedA;            ///< flag to indicate whether packet A has been dropped
     bool m_droppedB;            ///< flag to indicate whether packet B has been dropped
     mutable uint64_t m_uidA;    ///< UID to use for packet A
@@ -307,7 +307,7 @@
     m_txA->Dispose();
     rx->Dispose();
 
-    if (checkResults && (m_droppedA == expectRxASuccessfull || m_droppedB == expectRxBSuccessfull))
+    if (checkResults && (m_droppedA == expectRxASuccessful || m_droppedB == expectRxBSuccessful))
     {
         NS_LOG_ERROR("Results are not expected!");
         exit(1);
@@ -344,12 +344,12 @@
     cmd.AddValue("enableCapture", "Enable/disable physical layer capture", input.captureEnabled);
     cmd.AddValue("captureMargin", "Margin used for physical layer capture", input.captureMargin);
     cmd.AddValue("checkResults", "Used to check results at the end of the test", checkResults);
-    cmd.AddValue("expectRxASuccessfull",
+    cmd.AddValue("expectRxASuccessful",
                  "Indicate whether packet A is expected to be successfully received",
-                 expectRxASuccessfull);
-    cmd.AddValue("expectRxBSuccessfull",
+                 expectRxASuccessful);
+    cmd.AddValue("expectRxBSuccessful",
                  "Indicate whether packet B is expected to be successfully received",
-                 expectRxBSuccessfull);
+                 expectRxBSuccessful);
     cmd.Parse(argc, argv);
 
     input.interval = MicroSeconds(delay);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/helper/wifi-helper.cc ns-3.38/src/wifi/helper/wifi-helper.cc
--- ns-3.37/src/wifi/helper/wifi-helper.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/helper/wifi-helper.cc	2023-03-16 20:54:23.852869300 -0700
@@ -798,10 +798,20 @@
         std::vector<Ptr<WifiRemoteStationManager>> managers;
         std::vector<Ptr<WifiPhy>> phys = phyHelper.Create(node, device);
         device->SetPhys(phys);
+        // if only one remote station manager model was provided, replicate it for all the links
+        auto stationManagers = m_stationManager;
+        if (stationManagers.size() == 1 && phys.size() > 1)
+        {
+            stationManagers.resize(phys.size(), stationManagers[0]);
+        }
+        NS_ABORT_MSG_IF(stationManagers.size() != phys.size(),
+                        "Number of station manager models ("
+                            << stationManagers.size() << ") does not match the number of links ("
+                            << phys.size() << ")");
         for (std::size_t i = 0; i < phys.size(); i++)
         {
             phys[i]->ConfigureStandard(m_standard);
-            managers.push_back(m_stationManager.Create<WifiRemoteStationManager>());
+            managers.push_back(stationManagers[i].Create<WifiRemoteStationManager>());
         }
         device->SetRemoteStationManagers(managers);
         Ptr<WifiMac> mac = macHelper.Create(device, m_standard);
@@ -890,6 +900,7 @@
     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);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/helper/wifi-helper.h ns-3.38/src/wifi/helper/wifi-helper.h
--- ns-3.37/src/wifi/helper/wifi-helper.h	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/helper/wifi-helper.h	2023-03-16 20:54:51.628957000 -0700
@@ -347,6 +347,21 @@
     void SetRemoteStationManager(std::string type, Args&&... args);
 
     /**
+     * \tparam Args \deduced Template type parameter pack for the sequence of name-value pairs.
+     * \param linkId ID of the link to configure (>0 only for 11be devices)
+     * \param type the type of the preamble detection model to set.
+     * \param args A sequence of name-value pairs of the attributes to set.
+     *
+     * Set the remote station manager model and its attributes to use for the given link.
+     * If the helper stored a remote station manager model for the first N links only
+     * (corresponding to link IDs from 0 to N-1) and the given linkId is M >= N, then a
+     * remote station manager model using the given attributes is configured for all links
+     * with ID from N to M.
+     */
+    template <typename... Args>
+    void SetRemoteStationManager(uint8_t linkId, std::string type, Args&&... args);
+
+    /**
      * Helper function used to set the OBSS-PD algorithm
      *
      * \tparam Args \deduced Template type parameter pack for the sequence of name-value pairs.
@@ -503,15 +518,15 @@
     int64_t AssignStreams(NetDeviceContainer c, int64_t stream);
 
   protected:
-    ObjectFactory m_stationManager;            ///< station manager
-    WifiStandard m_standard;                   ///< wifi standard
-    ObjectFactory m_htConfig;                  ///< HT configuration
-    ObjectFactory m_vhtConfig;                 ///< VHT configuration
-    ObjectFactory m_heConfig;                  ///< HE configuration
-    ObjectFactory m_ehtConfig;                 ///< EHT configuration
-    SelectQueueCallback m_selectQueueCallback; ///< select queue callback
-    ObjectFactory m_obssPdAlgorithm;           ///< OBSS_PD algorithm
-    bool m_enableFlowControl;                  //!< whether to enable flow control
+    mutable std::vector<ObjectFactory> m_stationManager; ///< station manager
+    WifiStandard m_standard;                             ///< wifi standard
+    ObjectFactory m_htConfig;                            ///< HT configuration
+    ObjectFactory m_vhtConfig;                           ///< VHT configuration
+    ObjectFactory m_heConfig;                            ///< HE configuration
+    ObjectFactory m_ehtConfig;                           ///< EHT configuration
+    SelectQueueCallback m_selectQueueCallback;           ///< select queue callback
+    ObjectFactory m_obssPdAlgorithm;                     ///< OBSS_PD algorithm
+    bool m_enableFlowControl;                            //!< whether to enable flow control
 };
 
 } // namespace ns3
@@ -589,8 +604,21 @@
 void
 WifiHelper::SetRemoteStationManager(std::string type, Args&&... args)
 {
-    m_stationManager.SetTypeId(type);
-    m_stationManager.Set(args...);
+    SetRemoteStationManager(0, type, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+void
+WifiHelper::SetRemoteStationManager(uint8_t linkId, std::string type, Args&&... args)
+{
+    if (m_stationManager.size() > linkId)
+    {
+        m_stationManager[linkId] = ObjectFactory(type, std::forward<Args>(args)...);
+    }
+    else
+    {
+        m_stationManager.resize(linkId + 1, ObjectFactory(type, std::forward<Args>(args)...));
+    }
 }
 
 template <typename... Args>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ap-wifi-mac.cc ns-3.38/src/wifi/model/ap-wifi-mac.cc
--- ns-3.37/src/wifi/model/ap-wifi-mac.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/ap-wifi-mac.cc	2023-03-16 20:54:23.852869300 -0700
@@ -30,6 +30,7 @@
 #include "msdu-aggregator.h"
 #include "qos-txop.h"
 #include "reduced-neighbor-report.h"
+#include "wifi-mac-queue-scheduler.h"
 #include "wifi-mac-queue.h"
 #include "wifi-net-device.h"
 #include "wifi-phy.h"
@@ -341,28 +342,54 @@
         hdr.SetNoOrder(); // explicitly set to 0 for the time being since HT control field is not
                           // yet implemented (set it to 1 when implemented)
     }
-    hdr.SetAddr1(to);
-    hdr.SetAddr2(GetAddress());
-    hdr.SetAddr3(from);
-    hdr.SetDsFrom();
-    hdr.SetDsNotTo();
 
-    if (GetQosSupported())
+    std::list<Mac48Address> addr2Set;
+    if (to.IsGroup())
     {
-        // Sanity check that the TID is valid
-        NS_ASSERT(tid < 8);
-        GetQosTxop(tid)->Queue(packet, hdr);
+        // broadcast frames are transmitted on all the links
+        for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
+        {
+            addr2Set.push_back(GetFrameExchangeManager(linkId)->GetAddress());
+        }
     }
     else
     {
-        GetTxop()->Queue(packet, hdr);
+        // the Transmitter Address (TA) is the MLD address only for non-broadcast data frames
+        // exchanged between two MLDs
+        addr2Set = {GetAddress()};
+        auto linkId = IsAssociated(to);
+        NS_ASSERT_MSG(linkId, "Station " << to << "is not associated, cannot send it a frame");
+        if (GetNLinks() == 1 || !GetWifiRemoteStationManager(*linkId)->GetMldAddress(to))
+        {
+            addr2Set = {GetFrameExchangeManager(*linkId)->GetAddress()};
+        }
+    }
+
+    for (const auto& addr2 : addr2Set)
+    {
+        hdr.SetAddr1(to);
+        hdr.SetAddr2(addr2);
+        hdr.SetAddr3(from);
+        hdr.SetDsFrom();
+        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);
+        }
     }
 }
 
 bool
 ApWifiMac::CanForwardPacketsTo(Mac48Address to) const
 {
-    return (to.IsGroup() || GetWifiRemoteStationManager()->IsAssociated(to));
+    return (to.IsGroup() || IsAssociated(to));
 }
 
 void
@@ -796,7 +823,7 @@
     // segment that contains the primary channel.
     operation.SetChannelCenterFrequencySegment0(
         (bssBandwidth == 160)
-            ? phy->GetOperatingChannel().GetPrimaryChannelNumber(80, WIFI_STANDARD_80211ac)
+            ? phy->GetOperatingChannel().GetPrimaryChannelNumber(80, phy->GetStandard())
             : phy->GetChannelNumber());
     // For a 20, 40, or 80 MHz BSS bandwidth, this subfield is set to 0.
     // For a 160 MHz BSS bandwidth and the Channel Width subfield equal to 1,
@@ -855,6 +882,30 @@
     return operation;
 }
 
+EhtOperation
+ApWifiMac::GetEhtOperation(uint8_t linkId) const
+{
+    NS_LOG_FUNCTION(this << +linkId);
+    NS_ASSERT(GetEhtSupported());
+    EhtOperation operation;
+    auto remoteStationManager = GetWifiRemoteStationManager(linkId);
+
+    auto maxSpatialStream = GetWifiPhy(linkId)->GetMaxSupportedRxSpatialStreams();
+    for (const auto& sta : GetLink(linkId).staList)
+    {
+        if (remoteStationManager->GetEhtSupported(sta.second))
+        {
+            if (remoteStationManager->GetNumberOfSupportedStreams(sta.second) < maxSpatialStream)
+            {
+                maxSpatialStream = remoteStationManager->GetNumberOfSupportedStreams(sta.second);
+            }
+        }
+    }
+    operation.SetMaxRxNss(maxSpatialStream, 0, WIFI_EHT_MAX_MCS_INDEX);
+    operation.SetMaxTxNss(maxSpatialStream, 0, WIFI_EHT_MAX_MCS_INDEX);
+    return operation;
+}
+
 void
 ApWifiMac::SendProbeResp(Mac48Address to, uint8_t linkId)
 {
@@ -911,6 +962,7 @@
     if (GetEhtSupported())
     {
         probe.SetEhtCapabilities(GetEhtCapabilities(linkId));
+        probe.SetEhtOperation(GetEhtOperation(linkId));
 
         if (GetNLinks() > 1)
         {
@@ -1010,27 +1062,25 @@
     if (GetEhtSupported())
     {
         assoc.SetEhtCapabilities(GetEhtCapabilities(linkId));
+        assoc.SetEhtOperation(GetEhtOperation(linkId));
     }
     return assoc;
 }
 
-void
-ApWifiMac::SetAid(MgtAssocResponseHeader& assoc, const Mac48Address& to, uint8_t linkId)
+ApWifiMac::LinkIdStaAddrMap
+ApWifiMac::GetLinkIdStaAddrMap(MgtAssocResponseHeader& assoc,
+                               const Mac48Address& to,
+                               uint8_t linkId)
 {
-    NS_LOG_FUNCTION(this << to << +linkId);
-
     // find all the links to setup (i.e., those for which status code is success)
-    std::list<std::reference_wrapper<MgtAssocResponseHeader>> assocResponses;
     std::map<uint8_t /* link ID */, Mac48Address> linkIdStaAddrMap;
 
     if (assoc.GetStatusCode().IsSuccess())
     {
-        assocResponses.push_back(std::ref(assoc));
         linkIdStaAddrMap[linkId] = to;
     }
 
-    const auto& mle = assoc.GetMultiLinkElement();
-    if (mle.has_value())
+    if (const auto& mle = assoc.GetMultiLinkElement())
     {
         const auto staMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(to);
         NS_ABORT_MSG_IF(!staMldAddress.has_value(),
@@ -1041,7 +1091,6 @@
             if (perStaProfile.HasAssocResponse() &&
                 perStaProfile.GetAssocResponse().GetStatusCode().IsSuccess())
             {
-                assocResponses.emplace_back(perStaProfile.GetAssocResponse());
                 uint8_t otherLinkId = perStaProfile.GetLinkId();
                 auto staAddress = GetWifiRemoteStationManager(otherLinkId)
                                       ->GetAffiliatedStaAddress(*staMldAddress);
@@ -1055,7 +1104,13 @@
         }
     }
 
-    if (assocResponses.empty())
+    return linkIdStaAddrMap;
+}
+
+void
+ApWifiMac::SetAid(MgtAssocResponseHeader& assoc, const LinkIdStaAddrMap& linkIdStaAddrMap)
+{
+    if (linkIdStaAddrMap.empty())
     {
         // no link to setup, nothing to do
         return;
@@ -1122,6 +1177,11 @@
         aid = GetNextAssociationId(linkIds);
     }
 
+    // store the MLD or link address in the AID-to-address map
+    const auto& [linkId, staAddr] = *linkIdStaAddrMap.cbegin();
+    m_aidToMldOrLinkAddress[aid] =
+        GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr);
+
     for (const auto& [id, staAddr] : linkIdStaAddrMap)
     {
         auto remoteStationManager = GetWifiRemoteStationManager(id);
@@ -1162,9 +1222,21 @@
     // Element must not contain the AID field. We set the AID field in such
     // Association Responses anyway, in order to ease future implementation of
     // the inheritance mechanism.
-    for (auto& assocResp : assocResponses)
+    if (assoc.GetStatusCode().IsSuccess())
     {
-        assocResp.get().SetAssociationId(aid);
+        assoc.SetAssociationId(aid);
+    }
+    if (const auto& mle = assoc.GetMultiLinkElement())
+    {
+        for (std::size_t idx = 0; idx < mle->GetNPerStaProfileSubelements(); idx++)
+        {
+            if (const auto& perStaProfile = mle->GetPerStaProfile(idx);
+                perStaProfile.HasAssocResponse() &&
+                perStaProfile.GetAssocResponse().GetStatusCode().IsSuccess())
+            {
+                perStaProfile.GetAssocResponse().SetAssociationId(aid);
+            }
+        }
     }
 }
 
@@ -1194,7 +1266,13 @@
         assoc.SetMultiLinkElement(GetMultiLinkElement(linkId, hdr.GetType(), to));
     }
 
-    SetAid(assoc, to, linkId);
+    auto linkIdStaAddrMap = GetLinkIdStaAddrMap(assoc, to, linkId);
+    SetAid(assoc, linkIdStaAddrMap);
+
+    if (GetNLinks() > 1)
+    {
+        ConfigQueueScheduler(linkIdStaAddrMap, to, linkId);
+    }
 
     Ptr<Packet> packet = Create<Packet>();
     packet->AddHeader(assoc);
@@ -1221,6 +1299,39 @@
 }
 
 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);
@@ -1275,6 +1386,7 @@
     if (GetEhtSupported())
     {
         beacon.SetEhtCapabilities(GetEhtCapabilities(linkId));
+        beacon.SetEhtOperation(GetEhtOperation(linkId));
 
         if (GetNLinks() > 1)
         {
@@ -1402,21 +1514,59 @@
     }
 }
 
+std::optional<uint8_t>
+ApWifiMac::IsAssociated(const Mac48Address& address) const
+{
+    for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
+    {
+        if (GetWifiRemoteStationManager(linkId)->IsAssociated(address))
+        {
+            return linkId;
+        }
+    }
+    NS_LOG_DEBUG(address << " is not associated");
+    return std::nullopt;
+}
+
+Mac48Address
+ApWifiMac::DoGetLocalAddress(const Mac48Address& remoteAddr) const
+{
+    auto linkId = IsAssociated(remoteAddr);
+    NS_ASSERT_MSG(linkId, remoteAddr << " is not associated");
+    return GetFrameExchangeManager(*linkId)->GetAddress();
+}
+
+std::optional<Mac48Address>
+ApWifiMac::GetMldOrLinkAddressByAid(uint16_t aid) const
+{
+    if (const auto staIt = m_aidToMldOrLinkAddress.find(aid);
+        staIt != m_aidToMldOrLinkAddress.cend())
+    {
+        return staIt->second;
+    }
+    return std::nullopt;
+}
+
 void
 ApWifiMac::Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
 {
     NS_LOG_FUNCTION(this << *mpdu << +linkId);
-    const WifiMacHeader* hdr = &mpdu->GetHeader();
+    // consider the MAC header of the original MPDU (makes a difference for data frames only)
+    const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
     Ptr<const Packet> packet = mpdu->GetPacket();
     Mac48Address from = hdr->GetAddr2();
     if (hdr->IsData())
     {
-        Mac48Address bssid = hdr->GetAddr1();
-        if (!hdr->IsFromDs() && hdr->IsToDs() && bssid == GetAddress() &&
-            GetWifiRemoteStationManager()->IsAssociated(from))
+        std::optional<uint8_t> apLinkId;
+        if (!hdr->IsFromDs() && hdr->IsToDs() &&
+            (apLinkId = IsAssociated(mpdu->GetHeader().GetAddr2())) &&
+            mpdu->GetHeader().GetAddr1() == GetFrameExchangeManager(*apLinkId)->GetAddress())
         {
             Mac48Address to = hdr->GetAddr3();
-            if (to == GetAddress())
+            // 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)
+            if (to == GetAddress() ||
+                (hdr->IsQosData() && hdr->IsQosAmsdu() && to == mpdu->GetHeader().GetAddr1()))
             {
                 NS_LOG_DEBUG("frame for me from=" << from);
                 if (hdr->IsQosData())
@@ -1430,15 +1580,15 @@
                     }
                     else
                     {
-                        ForwardUp(packet, from, bssid);
+                        ForwardUp(packet, from, GetAddress());
                     }
                 }
                 else if (hdr->HasData())
                 {
-                    ForwardUp(packet, from, bssid);
+                    ForwardUp(packet, from, GetAddress());
                 }
             }
-            else if (to.IsGroup() || GetWifiRemoteStationManager()->IsAssociated(to))
+            else if (to.IsGroup() || IsAssociated(to))
             {
                 NS_LOG_DEBUG("forwarding frame from=" << from << ", to=" << to);
                 Ptr<Packet> copy = packet->Copy();
@@ -1712,10 +1862,10 @@
         }
         if (GetEhtSupported())
         {
-            const auto& ehtCapabilities = frame.GetEhtCapabilities();
-            // TODO: once we support non constant rate managers, we should add checks here whether
-            // EHT is supported by the peer
-            remoteStationManager->AddStationEhtCapabilities(from, *ehtCapabilities);
+            if (const auto& ehtCapabilities = frame.GetEhtCapabilities())
+            {
+                remoteStationManager->AddStationEhtCapabilities(from, *ehtCapabilities);
+            }
             for (const auto& mcs : phy->GetMcsList(WIFI_MOD_CLASS_EHT))
             {
                 // TODO: Add check whether MCS is supported from the capabilities
@@ -1787,17 +1937,16 @@
     NS_LOG_FUNCTION(this << *mpdu);
     for (auto& i : *PeekPointer(mpdu))
     {
-        if (i.second.GetDestinationAddr() == GetAddress())
-        {
-            ForwardUp(i.first, i.second.GetSourceAddr(), i.second.GetDestinationAddr());
-        }
-        else
+        auto from = i.second.GetSourceAddr();
+        auto to = i.second.GetDestinationAddr();
+
+        if (to.IsGroup() || IsAssociated(to))
         {
-            Mac48Address from = i.second.GetSourceAddr();
-            Mac48Address to = i.second.GetDestinationAddr();
             NS_LOG_DEBUG("forwarding QoS frame from=" << from << ", to=" << to);
             ForwardDown(i.first->Copy(), from, to, mpdu->GetHeader().GetQosTid());
         }
+
+        ForwardUp(i.first, from, to);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ap-wifi-mac.h ns-3.38/src/wifi/model/ap-wifi-mac.h
--- ns-3.37/src/wifi/model/ap-wifi-mac.h	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/ap-wifi-mac.h	2023-03-16 20:54:23.852869300 -0700
@@ -42,6 +42,7 @@
 class HtOperation;
 class VhtOperation;
 class HeOperation;
+class EhtOperation;
 class CfParameterSet;
 class UniformRandomVariable;
 class MgtAssocRequestHeader;
@@ -107,7 +108,7 @@
      * \param linkId the ID of the given link
      * \return a const reference to the map of associated stations
      */
-    const std::map<uint16_t, Mac48Address>& GetStaList(uint8_t linkId = SINGLE_LINK_OP_ID) const;
+    const std::map<uint16_t, Mac48Address>& GetStaList(uint8_t linkId) const;
     /**
      * \param addr the address of the associated station
      * \param linkId the ID of the link on which the station is associated
@@ -116,6 +117,23 @@
     uint16_t GetAssociationId(Mac48Address addr, uint8_t linkId) const;
 
     /**
+     * Get the ID of a link (if any) that has been setup with the station having the given MAC
+     * address. The address can be either a link address or an MLD address. In the former case,
+     * the returned ID is the ID of the link connecting the AP to the STA with the given address.
+     *
+     * \param address the given MAC address
+     * \return the ID of a link (if any) that has been setup with the given station
+     */
+    std::optional<uint8_t> IsAssociated(const Mac48Address& address) const;
+
+    /**
+     * \param aid the given AID
+     * \return the MLD address (in case of MLD) or link address (in case of single link device)
+     *         of the STA having the given AID, if any
+     */
+    std::optional<Mac48Address> GetMldOrLinkAddressByAid(uint16_t aid) const;
+
+    /**
      * Return the value of the Queue Size subfield of the last QoS Data or QoS Null
      * frame received from the station with the given MAC address and belonging to
      * the given TID.
@@ -182,10 +200,15 @@
      */
     ApLinkEntity& GetLink(uint8_t linkId) const;
 
+    std::map<uint16_t, Mac48Address>
+        m_aidToMldOrLinkAddress; //!< Maps AIDs to MLD addresses (for MLDs) or link addresses (in
+                                 //!< case of single link devices)
+
   private:
     std::unique_ptr<LinkEntity> CreateLinkEntity() const override;
-
+    Mac48Address DoGetLocalAddress(const Mac48Address& remoteAddr) const override;
     void Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId) override;
+
     /**
      * Check whether the supported rate set included in the received (Re)Association
      * Request frame is compatible with our Basic Rate Set. If so, record all the station's
@@ -278,9 +301,10 @@
      * \return the Association Response frame
      */
     MgtAssocResponseHeader GetAssocResp(Mac48Address to, uint8_t linkId);
+    /// Map of (link ID, remote STA address) of the links to setup
+    using LinkIdStaAddrMap = std::map<uint8_t, Mac48Address>;
     /**
-     * Set the AID field of the given Association Response frame, which is going
-     * to be sent to the STA with the given address on the given link. In case of
+     * Set the AID field of the given Association Response frame. In case of
      * multi-link setup, the selected AID value must be assigned to all the STAs
      * corresponding to the setup links. The AID value is selected among the AID
      * values that are possibly already assigned to the STAs affiliated with the
@@ -288,10 +312,34 @@
      * a new AID value is selected.
      *
      * \param assoc the given Association Response frame
-     * \param to the address of the STA receiving the Association Response frame
-     * \param linkId the ID of the given link
+     * \param linkIdStaAddrMap a map of (link ID, remote STA address) of the links to setup
      */
-    void SetAid(MgtAssocResponseHeader& assoc, const Mac48Address& to, uint8_t linkId);
+    void SetAid(MgtAssocResponseHeader& assoc, const LinkIdStaAddrMap& linkIdStaAddrMap);
+    /**
+     * Get a map of (link ID, remote STA address) of the links to setup. Information
+     * is taken from the given Association Response that is sent over the given link
+     * to the given station.
+     *
+     * \param assoc the given Association Response frame
+     * \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 sent
+     * \return a map of (link ID, remote STA address) of the links to setup
+     */
+    LinkIdStaAddrMap GetLinkIdStaAddrMap(MgtAssocResponseHeader& assoc,
+                                         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.
      *
@@ -377,6 +425,13 @@
      */
     HeOperation GetHeOperation(uint8_t linkId) const;
     /**
+     * Return the EHT operation of the current AP for the given link.
+     *
+     * \param linkId the ID of the given link
+     * \return the EHT operation that we support
+     */
+    EhtOperation GetEhtOperation(uint8_t linkId) const;
+    /**
      * Return an instance of SupportedRates that contains all rates that we support
      * for the given link (including HT rates).
      *
@@ -448,14 +503,14 @@
     Time m_bsrLifetime;            //!< Lifetime of Buffer Status Reports
 
     /// store value and timestamp for each Buffer Status Report
-    typedef struct
+    struct BsrType
     {
         uint8_t value;  //!< value of BSR
         Time timestamp; //!< timestamp of BSR
-    } bsrType;
+    };
 
     /// Per (MAC address, TID) buffer status reports
-    std::unordered_map<WifiAddressTidPair, bsrType, WifiAddressTidHash> m_bufferStatus;
+    std::unordered_map<WifiAddressTidPair, BsrType, WifiAddressTidHash> m_bufferStatus;
 
     /**
      * TracedCallback signature for association/deassociation events.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/block-ack-agreement.h ns-3.38/src/wifi/model/block-ack-agreement.h
--- ns-3.37/src/wifi/model/block-ack-agreement.h	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/block-ack-agreement.h	2023-03-16 20:54:23.852869300 -0700
@@ -174,16 +174,16 @@
     static std::size_t GetDistance(uint16_t seqNumber, uint16_t startingSeqNumber);
 
   protected:
-    Mac48Address m_peer;       //!< Peer address
-    uint8_t m_amsduSupported;  //!< Flag whether MSDU aggregation is supported
-    uint8_t m_blockAckPolicy;  //!< Type of block ack: immediate or delayed
-    uint8_t m_tid;             //!< Traffic ID
-    uint16_t m_bufferSize;     //!< Buffer size
-    uint16_t m_timeout;        //!< Timeout
-    uint16_t m_startingSeq;    //!< Starting sequence control
-    uint16_t m_winEnd;         //!< Ending sequence number
-    uint8_t m_htSupported;     //!< Flag whether HT is supported
-    EventId m_inactivityEvent; //!< inactivity event
+    Mac48Address m_peer;               //!< Peer address
+    uint8_t m_amsduSupported;          //!< Flag whether MSDU aggregation is supported
+    uint8_t m_blockAckPolicy;          //!< Type of block ack: immediate or delayed
+    uint8_t m_tid;                     //!< Traffic ID
+    uint16_t m_bufferSize;             //!< Buffer size
+    uint16_t m_timeout;                //!< Timeout
+    uint16_t m_startingSeq;            //!< Starting sequence control
+    uint16_t m_winEnd;                 //!< Ending sequence number
+    uint8_t m_htSupported;             //!< Flag whether HT is supported
+    mutable EventId m_inactivityEvent; //!< inactivity event
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/block-ack-manager.cc ns-3.38/src/wifi/model/block-ack-manager.cc
--- ns-3.37/src/wifi/model/block-ack-manager.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/block-ack-manager.cc	2023-03-16 20:54:23.852869300 -0700
@@ -20,6 +20,7 @@
 #include "block-ack-manager.h"
 
 #include "ctrl-headers.h"
+#include "mac-rx-middle.h"
 #include "mgt-headers.h"
 #include "qos-utils.h"
 #include "wifi-mac-queue.h"
@@ -36,19 +37,6 @@
 
 NS_LOG_COMPONENT_DEFINE("BlockAckManager");
 
-Bar::Bar()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-Bar::Bar(Ptr<const WifiMpdu> bar, uint8_t tid, bool skipIfNoDataQueued)
-    : bar(bar),
-      tid(tid),
-      skipIfNoDataQueued(skipIfNoDataQueued)
-{
-    NS_LOG_FUNCTION(this << *bar << +tid << skipIfNoDataQueued);
-}
-
 NS_OBJECT_ENSURE_REGISTERED(BlockAckManager);
 
 TypeId
@@ -61,7 +49,7 @@
             .AddConstructor<BlockAckManager>()
             .AddTraceSource("AgreementState",
                             "The state of the ADDBA handshake",
-                            MakeTraceSourceAccessor(&BlockAckManager::m_agreementState),
+                            MakeTraceSourceAccessor(&BlockAckManager::m_originatorAgreementState),
                             "ns3::BlockAckManager::AgreementStateTracedCallback");
     return tid;
 }
@@ -80,69 +68,50 @@
 BlockAckManager::DoDispose()
 {
     NS_LOG_FUNCTION(this);
-    m_agreements.clear();
-    m_bars.clear();
+    m_originatorAgreements.clear();
     m_queue = nullptr;
-    m_bamMap.clear();
-}
-
-void
-BlockAckManager::SetBlockAckManagerMap(const std::map<AcIndex, Ptr<BlockAckManager>>& bamMap)
-{
-    m_bamMap = bamMap;
 }
 
-bool
-BlockAckManager::ExistsAgreement(Mac48Address recipient, uint8_t tid) const
+BlockAckManager::OriginatorAgreementOptConstRef
+BlockAckManager::GetAgreementAsOriginator(const Mac48Address& recipient, uint8_t tid) const
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
-    return (m_agreements.find(std::make_pair(recipient, tid)) != m_agreements.end());
+    if (auto it = m_originatorAgreements.find({recipient, tid}); it != m_originatorAgreements.end())
+    {
+        return std::cref(it->second.first);
+    }
+
+    return std::nullopt;
 }
 
-bool
-BlockAckManager::ExistsAgreementInState(Mac48Address recipient,
-                                        uint8_t tid,
-                                        OriginatorBlockAckAgreement::State state) const
-{
-    AgreementsCI it;
-    it = m_agreements.find(std::make_pair(recipient, tid));
-    if (it != m_agreements.end())
-    {
-        switch (state)
-        {
-        case OriginatorBlockAckAgreement::ESTABLISHED:
-            return it->second.first.IsEstablished();
-        case OriginatorBlockAckAgreement::PENDING:
-            return it->second.first.IsPending();
-        case OriginatorBlockAckAgreement::REJECTED:
-            return it->second.first.IsRejected();
-        case OriginatorBlockAckAgreement::NO_REPLY:
-            return it->second.first.IsNoReply();
-        case OriginatorBlockAckAgreement::RESET:
-            return it->second.first.IsReset();
-        default:
-            NS_FATAL_ERROR("Invalid state for block ack agreement");
-        }
+BlockAckManager::RecipientAgreementOptConstRef
+BlockAckManager::GetAgreementAsRecipient(const Mac48Address& originator, uint8_t tid) const
+{
+    NS_LOG_FUNCTION(this << originator << +tid);
+    if (auto it = m_recipientAgreements.find({originator, tid}); it != m_recipientAgreements.end())
+    {
+        return std::cref(it->second);
     }
-    return false;
+
+    return std::nullopt;
 }
 
 void
-BlockAckManager::CreateAgreement(const MgtAddBaRequestHeader* reqHdr,
-                                 Mac48Address recipient,
-                                 bool htSupported)
+BlockAckManager::CreateOriginatorAgreement(const MgtAddBaRequestHeader& reqHdr,
+                                           const Mac48Address& recipient,
+                                           bool htSupported)
 {
     NS_LOG_FUNCTION(this << reqHdr << recipient << htSupported);
-    std::pair<Mac48Address, uint8_t> key(recipient, reqHdr->GetTid());
-    OriginatorBlockAckAgreement agreement(recipient, reqHdr->GetTid());
-    agreement.SetStartingSequence(reqHdr->GetStartingSequence());
+    const uint8_t tid = reqHdr.GetTid();
+    OriginatorBlockAckAgreement agreement(recipient, tid);
+    agreement.SetStartingSequence(reqHdr.GetStartingSequence());
     /* For now we assume that originator doesn't use this field. Use of this field
        is mandatory only for recipient */
-    agreement.SetBufferSize(reqHdr->GetBufferSize());
-    agreement.SetTimeout(reqHdr->GetTimeout());
-    agreement.SetAmsduSupport(reqHdr->IsAmsduSupported());
+    agreement.SetBufferSize(reqHdr.GetBufferSize());
+    agreement.SetTimeout(reqHdr.GetTimeout());
+    agreement.SetAmsduSupport(reqHdr.IsAmsduSupported());
     agreement.SetHtSupported(htSupported);
-    if (reqHdr->IsImmediateBlockAck())
+    if (reqHdr.IsImmediateBlockAck())
     {
         agreement.SetImmediateBlockAck();
     }
@@ -150,61 +119,49 @@
     {
         agreement.SetDelayedBlockAck();
     }
-    uint8_t tid = reqHdr->GetTid();
-    m_agreementState(Simulator::Now(), recipient, tid, OriginatorBlockAckAgreement::PENDING);
     agreement.SetState(OriginatorBlockAckAgreement::PENDING);
-    PacketQueue queue;
-    std::pair<OriginatorBlockAckAgreement, PacketQueue> value(agreement, queue);
-    if (ExistsAgreement(recipient, tid))
+    m_originatorAgreementState(Simulator::Now(),
+                               recipient,
+                               tid,
+                               OriginatorBlockAckAgreement::PENDING);
+    if (auto existingAgreement = GetAgreementAsOriginator(recipient, tid))
     {
-        // Delete agreement if it exists and in RESET state
-        NS_ASSERT(ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::RESET));
-        m_agreements.erase(key);
+        NS_ASSERT_MSG(existingAgreement->get().IsReset(),
+                      "Existing agreement must be in RESET state");
     }
-    m_agreements.insert(std::make_pair(key, value));
-    m_blockPackets(recipient, reqHdr->GetTid());
+    m_originatorAgreements.insert_or_assign({recipient, tid},
+                                            std::make_pair(std::move(agreement), PacketQueue{}));
+    m_blockPackets(recipient, tid);
 }
 
 void
-BlockAckManager::DestroyAgreement(Mac48Address recipient, uint8_t tid)
+BlockAckManager::DestroyOriginatorAgreement(const Mac48Address& recipient, uint8_t tid)
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    if (it != m_agreements.end())
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it != m_originatorAgreements.end())
     {
-        m_agreements.erase(it);
-        // remove scheduled BAR
-        for (std::list<Bar>::const_iterator i = m_bars.begin(); i != m_bars.end();)
-        {
-            if (i->bar->GetHeader().GetAddr1() == recipient && i->tid == tid)
-            {
-                i = m_bars.erase(i);
-            }
-            else
-            {
-                i++;
-            }
-        }
+        m_originatorAgreements.erase(it);
     }
 }
 
 void
-BlockAckManager::UpdateAgreement(const MgtAddBaResponseHeader* respHdr,
-                                 Mac48Address recipient,
-                                 uint16_t startingSeq)
+BlockAckManager::UpdateOriginatorAgreement(const MgtAddBaResponseHeader& respHdr,
+                                           const Mac48Address& recipient,
+                                           uint16_t startingSeq)
 {
     NS_LOG_FUNCTION(this << respHdr << recipient << startingSeq);
-    uint8_t tid = respHdr->GetTid();
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    if (it != m_agreements.end())
+    uint8_t tid = respHdr.GetTid();
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it != m_originatorAgreements.end())
     {
         OriginatorBlockAckAgreement& agreement = it->second.first;
-        agreement.SetBufferSize(respHdr->GetBufferSize());
-        agreement.SetTimeout(respHdr->GetTimeout());
-        agreement.SetAmsduSupport(respHdr->IsAmsduSupported());
+        agreement.SetBufferSize(respHdr.GetBufferSize());
+        agreement.SetTimeout(respHdr.GetTimeout());
+        agreement.SetAmsduSupport(respHdr.IsAmsduSupported());
         agreement.SetStartingSequence(startingSeq);
         agreement.InitTxWindow();
-        if (respHdr->IsImmediateBlockAck())
+        if (respHdr.IsImmediateBlockAck())
         {
             agreement.SetImmediateBlockAck();
         }
@@ -214,10 +171,10 @@
         }
         if (!it->second.first.IsEstablished())
         {
-            m_agreementState(Simulator::Now(),
-                             recipient,
-                             tid,
-                             OriginatorBlockAckAgreement::ESTABLISHED);
+            m_originatorAgreementState(Simulator::Now(),
+                                       recipient,
+                                       tid,
+                                       OriginatorBlockAckAgreement::ESTABLISHED);
         }
         agreement.SetState(OriginatorBlockAckAgreement::ESTABLISHED);
         if (agreement.GetTimeout() != 0)
@@ -234,6 +191,50 @@
 }
 
 void
+BlockAckManager::CreateRecipientAgreement(const MgtAddBaResponseHeader& respHdr,
+                                          const Mac48Address& originator,
+                                          uint16_t startingSeq,
+                                          bool htSupported,
+                                          Ptr<MacRxMiddle> rxMiddle)
+{
+    NS_LOG_FUNCTION(this << respHdr << originator << startingSeq << htSupported << rxMiddle);
+    uint8_t tid = respHdr.GetTid();
+
+    RecipientBlockAckAgreement agreement(originator,
+                                         respHdr.IsAmsduSupported(),
+                                         tid,
+                                         respHdr.GetBufferSize(),
+                                         respHdr.GetTimeout(),
+                                         startingSeq,
+                                         htSupported);
+    agreement.SetMacRxMiddle(rxMiddle);
+    if (respHdr.IsImmediateBlockAck())
+    {
+        agreement.SetImmediateBlockAck();
+    }
+    else
+    {
+        agreement.SetDelayedBlockAck();
+    }
+
+    m_recipientAgreements.insert_or_assign({originator, tid}, agreement);
+}
+
+void
+BlockAckManager::DestroyRecipientAgreement(const Mac48Address& originator, uint8_t tid)
+{
+    NS_LOG_FUNCTION(this << originator << tid);
+
+    if (auto agreementIt = m_recipientAgreements.find({originator, tid});
+        agreementIt != m_recipientAgreements.end())
+    {
+        // forward up the buffered MPDUs before destroying the agreement
+        agreementIt->second.Flush();
+        m_recipientAgreements.erase(agreementIt);
+    }
+}
+
+void
 BlockAckManager::StorePacket(Ptr<WifiMpdu> mpdu)
 {
     NS_LOG_FUNCTION(this << *mpdu);
@@ -242,8 +243,8 @@
     uint8_t tid = mpdu->GetHeader().GetQosTid();
     Mac48Address recipient = mpdu->GetHeader().GetAddr1();
 
-    AgreementsI agreementIt = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(agreementIt != m_agreements.end());
+    auto agreementIt = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(agreementIt != m_originatorAgreements.end());
 
     uint16_t mpduDist =
         agreementIt->second.first.GetDistance(mpdu->GetHeader().GetSequenceNumber());
@@ -278,77 +279,14 @@
     }
     agreementIt->second.second.insert(it.base(), mpdu);
     agreementIt->second.first.NotifyTransmittedMpdu(mpdu);
-    mpdu->SetInFlight();
-}
-
-Ptr<const WifiMpdu>
-BlockAckManager::GetBar(bool remove, uint8_t tid, Mac48Address address)
-{
-    Time now = Simulator::Now();
-    Ptr<const WifiMpdu> bar;
-    // remove all expired MPDUs from the MAC queue, so that
-    // BlockAckRequest frames (if needed) are scheduled
-    m_queue->WipeAllExpiredMpdus();
-
-    auto nextBar = m_bars.begin();
-
-    while (nextBar != m_bars.end())
-    {
-        Mac48Address recipient = nextBar->bar->GetHeader().GetAddr1();
-
-        if (address != Mac48Address::GetBroadcast() && tid != 8 &&
-            (!nextBar->bar->GetHeader().IsBlockAckReq() || address != recipient ||
-             tid != nextBar->tid))
-        {
-            // we can only return a BAR addressed to the given station and for the given TID
-            nextBar++;
-            continue;
-        }
-        if (nextBar->bar->GetHeader().IsBlockAckReq())
-        {
-            auto bam = m_bamMap.at(QosUtilsMapTidToAc(nextBar->tid));
-            AgreementsI it = bam->m_agreements.find(std::make_pair(recipient, nextBar->tid));
-            if (it == m_agreements.end())
-            {
-                // BA agreement was torn down; remove this BAR and continue
-                nextBar = m_bars.erase(nextBar);
-                continue;
-            }
-            if (nextBar->skipIfNoDataQueued &&
-                !m_queue->PeekByTidAndAddress(nextBar->tid, recipient))
-            {
-                // skip this BAR as there is no data queued
-                nextBar++;
-                continue;
-            }
-            // update BAR if the starting sequence number changed
-            CtrlBAckRequestHeader reqHdr;
-            nextBar->bar->GetPacket()->PeekHeader(reqHdr);
-            if (reqHdr.GetStartingSequence() != it->second.first.GetStartingSequence())
-            {
-                reqHdr.SetStartingSequence(it->second.first.GetStartingSequence());
-                Ptr<Packet> packet = Create<Packet>();
-                packet->AddHeader(reqHdr);
-                nextBar->bar = Create<const WifiMpdu>(packet, nextBar->bar->GetHeader());
-            }
-        }
-
-        bar = nextBar->bar;
-        if (remove)
-        {
-            m_bars.erase(nextBar);
-        }
-        break;
-    }
-    return bar;
 }
 
 uint32_t
-BlockAckManager::GetNBufferedPackets(Mac48Address recipient, uint8_t tid) const
+BlockAckManager::GetNBufferedPackets(const Mac48Address& recipient, uint8_t tid) const
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
-    AgreementsCI it = m_agreements.find(std::make_pair(recipient, tid));
-    if (it == m_agreements.end())
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it == m_originatorAgreements.end())
     {
         return 0;
     }
@@ -363,12 +301,13 @@
 }
 
 BlockAckManager::PacketQueueI
-BlockAckManager::HandleInFlightMpdu(PacketQueueI mpduIt,
+BlockAckManager::HandleInFlightMpdu(uint8_t linkId,
+                                    PacketQueueI mpduIt,
                                     MpduStatus status,
-                                    const AgreementsI& it,
+                                    const OriginatorAgreementsI& it,
                                     const Time& now)
 {
-    NS_LOG_FUNCTION(this << **mpduIt << +static_cast<uint8_t>(status));
+    NS_LOG_FUNCTION(this << linkId << **mpduIt << +static_cast<uint8_t>(status));
 
     if (!(*mpduIt)->IsQueued())
     {
@@ -384,7 +323,7 @@
         return it->second.second.erase(mpduIt);
     }
 
-    WifiMacHeader& hdr = (*mpduIt)->GetHeader();
+    const WifiMacHeader& hdr = (*mpduIt)->GetHeader();
 
     NS_ASSERT(hdr.GetAddr1() == it->first.first);
     NS_ASSERT(hdr.IsQosData() && hdr.GetQosTid() == it->first.second);
@@ -423,23 +362,23 @@
 
     NS_ASSERT(status == TO_RETRANSMIT);
     (*mpduIt)->GetHeader().SetRetry();
-    (*mpduIt)->ResetInFlight(); // no longer in flight; will be if retransmitted
+    (*mpduIt)->ResetInFlight(linkId); // no longer in flight; will be if retransmitted
 
     return it->second.second.erase(mpduIt);
 }
 
 void
-BlockAckManager::NotifyGotAck(Ptr<const WifiMpdu> mpdu)
+BlockAckManager::NotifyGotAck(uint8_t linkId, Ptr<const WifiMpdu> mpdu)
 {
-    NS_LOG_FUNCTION(this << *mpdu);
+    NS_LOG_FUNCTION(this << linkId << *mpdu);
     NS_ASSERT(mpdu->GetHeader().IsQosData());
 
-    Mac48Address recipient = mpdu->GetHeader().GetAddr1();
+    Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
     uint8_t tid = mpdu->GetHeader().GetQosTid();
-    NS_ASSERT(ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED));
 
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(it != m_agreements.end());
+    auto it = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(it != m_originatorAgreements.end());
+    NS_ASSERT(it->second.first.IsEstablished());
 
     it->second.first.NotifyAckedMpdu(mpdu);
 
@@ -449,24 +388,24 @@
         if ((*queueIt)->GetHeader().GetSequenceNumber() == mpdu->GetHeader().GetSequenceNumber())
         {
             m_queue->DequeueIfQueued({*queueIt});
-            HandleInFlightMpdu(queueIt, ACKNOWLEDGED, it, Simulator::Now());
+            HandleInFlightMpdu(linkId, queueIt, ACKNOWLEDGED, it, Simulator::Now());
             break;
         }
     }
 }
 
 void
-BlockAckManager::NotifyMissedAck(Ptr<WifiMpdu> mpdu)
+BlockAckManager::NotifyMissedAck(uint8_t linkId, Ptr<WifiMpdu> mpdu)
 {
-    NS_LOG_FUNCTION(this << *mpdu);
+    NS_LOG_FUNCTION(this << linkId << *mpdu);
     NS_ASSERT(mpdu->GetHeader().IsQosData());
 
-    Mac48Address recipient = mpdu->GetHeader().GetAddr1();
+    Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
     uint8_t tid = mpdu->GetHeader().GetQosTid();
-    NS_ASSERT(ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED));
 
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(it != m_agreements.end());
+    auto it = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(it != m_originatorAgreements.end());
+    NS_ASSERT(it->second.first.IsEstablished());
 
     // remove the frame from the queue of outstanding packets (it will be re-inserted
     // if retransmitted)
@@ -474,21 +413,20 @@
     {
         if ((*queueIt)->GetHeader().GetSequenceNumber() == mpdu->GetHeader().GetSequenceNumber())
         {
-            HandleInFlightMpdu(queueIt, TO_RETRANSMIT, it, Simulator::Now());
+            HandleInFlightMpdu(linkId, queueIt, TO_RETRANSMIT, it, Simulator::Now());
             break;
         }
     }
 }
 
 std::pair<uint16_t, uint16_t>
-BlockAckManager::NotifyGotBlockAck(const CtrlBAckResponseHeader& blockAck,
-                                   Mac48Address recipient,
+BlockAckManager::NotifyGotBlockAck(uint8_t linkId,
+                                   const CtrlBAckResponseHeader& blockAck,
+                                   const Mac48Address& recipient,
                                    const std::set<uint8_t>& tids,
                                    size_t index)
 {
-    NS_LOG_FUNCTION(this << blockAck << recipient << index);
-    uint16_t nSuccessfulMpdus = 0;
-    uint16_t nFailedMpdus = 0;
+    NS_LOG_FUNCTION(this << linkId << blockAck << recipient << index);
 
     NS_ABORT_MSG_IF(blockAck.IsBasic(), "Basic Block Ack is not supported");
     NS_ABORT_MSG_IF(blockAck.IsMultiTid(), "Multi-TID Block Ack is not supported");
@@ -501,83 +439,104 @@
         NS_ASSERT(blockAck.GetAckType(index) && tids.size() == 1);
         tid = *tids.begin();
     }
-    if (ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
+
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
+    {
+        return {0, 0};
+    }
+
+    uint16_t nSuccessfulMpdus = 0;
+    uint16_t nFailedMpdus = 0;
+
+    if (it->second.first.m_inactivityEvent.IsRunning())
     {
-        AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
+        /* Upon reception of a BlockAck frame, the inactivity timer at the
+            originator must be reset.
+            For more details see section 11.5.3 in IEEE802.11e standard */
+        it->second.first.m_inactivityEvent.Cancel();
+        Time timeout = MicroSeconds(1024 * it->second.first.GetTimeout());
+        it->second.first.m_inactivityEvent =
+            Simulator::Schedule(timeout, &BlockAckManager::InactivityTimeout, this, recipient, tid);
+    }
+
+    NS_ASSERT(blockAck.IsCompressed() || blockAck.IsExtendedCompressed() || blockAck.IsMultiSta());
+    Time now = Simulator::Now();
+    std::list<Ptr<const WifiMpdu>> acked;
 
-        if (it->second.first.m_inactivityEvent.IsRunning())
+    for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
+    {
+        uint16_t currentSeq = (*queueIt)->GetHeader().GetSequenceNumber();
+        NS_LOG_DEBUG("Current seq=" << currentSeq);
+        if (blockAck.IsPacketReceived(currentSeq, index))
         {
-            /* Upon reception of a BlockAck frame, the inactivity timer at the
-                originator must be reset.
-                For more details see section 11.5.3 in IEEE802.11e standard */
-            it->second.first.m_inactivityEvent.Cancel();
-            Time timeout = MicroSeconds(1024 * it->second.first.GetTimeout());
-            it->second.first.m_inactivityEvent =
-                Simulator::Schedule(timeout,
-                                    &BlockAckManager::InactivityTimeout,
-                                    this,
-                                    recipient,
-                                    tid);
-        }
-
-        NS_ASSERT(blockAck.IsCompressed() || blockAck.IsExtendedCompressed() ||
-                  blockAck.IsMultiSta());
-        Time now = Simulator::Now();
-        std::list<Ptr<const WifiMpdu>> acked;
-
-        for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
-        {
-            uint16_t currentSeq = (*queueIt)->GetHeader().GetSequenceNumber();
-            NS_LOG_DEBUG("Current seq=" << currentSeq);
-            if (blockAck.IsPacketReceived(currentSeq, index))
-            {
-                it->second.first.NotifyAckedMpdu(*queueIt);
-                nSuccessfulMpdus++;
-                if (!m_txOkCallback.IsNull())
-                {
-                    m_txOkCallback(*queueIt);
-                }
-                acked.emplace_back(*queueIt);
-                queueIt = HandleInFlightMpdu(queueIt, ACKNOWLEDGED, it, now);
-            }
-            else
+            it->second.first.NotifyAckedMpdu(*queueIt);
+            nSuccessfulMpdus++;
+            if (!m_txOkCallback.IsNull())
             {
-                ++queueIt;
+                m_txOkCallback(*queueIt);
             }
+            acked.emplace_back(*queueIt);
+            queueIt = HandleInFlightMpdu(linkId, queueIt, ACKNOWLEDGED, it, now);
+        }
+        else
+        {
+            ++queueIt;
         }
+    }
+
+    // Dequeue all acknowledged MPDUs at once
+    m_queue->DequeueIfQueued(acked);
 
-        // Dequeue all acknowledged MPDUs at once
-        m_queue->DequeueIfQueued(acked);
+    // Remaining outstanding MPDUs have not been acknowledged
+    for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
+    {
+        // transmission actually failed if the MPDU is inflight only on the same link on
+        // which we received the BlockAck frame
+        auto linkIds = (*queueIt)->GetInFlightLinkIds();
 
-        // Remaining outstanding MPDUs have not been acknowledged
-        for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
+        if (linkIds.size() == 1 && *linkIds.begin() == linkId)
         {
             nFailedMpdus++;
             if (!m_txFailedCallback.IsNull())
             {
                 m_txFailedCallback(*queueIt);
             }
-            queueIt = HandleInFlightMpdu(queueIt, TO_RETRANSMIT, it, now);
+            queueIt = HandleInFlightMpdu(linkId, queueIt, TO_RETRANSMIT, it, now);
+            continue;
         }
+
+        queueIt = HandleInFlightMpdu(linkId, queueIt, STAY_INFLIGHT, it, now);
     }
+
     return {nSuccessfulMpdus, nFailedMpdus};
 }
 
 void
-BlockAckManager::NotifyMissedBlockAck(Mac48Address recipient, uint8_t tid)
+BlockAckManager::NotifyMissedBlockAck(uint8_t linkId, const Mac48Address& recipient, uint8_t tid)
 {
-    NS_LOG_FUNCTION(this << recipient << +tid);
-    if (ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
+    NS_LOG_FUNCTION(this << linkId << recipient << +tid);
+
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
     {
-        AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-        Time now = Simulator::Now();
+        return;
+    }
 
-        // remove all packets from the queue of outstanding packets (they will be
-        // re-inserted if retransmitted)
-        for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
+    Time now = Simulator::Now();
+
+    // remove all packets from the queue of outstanding packets (they will be
+    // re-inserted if retransmitted)
+    for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
+    {
+        // MPDUs that were transmitted on another link shall stay inflight
+        auto linkIds = (*mpduIt)->GetInFlightLinkIds();
+        if (linkIds.count(linkId) == 0)
         {
-            mpduIt = HandleInFlightMpdu(mpduIt, TO_RETRANSMIT, it, now);
+            mpduIt = HandleInFlightMpdu(linkId, mpduIt, STAY_INFLIGHT, it, now);
+            continue;
         }
+        mpduIt = HandleInFlightMpdu(linkId, mpduIt, TO_RETRANSMIT, it, now);
     }
 }
 
@@ -598,15 +557,15 @@
         return;
     }
 
-    Mac48Address recipient = mpdu->GetHeader().GetAddr1();
+    Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
     uint8_t tid = mpdu->GetHeader().GetQosTid();
-    if (!ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
     {
         NS_LOG_DEBUG("No established Block Ack agreement");
         return;
     }
 
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
     uint16_t currStartingSeq = it->second.first.GetStartingSequence();
     if (QosUtilsIsOldPacket(currStartingSeq, mpdu->GetHeader().GetSequenceNumber()))
     {
@@ -647,22 +606,51 @@
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_CTL_BACKREQ);
     hdr.SetAddr1(recipient);
-    hdr.SetAddr2(mpdu->GetHeader().GetAddr2());
-    hdr.SetAddr3(mpdu->GetHeader().GetAddr3());
+    hdr.SetAddr2(mpdu->GetOriginal()->GetHeader().GetAddr2());
     hdr.SetDsNotTo();
     hdr.SetDsNotFrom();
     hdr.SetNoRetry();
     hdr.SetNoMoreFragments();
 
-    ScheduleBar(Create<const WifiMpdu>(bar, hdr));
+    ScheduleBar(Create<WifiMpdu>(bar, hdr));
+}
+
+void
+BlockAckManager::NotifyGotBlockAckRequest(const Mac48Address& originator,
+                                          uint8_t tid,
+                                          uint16_t startingSeq)
+{
+    NS_LOG_FUNCTION(this << originator << tid << startingSeq);
+    auto it = m_recipientAgreements.find({originator, tid});
+    if (it == m_recipientAgreements.end())
+    {
+        return;
+    }
+    it->second.NotifyReceivedBar(startingSeq);
+}
+
+void
+BlockAckManager::NotifyGotMpdu(Ptr<const WifiMpdu> mpdu)
+{
+    NS_LOG_FUNCTION(this << *mpdu);
+    auto originator = mpdu->GetOriginal()->GetHeader().GetAddr2();
+    NS_ASSERT(mpdu->GetHeader().IsQosData());
+    auto tid = mpdu->GetHeader().GetQosTid();
+
+    auto it = m_recipientAgreements.find({originator, tid});
+    if (it == m_recipientAgreements.end())
+    {
+        return;
+    }
+    it->second.NotifyReceivedMpdu(mpdu);
 }
 
 CtrlBAckRequestHeader
-BlockAckManager::GetBlockAckReqHeader(Mac48Address recipient, uint8_t tid) const
+BlockAckManager::GetBlockAckReqHeader(const Mac48Address& recipient, uint8_t tid) const
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
-    AgreementsCI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(it != m_agreements.end());
+    auto it = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(it != m_originatorAgreements.end());
 
     CtrlBAckRequestHeader reqHdr;
     reqHdr.SetType((*it).second.first.GetBlockAckReqType());
@@ -672,116 +660,150 @@
 }
 
 void
-BlockAckManager::ScheduleBar(Ptr<const WifiMpdu> bar, bool skipIfNoDataQueued)
+BlockAckManager::ScheduleBar(Ptr<WifiMpdu> bar)
 {
     NS_LOG_FUNCTION(this << *bar);
-    NS_ASSERT(bar->GetHeader().IsBlockAckReq() || bar->GetHeader().IsTrigger());
+    NS_ASSERT(bar->GetHeader().IsBlockAckReq());
 
-    uint8_t tid = 0;
-    if (bar->GetHeader().IsBlockAckReq())
-    {
-        CtrlBAckRequestHeader reqHdr;
-        bar->GetPacket()->PeekHeader(reqHdr);
-        tid = reqHdr.GetTidInfo();
-    }
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    else
-    {
-        CtrlTriggerHeader triggerHdr;
-        bar->GetPacket()->PeekHeader(triggerHdr);
-        NS_ASSERT(triggerHdr.IsMuBar());
-    }
-#endif
-    Bar request(bar, tid, skipIfNoDataQueued);
+    CtrlBAckRequestHeader reqHdr;
+    bar->GetPacket()->PeekHeader(reqHdr);
+    uint8_t tid = reqHdr.GetTidInfo();
 
-    // if a BAR for the given agreement is present, replace it with the new one
-    std::list<Bar>::const_iterator i = m_bars.end();
+    WifiContainerQueueId queueId(WIFI_CTL_QUEUE, bar->GetHeader().GetAddr2(), std::nullopt);
+    Ptr<WifiMpdu> item = nullptr;
 
-    if (bar->GetHeader().IsBlockAckReq())
+    // if a BAR for the given agreement is present, replace it with the new one
+    while ((item = m_queue->PeekByQueueId(queueId, item)))
     {
-        for (i = m_bars.begin(); i != m_bars.end(); i++)
+        if (item->GetHeader().IsBlockAckReq() &&
+            item->GetHeader().GetAddr1() == bar->GetHeader().GetAddr1())
         {
-            if (i->bar->GetHeader().IsBlockAckReq() &&
-                i->bar->GetHeader().GetAddr1() == bar->GetHeader().GetAddr1() && i->tid == tid)
+            CtrlBAckRequestHeader otherHdr;
+            item->GetPacket()->PeekHeader(otherHdr);
+            if (otherHdr.GetTidInfo() == tid)
             {
-                i = m_bars.erase(i);
-                break;
+                // replace item with bar
+                m_queue->Replace(item, bar);
+                return;
             }
         }
     }
 
-    if (bar->GetHeader().IsRetry())
-    {
-        m_bars.push_front(request);
-    }
-    else
+    m_queue->Enqueue(bar);
+}
+
+void
+BlockAckManager::ScheduleMuBar(Ptr<WifiMpdu> muBar)
+{
+    NS_LOG_FUNCTION(this << *muBar);
+    NS_ASSERT(muBar->GetHeader().IsTrigger());
+
+#ifdef NS3_BUILD_PROFILE_DEBUG
+    CtrlTriggerHeader triggerHdr;
+    muBar->GetPacket()->PeekHeader(triggerHdr);
+    NS_ASSERT(triggerHdr.IsMuBar());
+#endif
+
+    m_queue->Enqueue(muBar);
+}
+
+const std::list<BlockAckManager::AgreementKey>&
+BlockAckManager::GetSendBarIfDataQueuedList() const
+{
+    return m_sendBarIfDataQueued;
+}
+
+void
+BlockAckManager::AddToSendBarIfDataQueuedList(const Mac48Address& recipient, uint8_t tid)
+{
+    NS_LOG_FUNCTION(this << recipient << tid);
+    // do nothing if the given pair is already in the list
+    if (std::find(m_sendBarIfDataQueued.begin(),
+                  m_sendBarIfDataQueued.end(),
+                  BlockAckManager::AgreementKey{recipient, tid}) == m_sendBarIfDataQueued.end())
     {
-        m_bars.insert(i, request);
+        m_sendBarIfDataQueued.emplace_back(recipient, tid);
     }
 }
 
 void
-BlockAckManager::InactivityTimeout(Mac48Address recipient, uint8_t tid)
+BlockAckManager::RemoveFromSendBarIfDataQueuedList(const Mac48Address& recipient, uint8_t tid)
+{
+    NS_LOG_FUNCTION(this << recipient << tid);
+    m_sendBarIfDataQueued.remove({recipient, tid});
+}
+
+void
+BlockAckManager::InactivityTimeout(const Mac48Address& recipient, uint8_t tid)
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
     m_blockAckInactivityTimeout(recipient, tid, true);
 }
 
 void
-BlockAckManager::NotifyAgreementEstablished(Mac48Address recipient,
-                                            uint8_t tid,
-                                            uint16_t startingSeq)
+BlockAckManager::NotifyOriginatorAgreementEstablished(const Mac48Address& recipient,
+                                                      uint8_t tid,
+                                                      uint16_t startingSeq)
 {
     NS_LOG_FUNCTION(this << recipient << +tid << startingSeq);
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(it != m_agreements.end());
+    auto it = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(it != m_originatorAgreements.end());
     if (!it->second.first.IsEstablished())
     {
-        m_agreementState(Simulator::Now(),
-                         recipient,
-                         tid,
-                         OriginatorBlockAckAgreement::ESTABLISHED);
+        m_originatorAgreementState(Simulator::Now(),
+                                   recipient,
+                                   tid,
+                                   OriginatorBlockAckAgreement::ESTABLISHED);
     }
     it->second.first.SetState(OriginatorBlockAckAgreement::ESTABLISHED);
     it->second.first.SetStartingSequence(startingSeq);
 }
 
 void
-BlockAckManager::NotifyAgreementRejected(Mac48Address recipient, uint8_t tid)
+BlockAckManager::NotifyOriginatorAgreementRejected(const Mac48Address& recipient, uint8_t tid)
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(it != m_agreements.end());
+    auto it = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(it != m_originatorAgreements.end());
     if (!it->second.first.IsRejected())
     {
-        m_agreementState(Simulator::Now(), recipient, tid, OriginatorBlockAckAgreement::REJECTED);
+        m_originatorAgreementState(Simulator::Now(),
+                                   recipient,
+                                   tid,
+                                   OriginatorBlockAckAgreement::REJECTED);
     }
     it->second.first.SetState(OriginatorBlockAckAgreement::REJECTED);
 }
 
 void
-BlockAckManager::NotifyAgreementNoReply(Mac48Address recipient, uint8_t tid)
+BlockAckManager::NotifyOriginatorAgreementNoReply(const Mac48Address& recipient, uint8_t tid)
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(it != m_agreements.end());
+    auto it = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(it != m_originatorAgreements.end());
     if (!it->second.first.IsNoReply())
     {
-        m_agreementState(Simulator::Now(), recipient, tid, OriginatorBlockAckAgreement::NO_REPLY);
+        m_originatorAgreementState(Simulator::Now(),
+                                   recipient,
+                                   tid,
+                                   OriginatorBlockAckAgreement::NO_REPLY);
     }
     it->second.first.SetState(OriginatorBlockAckAgreement::NO_REPLY);
     m_unblockPackets(recipient, tid);
 }
 
 void
-BlockAckManager::NotifyAgreementReset(Mac48Address recipient, uint8_t tid)
+BlockAckManager::NotifyOriginatorAgreementReset(const Mac48Address& recipient, uint8_t tid)
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
-    AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ASSERT(it != m_agreements.end());
+    auto it = m_originatorAgreements.find({recipient, tid});
+    NS_ASSERT(it != m_originatorAgreements.end());
     if (!it->second.first.IsReset())
     {
-        m_agreementState(Simulator::Now(), recipient, tid, OriginatorBlockAckAgreement::RESET);
+        m_originatorAgreementState(Simulator::Now(),
+                                   recipient,
+                                   tid,
+                                   OriginatorBlockAckAgreement::RESET);
     }
     it->second.first.SetState(OriginatorBlockAckAgreement::RESET);
 }
@@ -794,50 +816,31 @@
 }
 
 bool
-BlockAckManager::SwitchToBlockAckIfNeeded(Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
+BlockAckManager::NeedBarRetransmission(uint8_t tid, const Mac48Address& recipient)
 {
-    NS_LOG_FUNCTION(this << recipient << +tid << startingSeq);
-    NS_ASSERT(!ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::PENDING));
-    if (!ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::REJECTED) &&
-        ExistsAgreement(recipient, tid))
-    {
-        WifiContainerQueueId queueId{WIFI_QOSDATA_UNICAST_QUEUE, recipient, tid};
-        uint32_t packets = m_queue->GetNPackets(queueId) + GetNBufferedPackets(recipient, tid);
-        if (packets >= m_blockAckThreshold)
-        {
-            NotifyAgreementEstablished(recipient, tid, startingSeq);
-            return true;
-        }
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
+    {
+        // If the inactivity timer has expired, QosTxop::SendDelbaFrame has been called and
+        // has destroyed the agreement, hence we get here and correctly return false
+        return false;
     }
-    return false;
-}
 
-bool
-BlockAckManager::NeedBarRetransmission(uint8_t tid, Mac48Address recipient)
-{
-    if (ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
-    {
-        AgreementsI it = m_agreements.find(std::make_pair(recipient, tid));
-        NS_ASSERT(it != m_agreements.end());
+    Time now = Simulator::Now();
 
-        Time now = Simulator::Now();
+    // A BAR needs to be retransmitted if there is at least a non-expired in flight MPDU
+    for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
+    {
+        // remove MPDU if old or with expired lifetime
+        mpduIt = HandleInFlightMpdu(SINGLE_LINK_OP_ID, mpduIt, STAY_INFLIGHT, it, now);
 
-        // A BAR needs to be retransmitted if there is at least a non-expired in flight MPDU
-        for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
+        if (mpduIt != it->second.second.begin())
         {
-            // remove MPDU if old or with expired lifetime
-            mpduIt = HandleInFlightMpdu(mpduIt, STAY_INFLIGHT, it, now);
-
-            if (mpduIt != it->second.second.begin())
-            {
-                // the MPDU has not been removed
-                return true;
-            }
+            // the MPDU has not been removed
+            return true;
         }
     }
 
-    // If the inactivity timer has expired, QosTxop::SendDelbaFrame has been called and
-    // has destroyed the agreement, hence we get here and correctly return false
     return false;
 }
 
@@ -881,39 +884,23 @@
 }
 
 uint16_t
-BlockAckManager::GetRecipientBufferSize(Mac48Address recipient, uint8_t tid) const
+BlockAckManager::GetRecipientBufferSize(const Mac48Address& recipient, uint8_t tid) const
 {
     uint16_t size = 0;
-    AgreementsCI it = m_agreements.find(std::make_pair(recipient, tid));
-    if (it != m_agreements.end())
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it != m_originatorAgreements.end())
     {
         size = it->second.first.GetBufferSize();
     }
     return size;
 }
 
-BlockAckReqType
-BlockAckManager::GetBlockAckReqType(Mac48Address recipient, uint8_t tid) const
-{
-    AgreementsCI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ABORT_MSG_IF(it == m_agreements.end(), "No established Block Ack agreement");
-    return it->second.first.GetBlockAckReqType();
-}
-
-BlockAckType
-BlockAckManager::GetBlockAckType(Mac48Address recipient, uint8_t tid) const
-{
-    AgreementsCI it = m_agreements.find(std::make_pair(recipient, tid));
-    NS_ABORT_MSG_IF(it == m_agreements.end(), "No established Block Ack agreement");
-    return it->second.first.GetBlockAckType();
-}
-
 uint16_t
-BlockAckManager::GetOriginatorStartingSequence(Mac48Address recipient, uint8_t tid) const
+BlockAckManager::GetOriginatorStartingSequence(const Mac48Address& recipient, uint8_t tid) const
 {
     uint16_t seqNum = 0;
-    AgreementsCI it = m_agreements.find(std::make_pair(recipient, tid));
-    if (it != m_agreements.end())
+    auto it = m_originatorAgreements.find({recipient, tid});
+    if (it != m_originatorAgreements.end())
     {
         seqNum = it->second.first.GetStartingSequence();
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/block-ack-manager.h ns-3.38/src/wifi/model/block-ack-manager.h
--- ns-3.37/src/wifi/model/block-ack-manager.h	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/block-ack-manager.h	2023-03-16 20:54:23.852869300 -0700
@@ -22,6 +22,7 @@
 
 #include "block-ack-type.h"
 #include "originator-block-ack-agreement.h"
+#include "recipient-block-ack-agreement.h"
 #include "wifi-mac-header.h"
 #include "wifi-mpdu.h"
 #include "wifi-tx-vector.h"
@@ -40,29 +41,7 @@
 class CtrlBAckResponseHeader;
 class CtrlBAckRequestHeader;
 class WifiMacQueue;
-class WifiMode;
-class Packet;
-
-/**
- * \ingroup wifi
- * \brief BlockAckRequest frame information
- *
- */
-struct Bar
-{
-    Bar();
-    /**
-     * Store a BlockAckRequest along with the corresponding TID or a MU-BAR Trigger Frame.
-     *
-     * \param bar the BAR
-     * \param tid the Traffic ID
-     * \param skipIfNoDataQueued true to hold this BAR if there is no data queued
-     */
-    Bar(Ptr<const WifiMpdu> bar, uint8_t tid, bool skipIfNoDataQueued = false);
-    Ptr<const WifiMpdu> bar; ///< BlockAckRequest or MU-BAR Trigger Frame
-    uint8_t tid;             ///< TID (unused if MU-BAR)
-    bool skipIfNoDataQueued; ///< do not send if there is no data queued (unused if MU-BAR)
-};
+class MacRxMiddle;
 
 /**
  * \brief Manages all block ack agreements for an originator station.
@@ -95,46 +74,45 @@
     BlockAckManager(const BlockAckManager&) = delete;
     BlockAckManager& operator=(const BlockAckManager&) = delete;
 
+    /// optional const reference to OriginatorBlockAckAgreement
+    using OriginatorAgreementOptConstRef =
+        std::optional<std::reference_wrapper<const OriginatorBlockAckAgreement>>;
+    /// optional const reference to RecipientBlockAckAgreement
+    using RecipientAgreementOptConstRef =
+        std::optional<std::reference_wrapper<const RecipientBlockAckAgreement>>;
+
     /**
-     * Provide information about all the Block Ack Managers installed on this device.
-     *
-     * \param bamMap an AC-indexed map of all the Block Ack Managers installed on this device
-     */
-    void SetBlockAckManagerMap(const std::map<AcIndex, Ptr<BlockAckManager>>& bamMap);
-    /**
-     * \param recipient Address of peer station involved in block ack mechanism.
-     * \param tid Traffic ID.
+     * \param recipient MAC address of the recipient
+     * \param tid Traffic ID
      *
-     * \return true  if a block ack agreement exists, false otherwise
+     * \return a const reference to the block ack agreement with the given recipient, if it exists
      *
-     * Checks if a block ack agreement exists with station addressed by
-     * <i>recipient</i> for TID <i>tid</i>.
+     * Check if we are the originator of an existing block ack agreement with the given recipient.
      */
-    bool ExistsAgreement(Mac48Address recipient, uint8_t tid) const;
+    OriginatorAgreementOptConstRef GetAgreementAsOriginator(const Mac48Address& recipient,
+                                                            uint8_t tid) const;
     /**
-     * \param recipient Address of peer station involved in block ack mechanism.
-     * \param tid Traffic ID.
-     * \param state The state for block ack agreement
+     * \param originator MAC address of the originator
+     * \param tid Traffic ID
      *
-     * \return true if a block ack agreement exists, false otherwise
+     * \return a const reference to the block ack agreement with the given originator, if it exists
      *
-     * Checks if a block ack agreement with a state equals to <i>state</i> exists with
-     * station addressed by <i>recipient</i> for TID <i>tid</i>.
+     * Check if we are the recipient of an existing block ack agreement with the given originator.
      */
-    bool ExistsAgreementInState(Mac48Address recipient,
-                                uint8_t tid,
-                                OriginatorBlockAckAgreement::State state) const;
+    RecipientAgreementOptConstRef GetAgreementAsRecipient(const Mac48Address& originator,
+                                                          uint8_t tid) const;
+
     /**
      * \param reqHdr Relative Add block ack request (action frame).
      * \param recipient Address of peer station involved in block ack mechanism.
      * \param htSupported Whether both originator and recipient support HT
      *
-     * Creates a new block ack agreement in pending state. When a ADDBA response
+     * Creates a new originator block ack agreement in pending state. When a ADDBA response
      * with a successful status code is received, the relative agreement becomes established.
      */
-    void CreateAgreement(const MgtAddBaRequestHeader* reqHdr,
-                         Mac48Address recipient,
-                         bool htSupported = true);
+    void CreateOriginatorAgreement(const MgtAddBaRequestHeader& reqHdr,
+                                   const Mac48Address& recipient,
+                                   bool htSupported = true);
     /**
      * \param recipient Address of peer station involved in block ack mechanism.
      * \param tid traffic ID of transmitted packet.
@@ -142,7 +120,7 @@
      * Invoked when a recipient reject a block ack agreement or when a DELBA frame
      * is Received/Transmitted.
      */
-    void DestroyAgreement(Mac48Address recipient, uint8_t tid);
+    void DestroyOriginatorAgreement(const Mac48Address& recipient, uint8_t tid);
     /**
      * \param respHdr Relative Add block ack response (action frame).
      * \param recipient Address of peer station involved in block ack mechanism.
@@ -150,9 +128,40 @@
      *
      * Invoked upon receipt of a ADDBA response frame from <i>recipient</i>.
      */
-    void UpdateAgreement(const MgtAddBaResponseHeader* respHdr,
-                         Mac48Address recipient,
-                         uint16_t startingSeq);
+    void UpdateOriginatorAgreement(const MgtAddBaResponseHeader& respHdr,
+                                   const Mac48Address& recipient,
+                                   uint16_t startingSeq);
+
+    /**
+     * \param respHdr Add block ack response from originator (action
+     *        frame).
+     * \param originator Address of peer station involved in block ack
+     *        mechanism.
+     * \param startingSeq Sequence number of the first MPDU of all
+     *        packets for which block ack was negotiated.
+     * \param htSupported whether HT support is enabled
+     * \param rxMiddle the MAC RX Middle on this station
+     *
+     * This function is typically invoked only by ns3::WifiMac
+     * when the STA (which may be non-AP in ESS, or in an IBSS) has
+     * received an ADDBA Request frame and is transmitting an ADDBA
+     * Response frame. At this point the frame exchange manager must
+     * allocate buffers to collect all correctly received packets belonging
+     * to the category for which block ack was negotiated.
+     */
+    void CreateRecipientAgreement(const MgtAddBaResponseHeader& respHdr,
+                                  const Mac48Address& originator,
+                                  uint16_t startingSeq,
+                                  bool htSupported,
+                                  Ptr<MacRxMiddle> rxMiddle);
+    /**
+     * Destroy a recipient Block Ack agreement.
+     *
+     * \param originator the originator MAC address
+     * \param tid the TID associated with the Block Ack agreement
+     */
+    void DestroyRecipientAgreement(const Mac48Address& originator, uint8_t tid);
+
     /**
      * \param mpdu MPDU to store.
      *
@@ -161,39 +170,27 @@
      */
     void StorePacket(Ptr<WifiMpdu> mpdu);
     /**
-     * Returns the next BlockAckRequest or MU-BAR Trigger Frame to send, if any.
-     * If the given recipient is not the broadcast address and the given TID is less
-     * than 8, then only return a BlockAckRequest, if any, addressed to that recipient
-     * and for the given TID.
-     *
-     * \param remove true if the BAR has to be removed from the queue
-     * \param tid the TID
-     * \param recipient the recipient of the BAR
-     *
-     * \return the next BAR to be sent, if any
-     */
-    Ptr<const WifiMpdu> GetBar(bool remove = true,
-                               uint8_t tid = 8,
-                               Mac48Address recipient = Mac48Address::GetBroadcast());
-    /**
-     * Invoked upon receipt of an Ack frame after the transmission of a QoS data frame
-     * sent under an established block ack agreement. Remove the acknowledged frame
-     * from the outstanding packets and update the starting sequence number of the
+     * Invoked upon receipt of an Ack frame on the given link after the transmission of a
+     * QoS data frame sent under an established block ack agreement. Remove the acknowledged
+     * frame from the outstanding packets and update the starting sequence number of the
      * transmit window, if needed.
      *
+     * \param linkId the ID of the given link
      * \param mpdu The acknowledged MPDU.
      */
-    void NotifyGotAck(Ptr<const WifiMpdu> mpdu);
+    void NotifyGotAck(uint8_t linkId, Ptr<const WifiMpdu> mpdu);
     /**
-     * Invoked upon missed reception of an Ack frame after the transmission of a
-     * QoS data frame sent under an established block ack agreement. Remove the
-     * acknowledged frame from the outstanding packets and insert it in the
+     * Invoked upon missed reception of an Ack frame on the given link after the
+     * transmission of a QoS data frame sent under an established block ack agreement.
+     * Remove the acknowledged frame from the outstanding packets and insert it in the
      * retransmission queue.
      *
+     * \param linkId the ID of the given link
      * \param mpdu The unacknowledged MPDU.
      */
-    void NotifyMissedAck(Ptr<WifiMpdu> mpdu);
+    void NotifyMissedAck(uint8_t linkId, Ptr<WifiMpdu> mpdu);
     /**
+     * \param linkId the ID of the given link
      * \param blockAck The received BlockAck frame.
      * \param recipient Sender of BlockAck frame.
      * \param tids the set of TIDs the acknowledged MPDUs belong to
@@ -202,26 +199,44 @@
      * \return a pair of values indicating the number of successfully received MPDUs
      *         and the number of failed MPDUs
      *
-     * Invoked upon receipt of a BlockAck frame. Typically, this function, is called
-     * by ns3::QosTxop object. Performs a check on which MPDUs, previously sent
+     * Invoked upon receipt of a BlockAck frame on the given link. Typically, this function
+     * is called by ns3::QosTxop object. Performs a check on which MPDUs, previously sent
      * with Ack Policy set to Block Ack, were correctly received by the recipient.
      * An acknowledged MPDU is removed from the buffer, retransmitted otherwise.
      * Note that <i>tids</i> is only used if <i>blockAck</i> is a Multi-STA Block Ack
      * using All-ack context.
      */
-    std::pair<uint16_t, uint16_t> NotifyGotBlockAck(const CtrlBAckResponseHeader& blockAck,
-                                                    Mac48Address recipient,
+    std::pair<uint16_t, uint16_t> NotifyGotBlockAck(uint8_t linkId,
+                                                    const CtrlBAckResponseHeader& blockAck,
+                                                    const Mac48Address& recipient,
                                                     const std::set<uint8_t>& tids,
                                                     size_t index = 0);
     /**
+     * \param linkId the ID of the given link
      * \param recipient Sender of the expected BlockAck frame.
      * \param tid Traffic ID.
      *
-     * Invoked upon missed reception of a block ack frame. Typically, this function, is called
-     * by ns3::QosTxop object. Performs a check on which MPDUs, previously sent
-     * with ack policy set to Block Ack, should be placed in the retransmission queue.
+     * Invoked upon missed reception of a block ack frame on the given link. Typically, this
+     * function is called by ns3::QosTxop object. Performs a check on which MPDUs, previously
+     * sent with ack policy set to Block Ack, should be placed in the retransmission queue.
      */
-    void NotifyMissedBlockAck(Mac48Address recipient, uint8_t tid);
+    void NotifyMissedBlockAck(uint8_t linkId, const Mac48Address& recipient, uint8_t tid);
+    /**
+     * \param originator MAC address of the sender of the Block Ack Request
+     * \param tid Traffic ID
+     * \param startingSeq the starting sequence number in the Block Ack Request
+     *
+     * Perform required actions upon receiving a Block Ack Request frame.
+     */
+    void NotifyGotBlockAckRequest(const Mac48Address& originator,
+                                  uint8_t tid,
+                                  uint16_t startingSeq);
+    /**
+     * \param mpdu the received MPDU
+     *
+     * Perform required actions upon receiving an MPDU.
+     */
+    void NotifyGotMpdu(Ptr<const WifiMpdu> mpdu);
     /**
      * \param recipient Address of peer station involved in block ack mechanism.
      * \param tid Traffic ID.
@@ -231,42 +246,44 @@
      * Returns the number of packets buffered for a specified agreement. This methods doesn't return
      * the number of buffered MPDUs but the number of buffered MSDUs.
      */
-    uint32_t GetNBufferedPackets(Mac48Address recipient, uint8_t tid) const;
+    uint32_t GetNBufferedPackets(const Mac48Address& recipient, uint8_t tid) const;
     /**
      * \param recipient Address of peer station involved in block ack mechanism.
      * \param tid Traffic ID of transmitted packet.
      * \param startingSeq starting sequence field
      *
-     * Puts corresponding agreement in established state and updates number of packets
+     * Puts corresponding originator agreement in established state and updates number of packets
      * and starting sequence field. Invoked typically after a block ack refresh.
      */
-    void NotifyAgreementEstablished(Mac48Address recipient, uint8_t tid, uint16_t startingSeq);
+    void NotifyOriginatorAgreementEstablished(const Mac48Address& recipient,
+                                              uint8_t tid,
+                                              uint16_t startingSeq);
     /**
      * \param recipient Address of peer station involved in block ack mechanism.
      * \param tid Traffic ID of transmitted packet.
      *
-     * Marks an agreement as rejected. This happens if <i>recipient</i> station reject block ack
-     * setup by an ADDBA Response frame with a failure status code. For now we assume that every QoS
-     * station accepts a block ack setup.
+     * Marks an originator agreement as rejected. This happens if <i>recipient</i> station reject
+     * block ack setup by an ADDBA Response frame with a failure status code. For now we assume
+     * that every QoS station accepts a block ack setup.
      */
-    void NotifyAgreementRejected(Mac48Address recipient, uint8_t tid);
+    void NotifyOriginatorAgreementRejected(const Mac48Address& recipient, uint8_t tid);
     /**
      * \param recipient Address of peer station involved in block ack mechanism.
      * \param tid Traffic ID of transmitted packet.
      *
-     * Marks an agreement after not receiving response to ADDBA request. During this state
-     * any packets in queue will be transmitted using normal MPDU. This also unblock
+     * Marks an originator agreement after not receiving response to ADDBA request. During this
+     * state any packets in queue will be transmitted using normal MPDU. This also unblocks
      * recipient address.
      */
-    void NotifyAgreementNoReply(Mac48Address recipient, uint8_t tid);
+    void NotifyOriginatorAgreementNoReply(const Mac48Address& recipient, uint8_t tid);
     /**
      * \param recipient Address of peer station involved in block ack mechanism.
      * \param tid Traffic ID of transmitted packet.
      *
-     * Set BA agreement to a transitory state to reset it after not receiving response to ADDBA
-     * request.
+     * Set Originator BA agreement to a transitory state to reset it after not receiving response
+     * to ADDBA request.
      */
-    void NotifyAgreementReset(Mac48Address recipient, uint8_t tid);
+    void NotifyOriginatorAgreementReset(const Mac48Address& recipient, uint8_t tid);
     /**
      * \param nPackets Minimum number of packets for use of block ack.
      *
@@ -298,20 +315,6 @@
     void SetUnblockDestinationCallback(Callback<void, Mac48Address, uint8_t> callback);
 
     /**
-     * \param recipient the destination address
-     * \param tid the Traffic ID
-     * \param startingSeq the starting sequence number
-     *
-     * \return true if there are packets in the queue that could be sent under block ack,
-     *         false otherwise
-     *
-     * Checks if there are in the queue other packets that could be send under block ack.
-     * If yes adds these packets in current block ack exchange.
-     * However, number of packets exchanged in the current block ack, will not exceed
-     * the value of BufferSize in the corresponding OriginatorBlockAckAgreement object.
-     */
-    bool SwitchToBlockAckIfNeeded(Mac48Address recipient, uint8_t tid, uint16_t startingSeq);
-    /**
      * This function returns true if a block ack agreement is established with the
      * given recipient for the given TID and there is at least an outstanding MPDU
      * for such agreement whose lifetime is not expired.
@@ -321,7 +324,7 @@
      *
      * \returns true if BAR retransmission needed
      */
-    bool NeedBarRetransmission(uint8_t tid, Mac48Address recipient);
+    bool NeedBarRetransmission(uint8_t tid, const Mac48Address& recipient);
     /**
      * This function returns the buffer size negotiated with the recipient.
      *
@@ -330,25 +333,7 @@
      *
      * \returns the buffer size negotiated with the recipient
      */
-    uint16_t GetRecipientBufferSize(Mac48Address recipient, uint8_t tid) const;
-    /**
-     * This function returns the type of Block Acks sent to the recipient.
-     *
-     * \param recipient MAC address of recipient
-     * \param tid Traffic ID
-     *
-     * \returns the type of Block Acks sent to the recipient
-     */
-    BlockAckReqType GetBlockAckReqType(Mac48Address recipient, uint8_t tid) const;
-    /**
-     * This function returns the type of Block Acks sent by the recipient.
-     *
-     * \param recipient MAC address
-     * \param tid Traffic ID
-     *
-     * \returns the type of Block Acks sent by the recipient
-     */
-    BlockAckType GetBlockAckType(Mac48Address recipient, uint8_t tid) const;
+    uint16_t GetRecipientBufferSize(const Mac48Address& recipient, uint8_t tid) const;
     /**
      * This function returns the starting sequence number of the transmit window.
      *
@@ -357,7 +342,7 @@
      *
      * \returns the starting sequence number of the transmit window (WinStartO)
      */
-    uint16_t GetOriginatorStartingSequence(Mac48Address recipient, uint8_t tid) const;
+    uint16_t GetOriginatorStartingSequence(const Mac48Address& recipient, uint8_t tid) const;
 
     /**
      * typedef for a callback to invoke when an MPDU is successfully ack'ed.
@@ -379,7 +364,7 @@
     void SetTxOkCallback(TxOk callback);
     /**
      * \param callback the callback to invoke when a
-     * packet transmission was completed unsuccessfully.
+     * packet transmission was completed unsuccessfuly.
      */
     void SetTxFailedCallback(TxFailed callback);
     /**
@@ -396,7 +381,7 @@
      * \param [in] state The state.
      */
     typedef void (*AgreementStateTracedCallback)(Time now,
-                                                 Mac48Address recipient,
+                                                 const Mac48Address& recipient,
                                                  uint8_t tid,
                                                  OriginatorBlockAckAgreement::State state);
 
@@ -420,18 +405,50 @@
      * Get the BlockAckRequest header for the established BA agreement
      * (<i>recipient</i>,<i>tid</i>).
      */
-    CtrlBAckRequestHeader GetBlockAckReqHeader(Mac48Address recipient, uint8_t tid) const;
+    CtrlBAckRequestHeader GetBlockAckReqHeader(const Mac48Address& recipient, uint8_t tid) const;
 
     /**
      * \param bar the BlockAckRequest to enqueue
-     * \param skipIfNoDataQueued do not send if there is no data queued
      *
-     * Enqueue the given BlockAckRequest into the queue storing the next BAR
+     * Enqueue the given BlockAckRequest into the queue storing the next (MU-)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<const WifiMpdu> bar, bool skipIfNoDataQueued = false);
+    void ScheduleBar(Ptr<WifiMpdu> bar);
+    /**
+     * \param muBar the MU-BAR Trigger Frame to enqueue
+     *
+     * 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.
+     */
+    void ScheduleMuBar(Ptr<WifiMpdu> muBar);
+
+    /// agreement key typedef (MAC address and TID)
+    using AgreementKey = std::pair<Mac48Address, uint8_t>;
+
+    /**
+     * \return the list of BA agreements (identified by the recipient and TID pair) for which a BAR
+     * shall only be sent if there are queued data frames belonging to those agreements
+     */
+    const std::list<AgreementKey>& GetSendBarIfDataQueuedList() const;
+    /**
+     * Add the given (recipient, TID) pair to the list of BA agreements for which a BAR
+     * shall only be sent if there are queued data frames belonging to those agreements
+     *
+     * \param recipient the recipient
+     * \param tid the TID
+     */
+    void AddToSendBarIfDataQueuedList(const Mac48Address& recipient, uint8_t tid);
+    /**
+     * Remove the given (recipient, TID) pair from the list of BA agreements for which a BAR
+     * shall only be sent if there are queued data frames belonging to those agreements
+     *
+     * \param recipient the recipient
+     * \param tid the TID
+     */
+    void RemoveFromSendBarIfDataQueuedList(const Mac48Address& recipient, uint8_t tid);
 
   protected:
     void DoDispose() override;
@@ -442,7 +459,7 @@
      * \param recipient the recipient MAC address
      * \param tid Traffic ID
      */
-    void InactivityTimeout(Mac48Address recipient, uint8_t tid);
+    void InactivityTimeout(const Mac48Address& recipient, uint8_t tid);
 
     /**
      * typedef for a list of WifiMpdu.
@@ -452,27 +469,15 @@
      * typedef for an iterator for PacketQueue.
      */
     typedef std::list<Ptr<WifiMpdu>>::iterator PacketQueueI;
-    /**
-     * typedef for a const iterator for PacketQueue.
-     */
-    typedef std::list<Ptr<WifiMpdu>>::const_iterator PacketQueueCI;
-    /**
-     * typedef for a map between MAC address and block ack agreement.
-     */
-    typedef std::map<std::pair<Mac48Address, uint8_t>,
-                     std::pair<OriginatorBlockAckAgreement, PacketQueue>>
-        Agreements;
-    /**
-     * typedef for an iterator for Agreements.
-     */
-    typedef std::map<std::pair<Mac48Address, uint8_t>,
-                     std::pair<OriginatorBlockAckAgreement, PacketQueue>>::iterator AgreementsI;
-    /**
-     * typedef for a const iterator for Agreements.
-     */
-    typedef std::map<std::pair<Mac48Address, uint8_t>,
-                     std::pair<OriginatorBlockAckAgreement, PacketQueue>>::const_iterator
-        AgreementsCI;
+
+    /// AgreementKey-indexed map of originator block ack agreements
+    using OriginatorAgreements =
+        std::map<AgreementKey, std::pair<OriginatorBlockAckAgreement, PacketQueue>>;
+    /// typedef for an iterator for Agreements
+    using OriginatorAgreementsI = OriginatorAgreements::iterator;
+
+    /// AgreementKey-indexed map of recipient block ack agreements
+    using RecipientAgreements = std::map<AgreementKey, RecipientBlockAckAgreement>;
 
     /**
      * Handle the given in flight MPDU based on its given status. If the status is
@@ -482,30 +487,33 @@
      * queues (independently of the status) if the MPDU is not stored in the EDCA
      * queue, is an old packet or its lifetime expired.
      *
+     * \param linkId the ID of the link on which the MPDU has been transmitted
      * \param mpduIt an iterator pointing to the MPDU in the queue of in flight MPDUs
      * \param status the status of the in flight MPDU
      * \param it iterator pointing to the Block Ack agreement
      * \param now the current time
      * \return an iterator pointing to the next MPDU in the queue of in flight MPDUs
      */
-    PacketQueueI HandleInFlightMpdu(PacketQueueI mpduIt,
+    PacketQueueI HandleInFlightMpdu(uint8_t linkId,
+                                    PacketQueueI mpduIt,
                                     MpduStatus status,
-                                    const AgreementsI& it,
+                                    const OriginatorAgreementsI& it,
                                     const Time& now);
 
     /**
-     * This data structure contains, for each block ack agreement (recipient, TID), a set of packets
-     * for which an ack by block ack is requested.
+     * This data structure contains, for each originator block ack agreement (recipient, TID),
+     * a set of packets for which an ack by block ack is requested.
      * Every packet or fragment indicated as correctly received in BlockAck frame is
      * erased from this data structure. Pushed back in retransmission queue otherwise.
      */
-    Agreements m_agreements;
+    OriginatorAgreements m_originatorAgreements;
+    RecipientAgreements m_recipientAgreements; //!< Recipient Block Ack agreements
 
-    std::list<Bar> m_bars; ///< list of BARs
+    std::list<AgreementKey> m_sendBarIfDataQueued; ///< list of BA agreements for which a BAR shall
+                                                   ///< only be sent if data is queued
 
-    std::map<AcIndex, Ptr<BlockAckManager>> m_bamMap; ///< AC-indexed map of all Block Ack Managers
-    uint8_t m_blockAckThreshold;                      ///< block ack threshold
-    Ptr<WifiMacQueue> m_queue;                        ///< queue
+    uint8_t m_blockAckThreshold; ///< block ack threshold
+    Ptr<WifiMacQueue> m_queue;   ///< queue
     Callback<void, Mac48Address, uint8_t, bool>
         m_blockAckInactivityTimeout;                      ///< BlockAck inactivity timeout callback
     Callback<void, Mac48Address, uint8_t> m_blockPackets; ///< block packets callback
@@ -518,7 +526,7 @@
      * The trace source fired when a state transition occurred.
      */
     TracedCallback<Time, Mac48Address, uint8_t, OriginatorBlockAckAgreement::State>
-        m_agreementState;
+        m_originatorAgreementState;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/block-ack-type.h ns-3.38/src/wifi/model/block-ack-type.h
--- ns-3.37/src/wifi/model/block-ack-type.h	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/block-ack-type.h	2023-03-16 20:54:23.852869300 -0700
@@ -44,7 +44,8 @@
         MULTI_TID,
         MULTI_STA
     };
-    enum Variant m_variant;           //!< Block Ack variant
+
+    Variant m_variant;                //!< Block Ack variant
     std::vector<uint8_t> m_bitmapLen; //!< Length (bytes) of included bitmaps
 
     /**
@@ -84,7 +85,8 @@
         EXTENDED_COMPRESSED,
         MULTI_TID
     };
-    enum Variant m_variant; //!< Block Ack Request variant
+
+    Variant m_variant;      //!< Block Ack Request variant
     uint8_t m_nSeqControls; //!< Number of included Starting Sequence Control fields.
                             //!< This member is added for future support of Multi-TID BARs
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/channel-access-manager.cc ns-3.38/src/wifi/model/channel-access-manager.cc
--- ns-3.37/src/wifi/model/channel-access-manager.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/channel-access-manager.cc	2023-03-16 20:54:23.852869300 -0700
@@ -627,6 +627,30 @@
     return width;
 }
 
+bool
+ChannelAccessManager::GetPer20MHzBusy(const std::set<uint8_t>& indices) const
+{
+    const auto now = Simulator::Now();
+
+    if (m_phy->GetChannelWidth() < 40)
+    {
+        NS_ASSERT_MSG(indices.size() == 1 && *indices.cbegin() == 0,
+                      "Index 0 only can be specified if the channel width is less than 40 MHz");
+        return m_lastBusyEnd.at(WIFI_CHANLIST_PRIMARY) > now;
+    }
+
+    for (const auto index : indices)
+    {
+        NS_ASSERT(index < m_lastPer20MHzBusyEnd.size());
+        if (m_lastPer20MHzBusyEnd.at(index) > now)
+        {
+            NS_LOG_DEBUG("20 MHz channel with index " << +index << " is busy");
+            return true;
+        }
+    }
+    return false;
+}
+
 void
 ChannelAccessManager::DisableEdcaFor(Ptr<Txop> qosTxop, Time duration)
 {
@@ -706,8 +730,10 @@
     NS_ASSERT(lastBusyEndIt != m_lastBusyEnd.end());
     Time now = Simulator::Now();
     lastBusyEndIt->second = now + duration;
-    // TODO uncomment assert below when PHY passes correct parameters
-    // NS_ASSERT (per20MhzDurations.size () == m_lastPer20MHzBusyEnd.size ());
+    NS_ASSERT_MSG(per20MhzDurations.size() == m_lastPer20MHzBusyEnd.size(),
+                  "Size of received vector (" << per20MhzDurations.size()
+                                              << ") differs from the expected size ("
+                                              << m_lastPer20MHzBusyEnd.size() << ")");
     for (std::size_t chIdx = 0; chIdx < per20MhzDurations.size(); ++chIdx)
     {
         if (per20MhzDurations[chIdx].IsStrictlyPositive())
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/channel-access-manager.h ns-3.38/src/wifi/model/channel-access-manager.h
--- ns-3.37/src/wifi/model/channel-access-manager.h	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/channel-access-manager.h	2023-03-16 20:54:23.852869300 -0700
@@ -150,6 +150,13 @@
     uint16_t GetLargestIdlePrimaryChannel(Time interval, Time end);
 
     /**
+     * \param indices a set of indices (starting at 0) specifying the 20 MHz channels to test
+     * \return true if per-20 MHz CCA indicates busy for at least one of the
+     *         specified 20 MHz channels, false otherwise
+     */
+    bool GetPer20MHzBusy(const std::set<uint8_t>& indices) const;
+
+    /**
      * \param duration expected duration of reception
      *
      * Notify the Txop that a packet reception started
@@ -163,7 +170,7 @@
     void NotifyRxEndOkNow();
     /**
      * Notify the Txop that a packet reception was just
-     * completed unsuccessfully.
+     * completed unsuccessfuly.
      */
     void NotifyRxEndErrorNow();
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ctrl-headers.cc ns-3.38/src/wifi/model/ctrl-headers.cc
--- ns-3.37/src/wifi/model/ctrl-headers.cc	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/ctrl-headers.cc	2023-03-16 20:54:23.852869300 -0700
@@ -366,7 +366,7 @@
             i.WriteHtolsbU16(m_baInfo[index].m_aidTidInfo);
             if (GetAid11(index) != 2045)
             {
-                if (m_baInfo[index].m_bitmap.size() > 0)
+                if (!m_baInfo[index].m_bitmap.empty())
                 {
                     i.WriteHtolsbU16(GetStartingSequenceControl(index));
                     i = SerializeBitmap(i, index);
@@ -730,7 +730,7 @@
     else if (m_baType.m_variant == BlockAckType::MULTI_STA)
     {
         NS_ASSERT(m_baInfo.size() == m_baType.m_bitmapLen.size());
-        NS_ASSERT_MSG(m_baInfo[index].m_bitmap.size() > 0,
+        NS_ASSERT_MSG(!m_baInfo[index].m_bitmap.empty(),
                       "This Per AID TID Info subfield has no Starting Sequence Control subfield");
 
         if (m_baType.m_bitmapLen[index] == 16)
@@ -1068,12 +1068,15 @@
  * Trigger frame - User Info field
  ***********************************/
 
-CtrlTriggerUserInfoField::CtrlTriggerUserInfoField(uint8_t triggerType)
-    : m_aid12(0),
+CtrlTriggerUserInfoField::CtrlTriggerUserInfoField(TriggerFrameType triggerType,
+                                                   TriggerFrameVariant variant)
+    : m_variant(variant),
+      m_aid12(0),
       m_ruAllocation(0),
       m_ulFecCodingType(false),
       m_ulMcs(0),
       m_ulDcm(false),
+      m_ps160(true),
       m_ulTargetRssi(0),
       m_triggerType(triggerType),
       m_basicTriggerDependentUserInfo(0)
@@ -1096,11 +1099,13 @@
         return *this;
     }
 
+    m_variant = userInfo.m_variant;
     m_aid12 = userInfo.m_aid12;
     m_ruAllocation = userInfo.m_ruAllocation;
     m_ulFecCodingType = userInfo.m_ulFecCodingType;
     m_ulMcs = userInfo.m_ulMcs;
     m_ulDcm = userInfo.m_ulDcm;
+    m_ps160 = userInfo.m_ps160;
     m_bits26To31 = userInfo.m_bits26To31;
     m_ulTargetRssi = userInfo.m_ulTargetRssi;
     m_basicTriggerDependentUserInfo = userInfo.m_basicTriggerDependentUserInfo;
@@ -1111,7 +1116,8 @@
 void
 CtrlTriggerUserInfoField::Print(std::ostream& os) const
 {
-    os << ", USER_INFO AID=" << m_aid12 << ", RU_Allocation=" << +m_ruAllocation
+    os << ", USER_INFO " << (m_variant == TriggerFrameVariant::HE ? "HE" : "EHT")
+       << " variant AID=" << m_aid12 << ", RU_Allocation=" << +m_ruAllocation
        << ", MCS=" << +m_ulMcs;
 }
 
@@ -1123,14 +1129,15 @@
 
     switch (m_triggerType)
     {
-    case BASIC_TRIGGER:
-    case BFRP_TRIGGER:
+    case TriggerFrameType::BASIC_TRIGGER:
+    case TriggerFrameType::BFRP_TRIGGER:
         size += 1;
         break;
-    case MU_BAR_TRIGGER:
+    case TriggerFrameType::MU_BAR_TRIGGER:
         size +=
             m_muBarTriggerDependentUserInfo.GetSerializedSize(); // BAR Control and BAR Information
         break;
+    default:;
         // The Trigger Dependent User Info subfield is not present in the other variants
     }
 
@@ -1140,10 +1147,12 @@
 Buffer::Iterator
 CtrlTriggerUserInfoField::Serialize(Buffer::Iterator start) const
 {
-    NS_ABORT_MSG_IF(m_triggerType == BFRP_TRIGGER, "BFRP Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == GCR_MU_BAR_TRIGGER,
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
+                    "BFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
                     "GCR-MU-BAR Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == NFRP_TRIGGER, "NFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
+                    "NFRP Trigger frame is not supported");
 
     Buffer::Iterator i = start;
 
@@ -1152,7 +1161,10 @@
     userInfo |= (m_ruAllocation << 12);
     userInfo |= (m_ulFecCodingType ? 1 << 20 : 0);
     userInfo |= (m_ulMcs & 0x0f) << 21;
-    userInfo |= (m_ulDcm ? 1 << 25 : 0);
+    if (m_variant == TriggerFrameVariant::HE)
+    {
+        userInfo |= (m_ulDcm ? 1 << 25 : 0);
+    }
 
     if (m_aid12 != 0 && m_aid12 != 2045)
     {
@@ -1166,16 +1178,21 @@
     }
 
     i.WriteHtolsbU32(userInfo);
-    // Here we need to write 8 bits covering the UL Target RSSI (7 bits) and the
-    // Reserved bit. Given how m_ulTargetRssi is set, writing m_ulTargetRssi
-    // leads to setting the Reserved bit to zero
-    i.WriteU8(m_ulTargetRssi);
+    // Here we need to write 8 bits covering the UL Target RSSI (7 bits) and B39, which is
+    // reserved in the HE variant and the PS160 subfield in the EHT variant.
+    uint8_t bit32To39 = m_ulTargetRssi;
+    if (m_variant == TriggerFrameVariant::EHT)
+    {
+        bit32To39 |= (m_ps160 ? 1 << 7 : 0);
+    }
 
-    if (m_triggerType == BASIC_TRIGGER)
+    i.WriteU8(bit32To39);
+
+    if (m_triggerType == TriggerFrameType::BASIC_TRIGGER)
     {
         i.WriteU8(m_basicTriggerDependentUserInfo);
     }
-    else if (m_triggerType == MU_BAR_TRIGGER)
+    else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER)
     {
         m_muBarTriggerDependentUserInfo.Serialize(i);
         i.Next(m_muBarTriggerDependentUserInfo.GetSerializedSize());
@@ -1187,10 +1204,12 @@
 Buffer::Iterator
 CtrlTriggerUserInfoField::Deserialize(Buffer::Iterator start)
 {
-    NS_ABORT_MSG_IF(m_triggerType == BFRP_TRIGGER, "BFRP Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == GCR_MU_BAR_TRIGGER,
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
+                    "BFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
                     "GCR-MU-BAR Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == NFRP_TRIGGER, "NFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
+                    "NFRP Trigger frame is not supported");
 
     Buffer::Iterator i = start;
 
@@ -1201,7 +1220,10 @@
     m_ruAllocation = (userInfo >> 12) & 0xff;
     m_ulFecCodingType = (userInfo >> 20) & 0x01;
     m_ulMcs = (userInfo >> 21) & 0x0f;
-    m_ulDcm = (userInfo >> 25) & 0x01;
+    if (m_variant == TriggerFrameVariant::HE)
+    {
+        m_ulDcm = (userInfo >> 25) & 0x01;
+    }
 
     if (m_aid12 != 0 && m_aid12 != 2045)
     {
@@ -1214,13 +1236,18 @@
         m_bits26To31.raRuInformation.moreRaRu = (userInfo >> 31) & 0x01;
     }
 
-    m_ulTargetRssi = i.ReadU8() & 0x7f; // B39 is reserved
+    uint8_t bit32To39 = i.ReadU8();
+    m_ulTargetRssi = bit32To39 & 0x7f; // B39 is reserved in HE variant
+    if (m_variant == TriggerFrameVariant::EHT)
+    {
+        m_ps160 = (bit32To39 >> 7) == 1;
+    }
 
-    if (m_triggerType == BASIC_TRIGGER)
+    if (m_triggerType == TriggerFrameType::BASIC_TRIGGER)
     {
         m_basicTriggerDependentUserInfo = i.ReadU8();
     }
-    else if (m_triggerType == MU_BAR_TRIGGER)
+    else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER)
     {
         uint32_t len = m_muBarTriggerDependentUserInfo.Deserialize(i);
         i.Next(len);
@@ -1232,7 +1259,22 @@
 TriggerFrameType
 CtrlTriggerUserInfoField::GetType() const
 {
-    return static_cast<TriggerFrameType>(m_triggerType);
+    return m_triggerType;
+}
+
+WifiPreamble
+CtrlTriggerUserInfoField::GetPreambleType() const
+{
+    switch (m_variant)
+    {
+    case TriggerFrameVariant::HE:
+        return WIFI_PREAMBLE_HE_TB;
+    case TriggerFrameVariant::EHT:
+        return WIFI_PREAMBLE_EHT_TB;
+    default:
+        NS_ABORT_MSG("Unexpected variant: " << +static_cast<uint8_t>(m_variant));
+    }
+    return WIFI_PREAMBLE_LONG; // to silence warning
 }
 
 void
@@ -1263,6 +1305,8 @@
 CtrlTriggerUserInfoField::SetRuAllocation(HeRu::RuSpec ru)
 {
     NS_ABORT_MSG_IF(ru.GetIndex() == 0, "Valid indices start at 1");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
+                    "SetMuRtsRuAllocation() must be used for MU-RTS");
 
     switch (ru.GetRuType())
     {
@@ -1304,6 +1348,9 @@
 HeRu::RuSpec
 CtrlTriggerUserInfoField::GetRuAllocation() const
 {
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
+                    "GetMuRtsRuAllocation() must be used for MU-RTS");
+
     HeRu::RuType ruType;
     std::size_t index;
 
@@ -1355,6 +1402,39 @@
 }
 
 void
+CtrlTriggerUserInfoField::SetMuRtsRuAllocation(uint8_t value)
+{
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_RTS_TRIGGER,
+                    "SetMuRtsRuAllocation() can only be used for MU-RTS");
+    NS_ABORT_MSG_IF(
+        value < 61 || value > 68,
+        "Value "
+            << +value
+            << " is not admitted for B7-B1 of the RU Allocation subfield of MU-RTS Trigger Frames");
+
+    m_ruAllocation = (value << 1);
+    if (value == 68)
+    {
+        // set B0 for 160 MHz and 80+80 MHz indication
+        m_ruAllocation++;
+    }
+}
+
+uint8_t
+CtrlTriggerUserInfoField::GetMuRtsRuAllocation() const
+{
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_RTS_TRIGGER,
+                    "GetMuRtsRuAllocation() can only be used for MU-RTS");
+    uint8_t value = (m_ruAllocation >> 1);
+    NS_ABORT_MSG_IF(
+        value < 61 || value > 68,
+        "Value "
+            << +value
+            << " is not admitted for B7-B1 of the RU Allocation subfield of MU-RTS Trigger Frames");
+    return value;
+}
+
+void
 CtrlTriggerUserInfoField::SetUlFecCodingType(bool ldpc)
 {
     m_ulFecCodingType = ldpc;
@@ -1382,12 +1462,14 @@
 void
 CtrlTriggerUserInfoField::SetUlDcm(bool dcm)
 {
+    NS_ASSERT_MSG(m_variant == TriggerFrameVariant::HE, "UL DCM flag only present in HE variant");
     m_ulDcm = dcm;
 }
 
 bool
 CtrlTriggerUserInfoField::GetUlDcm() const
 {
+    NS_ASSERT_MSG(m_variant == TriggerFrameVariant::HE, "UL DCM flag only present in HE variant");
     return m_ulDcm;
 }
 
@@ -1481,7 +1563,7 @@
                                                      uint8_t tidLimit,
                                                      AcIndex prefAc)
 {
-    NS_ABORT_MSG_IF(m_triggerType != BASIC_TRIGGER, "Not a Basic Trigger Frame");
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
 
     m_basicTriggerDependentUserInfo = (spacingFactor & 0x03) |
                                       (tidLimit & 0x07) << 2
@@ -1492,7 +1574,7 @@
 uint8_t
 CtrlTriggerUserInfoField::GetMpduMuSpacingFactor() const
 {
-    NS_ABORT_MSG_IF(m_triggerType != BASIC_TRIGGER, "Not a Basic Trigger Frame");
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
 
     return m_basicTriggerDependentUserInfo & 0x03;
 }
@@ -1500,7 +1582,7 @@
 uint8_t
 CtrlTriggerUserInfoField::GetTidAggregationLimit() const
 {
-    NS_ABORT_MSG_IF(m_triggerType != BASIC_TRIGGER, "Not a Basic Trigger Frame");
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
 
     return (m_basicTriggerDependentUserInfo & 0x1c) >> 2;
 }
@@ -1508,7 +1590,7 @@
 AcIndex
 CtrlTriggerUserInfoField::GetPreferredAc() const
 {
-    NS_ABORT_MSG_IF(m_triggerType != BASIC_TRIGGER, "Not a Basic Trigger Frame");
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::BASIC_TRIGGER, "Not a Basic Trigger Frame");
 
     return AcIndex((m_basicTriggerDependentUserInfo & 0xc0) >> 6);
 }
@@ -1516,7 +1598,8 @@
 void
 CtrlTriggerUserInfoField::SetMuBarTriggerDepUserInfo(const CtrlBAckRequestHeader& bar)
 {
-    NS_ABORT_MSG_IF(m_triggerType != MU_BAR_TRIGGER, "Not a MU-BAR Trigger frame");
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER,
+                    "Not a MU-BAR Trigger frame");
     NS_ABORT_MSG_IF(bar.GetType().m_variant != BlockAckReqType::COMPRESSED &&
                         bar.GetType().m_variant != BlockAckReqType::MULTI_TID,
                     "BAR Control indicates it is neither the Compressed nor the Multi-TID variant");
@@ -1526,7 +1609,8 @@
 const CtrlBAckRequestHeader&
 CtrlTriggerUserInfoField::GetMuBarTriggerDepUserInfo() const
 {
-    NS_ABORT_MSG_IF(m_triggerType != MU_BAR_TRIGGER, "Not a MU-BAR Trigger frame");
+    NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER,
+                    "Not a MU-BAR Trigger frame");
 
     return m_muBarTriggerDependentUserInfo;
 }
@@ -1538,7 +1622,8 @@
 NS_OBJECT_ENSURE_REGISTERED(CtrlTriggerHeader);
 
 CtrlTriggerHeader::CtrlTriggerHeader()
-    : m_triggerType(0),
+    : m_variant(TriggerFrameVariant::HE),
+      m_triggerType(TriggerFrameType::BASIC_TRIGGER),
       m_ulLength(0),
       m_moreTF(false),
       m_csRequired(false),
@@ -1552,6 +1637,22 @@
 CtrlTriggerHeader::CtrlTriggerHeader(TriggerFrameType type, const WifiTxVector& txVector)
     : CtrlTriggerHeader()
 {
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
+                    "This constructor cannot be used for MU-RTS");
+
+    switch (txVector.GetPreambleType())
+    {
+    case WIFI_PREAMBLE_HE_TB:
+        m_variant = TriggerFrameVariant::HE;
+        break;
+    case WIFI_PREAMBLE_EHT_TB:
+        m_variant = TriggerFrameVariant::EHT;
+        break;
+    default:
+        NS_ABORT_MSG("Cannot create a TF out of a TXVECTOR with preamble type: "
+                     << txVector.GetPreambleType());
+    }
+
     m_triggerType = type;
     SetUlBandwidth(txVector.GetChannelWidth());
     SetUlLength(txVector.GetLength());
@@ -1569,7 +1670,7 @@
         CtrlTriggerUserInfoField& ui = AddUserInfoField();
         ui.SetAid12(userInfo.first);
         ui.SetRuAllocation(userInfo.second.ru);
-        ui.SetUlMcs(userInfo.second.mcs.GetMcsValue());
+        ui.SetUlMcs(userInfo.second.mcs);
         ui.SetSsAllocation(1, userInfo.second.nss); // MU-MIMO is not supported
     }
 }
@@ -1587,6 +1688,7 @@
         return *this;
     }
 
+    m_variant = trigger.m_variant;
     m_triggerType = trigger.m_triggerType;
     m_ulLength = trigger.m_ulLength;
     m_moreTF = trigger.m_moreTF;
@@ -1628,6 +1730,20 @@
     }
 }
 
+void
+CtrlTriggerHeader::SetVariant(TriggerFrameVariant variant)
+{
+    NS_ABORT_MSG_IF(!m_userInfoFields.empty(),
+                    "Cannot change Common Info field variant if User Info fields are present");
+    m_variant = variant;
+}
+
+TriggerFrameVariant
+CtrlTriggerHeader::GetVariant() const
+{
+    return m_variant;
+}
+
 uint32_t
 CtrlTriggerHeader::GetSerializedSize() const
 {
@@ -1635,7 +1751,7 @@
     size += 8; // Common Info (excluding Trigger Dependent Common Info)
 
     // Add the size of the Trigger Dependent Common Info subfield
-    if (m_triggerType == GCR_MU_BAR_TRIGGER)
+    if (m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER)
     {
         size += 4;
     }
@@ -1653,15 +1769,17 @@
 void
 CtrlTriggerHeader::Serialize(Buffer::Iterator start) const
 {
-    NS_ABORT_MSG_IF(m_triggerType == BFRP_TRIGGER, "BFRP Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == GCR_MU_BAR_TRIGGER,
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
+                    "BFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
                     "GCR-MU-BAR Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == NFRP_TRIGGER, "NFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
+                    "NFRP Trigger frame is not supported");
 
     Buffer::Iterator i = start;
 
     uint64_t commonInfo = 0;
-    commonInfo |= (m_triggerType & 0x0f);
+    commonInfo |= (static_cast<uint8_t>(m_triggerType) & 0x0f);
     commonInfo |= (m_ulLength & 0x0fff) << 4;
     commonInfo |= (m_moreTF ? 1 << 16 : 0);
     commonInfo |= (m_csRequired ? 1 << 17 : 0);
@@ -1669,6 +1787,11 @@
     commonInfo |= (m_giAndLtfType & 0x03) << 20;
     commonInfo |= static_cast<uint64_t>(m_apTxPower & 0x3f) << 28;
     commonInfo |= static_cast<uint64_t>(m_ulSpatialReuse) << 37;
+    if (m_variant == TriggerFrameVariant::HE)
+    {
+        uint64_t ulHeSigA2 = 0x01ff; // nine bits equal to 1
+        commonInfo |= ulHeSigA2 << 54;
+    }
 
     i.WriteHtolsbU64(commonInfo);
 
@@ -1687,7 +1810,7 @@
 
     uint64_t commonInfo = i.ReadLsbtohU64();
 
-    m_triggerType = (commonInfo & 0x0f);
+    m_triggerType = static_cast<TriggerFrameType>(commonInfo & 0x0f);
     m_ulLength = (commonInfo >> 4) & 0x0fff;
     m_moreTF = (commonInfo >> 16) & 0x01;
     m_csRequired = (commonInfo >> 17) & 0x01;
@@ -1695,12 +1818,16 @@
     m_giAndLtfType = (commonInfo >> 20) & 0x03;
     m_apTxPower = (commonInfo >> 28) & 0x3f;
     m_ulSpatialReuse = (commonInfo >> 37) & 0xffff;
+    uint8_t bit54and55 = (commonInfo >> 54) & 0x03;
+    m_variant = bit54and55 == 3 ? TriggerFrameVariant::HE : TriggerFrameVariant::EHT;
     m_userInfoFields.clear();
 
-    NS_ABORT_MSG_IF(m_triggerType == BFRP_TRIGGER, "BFRP Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == GCR_MU_BAR_TRIGGER,
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
+                    "BFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER,
                     "GCR-MU-BAR Trigger frame is not supported");
-    NS_ABORT_MSG_IF(m_triggerType == NFRP_TRIGGER, "NFRP Trigger frame is not supported");
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
+                    "NFRP Trigger frame is not supported");
 
     bool isPadding = false;
 
@@ -1733,7 +1860,7 @@
 TriggerFrameType
 CtrlTriggerHeader::GetType() const
 {
-    return static_cast<TriggerFrameType>(m_triggerType);
+    return m_triggerType;
 }
 
 const char*
@@ -1746,7 +1873,7 @@
 CtrlTriggerHeader::GetTypeString(TriggerFrameType type)
 {
 #define FOO(x)                                                                                     \
-    case x:                                                                                        \
+    case TriggerFrameType::x:                                                                      \
         return #x;                                                                                 \
         break;
 
@@ -1769,49 +1896,49 @@
 bool
 CtrlTriggerHeader::IsBasic() const
 {
-    return (m_triggerType == BASIC_TRIGGER);
+    return (m_triggerType == TriggerFrameType::BASIC_TRIGGER);
 }
 
 bool
 CtrlTriggerHeader::IsBfrp() const
 {
-    return (m_triggerType == BFRP_TRIGGER);
+    return (m_triggerType == TriggerFrameType::BFRP_TRIGGER);
 }
 
 bool
 CtrlTriggerHeader::IsMuBar() const
 {
-    return (m_triggerType == MU_BAR_TRIGGER);
+    return (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER);
 }
 
 bool
 CtrlTriggerHeader::IsMuRts() const
 {
-    return (m_triggerType == MU_RTS_TRIGGER);
+    return (m_triggerType == TriggerFrameType::MU_RTS_TRIGGER);
 }
 
 bool
 CtrlTriggerHeader::IsBsrp() const
 {
-    return (m_triggerType == BSRP_TRIGGER);
+    return (m_triggerType == TriggerFrameType::BSRP_TRIGGER);
 }
 
 bool
 CtrlTriggerHeader::IsGcrMuBar() const
 {
-    return (m_triggerType == GCR_MU_BAR_TRIGGER);
+    return (m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER);
 }
 
 bool
 CtrlTriggerHeader::IsBqrp() const
 {
-    return (m_triggerType == BQRP_TRIGGER);
+    return (m_triggerType == TriggerFrameType::BQRP_TRIGGER);
 }
 
 bool
 CtrlTriggerHeader::IsNfrp() const
 {
-    return (m_triggerType == NFRP_TRIGGER);
+    return (m_triggerType == TriggerFrameType::NFRP_TRIGGER);
 }
 
 void
@@ -1829,18 +1956,19 @@
 WifiTxVector
 CtrlTriggerHeader::GetHeTbTxVector(uint16_t staId) const
 {
+    NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER,
+                    "GetHeTbTxVector() cannot be used for MU-RTS");
     auto userInfoIt = FindUserInfoWithAid(staId);
     NS_ASSERT(userInfoIt != end());
 
     WifiTxVector v;
-    v.SetPreambleType(WifiPreamble::WIFI_PREAMBLE_HE_TB);
+    v.SetPreambleType(userInfoIt->GetPreambleType());
     v.SetChannelWidth(GetUlBandwidth());
     v.SetGuardInterval(GetGuardInterval());
     v.SetLength(GetUlLength());
-    v.SetHeMuUserInfo(staId,
-                      {userInfoIt->GetRuAllocation(),
-                       HePhy::GetHeMcs(userInfoIt->GetUlMcs()),
-                       userInfoIt->GetNss()});
+    v.SetHeMuUserInfo(
+        staId,
+        {userInfoIt->GetRuAllocation(), userInfoIt->GetUlMcs(), userInfoIt->GetNss()});
     return v;
 }
 
@@ -1996,7 +2124,7 @@
 CtrlTriggerUserInfoField&
 CtrlTriggerHeader::AddUserInfoField()
 {
-    m_userInfoFields.emplace_back(m_triggerType);
+    m_userInfoFields.emplace_back(m_triggerType, m_variant);
     return m_userInfoFields.back();
 }
 
@@ -2083,6 +2211,11 @@
 bool
 CtrlTriggerHeader::IsValid() const
 {
+    if (m_triggerType == TriggerFrameType::MU_RTS_TRIGGER)
+    {
+        return true;
+    }
+
     // check that allocated RUs do not overlap
     // TODO This is not a problem in case of UL MU-MIMO
     std::vector<HeRu::RuSpec> prevRus;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ctrl-headers.h ns-3.38/src/wifi/model/ctrl-headers.h
--- ns-3.37/src/wifi/model/ctrl-headers.h	2022-11-01 17:37:24.739395900 -0700
+++ ns-3.38/src/wifi/model/ctrl-headers.h	2023-03-16 20:54:23.852869300 -0700
@@ -21,6 +21,7 @@
 #define CTRL_HEADERS_H
 
 #include "block-ack-type.h"
+#include "wifi-phy-common.h"
 
 #include "ns3/he-ru.h"
 #include "ns3/header.h"
@@ -555,7 +556,7 @@
  * \ingroup wifi
  * The different Trigger frame types.
  */
-enum TriggerFrameType : uint8_t
+enum class TriggerFrameType : uint8_t
 {
     BASIC_TRIGGER = 0,      // Basic
     BFRP_TRIGGER = 1,       // Beamforming Report Poll
@@ -569,6 +570,16 @@
 
 /**
  * \ingroup wifi
+ * The different variants for Common Info field and User Info field of Trigger Frames.
+ */
+enum class TriggerFrameVariant : uint8_t
+{
+    HE = 0,
+    EHT
+};
+
+/**
+ * \ingroup wifi
  * \brief User Info field of Trigger frames.
  *
  * Trigger frames, introduced by 802.11ax amendment (see Section 9.3.1.23 of D3.0),
@@ -582,8 +593,9 @@
      * Constructor
      *
      * \param triggerType the Trigger frame type
+     * \param variant the Trigger Frame variant
      */
-    CtrlTriggerUserInfoField(uint8_t triggerType);
+    CtrlTriggerUserInfoField(TriggerFrameType triggerType, TriggerFrameVariant variant);
     /**
      * Copy assignment operator.
      *
@@ -633,6 +645,10 @@
      */
     TriggerFrameType GetType() const;
     /**
+     * \return the preamble type of the TB PPDU solicited by this User Info field.
+     */
+    WifiPreamble GetPreambleType() const;
+    /**
      * Set the AID12 subfield, which carries the 12 LSBs of the AID of the
      * station for which this User Info field is intended. The whole AID can
      * be passed, since the passed value is properly masked.
@@ -662,17 +678,55 @@
     bool HasRaRuForUnassociatedSta() const;
     /**
      * Set the RU Allocation subfield according to the specified RU.
+     * This method cannot be called on MU-RTS Trigger Frames (call SetMuRtsRuAllocation instead).
      *
      * \param ru the RU this User Info field is allocating
      */
     void SetRuAllocation(HeRu::RuSpec ru);
     /**
      * Get the RU specified by the RU Allocation subfield.
+     * This method cannot be called on MU-RTS Trigger Frames (call GetMuRtsRuAllocation instead).
      *
      * \return the RU this User Info field is allocating
      */
     HeRu::RuSpec GetRuAllocation() const;
     /**
+     * Set the RU Allocation subfield based on the given value for the B7-B1 bits.
+     * This method can only be called on MU-RTS Trigger Frames.
+     *
+     * B7–B1 of the RU Allocation subfield is set to indicate the primary 20 MHz channel
+     * as follows:
+     * - 61 if the primary 20 MHz channel is the only 20 MHz channel or the lowest frequency
+     *   20 MHz channel in the primary 40 MHz channel or primary 80 MHz channel
+     * - 62 if the primary 20 MHz channel is the second lowest frequency 20 MHz channel in the
+     *   primary 40 MHz channel or primary 80 MHz channel
+     * - 63 if the primary 20 MHz channel is the third lowest frequency 20 MHz channel in the
+     *   primary 80 MHz channel
+     * - 64 if the primary 20 MHz channel is the fourth lowest frequency 20 MHz channel in the
+     *   primary 80 MHz channel
+     *
+     * B7–B1 of the RU Allocation subfield is set to indicate the primary 40 MHz channel
+     * as follows:
+     * - 65 if the primary 40 MHz channel is the only 40 MHz channel or the lowest frequency
+     *   40 MHz channel in the primary 80 MHz channel
+     * - 66 if the primary 40 MHz channel is the second lowest frequency 40 MHz channel in the
+     *   primary 80 MHz channel
+     *
+     * B7–B1 of the RU Allocation subfield is set to 67 to indicate the primary 80 MHz channel.
+     *
+     * B7–B1 of the RU Allocation subfield is set to 68 to indicate the primary and secondary
+     * 80 MHz channel.
+     *
+     * \param value the value for B7–B1 of the RU Allocation subfield
+     */
+    void SetMuRtsRuAllocation(uint8_t value);
+    /**
+     * This method can only be called on MU-RTS Trigger Frames.
+     *
+     * \return the value of B7–B1 of the RU Allocation subfield (\see SetMuRtsRuAllocation)
+     */
+    uint8_t GetMuRtsRuAllocation() const;
+    /**
      * Set the UL FEC Coding Type subfield, which indicates whether BCC or LDPC is used
      *
      * \param ldpc whether to use LDPC or not
@@ -697,13 +751,15 @@
      */
     uint8_t GetUlMcs() const;
     /**
-     * Set the UL DCM subfield, which indicates whether or not DCM is used
+     * Set the UL DCM subfield, which indicates whether or not DCM is used.
+     * This method can only be used with HE variant User Info field.
      *
      * \param dcm whether to use DCM or not
      */
     void SetUlDcm(bool dcm);
     /**
      * Get the UL DCM subfield, which indicates whether or not DCM is used
+     * This method can only be used with HE variant User Info field.
      *
      * \return true if DCM is used
      */
@@ -831,11 +887,14 @@
     const CtrlBAckRequestHeader& GetMuBarTriggerDepUserInfo() const;
 
   private:
+    TriggerFrameVariant m_variant; //!< User Info field variant
+
     uint16_t m_aid12;       //!< Association ID of the addressed station
     uint8_t m_ruAllocation; //!< RU Allocation
     bool m_ulFecCodingType; //!< UL FEC Coding Type
     uint8_t m_ulMcs;        //!< MCS to be used by the addressed station
-    bool m_ulDcm;           //!< whether or not to use Dual Carrier Modulation
+    bool m_ulDcm;           //!< whether or not to use Dual Carrier Modulation (HE variant only)
+    bool m_ps160;           //!< identifies the location of the RU (EHT variant only)
 
     union {
         struct
@@ -852,7 +911,7 @@
     } m_bits26To31;        //!< Fields occupying bits 26-31 in the User Info field
 
     uint8_t m_ulTargetRssi;                  //!< Expected receive signal power
-    uint8_t m_triggerType;                   //!< Trigger frame type
+    TriggerFrameType m_triggerType;          //!< Trigger frame type
     uint8_t m_basicTriggerDependentUserInfo; //!< Basic Trigger variant of Trigger Dependent User
                                              //!< Info subfield
     CtrlBAckRequestHeader
@@ -898,6 +957,8 @@
      *     UL MCS and SS Allocation subfields of each User Info field are set based
      *     on the values stored in the corresponding entry of the HeMuUserInfoMap.
      *
+     * This constructor cannot be used to construct MU-RTS Trigger Frames.
+     *
      * \param type the Trigger frame type
      * \param txVector the TX vector used to build this Trigger Frame
      */
@@ -925,6 +986,21 @@
     uint32_t Deserialize(Buffer::Iterator start) override;
 
     /**
+     * Set the Common Info field variant.
+     *
+     * For the moment, all User Info fields are of the same variant type, hence we
+     * forbid changing the Common Info field variant type after adding User Info fields.
+     *
+     * \param variant the Common Info field variant
+     */
+    void SetVariant(TriggerFrameVariant variant);
+    /**
+     * Get the Common Info field variant.
+     *
+     * \return the Common Info field variant
+     */
+    TriggerFrameVariant GetVariant() const;
+    /**
      * Set the Trigger frame type.
      *
      * \param type the Trigger frame type
@@ -1244,14 +1320,16 @@
     /**
      * Common Info field
      */
-    uint8_t m_triggerType;     //!< Trigger type
-    uint16_t m_ulLength;       //!< Value for the L-SIG Length field
-    bool m_moreTF;             //!< True if a subsequent Trigger frame follows
-    bool m_csRequired;         //!< Carrier Sense required
-    uint8_t m_ulBandwidth;     //!< UL BW subfield
-    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
+    TriggerFrameVariant m_variant;  //!< Common Info field variant
+    TriggerFrameType m_triggerType; //!< Trigger type
+    uint16_t m_ulLength;            //!< Value for the L-SIG Length field
+    bool m_moreTF;                  //!< True if a subsequent Trigger frame follows
+    bool m_csRequired;              //!< Carrier Sense required
+    uint8_t m_ulBandwidth;          //!< UL BW subfield
+    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
+
     /**
      * List of User Info fields
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-capabilities.cc ns-3.38/src/wifi/model/eht/eht-capabilities.cc
--- ns-3.37/src/wifi/model/eht/eht-capabilities.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/eht/eht-capabilities.cc	2023-03-16 20:54:23.852869300 -0700
@@ -19,10 +19,395 @@
 
 #include "eht-capabilities.h"
 
+#include <bitset>
+#include <cmath>
+
 namespace ns3
 {
 
+uint16_t
+EhtMacCapabilities::GetSize() const
+{
+    return 2;
+}
+
+void
+EhtMacCapabilities::Serialize(Buffer::Iterator& start) const
+{
+    uint16_t val = epcsPriorityAccessSupported | (ehtOmControlSupport << 1) |
+                   (triggeredTxopSharingMode1Support << 2) |
+                   (triggeredTxopSharingMode2Support << 3) | (restrictedTwtSupport << 4) |
+                   (scsTrafficDescriptionSupport << 5) | (maxMpduLength << 6) |
+                   (maxAmpduLengthExponentExtension << 8);
+    start.WriteHtolsbU16(val);
+}
+
+uint16_t
+EhtMacCapabilities::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    uint16_t val = i.ReadLsbtohU16();
+    epcsPriorityAccessSupported = val & 0x0001;
+    ehtOmControlSupport = (val >> 1) & 0x0001;
+    triggeredTxopSharingMode1Support = (val >> 2) & 0x0001;
+    triggeredTxopSharingMode2Support = (val >> 3) & 0x0001;
+    restrictedTwtSupport = (val >> 4) & 0x0001;
+    scsTrafficDescriptionSupport = (val >> 5) & 0x0001;
+    maxMpduLength = (val >> 6) & 0x0003;
+    maxAmpduLengthExponentExtension = (val >> 8) & 0x0001;
+    return 2;
+}
+
+uint16_t
+EhtPhyCapabilities::GetSize() const
+{
+    return 9;
+}
+
+void
+EhtPhyCapabilities::Serialize(Buffer::Iterator& start) const
+{
+    uint64_t val1 =
+        (support320MhzIn6Ghz << 1) | (support242ToneRuInBwLargerThan20Mhz << 2) |
+        (ndpWith4TimesEhtLtfAnd32usGi << 3) | (partialBandwidthUlMuMimo << 4) |
+        (suBeamformer << 5) | (suBeamformee << 6) | (beamformeeSsBwNotLargerThan80Mhz << 7) |
+        (beamformeeSs160Mhz << 10) | (beamformeeSs320Mhz << 13) |
+        (nSoundingDimensionsBwNotLargerThan80Mhz << 16) | (nSoundingDimensions160Mhz << 19) |
+        (nSoundingDimensions320Mhz << 22) | (ng16SuFeedback << 25) | (ng16MuFeedback << 26) |
+        (codebooksizeSuFeedback << 27) | (codebooksizeMuFeedback << 28) |
+        (triggeredSuBeamformingFeedback << 29) | (triggeredMuBeamformingPartialBwFeedback << 30) |
+        (triggeredCqiFeedback << 31) | (static_cast<uint64_t>(partialBandwidthDlMuMimo) << 32) |
+        (static_cast<uint64_t>(psrBasedSpatialReuseSupport) << 33) |
+        (static_cast<uint64_t>(powerBoostFactorSupport) << 34) |
+        (static_cast<uint64_t>(muPpdu4xEhtLtfAnd800nsGi) << 35) |
+        (static_cast<uint64_t>(maxNc) << 36) |
+        (static_cast<uint64_t>(nonTriggeredCqiFeedback) << 40) |
+        (static_cast<uint64_t>(supportTx1024And4096QamForRuSmallerThan242Tones) << 41) |
+        (static_cast<uint64_t>(supportRx1024And4096QamForRuSmallerThan242Tones) << 42) |
+        (static_cast<uint64_t>(ppeThresholdsPresent) << 43) |
+        (static_cast<uint64_t>(commonNominalPacketPadding) << 44) |
+        (static_cast<uint64_t>(maxNumSupportedEhtLtfs) << 46) |
+        (static_cast<uint64_t>(supportMcs15) << 51) |
+        (static_cast<uint64_t>(supportEhtDupIn6GHz) << 55) |
+        (static_cast<uint64_t>(support20MhzOperatingStaReceivingNdpWithWiderBw) << 56) |
+        (static_cast<uint64_t>(nonOfdmaUlMuMimoBwNotLargerThan80Mhz) << 57) |
+        (static_cast<uint64_t>(nonOfdmaUlMuMimo160Mhz) << 58) |
+        (static_cast<uint64_t>(nonOfdmaUlMuMimo320Mhz) << 59) |
+        (static_cast<uint64_t>(muBeamformerBwNotLargerThan80Mhz) << 60) |
+        (static_cast<uint64_t>(muBeamformer160Mhz) << 61) |
+        (static_cast<uint64_t>(muBeamformer320Mhz) << 62) |
+        (static_cast<uint64_t>(tbSoundingFeedbackRateLimit) << 63);
+    uint8_t val2 = rx1024QamInWiderBwDlOfdmaSupport | (rx4096QamInWiderBwDlOfdmaSupport << 1);
+    start.WriteHtolsbU64(val1);
+    start.WriteU8(val2);
+}
+
+uint16_t
+EhtPhyCapabilities::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    uint64_t val1 = i.ReadLsbtohU64();
+    uint8_t val2 = i.ReadU8();
+
+    support320MhzIn6Ghz = (val1 >> 1) & 0x0001;
+    support242ToneRuInBwLargerThan20Mhz = (val1 >> 2) & 0x0001;
+    ndpWith4TimesEhtLtfAnd32usGi = (val1 >> 3) & 0x0001;
+    partialBandwidthUlMuMimo = (val1 >> 4) & 0x0001;
+    suBeamformer = (val1 >> 5) & 0x0001;
+    suBeamformee = (val1 >> 6) & 0x0001;
+    beamformeeSsBwNotLargerThan80Mhz = (val1 >> 7) & 0x0007;
+    beamformeeSs160Mhz = (val1 >> 10) & 0x0007;
+    beamformeeSs320Mhz = (val1 >> 13) & 0x0007;
+    nSoundingDimensionsBwNotLargerThan80Mhz = (val1 >> 16) & 0x0007;
+    nSoundingDimensions160Mhz = (val1 >> 19) & 0x0007;
+    nSoundingDimensions320Mhz = (val1 >> 22) & 0x0007;
+    ng16SuFeedback = (val1 >> 25) & 0x0001;
+    ng16MuFeedback = (val1 >> 26) & 0x0001;
+    codebooksizeSuFeedback = (val1 >> 27) & 0x0001;
+    codebooksizeMuFeedback = (val1 >> 28) & 0x0001;
+    triggeredSuBeamformingFeedback = (val1 >> 29) & 0x0001;
+    triggeredMuBeamformingPartialBwFeedback = (val1 >> 30) & 0x0001;
+    triggeredCqiFeedback = (val1 >> 31) & 0x0001;
+    partialBandwidthDlMuMimo = (val1 >> 32) & 0x0001;
+    psrBasedSpatialReuseSupport = (val1 >> 33) & 0x0001;
+    powerBoostFactorSupport = (val1 >> 34) & 0x0001;
+    muPpdu4xEhtLtfAnd800nsGi = (val1 >> 35) & 0x0001;
+    maxNc = (val1 >> 36) & 0x000f;
+    nonTriggeredCqiFeedback = (val1 >> 40) & 0x0001;
+    supportTx1024And4096QamForRuSmallerThan242Tones = (val1 >> 41) & 0x0001;
+    supportRx1024And4096QamForRuSmallerThan242Tones = (val1 >> 42) & 0x0001;
+    ppeThresholdsPresent = (val1 >> 43) & 0x0001;
+    commonNominalPacketPadding = (val1 >> 44) & 0x0003;
+    maxNumSupportedEhtLtfs = (val1 >> 46) & 0x001f;
+    supportMcs15 = (val1 >> 51) & 0x000f;
+    supportEhtDupIn6GHz = (val1 >> 55) & 0x0001;
+    support20MhzOperatingStaReceivingNdpWithWiderBw = (val1 >> 56) & 0x0001;
+    nonOfdmaUlMuMimoBwNotLargerThan80Mhz = (val1 >> 57) & 0x0001;
+    nonOfdmaUlMuMimo160Mhz = (val1 >> 58) & 0x0001;
+    nonOfdmaUlMuMimo320Mhz = (val1 >> 59) & 0x0001;
+    muBeamformerBwNotLargerThan80Mhz = (val1 >> 60) & 0x0001;
+    muBeamformer160Mhz = (val1 >> 61) & 0x0001;
+    muBeamformer320Mhz = (val1 >> 62) & 0x0001;
+    tbSoundingFeedbackRateLimit = (val1 >> 63) & 0x0001;
+
+    rx1024QamInWiderBwDlOfdmaSupport = val2 & 0x01;
+    rx4096QamInWiderBwDlOfdmaSupport = (val2 >> 1) & 0x01;
+
+    return 9;
+}
+
+uint16_t
+EhtMcsAndNssSet::GetSize() const
+{
+    if (supportedEhtMcsAndNssSet.empty())
+    {
+        return 0;
+    }
+    uint16_t size = 0;
+    for (const auto& ehtMcsAndNssSet : supportedEhtMcsAndNssSet)
+    {
+        size += ehtMcsAndNssSet.second.size();
+    }
+    return size;
+}
+
+void
+EhtMcsAndNssSet::Serialize(Buffer::Iterator& start) const
+{
+    NS_ASSERT(!supportedEhtMcsAndNssSet.empty());
+    for (const auto& ehtMcsAndNssSet : supportedEhtMcsAndNssSet)
+    {
+        for (const auto& byte : ehtMcsAndNssSet.second)
+        {
+            start.WriteU8(byte);
+        }
+    }
+}
+
+uint16_t
+EhtMcsAndNssSet::Deserialize(Buffer::Iterator start,
+                             bool is2_4Ghz,
+                             uint8_t heSupportedChannelWidthSet,
+                             bool support320MhzIn6Ghz)
+{
+    Buffer::Iterator i = start;
+    uint16_t count = 0;
+    supportedEhtMcsAndNssSet.clear();
+    std::vector<uint8_t> bytes;
+    if (is2_4Ghz)
+    {
+        if ((heSupportedChannelWidthSet & 0x01) == 0)
+        {
+            bytes.clear();
+            for (std::size_t j = 0; j < 4; ++j)
+            {
+                uint8_t byte = i.ReadU8();
+                bytes.push_back(byte);
+                count++;
+            }
+            supportedEhtMcsAndNssSet[EHT_MCS_MAP_TYPE_20_MHZ_ONLY] = bytes;
+        }
+        else
+        {
+            bytes.clear();
+            for (std::size_t j = 0; j < 3; ++j)
+            {
+                uint8_t byte = i.ReadU8();
+                bytes.push_back(byte);
+                count++;
+            }
+            supportedEhtMcsAndNssSet[EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ] = bytes;
+        }
+    }
+    else
+    {
+        if ((heSupportedChannelWidthSet & 0x0e) == 0)
+        {
+            bytes.clear();
+            for (std::size_t j = 0; j < 4; ++j)
+            {
+                uint8_t byte = i.ReadU8();
+                bytes.push_back(byte);
+                count++;
+            }
+            supportedEhtMcsAndNssSet[EHT_MCS_MAP_TYPE_20_MHZ_ONLY] = bytes;
+        }
+        if ((heSupportedChannelWidthSet & 0x02) != 0)
+        {
+            bytes.clear();
+            for (std::size_t j = 0; j < 3; ++j)
+            {
+                uint8_t byte = i.ReadU8();
+                bytes.push_back(byte);
+                count++;
+            }
+            supportedEhtMcsAndNssSet[EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ] = bytes;
+        }
+        if ((heSupportedChannelWidthSet & 0x04) != 0)
+        {
+            bytes.clear();
+            for (std::size_t j = 0; j < 3; ++j)
+            {
+                uint8_t byte = i.ReadU8();
+                bytes.push_back(byte);
+                count++;
+            }
+            supportedEhtMcsAndNssSet[EHT_MCS_MAP_TYPE_160_MHZ] = bytes;
+        }
+        if (support320MhzIn6Ghz)
+        {
+            bytes.clear();
+            for (std::size_t j = 0; j < 3; ++j)
+            {
+                uint8_t byte = i.ReadU8();
+                bytes.push_back(byte);
+                count++;
+            }
+            supportedEhtMcsAndNssSet[EHT_MCS_MAP_TYPE_320_MHZ] = bytes;
+        }
+    }
+    return count;
+}
+
+uint16_t
+EhtPpeThresholds::GetSize() const
+{
+    const auto numBitsSet = std::bitset<5>(ruIndexBitmask).count();
+    const uint64_t nBitsNoPadding = 4 + 5 + (6 * numBitsSet * (nssPe + 1));
+    return std::ceil(static_cast<double>(nBitsNoPadding) / 8.0);
+}
+
+void
+EhtPpeThresholds::Serialize(Buffer::Iterator& start) const
+{
+    uint64_t nBitsNoPadding = 0;
+    uint8_t val = nssPe | ((ruIndexBitmask & 0x0f) << 4);
+    start.WriteU8(val);
+    nBitsNoPadding += 8;
+    val = (ruIndexBitmask & 0x10) >> 4;
+    nBitsNoPadding += 1;
+    uint8_t bitsPerPpet = 3;
+    for (const auto& info : ppeThresholdsInfo)
+    {
+        uint8_t offset = nBitsNoPadding % 8;
+        uint8_t bitsLeft = (8 - offset);
+        uint8_t bitMask = (0x01 << bitsLeft) - 0x01;
+        val |= (info.ppetMax & bitMask) << offset;
+        nBitsNoPadding += std::min(bitsLeft, bitsPerPpet);
+        if (nBitsNoPadding % 8 == 0)
+        {
+            start.WriteU8(val);
+            if (bitsLeft < 3)
+            {
+                const uint8_t remainingBits = (3 - bitsLeft);
+                bitMask = (0x01 << remainingBits) - 0x01;
+                val = (info.ppetMax >> bitsLeft) & bitMask;
+                nBitsNoPadding += remainingBits;
+            }
+            else
+            {
+                val = 0;
+            }
+        }
+        offset = nBitsNoPadding % 8;
+        bitsLeft = (8 - offset);
+        bitMask = (0x01 << bitsLeft) - 0x01;
+        val |= (info.ppet8 & bitMask) << offset;
+        nBitsNoPadding += std::min(bitsLeft, bitsPerPpet);
+        if (nBitsNoPadding % 8 == 0)
+        {
+            start.WriteU8(val);
+            if (bitsLeft < 3)
+            {
+                const uint8_t remainingBits = (3 - bitsLeft);
+                bitMask = (0x01 << remainingBits) - 0x01;
+                val = (info.ppet8 >> bitsLeft) & bitMask;
+                nBitsNoPadding += remainingBits;
+            }
+            else
+            {
+                val = 0;
+            }
+        }
+    }
+    if (nBitsNoPadding % 8 > 0)
+    {
+        start.WriteU8(val);
+    }
+}
+
+uint16_t
+EhtPpeThresholds::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    uint64_t nBitsNoPadding = 0;
+    uint8_t val = i.ReadU8();
+    nssPe = val & 0x0f;
+    ruIndexBitmask = ((val >> 4) & 0x0f);
+    nBitsNoPadding += 8;
+    val = i.ReadU8();
+    ruIndexBitmask |= ((val & 0x01) << 4);
+    nBitsNoPadding += 1;
+    const auto numBitsSet = std::bitset<5>(ruIndexBitmask).count();
+    const uint64_t bitsToDeserialize = (4 + 5 + (6 * numBitsSet * (nssPe + 1)));
+    uint8_t bitsPerPpet = 3;
+    while (nBitsNoPadding < bitsToDeserialize)
+    {
+        EhtPpeThresholdsInfo info;
+        uint8_t offset = nBitsNoPadding % 8;
+        uint8_t bitsLeft = (8 - offset);
+        uint8_t bitMask = (1 << bitsLeft) - 1;
+        info.ppetMax = ((val >> offset) & bitMask);
+        nBitsNoPadding += std::min(bitsLeft, bitsPerPpet);
+        if (nBitsNoPadding % 8 == 0)
+        {
+            val = i.ReadU8();
+            if (bitsLeft < 3)
+            {
+                const uint8_t remainingBits = (3 - bitsLeft);
+                bitMask = (1 << remainingBits) - 1;
+                info.ppetMax |= ((val & bitMask) << bitsLeft);
+                nBitsNoPadding += remainingBits;
+            }
+        }
+        offset = nBitsNoPadding % 8;
+        bitsLeft = (8 - offset);
+        bitMask = (1 << bitsLeft) - 1;
+        info.ppet8 = ((val >> offset) & bitMask);
+        nBitsNoPadding += std::min(bitsLeft, bitsPerPpet);
+        if (nBitsNoPadding % 8 == 0)
+        {
+            val = i.ReadU8();
+            if (bitsLeft < 3)
+            {
+                const uint8_t remainingBits = (3 - bitsLeft);
+                bitMask = (1 << remainingBits) - 1;
+                info.ppet8 |= ((val & bitMask) << bitsLeft);
+                nBitsNoPadding += remainingBits;
+            }
+        }
+        ppeThresholdsInfo.push_back(info);
+    }
+    return std::ceil(static_cast<double>(bitsToDeserialize) / 8.0);
+}
+
 EhtCapabilities::EhtCapabilities()
+    : m_macCapabilities{},
+      m_phyCapabilities{},
+      m_supportedEhtMcsAndNssSet{},
+      m_ppeThresholds{},
+      m_is2_4Ghz{false},
+      m_heCapabilities{std::nullopt}
+{
+}
+
+EhtCapabilities::EhtCapabilities(bool is2_4Ghz, const std::optional<HeCapabilities>& heCapabilities)
+    : m_macCapabilities{},
+      m_phyCapabilities{},
+      m_supportedEhtMcsAndNssSet{},
+      m_ppeThresholds{},
+      m_is2_4Ghz{is2_4Ghz},
+      m_heCapabilities{heCapabilities}
 {
 }
 
@@ -41,20 +426,304 @@
 uint16_t
 EhtCapabilities::GetInformationFieldSize() const
 {
-    return 1; // FIXME
+    uint16_t size = 1 + // ElementIdExt
+                    m_macCapabilities.GetSize() + m_phyCapabilities.GetSize() +
+                    m_supportedEhtMcsAndNssSet.GetSize();
+    if (m_phyCapabilities.ppeThresholdsPresent)
+    {
+        size += m_ppeThresholds.GetSize();
+    }
+    return size;
+}
+
+void
+EhtCapabilities::SetMaxMpduLength(uint16_t length)
+{
+    switch (length)
+    {
+    case 3895:
+        m_macCapabilities.maxMpduLength = 0;
+        break;
+    case 7991:
+        m_macCapabilities.maxMpduLength = 1;
+        break;
+    case 11454:
+        m_macCapabilities.maxMpduLength = 2;
+        break;
+    default:
+        NS_ABORT_MSG("Invalid MPDU Max Length value");
+    }
+}
+
+uint16_t
+EhtCapabilities::GetMaxMpduLength() const
+{
+    switch (m_macCapabilities.maxMpduLength)
+    {
+    case 0:
+        return 3895;
+    case 1:
+        return 7991;
+    case 2:
+        return 11454;
+    default:
+        NS_ABORT_MSG("The value 3 is reserved");
+    }
+    return 0;
+}
+
+void
+EhtCapabilities::SetMaxAmpduLength(uint32_t maxAmpduLength)
+{
+    for (uint8_t i = 0; i <= 1; i++)
+    {
+        if ((1UL << (23 + i)) - 1 == maxAmpduLength)
+        {
+            m_macCapabilities.maxAmpduLengthExponentExtension = i;
+            return;
+        }
+    }
+    NS_ABORT_MSG("Invalid A-MPDU Max Length value");
+}
+
+uint32_t
+EhtCapabilities::GetMaxAmpduLength() const
+{
+    return std::min<uint32_t>((1UL << (23 + m_macCapabilities.maxAmpduLengthExponentExtension)) - 1,
+                              15523200);
+}
+
+void
+EhtCapabilities::SetSupportedRxEhtMcsAndNss(EhtMcsAndNssSet::EhtMcsMapType mapType,
+                                            uint8_t upperMcs,
+                                            uint8_t maxNss)
+{
+    NS_ASSERT_MSG(maxNss <= 8, "Invalid maximum NSS " << maxNss);
+    std::size_t index = 0;
+    switch (upperMcs)
+    {
+    case 7:
+        NS_ASSERT(mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY);
+        index = 0;
+        break;
+    case 9:
+        index = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 1 : 0;
+        break;
+    case 11:
+        index = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 2 : 1;
+        break;
+    case 13:
+        index = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 3 : 2;
+        break;
+    default:
+        NS_ASSERT_MSG(false, "Invalid upper MCS " << +upperMcs);
+    }
+    uint8_t nBytes = 0;
+    switch (mapType)
+    {
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY:
+        nBytes = 4;
+        break;
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ:
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_160_MHZ:
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_320_MHZ:
+        nBytes = 3;
+        break;
+    default:
+        NS_ASSERT_MSG(false, "Invalid map type " << mapType);
+    }
+    auto it = m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.find(mapType);
+    if (it == m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.end())
+    {
+        m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet[mapType].resize(nBytes);
+        m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet[mapType][index] = (maxNss & 0x0f);
+    }
+    else
+    {
+        NS_ASSERT(it->second.size() == nBytes);
+        it->second[index] |= (maxNss & 0x0f);
+    }
+}
+
+void
+EhtCapabilities::SetSupportedTxEhtMcsAndNss(EhtMcsAndNssSet::EhtMcsMapType mapType,
+                                            uint8_t upperMcs,
+                                            uint8_t maxNss)
+{
+    NS_ASSERT_MSG(maxNss <= 8, "Invalid maximum NSS " << maxNss);
+    std::size_t index = 0;
+    switch (upperMcs)
+    {
+    case 7:
+        NS_ASSERT(mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY);
+        index = 0;
+        break;
+    case 9:
+        index = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 1 : 0;
+        break;
+    case 11:
+        index = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 2 : 1;
+        break;
+    case 13:
+        index = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 3 : 2;
+        break;
+    default:
+        NS_ASSERT_MSG(false, "Invalid upper MCS " << upperMcs);
+    }
+    uint8_t nBytes = 0;
+    switch (mapType)
+    {
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY:
+        nBytes = 4;
+        break;
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ:
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_160_MHZ:
+    case EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_320_MHZ:
+        nBytes = 3;
+        break;
+    default:
+        NS_ASSERT_MSG(false, "Invalid map type " << mapType);
+    }
+    auto it = m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.find(mapType);
+    if (it == m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.end())
+    {
+        m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet[mapType].resize(nBytes);
+        m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet[mapType][index] =
+            ((maxNss & 0x0f) << 4);
+    }
+    else
+    {
+        NS_ASSERT(it->second.size() == nBytes);
+        it->second[index] |= ((maxNss & 0x0f) << 4);
+    }
+}
+
+uint8_t
+EhtCapabilities::GetHighestSupportedRxMcs(EhtMcsAndNssSet::EhtMcsMapType mapType)
+{
+    const auto it = m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.find(mapType);
+    if (it == m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.cend())
+    {
+        return 0;
+    }
+    int8_t index = -1;
+    for (int8_t i = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 3 : 2; i >= 0; i--)
+    {
+        if ((it->second[i] & 0x0f) > 0)
+        {
+            index = i;
+            break;
+        }
+    }
+    NS_ASSERT_MSG(index >= 0, "Supported EHT-MCS And NSS Set subfield is incorrect");
+    switch (index)
+    {
+    case 0:
+        return (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 7 : 9;
+    case 1:
+        return (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 9 : 11;
+    case 2:
+        return (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 11 : 13;
+    case 3:
+        NS_ASSERT(mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY);
+        return 13;
+    default:
+        NS_ASSERT(false);
+    }
+    return 0;
+}
+
+uint8_t
+EhtCapabilities::GetHighestSupportedTxMcs(EhtMcsAndNssSet::EhtMcsMapType mapType)
+{
+    const auto it = m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.find(mapType);
+    if (it == m_supportedEhtMcsAndNssSet.supportedEhtMcsAndNssSet.cend())
+    {
+        return 0;
+    }
+    int8_t index = -1;
+    for (int8_t i = (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 3 : 2; i >= 0; i--)
+    {
+        if ((it->second[i] & 0xf0) > 0)
+        {
+            index = i;
+            break;
+        }
+    }
+    NS_ASSERT_MSG(index >= 0, "Supported EHT-MCS And NSS Set subfield is incorrect");
+    switch (index)
+    {
+    case 0:
+        return (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 7 : 9;
+    case 1:
+        return (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 9 : 11;
+    case 2:
+        return (mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY) ? 11 : 13;
+    case 3:
+        NS_ASSERT(mapType == EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY);
+        return 13;
+    default:
+        NS_ASSERT(false);
+    }
+    return 0;
+}
+
+void
+EhtCapabilities::SetPpeThresholds(uint8_t nssPe,
+                                  uint8_t ruIndexBitmask,
+                                  const std::vector<std::pair<uint8_t, uint8_t>>& ppeThresholds)
+{
+    NS_ASSERT(ppeThresholds.size() == (std::bitset<5>(ruIndexBitmask).count() * (nssPe + 1)));
+    m_phyCapabilities.ppeThresholdsPresent = 1;
+    m_ppeThresholds.nssPe = nssPe;
+    m_ppeThresholds.ruIndexBitmask = ruIndexBitmask;
+    m_ppeThresholds.ppeThresholdsInfo.clear();
+    for (const auto& [ppetMax, ppet8] : ppeThresholds)
+    {
+        m_ppeThresholds.ppeThresholdsInfo.push_back({ppetMax, ppet8});
+    }
 }
 
 void
 EhtCapabilities::SerializeInformationField(Buffer::Iterator start) const
 {
-    // TODO
+    m_macCapabilities.Serialize(start);
+    m_phyCapabilities.Serialize(start);
+    m_supportedEhtMcsAndNssSet.Serialize(start);
+    if (m_phyCapabilities.ppeThresholdsPresent)
+    {
+        m_ppeThresholds.Serialize(start);
+    }
 }
 
 uint16_t
 EhtCapabilities::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
 {
-    // TODO
-    return length;
+    uint16_t count = 0;
+    Buffer::Iterator i = start;
+
+    uint16_t nBytes = m_macCapabilities.Deserialize(i);
+    i.Next(nBytes);
+    count += nBytes;
+
+    nBytes = m_phyCapabilities.Deserialize(i);
+    i.Next(nBytes);
+    count += nBytes;
+
+    NS_ASSERT(m_heCapabilities.has_value());
+    nBytes = m_supportedEhtMcsAndNssSet.Deserialize(i,
+                                                    m_is2_4Ghz,
+                                                    m_heCapabilities->GetChannelWidthSet(),
+                                                    m_phyCapabilities.support320MhzIn6Ghz);
+    i.Next(nBytes);
+    count += nBytes;
+
+    if (m_phyCapabilities.ppeThresholdsPresent)
+    {
+        count += m_ppeThresholds.Deserialize(i);
+    }
+
+    return count;
 }
 
 std::ostream&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-capabilities.h ns-3.38/src/wifi/model/eht/eht-capabilities.h
--- ns-3.37/src/wifi/model/eht/eht-capabilities.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/eht/eht-capabilities.h	2023-03-16 20:54:23.852869300 -0700
@@ -20,11 +20,225 @@
 #ifndef EHT_CAPABILITIES_H
 #define EHT_CAPABILITIES_H
 
+#include "ns3/he-capabilities.h"
 #include "ns3/wifi-information-element.h"
 
+#include <map>
+#include <optional>
+#include <vector>
+
 namespace ns3
 {
 
+class HeCapabilities;
+
+/**
+ * EHT MAC Capabilities Info subfield.
+ * See IEEE 802.11be D1.5 9.4.2.313.2 EHT MAC Capabilities Information subfield
+ */
+struct EhtMacCapabilities
+{
+    uint8_t epcsPriorityAccessSupported : 1;      //!< EPCS Priority Access Supported
+    uint8_t ehtOmControlSupport : 1;              //!< EHT OM Control Support
+    uint8_t triggeredTxopSharingMode1Support : 1; //!< Triggered TXOP Sharing Mode 1 Support
+    uint8_t triggeredTxopSharingMode2Support : 1; //!< Triggered TXOP Sharing Mode 2 Support
+    uint8_t restrictedTwtSupport : 1;             //!< Restricted TWT Support
+    uint8_t scsTrafficDescriptionSupport : 1;     //!< SCS Traffic Description Support
+    uint8_t maxMpduLength : 2;                    //!< Maximum MPDU Length
+    uint8_t maxAmpduLengthExponentExtension : 1;  //!< Maximum A-MPDU length exponent extension
+
+    /**
+     * Get the size of the serialized EHT MAC capabilities subfield
+     *
+     * \return the size of the serialized EHT MAC capabilities subfield
+     */
+    uint16_t GetSize() const;
+    /**
+     * Serialize the EHT MAC capabilities subfield
+     *
+     * \param start iterator pointing to where the EHT MAC capabilities subfield should be written
+     * to
+     */
+    void Serialize(Buffer::Iterator& start) const;
+    /**
+     * Deserialize the EHT MAC capabilities subfield
+     *
+     * \param start iterator pointing to where the EHT MAC capabilities subfield should be read from
+     * \return the number of bytes read
+     */
+    uint16_t Deserialize(Buffer::Iterator start);
+};
+
+/**
+ * EHT PHY Capabilities Info subfield.
+ * See IEEE 802.11be D1.5 9.4.2.313.3 EHT PHY Capabilities Information subfield
+ */
+struct EhtPhyCapabilities
+{
+    uint8_t support320MhzIn6Ghz : 1;                 //!< Support For 320 MHz In 6 GHz
+    uint8_t support242ToneRuInBwLargerThan20Mhz : 1; //!< Support For 242-tone RU In BW Wider Than
+                                                     //!< 20 MHz
+    uint8_t ndpWith4TimesEhtLtfAnd32usGi : 1;        //!< NDP With 4x EHT-LTF And 3.2 μs GI
+    uint8_t partialBandwidthUlMuMimo : 1;            //!< Partial Bandwidth UL MU-MIMO
+    uint8_t suBeamformer : 1;                        //!< SU Beamformer
+    uint8_t suBeamformee : 1;                        //!< SU Beamformee
+    uint8_t beamformeeSsBwNotLargerThan80Mhz : 3;    //!< Beamformee SS (≤ 80 MHz)
+    uint8_t beamformeeSs160Mhz : 3;                  //!< Beamformee SS (= 160 MHz)
+    uint8_t beamformeeSs320Mhz : 3;                  //!< Beamformee SS (= 320 MHz)
+    uint8_t nSoundingDimensionsBwNotLargerThan80Mhz : 3; //!< Beamformee SS (≤ 80 MHz)
+    uint8_t nSoundingDimensions160Mhz : 3;               //!< Beamformee SS (= 160 MHz)
+    uint8_t nSoundingDimensions320Mhz : 3;               //!< Beamformee SS (= 320 MHz)
+    uint8_t ng16SuFeedback : 1;         //!< Support for subcarrier grouping of 16 for SU feedback
+    uint8_t ng16MuFeedback : 1;         //!< Support for subcarrier grouping of 16 for MU feedback
+    uint8_t codebooksizeSuFeedback : 1; //!< Support for a codebook size for SU feedback.
+    uint8_t codebooksizeMuFeedback : 1; //!< Support for a codebook size for MU feedback.
+    uint8_t triggeredSuBeamformingFeedback : 1;          //!< Triggered SU Beamforming Feedback
+    uint8_t triggeredMuBeamformingPartialBwFeedback : 1; //!< Triggered MU Beamforming Partial BW
+                                                         //!< Feedback
+    uint8_t triggeredCqiFeedback : 1;                    //!< Triggered CQI Feedback
+    uint8_t partialBandwidthDlMuMimo : 1;                //!< Partial Bandwidth DL MU-MIMO
+    uint8_t psrBasedSpatialReuseSupport : 1;             //!< EHT PSR-Based SR Support
+    uint8_t powerBoostFactorSupport : 1;                 //!< Power Boost Factor Support
+    uint8_t muPpdu4xEhtLtfAnd800nsGi : 1; //!< EHT MU PPDU With 4x EHT-LTF And 0.8 μs GI
+    uint8_t maxNc : 4;                    //!< Max Nc
+    uint8_t nonTriggeredCqiFeedback : 1;  //!< Non-Triggered CQI Feedback
+    uint8_t supportTx1024And4096QamForRuSmallerThan242Tones : 1; //!< Tx 1024-QAM And 4096-QAM <
+                                                                 //!< 242-tone RU Support
+    uint8_t supportRx1024And4096QamForRuSmallerThan242Tones : 1; //!< Rx 1024-QAM And 4096-QAM <
+                                                                 //!< 242-tone RU Support
+    uint8_t ppeThresholdsPresent : 1;                            //!< PPE Thresholds Present
+    uint8_t commonNominalPacketPadding : 2;                      //!< Common Nominal Packet Padding
+    uint8_t maxNumSupportedEhtLtfs : 5; //!< Maximum Number Of Supported EHT-LTFs
+    uint8_t supportMcs15 : 4;           //!< Support Of MCS 15
+    uint8_t supportEhtDupIn6GHz : 1;    //!< Support Of EHT DUP (MCS 14) In 6 GHz
+    uint8_t
+        support20MhzOperatingStaReceivingNdpWithWiderBw : 1; //!< Support For 20 MHz Operating STA
+                                                             //!< Receiving NDP With Wider Bandwidth
+    uint8_t nonOfdmaUlMuMimoBwNotLargerThan80Mhz : 1; //!< Non-OFDMA UL MU-MIMO (BW ≤ 80 MHz)
+    uint8_t nonOfdmaUlMuMimo160Mhz : 1;               //!< Non-OFDMA UL MU-MIMO (BW = 160 MHz)
+    uint8_t nonOfdmaUlMuMimo320Mhz : 1;               //!< Non-OFDMA UL MU-MIMO (BW = 320 MHz)
+    uint8_t muBeamformerBwNotLargerThan80Mhz : 1;     //!< MU Beamformer (BW ≤ 80 MHz)
+    uint8_t muBeamformer160Mhz : 1;                   //!< MU Beamformer (BW = 160 MHz)
+    uint8_t muBeamformer320Mhz : 1;                   //!< MU Beamformer (BW = 320 MHz)
+    uint8_t tbSoundingFeedbackRateLimit : 1;          //!< TB Sounding Feedback Rate Limit
+    uint8_t
+        rx1024QamInWiderBwDlOfdmaSupport : 1; //!< Rx 1024-QAM In Wider Bandwidth DL OFDMA Support
+    uint8_t
+        rx4096QamInWiderBwDlOfdmaSupport : 1; //!< Rx 4096-QAM In Wider Bandwidth DL OFDMA Support
+
+    /**
+     * Get the size of the serialized EHT PHY capabilities subfield
+     *
+     * \return the size of the serialized EHT PHY capabilities subfield
+     */
+    uint16_t GetSize() const;
+    /**
+     * Serialize the EHT PHY capabilities subfield
+     *
+     * \param start iterator pointing to where the EHT PHY capabilities subfield should be written
+     * to
+     */
+    void Serialize(Buffer::Iterator& start) const;
+    /**
+     * Deserialize the EHT PHY capabilities subfield
+     *
+     * \param start iterator pointing to where the EHT PHY capabilities subfield should be read from
+     * \return the number of bytes read
+     */
+    uint16_t Deserialize(Buffer::Iterator start);
+};
+
+/**
+ * EHT MCS and NSS Set subfield.
+ * See IEEE 802.11be D1.5 9.4.2.313.4 Supported EHT-MCS And NSS Set subfield
+ */
+struct EhtMcsAndNssSet
+{
+    /**
+     * The different EHT-MCS map types as defined in 9.4.2.313.4 Supported EHT-MCS And NSS Set
+     * field.
+     */
+    enum EhtMcsMapType : uint8_t
+    {
+        EHT_MCS_MAP_TYPE_20_MHZ_ONLY = 0,
+        EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ,
+        EHT_MCS_MAP_TYPE_160_MHZ,
+        EHT_MCS_MAP_TYPE_320_MHZ,
+        EHT_MCS_MAP_TYPE_MAX
+    };
+
+    std::map<EhtMcsMapType, std::vector<uint8_t>>
+        supportedEhtMcsAndNssSet; //!< Supported EHT-MCS And NSS Set
+
+    /**
+     * Get the size of the serialized Supported EHT-MCS And NSS Set subfield
+     *
+     * \return the size of the serialized Supported EHT-MCS And NSS Set  subfield
+     */
+    uint16_t GetSize() const;
+    /**
+     * Serialize the Supported EHT-MCS And NSS Set subfield
+     *
+     * \param start iterator pointing to where the Supported EHT-MCS And NSS Set subfield should be
+     * written to
+     */
+    void Serialize(Buffer::Iterator& start) const;
+    /**
+     * Deserialize the Supported EHT-MCS And NSS Set subfield
+     *
+     * \param start iterator pointing to where the Supported EHT-MCS And NSS Set subfield should be
+     * read from \param is2_4Ghz indicating whether PHY is operating in 2.4 GHz based on previously
+     * serialized IEs \param heSupportedChannelWidthSet supported channel width set based on
+     * previously deserialized HE capabilities IE \param support320MhzIn6Ghz flag whether 320 MHz is
+     * supported in 6 GHz band based on EHT PHY capabilities subfield \return the number of bytes
+     * read
+     */
+    uint16_t Deserialize(Buffer::Iterator start,
+                         bool is2_4Ghz,
+                         uint8_t heSupportedChannelWidthSet,
+                         bool support320MhzIn6Ghz);
+};
+
+/**
+ * EHT PPE Thresholds subfield.
+ * See IEEE 802.11be D1.5 9.4.2.313.5 EHT PPE Thresholds subfield
+ */
+struct EhtPpeThresholds
+{
+    /**
+     * EHT PPE Thresholds Info
+     */
+    struct EhtPpeThresholdsInfo
+    {
+        uint8_t ppetMax : 3; //!< PPETmax
+        uint8_t ppet8 : 3;   //!< PPE8
+    };
+
+    uint8_t nssPe : 4;                                   //!< NSS_PE
+    uint8_t ruIndexBitmask : 5;                          //!< RU Index Bitmask
+    std::vector<EhtPpeThresholdsInfo> ppeThresholdsInfo; //!< PPE Thresholds Info
+
+    /**
+     * Get the size of the serialized EHT PPE Thresholds subfield
+     *
+     * \return the size of the serialized EHT PPE Thresholds subfield
+     */
+    uint16_t GetSize() const;
+    /**
+     * Serialize the EHT PPE Thresholds subfield
+     *
+     * \param start iterator pointing to where the EHT PPE Thresholds subfield should be written to
+     */
+    void Serialize(Buffer::Iterator& start) const;
+    /**
+     * Deserialize the EHT PPE Thresholds subfield
+     *
+     * \param start iterator pointing to where the EHT PPE Thresholds subfield should be read from
+     * \return the number of bytes read
+     */
+    uint16_t Deserialize(Buffer::Iterator start);
+};
+
 /**
  * \ingroup wifi
  *
@@ -33,17 +247,112 @@
 class EhtCapabilities : public WifiInformationElement
 {
   public:
+    /**
+     * Constructor
+     */
     EhtCapabilities();
+    /**
+     * Constructor
+     *
+     * \param is2_4Ghz indicating whether PHY is operating in 2.4 GHz based on previously serialized
+     * IEs \param heCapabilities the optional HE capabilities contained in the same management frame
+     */
+    EhtCapabilities(bool is2_4Ghz, const std::optional<HeCapabilities>& heCapabilities);
     // Implementations of pure virtual methods, or overridden from base class.
     WifiInformationElementId ElementId() const override;
     WifiInformationElementId ElementIdExt() const override;
 
+    /**
+     * Set the maximum MPDU length.
+     *
+     * \param length the maximum MPDU length (3895, 7991 or 11454)
+     */
+    void SetMaxMpduLength(uint16_t length);
+
+    /**
+     * Set the maximum A-MPDU length.
+     *
+     * \param maxAmpduLength 2^(23 + x) - 1, x in the range 0 to 3
+     */
+    void SetMaxAmpduLength(uint32_t maxAmpduLength);
+
+    /**
+     * Return the maximum A-MPDU length.
+     *
+     * \return the maximum A-MPDU length
+     */
+    uint32_t GetMaxAmpduLength() const;
+
+    /**
+     * Get the maximum MPDU length.
+     *
+     * \return the maximum MPDU length in bytes
+     */
+    uint16_t GetMaxMpduLength() const;
+
+    /**
+     * Set a subfield of the Supported EHT-MCS And NSS Set.
+     *
+     * \param mapType the type of the subfield
+     * \param upperMcs the upper MCS of the range
+     * \param maxNss the maximum NSS supported for transmission in the MCS range
+     */
+    void SetSupportedTxEhtMcsAndNss(EhtMcsAndNssSet::EhtMcsMapType mapType,
+                                    uint8_t upperMcs,
+                                    uint8_t maxNss);
+    /**
+     * Set a subfield of the Supported EHT-MCS And NSS Set.
+     *
+     * \param mapType the type of the subfield
+     * \param upperMcs the upper MCS of the range
+     * \param maxNss the maximum NSS supported for reception in the MCS range
+     */
+    void SetSupportedRxEhtMcsAndNss(EhtMcsAndNssSet::EhtMcsMapType mapType,
+                                    uint8_t upperMcs,
+                                    uint8_t maxNss);
+
+    /**
+     * Get the highest supported RX MCS for a given EHT-MCS map type.
+     *
+     * \param mapType the EHT-MCS map type
+     * \return the highest supported RX MCS for the given EHT-MCS map type
+     */
+    uint8_t GetHighestSupportedRxMcs(EhtMcsAndNssSet::EhtMcsMapType mapType);
+    /**
+     * Get the highest supported TX MCS for a given EHT-MCS map type.
+     *
+     * \param mapType the EHT-MCS map type
+     * \return the highest supported TX MCS for the given EHT-MCS map type
+     */
+    uint8_t GetHighestSupportedTxMcs(EhtMcsAndNssSet::EhtMcsMapType mapType);
+
+    /**
+     * Set the EHT PPE threshold info subfield
+     *
+     * \param nssPe the NSS_PE subfield
+     * \param ruIndexBitmask the RU Index Bitmask subfield
+     * \param ppeThresholds the PPE thresholds made of pairs (PPETmax, PPET8)
+     */
+    void SetPpeThresholds(uint8_t nssPe,
+                          uint8_t ruIndexBitmask,
+                          const std::vector<std::pair<uint8_t, uint8_t>>& ppeThresholds);
+
+    EhtMacCapabilities m_macCapabilities;       //!< EHT MAC Capabilities Info subfield
+    EhtPhyCapabilities m_phyCapabilities;       //!< EHT PHY Capabilities Info subfield
+    EhtMcsAndNssSet m_supportedEhtMcsAndNssSet; //!< Supported EHT-MCS And NSS Set subfield
+    EhtPpeThresholds m_ppeThresholds;           //!< EHT PPE Threshold Info subfield
+
   private:
     uint16_t GetInformationFieldSize() const override;
     void SerializeInformationField(Buffer::Iterator start) const override;
     uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
 
-    // TODO: add fields
+    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
 };
 
 /**
@@ -56,4 +365,4 @@
 
 } // namespace ns3
 
-#endif /* HE_CAPABILITY_H */
+#endif /* EHT_CAPABILITIES_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-frame-exchange-manager.cc ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.cc
--- ns-3.37/src/wifi/model/eht/eht-frame-exchange-manager.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2022 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 "eht-frame-exchange-manager.h"
+
+#include "eht-phy.h"
+
+#include "ns3/abort.h"
+#include "ns3/log.h"
+
+#undef NS_LOG_APPEND_CONTEXT
+#define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "][mac=" << m_self << "] "
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("EhtFrameExchangeManager");
+
+NS_OBJECT_ENSURE_REGISTERED(EhtFrameExchangeManager);
+
+TypeId
+EhtFrameExchangeManager::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::EhtFrameExchangeManager")
+                            .SetParent<HeFrameExchangeManager>()
+                            .AddConstructor<EhtFrameExchangeManager>()
+                            .SetGroupName("Wifi");
+    return tid;
+}
+
+EhtFrameExchangeManager::EhtFrameExchangeManager()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+EhtFrameExchangeManager::~EhtFrameExchangeManager()
+{
+    NS_LOG_FUNCTION_NOARGS();
+}
+
+void
+EhtFrameExchangeManager::SetLinkId(uint8_t linkId)
+{
+    m_msduAggregator->SetLinkId(linkId);
+    m_mpduAggregator->SetLinkId(linkId);
+    HeFrameExchangeManager::SetLinkId(linkId);
+}
+
+Ptr<WifiMpdu>
+EhtFrameExchangeManager::CreateAliasIfNeeded(Ptr<WifiMpdu> mpdu) const
+{
+    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 ||
+        mpdu->GetHeader().GetAddr1().IsGroup() ||
+        !GetWifiRemoteStationManager()->GetMldAddress(mpdu->GetHeader().GetAddr1()))
+    {
+        return HeFrameExchangeManager::CreateAliasIfNeeded(mpdu);
+    }
+
+    mpdu = mpdu->CreateAlias(m_linkId);
+    auto& hdr = mpdu->GetHeader();
+    hdr.SetAddr2(GetAddress());
+    auto address = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(hdr.GetAddr1());
+    NS_ASSERT(address);
+    hdr.SetAddr1(*address);
+    /*
+     * Set Address3 according to Table 9-30 of 802.11-2020 and Section 35.3.3 of
+     * 802.11be D2.0 ["the value of the Address 3 field and the Address 4 field (if present)
+     * in the MAC header of a data frame shall be set based on Table 9-30 (Address field
+     * contents) and the settings of the To DS and From DS bits, where the BSSID is the
+     * MAC address of the AP affiliated with the AP MLD corresponding to that link"].
+     */
+    if (hdr.IsQosAmsdu())
+    {
+        if (hdr.IsToDs() && !hdr.IsFromDs())
+        {
+            // from STA to AP: BSSID is in Address1
+            hdr.SetAddr3(hdr.GetAddr1());
+        }
+        else if (!hdr.IsToDs() && hdr.IsFromDs())
+        {
+            // from AP to STA: BSSID is in Address2
+            hdr.SetAddr3(hdr.GetAddr2());
+        }
+    }
+
+    return mpdu;
+}
+
+void
+EhtFrameExchangeManager::ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << psdu << txVector);
+
+    // EHT-SIG, the equivalent of HE-SIG-B, is present in EHT SU transmissions, too
+    if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
+    {
+        auto phy = StaticCast<EhtPhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_EHT));
+        auto sigBMode = phy->GetSigBMode(txVector);
+        txVector.SetSigBMode(sigBMode);
+    }
+
+    HeFrameExchangeManager::ForwardPsduDown(psdu, txVector);
+}
+
+std::optional<double>
+EhtFrameExchangeManager::GetMostRecentRssi(const Mac48Address& address) const
+{
+    auto optRssi = HeFrameExchangeManager::GetMostRecentRssi(address);
+
+    if (optRssi)
+    {
+        return optRssi;
+    }
+
+    auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
+
+    if (!mldAddress)
+    {
+        // not an MLD, nothing else can be done
+        return std::nullopt;
+    }
+
+    for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
+    {
+        std::optional<Mac48Address> linkAddress;
+        if (linkId != m_linkId &&
+            (linkAddress = m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(
+                 *mldAddress)) &&
+            (optRssi = m_mac->GetWifiRemoteStationManager(linkId)->GetMostRecentRssi(*linkAddress)))
+        {
+            return optRssi;
+        }
+    }
+
+    return std::nullopt;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-frame-exchange-manager.h ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.h
--- ns-3.37/src/wifi/model/eht/eht-frame-exchange-manager.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022 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 EHT_FRAME_EXCHANGE_MANAGER_H
+#define EHT_FRAME_EXCHANGE_MANAGER_H
+
+#include "ns3/he-frame-exchange-manager.h"
+
+namespace ns3
+{
+
+/**
+ * \ingroup wifi
+ *
+ * EhtFrameExchangeManager handles the frame exchange sequences
+ * for EHT stations.
+ */
+class EhtFrameExchangeManager : public HeFrameExchangeManager
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+    EhtFrameExchangeManager();
+    ~EhtFrameExchangeManager() override;
+
+    void SetLinkId(uint8_t linkId) override;
+    Ptr<WifiMpdu> CreateAliasIfNeeded(Ptr<WifiMpdu> mpdu) const override;
+
+    /**
+     * 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
+     * packet received from another station of the same MLD.
+     *
+     * \param address of the remote station
+     * \return the RSSI (in dBm) of the most recent packet received from the remote station
+     */
+    std::optional<double> GetMostRecentRssi(const Mac48Address& address) const override;
+
+  protected:
+    void ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector& txVector) override;
+};
+
+} // namespace ns3
+
+#endif /* EHT_FRAME_EXCHANGE_MANAGER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-operation.cc ns-3.38/src/wifi/model/eht/eht-operation.cc
--- ns-3.37/src/wifi/model/eht/eht-operation.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/model/eht/eht-operation.cc	2023-03-16 20:54:23.856869200 -0700
@@ -0,0 +1,293 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2022
+ *
+ * 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: Sharan Naribole <sharan.naribole@gmail.com>
+ */
+
+#include "eht-operation.h"
+
+#include <ns3/assert.h>
+
+#include <algorithm>
+
+namespace ns3
+{
+
+void
+EhtOperation::EhtOpParams::Serialize(Buffer::Iterator& start) const
+{
+    uint8_t val = opInfoPresent | (disabledSubchBmPresent << 1) | (defaultPeDur << 2) |
+                  (grpBuIndLimit << 3) | (grpBuExp << 4);
+    start.WriteU8(val);
+}
+
+uint16_t
+EhtOperation::EhtOpParams::Deserialize(Buffer::Iterator start)
+{
+    auto params = start.ReadU8();
+    opInfoPresent = params & 0x01;
+    disabledSubchBmPresent = (params >> 1) & 0x01;
+    defaultPeDur = (params >> 2) & 0x01;
+    grpBuIndLimit = (params >> 3) & 0x01;
+    grpBuExp = (params >> 4) & 0x03;
+    return WIFI_EHT_OP_PARAMS_SIZE_B;
+}
+
+/**
+ * set the max Tx/Rx NSS for input MCS index range
+ * \param vec vector of max NSS per MCS
+ * \param maxNss max NSS for input MCS range
+ * \param mcsStart MCS index start
+ * \param mcsEnd MCS index end
+ */
+void
+SetMaxNss(std::vector<uint8_t>& vec, uint8_t maxNss, uint8_t mcsStart, uint8_t mcsEnd)
+{
+    NS_ASSERT(mcsStart <= mcsEnd);
+    NS_ASSERT((mcsStart >= 0) && (mcsEnd <= WIFI_EHT_MAX_MCS_INDEX));
+    NS_ASSERT((maxNss >= 1) && (maxNss <= WIFI_EHT_MAX_NSS_CONFIGURABLE));
+    for (auto index = mcsStart; index <= mcsEnd; index++)
+    {
+        vec[index] = maxNss;
+    }
+}
+
+/**
+ * Get the max Tx/Rx NSS for input MCS index range
+ * \param vec vector of max NSS per MCS
+ * \param mcsStart MCS index start
+ * \param mcsEnd MCS index end
+ * \return max Rx NSS
+ */
+uint32_t
+GetMaxNss(const std::vector<uint8_t>& vec, uint8_t mcsStart, uint8_t mcsEnd)
+{
+    NS_ASSERT(mcsStart <= mcsEnd);
+    NS_ASSERT((mcsStart >= 0) && (mcsEnd <= WIFI_EHT_MAX_MCS_INDEX));
+    auto minMaxNss = WIFI_EHT_MAX_NSS_CONFIGURABLE;
+    for (auto index = mcsStart; index <= mcsEnd; index++)
+    {
+        if (vec[index] < minMaxNss)
+        {
+            minMaxNss = vec[index];
+        }
+    }
+    return minMaxNss;
+}
+
+void
+EhtOperation::EhtBasicMcsNssSet::Serialize(Buffer::Iterator& start) const
+{
+    uint32_t val = GetMaxNss(maxRxNss, 0, 7) | (GetMaxNss(maxTxNss, 0, 7) << 4) |
+                   (GetMaxNss(maxRxNss, 8, 9) << 8) | (GetMaxNss(maxTxNss, 8, 9) << 12) |
+                   (GetMaxNss(maxRxNss, 10, 11) << 16) | (GetMaxNss(maxTxNss, 10, 11) << 20) |
+                   (GetMaxNss(maxRxNss, 12, 13) << 24) | (GetMaxNss(maxTxNss, 12, 13) << 28);
+    start.WriteHtolsbU32(val);
+}
+
+uint16_t
+EhtOperation::EhtBasicMcsNssSet::Deserialize(Buffer::Iterator start)
+{
+    auto subfield = start.ReadLsbtohU32();
+    auto rxNssMcs0_7 = subfield & 0xf; // Max Rx NSS MCS 0-7
+    SetMaxNss(maxRxNss, rxNssMcs0_7, 0, 7);
+    auto txNssMcs0_7 = (subfield >> 4) & 0xf; // Max Tx NSS MCS 0-7
+    SetMaxNss(maxTxNss, txNssMcs0_7, 0, 7);
+    auto rxNssMcs8_9 = (subfield >> 8) & 0xf; // Max Rx NSS MCS 8-9
+    SetMaxNss(maxRxNss, rxNssMcs8_9, 8, 9);
+    auto txNssMcs8_9 = (subfield >> 12) & 0xf; // Max Tx NSS MCS 8-9
+    SetMaxNss(maxTxNss, txNssMcs8_9, 8, 9);
+    auto rxNssMcs10_11 = (subfield >> 16) & 0xf; // Max Rx NSS MCS 10-11
+    SetMaxNss(maxRxNss, rxNssMcs10_11, 10, 11);
+    auto txNssMcs10_11 = (subfield >> 20) & 0xf; // Max Tx NSS MCS 10-11
+    SetMaxNss(maxTxNss, txNssMcs10_11, 10, 11);
+    auto rxNssMcs12_13 = (subfield >> 24) & 0xf; // Max Rx NSS MCS 12-13
+    SetMaxNss(maxRxNss, rxNssMcs12_13, 12, 13);
+    auto txNssMcs12_13 = (subfield >> 28) & 0xf; // Max Tx NSS MCS 12-13
+    SetMaxNss(maxTxNss, txNssMcs12_13, 12, 13);
+    return WIFI_EHT_BASIC_MCS_NSS_SET_SIZE_B;
+}
+
+void
+EhtOperation::EhtOpInfo::Serialize(Buffer::Iterator& start) const
+{
+    start.WriteU8(control.channelWidth); // Control
+    start.WriteU8(ccfs0);                // CCFS 0
+    start.WriteU8(ccfs1);                // CCFS 1
+    if (disabledSubchBm.has_value())
+    {
+        start.WriteU16(disabledSubchBm.value());
+    }
+}
+
+uint16_t
+EhtOperation::EhtOpInfo::Deserialize(Buffer::Iterator start, bool disabledSubchBmPresent)
+{
+    auto i = start;
+    uint16_t count = 0;
+    auto controlSubfield = i.ReadU8();
+    count++;
+    control.channelWidth = controlSubfield & 0x7;
+    ccfs0 = i.ReadU8();
+    count++;
+    ccfs1 = i.ReadU8();
+    count++;
+    NS_ASSERT_MSG(count == WIFI_EHT_OP_INFO_BASIC_SIZE_B,
+                  "Incorrect EHT Operation Info deserialize");
+    if (!disabledSubchBmPresent)
+    {
+        return count;
+    }
+    disabledSubchBm = i.ReadU16();
+    count += 2;
+    NS_ASSERT_MSG(count == WIFI_EHT_OP_INFO_BASIC_SIZE_B + WIFI_EHT_DISABLED_SUBCH_BM_SIZE_B,
+                  "Incorrect EHT Operation Info deserialize");
+    return count;
+}
+
+EhtOperation::EhtOperation()
+{
+    m_mcsNssSet.maxRxNss.assign(WIFI_EHT_MAX_MCS_INDEX + 1, WIFI_DEFAULT_EHT_MAX_NSS);
+    m_mcsNssSet.maxTxNss.assign(WIFI_EHT_MAX_MCS_INDEX + 1, WIFI_DEFAULT_EHT_MAX_NSS);
+}
+
+WifiInformationElementId
+EhtOperation::ElementId() const
+{
+    return IE_EXTENSION;
+}
+
+WifiInformationElementId
+EhtOperation::ElementIdExt() const
+{
+    return IE_EXT_EHT_OPERATION;
+}
+
+uint16_t
+EhtOperation::GetInformationFieldSize() const
+{
+    // IEEE 802.11be D2.0 9.4.2.311
+    auto ret =
+        WIFI_IE_ELEMENT_ID_EXT_SIZE + WIFI_EHT_OP_PARAMS_SIZE_B + WIFI_EHT_BASIC_MCS_NSS_SET_SIZE_B;
+    if (!m_params.opInfoPresent)
+    {
+        return ret;
+    }
+    ret += WIFI_EHT_OP_INFO_BASIC_SIZE_B;
+    if (!m_params.disabledSubchBmPresent)
+    {
+        return ret;
+    }
+    return ret + WIFI_EHT_DISABLED_SUBCH_BM_SIZE_B;
+}
+
+void
+EhtOperation::SetMaxRxNss(uint8_t maxNss, uint8_t mcsStart, uint8_t mcsEnd)
+{
+    NS_ASSERT(mcsStart <= mcsEnd);
+    NS_ASSERT((mcsStart >= 0) && (mcsEnd <= WIFI_EHT_MAX_MCS_INDEX));
+    NS_ASSERT((maxNss >= 1) && (maxNss <= WIFI_EHT_MAX_NSS_CONFIGURABLE));
+    SetMaxNss(m_mcsNssSet.maxRxNss, maxNss, mcsStart, mcsEnd);
+}
+
+void
+EhtOperation::SetMaxTxNss(uint8_t maxNss, uint8_t mcsStart, uint8_t mcsEnd)
+{
+    NS_ASSERT(mcsStart <= mcsEnd);
+    NS_ASSERT((mcsStart >= 0) && (mcsEnd <= WIFI_EHT_MAX_MCS_INDEX));
+    NS_ASSERT((maxNss >= 1) && (maxNss <= WIFI_EHT_MAX_NSS_CONFIGURABLE));
+    SetMaxNss(m_mcsNssSet.maxTxNss, maxNss, mcsStart, mcsEnd);
+}
+
+void
+EhtOperation::SerializeInformationField(Buffer::Iterator start) const
+{
+    m_params.Serialize(start);
+    m_mcsNssSet.Serialize(start);
+    NS_ASSERT_MSG(m_params.opInfoPresent == m_opInfo.has_value(),
+                  "Incorrect setting of EHT Operation Information Present bit");
+
+    if (!m_params.opInfoPresent)
+    { // EHT Operation Information Present not set
+        return;
+    }
+
+    auto disabledSubchBmPresent = m_params.disabledSubchBmPresent > 0;
+    NS_ASSERT_MSG(disabledSubchBmPresent == m_opInfo->disabledSubchBm.has_value(),
+                  "Incorrect setting of Disabled Subchannel Bitmap Present bit");
+    m_opInfo->Serialize(start);
+}
+
+uint16_t
+EhtOperation::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
+{
+    auto i = start;
+    i.Next(m_params.Deserialize(i));
+    i.Next(m_mcsNssSet.Deserialize(i));
+    uint16_t count = i.GetDistanceFrom(start);
+
+    if (!m_params.opInfoPresent)
+    {
+        NS_ASSERT_MSG(count == length, "Unexpected EHT Operation size");
+    }
+
+    if (m_params.opInfoPresent > 0)
+    {
+        auto disabledSubchBmPresent = m_params.disabledSubchBmPresent > 0;
+        m_opInfo = EhtOpInfo();
+        i.Next(m_opInfo->Deserialize(i, disabledSubchBmPresent));
+        count = i.GetDistanceFrom(start);
+    }
+
+    NS_ABORT_MSG_IF(count != length,
+                    "EHT Operation Length (" << +length
+                                             << ") differs "
+                                                "from actual number of bytes read ("
+                                             << +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.37/src/wifi/model/eht/eht-operation.h ns-3.38/src/wifi/model/eht/eht-operation.h
--- ns-3.37/src/wifi/model/eht/eht-operation.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/model/eht/eht-operation.h	2023-03-16 20:54:23.856869200 -0700
@@ -0,0 +1,202 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2022
+ *
+ * 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: Sharan Naribole <sharan.naribole@gmail.com>
+ */
+
+#ifndef EHT_OPERATION_H
+#define EHT_OPERATION_H
+
+#include <ns3/wifi-information-element.h>
+
+#include <optional>
+#include <vector>
+
+namespace ns3
+{
+
+/// IEEE 802.11be D2.0 Figure 9-1002ai
+constexpr uint8_t WIFI_EHT_MAX_MCS_INDEX = 13;
+/// IEEE 802.11be D2.0 Figure 9-1002b
+constexpr uint16_t WIFI_EHT_OP_PARAMS_SIZE_B = 1;
+/// IEEE 802.11be D2.0 Figure 9-1002c
+constexpr uint16_t WIFI_EHT_OP_INFO_BASIC_SIZE_B = 3;
+/// IEEE 802.11be D2.0 Figure 9-1002c
+constexpr uint16_t WIFI_EHT_DISABLED_SUBCH_BM_SIZE_B = 2;
+/// IEEE 802.11be D2.0 Figure 9-1002ai
+constexpr uint16_t WIFI_EHT_BASIC_MCS_NSS_SET_SIZE_B = 4;
+/// Default max Tx/Rx NSS
+constexpr uint8_t WIFI_DEFAULT_EHT_MAX_NSS = 1;
+/// Max NSS configurable, 802.11be D2.0 Table 9-401m
+constexpr uint8_t WIFI_EHT_MAX_NSS_CONFIGURABLE = 8;
+/// Default EHT Operation Info Present
+constexpr uint8_t WIFI_DEFAULT_EHT_OP_INFO_PRESENT = 0;
+/// Default Disabled Subch Bitmap Present
+constexpr uint8_t WIFI_DEFAULT_EHT_OP_DIS_SUBCH_BM_PRESENT = 0;
+/// Default PE Duration
+constexpr uint8_t WIFI_DEFAULT_EHT_OP_PE_DUR = 0;
+/// Default Group Addressed BU Indication Limit
+constexpr uint8_t WIFI_DEFAULT_GRP_BU_IND_LIMIT = 0;
+/// Default Group Addressed BU Exponent
+constexpr uint8_t WIFI_DEFAULT_GRP_BU_EXP = 0;
+
+/**
+ * \brief EHT Operation Information Element
+ * \ingroup wifi
+ *
+ * This class serializes and deserializes
+ * the EHT Operation Information Element
+ * IEEE 802.11be D2.0 9.4.2.311
+ *
+ */
+class EhtOperation : public WifiInformationElement
+{
+  public:
+    /**
+     * EHT Operation Parameters subfield
+     * IEEE 802.11be D2.0 Figure 9-1002b
+     */
+    struct EhtOpParams
+    {
+        /// EHT Operation Information Present
+        uint8_t opInfoPresent{WIFI_DEFAULT_EHT_OP_INFO_PRESENT};
+        /// Disabled Subchannel Bitmap Present
+        uint8_t disabledSubchBmPresent{WIFI_DEFAULT_EHT_OP_DIS_SUBCH_BM_PRESENT};
+        /// EHT Default PE Duration
+        uint8_t defaultPeDur{WIFI_DEFAULT_EHT_OP_PE_DUR};
+        /// Group Addressed BU Indication Limit
+        uint8_t grpBuIndLimit{WIFI_DEFAULT_GRP_BU_IND_LIMIT};
+        /// Group Addressed BU Indication Exponent
+        uint8_t grpBuExp{WIFI_DEFAULT_GRP_BU_EXP};
+
+        /**
+         * Serialize the EHT Operation Parameters subfield
+         *
+         * \param start iterator pointing to where the subfield should be written to
+         */
+        void Serialize(Buffer::Iterator& start) const;
+        /**
+         * Deserialize the EHT Operation Parameters subfield
+         *
+         * \param start iterator pointing to where the subfield should be read from
+         * \return the number of octets read
+         */
+        uint16_t Deserialize(Buffer::Iterator start);
+    };
+
+    /**
+     * EHT Operation Information Control subfield
+     * IEEE 802.11be D2.0 Figure 9-1002D
+     */
+    struct EhtOpControl
+    {
+        uint8_t channelWidth : 3; ///< EHT BSS bandwidth
+    };
+
+    /**
+     * EHT Operation Information subfield
+     * IEEE 802.11be D2.0 Figure 9-1002c
+     */
+    struct EhtOpInfo
+    {
+        EhtOpControl control;                    ///< Control subfield
+        uint8_t ccfs0;                           ///< Channel center frequency segment 0
+        uint8_t ccfs1;                           ///< Channel center frequency segment 1
+        std::optional<uint16_t> disabledSubchBm; ///< Disabled subchannel bitmap
+
+        /**
+         * Serialize the EHT Operation Information subfield
+         *
+         * \param start iterator pointing to where the subfield should be written to
+         */
+        void Serialize(Buffer::Iterator& start) const;
+        /**
+         * Deserialize the EHT Operation Information subfield
+         *
+         * \param start iterator pointing to where the subfield should be read from
+         * \param disabledSubchBmPresent EHT Operation Param Disabled Subchannel Bitmap Present
+         * \return the number of octets read
+         */
+        uint16_t Deserialize(Buffer::Iterator start, bool disabledSubchBmPresent);
+    };
+
+    /**
+     * Basic EHT-MCS and NSS Set subfield
+     * IEEE 802.11be D2.0 Figure 9-1002ai
+     */
+    struct EhtBasicMcsNssSet
+    {
+        std::vector<uint8_t> maxRxNss{}; ///< Max Rx NSS per MCS
+        std::vector<uint8_t> maxTxNss{}; ///< Max Tx NSS per MCS
+
+        /**
+         * Serialize the Basic EHT-MCS and NSS Set subfield
+         *
+         * \param start iterator pointing to where the subfield should be written to
+         */
+        void Serialize(Buffer::Iterator& start) const;
+        /**
+         * Deserialize the Basic EHT-MCS and NSS Set subfield
+         *
+         * \param start iterator pointing to where the subfield should be read from
+         * \return the number of octets read
+         */
+        uint16_t Deserialize(Buffer::Iterator start);
+    };
+
+    EhtOperation();
+    WifiInformationElementId ElementId() const override;
+    WifiInformationElementId ElementIdExt() const override;
+
+    /**
+     * Set the max Rx NSS for input MCS index range
+     * \param maxNss the maximum supported Rx NSS for MCS group
+     * \param mcsStart MCS index start
+     * \param mcsEnd MCS index end
+     */
+    void SetMaxRxNss(uint8_t maxNss, uint8_t mcsStart, uint8_t mcsEnd);
+    /**
+     * Set the max Tx NSS for input MCS index range
+     * \param maxNss the maximum supported Rx NSS for MCS group
+     * \param mcsStart MCS index start
+     * \param mcsEnd MCS index end
+     */
+    void SetMaxTxNss(uint8_t maxNss, uint8_t mcsStart, uint8_t mcsEnd);
+
+    EhtOpParams m_params;              ///< EHT Operation Parameters
+    EhtBasicMcsNssSet m_mcsNssSet;     ///< Basic EHT-MCS and NSS set
+    std::optional<EhtOpInfo> m_opInfo; ///< EHT Operation Information
+
+  private:
+    uint16_t GetInformationFieldSize() const override;
+    void SerializeInformationField(Buffer::Iterator start) const override;
+    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.37/src/wifi/model/eht/eht-phy.cc ns-3.38/src/wifi/model/eht/eht-phy.cc
--- ns-3.37/src/wifi/model/eht/eht-phy.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/eht/eht-phy.cc	2023-03-16 20:54:23.856869200 -0700
@@ -98,6 +98,19 @@
     }
 }
 
+WifiMode
+EhtPhy::GetSigBMode(const WifiTxVector& txVector) const
+{
+    if (txVector.IsDlMu())
+    {
+        return HePhy::GetSigBMode(txVector);
+    }
+    // we get here in case of EHT SU transmission
+    // TODO fix the MCS used for EHT-SIG
+    auto smallestMcs = std::min<uint8_t>(5, txVector.GetMode().GetMcsValue());
+    return VhtPhy::GetVhtMcs(smallestMcs);
+}
+
 Time
 EhtPhy::GetDuration(WifiPpduField field, const WifiTxVector& txVector) const
 {
@@ -116,6 +129,25 @@
     }
 }
 
+Time
+EhtPhy::CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const
+{
+    Time duration = GetDuration(WIFI_PPDU_FIELD_PREAMBLE, txVector) +
+                    GetDuration(WIFI_PPDU_FIELD_NON_HT_HEADER, txVector) +
+                    GetDuration(WIFI_PPDU_FIELD_U_SIG, txVector);
+    return duration;
+}
+
+Time
+EhtPhy::CalculateNonOfdmaDurationForHeMu(const WifiTxVector& txVector) const
+{
+    Time duration = GetDuration(WIFI_PPDU_FIELD_PREAMBLE, txVector) +
+                    GetDuration(WIFI_PPDU_FIELD_NON_HT_HEADER, txVector) +
+                    GetDuration(WIFI_PPDU_FIELD_U_SIG, txVector) +
+                    GetDuration(WIFI_PPDU_FIELD_EHT_SIG, txVector);
+    return duration;
+}
+
 const PhyEntity::PpduFormats&
 EhtPhy::GetPpduFormats() const
 {
@@ -133,8 +165,7 @@
                            ppduDuration,
                            m_wifiPhy->GetPhyBand(),
                            ObtainNextUid(txVector),
-                           HePpdu::PSD_NON_HE_PORTION,
-                           m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20));
+                           HePpdu::PSD_NON_HE_PORTION);
 }
 
 PhyEntity::PhyFieldRxStatus
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-phy.h ns-3.38/src/wifi/model/eht/eht-phy.h
--- ns-3.37/src/wifi/model/eht/eht-phy.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/eht/eht-phy.h	2023-03-16 20:54:23.856869200 -0700
@@ -64,6 +64,7 @@
     Ptr<WifiPpdu> BuildPpdu(const WifiConstPsduMap& psdus,
                             const WifiTxVector& txVector,
                             Time ppduDuration) override;
+    WifiMode GetSigBMode(const WifiTxVector& txVector) const override;
 
     /**
      * Initialize all EHT modes.
@@ -259,6 +260,9 @@
                                 PhyFieldRxStatus status,
                                 WifiPpduField field) override;
     WifiPhyRxfailureReason GetFailureReason(WifiPpduField field) const override;
+    Time CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const override;
+    Time CalculateNonOfdmaDurationForHeMu(const WifiTxVector& txVector) const override;
+
     /**
      * Create and return the EHT MCS corresponding to
      * the provided index.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-ppdu.cc ns-3.38/src/wifi/model/eht/eht-ppdu.cc
--- ns-3.37/src/wifi/model/eht/eht-ppdu.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/eht/eht-ppdu.cc	2023-03-16 20:54:23.856869200 -0700
@@ -35,17 +35,24 @@
                  Time ppduDuration,
                  WifiPhyBand band,
                  uint64_t uid,
-                 TxPsdFlag flag,
-                 uint8_t p20Index)
-    : HePpdu(psdus, txVector, txCenterFreq, ppduDuration, band, uid, flag, p20Index)
+                 TxPsdFlag flag)
+    : HePpdu(psdus, txVector, txCenterFreq, ppduDuration, band, uid, flag)
 {
-    NS_LOG_FUNCTION(this << psdus << txVector << txCenterFreq << ppduDuration << band << uid << flag
-                         << p20Index);
-}
+    NS_LOG_FUNCTION(this << psdus << txVector << txCenterFreq << ppduDuration << band << uid
+                         << flag);
 
-EhtPpdu::~EhtPpdu()
-{
-    NS_LOG_FUNCTION(this);
+    // 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();
+    }
 }
 
 WifiPpduType
@@ -79,30 +86,37 @@
     return (m_preamble == WIFI_PREAMBLE_EHT_TB) && !m_muUserInfos.empty();
 }
 
-WifiTxVector
-EhtPpdu::DoGetTxVector() const
-{
-    // FIXME: define EHT PHY headers
-    WifiTxVector txVector;
-    txVector.SetPreambleType(m_preamble);
-    txVector.SetMode(EhtPhy::GetEhtMcs(m_heSig.GetMcs()));
-    txVector.SetChannelWidth(m_heSig.GetChannelWidth());
-    txVector.SetNss(m_heSig.GetNStreams());
-    txVector.SetGuardInterval(m_heSig.GetGuardInterval());
-    txVector.SetBssColor(m_heSig.GetBssColor());
-    txVector.SetLength(m_lSig.GetLength());
+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());
     txVector.SetAggregation(m_psdus.size() > 1 || m_psdus.begin()->second->IsAggregate());
+    if (!m_muUserInfos.empty())
+    {
+        txVector.SetEhtPpduType(0); // FIXME set to 2 for DL MU-MIMO (non-OFDMA) transmission
+    }
     for (const auto& muUserInfo : m_muUserInfos)
     {
         txVector.SetHeMuUserInfo(muUserInfo.first, muUserInfo.second);
     }
-    return txVector;
+    if (ns3::IsDlMu(m_preamble))
+    {
+        txVector.SetSigBMode(HePhy::GetVhtMcs(heSig.GetMcs()));
+        txVector.SetRuAllocation(m_ruAllocation);
+    }
 }
 
 Ptr<WifiPpdu>
 EhtPpdu::Copy() const
 {
-    return ns3::Copy(Ptr(this));
+    return Ptr<WifiPpdu>(new EhtPpdu(*this), false);
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/eht-ppdu.h ns-3.38/src/wifi/model/eht/eht-ppdu.h
--- ns-3.37/src/wifi/model/eht/eht-ppdu.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/eht/eht-ppdu.h	2023-03-16 20:54:23.856869200 -0700
@@ -52,7 +52,6 @@
      * \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
-     * \param p20Index the index of the primary 20 MHz channel
      */
     EhtPpdu(const WifiConstPsduMap& psdus,
             const WifiTxVector& txVector,
@@ -60,21 +59,21 @@
             Time ppduDuration,
             WifiPhyBand band,
             uint64_t uid,
-            TxPsdFlag flag,
-            uint8_t p20Index);
-    /**
-     * Destructor for EhtPpdu.
-     */
-    ~EhtPpdu() override;
+            TxPsdFlag flag);
 
     WifiPpduType GetType() const override;
     Ptr<WifiPpdu> Copy() const override;
 
-  protected:
+  private:
     bool IsDlMu() const override;
     bool IsUlMu() const override;
-    WifiTxVector DoGetTxVector() const override;
-}; // class EhtPpdu
+    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
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/tid-to-link-mapping-element.cc ns-3.38/src/wifi/model/eht/tid-to-link-mapping-element.cc
--- ns-3.37/src/wifi/model/eht/tid-to-link-mapping-element.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/model/eht/tid-to-link-mapping-element.cc	2023-03-16 20:54:23.856869200 -0700
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2022
+ *
+ * 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: Sharan Naribole <sharan.naribole@gmail.com>
+ */
+
+#include <ns3/assert.h>
+#include <ns3/tid-to-link-mapping-element.h>
+
+namespace ns3
+{
+
+uint16_t
+TidToLinkMapping::Control::GetSubfieldSize() const
+{
+    // IEEE 802.11be D2.0 Figure 9-1002an
+    NS_ASSERT_MSG(defaultMapping != presenceBitmap.has_value(),
+                  "Presence bitmap not expected if default mapping is set");
+    auto size = WIFI_TID_TO_LINK_MAPPING_CONTROL_BASIC_SIZE_B;
+    if (defaultMapping)
+    {
+        return size;
+    }
+    return size + WIFI_LINK_MAPPING_PRESENCE_IND_SIZE_B;
+}
+
+void
+TidToLinkMapping::Control::Serialize(Buffer::Iterator& start) const
+{
+    uint8_t val = static_cast<uint8_t>(direction);
+    val |= ((defaultMapping ? 1 : 0) << 2);
+    start.WriteU8(val);
+    NS_ASSERT_MSG(defaultMapping != presenceBitmap.has_value(),
+                  "Presence bitmap not expected if default mapping is set");
+    if (presenceBitmap.has_value())
+    {
+        start.WriteU8(presenceBitmap.value());
+    }
+}
+
+uint16_t
+TidToLinkMapping::Control::Deserialize(Buffer::Iterator start)
+{
+    auto i = start;
+    uint8_t count = 0;
+    auto val = i.ReadU8();
+    count++;
+
+    direction = static_cast<TidLinkMapDir>(val & 0x03);
+    defaultMapping = (((val >> 2) & 0x01) == 1);
+    if (defaultMapping)
+    {
+        return count;
+    }
+    presenceBitmap = i.ReadU8();
+    return ++count;
+}
+
+WifiInformationElementId
+TidToLinkMapping::ElementId() const
+{
+    return IE_EXTENSION;
+}
+
+WifiInformationElementId
+TidToLinkMapping::ElementIdExt() const
+{
+    return IE_EXT_TID_TO_LINK_MAPPING_ELEMENT;
+}
+
+void
+TidToLinkMapping::SetLinkMappingOfTid(uint8_t tid, std::list<uint8_t> linkIds)
+{
+    NS_ABORT_MSG_IF(tid > 7, "Invalid tid: " << +tid);
+    NS_ABORT_MSG_IF(m_control.defaultMapping,
+                    "Per-TID link mapping not expected if default mapping is set");
+
+    // derive link mapping for the given TID
+    uint16_t linkMapping = 0;
+
+    for (const auto& linkId : linkIds)
+    {
+        linkMapping |= (1 << linkId);
+    }
+
+    m_linkMapping[tid] = linkMapping;
+
+    if (!m_control.presenceBitmap.has_value())
+    {
+        m_control.presenceBitmap = 0;
+    }
+    *m_control.presenceBitmap |= (1 << tid);
+}
+
+std::list<uint8_t>
+TidToLinkMapping::GetLinkMappingOfTid(uint8_t tid) const
+{
+    auto it = m_linkMapping.find(tid);
+
+    if (it == m_linkMapping.end())
+    {
+        return {};
+    }
+
+    std::list<uint8_t> linkIds;
+    for (uint8_t linkId = 0; linkId < 15; linkId++)
+    {
+        if (((it->second >> linkId) & 0x0001) == 1)
+        {
+            linkIds.push_back(linkId);
+        }
+    }
+
+    return linkIds;
+}
+
+uint16_t
+TidToLinkMapping::GetInformationFieldSize() const
+{
+    // IEEE 802.11be D2.0 9.4.2.314 TID-To-Link Mapping element
+    uint16_t ret = WIFI_IE_ELEMENT_ID_EXT_SIZE; // Element ID Extension
+    ret += m_control.GetSubfieldSize();
+    NS_ASSERT_MSG(m_linkMapping.empty() == m_control.defaultMapping,
+                  "Per-TID link mapping not expected if default mapping is set");
+    ret += WIFI_LINK_MAPPING_PER_TID_SIZE_B * (m_linkMapping.size());
+    return ret;
+}
+
+void
+TidToLinkMapping::SerializeInformationField(Buffer::Iterator start) const
+{
+    // IEEE 802.11be D2.0 9.4.2.314 TID-To-Link Mapping element
+    m_control.Serialize(start);
+    NS_ASSERT_MSG(m_linkMapping.empty() == m_control.defaultMapping,
+                  "Per-TID link mapping not expected if default mapping is set");
+
+    for (const auto& [tid, linkMapping] : m_linkMapping)
+    {
+        start.WriteHtolsbU16(linkMapping);
+    }
+}
+
+uint16_t
+TidToLinkMapping::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
+{
+    auto i = start;
+    uint16_t count = 0;
+    auto nCtrlOctets = m_control.Deserialize(i);
+    NS_ASSERT_MSG(nCtrlOctets <= length, "Tid-to-Link Mapping deserialize error");
+    i.Next(nCtrlOctets);
+    count += nCtrlOctets;
+    m_linkMapping.clear();
+    if (m_control.presenceBitmap.has_value())
+    {
+        NS_ABORT_MSG_IF(m_control.defaultMapping,
+                        "Default mapping should not be set when presence bitmap is present");
+        const auto presenceBitmap = m_control.presenceBitmap.value();
+        for (uint8_t tid = 0; tid < 8; tid++)
+        {
+            if (((presenceBitmap >> tid) & 0x01) == 1)
+            {
+                m_linkMapping[tid] = i.ReadLsbtohU16();
+                count += 2;
+            }
+        }
+    }
+
+    NS_ABORT_MSG_IF(count != length,
+                    "TID-to-Link Mapping Length (" << +length
+                                                   << ") differs "
+                                                      "from actual number of bytes read ("
+                                                   << +count << ")");
+    return count;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/eht/tid-to-link-mapping-element.h ns-3.38/src/wifi/model/eht/tid-to-link-mapping-element.h
--- ns-3.37/src/wifi/model/eht/tid-to-link-mapping-element.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/model/eht/tid-to-link-mapping-element.h	2023-03-16 20:54:23.856869200 -0700
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2022
+ *
+ * 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: Sharan Naribole <sharan.naribole@gmail.com>
+ */
+
+#ifndef TID_TO_LINK_MAPPING_H
+#define TID_TO_LINK_MAPPING_H
+
+#include <ns3/wifi-information-element.h>
+
+#include <list>
+#include <map>
+#include <optional>
+#include <vector>
+
+namespace ns3
+{
+
+/**
+ * TID-to-Link Mapping Control Direction
+ * IEEE 802.11be D2.0 Figure 9-1002an
+ */
+enum class TidLinkMapDir : uint8_t
+{
+    DOWNLINK = 0,
+    UPLINK = 1,
+    BOTH_DIRECTIONS = 2,
+};
+
+/// whether to enforce the default link mapping
+constexpr auto DEFAULT_WIFI_TID_LINK_MAPPING{true};
+/// default value for the Direction subfield of the TID-To-Link Control field
+constexpr auto DEFAULT_WIFI_TID_LINK_MAP_DIR{TidLinkMapDir::BOTH_DIRECTIONS};
+/// size in bytes of the Link Mapping Of TID n field (IEEE 802.11be D2.0 9.4.2.314)
+constexpr uint16_t WIFI_LINK_MAPPING_PER_TID_SIZE_B = 2;
+/// size in bytes of the TID-To-Link Control field with default link mapping
+constexpr uint16_t WIFI_TID_TO_LINK_MAPPING_CONTROL_BASIC_SIZE_B =
+    1; // IEEE 802.11be D2.0 9.4.2.314
+/// size in bytes of the Link Mapping Presence Indicator field (IEEE 802.11be D2.0 9.4.2.314)
+constexpr uint16_t WIFI_LINK_MAPPING_PRESENCE_IND_SIZE_B = 1;
+
+/**
+ * \brief TID-to-Link Mapping Information Element
+ * \ingroup wifi
+ *
+ * This class serializes and deserializes
+ * the TID-to-Link Mapping element
+ * IEEE 802.11be D2.0 9.4.2.314
+ *
+ */
+class TidToLinkMapping : public WifiInformationElement
+{
+  public:
+    /**
+     * TID-to-Link Mapping Control subfield
+     * IEEE 802.11be D2.0 Figure 9-1002an
+     */
+    struct Control
+    {
+        TidLinkMapDir direction{DEFAULT_WIFI_TID_LINK_MAP_DIR}; ///< Direction
+        bool defaultMapping{DEFAULT_WIFI_TID_LINK_MAPPING};     ///< Default link mapping
+        std::optional<uint8_t> presenceBitmap;                  ///< Link Mapping Presence Indicator
+
+        /// \return Serialized size of TID-to-Link Mapping Control in octets
+        uint16_t GetSubfieldSize() const;
+
+        /**
+         * Serialize the TID-to-Link Mapping Control subfield
+         *
+         * \param start iterator pointing to where the subfield should be written to
+         */
+        void Serialize(Buffer::Iterator& start) const;
+        /**
+         * Deserialize the TID-to-Link Mapping Control subfield
+         *
+         * \param start iterator pointing to where the subfield should be read from
+         * \return the number of octets read
+         */
+        uint16_t Deserialize(Buffer::Iterator start);
+    };
+
+    WifiInformationElementId ElementId() const override;
+    WifiInformationElementId ElementIdExt() const override;
+
+    /**
+     * Set the Link Mapping field of the given TID such that the given TID is mapped
+     * to the links associated with the given link IDs.
+     *
+     * \param tid the given TID
+     * \param linkIds the IDs of the links which the given TID is mapped to
+     */
+    void SetLinkMappingOfTid(uint8_t tid, std::list<uint8_t> linkIds);
+    /**
+     * Get the Link Mapping field of the given TID.
+     *
+     * \param tid the given TID
+     * \return the IDs of the links which the given TID is mapped to
+     */
+    std::list<uint8_t> GetLinkMappingOfTid(uint8_t tid) const;
+
+    TidToLinkMapping::Control m_control;       ///< TID-to-link Mapping Control
+    std::map<uint8_t, uint16_t> m_linkMapping; ///< TID-indexed Link Mapping
+
+  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 /* TID_TO_LINK_MAPPING_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/fcfs-wifi-queue-scheduler.cc ns-3.38/src/wifi/model/fcfs-wifi-queue-scheduler.cc
--- ns-3.37/src/wifi/model/fcfs-wifi-queue-scheduler.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/fcfs-wifi-queue-scheduler.cc	2023-03-16 20:54:23.856869200 -0700
@@ -29,6 +29,38 @@
 
 NS_LOG_COMPONENT_DEFINE("FcfsWifiQueueScheduler");
 
+bool
+operator==(const FcfsPrio& lhs, const FcfsPrio& rhs)
+{
+    return lhs.priority == rhs.priority && lhs.type == rhs.type;
+}
+
+bool
+operator<(const FcfsPrio& lhs, const FcfsPrio& rhs)
+{
+    // Control queues have the highest priority
+    if (lhs.type == WIFI_CTL_QUEUE && rhs.type != WIFI_CTL_QUEUE)
+    {
+        return true;
+    }
+    if (lhs.type != WIFI_CTL_QUEUE && rhs.type == WIFI_CTL_QUEUE)
+    {
+        return false;
+    }
+    // Management queues have the second highest priority
+    if (lhs.type == WIFI_MGT_QUEUE && rhs.type != WIFI_MGT_QUEUE)
+    {
+        return true;
+    }
+    if (lhs.type != WIFI_MGT_QUEUE && rhs.type == WIFI_MGT_QUEUE)
+    {
+        return false;
+    }
+    // we get here if both priority values refer to container queues of the same type,
+    // hence we can compare the time values.
+    return lhs.priority < rhs.priority;
+}
+
 NS_OBJECT_ENSURE_REGISTERED(FcfsWifiQueueScheduler);
 
 TypeId
@@ -65,23 +97,31 @@
         return nullptr;
     }
 
-    // Management frames should be prioritized
-    if (m_dropPolicy == DROP_OLDEST || mpdu->GetHeader().IsMgt())
+    // Control and management frames should be prioritized
+    if (m_dropPolicy == DROP_OLDEST || mpdu->GetHeader().IsCtl() || mpdu->GetHeader().IsMgt())
     {
-        auto sortedQueuesIt = GetSortedQueues(ac).begin();
-
-        while (sortedQueuesIt != GetSortedQueues(ac).end() &&
-               std::get<WifiContainerQueueType>(sortedQueuesIt->second.get().first) ==
-                   WIFI_MGT_QUEUE)
-        {
-            sortedQueuesIt++;
-        }
-
-        if (sortedQueuesIt != GetSortedQueues(ac).end())
+        for (const auto& [priority, queueInfo] : GetSortedQueues(ac))
         {
-            return queue->PeekByQueueId(sortedQueuesIt->second.get().first);
+            if (std::get<WifiContainerQueueType>(queueInfo.get().first) == WIFI_MGT_QUEUE ||
+                std::get<WifiContainerQueueType>(queueInfo.get().first) == WIFI_CTL_QUEUE)
+            {
+                // do not drop control or management frames
+                continue;
+            }
+
+            // do not drop frames that are inflight or to be retransmitted
+            Ptr<WifiMpdu> item;
+            while ((item = queue->PeekByQueueId(queueInfo.get().first, item)))
+            {
+                if (!item->IsInFlight() && !item->GetHeader().IsRetry())
+                {
+                    NS_LOG_DEBUG("Dropping " << *item);
+                    return item;
+                }
+            }
         }
     }
+    NS_LOG_DEBUG("Dropping received MPDU: " << *mpdu);
     return mpdu;
 }
 
@@ -100,10 +140,7 @@
         return;
     }
 
-    auto priority =
-        (mpdu->GetHeader().IsMgt() ? Seconds(0) // Highest priority for management frames
-                                   : mpdu->GetExpiryTime());
-    SetPriority(ac, queueId, priority);
+    SetPriority(ac, queueId, {mpdu->GetExpiryTime(), std::get<WifiContainerQueueType>(queueId)});
 }
 
 void
@@ -120,15 +157,11 @@
 
     for (const auto& queueId : queueIds)
     {
-        if (std::get<WifiContainerQueueType>(queueId) == WIFI_MGT_QUEUE)
+        if (auto item = GetWifiMacQueue(ac)->PeekByQueueId(queueId))
         {
-            // the priority of management queues does not change
-            continue;
-        }
-
-        if (auto item = GetWifiMacQueue(ac)->PeekByQueueId(queueId); item != nullptr)
-        {
-            SetPriority(ac, queueId, item->GetExpiryTime());
+            SetPriority(ac,
+                        queueId,
+                        {item->GetExpiryTime(), std::get<WifiContainerQueueType>(queueId)});
         }
     }
 }
@@ -147,15 +180,11 @@
 
     for (const auto& queueId : queueIds)
     {
-        if (std::get<0>(queueId) == WIFI_MGT_QUEUE)
-        {
-            // the priority of management queues does not change
-            continue;
-        }
-
-        if (auto item = GetWifiMacQueue(ac)->PeekByQueueId(queueId); item != nullptr)
+        if (auto item = GetWifiMacQueue(ac)->PeekByQueueId(queueId))
         {
-            SetPriority(ac, queueId, item->GetExpiryTime());
+            SetPriority(ac,
+                        queueId,
+                        {item->GetExpiryTime(), std::get<WifiContainerQueueType>(queueId)});
         }
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/fcfs-wifi-queue-scheduler.h ns-3.38/src/wifi/model/fcfs-wifi-queue-scheduler.h
--- ns-3.37/src/wifi/model/fcfs-wifi-queue-scheduler.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/fcfs-wifi-queue-scheduler.h	2023-03-16 20:54:23.856869200 -0700
@@ -32,11 +32,36 @@
 /**
  * \ingroup wifi
  *
+ * Definition of priority for container queues.
+ */
+struct FcfsPrio
+{
+    Time priority;               ///< time priority
+    WifiContainerQueueType type; ///< type of container queue
+};
+
+/**
+ * \param lhs the left hand side priority
+ * \param rhs the right hand side priority
+ * \return whether the left hand side priority is equal to the right hand side priority
+ */
+bool operator==(const FcfsPrio& lhs, const FcfsPrio& rhs);
+/**
+ * \param lhs the left hand side priority
+ * \param rhs the right hand side priority
+ * \return whether the left hand side priority is less than the right hand side priority
+ */
+bool operator<(const FcfsPrio& lhs, const FcfsPrio& rhs);
+
+/**
+ * \ingroup wifi
+ *
  * FcfsWifiQueueScheduler is a wifi queue scheduler that serves data frames in a
- * first come first serve fashion. Management frames have higher priority than data
- * frames, hence the former will be served before the latter.
+ * first come first serve fashion. Control frames have the highest priority.
+ * Management frames have the second highest priority. Hence, data frames are
+ * served after control and management frames.
  */
-class FcfsWifiQueueScheduler : public WifiMacQueueSchedulerImpl<Time>
+class FcfsWifiQueueScheduler : public WifiMacQueueSchedulerImpl<FcfsPrio>
 {
   public:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/frame-exchange-manager.cc ns-3.38/src/wifi/model/frame-exchange-manager.cc
--- ns-3.37/src/wifi/model/frame-exchange-manager.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
@@ -95,13 +95,7 @@
     m_channelAccessManager = nullptr;
     m_protectionManager = nullptr;
     m_ackManager = nullptr;
-    if (m_phy)
-    {
-        m_phy->TraceDisconnectWithoutContext(
-            "PhyRxPayloadBegin",
-            MakeCallback(&FrameExchangeManager::RxStartIndication, this));
-    }
-    m_phy = nullptr;
+    ResetPhy();
     Object::DoDispose();
 }
 
@@ -191,11 +185,14 @@
         m_phy->TraceDisconnectWithoutContext(
             "PhyRxPayloadBegin",
             MakeCallback(&FrameExchangeManager::RxStartIndication, this));
-        m_phy->SetReceiveOkCallback(MakeNullCallback<void,
-                                                     Ptr<const WifiPsdu>,
-                                                     RxSignalInfo,
-                                                     WifiTxVector,
-                                                     std::vector<bool>>());
+        if (m_phy->GetState())
+        {
+            m_phy->SetReceiveOkCallback(MakeNullCallback<void,
+                                                         Ptr<const WifiPsdu>,
+                                                         RxSignalInfo,
+                                                         WifiTxVector,
+                                                         std::vector<bool>>());
+        }
         m_phy = nullptr;
     }
 }
@@ -335,7 +332,7 @@
     if (!mpdu->IsFragment() && !mpdu->GetHeader().IsRetry())
     {
         uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
-        mpdu->GetHeader().SetSequenceNumber(sequence);
+        mpdu->AssignSeqNo(sequence);
     }
 
     NS_LOG_DEBUG("MPDU payload size=" << mpdu->GetPacketSize()
@@ -412,6 +409,11 @@
     // Set QoS Ack policy if this is a QoS data frame
     WifiAckManager::SetQosAckPolicy(m_mpdu, m_txParams.m_acknowledgment.get());
 
+    if (m_mpdu->IsQueued())
+    {
+        m_mpdu->SetInFlight(m_linkId);
+    }
+
     switch (m_txParams.m_protection->method)
     {
     case WifiProtection::RTS_CTS:
@@ -426,6 +428,12 @@
     default:
         NS_ABORT_MSG("Unknown protection type");
     }
+
+    if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
+    {
+        // we are done with frames that do not require acknowledgment
+        m_mpdu = nullptr;
+    }
 }
 
 void
@@ -484,12 +492,6 @@
 
     // transmit the MPDU
     ForwardMpduDown(m_mpdu, m_txParams.m_txVector);
-
-    if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
-    {
-        // we are done with frames that do not require acknowledgment
-        m_mpdu = nullptr;
-    }
 }
 
 void
@@ -877,6 +879,11 @@
     else
     {
         NS_LOG_DEBUG("Missed Ack, retransmit MPDU");
+        if (mpdu->IsQueued()) // the MPDU may have been removed due to lifetime expiration
+        {
+            mpdu = m_mac->GetTxopQueue(mpdu->GetQueueAc())->GetOriginal(mpdu);
+            mpdu->ResetInFlight(m_linkId);
+        }
         mpdu->GetHeader().SetRetry();
         RetransmitMpduAfterMissedAck(mpdu);
         m_dcf->UpdateFailedCw(m_linkId);
@@ -906,6 +913,14 @@
 {
     NS_LOG_FUNCTION(this << *psdu);
 
+    for (const auto& mpdu : *PeekPointer(psdu))
+    {
+        if (mpdu->IsQueued())
+        {
+            mpdu->ResetInFlight(m_linkId);
+        }
+    }
+
     GetWifiRemoteStationManager()->ReportRtsFailed(psdu->GetHeader(0));
 
     if (!GetWifiRemoteStationManager()->NeedRetransmission(*psdu->begin()))
@@ -929,25 +944,27 @@
     // been transmitted, both in case the MPDUs have been discarded and in case the
     // MPDUs have to be transmitted (because a new sequence number is assigned to
     // MPDUs that have never been transmitted and are selected for transmission)
-    for (const auto& mpdu : *PeekPointer(psdu))
-    {
-        ReleaseSequenceNumber(mpdu);
-    }
+    ReleaseSequenceNumbers(psdu);
+
     TransmissionFailed();
 }
 
 void
-FrameExchangeManager::ReleaseSequenceNumber(Ptr<WifiMpdu> mpdu) const
+FrameExchangeManager::ReleaseSequenceNumbers(Ptr<const WifiPsdu> psdu) const
 {
-    NS_LOG_FUNCTION(this << *mpdu);
+    NS_LOG_FUNCTION(this << *psdu);
+
+    NS_ASSERT_MSG(psdu->GetNMpdus() == 1, "A-MPDUs should be handled by the HT FEM override");
+    auto mpdu = *psdu->begin();
 
     // the MPDU should be still in the DCF queue, unless it expired.
-    // If the MPDU has never been transmitted, it will be assigned a sequence
-    // number again the next time we try to transmit it. Therefore, we need to
+    // If the MPDU has never been transmitted and is not in-flight, it will be assigned
+    // a sequence number again the next time we try to transmit it. Therefore, we need to
     // make its sequence number available again
-    if (!mpdu->GetHeader().IsRetry())
+    if (!mpdu->GetHeader().IsRetry() && !mpdu->IsInFlight())
     {
-        m_txMiddle->SetSequenceNumberFor(&mpdu->GetHeader());
+        mpdu->UnassignSeqNo();
+        m_txMiddle->SetSequenceNumberFor(&mpdu->GetOriginal()->GetHeader());
     }
 }
 
@@ -1038,35 +1055,46 @@
         PreProcessFrame(psdu, txVector);
     }
 
-    // ignore unicast frames that are not addressed to us
     Mac48Address addr1 = psdu->GetAddr1();
-    if (!addr1.IsGroup() && addr1 != m_self)
+
+    if (addr1.IsGroup() || addr1 == m_self)
     {
-        if (m_promisc && psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsData())
+        // receive broadcast frames or frames addressed to us only
+        if (psdu->GetNMpdus() == 1)
         {
-            m_rxMiddle->Receive(*psdu->begin(), m_linkId);
+            // if perMpduStatus is not empty (i.e., this MPDU is not included in an A-MPDU)
+            // then it must contain a single value which must be true (i.e., the MPDU
+            // has been correctly received)
+            NS_ASSERT(perMpduStatus.empty() || (perMpduStatus.size() == 1 && perMpduStatus[0]));
+            // Ack and CTS do not carry Addr2
+            if (!psdu->GetHeader(0).IsAck() && !psdu->GetHeader(0).IsCts())
+            {
+                GetWifiRemoteStationManager()->ReportRxOk(psdu->GetHeader(0).GetAddr2(),
+                                                          rxSignalInfo,
+                                                          txVector);
+            }
+            ReceiveMpdu(*(psdu->begin()), rxSignalInfo, txVector, perMpduStatus.empty());
+        }
+        else
+        {
+            EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
         }
-        return;
     }
-
-    if (psdu->GetNMpdus() == 1)
+    else if (m_promisc)
     {
-        // if perMpduStatus is not empty (i.e., this MPDU is not included in an A-MPDU)
-        // then it must contain a single value which must be true (i.e., the MPDU
-        // has been correctly received)
-        NS_ASSERT(perMpduStatus.empty() || (perMpduStatus.size() == 1 && perMpduStatus[0]));
-        // Ack and CTS do not carry Addr2
-        if (!psdu->GetHeader(0).IsAck() && !psdu->GetHeader(0).IsCts())
+        for (const auto& mpdu : *PeekPointer(psdu))
         {
-            GetWifiRemoteStationManager()->ReportRxOk(psdu->GetHeader(0).GetAddr2(),
-                                                      rxSignalInfo,
-                                                      txVector);
+            if (!mpdu->GetHeader().IsCtl())
+            {
+                m_rxMiddle->Receive(mpdu, m_linkId);
+            }
         }
-        ReceiveMpdu(*(psdu->begin()), rxSignalInfo, txVector, perMpduStatus.empty());
     }
-    else
+
+    if (!perMpduStatus.empty())
     {
-        EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
+        // for A-MPDUs, we get here only once
+        PostProcessFrame(psdu, txVector);
     }
 }
 
@@ -1074,6 +1102,12 @@
 FrameExchangeManager::PreProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
 {
     NS_LOG_FUNCTION(this << psdu << txVector);
+}
+
+void
+FrameExchangeManager::PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << psdu << txVector);
 
     UpdateNav(psdu, txVector);
 }
@@ -1083,13 +1117,8 @@
 {
     NS_LOG_FUNCTION(this << psdu << txVector);
 
-    if (psdu->GetHeader(0).GetRawDuration() > 32767)
+    if (!psdu->HasNav())
     {
-        // When the contents of a received Duration/ID field, treated as an unsigned
-        // integer, are greater than 32 768, the contents are interpreted as appropriate
-        // for the frame type and subtype or ignored if the receiving MAC entity does
-        // not have a defined interpretation for that type and subtype (IEEE 802.11-2016
-        // sec. 10.27.3)
         return;
     }
 
@@ -1122,10 +1151,12 @@
         // (IEEE 802.11-2016 sec. 10.3.2.4)
         if (psdu->GetHeader(0).IsRts())
         {
+            WifiTxVector ctsTxVector =
+                GetWifiRemoteStationManager()->GetCtsTxVector(psdu->GetAddr2(), txVector.GetMode());
             Time navResetDelay =
                 2 * m_phy->GetSifs() +
-                WifiPhy::CalculateTxDuration(GetCtsSize(), txVector, m_phy->GetPhyBand()) +
-                m_phy->CalculatePhyPreambleAndHeaderDuration(txVector) + 2 * m_phy->GetSlot();
+                WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
+                m_phy->CalculatePhyPreambleAndHeaderDuration(ctsTxVector) + 2 * m_phy->GetSlot();
             m_navResetEvent =
                 Simulator::Schedule(navResetDelay, &FrameExchangeManager::NavResetTimeout, this);
         }
@@ -1143,6 +1174,12 @@
     m_channelAccessManager->NotifyNavResetNow(Seconds(0));
 }
 
+bool
+FrameExchangeManager::VirtualCsMediumIdle() const
+{
+    return m_navEnd <= Simulator::Now();
+}
+
 void
 FrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
                                   RxSignalInfo rxSignalInfo,
@@ -1166,7 +1203,7 @@
             // - If the NAV indicates idle, the STA shall respond with a CTS frame after a SIFS
             // - Otherwise, the STA shall not respond with a CTS frame
             // (IEEE 802.11-2016 sec. 10.3.2.7)
-            if (m_navEnd <= Simulator::Now())
+            if (VirtualCsMediumIdle())
             {
                 NS_LOG_DEBUG("Received RTS from=" << hdr.GetAddr2() << ", schedule CTS");
                 Simulator::Schedule(m_phy->GetSifs(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/frame-exchange-manager.h ns-3.38/src/wifi/model/frame-exchange-manager.h
--- ns-3.37/src/wifi/model/frame-exchange-manager.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
@@ -101,7 +101,7 @@
      *
      * \param linkId the ID of the link this Frame Exchange Manager is associated with
      */
-    void SetLinkId(uint8_t linkId);
+    virtual void SetLinkId(uint8_t linkId);
     /**
      * Set the MAC layer to use.
      *
@@ -236,6 +236,11 @@
     virtual void CalculateAcknowledgmentTime(WifiAcknowledgment* acknowledgment) const;
 
     /**
+     * \return true if the virtual CS indication is that the medium is idle
+     */
+    virtual bool VirtualCsMediumIdle() const;
+
+    /**
      * Notify that an internal collision has occurred for the given Txop
      *
      * \param txop the Txop for which an internal collision has occurred
@@ -364,12 +369,12 @@
     virtual void RetransmitMpduAfterMissedAck(Ptr<WifiMpdu> mpdu) const;
 
     /**
-     * Make the sequence number of the given MPDU available again if the MPDU has
-     * never been transmitted.
+     * Make the sequence numbers of MPDUs included in the given PSDU available again
+     * if the MPDUs have never been transmitted.
      *
-     * \param mpdu the given MPDU
+     * \param psdu the given PSDU
      */
-    virtual void ReleaseSequenceNumber(Ptr<WifiMpdu> mpdu) const;
+    virtual void ReleaseSequenceNumbers(Ptr<const WifiPsdu> psdu) const;
 
     /**
      * Pass the given MPDU, discarded because of the max retry limit was reached,
@@ -382,7 +387,7 @@
     /**
      * Perform actions that are possibly needed when receiving any frame,
      * independently of whether the frame is addressed to this station
-     * (e.g., setting the NAV or the TXOP holder).
+     * (e.g., storing buffer status reports).
      *
      * \param psdu the received PSDU
      * \param txVector TX vector of the received PSDU
@@ -390,6 +395,16 @@
     virtual void PreProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector);
 
     /**
+     * Perform actions that are possibly needed after receiving any frame,
+     * independently of whether the frame is addressed to this station
+     * (e.g., setting the NAV or the TXOP holder).
+     *
+     * \param psdu the received PSDU
+     * \param txVector TX vector of the received PSDU
+     */
+    virtual void PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector);
+
+    /**
      * Get the updated TX duration of the frame associated with the given TX
      * parameters if the size of the PSDU addressed to the given receiver
      * becomes <i>ppduPayloadSize</i>.
@@ -581,6 +596,11 @@
      */
     void DoCtsTimeout(Ptr<WifiPsdu> psdu);
 
+    /**
+     * Reset this frame exchange manager.
+     */
+    virtual void Reset();
+
   private:
     /**
      * \param txVector the TXVECTOR decoded from PHY header.
@@ -599,11 +619,6 @@
      */
     void SendMpdu();
 
-    /**
-     * Reset this frame exchange manager.
-     */
-    virtual void Reset();
-
     Ptr<WifiMpdu> m_mpdu;           //!< the MPDU being transmitted
     WifiTxParameters m_txParams;    //!< the TX parameters for the current frame
     Ptr<Packet> m_fragmentedPacket; //!< the MSDU being fragmented
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-capabilities.cc ns-3.38/src/wifi/model/he/he-capabilities.cc
--- ns-3.37/src/wifi/model/he/he-capabilities.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-capabilities.cc	2023-03-16 20:54:23.856869200 -0700
@@ -152,7 +152,7 @@
 {
     // write the corresponding value for each bit
     start.WriteHtolsbU32(GetHeMacCapabilitiesInfo1());
-    start.WriteU16(GetHeMacCapabilitiesInfo2());
+    start.WriteHtolsbU16(GetHeMacCapabilitiesInfo2());
     start.WriteHtolsbU64(GetHePhyCapabilitiesInfo1());
     start.WriteHtolsbU16(GetHePhyCapabilitiesInfo2());
     start.WriteU8(GetHePhyCapabilitiesInfo3());
@@ -610,14 +610,14 @@
 }
 
 std::ostream&
-operator<<(std::ostream& os, const HeCapabilities& HeCapabilities)
+operator<<(std::ostream& os, const HeCapabilities& heCapabilities)
 {
-    os << HeCapabilities.GetHeMacCapabilitiesInfo1() << "|"
-       << +HeCapabilities.GetHeMacCapabilitiesInfo2() << "|"
-       << HeCapabilities.GetHePhyCapabilitiesInfo1() << "|"
-       << HeCapabilities.GetHePhyCapabilitiesInfo2() << "|"
-       << +HeCapabilities.GetHePhyCapabilitiesInfo3() << "|"
-       << HeCapabilities.GetSupportedMcsAndNss();
+    os << heCapabilities.GetHeMacCapabilitiesInfo1() << "|"
+       << +heCapabilities.GetHeMacCapabilitiesInfo2() << "|"
+       << heCapabilities.GetHePhyCapabilitiesInfo1() << "|"
+       << heCapabilities.GetHePhyCapabilitiesInfo2() << "|"
+       << +heCapabilities.GetHePhyCapabilitiesInfo3() << "|"
+       << heCapabilities.GetSupportedMcsAndNss();
     return os;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-capabilities.h ns-3.38/src/wifi/model/he/he-capabilities.h
--- ns-3.37/src/wifi/model/he/he-capabilities.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-capabilities.h	2023-03-16 20:54:23.856869200 -0700
@@ -336,10 +336,10 @@
 /**
  * output stream output operator
  * \param os the output stream
- * \param HeCapabilities the HE capabilities
+ * \param heCapabilities the HE capabilities
  * \returns the output stream
  */
-std::ostream& operator<<(std::ostream& os, const HeCapabilities& HeCapabilities);
+std::ostream& operator<<(std::ostream& os, const HeCapabilities& heCapabilities);
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-frame-exchange-manager.cc ns-3.38/src/wifi/model/he/he-frame-exchange-manager.cc
--- ns-3.37/src/wifi/model/he/he-frame-exchange-manager.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
@@ -25,10 +25,13 @@
 
 #include "ns3/abort.h"
 #include "ns3/ap-wifi-mac.h"
+#include "ns3/erp-ofdm-phy.h"
 #include "ns3/log.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-mac-trailer.h"
 
 #include <algorithm>
 #include <functional>
@@ -43,6 +46,14 @@
 
 NS_OBJECT_ENSURE_REGISTERED(HeFrameExchangeManager);
 
+bool
+IsTrigger(const WifiPsduMap& 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()
 {
@@ -54,7 +65,8 @@
 }
 
 HeFrameExchangeManager::HeFrameExchangeManager()
-    : m_triggerFrameInAmpdu(false)
+    : m_intraBssNavEnd(0),
+      m_triggerFrameInAmpdu(false)
 {
     NS_LOG_FUNCTION(this);
 }
@@ -64,6 +76,18 @@
     NS_LOG_FUNCTION_NOARGS();
 }
 
+void
+HeFrameExchangeManager::Reset()
+{
+    NS_LOG_FUNCTION(this);
+    if (m_intraBssNavResetEvent.IsRunning())
+    {
+        m_intraBssNavResetEvent.Cancel();
+    }
+    m_intraBssNavEnd = Simulator::Now();
+    VhtFrameExchangeManager::Reset();
+}
+
 uint16_t
 HeFrameExchangeManager::GetSupportedBaBufferSize() const
 {
@@ -84,6 +108,18 @@
 }
 
 void
+HeFrameExchangeManager::SetWifiPhy(const Ptr<WifiPhy> phy)
+{
+    NS_LOG_FUNCTION(this << phy);
+    VhtFrameExchangeManager::SetWifiPhy(phy);
+    // Cancel intra-BSS NAV reset timer when receiving a frame from the PHY
+    phy->TraceConnectWithoutContext("PhyRxPayloadBegin",
+                                    Callback<void, WifiTxVector, Time>([this](WifiTxVector, Time) {
+                                        m_intraBssNavResetEvent.Cancel();
+                                    }));
+}
+
+void
 HeFrameExchangeManager::DoDispose()
 {
     NS_LOG_FUNCTION(this);
@@ -122,14 +158,17 @@
      *   or the next frame in the AC queue is a non-broadcast QoS data frame addressed to
      *   a receiver with which a BA agreement has been already established
      */
-    if (m_muScheduler && !edca->GetBaManager()->GetBar(false) &&
+    if (m_muScheduler && !GetBar(edca->GetAccessCategory()) &&
         (!(mpdu = edca->PeekNextMpdu(m_linkId)) ||
          (mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().GetAddr1().IsGroup() &&
-          edca->GetBaAgreementEstablished(mpdu->GetHeader().GetAddr1(),
-                                          mpdu->GetHeader().GetQosTid()))))
+          m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(),
+                                                       mpdu->GetHeader().GetQosTid()))))
     {
-        txFormat =
-            m_muScheduler->NotifyAccessGranted(edca, availableTime, initialFrame, m_allowedWidth);
+        txFormat = m_muScheduler->NotifyAccessGranted(edca,
+                                                      availableTime,
+                                                      initialFrame,
+                                                      m_allowedWidth,
+                                                      m_linkId);
     }
 
     if (txFormat == MultiUserScheduler::SU_TX)
@@ -139,27 +178,28 @@
 
     if (txFormat == MultiUserScheduler::DL_MU_TX)
     {
-        if (m_muScheduler->GetDlMuInfo().psduMap.empty())
+        if (m_muScheduler->GetDlMuInfo(m_linkId).psduMap.empty())
         {
             NS_LOG_DEBUG(
                 "The Multi-user Scheduler returned DL_MU_TX with empty psduMap, do not transmit");
             return false;
         }
 
-        SendPsduMapWithProtection(m_muScheduler->GetDlMuInfo().psduMap,
-                                  m_muScheduler->GetDlMuInfo().txParams);
+        SendPsduMapWithProtection(m_muScheduler->GetDlMuInfo(m_linkId).psduMap,
+                                  m_muScheduler->GetDlMuInfo(m_linkId).txParams);
         return true;
     }
 
     if (txFormat == MultiUserScheduler::UL_MU_TX)
     {
         auto packet = Create<Packet>();
-        packet->AddHeader(m_muScheduler->GetUlMuInfo().trigger);
-        auto trigger = Create<WifiMpdu>(packet, m_muScheduler->GetUlMuInfo().macHdr);
+        packet->AddHeader(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
+        auto trigger = Create<WifiMpdu>(packet, m_muScheduler->GetUlMuInfo(m_linkId).macHdr);
         SendPsduMapWithProtection(
-            WifiPsduMap{{SU_STA_ID,
-                         GetWifiPsdu(trigger, m_muScheduler->GetUlMuInfo().txParams.m_txVector)}},
-            m_muScheduler->GetUlMuInfo().txParams);
+            WifiPsduMap{
+                {SU_STA_ID,
+                 GetWifiPsdu(trigger, m_muScheduler->GetUlMuInfo(m_linkId).txParams.m_txVector)}},
+            m_muScheduler->GetUlMuInfo(m_linkId).txParams);
         return true;
     }
 
@@ -167,29 +207,20 @@
 }
 
 bool
-HeFrameExchangeManager::SendMpduFromBaManager(Ptr<QosTxop> edca,
+HeFrameExchangeManager::SendMpduFromBaManager(Ptr<WifiMpdu> mpdu,
                                               Time availableTime,
                                               bool initialFrame)
 {
-    NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
-
-    // First, check if there is a BAR to be transmitted
-    Ptr<const WifiMpdu> peekedItem = edca->GetBaManager()->GetBar(false);
-
-    if (!peekedItem)
-    {
-        NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
-        return false;
-    }
+    NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
 
-    if (peekedItem->GetHeader().IsBlockAckReq())
+    // First, check if there is a Trigger Frame to be transmitted
+    if (!mpdu->GetHeader().IsTrigger())
     {
         // BlockAckReq are handled by the HT FEM
-        return HtFrameExchangeManager::SendMpduFromBaManager(edca, availableTime, initialFrame);
+        return HtFrameExchangeManager::SendMpduFromBaManager(mpdu, availableTime, initialFrame);
     }
 
-    NS_ASSERT(peekedItem->GetHeader().IsTrigger());
-    m_triggerFrame = Copy(edca->GetBaManager()->GetBar());
+    m_triggerFrame = mpdu;
 
     SendPsduMap();
     return true;
@@ -203,22 +234,6 @@
     m_psduMap = std::move(psduMap);
     m_txParams = std::move(txParams);
 
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    // If protection is required, the MPDUs must be stored in some queue because
-    // they are not put back in a queue if the MU-RTS/CTS exchange fails
-    if (m_txParams.m_protection->method != WifiProtection::NONE)
-    {
-        for (const auto& psdu : psduMap)
-        {
-            for (const auto& mpdu : *PeekPointer(psdu.second))
-            {
-                NS_ASSERT(mpdu->GetHeader().IsCtl() || !mpdu->GetHeader().HasData() ||
-                          mpdu->IsQueued());
-            }
-        }
-    }
-#endif
-
     // Make sure that the acknowledgment time has been computed, so that SendMuRts()
     // can reuse this value.
     NS_ASSERT(m_txParams.m_acknowledgment);
@@ -228,17 +243,54 @@
         CalculateAcknowledgmentTime(m_txParams.m_acknowledgment.get());
     }
 
+    // in case we are sending a Trigger Frame, update the acknowledgment time so that
+    // the Duration/ID of the MU-RTS is correctly computed
+    if (!m_txParams.m_txVector.IsUlMu() && IsTrigger(m_psduMap))
+    {
+        NS_ASSERT(m_muScheduler);
+        const auto& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
+        NS_ASSERT_MSG(!trigger.IsBasic() || m_txParams.m_acknowledgment->method ==
+                                                WifiAcknowledgment::UL_MU_MULTI_STA_BA,
+                      "Acknowledgment (" << m_txParams.m_acknowledgment.get()
+                                         << ") incompatible with Basic Trigger Frame");
+        NS_ASSERT_MSG(!trigger.IsBsrp() ||
+                          m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE,
+                      "Acknowledgment (" << m_txParams.m_acknowledgment.get()
+                                         << ") incompatible with BSRP Trigger Frame");
+        // Add a SIFS and the TB PPDU duration to the acknowledgment time of the Trigger Frame
+        auto txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
+        m_txParams.m_acknowledgment->acknowledgmentTime +=
+            m_phy->GetSifs() + HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
+                                                                          txVector,
+                                                                          m_phy->GetPhyBand());
+    }
+
     // Set QoS Ack policy
     for (auto& psdu : m_psduMap)
     {
         WifiAckManager::SetQosAckPolicy(psdu.second, m_txParams.m_acknowledgment.get());
     }
 
+    for (const auto& psdu : m_psduMap)
+    {
+        for (const auto& mpdu : *PeekPointer(psdu.second))
+        {
+            if (mpdu->IsQueued())
+            {
+                mpdu->SetInFlight(m_linkId);
+            }
+        }
+    }
+
     if (m_txParams.m_protection->method == WifiProtection::RTS_CTS)
     {
         NS_ABORT_MSG_IF(m_psduMap.size() > 1, "Cannot use RTS/CTS with MU PPDUs");
         SendRts(m_txParams);
     }
+    else if (m_txParams.m_protection->method == WifiProtection::MU_RTS_CTS)
+    {
+        SendMuRts(m_txParams);
+    }
     else if (m_txParams.m_protection->method == WifiProtection::NONE)
     {
         SendPsduMap();
@@ -249,6 +301,145 @@
     }
 }
 
+Time
+HeFrameExchangeManager::GetMuRtsDurationId(uint32_t muRtsSize,
+                                           const WifiTxVector& muRtsTxVector,
+                                           Time txDuration,
+                                           Time response) const
+{
+    NS_LOG_FUNCTION(this << muRtsSize << muRtsTxVector << txDuration << response);
+
+    if (m_edca->GetTxopLimit(m_linkId).IsZero())
+    {
+        WifiTxVector txVector;
+        txVector.SetMode(GetCtsModeAfterMuRts());
+        return VhtFrameExchangeManager::GetRtsDurationId(txVector, txDuration, response);
+    }
+
+    // under multiple protection settings, if the TXOP limit is not null, Duration/ID
+    // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
+    // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
+    // of 802.11-2016)
+    return std::max(m_edca->GetRemainingTxop(m_linkId) -
+                        m_phy->CalculateTxDuration(muRtsSize, muRtsTxVector, m_phy->GetPhyBand()),
+                    Seconds(0));
+}
+
+void
+HeFrameExchangeManager::SendMuRts(const WifiTxParameters& txParams)
+{
+    NS_LOG_FUNCTION(this << &txParams);
+    WifiMacHeader hdr;
+    hdr.SetType(WIFI_MAC_CTL_TRIGGER);
+    hdr.SetAddr1(Mac48Address::GetBroadcast());
+    hdr.SetAddr2(m_self);
+    hdr.SetDsNotTo();
+    hdr.SetDsNotFrom();
+    hdr.SetNoRetry();
+    hdr.SetNoMoreFragments();
+
+    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());
+    protection->muRts.SetCsRequired(true);
+    Ptr<Packet> payload = Create<Packet>();
+    payload->AddHeader(protection->muRts);
+
+    auto mpdu = Create<WifiMpdu>(payload, hdr);
+
+    NS_ASSERT(txParams.m_txDuration != Time::Min());
+    mpdu->GetHeader().SetDuration(
+        GetMuRtsDurationId(mpdu->GetSize(),
+                           protection->muRtsTxVector,
+                           txParams.m_txDuration,
+                           txParams.m_acknowledgment->acknowledgmentTime));
+
+    // Get the TXVECTOR used by one station to send the CTS response. This is used
+    // to compute the preamble duration, so it does not matter which station we choose
+    WifiTxVector ctsTxVector =
+        GetCtsTxVectorAfterMuRts(protection->muRts, protection->muRts.begin()->GetAid12());
+
+    // After transmitting an MU-RTS frame, the STA shall wait for a CTSTimeout interval of
+    // aSIFSTime + aSlotTime + aRxPHYStartDelay (Sec. 27.2.5.2 of 802.11ax D3.0).
+    // aRxPHYStartDelay equals the time to transmit the PHY header.
+    Time timeout = m_phy->CalculateTxDuration(mpdu->GetSize(),
+                                              protection->muRtsTxVector,
+                                              m_phy->GetPhyBand()) +
+                   m_phy->GetSifs() + m_phy->GetSlot() +
+                   m_phy->CalculatePhyPreambleAndHeaderDuration(ctsTxVector);
+
+    NS_ASSERT(!m_txTimer.IsRunning());
+    m_txTimer.Set(WifiTxTimer::WAIT_CTS_AFTER_MU_RTS,
+                  timeout,
+                  &HeFrameExchangeManager::CtsAfterMuRtsTimeout,
+                  this,
+                  mpdu,
+                  protection->muRtsTxVector);
+    m_channelAccessManager->NotifyCtsTimeoutStartNow(timeout);
+
+    ForwardMpduDown(mpdu, protection->muRtsTxVector);
+}
+
+void
+HeFrameExchangeManager::CtsAfterMuRtsTimeout(Ptr<WifiMpdu> muRts, const WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << *muRts << txVector);
+
+    NS_ASSERT(!m_psduMap.empty());
+
+    for (const auto& psdu : m_psduMap)
+    {
+        for (const auto& mpdu : *PeekPointer(psdu.second))
+        {
+            if (mpdu->IsQueued())
+            {
+                mpdu->ResetInFlight(m_linkId);
+            }
+        }
+    }
+
+    // NOTE Implementation of QSRC[AC] and QLRC[AC] should be improved...
+    const auto& hdr = m_psduMap.cbegin()->second->GetHeader(0);
+    if (!hdr.GetAddr1().IsGroup())
+    {
+        GetWifiRemoteStationManager()->ReportRtsFailed(hdr);
+    }
+
+    if (!hdr.GetAddr1().IsGroup() &&
+        !GetWifiRemoteStationManager()->NeedRetransmission(*m_psduMap.cbegin()->second->begin()))
+    {
+        NS_LOG_DEBUG("Missed CTS, discard MPDUs");
+        GetWifiRemoteStationManager()->ReportFinalRtsFailed(hdr);
+        for (const auto& psdu : m_psduMap)
+        {
+            // Dequeue the MPDUs if they are stored in a queue
+            DequeuePsdu(psdu.second);
+            for (const auto& mpdu : *PeekPointer(psdu.second))
+            {
+                NotifyPacketDiscarded(mpdu);
+            }
+        }
+        m_edca->ResetCw(m_linkId);
+    }
+    else
+    {
+        NS_LOG_DEBUG("Missed CTS, retransmit MPDUs");
+        m_edca->UpdateFailedCw(m_linkId);
+    }
+    // Make the sequence numbers of the MPDUs available again if the MPDUs have never
+    // been transmitted, both in case the MPDUs have been discarded and in case the
+    // MPDUs have to be transmitted (because a new sequence number is assigned to
+    // MPDUs that have never been transmitted and are selected for transmission)
+    for (const auto& [staId, psdu] : m_psduMap)
+    {
+        ReleaseSequenceNumbers(psdu);
+    }
+    m_psduMap.clear();
+    TransmissionFailed();
+}
+
 Ptr<WifiPsdu>
 HeFrameExchangeManager::GetPsduTo(Mac48Address to, const WifiPsduMap& psduMap)
 {
@@ -318,7 +509,7 @@
                 uint8_t tid = *tids.begin();
 
                 NS_ASSERT(m_edca);
-                m_edca->ScheduleBar(
+                m_edca->GetBaManager()->ScheduleBar(
                     m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid));
             }
         }
@@ -379,7 +570,7 @@
             m_trigVector.SetLength(acknowledgment->ulLength);
 
             NS_ASSERT(m_edca);
-            m_edca->ScheduleBar(PrepareMuBar(m_trigVector, recipients));
+            m_edca->GetBaManager()->ScheduleMuBar(PrepareMuBar(m_trigVector, recipients));
         }
         else
         {
@@ -417,7 +608,8 @@
             ForwardPsduDown(triggerPsdu, acknowledgment->muBarTxVector);
 
             // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
-            auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
+            auto hePhy =
+                StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
             hePhy->SetTrigVector(m_trigVector, timeout);
 
             return;
@@ -475,8 +667,8 @@
     else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
     {
         // the PSDU map being sent must contain a (Basic) Trigger Frame
-        NS_ASSERT(m_psduMap.size() == 1 && m_psduMap.begin()->first == SU_STA_ID &&
-                  (mpdu = *m_psduMap.begin()->second->begin())->GetHeader().IsTrigger());
+        NS_ASSERT(IsTrigger(m_psduMap));
+        mpdu = *m_psduMap.begin()->second->begin();
 
         WifiUlMuMultiStaBa* acknowledgment =
             static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
@@ -494,28 +686,17 @@
         acknowledgment->stationsReceivingMultiStaBa.clear();
         acknowledgment->baType.m_bitmapLen.clear();
 
-        // Add a SIFS and the TB PPDU duration to the acknowledgment time of the
-        // Trigger Frame, so that its Duration/ID is correctly computed
-        NS_ASSERT(m_muScheduler);
-        Time tbPpduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(
-            m_muScheduler->GetUlMuInfo().trigger.GetUlLength(),
-            acknowledgment->tbPpduTxVector,
-            m_phy->GetPhyBand());
-        acknowledgment->acknowledgmentTime += m_mac->GetWifiPhy()->GetSifs() + tbPpduDuration;
-
         timerType = WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF;
         responseTxVector = &acknowledgment->tbPpduTxVector;
-        m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo().trigger);
+        m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
     }
     /*
      * BSRP Trigger Frame
      */
     else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE &&
-             !m_txParams.m_txVector.IsUlMu() && m_psduMap.size() == 1 &&
-             m_psduMap.begin()->first == SU_STA_ID &&
-             (*m_psduMap.begin()->second->begin())->GetHeader().IsTrigger())
+             !m_txParams.m_txVector.IsUlMu() && IsTrigger(m_psduMap))
     {
-        CtrlTriggerHeader& trigger = m_muScheduler->GetUlMuInfo().trigger;
+        CtrlTriggerHeader& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
         NS_ASSERT(trigger.IsBsrp());
         NS_ASSERT(m_apMac);
 
@@ -524,24 +705,15 @@
 
         for (const auto& userInfo : trigger)
         {
-            auto staIt = m_apMac->GetStaList().find(userInfo.GetAid12());
-            NS_ASSERT(staIt != m_apMac->GetStaList().end());
+            auto staIt = m_apMac->GetStaList(m_linkId).find(userInfo.GetAid12());
+            NS_ASSERT(staIt != m_apMac->GetStaList(m_linkId).end());
             m_staExpectTbPpduFrom.insert(staIt->second);
         }
 
-        // Add a SIFS and the TB PPDU duration to the acknowledgment time of the
-        // Trigger Frame, so that its Duration/ID is correctly computed
-        WifiNoAck* acknowledgment = static_cast<WifiNoAck*>(m_txParams.m_acknowledgment.get());
-        txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
-        acknowledgment->acknowledgmentTime +=
-            m_mac->GetWifiPhy()->GetSifs() +
-            HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
-                                                       txVector,
-                                                       m_phy->GetPhyBand());
-
         timerType = WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF;
+        txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
         responseTxVector = &txVector;
-        m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo().trigger);
+        m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
     }
     /*
      * TB PPDU solicited by a Basic Trigger Frame
@@ -673,7 +845,7 @@
         timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
     {
         // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
-        auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
+        auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
         hePhy->SetTrigVector(m_trigVector, m_txTimer.GetDelayLeft());
     }
     else if (timerType == WifiTxTimer::NOT_RUNNING && m_txParams.m_txVector.IsUlMu())
@@ -688,9 +860,9 @@
 {
     NS_LOG_FUNCTION(this << psduMap << txVector);
 
-    if (txVector.IsDlMu())
+    if (ns3::IsDlMu(txVector.GetPreambleType()))
     {
-        auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
+        auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(txVector.GetModulationClass()));
         auto sigBMode = hePhy->GetSigBMode(txVector);
         txVector.SetSigBMode(sigBMode);
     }
@@ -751,7 +923,7 @@
     else
     {
         NS_ASSERT(m_apMac);
-        rxAddress = m_apMac->GetStaList().at(recipients.begin()->first);
+        rxAddress = m_apMac->GetStaList(m_linkId).at(recipients.begin()->first);
     }
 
     WifiMacHeader hdr;
@@ -767,6 +939,38 @@
 }
 
 void
+HeFrameExchangeManager::CalculateProtectionTime(WifiProtection* protection) const
+{
+    NS_LOG_FUNCTION(this << protection);
+    NS_ASSERT(protection != nullptr);
+
+    if (protection->method == WifiProtection::MU_RTS_CTS)
+    {
+        WifiMuRtsCtsProtection* muRtsCtsProtection =
+            static_cast<WifiMuRtsCtsProtection*>(protection);
+
+        // Get the TXVECTOR used by one station to send the CTS response. This is used
+        // to compute the TX duration, so it does not matter which station we choose
+        WifiTxVector ctsTxVector =
+            GetCtsTxVectorAfterMuRts(muRtsCtsProtection->muRts,
+                                     muRtsCtsProtection->muRts.begin()->GetAid12());
+
+        uint32_t muRtsSize = WifiMacHeader(WIFI_MAC_CTL_TRIGGER).GetSize() +
+                             muRtsCtsProtection->muRts.GetSerializedSize() + WIFI_MAC_FCS_LENGTH;
+        muRtsCtsProtection->protectionTime =
+            m_phy->CalculateTxDuration(muRtsSize,
+                                       muRtsCtsProtection->muRtsTxVector,
+                                       m_phy->GetPhyBand()) +
+            m_phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
+            2 * m_phy->GetSifs();
+    }
+    else
+    {
+        VhtFrameExchangeManager::CalculateProtectionTime(protection);
+    }
+}
+
+void
 HeFrameExchangeManager::CalculateAcknowledgmentTime(WifiAcknowledgment* acknowledgment) const
 {
     NS_LOG_FUNCTION(this << acknowledgment);
@@ -929,6 +1133,50 @@
     }
 }
 
+WifiMode
+HeFrameExchangeManager::GetCtsModeAfterMuRts() const
+{
+    // The CTS frame sent in response to an MU-RTS Trigger frame shall be carried in a non-HT or
+    // non-HT duplicate PPDU (see Clause 17) with a 6 Mb/s rate (Sec. 26.2.6.3 of 802.11ax-2021)
+    return m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ ? ErpOfdmPhy::GetErpOfdmRate6Mbps()
+                                                       : OfdmPhy::GetOfdmRate6Mbps();
+}
+
+WifiTxVector
+HeFrameExchangeManager::GetCtsTxVectorAfterMuRts(const CtrlTriggerHeader& trigger,
+                                                 uint16_t staId) const
+{
+    NS_LOG_FUNCTION(this << trigger << staId);
+
+    auto userInfoIt = trigger.FindUserInfoWithAid(staId);
+    NS_ASSERT_MSG(userInfoIt != trigger.end(), "User Info field for AID=" << staId << " not found");
+    uint16_t bw = 0;
+
+    if (uint8_t ru = userInfoIt->GetMuRtsRuAllocation(); ru < 65)
+    {
+        bw = 20;
+    }
+    else if (ru < 67)
+    {
+        bw = 40;
+    }
+    else if (ru == 67)
+    {
+        bw = 80;
+    }
+    else
+    {
+        NS_ASSERT(ru == 68);
+        bw = 160;
+    }
+
+    auto txVector = GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
+    // set the channel width of the CTS TXVECTOR according to the allocated RU
+    txVector.SetChannelWidth(bw);
+
+    return txVector;
+}
+
 Time
 HeFrameExchangeManager::GetTxDuration(uint32_t ppduPayloadSize,
                                       Mac48Address receiver,
@@ -974,8 +1222,7 @@
     NS_LOG_FUNCTION(this << psduMap << staMissedTbPpduFrom->size() << nSolicitedStations);
 
     NS_ASSERT(psduMap);
-    NS_ASSERT(psduMap->size() == 1 && psduMap->begin()->first == SU_STA_ID &&
-              psduMap->begin()->second->GetHeader(0).IsTrigger());
+    NS_ASSERT(IsTrigger(*psduMap));
 
     // This method is called if some station(s) did not send a TB PPDU
     NS_ASSERT(!staMissedTbPpduFrom->empty());
@@ -1028,7 +1275,12 @@
         resetCw = true;
     }
 
-    m_triggerFrame = nullptr; // this is strictly needed for DL_MU_TF_MU_BAR only
+    if (m_triggerFrame)
+    {
+        // this is strictly needed for DL_MU_TF_MU_BAR only
+        DequeueMpdu(m_triggerFrame);
+        m_triggerFrame = nullptr;
+    }
 
     for (const auto& sta : *staMissedBlockAckFrom)
     {
@@ -1102,7 +1354,8 @@
         {
             if (mpdu->IsQueued())
             {
-                mpdu->GetHeader().SetRetry();
+                m_mac->GetTxopQueue(mpdu->GetQueueAc())->GetOriginal(mpdu)->GetHeader().SetRetry();
+                mpdu->ResetInFlight(m_linkId);
             }
         }
     }
@@ -1136,16 +1389,16 @@
 HeFrameExchangeManager::GetTrigVector(const CtrlTriggerHeader& trigger) const
 {
     WifiTxVector v;
-    v.SetPreambleType(WifiPreamble::WIFI_PREAMBLE_HE_TB);
+    v.SetPreambleType(trigger.GetVariant() == TriggerFrameVariant::HE ? WIFI_PREAMBLE_HE_TB
+                                                                      : WIFI_PREAMBLE_EHT_TB);
     v.SetChannelWidth(trigger.GetUlBandwidth());
     v.SetGuardInterval(trigger.GetGuardInterval());
     v.SetLength(trigger.GetUlLength());
     for (const auto& userInfoField : trigger)
     {
-        v.SetHeMuUserInfo(userInfoField.GetAid12(),
-                          {userInfoField.GetRuAllocation(),
-                           HePhy::GetHeMcs(userInfoField.GetUlMcs()),
-                           userInfoField.GetNss()});
+        v.SetHeMuUserInfo(
+            userInfoField.GetAid12(),
+            {userInfoField.GetRuAllocation(), userInfoField.GetUlMcs(), userInfoField.GetNss()});
     }
     return v;
 }
@@ -1193,10 +1446,12 @@
      *
      * Refer to section 27.3.14.2 (Power pre-correction) of 802.11ax Draft 4.0 for more details.
      */
+    auto optRssi = GetMostRecentRssi(triggerSender);
+    NS_ASSERT(optRssi);
     int8_t pathLossDb =
         trigger.GetApTxPower() -
-        static_cast<int8_t>(GetWifiRemoteStationManager()->GetMostRecentRssi(
-            triggerSender)); // cast RSSI to be on equal footing with AP Tx power information
+        static_cast<int8_t>(
+            *optRssi); // cast RSSI to be on equal footing with AP Tx power information
     double reqTxPowerDbm = static_cast<double>(userInfoIt->GetUlTargetRssi() + pathLossDb);
 
     // Convert the transmit power to a power level
@@ -1229,6 +1484,12 @@
     return v;
 }
 
+std::optional<double>
+HeFrameExchangeManager::GetMostRecentRssi(const Mac48Address& address) const
+{
+    return GetWifiRemoteStationManager()->GetMostRecentRssi(address);
+}
+
 void
 HeFrameExchangeManager::SetTargetRssi(CtrlTriggerHeader& trigger) const
 {
@@ -1239,11 +1500,12 @@
         m_phy->GetPowerDbm(GetWifiRemoteStationManager()->GetDefaultTxPowerLevel())));
     for (auto& userInfo : trigger)
     {
-        const auto staList = m_apMac->GetStaList();
+        const auto staList = m_apMac->GetStaList(m_linkId);
         auto itAidAddr = staList.find(userInfo.GetAid12());
         NS_ASSERT(itAidAddr != staList.end());
-        int8_t rssi = static_cast<int8_t>(
-            GetWifiRemoteStationManager()->GetMostRecentRssi(itAidAddr->second));
+        auto optRssi = GetMostRecentRssi(itAidAddr->second);
+        NS_ASSERT(optRssi);
+        int8_t rssi = static_cast<int8_t>(*optRssi);
         rssi = (rssi >= -20)
                    ? -20
                    : ((rssi <= -110) ? -110 : rssi); // cap so as to keep within [-110; -20] dBm
@@ -1252,9 +1514,68 @@
 }
 
 void
-HeFrameExchangeManager::SendMultiStaBlockAck(const WifiTxParameters& txParams)
+HeFrameExchangeManager::PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
 {
-    NS_LOG_FUNCTION(this << &txParams);
+    NS_LOG_FUNCTION(this << psdu << txVector);
+
+    auto txVectorCopy = txVector;
+
+    if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsTrigger())
+    {
+        CtrlTriggerHeader trigger;
+        psdu->GetPayload(0)->PeekHeader(trigger);
+        if (trigger.IsMuRts())
+        {
+            const WifiMacHeader& muRts = psdu->GetHeader(0);
+            // A station receiving an MU-RTS behaves just like as if it received an RTS.
+            // Determine whether the MU-RTS is addressed to this station or not and
+            // prepare an "equivalent" RTS frame so that we can reuse the UpdateNav()
+            // and SetTxopHolder() methods of the parent classes
+            WifiMacHeader rts;
+            rts.SetType(WIFI_MAC_CTL_RTS);
+            rts.SetDsNotFrom();
+            rts.SetDsNotTo();
+            rts.SetDuration(muRts.GetDuration());
+            rts.SetAddr2(muRts.GetAddr2());
+            if (m_staMac != nullptr && m_staMac->IsAssociated() &&
+                muRts.GetAddr2() == m_bssid // sent by the AP this STA is associated with
+                && trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) != trigger.end())
+            {
+                // the MU-RTS is addressed to this station
+                rts.SetAddr1(m_self);
+            }
+            else
+            {
+                rts.SetAddr1(muRts.GetAddr2()); // an address different from that of this station
+            }
+            psdu = Create<const WifiPsdu>(Create<Packet>(), rts);
+            // The duration of the NAV reset timeout has to take into account that the CTS
+            // response is sent using the 6 Mbps data rate
+            txVectorCopy =
+                GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
+        }
+    }
+    VhtFrameExchangeManager::PostProcessFrame(psdu, txVectorCopy);
+}
+
+void
+HeFrameExchangeManager::SendCtsAfterMuRts(const WifiMacHeader& muRtsHdr,
+                                          const CtrlTriggerHeader& trigger,
+                                          double muRtsSnr)
+{
+    NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
+
+    NS_ASSERT(m_staMac != nullptr && m_staMac->IsAssociated());
+    WifiTxVector ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
+    ctsTxVector.SetTriggerResponding(true);
+
+    DoSendCtsAfterRts(muRtsHdr, ctsTxVector, muRtsSnr);
+}
+
+void
+HeFrameExchangeManager::SendMultiStaBlockAck(const WifiTxParameters& txParams, Time durationId)
+{
+    NS_LOG_FUNCTION(this << &txParams << durationId.As(Time::US));
 
     NS_ASSERT(m_apMac);
     NS_ASSERT(txParams.m_acknowledgment &&
@@ -1297,10 +1618,9 @@
             // Block acknowledgment context
             blockAck.SetAckType(false, index);
 
-            auto addressTidPair = staInfo.first;
-            auto agreementIt = m_agreements.find(addressTidPair);
-            NS_ASSERT(agreementIt != m_agreements.end());
-            agreementIt->second.FillBlockAckBitmap(&blockAck, index);
+            auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(receiver, tid);
+            NS_ASSERT(agreement);
+            agreement->get().FillBlockAckBitmap(&blockAck, index);
             NS_LOG_DEBUG("Multi-STA Block Ack: Sending Block Ack with seq="
                          << blockAck.GetStartingSequence(index) << " to=" << receiver
                          << " tid=" << +tid);
@@ -1321,17 +1641,30 @@
     Ptr<WifiPsdu> psdu =
         GetWifiPsdu(Create<WifiMpdu>(packet, hdr), acknowledgment->multiStaBaTxVector);
 
-    // The Duration/ID field in a BlockAck frame transmitted in response to a frame
-    // carried in HE TB PPDU is set according to the multiple protection settings
-    // (Sec. 9.2.5.7 of 802.11ax D3.0)
     Time txDuration = m_phy->CalculateTxDuration(GetBlockAckSize(acknowledgment->baType),
                                                  acknowledgment->multiStaBaTxVector,
                                                  m_phy->GetPhyBand());
-    WifiTxParameters params;
-    // if the TXOP limit is null, GetPsduDurationId returns the acknowledgment time,
-    // hence we set an method with acknowledgment time equal to zero.
-    params.m_acknowledgment = std::unique_ptr<WifiAcknowledgment>(new WifiNoAck);
-    psdu->SetDuration(GetPsduDurationId(txDuration, params));
+    /**
+     * In a BlockAck frame transmitted in response to a frame carried in HE TB PPDU under
+     * single protection settings, the Duration/ID field is set to the value obtained from
+     * the Duration/ID field of the frame that elicited the response minus the time, in
+     * microseconds between the end of the PPDU carrying the frame that elicited the response
+     * and the end of the PPDU carrying the BlockAck frame.
+     * Under multiple protection settings, the Duration/ID field in a BlockAck frame transmitted
+     * in response to a frame carried in HE TB PPDU is set according to the multiple protection
+     * settings defined in 9.2.5.2. (Sec. 9.2.5.7 of 802.11ax-2021)
+     */
+    NS_ASSERT(m_edca);
+    if (m_edca->GetTxopLimit(m_linkId).IsZero())
+    {
+        // single protection settings
+        psdu->SetDuration(Max(durationId - m_phy->GetSifs() - txDuration, Seconds(0)));
+    }
+    else
+    {
+        // multiple protection settings
+        psdu->SetDuration(Max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0)));
+    }
 
     psdu->GetPayload(0)->AddPacketTag(m_muSnrTag);
 
@@ -1354,9 +1687,8 @@
 
     NS_LOG_DEBUG("Received a Trigger Frame (basic variant) soliciting a transmission");
 
-    if (trigger.GetCsRequired() && hdr.GetAddr2() != m_txopHolder && m_navEnd > Simulator::Now())
+    if (!UlMuCsMediumIdle(trigger))
     {
-        NS_LOG_DEBUG("Carrier Sensing required and channel busy, do nothing");
         return;
     }
 
@@ -1394,7 +1726,7 @@
     {
         Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
 
-        if (!edca->GetBaAgreementEstablished(hdr.GetAddr2(), tid))
+        if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
         {
             // no Block Ack agreement established for this TID
             continue;
@@ -1404,21 +1736,21 @@
         txParams.m_txVector = tbTxVector;
 
         // first, check if there is a pending BlockAckReq frame
-        if (Ptr<const WifiMpdu> mpdu;
-            (mpdu = edca->GetBaManager()->GetBar(false, tid, hdr.GetAddr2())) &&
-            TryAddMpdu(mpdu, txParams, ppduDuration))
+        if (auto mpdu = GetBar(edca->GetAccessCategory(), tid, hdr.GetAddr2());
+            mpdu && TryAddMpdu(mpdu, txParams, ppduDuration))
         {
             NS_LOG_DEBUG("Sending a BAR within a TB PPDU");
-            psdu = Create<WifiPsdu>(edca->GetBaManager()->GetBar(true, tid, hdr.GetAddr2()), true);
+            psdu = Create<WifiPsdu>(mpdu, true);
             break;
         }
 
         // otherwise, check if a suitable data frame is available
-        if (Ptr<WifiMpdu> mpdu; (mpdu = edca->PeekNextMpdu(m_linkId, tid, hdr.GetAddr2())))
+        auto receiver =
+            GetWifiRemoteStationManager()->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2());
+        if (auto mpdu = edca->PeekNextMpdu(m_linkId, tid, receiver))
         {
-            Ptr<WifiMpdu> item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration, false);
-
-            if (item)
+            mpdu = CreateAliasIfNeeded(mpdu);
+            if (auto item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration, false))
             {
                 // try A-MPDU aggregation
                 std::vector<Ptr<WifiMpdu>> mpduList =
@@ -1452,11 +1784,8 @@
 
     NS_LOG_DEBUG("Requested to send QoS Null frames");
 
-    if (trigger.GetCsRequired() && hdr.GetAddr2() != m_txopHolder && m_navEnd > Simulator::Now())
+    if (!UlMuCsMediumIdle(trigger))
     {
-        NS_LOG_DEBUG("Carrier Sensing required and channel busy (TA="
-                     << hdr.GetAddr2() << ", TxopHolder=" << m_txopHolder
-                     << ", NAV end=" << m_navEnd.As(Time::S) << "), do nothing");
         return;
     }
 
@@ -1495,7 +1824,7 @@
                txParams,
                ppduDuration))
     {
-        if (!m_mac->GetQosTxop(tid)->GetBaAgreementEstablished(hdr.GetAddr2(), tid))
+        if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
         {
             NS_LOG_DEBUG("Skipping tid=" << +tid << " because no agreement established");
             header.SetQosTid(++tid);
@@ -1528,13 +1857,207 @@
 }
 
 void
+HeFrameExchangeManager::ReceiveMuBarTrigger(const CtrlTriggerHeader& trigger,
+                                            uint8_t tid,
+                                            Time durationId,
+                                            double snr)
+{
+    NS_LOG_FUNCTION(this << trigger << tid << durationId.As(Time::US) << snr);
+
+    auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(m_bssid, tid);
+
+    if (!agreement)
+    {
+        NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
+        return;
+    }
+
+    if (!UlMuCsMediumIdle(trigger))
+    {
+        return;
+    }
+
+    NS_LOG_DEBUG("Send Block Ack in TB PPDU");
+    auto txVector = GetHeTbTxVector(trigger, m_bssid);
+    SendBlockAck(*agreement, durationId, txVector, snr);
+}
+
+bool
+HeFrameExchangeManager::IsIntraBssPpdu(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) const
+{
+    NS_LOG_FUNCTION(this << psdu << txVector);
+
+    // "If, based on the MAC address information of a frame carried in a received PPDU, the
+    // received PPDU satisfies both intra-BSS and inter-BSS conditions, then the received PPDU is
+    // classified as an intra-BSS PPDU." (Sec. 26.2.2 of 802.11ax-2021)
+    // Hence, check first if the intra-BSS conditions using MAC address information are satisfied:
+    // 1. "The PPDU carries a frame that has an RA, TA, or BSSID field value that is equal to
+    //    the BSSID of the BSS in which the STA is associated"
+    const auto ra = psdu->GetAddr1();
+    const auto ta = psdu->GetAddr2();
+    const auto bssid = psdu->GetHeader(0).GetAddr3();
+    const auto empty = Mac48Address();
+
+    if (ra == m_bssid || ta == m_bssid || bssid == m_bssid)
+    {
+        return true;
+    }
+
+    // 2. "The PPDU carries a Control frame that does not have a TA field and that has an
+    //    RA field value that matches the saved TXOP holder address of the BSS in which
+    //    the STA is associated"
+    if (psdu->GetHeader(0).IsCtl() && ta == empty && ra == m_txopHolder)
+    {
+        return true;
+    }
+
+    // If we get here, the intra-BSS conditions using MAC address information are not satisfied.
+    // "If the received PPDU satisfies the intra-BSS conditions using the RXVECTOR parameter
+    // BSS_COLOR and also satisfies the inter-BSS conditions using MAC address information of a
+    // frame carried in the PPDU, then the classification made using the MAC address information
+    // takes precedence."
+    // Hence, if the inter-BSS conditions using MAC address information are satisfied, the frame
+    // is classified as inter-BSS
+    // 1. "The PPDU carries a frame that has a BSSID field, the value of which is not the BSSID
+    //    of the BSS in which the STA is associated"
+    if (bssid != empty && bssid != m_bssid)
+    {
+        return false;
+    }
+
+    // 2. The PPDU carries a frame that does not have a BSSID field but has both an RA field and
+    //    TA field, neither value of which is equal to the BSSID of the BSS in which the STA is
+    //    associated
+    if (bssid == empty && ta != empty && ra != empty && ta != m_bssid && ra != m_bssid)
+    {
+        return false;
+    }
+
+    // If we get here, both intra-BSS and inter-bss conditions using MAC address information
+    // are not satisfied. Hence, the frame is classified as intra-BSS if the intra-BSS conditions
+    // using the RXVECTOR parameters are satisfied:
+    // 1. The RXVECTOR parameter BSS_COLOR of the PPDU carrying the frame is the BSS color of the
+    //    BSS of which the STA is a member
+    // This condition is used if the BSS is not disabled ("If a STA determines that the BSS color
+    // 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;
+}
+
+void
+HeFrameExchangeManager::UpdateNav(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << psdu << txVector);
+
+    if (!psdu->HasNav())
+    {
+        return;
+    }
+
+    if (psdu->GetAddr1() == m_self)
+    {
+        // When the received frame’s RA is equal to the STA’s own MAC address, the STA
+        // shall not update its NAV (IEEE 802.11-2020, sec. 10.3.2.4)
+        return;
+    }
+
+    // The intra-BSS NAV is updated by an intra-BSS PPDU. The basic NAV is updated by an
+    // inter-BSS PPDU or a PPDU that cannot be classified as intra-BSS or inter-BSS.
+    // (Section 26.2.4 of 802.11ax-2021)
+    if (!IsIntraBssPpdu(psdu, txVector))
+    {
+        NS_LOG_DEBUG("PPDU not classified as intra-BSS, update the basic NAV");
+        VhtFrameExchangeManager::UpdateNav(psdu, txVector);
+        return;
+    }
+
+    NS_LOG_DEBUG("PPDU classified as intra-BSS, update the intra-BSS NAV");
+    Time duration = psdu->GetDuration();
+    NS_LOG_DEBUG("Duration/ID=" << duration);
+
+    // 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;
+    if (intraBssNavEnd > m_intraBssNavEnd)
+    {
+        m_intraBssNavEnd = intraBssNavEnd;
+        NS_LOG_DEBUG("Updated intra-BSS NAV=" << m_intraBssNavEnd);
+
+        // A STA that used information from an RTS frame as the most recent basis to update
+        // its NAV setting is permitted to reset its NAV if no PHY-RXSTART.indication
+        // primitive is received from the PHY during a NAVTimeout period starting when the
+        // MAC receives a PHY-RXEND.indication primitive corresponding to the detection of
+        // the RTS frame. NAVTimeout period is equal to:
+        // (2 x aSIFSTime) + (CTS_Time) + aRxPHYStartDelay + (2 x aSlotTime)
+        // The “CTS_Time” shall be calculated using the length of the CTS frame and the data
+        // rate at which the RTS frame used for the most recent NAV update was received
+        // (IEEE 802.11-2016 sec. 10.3.2.4)
+        if (psdu->GetHeader(0).IsRts())
+        {
+            WifiTxVector ctsTxVector =
+                GetWifiRemoteStationManager()->GetCtsTxVector(psdu->GetAddr2(), txVector.GetMode());
+            auto navResetDelay =
+                2 * m_phy->GetSifs() +
+                WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
+                m_phy->CalculatePhyPreambleAndHeaderDuration(ctsTxVector) + 2 * m_phy->GetSlot();
+            m_intraBssNavResetEvent =
+                Simulator::Schedule(navResetDelay,
+                                    &HeFrameExchangeManager::IntraBssNavResetTimeout,
+                                    this);
+        }
+    }
+    NS_LOG_DEBUG("Current intra-BSS NAV=" << m_intraBssNavEnd);
+
+    m_channelAccessManager->NotifyNavStartNow(duration);
+}
+
+void
+HeFrameExchangeManager::ClearTxopHolderIfNeeded()
+{
+    NS_LOG_FUNCTION(this);
+    if (m_intraBssNavEnd <= Simulator::Now())
+    {
+        m_txopHolder.reset();
+    }
+}
+
+void
+HeFrameExchangeManager::NavResetTimeout()
+{
+    NS_LOG_FUNCTION(this);
+    m_navEnd = Simulator::Now();
+    // Do not reset the TXOP holder because the basic NAV is updated by inter-BSS frames
+    // The NAV seen by the ChannelAccessManager is now the intra-BSS NAV only
+    Time intraBssNav = Simulator::GetDelayLeft(m_intraBssNavResetEvent);
+    m_channelAccessManager->NotifyNavResetNow(intraBssNav);
+}
+
+void
+HeFrameExchangeManager::IntraBssNavResetTimeout()
+{
+    NS_LOG_FUNCTION(this);
+    m_intraBssNavEnd = Simulator::Now();
+    ClearTxopHolderIfNeeded();
+    // The NAV seen by the ChannelAccessManager is now the basic NAV only
+    Time basicNav = Simulator::GetDelayLeft(m_navResetEvent);
+    m_channelAccessManager->NotifyNavResetNow(basicNav);
+}
+
+void
 HeFrameExchangeManager::SetTxopHolder(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
 {
     NS_LOG_FUNCTION(this << psdu << txVector);
 
-    if (psdu->GetHeader(0).IsTrigger())
+    if (psdu->GetHeader(0).IsTrigger() && psdu->GetAddr2() == m_bssid)
     {
-        m_txopHolder = psdu->GetAddr2();
+        m_txopHolder = m_bssid;
     }
     else if (!txVector.IsUlMu()) // the sender of a TB PPDU is not the TXOP holder
     {
@@ -1542,6 +2065,58 @@
     }
 }
 
+bool
+HeFrameExchangeManager::VirtualCsMediumIdle() const
+{
+    // For an HE STA maintaining two NAVs, if both the NAV timers are 0, the virtual CS indication
+    // is that the medium is idle; if at least one of the two NAV timers is nonzero, the virtual CS
+    // indication is that the medium is busy. (Sec. 26.2.4 of 802.11ax-2021)
+    return m_navEnd <= Simulator::Now() && m_intraBssNavEnd <= Simulator::Now();
+}
+
+bool
+HeFrameExchangeManager::UlMuCsMediumIdle(const CtrlTriggerHeader& trigger) const
+{
+    if (!trigger.GetCsRequired())
+    {
+        NS_LOG_DEBUG("CS not required");
+        return true;
+    }
+
+    // A non-AP STA does not consider the intra-BSS NAV in determining whether to respond to a
+    // Trigger frame sent by the AP with which the non-AP STA is associated.
+    // A non-AP STA considers the basic NAV in determining whether to respond to a Trigger frame
+    // sent by the AP with which the non-AP STA is associated. (Sec. 26.5.2.5 of 802.11ax-2021)
+    const Time now = Simulator::Now();
+    if (m_navEnd > now)
+    {
+        NS_LOG_DEBUG("Basic NAV indicates medium busy");
+        return false;
+    }
+
+    NS_ASSERT_MSG(m_staMac, "UL MU CS is only performed by non-AP STAs");
+    const auto userInfoIt = trigger.FindUserInfoWithAid(m_staMac->GetAssociationId());
+    NS_ASSERT_MSG(userInfoIt != trigger.end(),
+                  "No User Info field for STA (" << m_self
+                                                 << ") AID=" << m_staMac->GetAssociationId());
+
+    std::set<uint8_t> indices;
+
+    if (trigger.IsMuRts())
+    {
+        auto ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
+        auto bw = ctsTxVector.GetChannelWidth();
+        indices = m_phy->GetOperatingChannel().GetAll20MHzChannelIndicesInPrimary(bw);
+    }
+    else
+    {
+        indices =
+            m_phy->GetOperatingChannel().Get20MHzIndicesCoveringRu(userInfoIt->GetRuAllocation(),
+                                                                   trigger.GetUlBandwidth());
+    }
+    return !m_channelAccessManager->GetPer20MHzBusy(indices);
+}
+
 void
 HeFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
                                     RxSignalInfo rxSignalInfo,
@@ -1577,14 +2152,15 @@
             mpdu->GetPacket()->PeekHeader(blockAckReq);
             NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
             uint8_t tid = blockAckReq.GetTidInfo();
-            auto agreementIt = m_agreements.find({sender, tid});
-            NS_ASSERT(agreementIt != m_agreements.end());
-            agreementIt->second.NotifyReceivedBar(blockAckReq.GetStartingSequence());
+            GetBaManager(tid)->NotifyGotBlockAckRequest(
+                m_mac->GetMldAddress(sender).value_or(sender),
+                tid,
+                blockAckReq.GetStartingSequence());
 
             // Block Acknowledgment context
             acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
             acknowledgment->baType.m_bitmapLen.push_back(
-                GetBlockAckType(sender, tid).m_bitmapLen.at(0));
+                m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
             uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
             m_muSnrTag.Set(staId, rxSignalInfo.snr);
         }
@@ -1593,9 +2169,7 @@
             NS_LOG_DEBUG("Received an S-MPDU in a TB PPDU from " << sender << " (" << *mpdu << ")");
 
             uint8_t tid = hdr.GetQosTid();
-            auto agreementIt = m_agreements.find({sender, tid});
-            NS_ASSERT(agreementIt != m_agreements.end());
-            agreementIt->second.NotifyReceivedMpdu(mpdu);
+            GetBaManager(tid)->NotifyGotMpdu(mpdu);
 
             // Acknowledgment context of Multi-STA Block Acks
             acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
@@ -1620,7 +2194,8 @@
             m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
                                                     &HeFrameExchangeManager::SendMultiStaBlockAck,
                                                     this,
-                                                    std::cref(m_txParams));
+                                                    std::cref(m_txParams),
+                                                    mpdu->GetHeader().GetDuration());
         }
 
         // remove the sender from the set of stations that are expected to send a TB PPDU
@@ -1650,7 +2225,7 @@
         m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF &&
         !inAmpdu) // if in A-MPDU, processing is done at the end of A-MPDU reception
     {
-        Mac48Address sender = hdr.GetAddr2();
+        const auto& sender = hdr.GetAddr2();
 
         if (m_staExpectTbPpduFrom.find(sender) == m_staExpectTbPpduFrom.end())
         {
@@ -1707,6 +2282,18 @@
             m_channelAccessManager->NotifyCtsTimeoutResetNow();
             Simulator::Schedule(m_phy->GetSifs(), &HeFrameExchangeManager::SendPsduMap, this);
         }
+        else if (hdr.IsCts() && !m_psduMap.empty() && 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");
+            NS_ASSERT(hdr.GetAddr1() == m_self);
+
+            NS_LOG_DEBUG("Received a CTS frame in response to an MU-RTS");
+
+            m_txTimer.Cancel();
+            m_channelAccessManager->NotifyCtsTimeoutResetNow();
+            Simulator::Schedule(m_phy->GetSifs(), &HeFrameExchangeManager::SendPsduMap, this);
+        }
         else if (hdr.IsAck() && m_txTimer.IsRunning() &&
                  m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
         {
@@ -1755,8 +2342,11 @@
             mpdu->GetPacket()->PeekHeader(blockAck);
             uint8_t tid = blockAck.GetTidInfo();
             std::pair<uint16_t, uint16_t> ret =
-                GetBaManager(tid)->NotifyGotBlockAck(blockAck, hdr.GetAddr2(), {tid});
-            GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
+                GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
+                                                     blockAck,
+                                                     m_mac->GetMldAddress(sender).value_or(sender),
+                                                     {tid});
+            GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
                                                                ret.first,
                                                                ret.second,
                                                                rxSignalInfo.snr,
@@ -1775,7 +2365,12 @@
                 // we do not expect any other BlockAck frame
                 m_txTimer.Cancel();
                 m_channelAccessManager->NotifyAckTimeoutResetNow();
-                m_triggerFrame = nullptr; // this is strictly needed for DL_MU_TF_MU_BAR only
+                if (m_triggerFrame)
+                {
+                    // this is strictly needed for DL_MU_TF_MU_BAR only
+                    DequeueMpdu(m_triggerFrame);
+                    m_triggerFrame = nullptr;
+                }
 
                 m_edca->ResetCw(m_linkId);
                 m_psduMap.clear();
@@ -1820,7 +2415,7 @@
                 {
                     // Acknowledgment context
                     NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
-                    GetBaManager(tid)->NotifyGotAck(*m_psduMap.at(staId)->begin());
+                    GetBaManager(tid)->NotifyGotAck(m_linkId, *m_psduMap.at(staId)->begin());
                 }
                 else
                 {
@@ -1835,11 +2430,12 @@
                         tid = *tids.begin();
                     }
 
-                    std::pair<uint16_t, uint16_t> ret =
-                        GetBaManager(tid)->NotifyGotBlockAck(blockAck,
-                                                             hdr.GetAddr2(),
-                                                             {tid},
-                                                             index);
+                    std::pair<uint16_t, uint16_t> ret = GetBaManager(tid)->NotifyGotBlockAck(
+                        m_linkId,
+                        blockAck,
+                        m_mac->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2()),
+                        {tid},
+                        index);
                     GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
                                                                        ret.first,
                                                                        ret.second,
@@ -1867,6 +2463,14 @@
             // cancel the timer
             m_txTimer.Cancel();
             m_channelAccessManager->NotifyAckTimeoutResetNow();
+            // dequeue BlockAckReq frames included in acknowledged TB PPDUs (if any)
+            for (const auto& [staId, psdu] : m_psduMap)
+            {
+                if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
+                {
+                    DequeuePsdu(psdu);
+                }
+            }
             m_psduMap.clear();
         }
         else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
@@ -1907,7 +2511,35 @@
 
             uint16_t staId = m_staMac->GetAssociationId();
 
-            if (trigger.IsMuBar())
+            if (trigger.IsMuRts())
+            {
+                Mac48Address sender = hdr.GetAddr2();
+                NS_LOG_DEBUG("Received MU-RTS Trigger Frame from=" << sender);
+                GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
+
+                // If a non-AP STA receives an MU-RTS Trigger frame, the non-AP STA shall commence
+                // the transmission of a CTS frame response at the SIFS time boundary after
+                // the end of a received PPDU when all the following conditions are met:
+                // - The MU-RTS Trigger frame has one of the User Info fields addressed to
+                //   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");
+                }
+            }
+            else if (trigger.IsMuBar())
             {
                 Mac48Address sender = hdr.GetAddr2();
                 NS_LOG_DEBUG("Received MU-BAR Trigger Frame from=" << sender);
@@ -1919,23 +2551,17 @@
                 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
                 uint8_t tid = blockAckReq.GetTidInfo();
 
-                auto agreementIt = m_agreements.find({sender, tid});
+                GetBaManager(tid)->NotifyGotBlockAckRequest(
+                    m_mac->GetMldAddress(sender).value_or(sender),
+                    tid,
+                    blockAckReq.GetStartingSequence());
 
-                if (agreementIt == m_agreements.end())
-                {
-                    NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
-                    return;
-                }
-
-                agreementIt->second.NotifyReceivedBar(blockAckReq.GetStartingSequence());
-
-                NS_LOG_DEBUG("Schedule Block Ack in TB PPDU");
                 Simulator::Schedule(m_phy->GetSifs(),
-                                    &HeFrameExchangeManager::SendBlockAck,
+                                    &HeFrameExchangeManager::ReceiveMuBarTrigger,
                                     this,
-                                    agreementIt->second,
+                                    trigger,
+                                    tid,
                                     hdr.GetDuration(),
-                                    GetHeTbTxVector(trigger, hdr.GetAddr2()),
                                     rxSignalInfo.snr);
             }
             else if (trigger.IsBasic())
@@ -2016,7 +2642,7 @@
                     acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid),
                                                                         index + i++);
                     acknowledgment->baType.m_bitmapLen.push_back(
-                        GetBlockAckType(sender, tid).m_bitmapLen.at(0));
+                        m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
                 }
             }
             uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
@@ -2029,7 +2655,8 @@
             m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
                                                     &HeFrameExchangeManager::SendMultiStaBlockAck,
                                                     this,
-                                                    std::cref(m_txParams));
+                                                    std::cref(m_txParams),
+                                                    psdu->GetDuration());
         }
 
         // remove the sender from the set of stations that are expected to send a TB PPDU
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-frame-exchange-manager.h ns-3.38/src/wifi/model/he/he-frame-exchange-manager.h
--- ns-3.37/src/wifi/model/he/he-frame-exchange-manager.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
@@ -45,6 +45,13 @@
 typedef std::unordered_map<uint16_t /* staId */, Ptr<const WifiPsdu> /* PSDU */> WifiConstPsduMap;
 
 /**
+ * \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 WifiPsduMap& psduMap);
+
+/**
  * \ingroup wifi
  *
  * HeFrameExchangeManager handles the frame exchange sequences
@@ -64,8 +71,11 @@
     uint16_t GetSupportedBaBufferSize() const override;
     bool StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame) override;
     void SetWifiMac(const Ptr<WifiMac> mac) override;
+    void SetWifiPhy(const Ptr<WifiPhy> phy) override;
     void CalculateAcknowledgmentTime(WifiAcknowledgment* acknowledgment) const override;
+    void CalculateProtectionTime(WifiProtection* protection) const override;
     void SetTxopHolder(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
+    bool VirtualCsMediumIdle() const override;
 
     /**
      * Set the Multi-user Scheduler associated with this Frame Exchange Manager.
@@ -93,8 +103,40 @@
      */
     virtual void SetTargetRssi(CtrlTriggerHeader& trigger) const;
 
+    /**
+     * Get the RSSI (in dBm) of the most recent packet received from the station having
+     * the given address.
+     *
+     * \param address of the remote station
+     * \return the RSSI (in dBm) of the most recent packet received from the remote station
+     */
+    virtual std::optional<double> GetMostRecentRssi(const Mac48Address& address) const;
+
+    /**
+     * Return whether the received frame is classified as intra-BSS. It is assumed that
+     * this station is already associated with an AP.
+     *
+     * \param psdu the received PSDU
+     * \param txVector TX vector of the received PSDU
+     * \return true if the received frame is classified as intra-BSS, false otherwise
+     *         (the received frame is classified as inter-BSS or it cannot be classified
+     *         as intra-BSS or inter-BSS)
+     */
+    bool IsIntraBssPpdu(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) const;
+
+    /**
+     * This method is intended to be called a SIFS after the reception of a Trigger Frame
+     * to determine whether the station is allowed to respond.
+     *
+     * \param trigger the Trigger Frame soliciting a response
+     * \return true if CS is not required or the UL MU CS mechanism indicates that the medium
+     *         is idle, false otherwise
+     */
+    bool UlMuCsMediumIdle(const CtrlTriggerHeader& trigger) const;
+
   protected:
     void DoDispose() override;
+    void Reset() override;
 
     void ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
                      RxSignalInfo rxSignalInfo,
@@ -104,13 +146,83 @@
                          const RxSignalInfo& rxSignalInfo,
                          const WifiTxVector& txVector,
                          const std::vector<bool>& perMpduStatus) override;
+    void PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
     Time GetTxDuration(uint32_t ppduPayloadSize,
                        Mac48Address receiver,
                        const WifiTxParameters& txParams) const override;
-    bool SendMpduFromBaManager(Ptr<QosTxop> edca, Time availableTime, bool initialFrame) override;
+    bool SendMpduFromBaManager(Ptr<WifiMpdu> mpdu, Time availableTime, bool initialFrame) override;
     void NormalAckTimeout(Ptr<WifiMpdu> mpdu, const WifiTxVector& txVector) override;
     void BlockAckTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector) override;
     void CtsTimeout(Ptr<WifiMpdu> rts, const WifiTxVector& txVector) override;
+    void UpdateNav(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
+    void NavResetTimeout() override;
+
+    /**
+     * Clear the TXOP holder if the intra-BSS NAV counted down to zero (includes the case
+     * of intra-BSS NAV reset).
+     */
+    void ClearTxopHolderIfNeeded() override;
+
+    /**
+     * Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
+     */
+    virtual void IntraBssNavResetTimeout();
+
+    /**
+     * Compute how to set the Duration/ID field of an MU-RTS Trigger Frame to send to protect
+     * a frame transmitted with the given TX vector.
+     *
+     * \param muRtsSize the size of the MU-RTS Trigger Frame in bytes
+     * \param muRtsTxVector the TX vector used to send the MU-RTS Trigger Frame
+     * \param txDuration the TX duration of the data frame
+     * \param response the time taken by the response (acknowledgment) to the data frame
+     * \return the computed Duration/ID value for the MU-RTS Trigger Frame
+     */
+    virtual Time GetMuRtsDurationId(uint32_t muRtsSize,
+                                    const WifiTxVector& muRtsTxVector,
+                                    Time txDuration,
+                                    Time response) const;
+
+    /**
+     * 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);
+
+    /**
+     * Called when no CTS frame is received after an MU-RTS.
+     *
+     * \param muRts the MU-RTS that solicited CTS responses
+     * \param txVector the TXVECTOR used to transmit the MU-RTS frame
+     */
+    virtual void CtsAfterMuRtsTimeout(Ptr<WifiMpdu> muRts, const WifiTxVector& txVector);
+
+    /**
+     * Send CTS after receiving an MU-RTS.
+     *
+     * \param muRtsHdr the MAC header of the received MU-RTS
+     * \param trigger the MU-RTS Trigger Frame header
+     * \param muRtsSnr the SNR of the MU-RTS in linear scale
+     */
+    void SendCtsAfterMuRts(const WifiMacHeader& muRtsHdr,
+                           const CtrlTriggerHeader& trigger,
+                           double muRtsSnr);
+
+    /**
+     * \return the mode used to transmit a CTS after an MU-RTS.
+     */
+    WifiMode GetCtsModeAfterMuRts() const;
+
+    /**
+     * Get the TXVECTOR that the station having the given station ID has to use to send a
+     * CTS frame after receiving an MU-RTS Trigger Frame from the AP it is associated with.
+     *
+     * \param trigger the MU-RTS Trigger Frame
+     * \param staId the station ID for MU
+     * \return the TXVECTOR to use to send a CTS frame
+     */
+    WifiTxVector GetCtsTxVectorAfterMuRts(const CtrlTriggerHeader& trigger, uint16_t staId) const;
 
     /**
      * Send a map of PSDUs as a DL MU PPDU.
@@ -205,8 +317,10 @@
      * Send a Multi-STA Block Ack frame after the reception of some TB PPDUs.
      *
      * \param txParams the TX parameters for the Trigger Frame that solicited the TB PPDUs
+     * \param durationId the Duration/ID field of the HE TB PPDU that elicited the Multi-STA
+     *                   Block Ack response
      */
-    void SendMultiStaBlockAck(const WifiTxParameters& txParams);
+    void SendMultiStaBlockAck(const WifiTxParameters& txParams, Time durationId);
 
     /**
      * Send QoS Null frames in response to a Basic or BSRP Trigger Frame. The number
@@ -218,9 +332,11 @@
      */
     void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr);
 
-    Ptr<ApWifiMac> m_apMac;    //!< MAC pointer (null if not an AP)
-    Ptr<StaWifiMac> m_staMac;  //!< MAC pointer (null if not a STA)
-    WifiTxVector m_trigVector; //!< the TRIGVECTOR
+    Ptr<ApWifiMac> m_apMac;          //!< MAC pointer (null if not an AP)
+    Ptr<StaWifiMac> m_staMac;        //!< MAC pointer (null if not a STA)
+    WifiTxVector m_trigVector;       //!< the TRIGVECTOR
+    Time m_intraBssNavEnd;           //!< intra-BSS NAV expiration time
+    EventId m_intraBssNavResetEvent; //!< the event to reset the intra-BSS NAV after an RTS
 
   private:
     /**
@@ -229,13 +345,26 @@
     void SendPsduMap();
 
     /**
-     * Take the necessary actions when receiveing a Basic Trigger Frame.
+     * Take the necessary actions when receiving a Basic Trigger Frame.
      *
      * \param trigger the Basic Trigger Frame content
      * \param hdr the MAC header of the Basic Trigger Frame
      */
     void ReceiveBasicTrigger(const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr);
 
+    /**
+     * Respond to a MU-BAR Trigger Frame (if permitted by UL MU CS mechanism).
+     *
+     * \param trigger the Basic Trigger Frame content
+     * \param tid the TID requested for us in the MU-BAR Trigger Frame
+     * \param durationId the Duration/ID field of the MPDU carrying the Trigger Frame
+     * \param snr the receive SNR
+     */
+    void ReceiveMuBarTrigger(const CtrlTriggerHeader& trigger,
+                             uint8_t tid,
+                             Time durationId,
+                             double snr);
+
     WifiPsduMap m_psduMap;                        //!< the A-MPDU being transmitted
     WifiTxParameters m_txParams;                  //!< the TX parameters for the current PPDU
     Ptr<MultiUserScheduler> m_muScheduler;        //!< Multi-user Scheduler (HE APs only)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-operation.cc ns-3.38/src/wifi/model/he/he-operation.cc
--- ns-3.37/src/wifi/model/he/he-operation.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-operation.cc	2023-03-16 20:54:23.856869200 -0700
@@ -32,7 +32,7 @@
       m_txBssidIndicator(0),
       m_bssColorDisabled(0),
       m_dualBeacon(0),
-      m_basicHeMcsAndNssSet(0)
+      m_basicHeMcsAndNssSet(0xfffc)
 {
 }
 
@@ -88,19 +88,27 @@
 HeOperation::SetMaxHeMcsPerNss(uint8_t nss, uint8_t maxHeMcs)
 {
     NS_ASSERT((maxHeMcs >= 7 && maxHeMcs <= 11) && (nss >= 1 && nss <= 8));
-    uint8_t val = 3;  // 3 means not supported
-    if (maxHeMcs > 9) // MCS 0 - 11
+
+    // IEEE 802.11ax-2021 9.4.2.248.4 Supported HE-MCS And NSS Set field
+    uint8_t val = 0x03; // not supported
+    if (maxHeMcs > 9)   // MCS 0 - 11
     {
-        val = 2;
+        val = 0x02;
     }
     else if (maxHeMcs > 7) // MCS 0 - 9
     {
-        val = 1;
+        val = 0x01;
     }
     else if (maxHeMcs == 7) // MCS 0 - 7
     {
-        val = 0;
+        val = 0x01;
     }
+
+    // clear bits for that nss
+    const uint16_t mask = ~(0x03 << ((nss - 1) * 2));
+    m_basicHeMcsAndNssSet &= mask;
+
+    // update bits for that nss
     m_basicHeMcsAndNssSet |= ((val & 0x03) << ((nss - 1) * 2));
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-phy.cc ns-3.38/src/wifi/model/he/he-phy.cc
--- ns-3.37/src/wifi/model/he/he-phy.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-phy.cc	2023-03-16 20:54:23.856869200 -0700
@@ -22,7 +22,6 @@
 #include "he-phy.h"
 
 #include "he-configuration.h"
-#include "he-ppdu.h"
 #include "obss-pd-algorithm.h"
 
 #include "ns3/ap-wifi-mac.h"
@@ -78,7 +77,9 @@
 
 HePhy::HePhy(bool buildModeList /* = true */)
     : VhtPhy(false), // don't add VHT modes to list
-      m_trigVectorExpirationTime(Seconds(0)),
+      m_trigVector(std::nullopt),
+      m_trigVectorExpirationTime(std::nullopt),
+      m_currentTxVector(std::nullopt),
       m_rxHeTbPpdus(0),
       m_lastPer20MHzDurations()
 {
@@ -154,7 +155,7 @@
     uint8_t smallestMcs = 5; // maximum MCS for HE-SIG-B
     for (auto& info : txVector.GetHeMuUserInfoMap())
     {
-        smallestMcs = std::min(smallestMcs, info.second.mcs.GetMcsValue());
+        smallestMcs = std::min(smallestMcs, info.second.mcs);
     }
     switch (smallestMcs)
     {
@@ -218,7 +219,7 @@
 Time
 HePhy::GetSigBDuration(const WifiTxVector& txVector) const
 {
-    if (txVector.IsDlMu()) // See section 27.3.10.8 of IEEE 802.11ax draft 4.0.
+    if (ns3::IsDlMu(txVector.GetPreambleType())) // See section 27.3.11.8 of IEEE 802.11ax-2021
     {
         NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
 
@@ -287,7 +288,6 @@
 Time
 HePhy::CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const
 {
-    NS_ABORT_IF(!txVector.IsUlMu() || (txVector.GetModulationClass() < WIFI_MOD_CLASS_HE));
     Time duration = GetDuration(WIFI_PPDU_FIELD_PREAMBLE, txVector) +
                     GetDuration(WIFI_PPDU_FIELD_NON_HT_HEADER, txVector) +
                     GetDuration(WIFI_PPDU_FIELD_SIG_A, txVector);
@@ -297,7 +297,6 @@
 Time
 HePhy::CalculateNonOfdmaDurationForHeMu(const WifiTxVector& txVector) const
 {
-    NS_ABORT_IF(!txVector.IsDlMu() || (txVector.GetModulationClass() < WIFI_MOD_CLASS_HE));
     Time duration = GetDuration(WIFI_PPDU_FIELD_PREAMBLE, txVector) +
                     GetDuration(WIFI_PPDU_FIELD_NON_HT_HEADER, txVector) +
                     GetDuration(WIFI_PPDU_FIELD_SIG_A, txVector) +
@@ -322,9 +321,16 @@
 void
 HePhy::SetTrigVector(const WifiTxVector& trigVector, Time validity)
 {
+    NS_LOG_FUNCTION(this << trigVector << validity);
+    NS_ASSERT_MSG(trigVector.GetGuardInterval() > 800,
+                  "Invalid guard interval " << trigVector.GetGuardInterval());
+    if (auto mac = m_wifiPhy->GetDevice()->GetMac(); mac && mac->GetTypeOfStation() != AP)
+    {
+        return;
+    }
     m_trigVector = trigVector;
     m_trigVectorExpirationTime = Simulator::Now() + validity;
-    NS_LOG_FUNCTION(this << m_trigVector << m_trigVectorExpirationTime.As(Time::US));
+    NS_LOG_FUNCTION(this << m_trigVector.value() << m_trigVectorExpirationTime->As(Time::US));
 }
 
 Ptr<WifiPpdu>
@@ -338,8 +344,7 @@
                           ppduDuration,
                           m_wifiPhy->GetPhyBand(),
                           ObtainNextUid(txVector),
-                          HePpdu::PSD_NON_HE_PORTION,
-                          m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20));
+                          HePpdu::PSD_NON_HE_PORTION);
 }
 
 void
@@ -348,10 +353,10 @@
                             Time rxDuration)
 {
     NS_LOG_FUNCTION(this << ppdu << rxDuration);
-    const WifiTxVector& txVector = ppdu->GetTxVector();
+    const auto& txVector = ppdu->GetTxVector();
     auto hePpdu = DynamicCast<const HePpdu>(ppdu);
     NS_ASSERT(hePpdu);
-    HePpdu::TxPsdFlag psdFlag = hePpdu->GetTxPsdFlag();
+    const auto psdFlag = hePpdu->GetTxPsdFlag();
     if (psdFlag == HePpdu::PSD_HE_PORTION)
     {
         NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
@@ -446,19 +451,35 @@
     // 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.
-    if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
-    {
-        auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
-        const WifiTxVector& txVector = ppdu->GetTxVector();
-        Time rxDuration = CalculateNonOfdmaDurationForHeTb(
-            txVector); // the OFDMA part of the transmission will be added later on
-        const auto& currentPreambleEvents = GetCurrentPreambleEvents();
-        auto it = currentPreambleEvents.find(uidPreamblePair);
+    auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
+    const auto& currentPreambleEvents = GetCurrentPreambleEvents();
+    auto it = currentPreambleEvents.find(uidPreamblePair);
+    bool 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())
         {
-            NS_LOG_DEBUG("Received another HE TB PPDU for UID "
-                         << ppdu->GetUid() << " from STA-ID " << ppdu->GetStaId()
-                         << " and BSS color " << +txVector.GetBssColor());
+            if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
+            {
+                NS_LOG_DEBUG("Received another HE TB PPDU for UID "
+                             << ppdu->GetUid() << " from STA-ID " << ppdu->GetStaId()
+                             << " and BSS color " << +txVector.GetBssColor());
+            }
+            else
+            {
+                NS_LOG_DEBUG("Received another response to a trigger frame " << ppdu->GetUid());
+            }
             event = it->second;
 
             auto heConfiguration = m_wifiPhy->GetDevice()->GetHeConfiguration();
@@ -489,25 +510,40 @@
                 UpdateInterferenceEvent(event, rxPowersW);
             }
 
-            if (GetCurrentEvent() && (GetCurrentEvent()->GetPpdu()->GetUid() != ppdu->GetUid()))
+            if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU && GetCurrentEvent() &&
+                (GetCurrentEvent()->GetPpdu()->GetUid() != ppdu->GetUid()))
             {
                 NS_LOG_DEBUG("Drop packet because already receiving another HE TB PPDU");
                 m_wifiPhy->NotifyRxDrop(GetAddressedPsduInPpdu(ppdu), RXING);
             }
+            else if (isResponseToTrigger && GetCurrentEvent() &&
+                     (GetCurrentEvent()->GetPpdu()->GetUid() != ppdu->GetUid()))
+            {
+                NS_LOG_DEBUG(
+                    "Drop packet because already receiving another response to a trigger frame");
+                m_wifiPhy->NotifyRxDrop(GetAddressedPsduInPpdu(ppdu), RXING);
+            }
             return nullptr;
         }
         else
         {
-            NS_LOG_DEBUG("Received a new HE TB PPDU for UID "
-                         << ppdu->GetUid() << " from STA-ID " << ppdu->GetStaId()
-                         << " and BSS color " << +txVector.GetBssColor());
+            if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
+            {
+                NS_LOG_DEBUG("Received a new HE TB PPDU for UID "
+                             << ppdu->GetUid() << " from STA-ID " << ppdu->GetStaId()
+                             << " and BSS color " << +txVector.GetBssColor());
+            }
+            else
+            {
+                NS_LOG_DEBUG("Received response to a trigger frame for UID " << ppdu->GetUid());
+            }
             event = CreateInterferenceEvent(ppdu, txVector, rxDuration, rxPowersW);
             AddPreambleEvent(event);
         }
     }
     else if (ppdu->GetType() == WIFI_PPDU_TYPE_DL_MU)
     {
-        const WifiTxVector& txVector = ppdu->GetTxVector();
+        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);
@@ -515,7 +551,19 @@
     }
     else
     {
-        event = PhyEntity::DoGetEvent(ppdu, rxPowersW);
+        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;
+        }
     }
     return event;
 }
@@ -556,7 +604,7 @@
     }
     else if (ppdu->GetType() == WIFI_PPDU_TYPE_DL_MU)
     {
-        Ptr<StaWifiMac> mac = DynamicCast<StaWifiMac>(m_wifiPhy->GetDevice()->GetMac());
+        auto mac = DynamicCast<StaWifiMac>(m_wifiPhy->GetDevice()->GetMac());
         if (mac && mac->IsAssociated())
         {
             return mac->GetAssociationId();
@@ -609,18 +657,21 @@
 
         // When SIG-A is decoded, we know the type of frame being received. If we stored a
         // valid TRIGVECTOR and we are not receiving a TB PPDU, we drop the frame.
-        if (m_trigVectorExpirationTime >= Simulator::Now() && !txVector.IsUlMu())
+        Ptr<const WifiPpdu> ppdu = event->GetPpdu();
+        if (m_trigVectorExpirationTime.has_value() &&
+            (m_trigVectorExpirationTime.value() >= Simulator::Now()) &&
+            (ppdu->GetType() != WIFI_PPDU_TYPE_UL_MU))
         {
             NS_LOG_DEBUG("Expected an HE TB PPDU, receiving a " << txVector.GetPreambleType());
             return PhyFieldRxStatus(false, FILTERED, DROP);
         }
 
-        Ptr<const WifiPpdu> ppdu = event->GetPpdu();
-        if (txVector.IsUlMu())
+        if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
         {
             NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
             // check that the stored TRIGVECTOR is still valid
-            if (m_trigVectorExpirationTime < Simulator::Now())
+            if (!m_trigVectorExpirationTime.has_value() ||
+                (m_trigVectorExpirationTime < Simulator::Now()))
             {
                 NS_LOG_DEBUG("No valid TRIGVECTOR, the PHY was not expecting a TB PPDU");
                 return PhyFieldRxStatus(false, FILTERED, DROP);
@@ -630,28 +681,32 @@
             // OBSS, as BSS Colors are not guaranteed to be different for all APs in
             // range (an example is when BSS Color is 0). We can detect this situation
             // by comparing the TRIGVECTOR with the TXVECTOR of the TB PPDU being received
-            if (m_trigVector.GetChannelWidth() != txVector.GetChannelWidth())
+            NS_ABORT_IF(!m_trigVector.has_value());
+            if (m_trigVector->GetChannelWidth() != txVector.GetChannelWidth())
             {
                 NS_LOG_DEBUG("Received channel width different than in TRIGVECTOR");
                 return PhyFieldRxStatus(false, FILTERED, DROP);
             }
-            if (m_trigVector.GetLength() != txVector.GetLength())
+            if (m_trigVector->GetLength() != txVector.GetLength())
             {
                 NS_LOG_DEBUG("Received UL Length (" << txVector.GetLength()
                                                     << ") different than in TRIGVECTOR ("
-                                                    << m_trigVector.GetLength() << ")");
+                                                    << m_trigVector->GetLength() << ")");
                 return PhyFieldRxStatus(false, FILTERED, DROP);
             }
             uint16_t staId = ppdu->GetStaId();
-            if (m_trigVector.GetHeMuUserInfoMap().find(staId) ==
-                    m_trigVector.GetHeMuUserInfoMap().end() ||
-                m_trigVector.GetHeMuUserInfo(staId) != txVector.GetHeMuUserInfo(staId))
+            if (m_trigVector->GetHeMuUserInfoMap().find(staId) ==
+                m_trigVector->GetHeMuUserInfoMap().end())
             {
-                NS_LOG_DEBUG(
-                    "User Info map of TB PPDU being received differs from that of TRIGVECTOR");
+                NS_LOG_DEBUG("TB PPDU received from un unexpected STA ID");
                 return PhyFieldRxStatus(false, FILTERED, DROP);
             }
 
+            NS_ASSERT(txVector.GetGuardInterval() == m_trigVector->GetGuardInterval());
+            NS_ASSERT(txVector.GetMode(staId) == m_trigVector->GetMode(staId));
+            NS_ASSERT(txVector.GetNss(staId) == m_trigVector->GetNss(staId));
+            NS_ASSERT(txVector.GetHeMuUserInfo(staId) == m_trigVector->GetHeMuUserInfo(staId));
+
             m_currentMuPpduUid =
                 ppdu->GetUid(); // to be able to correctly schedule start of OFDMA payload
         }
@@ -706,14 +761,29 @@
             return PhyFieldRxStatus(false, FILTERED, DROP);
         }
     }
-    m_currentMuPpduUid =
-        event->GetPpdu()->GetUid(); // to be able to correctly schedule start of OFDMA payload
+    if (event->GetTxVector().IsDlMu())
+    {
+        // When including a Trigger Frame, a DL MU PPDU solicits a TB PPDU.
+        // NOTE that the 'if' condition above is not needed for HE because SIG-B is only
+        // included in HE MU PPDUs, but it is necessary for EHT to avoid that a non-AP
+        // STA receiving a Trigger Frame sent as an EHT SU transmission (which carries
+        // the EHT-SIG field) stores the PPDU UID and uses it later to schedule the
+        // reception of the OFDMA payload of the TB PPDU (see HePhy::StartReceivePreamble())
+        // despite it lacks the TRIGVECTOR.
+        m_currentMuPpduUid =
+            event->GetPpdu()->GetUid(); // to be able to correctly schedule start of OFDMA payload
+    }
     return status;
 }
 
 bool
 HePhy::IsConfigSupported(Ptr<const WifiPpdu> ppdu) const
 {
+    if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
+    {
+        return true; // evaluated in ProcessSigA
+    }
+
     const WifiTxVector& txVector = ppdu->GetTxVector();
     uint16_t staId = GetStaId(ppdu);
     WifiMode txMode = txVector.GetMode(staId);
@@ -749,7 +819,7 @@
 HePhy::DoStartReceivePayload(Ptr<Event> event)
 {
     NS_LOG_FUNCTION(this << *event);
-    const WifiTxVector& txVector = event->GetTxVector();
+    const auto& txVector = event->GetTxVector();
 
     if (!txVector.IsMu())
     {
@@ -782,8 +852,7 @@
     }
     Time timeToEndRx = payloadDuration + maxOffset;
 
-    bool isAp = (bool)(DynamicCast<ApWifiMac>(m_wifiPhy->GetDevice()->GetMac()));
-    if (!isAp)
+    if (m_wifiPhy->GetDevice()->GetMac()->GetTypeOfStation() != AP)
     {
         NS_LOG_DEBUG("Ignore HE TB PPDU payload received by STA but keep state in Rx");
         NotifyPayloadBegin(txVector, timeToEndRx);
@@ -866,8 +935,8 @@
         if (m_endRxPayloadEvents.empty())
         {
             // We've got the last PPDU of the UL-OFDMA transmission.
-            // Indicate a successfull reception is terminated if at least one HE TB PPDU
-            // has been successfully received, otherwise indicate a unsuccessfull reception is
+            // Indicate a successful reception is terminated if at least one HE TB PPDU
+            // has been successfully received, otherwise indicate a unsuccessful reception is
             // terminated.
             if (m_rxHeTbPpdus > 0)
             {
@@ -891,6 +960,7 @@
 void
 HePhy::StartReceiveOfdmaPayload(Ptr<Event> event)
 {
+    NS_LOG_FUNCTION(this << event);
     Ptr<const WifiPpdu> ppdu = event->GetPpdu();
     const RxPowerWattPerChannelBand& rxPowersW = event->GetRxPowerWPerBand();
     // The total RX power corresponds to the maximum over all the bands.
@@ -952,8 +1022,10 @@
     HeRu::RuSpec ru = txVector.GetRu(staId);
     uint16_t channelWidth = txVector.GetChannelWidth();
     NS_ASSERT(channelWidth <= m_wifiPhy->GetChannelWidth());
-    HeRu::SubcarrierGroup group =
-        HeRu::GetSubcarrierGroup(channelWidth, ru.GetRuType(), ru.GetPhyIndex());
+    HeRu::SubcarrierGroup group = HeRu::GetSubcarrierGroup(
+        channelWidth,
+        ru.GetRuType(),
+        ru.GetPhyIndex(channelWidth, m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
     HeRu::SubcarrierRange range = 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)
@@ -970,8 +1042,10 @@
     HeRu::RuSpec ru = txVector.GetRu(staId);
     uint16_t channelWidth = txVector.GetChannelWidth();
     NS_ASSERT(channelWidth <= m_wifiPhy->GetChannelWidth());
-    HeRu::SubcarrierGroup group =
-        HeRu::GetSubcarrierGroup(channelWidth, ru.GetRuType(), ru.GetPhyIndex());
+    HeRu::SubcarrierGroup group = HeRu::GetSubcarrierGroup(
+        channelWidth,
+        ru.GetRuType(),
+        ru.GetPhyIndex(channelWidth, m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
     HeRu::SubcarrierRange range = 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
@@ -996,11 +1070,12 @@
     // Find the RU that encompasses the non-OFDMA part of the HE TB PPDU for the STA-ID
     HeRu::RuSpec nonOfdmaRu =
         HeRu::FindOverlappingRu(channelWidth, ru, HeRu::GetRuType(nonOfdmaWidth));
-    nonOfdmaRu.SetPhyIndex(channelWidth,
-                           m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20));
 
-    HeRu::SubcarrierGroup groupPreamble =
-        HeRu::GetSubcarrierGroup(channelWidth, nonOfdmaRu.GetRuType(), nonOfdmaRu.GetPhyIndex());
+    HeRu::SubcarrierGroup groupPreamble = HeRu::GetSubcarrierGroup(
+        channelWidth,
+        nonOfdmaRu.GetRuType(),
+        nonOfdmaRu.GetPhyIndex(channelWidth,
+                               m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
     HeRu::SubcarrierRange range =
         std::make_pair(groupPreamble.front().first, groupPreamble.back().second);
     return m_wifiPhy->ConvertHeRuSubcarriers(
@@ -1227,9 +1302,8 @@
 {
     NS_LOG_FUNCTION(this << txVector);
     uint64_t uid;
-    if (txVector.IsUlMu())
+    if (txVector.IsUlMu() || txVector.IsTriggerResponding())
     {
-        NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
         // Use UID of PPDU containing trigger frame to identify resulting HE TB PPDUs, since the
         // latter should immediately follow the former
         uid = m_wifiPhy->GetPreviouslyRxPpduUid();
@@ -1244,13 +1318,23 @@
 }
 
 Ptr<SpectrumValue>
+HePhy::GetTxPowerSpectralDensity(double txPowerW, Ptr<const WifiPpdu> ppdu) const
+{
+    auto hePpdu = DynamicCast<const HePpdu>(ppdu);
+    NS_ASSERT(hePpdu);
+    HePpdu::TxPsdFlag flag = hePpdu->GetTxPsdFlag();
+    return GetTxPowerSpectralDensity(txPowerW, ppdu, flag);
+}
+
+Ptr<SpectrumValue>
 HePhy::GetTxPowerSpectralDensity(double txPowerW,
                                  Ptr<const WifiPpdu> ppdu,
-                                 const WifiTxVector& txVector) const
+                                 HePpdu::TxPsdFlag flag) const
 {
+    const auto& txVector = ppdu->GetTxVector();
     uint16_t centerFrequency = GetCenterFrequencyForChannelWidth(txVector);
     uint16_t channelWidth = txVector.GetChannelWidth();
-    NS_LOG_FUNCTION(this << centerFrequency << channelWidth << txPowerW);
+    NS_LOG_FUNCTION(this << centerFrequency << channelWidth << txPowerW << txVector);
     const auto& puncturedSubchannels = txVector.GetInactiveSubchannels();
     if (!puncturedSubchannels.empty())
     {
@@ -1261,9 +1345,6 @@
         NS_ASSERT(
             !puncturedSubchannels.at(p20IndexInBitmap)); // the primary channel cannot be punctured
     }
-    auto hePpdu = DynamicCast<const HePpdu>(ppdu);
-    NS_ASSERT(hePpdu);
-    HePpdu::TxPsdFlag flag = hePpdu->GetTxPsdFlag();
     const auto& txMaskRejectionParams = GetTxMaskRejectionParams();
     switch (ppdu->GetType())
     {
@@ -1271,7 +1352,7 @@
         if (flag == HePpdu::PSD_NON_HE_PORTION)
         {
             // non-OFDMA portion is sent only on the 20 MHz channels covering the RU
-            const uint16_t staId = GetStaId(hePpdu);
+            const uint16_t staId = GetStaId(ppdu);
             centerFrequency = GetCenterFrequencyForNonOfdmaPart(txVector, staId);
             const uint16_t ruWidth = HeRu::GetBandwidth(txVector.GetRu(staId).GetRuType());
             channelWidth = (ruWidth < 20) ? 20 : ruWidth;
@@ -1287,10 +1368,7 @@
         }
         else
         {
-            const auto band =
-                GetRuBandForTx(ppdu->GetTxVector(),
-                               GetStaId(hePpdu)); // Use TXVECTOR from PPDU since the one passed by
-                                                  // the MAC does not have PHY index set
+            const auto band = GetRuBandForTx(txVector, GetStaId(ppdu));
             return WifiSpectrumValueHelper::CreateHeMuOfdmTxPowerSpectralDensity(
                 centerFrequency,
                 channelWidth,
@@ -1355,47 +1433,84 @@
         // Obtain the index of the non-OFDMA portion
         HeRu::RuSpec nonOfdmaRu =
             HeRu::FindOverlappingRu(currentWidth, ru, HeRu::GetRuType(nonOfdmaWidth));
-        nonOfdmaRu.SetPhyIndex(currentWidth,
-                               m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20));
 
         uint16_t startingFrequency = centerFrequency - (currentWidth / 2);
         centerFrequency =
-            startingFrequency + nonOfdmaWidth * (nonOfdmaRu.GetPhyIndex() - 1) + nonOfdmaWidth / 2;
+            startingFrequency +
+            nonOfdmaWidth * (nonOfdmaRu.GetPhyIndex(
+                                 currentWidth,
+                                 m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)) -
+                             1) +
+            nonOfdmaWidth / 2;
     }
     return centerFrequency;
 }
 
 void
-HePhy::StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector)
+HePhy::StartTx(Ptr<const WifiPpdu> ppdu)
 {
     NS_LOG_FUNCTION(this << ppdu);
+    const auto& txVector = ppdu->GetTxVector();
+    if (auto mac = m_wifiPhy->GetDevice()->GetMac(); mac && (mac->GetTypeOfStation() == AP))
+    {
+        m_currentTxVector = txVector;
+    }
     if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU || ppdu->GetType() == WIFI_PPDU_TYPE_DL_MU)
     {
+        auto nonOfdmaTxPowerDbm =
+            m_wifiPhy->GetTxPowerForTransmission(ppdu) + m_wifiPhy->GetTxGain();
+
+        // temporarily set WifiPpdu flag to PSD_HE_PORTION for correct calculation of the OFDMA TX
+        // power
+        auto hePpdu = DynamicCast<const HePpdu>(ppdu);
+        NS_ASSERT(hePpdu);
+        hePpdu->SetTxPsdFlag(HePpdu::PSD_HE_PORTION);
+        auto ofdmaTxPowerDbm = m_wifiPhy->GetTxPowerForTransmission(ppdu) + m_wifiPhy->GetTxGain();
+        hePpdu->SetTxPsdFlag(HePpdu::PSD_NON_HE_PORTION);
+
         // non-OFDMA part
-        Time nonOfdmaDuration = ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU
+        auto nonOfdmaDuration = ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU
                                     ? CalculateNonOfdmaDurationForHeTb(txVector)
                                     : CalculateNonOfdmaDurationForHeMu(txVector);
-        Transmit(nonOfdmaDuration, ppdu, txVector, "non-OFDMA transmission");
+        auto nonOfdmaTxPowerSpectrum =
+            GetTxPowerSpectralDensity(DbmToW(nonOfdmaTxPowerDbm), ppdu, HePpdu::PSD_NON_HE_PORTION);
+        Transmit(nonOfdmaDuration,
+                 ppdu,
+                 nonOfdmaTxPowerDbm,
+                 nonOfdmaTxPowerSpectrum,
+                 "non-OFDMA transmission");
 
         // OFDMA part
-        auto hePpdu = DynamicCast<HePpdu>(ppdu->Copy()); // since flag will be modified
-        NS_ASSERT(hePpdu);
-        hePpdu->SetTxPsdFlag(HePpdu::PSD_HE_PORTION);
-        Time ofdmaDuration = ppdu->GetTxDuration() - nonOfdmaDuration;
+        auto ofdmaDuration = ppdu->GetTxDuration() - nonOfdmaDuration;
+        auto ofdmaTxPowerSpectrum =
+            GetTxPowerSpectralDensity(DbmToW(ofdmaTxPowerDbm), ppdu, HePpdu::PSD_HE_PORTION);
         Simulator::Schedule(nonOfdmaDuration,
-                            &PhyEntity::Transmit,
+                            &HePhy::StartTxOfdma,
                             this,
-                            ofdmaDuration,
-                            hePpdu,
-                            txVector,
-                            "OFDMA transmission");
+                            ppdu,
+                            ofdmaTxPowerDbm,
+                            ofdmaTxPowerSpectrum,
+                            ofdmaDuration);
     }
     else
     {
-        PhyEntity::StartTx(ppdu, txVector);
+        PhyEntity::StartTx(ppdu);
     }
 }
 
+void
+HePhy::StartTxOfdma(Ptr<const WifiPpdu> ppdu,
+                    double txPowerDbm,
+                    Ptr<SpectrumValue> txPowerSpectrum,
+                    Time ofdmaDuration)
+{
+    NS_LOG_FUNCTION(this << ppdu << txPowerDbm << ofdmaDuration);
+    auto hePpdu = DynamicCast<const HePpdu>(ppdu);
+    NS_ASSERT(hePpdu);
+    hePpdu->SetTxPsdFlag(HePpdu::PSD_HE_PORTION);
+    Transmit(ofdmaDuration, ppdu, txPowerDbm, txPowerSpectrum, "OFDMA transmission");
+}
+
 Time
 HePhy::CalculateTxDuration(WifiConstPsduMap psduMap,
                            const WifiTxVector& txVector,
@@ -1670,7 +1785,7 @@
 HePhy::GetSigBFieldSize(const WifiTxVector& txVector)
 {
     NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
-    NS_ASSERT(txVector.IsDlMu());
+    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
@@ -1700,6 +1815,64 @@
 
     return commonFieldSize + userSpecificFieldSize;
 }
+
+bool
+HePhy::CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txChannelWidth) const
+{
+    /*
+     * The PHY shall not issue a PHY-RXSTART.indication primitive in response to a PPDU
+     * that does not overlap the primary channel, unless the PHY at an AP receives the
+     * HE TB PPDU solicited by the AP. For the HE TB PPDU solicited by the AP, the PHY
+     * shall issue a PHY-RXSTART.indication primitive for a PPDU received in the primary
+     * or at the secondary 20 MHz channel, the secondary 40 MHz channel, or the secondary
+     * 80 MHz channel.
+     */
+    Ptr<WifiMac> mac = m_wifiPhy->GetDevice() ? m_wifiPhy->GetDevice()->GetMac() : nullptr;
+    if (ppdu->GetTxVector().IsUlMu() && mac && mac->GetTypeOfStation() == AP)
+    {
+        return true;
+    }
+    return PhyEntity::CanStartRx(ppdu, txChannelWidth);
+}
+
+Ptr<const WifiPpdu>
+HePhy::GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu)
+{
+    if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
+    {
+        Ptr<const WifiPpdu> rxPpdu;
+        if ((m_trigVectorExpirationTime.has_value()) &&
+            (Simulator::Now() <= m_trigVectorExpirationTime.value()))
+        {
+            // We only copy if the AP that is expecting a HE TB PPDU, since the content
+            // of the TXVECTOR is reconstructed from the TRIGVECTOR, hence the other RX
+            // PHYs should not have this information.
+            rxPpdu = ppdu->Copy();
+        }
+        else
+        {
+            rxPpdu = ppdu;
+        }
+        auto hePpdu = DynamicCast<const HePpdu>(rxPpdu);
+        NS_ASSERT(hePpdu);
+        hePpdu->UpdateTxVectorForUlMu(m_trigVector);
+        return rxPpdu;
+    }
+    else if (auto txVector = ppdu->GetTxVector();
+             m_currentTxVector.has_value() &&
+             (m_previouslyTxPpduUid == ppdu->GetUid()) &&         // response to a trigger frame
+             (txVector.GetModulationClass() < WIFI_MOD_CLASS_HT)) // PPDU is a non-HT (duplicate)
+    {
+        auto triggerChannelWidth = m_currentTxVector->GetChannelWidth();
+        if (txVector.GetChannelWidth() != triggerChannelWidth)
+        {
+            txVector.SetChannelWidth(triggerChannelWidth);
+            ppdu->UpdateTxVector(txVector);
+        }
+    }
+    return PhyEntity::GetRxPpduFromTxPpdu(ppdu);
+}
+
 } // namespace ns3
 
 namespace
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-phy.h ns-3.38/src/wifi/model/he/he-phy.h
--- ns-3.37/src/wifi/model/he/he-phy.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-phy.h	2023-03-16 20:54:23.856869200 -0700
@@ -22,10 +22,14 @@
 #ifndef HE_PHY_H
 #define HE_PHY_H
 
+#include "he-ppdu.h"
+
 #include "ns3/callback.h"
 #include "ns3/vht-phy.h"
 #include "ns3/wifi-phy-band.h"
 
+#include <optional>
+
 /**
  * \file
  * \ingroup wifi
@@ -102,7 +106,7 @@
     void CancelAllEvents() override;
     uint16_t GetStaId(const Ptr<const WifiPpdu> ppdu) const override;
     uint16_t GetMeasurementChannelWidth(const Ptr<const WifiPpdu> ppdu) const override;
-    void StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector) override;
+    void StartTx(Ptr<const WifiPpdu> ppdu) override;
     Time CalculateTxDuration(WifiConstPsduMap psduMap,
                              const WifiTxVector& txVector,
                              WifiPhyBand band) const override;
@@ -112,6 +116,8 @@
     void NotifyCcaBusy(const Ptr<const WifiPpdu> ppdu,
                        Time duration,
                        WifiChannelListType channelType) override;
+    bool CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txChannelWidth) const override;
+    Ptr<const WifiPpdu> GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu) override;
 
     /**
      * \return the BSS color of this PHY.
@@ -148,14 +154,14 @@
      *
      * \return the duration of the non-OFDMA portion of the HE TB PPDU.
      */
-    Time CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const;
+    virtual Time CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const;
 
     /**
      * \param txVector the transmission parameters used for the HE MU PPDU
      *
      * \return the duration of the non-OFDMA portion of the HE MU PPDU.
      */
-    Time CalculateNonOfdmaDurationForHeMu(const WifiTxVector& txVector) const;
+    virtual Time CalculateNonOfdmaDurationForHeMu(const WifiTxVector& txVector) const;
 
     /**
      * Get the band in the TX spectrum associated with the RU used by the PSDU
@@ -235,7 +241,7 @@
 
     /**
      * Fire a EndOfHeSigA callback (if connected) once HE-SIG-A field has been received.
-     * This method is scheduled immediatly after end of HE-SIG-A, once
+     * This method is scheduled immediately after end of HE-SIG-A, once
      * field processing is finished.
      *
      * \param params the HE-SIG-A parameters
@@ -446,8 +452,7 @@
     void DoAbortCurrentReception(WifiPhyRxfailureReason reason) override;
     uint64_t ObtainNextUid(const WifiTxVector& txVector) override;
     Ptr<SpectrumValue> GetTxPowerSpectralDensity(double txPowerW,
-                                                 Ptr<const WifiPpdu> ppdu,
-                                                 const WifiTxVector& txVector) const override;
+                                                 Ptr<const WifiPpdu> ppdu) const override;
     uint32_t GetMaxPsduSize() const override;
     WifiConstPsduMap GetWifiConstPsduMap(Ptr<const WifiPsdu> psdu,
                                          const WifiTxVector& txVector) const override;
@@ -475,9 +480,9 @@
     virtual PhyFieldRxStatus ProcessSigB(Ptr<Event> event, PhyFieldRxStatus status);
 
     /**
-     * Start receiving the PSDU (i.e. the first symbol of the PSDU has arrived) of an UL-OFDMA
+     * 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 UL MU PPDU.
+     * the MU PPDU.
      *
      * \param event the event holding incoming OFDMA part of the PPDU's information
      */
@@ -518,9 +523,11 @@
         m_beginOfdmaPayloadRxEvents; //!< the beginning of the OFDMA payload reception events
                                      //!< (indexed by STA-ID)
 
-    EndOfHeSigACallback m_endOfHeSigACallback; //!< end of HE-SIG-A callback
-    WifiTxVector m_trigVector;                 //!< the TRIGVECTOR
-    Time m_trigVectorExpirationTime;           //!< expiration time of the TRIGVECTOR
+    EndOfHeSigACallback m_endOfHeSigACallback;      //!< end of HE-SIG-A callback
+    std::optional<WifiTxVector> m_trigVector;       //!< the TRIGVECTOR
+    std::optional<Time> m_trigVectorExpirationTime; //!< expiration time of the TRIGVECTOR
+    std::optional<WifiTxVector> m_currentTxVector;  //!< If the STA is an AP STA, this holds the
+                                                    //!< TXVECTOR of the PPDU that has been sent
 
   private:
     void BuildModeList() override;
@@ -528,6 +535,31 @@
     Time GetSymbolDuration(const WifiTxVector& txVector) const override;
 
     /**
+     * This is a helper function to create the TX PSD of the non-HE and HE portions.
+     *
+     * \param txPowerW power in W to spread across the bands
+     * \param ppdu the PPDU that will be transmitted
+     * \param flag flag indicating whether the PSD is for non-HE portion or HE portion
+     * \return Pointer to SpectrumValue
+     */
+    Ptr<SpectrumValue> GetTxPowerSpectralDensity(double txPowerW,
+                                                 Ptr<const WifiPpdu> ppdu,
+                                                 HePpdu::TxPsdFlag flag) const;
+
+    /**
+     * Start the transmission of the OFDMA part of the MU PPDU.
+     *
+     * \param ppdu the PPDU
+     * \param txPowerDbm the total TX power in dBm
+     * \param txPowerSpectrum the TX PSD
+     * \param ofdmaDuration the duration of the OFDMA part
+     */
+    void StartTxOfdma(Ptr<const WifiPpdu> ppdu,
+                      double txPowerDbm,
+                      Ptr<SpectrumValue> txPowerSpectrum,
+                      Time ofdmaDuration);
+
+    /**
      * Notify PHY state helper to switch to CCA busy state,
      *
      * \param duration the duration of the CCA state
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-ppdu.cc ns-3.38/src/wifi/model/he/he-ppdu.cc
--- ns-3.37/src/wifi/model/he/he-ppdu.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-ppdu.cc	2023-03-16 20:54:23.856869200 -0700
@@ -54,8 +54,7 @@
                Time ppduDuration,
                WifiPhyBand band,
                uint64_t uid,
-               TxPsdFlag flag,
-               uint8_t p20Index)
+               TxPsdFlag flag)
     : OfdmPpdu(psdus.begin()->second,
                txVector,
                txCenterFreq,
@@ -72,10 +71,8 @@
     m_psdus = psdus;
     if (txVector.IsMu())
     {
-        for (auto heMuUserInfo : txVector.GetHeMuUserInfoMap())
+        for (const auto& heMuUserInfo : txVector.GetHeMuUserInfoMap())
         {
-            // Set RU PHY index
-            heMuUserInfo.second.ru.SetPhyIndex(txVector.GetChannelWidth(), p20Index);
             auto [it, ret] = m_muUserInfos.emplace(heMuUserInfo);
             NS_ABORT_MSG_IF(!ret, "STA-ID " << heMuUserInfo.first << " already present");
         }
@@ -105,14 +102,29 @@
     SetTxPsdFlag(PSD_NON_HE_PORTION);
 }
 
-HePpdu::~HePpdu()
+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
 }
 
 void
-HePpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
+HePpdu::SetLSigHeader(LSigHeader& lSig, Time ppduDuration) const
 {
-    NS_LOG_FUNCTION(this << txVector << ppduDuration);
     uint8_t sigExtension = 0;
     if (m_band == WIFI_PHY_BAND_2_4GHZ)
     {
@@ -125,20 +137,28 @@
                              4.0) *
                         3) -
                        3 - m);
-    m_lSig.SetLength(length);
-    if (txVector.IsDlMu())
+    lSig.SetLength(length);
+}
+
+void
+HePpdu::SetHeSigHeader(HeSigHeader& heSig, const WifiTxVector& txVector) const
+{
+    if (ns3::IsDlMu(m_preamble))
+    {
+        heSig.SetMuFlag(true);
+        heSig.SetMcs(txVector.GetSigBMode().GetMcsValue());
+    }
+    else if (!ns3::IsUlMu(m_preamble))
     {
-        m_heSig.SetMuFlag(true);
-        m_heSig.SetMcs(txVector.GetSigBMode().GetMcsValue());
+        heSig.SetMcs(txVector.GetMode().GetMcsValue());
+        heSig.SetNStreams(txVector.GetNss());
     }
-    else if (!txVector.IsUlMu())
+    heSig.SetBssColor(txVector.GetBssColor());
+    heSig.SetChannelWidth(txVector.GetChannelWidth());
+    if (!txVector.IsUlMu())
     {
-        m_heSig.SetMcs(txVector.GetMode().GetMcsValue());
-        m_heSig.SetNStreams(txVector.GetNss());
+        heSig.SetGuardIntervalAndLtfSize(txVector.GetGuardInterval(), 2 /*NLTF currently unused*/);
     }
-    m_heSig.SetBssColor(txVector.GetBssColor());
-    m_heSig.SetChannelWidth(m_channelWidth);
-    m_heSig.SetGuardIntervalAndLtfSize(txVector.GetGuardInterval(), 2 /*NLTF currently unused*/);
 }
 
 WifiTxVector
@@ -146,23 +166,60 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-    txVector.SetMode(HePhy::GetHeMcs(m_heSig.GetMcs()));
-    txVector.SetChannelWidth(m_heSig.GetChannelWidth());
-    txVector.SetNss(m_heSig.GetNStreams());
-    txVector.SetGuardInterval(m_heSig.GetGuardInterval());
-    txVector.SetBssColor(m_heSig.GetBssColor());
-    txVector.SetLength(m_lSig.GetLength());
+
+#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
+
+    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());
     txVector.SetAggregation(m_psdus.size() > 1 || m_psdus.begin()->second->IsAggregate());
-    for (const auto& muUserInfo : m_muUserInfos)
+    if (!IsMu())
+    {
+        txVector.SetMode(HePhy::GetHeMcs(heSig.GetMcs()));
+        txVector.SetNss(heSig.GetNStreams());
+    }
+    if (!IsUlMu())
     {
-        txVector.SetHeMuUserInfo(muUserInfo.first, muUserInfo.second);
+        txVector.SetGuardInterval(heSig.GetGuardInterval());
+    }
+    if (IsDlMu())
+    {
+        for (const auto& muUserInfo : m_muUserInfos)
+        {
+            txVector.SetHeMuUserInfo(muUserInfo.first, muUserInfo.second);
+        }
     }
     if (txVector.IsDlMu())
     {
-        txVector.SetSigBMode(HePhy::GetVhtMcs(m_heSig.GetMcs()));
+        txVector.SetSigBMode(HePhy::GetVhtMcs(heSig.GetMcs()));
         txVector.SetRuAllocation(m_ruAllocation);
     }
-    return txVector;
 }
 
 Time
@@ -170,6 +227,16 @@
 {
     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;
@@ -179,8 +246,8 @@
     }
     uint8_t m = IsDlMu() ? 1 : 2;
     // Equation 27-11 of IEEE P802.11ax/D4.0
-    Time calculatedDuration = MicroSeconds(
-        ((ceil(static_cast<double>(m_lSig.GetLength() + 3 + m) / 3)) * 4) + 20 + sigExtension);
+    Time 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() -
@@ -193,7 +260,7 @@
 Ptr<WifiPpdu>
 HePpdu::Copy() const
 {
-    return ns3::Copy(Ptr(this));
+    return Ptr<WifiPpdu>(new HePpdu(*this), false);
 }
 
 WifiPpduType
@@ -236,20 +303,33 @@
         NS_ASSERT(m_psdus.size() == 1);
         return m_psdus.at(SU_STA_ID);
     }
-    else if (IsUlMu())
+
+    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())
     {
         NS_ASSERT(m_psdus.size() == 1);
-        if (bssColor == 0 || m_heSig.GetBssColor() == 0 || (bssColor == m_heSig.GetBssColor()))
+        if (bssColor == 0 || ppduBssColor == 0 || (bssColor == ppduBssColor))
         {
-            return m_psdus.begin()->second;
+            return m_psdus.cbegin()->second;
         }
     }
     else
     {
-        if (bssColor == 0 || m_heSig.GetBssColor() == 0 || (bssColor == m_heSig.GetBssColor()))
+        if (bssColor == 0 || ppduBssColor == 0 || (bssColor == ppduBssColor))
         {
-            auto it = m_psdus.find(staId);
-            if (it != m_psdus.end())
+            const auto it = m_psdus.find(staId);
+            if (it != m_psdus.cend())
             {
                 return it->second;
             }
@@ -268,8 +348,8 @@
 uint16_t
 HePpdu::GetTransmissionChannelWidth() const
 {
-    WifiTxVector txVector = GetTxVector();
-    if (txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB && GetStaId() != SU_STA_ID)
+    const WifiTxVector& txVector = GetTxVector();
+    if (txVector.IsUlMu() && GetStaId() != SU_STA_ID)
     {
         TxPsdFlag flag = GetTxPsdFlag();
         uint16_t ruWidth = HeRu::GetBandwidth(txVector.GetRu(GetStaId()).GetRuType());
@@ -284,18 +364,6 @@
     }
 }
 
-bool
-HePpdu::CanBeReceived(uint16_t p20MinFreq, uint16_t p20MaxFreq) const
-{
-    NS_LOG_FUNCTION(this << p20MinFreq << p20MaxFreq);
-    if (IsUlMu())
-    {
-        // APs are able to receive TB PPDUs sent on a band other than the primary20 channel
-        return true;
-    }
-    return OfdmPpdu::CanBeReceived(p20MinFreq, p20MaxFreq);
-}
-
 HePpdu::TxPsdFlag
 HePpdu::GetTxPsdFlag() const
 {
@@ -303,12 +371,48 @@
 }
 
 void
-HePpdu::SetTxPsdFlag(TxPsdFlag flag)
+HePpdu::SetTxPsdFlag(TxPsdFlag flag) const
 {
     NS_LOG_FUNCTION(this << flag);
     m_txPsdFlag = flag;
 }
 
+void
+HePpdu::UpdateTxVectorForUlMu(const std::optional<WifiTxVector>& trigVector) const
+{
+    if (trigVector.has_value())
+    {
+        NS_LOG_FUNCTION(this << trigVector.value());
+    }
+    else
+    {
+        NS_LOG_FUNCTION(this);
+    }
+    if (!m_txVector.has_value())
+    {
+        m_txVector = GetTxVector();
+    }
+    NS_ASSERT(GetModulation() >= WIFI_MOD_CLASS_HE);
+    NS_ASSERT(GetType() == WIFI_PPDU_TYPE_UL_MU);
+    // HE TB PPDU reception needs information from the TRIGVECTOR to be able to receive the PPDU
+    const auto staId = GetStaId();
+    if (trigVector.has_value() && trigVector->IsUlMu() &&
+        (trigVector->GetHeMuUserInfoMap().count(staId) > 0))
+    {
+        // These information are not carried in HE-SIG-A for a HE TB PPDU,
+        // but they are carried in the Trigger frame soliciting the HE TB PPDU
+        m_txVector->SetGuardInterval(trigVector->GetGuardInterval());
+        m_txVector->SetHeMuUserInfo(staId, trigVector->GetHeMuUserInfo(staId));
+    }
+    else
+    {
+        // Set dummy user info, PPDU will be dropped later after decoding PHY headers.
+        m_txVector->SetHeMuUserInfo(
+            staId,
+            {{HeRu::GetRuType(m_txVector->GetChannelWidth()), 1, true}, 0, 1});
+    }
+}
+
 bool
 HePpdu::IsAllocated(uint16_t staId) const
 {
@@ -353,10 +457,6 @@
 {
 }
 
-HePpdu::HeSigHeader::~HeSigHeader()
-{
-}
-
 TypeId
 HePpdu::HeSigHeader::GetTypeId()
 {
@@ -402,7 +502,7 @@
 void
 HePpdu::HeSigHeader::SetMcs(uint8_t mcs)
 {
-    NS_ASSERT(mcs <= 11);
+    // NS_ASSERT (mcs <= 11); // TODO: reactivate once EHT PHY headers are implemented
     m_mcs = mcs;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-ppdu.h ns-3.38/src/wifi/model/he/he-ppdu.h
--- ns-3.37/src/wifi/model/he/he-ppdu.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-ppdu.h	2023-03-16 20:54:23.856869200 -0700
@@ -51,7 +51,6 @@
     {
       public:
         HeSigHeader();
-        ~HeSigHeader() override;
 
         /**
          * \brief Get the type ID.
@@ -187,7 +186,6 @@
      * \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
-     * \param p20Index the index of the primary 20 MHz channel
      */
     HePpdu(const WifiConstPsduMap& psdus,
            const WifiTxVector& txVector,
@@ -195,19 +193,13 @@
            Time ppduDuration,
            WifiPhyBand band,
            uint64_t uid,
-           TxPsdFlag flag,
-           uint8_t p20Index);
-    /**
-     * Destructor for HePpdu.
-     */
-    ~HePpdu() override;
+           TxPsdFlag flag);
 
     Time GetTxDuration() const override;
     Ptr<WifiPpdu> Copy() const override;
     WifiPpduType GetType() const override;
     uint16_t GetStaId() const override;
     uint16_t GetTransmissionChannelWidth() const override;
-    bool CanBeReceived(uint16_t p20MinFreq, uint16_t p20MaxFreq) const override;
 
     /**
      * Get the payload of the PPDU.
@@ -230,7 +222,16 @@
      *
      * \see TxPsdFlag
      */
-    void SetTxPsdFlag(TxPsdFlag flag);
+    void SetTxPsdFlag(TxPsdFlag flag) const;
+
+    /**
+     * Update the TXVECTOR for HE TB PPDUs, since the information to decode HE TB PPDUs
+     * is not available from the PHY headers but it requires information from the TRIGVECTOR
+     * of the AP expecting these HE TB PPDUs.
+     *
+     * \param trigVector the TRIGVECTOR or std::nullopt if no TRIGVECTOR is available at the caller
+     */
+    void UpdateTxVectorForUlMu(const std::optional<WifiTxVector>& trigVector) const;
 
     /**
      * Check if STA ID is in HE SIG-B Content Channel ID
@@ -248,6 +249,31 @@
     bool IsAllocated(uint16_t staId) const;
 
   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;
+
+#ifndef NS3_BUILD_PROFILE_DEBUG
+    HeSigHeader m_heSig; //!< the HE-SIG PHY header
+#endif
+    mutable TxPsdFlag m_txPsdFlag; //!< the transmit power spectral density flag
+
+    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)
+
+  private:
     std::string PrintPayload() const override;
     WifiTxVector DoGetTxVector() const override;
 
@@ -256,11 +282,13 @@
      * \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
@@ -268,24 +296,29 @@
     virtual bool IsUlMu() const;
 
     /**
-     * Fill in the HE PHY headers.
+     * Fill in the PHY headers.
      *
      * \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);
 
-    HeSigHeader m_heSig;   //!< the HE-SIG PHY header
-    TxPsdFlag m_txPsdFlag; //!< the transmit power spectral density flag
+    /**
+     * 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;
 
-    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)
-};                               // class HePpdu
+    /**
+     * 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
 
 /**
  * \brief Stream insertion operator.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-ru.cc ns-3.38/src/wifi/model/he/he-ru.cc
--- ns-3.37/src/wifi/model/he/he-ru.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-ru.cc	2023-03-16 20:54:23.856869200 -0700
@@ -159,7 +159,8 @@
     {{80, HeRu::RU_484_TONE},
      {/* 1 */ {{-500, -17}},
       /* 2 */ {{17, 500}}}},
-    {{80, HeRu::RU_996_TONE}, {/* 1 */ {{-500, -3}, {3, 500}}}}};
+    {{80, HeRu::RU_996_TONE}, {/* 1 */ {{-500, -3}, {3, 500}}}},
+};
 
 // Table 27-26 IEEE802.11ax-2021
 const HeRu::RuAllocationMap HeRu::m_heRuAllocations = {
@@ -425,8 +426,7 @@
 HeRu::RuSpec::RuSpec(RuType ruType, std::size_t index, bool primary80MHz)
     : m_ruType(ruType),
       m_index(index),
-      m_primary80MHz(primary80MHz),
-      m_phyIndex(0)
+      m_primary80MHz(primary80MHz)
 {
     NS_ABORT_MSG_IF(index == 0, "Index cannot be zero");
 }
@@ -452,35 +452,22 @@
     return m_primary80MHz;
 }
 
-void
-HeRu::RuSpec::SetPhyIndex(uint16_t bw, uint8_t p20Index)
+std::size_t
+HeRu::RuSpec::GetPhyIndex(uint16_t bw, uint8_t p20Index) const
 {
     bool primary80IsLower80 = (p20Index < bw / 40);
 
     if (bw < 160 || m_ruType == HeRu::RU_2x996_TONE || (primary80IsLower80 && m_primary80MHz) ||
         (!primary80IsLower80 && !m_primary80MHz))
     {
-        m_phyIndex = m_index;
+        return m_index;
     }
     else
     {
-        m_phyIndex = m_index + GetNRus(bw, m_ruType) / 2;
+        return m_index + GetNRus(bw, m_ruType) / 2;
     }
 }
 
-bool
-HeRu::RuSpec::IsPhyIndexSet() const
-{
-    return (m_phyIndex != 0);
-}
-
-std::size_t
-HeRu::RuSpec::GetPhyIndex() const
-{
-    NS_ABORT_MSG_IF(m_phyIndex == 0, "RU PHY index not set");
-    return m_phyIndex;
-}
-
 std::size_t
 HeRu::GetNRus(uint16_t bw, RuType ruType)
 {
@@ -653,7 +640,7 @@
 }
 
 bool
-HeRu::DoesOverlap(uint16_t bw, RuSpec ru, const SubcarrierGroup& toneRanges)
+HeRu::DoesOverlap(uint16_t bw, RuSpec ru, const SubcarrierGroup& toneRanges, uint8_t p20Index)
 {
     for (const auto& range : toneRanges)
     {
@@ -662,7 +649,8 @@
             return true;
         }
 
-        SubcarrierGroup rangesRu = GetSubcarrierGroup(bw, ru.GetRuType(), ru.GetPhyIndex());
+        SubcarrierGroup rangesRu =
+            GetSubcarrierGroup(bw, ru.GetRuType(), ru.GetPhyIndex(bw, p20Index));
         for (auto& r : rangesRu)
         {
             if (range.second >= r.first && r.second >= range.first)
@@ -748,10 +736,6 @@
 {
     os << "RU{" << ru.GetRuType() << "/" << ru.GetIndex() << "/"
        << (ru.GetPrimary80MHz() ? "primary80MHz" : "secondary80MHz");
-    if (ru.IsPhyIndexSet())
-    {
-        os << "[" << ru.GetPhyIndex() << "]";
-    }
     os << "}";
     return os;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/he-ru.h ns-3.38/src/wifi/model/he/he-ru.h
--- ns-3.37/src/wifi/model/he/he-ru.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/he-ru.h	2023-03-16 20:54:23.856869200 -0700
@@ -97,24 +97,13 @@
          */
         bool GetPrimary80MHz() const;
         /**
-         * Set the RU PHY index
+         * Get the RU PHY index
          *
          * \param bw the width of the channel of which the RU is part (in MHz)
          * \param p20Index the index of the primary20 channel
-         */
-        void SetPhyIndex(uint16_t bw, uint8_t p20Index);
-        /**
-         * Return true if the RU PHY index has been set, false otherwise
-         *
-         * \return true if the RU PHY index has been set, false otherwise
-         */
-        bool IsPhyIndexSet() const;
-        /**
-         * Get the RU PHY index
-         *
          * \return the RU PHY index
          */
-        std::size_t GetPhyIndex() const;
+        std::size_t GetPhyIndex(uint16_t bw, uint8_t p20Index) const;
 
         /**
          * Compare this RU to the given RU.
@@ -132,14 +121,10 @@
         bool operator!=(const RuSpec& other) const;
 
       private:
-        RuType m_ruType;        //!< RU type
-        std::size_t m_index;    /**< RU index (starting at 1) as defined by Tables 27-7
-                                     to 27-9 of 802.11ax D8.0 */
-        bool m_primary80MHz;    //!< true if the RU is allocated in the primary 80MHz channel
-        std::size_t m_phyIndex; /**< the RU PHY index, which is used to indicate whether an
-                                     RU is located in the lower half or the higher half of
-                                     a 160MHz channel. For channel widths less than 160MHz,
-                                     the RU PHY index equals the RU index */
+        RuType m_ruType;     //!< RU type
+        std::size_t m_index; /**< RU index (starting at 1) as defined by Tables 27-7
+                                  to 27-9 of 802.11ax D8.0 */
+        bool m_primary80MHz; //!< true if the RU is allocated in the primary 80MHz channel
     };
 
     /**
@@ -208,9 +193,13 @@
      * \param bw the bandwidth (MHz) of the HE PPDU (20, 40, 80, 160)
      * \param ru the given RU allocation
      * \param toneRanges the given set of tone ranges
+     * \param p20Index the index of the primary20 channel
      * \return true if the given RU overlaps with the given set of tone ranges.
      */
-    static bool DoesOverlap(uint16_t bw, RuSpec ru, const SubcarrierGroup& toneRanges);
+    static bool DoesOverlap(uint16_t bw,
+                            RuSpec ru,
+                            const SubcarrierGroup& toneRanges,
+                            uint8_t p20Index);
 
     /**
      * Find the RU allocation of the given RU type overlapping the given
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/multi-user-scheduler.cc ns-3.38/src/wifi/model/he/multi-user-scheduler.cc
--- ns-3.37/src/wifi/model/he/multi-user-scheduler.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/multi-user-scheduler.cc	2023-03-16 20:54:23.856869200 -0700
@@ -92,11 +92,8 @@
 {
     NS_LOG_FUNCTION(this);
     m_apMac = nullptr;
-    m_heFem = nullptr;
     m_edca = nullptr;
-    m_dlInfo.psduMap.clear();
-    m_dlInfo.txParams.Clear();
-    m_ulInfo.txParams.Clear();
+    m_lastTxInfo.clear();
     m_accessReqTimer.Cancel();
     Object::DoDispose();
 }
@@ -131,6 +128,19 @@
 }
 
 void
+MultiUserScheduler::SetAccessReqInterval(Time interval)
+{
+    NS_LOG_FUNCTION(this << interval.As(Time::MS));
+    m_accessReqInterval = interval;
+    // start the timer if past initialization
+    if (m_accessReqInterval.IsStrictlyPositive() && IsInitialized())
+    {
+        m_accessReqTimer =
+            Simulator::Schedule(m_accessReqInterval, &MultiUserScheduler::AccessReqTimeout, this);
+    }
+}
+
+void
 MultiUserScheduler::SetWifiMac(Ptr<ApWifiMac> mac)
 {
     NS_LOG_FUNCTION(this << mac);
@@ -141,14 +151,24 @@
     NS_ABORT_MSG_IF(!m_apMac || !m_apMac->GetHeConfiguration(),
                     "MultiUserScheduler can only be installed on HE APs");
 
-    m_heFem = DynamicCast<HeFrameExchangeManager>(m_apMac->GetFrameExchangeManager());
-    m_heFem->SetMultiUserScheduler(this);
+    for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+    {
+        auto heFem = DynamicCast<HeFrameExchangeManager>(m_apMac->GetFrameExchangeManager(linkId));
+        NS_ASSERT(heFem);
+        heFem->SetMultiUserScheduler(this);
+    }
 }
 
 Ptr<WifiRemoteStationManager>
-MultiUserScheduler::GetWifiRemoteStationManager() const
+MultiUserScheduler::GetWifiRemoteStationManager(uint8_t linkId) const
+{
+    return m_apMac->GetWifiRemoteStationManager(linkId);
+}
+
+Ptr<HeFrameExchangeManager>
+MultiUserScheduler::GetHeFem(uint8_t linkId) const
 {
-    return m_apMac->GetWifiRemoteStationManager();
+    return StaticCast<HeFrameExchangeManager>(m_apMac->GetFrameExchangeManager(linkId));
 }
 
 void
@@ -159,92 +179,108 @@
     // request channel access if not requested yet
     auto edca = m_apMac->GetQosTxop(m_accessReqAc);
 
-    if (edca->GetAccessStatus(SINGLE_LINK_OP_ID) == Txop::NOT_REQUESTED)
+    for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
     {
-        m_apMac->GetChannelAccessManager()->RequestAccess(edca);
+        if (edca->GetAccessStatus(linkId) == Txop::NOT_REQUESTED)
+        {
+            m_apMac->GetChannelAccessManager(linkId)->RequestAccess(edca);
+        }
     }
 
     // restart timer
-    m_accessReqTimer =
-        Simulator::Schedule(m_accessReqInterval, &MultiUserScheduler::AccessReqTimeout, this);
+    if (m_accessReqInterval.IsStrictlyPositive())
+    {
+        m_accessReqTimer =
+            Simulator::Schedule(m_accessReqInterval, &MultiUserScheduler::AccessReqTimeout, this);
+    }
 }
 
 MultiUserScheduler::TxFormat
 MultiUserScheduler::NotifyAccessGranted(Ptr<QosTxop> edca,
                                         Time availableTime,
                                         bool initialFrame,
-                                        uint16_t allowedWidth)
+                                        uint16_t allowedWidth,
+                                        uint8_t linkId)
 {
-    NS_LOG_FUNCTION(this << edca << availableTime << initialFrame << allowedWidth);
+    NS_LOG_FUNCTION(this << edca << availableTime << initialFrame << allowedWidth << linkId);
 
     m_edca = edca;
     m_availableTime = availableTime;
     m_initialFrame = initialFrame;
     m_allowedWidth = allowedWidth;
+    m_linkId = linkId;
 
     if (m_accessReqTimer.IsRunning() && m_restartTimerUponAccess)
     {
         // restart access timer
         m_accessReqTimer.Cancel();
-        m_accessReqTimer =
-            Simulator::Schedule(m_accessReqInterval, &MultiUserScheduler::AccessReqTimeout, this);
+        if (m_accessReqInterval.IsStrictlyPositive())
+        {
+            m_accessReqTimer = Simulator::Schedule(m_accessReqInterval,
+                                                   &MultiUserScheduler::AccessReqTimeout,
+                                                   this);
+        }
     }
 
     TxFormat txFormat = SelectTxFormat();
 
     if (txFormat == DL_MU_TX)
     {
-        m_dlInfo = ComputeDlMuInfo();
+        m_lastTxInfo[linkId].dlInfo = ComputeDlMuInfo();
     }
     else if (txFormat == UL_MU_TX)
     {
-        NS_ABORT_MSG_IF(!m_heFem, "UL MU PPDUs are only supported by HE APs");
-        m_ulInfo = ComputeUlMuInfo();
+        m_lastTxInfo[linkId].ulInfo = ComputeUlMuInfo();
         CheckTriggerFrame();
     }
 
     if (txFormat != NO_TX)
     {
-        m_lastTxFormat = txFormat;
+        m_lastTxInfo[linkId].lastTxFormat = txFormat;
     }
     return txFormat;
 }
 
 MultiUserScheduler::TxFormat
-MultiUserScheduler::GetLastTxFormat() const
+MultiUserScheduler::GetLastTxFormat(uint8_t linkId)
 {
-    return m_lastTxFormat;
+    return m_lastTxInfo[linkId].lastTxFormat;
 }
 
 MultiUserScheduler::DlMuInfo&
-MultiUserScheduler::GetDlMuInfo()
+MultiUserScheduler::GetDlMuInfo(uint8_t linkId)
 {
-    NS_ABORT_MSG_IF(m_lastTxFormat != DL_MU_TX, "Next transmission is not DL MU");
+    NS_ABORT_MSG_IF(m_lastTxInfo[linkId].lastTxFormat != DL_MU_TX,
+                    "Next transmission is not DL MU");
 
 #ifdef NS3_BUILD_PROFILE_DEBUG
     // check that all the addressed stations support HE
-    for (auto& psdu : m_dlInfo.psduMap)
+    for (auto& psdu : m_lastTxInfo[linkId].dlInfo.psduMap)
     {
-        NS_ABORT_MSG_IF(!GetWifiRemoteStationManager()->GetHeSupported(psdu.second->GetAddr1()),
+        auto receiver = psdu.second->GetAddr1();
+        auto linkId = m_apMac->IsAssociated(receiver);
+        NS_ABORT_MSG_IF(!linkId, "Station " << receiver << " should be associated");
+        NS_ABORT_MSG_IF(!GetWifiRemoteStationManager(*linkId)->GetHeSupported(receiver),
                         "Station " << psdu.second->GetAddr1() << " does not support HE");
     }
 #endif
 
-    return m_dlInfo;
+    return m_lastTxInfo[linkId].dlInfo;
 }
 
 MultiUserScheduler::UlMuInfo&
-MultiUserScheduler::GetUlMuInfo()
+MultiUserScheduler::GetUlMuInfo(uint8_t linkId)
 {
-    NS_ABORT_MSG_IF(m_lastTxFormat != UL_MU_TX, "Next transmission is not UL MU");
+    NS_ABORT_MSG_IF(m_lastTxInfo[linkId].lastTxFormat != UL_MU_TX,
+                    "Next transmission is not UL MU");
 
-    return m_ulInfo;
+    return m_lastTxInfo[linkId].ulInfo;
 }
 
 Ptr<WifiMpdu>
-MultiUserScheduler::GetTriggerFrame(const CtrlTriggerHeader& trigger) const
+MultiUserScheduler::GetTriggerFrame(const CtrlTriggerHeader& trigger, uint8_t linkId) const
 {
-    NS_LOG_FUNCTION(this);
+    NS_LOG_FUNCTION(this << linkId);
 
     Ptr<Packet> packet = Create<Packet>();
     packet->AddHeader(trigger);
@@ -253,14 +289,14 @@
     if (trigger.GetNUserInfoFields() == 1)
     {
         auto aid = trigger.begin()->GetAid12();
-        auto aidAddrMapIt = m_apMac->GetStaList().find(aid);
-        NS_ASSERT(aidAddrMapIt != m_apMac->GetStaList().end());
+        auto aidAddrMapIt = m_apMac->GetStaList(linkId).find(aid);
+        NS_ASSERT(aidAddrMapIt != m_apMac->GetStaList(linkId).end());
         receiver = aidAddrMapIt->second;
     }
 
     WifiMacHeader hdr(WIFI_MAC_CTL_TRIGGER);
     hdr.SetAddr1(receiver);
-    hdr.SetAddr2(m_apMac->GetAddress());
+    hdr.SetAddr2(GetHeFem(linkId)->GetAddress());
     hdr.SetDsNotTo();
     hdr.SetDsNotFrom();
 
@@ -274,9 +310,10 @@
 
     // Set the CS Required subfield to true, unless the UL Length subfield is less
     // than or equal to 76 (see Section 26.5.2.5 of 802.11ax-2021)
-    m_ulInfo.trigger.SetCsRequired(m_ulInfo.trigger.GetUlLength() > 76);
+    m_lastTxInfo[m_linkId].ulInfo.trigger.SetCsRequired(
+        m_lastTxInfo[m_linkId].ulInfo.trigger.GetUlLength() > 76);
 
-    m_heFem->SetTargetRssi(m_ulInfo.trigger);
+    GetHeFem(m_linkId)->SetTargetRssi(m_lastTxInfo[m_linkId].ulInfo.trigger);
 }
 
 uint32_t
@@ -287,12 +324,13 @@
     uint8_t maxNTids = 0;
     for (const auto& userInfo : trigger)
     {
-        const auto staIt = m_apMac->GetStaList().find(userInfo.GetAid12());
-        NS_ASSERT(staIt != m_apMac->GetStaList().cend());
+        auto address = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
+        NS_ASSERT_MSG(address, "AID " << userInfo.GetAid12() << " not found");
+
         uint8_t staNTids = 0;
         for (uint8_t tid = 0; tid < 8; tid++)
         {
-            if (m_heFem->GetBaAgreementEstablished(staIt->second, tid))
+            if (m_apMac->GetBaAgreementEstablishedAsRecipient(*address, tid))
             {
                 staNTids++;
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/multi-user-scheduler.h ns-3.38/src/wifi/model/he/multi-user-scheduler.h
--- ns-3.37/src/wifi/model/he/multi-user-scheduler.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/multi-user-scheduler.h	2023-03-16 20:54:23.856869200 -0700
@@ -94,52 +94,76 @@
      *                     of the TXOP. This is used to determine whether the TXOP
      *                     limit can be exceeded
      * \param allowedWidth the allowed width in MHz for the next transmission
+     * \param linkId the ID of the link over which channel access was gained
      * \return the format of the next transmission
      */
     TxFormat NotifyAccessGranted(Ptr<QosTxop> edca,
                                  Time availableTime,
                                  bool initialFrame,
-                                 uint16_t allowedWidth);
+                                 uint16_t allowedWidth,
+                                 uint8_t linkId);
 
     /**
-     * Get the information required to perform a DL MU transmission. Note
-     * that this method can only be called if GetTxFormat returns DL_MU_TX.
+     * Get the information required to perform a DL MU transmission on the given link. Note
+     * that this method can only be called if GetTxFormat returns DL_MU_TX on the given link.
      *
+     * \param linkId the ID of the given link
      * \return the information required to perform a DL MU transmission
      */
-    DlMuInfo& GetDlMuInfo();
+    DlMuInfo& GetDlMuInfo(uint8_t linkId);
 
     /**
-     * Get the information required to solicit an UL MU transmission. Note
-     * that this method can only be called if GetTxFormat returns UL_MU_TX.
+     * Get the information required to solicit an UL MU transmission on the given link. Note
+     * that this method can only be called if GetTxFormat returns UL_MU_TX on the given link.
      *
+     * \param linkId the ID of the given link
      * \return the information required to solicit an UL MU transmission
      */
-    UlMuInfo& GetUlMuInfo();
+    UlMuInfo& GetUlMuInfo(uint8_t linkId);
+
+    /**
+     * Set the duration of the interval between two consecutive requests for channel
+     * access made by the MultiUserScheduler.
+     *
+     * \param interval the duration of the interval between two consecutive requests
+     *                 for channel access
+     */
+    void SetAccessReqInterval(Time interval);
 
   protected:
     /**
-     * Get the station manager attached to the AP.
+     * Get the station manager attached to the AP on the given link.
+     *
+     * \param linkId the ID of the given link
+     * \return the station manager attached to the AP on the given link
+     */
+    Ptr<WifiRemoteStationManager> GetWifiRemoteStationManager(uint8_t linkId) const;
+
+    /**
+     * Get the HE Frame Exchange Manager attached to the AP on the given link.
      *
-     * \return the station manager attached to the AP
+     * \param linkId the ID of the given link
+     * \return the HE Frame Exchange Manager attached to the AP on the given link
      */
-    Ptr<WifiRemoteStationManager> GetWifiRemoteStationManager() const;
+    Ptr<HeFrameExchangeManager> GetHeFem(uint8_t linkId) const;
 
     /**
      * Get an MPDU containing the given Trigger Frame.
      *
      * \param trigger the given Trigger Frame
+     * \param linkId the ID of the link on which the Trigger Frame has to be sent
      * \return an MPDU containing the given Trigger Frame
      */
-    Ptr<WifiMpdu> GetTriggerFrame(const CtrlTriggerHeader& trigger) const;
+    Ptr<WifiMpdu> GetTriggerFrame(const CtrlTriggerHeader& trigger, uint8_t linkId) const;
 
     /**
-     * Get the format of the last transmission, as determined by the last call
-     * to NotifyAccessGranted that did not return NO_TX.
+     * Get the format of the last transmission on the given link, as determined by
+     * the last call to NotifyAccessGranted that did not return NO_TX.
      *
-     * \return the format of the last transmission
+     * \param linkId the ID of the given link
+     * \return the format of the last transmission on the given link
      */
-    TxFormat GetLastTxFormat() const;
+    TxFormat GetLastTxFormat(uint8_t linkId);
 
     /**
      * Get the maximum size in bytes among the A-MPDUs containing QoS Null frames
@@ -158,12 +182,12 @@
     void NotifyNewAggregate() override;
     void DoInitialize() override;
 
-    Ptr<ApWifiMac> m_apMac;              //!< the AP wifi MAC
-    Ptr<HeFrameExchangeManager> m_heFem; //!< HE Frame Exchange Manager
-    Ptr<QosTxop> m_edca;                 //!< the AC that gained channel access
-    Time m_availableTime;                //!< the time available for frame exchange
-    bool m_initialFrame;                 //!< true if a TXOP is being started
-    uint16_t m_allowedWidth;             //!< the allowed width in MHz for the current transmission
+    Ptr<ApWifiMac> m_apMac;  //!< the AP wifi MAC
+    Ptr<QosTxop> m_edca;     //!< the AC that gained channel access
+    Time m_availableTime;    //!< the time available for frame exchange
+    bool m_initialFrame;     //!< true if a TXOP is being started
+    uint16_t m_allowedWidth; //!< the allowed width in MHz for the current transmission
+    uint8_t m_linkId;        //!< the ID of the link over which channel access has been granted
 
   private:
     /**
@@ -208,14 +232,22 @@
      */
     void CheckTriggerFrame();
 
-    TxFormat m_lastTxFormat{NO_TX}; ///< the format of last transmission
-    DlMuInfo m_dlInfo;              ///< information required to perform a DL MU transmission
-    UlMuInfo m_ulInfo;              ///< information required to solicit an UL MU transmission
-    EventId m_accessReqTimer;       ///< the timer controlling additional channel access requests
-    Time m_accessReqInterval;       ///< duration of the interval between channel access requests
-    AcIndex m_accessReqAc;          ///< AC we request channel access for
-    bool m_restartTimerUponAccess;  ///< whether the channel access timer has to be restarted
-                                    ///< upon channel access
+    /**
+     * Type for the information about the last transmission
+     */
+    struct LastTxInfo
+    {
+        TxFormat lastTxFormat{NO_TX}; ///< the format of last transmission
+        DlMuInfo dlInfo;              ///< information required to perform a DL MU transmission
+        UlMuInfo ulInfo;              ///< information required to solicit an UL MU transmission
+    };
+
+    std::map<uint8_t, LastTxInfo> m_lastTxInfo; ///< Information about the last transmission
+    EventId m_accessReqTimer;      ///< the timer controlling additional channel access requests
+    Time m_accessReqInterval;      ///< duration of the interval between channel access requests
+    AcIndex m_accessReqAc;         ///< AC we request channel access for
+    bool m_restartTimerUponAccess; ///< whether the channel access timer has to be restarted
+                                   ///< upon channel access
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/rr-multi-user-scheduler.cc ns-3.38/src/wifi/model/he/rr-multi-user-scheduler.cc
--- ns-3.37/src/wifi/model/he/rr-multi-user-scheduler.cc	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/rr-multi-user-scheduler.cc	2023-03-16 20:54:23.856869200 -0700
@@ -148,14 +148,14 @@
 {
     NS_LOG_FUNCTION(this);
 
-    Ptr<const WifiMpdu> mpdu = m_edca->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    Ptr<const WifiMpdu> mpdu = m_edca->PeekNextMpdu(m_linkId);
 
-    if (mpdu && !GetWifiRemoteStationManager()->GetHeSupported(mpdu->GetHeader().GetAddr1()))
+    if (mpdu && !m_apMac->GetHeSupported(mpdu->GetHeader().GetAddr1()))
     {
         return SU_TX;
     }
 
-    if (m_enableUlOfdma && m_enableBsrp && (GetLastTxFormat() == DL_MU_TX || !mpdu))
+    if (m_enableUlOfdma && m_enableBsrp && (GetLastTxFormat(m_linkId) == DL_MU_TX || !mpdu))
     {
         TxFormat txFormat = TrySendingBsrpTf();
 
@@ -164,7 +164,7 @@
             return txFormat;
         }
     }
-    else if (m_enableUlOfdma && ((GetLastTxFormat() == DL_MU_TX) ||
+    else if (m_enableUlOfdma && ((GetLastTxFormat(m_linkId) == DL_MU_TX) ||
                                  (m_trigger.GetType() == TriggerFrameType::BSRP_TRIGGER) || !mpdu))
     {
         TxFormat txFormat = TrySendingBasicTf();
@@ -221,12 +221,21 @@
             continue;
         }
 
+        if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_TB &&
+            !m_apMac->GetEhtSupported(staIt->address))
+        {
+            NS_LOG_DEBUG(
+                "Skipping non-EHT STA because this Trigger Frame is only soliciting EHT STAs");
+            staIt++;
+            continue;
+        }
+
         uint8_t tid = 0;
         while (tid < 8)
         {
             // check that a BA agreement is established with the receiver for the
             // considered TID, since ack sequences for UL MU require block ack
-            if (m_heFem->GetBaAgreementEstablished(staIt->address, tid))
+            if (m_apMac->GetBaAgreementEstablishedAsRecipient(staIt->address, tid))
             {
                 break;
             }
@@ -239,16 +248,29 @@
             continue;
         }
 
+        // if the first candidate STA is an EHT STA, we switch to soliciting EHT TB PPDUs
+        if (txVector.GetHeMuUserInfoMap().empty())
+        {
+            if (m_apMac->GetEhtSupported() && m_apMac->GetEhtSupported(staIt->address))
+            {
+                txVector.SetPreambleType(WIFI_PREAMBLE_EHT_TB);
+                txVector.SetEhtPpduType(0);
+            }
+            // TODO otherwise, make sure the TX width does not exceed 160 MHz
+        }
+
         // prepare the MAC header of a frame that would be sent to the candidate station,
         // just for the purpose of retrieving the TXVECTOR used to transmit to that station
         WifiMacHeader hdr(WIFI_MAC_QOSDATA);
-        hdr.SetAddr1(staIt->address);
-        hdr.SetAddr2(m_apMac->GetAddress());
+        hdr.SetAddr1(GetWifiRemoteStationManager(m_linkId)
+                         ->GetAffiliatedStaAddress(staIt->address)
+                         .value_or(staIt->address));
+        hdr.SetAddr2(m_apMac->GetFrameExchangeManager(m_linkId)->GetAddress());
         WifiTxVector suTxVector =
-            GetWifiRemoteStationManager()->GetDataTxVector(hdr, m_allowedWidth);
+            GetWifiRemoteStationManager(m_linkId)->GetDataTxVector(hdr, m_allowedWidth);
         txVector.SetHeMuUserInfo(staIt->aid,
                                  {HeRu::RuSpec(), // assigned later by FinalizeTxVector
-                                  suTxVector.GetMode(),
+                                  suTxVector.GetMode().GetMcsValue(),
                                   suTxVector.GetNss()});
         m_candidates.emplace_back(staIt, nullptr);
 
@@ -277,7 +299,11 @@
         return TxFormat::SU_TX;
     }
 
-    WifiTxVector txVector = GetTxVectorForUlMu([](const MasterInfo&) { return true; });
+    // only consider stations that have setup the current link
+    WifiTxVector txVector = GetTxVectorForUlMu([this](const MasterInfo& info) {
+        const auto& staList = m_apMac->GetStaList(m_linkId);
+        return staList.find(info.aid) != staList.cend();
+    });
 
     if (txVector.GetHeMuUserInfoMap().empty())
     {
@@ -288,15 +314,15 @@
     m_trigger = CtrlTriggerHeader(TriggerFrameType::BSRP_TRIGGER, txVector);
     txVector.SetGuardInterval(m_trigger.GetGuardInterval());
 
-    auto item = GetTriggerFrame(m_trigger);
+    auto item = GetTriggerFrame(m_trigger, m_linkId);
     m_triggerMacHdr = item->GetHeader();
 
     m_txParams.Clear();
     // set the TXVECTOR used to send the Trigger Frame
     m_txParams.m_txVector =
-        m_apMac->GetWifiRemoteStationManager()->GetRtsTxVector(m_triggerMacHdr.GetAddr1());
+        m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1());
 
-    if (!m_heFem->TryAddMpdu(item, m_txParams, m_availableTime))
+    if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
     {
         // sending the BSRP Trigger Frame is not possible, hence return NO_TX. In
         // this way, no transmission will occur now and the next time we will
@@ -311,7 +337,7 @@
     {
         Time duration = WifiPhy::CalculateTxDuration(GetMaxSizeOfQosNullAmpdu(m_trigger),
                                                      txVector,
-                                                     m_apMac->GetWifiPhy()->GetPhyBand(),
+                                                     m_apMac->GetWifiPhy(m_linkId)->GetPhyBand(),
                                                      userInfo.GetAid12());
         qosNullTxDuration = Max(qosNullTxDuration, duration);
     }
@@ -326,7 +352,7 @@
         NS_ASSERT(m_txParams.m_txDuration != Time::Min());
 
         if (m_txParams.m_protection->protectionTime + m_txParams.m_txDuration // BSRP TF tx time
-                + m_apMac->GetWifiPhy()->GetSifs() + qosNullTxDuration >
+                + m_apMac->GetWifiPhy(m_linkId)->GetSifs() + qosNullTxDuration >
             m_availableTime)
         {
             NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
@@ -338,7 +364,7 @@
     std::tie(ulLength, qosNullTxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
         qosNullTxDuration,
         m_trigger.GetHeTbTxVector(m_trigger.begin()->GetAid12()),
-        m_apMac->GetWifiPhy()->GetPhyBand());
+        m_apMac->GetWifiPhy(m_linkId)->GetPhyBand());
     NS_LOG_DEBUG("Duration of QoS Null frames: " << qosNullTxDuration.As(Time::MS));
     m_trigger.SetUlLength(ulLength);
 
@@ -359,9 +385,13 @@
     // check if an UL OFDMA transmission is possible after a DL OFDMA transmission
     NS_ABORT_MSG_IF(m_ulPsduSize == 0, "The UlPsduSize attribute must be set to a non-null value");
 
-    // only consider stations that do not have reported a null queue size
-    WifiTxVector txVector = GetTxVectorForUlMu(
-        [this](const MasterInfo& info) { return m_apMac->GetMaxBufferStatus(info.address) > 0; });
+    // only consider stations that have setup the current link and do not have
+    // reported a null queue size
+    WifiTxVector txVector = GetTxVectorForUlMu([this](const MasterInfo& info) {
+        const auto& staList = m_apMac->GetStaList(m_linkId);
+        return staList.find(info.aid) != staList.cend() &&
+               m_apMac->GetMaxBufferStatus(info.address) > 0;
+    });
 
     if (txVector.GetHeMuUserInfoMap().empty())
     {
@@ -373,22 +403,23 @@
 
     for (const auto& candidate : txVector.GetHeMuUserInfoMap())
     {
-        auto staIt = m_apMac->GetStaList().find(candidate.first);
-        NS_ASSERT(staIt != m_apMac->GetStaList().end());
-        uint8_t queueSize = m_apMac->GetMaxBufferStatus(staIt->second);
+        auto address = m_apMac->GetMldOrLinkAddressByAid(candidate.first);
+        NS_ASSERT_MSG(address, "AID " << candidate.first << " not found");
+
+        uint8_t queueSize = m_apMac->GetMaxBufferStatus(*address);
         if (queueSize == 255)
         {
-            NS_LOG_DEBUG("Buffer status of station " << staIt->second << " is unknown");
+            NS_LOG_DEBUG("Buffer status of station " << *address << " is unknown");
             maxBufferSize = std::max(maxBufferSize, m_ulPsduSize);
         }
         else if (queueSize == 254)
         {
-            NS_LOG_DEBUG("Buffer status of station " << staIt->second << " is not limited");
+            NS_LOG_DEBUG("Buffer status of station " << *address << " is not limited");
             maxBufferSize = 0xffffffff;
         }
         else
         {
-            NS_LOG_DEBUG("Buffer status of station " << staIt->second << " is " << +queueSize);
+            NS_LOG_DEBUG("Buffer status of station " << *address << " is " << +queueSize);
             maxBufferSize = std::max(maxBufferSize, static_cast<uint32_t>(queueSize * 256));
         }
     }
@@ -401,7 +432,7 @@
     m_trigger = CtrlTriggerHeader(TriggerFrameType::BASIC_TRIGGER, txVector);
     txVector.SetGuardInterval(m_trigger.GetGuardInterval());
 
-    auto item = GetTriggerFrame(m_trigger);
+    auto item = GetTriggerFrame(m_trigger, m_linkId);
     m_triggerMacHdr = item->GetHeader();
 
     // compute the maximum amount of time that can be granted to stations.
@@ -411,9 +442,9 @@
     m_txParams.Clear();
     // set the TXVECTOR used to send the Trigger Frame
     m_txParams.m_txVector =
-        m_apMac->GetWifiRemoteStationManager()->GetRtsTxVector(m_triggerMacHdr.GetAddr1());
+        m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1());
 
-    if (!m_heFem->TryAddMpdu(item, m_txParams, m_availableTime))
+    if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
     {
         // an UL OFDMA transmission is not possible, hence return NO_TX. In
         // this way, no transmission will occur now and the next time we will
@@ -433,7 +464,7 @@
 
         maxDuration = Min(maxDuration,
                           m_availableTime - m_txParams.m_protection->protectionTime -
-                              m_txParams.m_txDuration - m_apMac->GetWifiPhy()->GetSifs() -
+                              m_txParams.m_txDuration - m_apMac->GetWifiPhy(m_linkId)->GetSifs() -
                               m_txParams.m_acknowledgment->acknowledgmentTime);
         if (maxDuration.IsNegative())
         {
@@ -448,7 +479,7 @@
     {
         Time duration = WifiPhy::CalculateTxDuration(maxBufferSize,
                                                      txVector,
-                                                     m_apMac->GetWifiPhy()->GetPhyBand(),
+                                                     m_apMac->GetWifiPhy(m_linkId)->GetPhyBand(),
                                                      userInfo.GetAid12());
         bufferTxTime = Max(bufferTxTime, duration);
     }
@@ -465,10 +496,11 @@
         Time minDuration = Seconds(0);
         for (const auto& userInfo : m_trigger)
         {
-            Time duration = WifiPhy::CalculateTxDuration(m_ulPsduSize,
-                                                         txVector,
-                                                         m_apMac->GetWifiPhy()->GetPhyBand(),
-                                                         userInfo.GetAid12());
+            Time duration =
+                WifiPhy::CalculateTxDuration(m_ulPsduSize,
+                                             txVector,
+                                             m_apMac->GetWifiPhy(m_linkId)->GetPhyBand(),
+                                             userInfo.GetAid12());
             minDuration = (minDuration.IsZero() ? duration : Min(minDuration, duration));
         }
 
@@ -487,7 +519,7 @@
     std::tie(ulLength, maxDuration) =
         HePhy::ConvertHeTbPpduDurationToLSigLength(maxDuration,
                                                    txVector,
-                                                   m_apMac->GetWifiPhy()->GetPhyBand());
+                                                   m_apMac->GetWifiPhy(m_linkId)->GetPhyBand());
     NS_LOG_DEBUG("TB PPDU duration: " << maxDuration.As(Time::MS));
     m_trigger.SetUlLength(ulLength);
     // set Preferred AC to the AC that gained channel access
@@ -506,13 +538,33 @@
 {
     NS_LOG_FUNCTION(this << aid << address);
 
-    if (GetWifiRemoteStationManager()->GetHeSupported(address))
+    if (!m_apMac->GetHeSupported(address))
+    {
+        return;
+    }
+
+    auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
+    NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
+
+    for (auto& staList : m_staListDl)
     {
-        for (auto& staList : m_staListDl)
+        // if this is not the first STA of a non-AP MLD to be notified, an entry
+        // for this non-AP MLD already exists
+        const auto staIt = std::find_if(staList.second.cbegin(),
+                                        staList.second.cend(),
+                                        [aid](auto&& info) { return info.aid == aid; });
+        if (staIt == staList.second.cend())
         {
-            staList.second.push_back(MasterInfo{aid, address, 0.0});
+            staList.second.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
         }
-        m_staListUl.push_back(MasterInfo{aid, address, 0.0});
+    }
+
+    const auto staIt = std::find_if(m_staListUl.cbegin(), m_staListUl.cend(), [aid](auto&& info) {
+        return info.aid == aid;
+    });
+    if (staIt == m_staListUl.cend())
+    {
+        m_staListUl.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
     }
 }
 
@@ -521,18 +573,25 @@
 {
     NS_LOG_FUNCTION(this << aid << address);
 
-    if (GetWifiRemoteStationManager()->GetHeSupported(address))
+    if (!m_apMac->GetHeSupported(address))
     {
-        for (auto& staList : m_staListDl)
-        {
-            staList.second.remove_if([&aid, &address](const MasterInfo& info) {
-                return info.aid == aid && info.address == address;
-            });
-        }
-        m_staListUl.remove_if([&aid, &address](const MasterInfo& info) {
-            return info.aid == aid && info.address == address;
-        });
+        return;
+    }
+
+    auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
+    NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
+
+    if (m_apMac->IsAssociated(*mldOrLinkAddress))
+    {
+        // Another STA of the non-AP MLD is still associated
+        return;
     }
+
+    for (auto& staList : m_staListDl)
+    {
+        staList.second.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
+    }
+    m_staListUl.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
 }
 
 MultiUserScheduler::TxFormat
@@ -562,7 +621,7 @@
 
     uint8_t currTid = wifiAcList.at(primaryAc).GetHighTid();
 
-    Ptr<WifiMpdu> mpdu = m_edca->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    Ptr<WifiMpdu> mpdu = m_edca->PeekNextMpdu(m_linkId);
 
     if (mpdu && mpdu->GetHeader().IsQosData())
     {
@@ -616,6 +675,14 @@
     {
         NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
 
+        if (m_txParams.m_txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU &&
+            !m_apMac->GetEhtSupported(staIt->address))
+        {
+            NS_LOG_DEBUG("Skipping non-EHT STA because this DL MU PPDU is sent to EHT STAs only");
+            staIt++;
+            continue;
+        }
+
         HeRu::RuType currRuType = (m_candidates.size() < count ? ruType : HeRu::RU_26_TONE);
 
         // check if the AP has at least one frame to be sent to the current station
@@ -625,29 +692,30 @@
             NS_ASSERT(ac >= primaryAc);
             // check that a BA agreement is established with the receiver for the
             // considered TID, since ack sequences for DL MU PPDUs require block ack
-            if (m_apMac->GetQosTxop(ac)->GetBaAgreementEstablished(staIt->address, tid))
+            if (m_apMac->GetBaAgreementEstablishedAsOriginator(staIt->address, tid))
             {
-                mpdu =
-                    m_apMac->GetQosTxop(ac)->PeekNextMpdu(SINGLE_LINK_OP_ID, tid, staIt->address);
+                mpdu = m_apMac->GetQosTxop(ac)->PeekNextMpdu(m_linkId, tid, staIt->address);
 
                 // we only check if the first frame of the current TID meets the size
                 // and duration constraints. We do not explore the queues further.
                 if (mpdu)
                 {
+                    mpdu = GetHeFem(m_linkId)->CreateAliasIfNeeded(mpdu);
                     // Use a temporary TX vector including only the STA-ID of the
                     // candidate station to check if the MPDU meets the size and time limits.
                     // An RU of the computed size is tentatively assigned to the candidate
                     // station, so that the TX duration can be correctly computed.
                     WifiTxVector suTxVector =
-                        GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(),
-                                                                       m_allowedWidth);
+                        GetWifiRemoteStationManager(m_linkId)->GetDataTxVector(mpdu->GetHeader(),
+                                                                               m_allowedWidth);
                     WifiTxVector txVectorCopy = m_txParams.m_txVector;
 
-                    m_txParams.m_txVector.SetHeMuUserInfo(
-                        staIt->aid,
-                        {{currRuType, 1, true}, suTxVector.GetMode(), suTxVector.GetNss()});
+                    m_txParams.m_txVector.SetHeMuUserInfo(staIt->aid,
+                                                          {{currRuType, 1, true},
+                                                           suTxVector.GetMode().GetMcsValue(),
+                                                           suTxVector.GetNss()});
 
-                    if (!m_heFem->TryAddMpdu(mpdu, m_txParams, actualAvailableTime))
+                    if (!GetHeFem(m_linkId)->TryAddMpdu(mpdu, m_txParams, actualAvailableTime))
                     {
                         NS_LOG_DEBUG("Adding the peeked frame violates the time constraints");
                         m_txParams.m_txVector = txVectorCopy;
@@ -669,6 +737,17 @@
             }
         }
 
+        // the first candidate STA determines the preamble type for the DL MU PPDU
+        if (m_candidates.size() == 1)
+        {
+            if (m_apMac->GetEhtSupported() && m_apMac->GetEhtSupported(staIt->address))
+            {
+                m_txParams.m_txVector.SetPreambleType(WIFI_PREAMBLE_EHT_MU);
+                m_txParams.m_txVector.SetEhtPpduType(0); // indicates DL OFDMA transmission
+            }
+            // TODO otherwise, make sure the TX width does not exceed 160 MHz
+        }
+
         // move to the next station in the list
         staIt++;
     }
@@ -811,7 +890,7 @@
         NS_ASSERT(mpdu);
 
         bool ret [[maybe_unused]] =
-            m_heFem->TryAddMpdu(mpdu, dlMuInfo.txParams, actualAvailableTime);
+            GetHeFem(m_linkId)->TryAddMpdu(mpdu, dlMuInfo.txParams, actualAvailableTime);
         NS_ASSERT_MSG(ret,
                       "Weird that an MPDU does not meet constraints when "
                       "transmitted over a larger RU");
@@ -819,7 +898,6 @@
 
     // We have to complete the PSDUs to send
     Ptr<WifiMacQueue> queue;
-    Mac48Address receiver;
 
     for (const auto& candidate : m_candidates)
     {
@@ -827,8 +905,8 @@
         mpdu = candidate.second;
         NS_ASSERT(mpdu);
         uint8_t tid = mpdu->GetHeader().GetQosTid();
-        receiver = mpdu->GetHeader().GetAddr1();
-        NS_ASSERT(receiver == candidate.first->address);
+        NS_ASSERT_MSG(mpdu->GetOriginal()->GetHeader().GetAddr1() == candidate.first->address,
+                      "RA of the stored MPDU must match the stored address");
 
         NS_ASSERT(mpdu->IsQueued());
         Ptr<WifiMpdu> item = mpdu;
@@ -837,9 +915,9 @@
         {
             // this MPDU must have been dequeued from the AC queue and we can try
             // A-MSDU aggregation
-            item = m_heFem->GetMsduAggregator()->GetNextAmsdu(mpdu,
-                                                              dlMuInfo.txParams,
-                                                              m_availableTime);
+            item = GetHeFem(m_linkId)->GetMsduAggregator()->GetNextAmsdu(mpdu,
+                                                                         dlMuInfo.txParams,
+                                                                         m_availableTime);
 
             if (!item)
             {
@@ -851,7 +929,9 @@
 
         // Now, let's try A-MPDU aggregation if possible
         std::vector<Ptr<WifiMpdu>> mpduList =
-            m_heFem->GetMpduAggregator()->GetNextAmpdu(item, dlMuInfo.txParams, m_availableTime);
+            GetHeFem(m_linkId)->GetMpduAggregator()->GetNextAmpdu(item,
+                                                                  dlMuInfo.txParams,
+                                                                  m_availableTime);
 
         if (mpduList.size() > 1)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/he/rr-multi-user-scheduler.h ns-3.38/src/wifi/model/he/rr-multi-user-scheduler.h
--- ns-3.37/src/wifi/model/he/rr-multi-user-scheduler.h	2022-11-01 17:37:24.743395800 -0700
+++ ns-3.38/src/wifi/model/he/rr-multi-user-scheduler.h	2023-03-16 20:54:23.856869200 -0700
@@ -34,8 +34,9 @@
  * transmission if the AP has frames to transmit to at least one station.
  * RrMultiUserScheduler assigns RUs of equal size (in terms of tones) to stations to
  * which the AP has frames to transmit belonging to the AC who gained access to the
- * channel or higher. The maximum number of stations that can be granted an RU
- * is configurable. Associated stations are served in a round robin fashion.
+ * channel or higher. The maximum number of stations that can be granted an RU is
+ * configurable. Associated stations are served based on their priority. The priority is
+ * determined by the credits/debits a station gets when it is selected or not for transmission.
  *
  * \todo Take the supported channel width of the stations into account while selecting
  * stations and assigning RUs to them.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ht/ht-frame-exchange-manager.cc ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.cc
--- ns-3.37/src/wifi/model/ht/ht-frame-exchange-manager.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
@@ -67,8 +67,6 @@
 HtFrameExchangeManager::DoDispose()
 {
     NS_LOG_FUNCTION(this);
-    m_agreements.clear();
-    m_pendingAgreements.clear();
     m_pendingAddBaResp.clear();
     m_msduAggregator = nullptr;
     m_mpduAggregator = nullptr;
@@ -113,10 +111,8 @@
     {
         establish = false;
     }
-    else if (qosTxop->GetBaManager()->ExistsAgreement(recipient, tid) &&
-             !qosTxop->GetBaManager()->ExistsAgreementInState(recipient,
-                                                              tid,
-                                                              OriginatorBlockAckAgreement::RESET))
+    else if (auto agreement = qosTxop->GetBaManager()->GetAgreementAsOriginator(recipient, tid);
+             agreement && !agreement->get().IsReset())
     {
         establish = false;
     }
@@ -147,7 +143,9 @@
 
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_MGT_ACTION);
-    hdr.SetAddr1(dest);
+    // use the remote link address if dest is an MLD address
+    auto addr1 = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(dest);
+    hdr.SetAddr1(addr1 ? *addr1 : dest);
     hdr.SetAddr2(m_self);
     hdr.SetAddr3(m_bssid);
     hdr.SetDsNotTo();
@@ -179,7 +177,7 @@
     // set the starting sequence number for the BA agreement
     reqHdr.SetStartingSequence(startingSeq);
 
-    GetBaManager(tid)->CreateAgreement(&reqHdr, dest);
+    GetBaManager(tid)->CreateOriginatorAgreement(reqHdr, dest);
 
     packet->AddHeader(reqHdr);
     packet->AddHeader(actionHdr);
@@ -229,7 +227,8 @@
     {
         respHdr.SetDelayedBlockAck();
     }
-    respHdr.SetTid(reqHdr->GetTid());
+    auto tid = reqHdr->GetTid();
+    respHdr.SetTid(tid);
 
     respHdr.SetBufferSize(GetSupportedBaBufferSize());
     respHdr.SetTimeout(reqHdr->GetTimeout());
@@ -243,7 +242,33 @@
     packet->AddHeader(respHdr);
     packet->AddHeader(actionHdr);
 
-    CreateBlockAckAgreement(&respHdr, originator, reqHdr->GetStartingSequence());
+    // Get the MLD address of the originator, if an ML setup was performed
+    if (auto originatorMld = GetWifiRemoteStationManager()->GetMldAddress(originator))
+    {
+        originator = *originatorMld;
+    }
+    bool htSupported = GetWifiRemoteStationManager()->GetHtSupported() &&
+                       GetWifiRemoteStationManager()->GetHtSupported(originator);
+    GetBaManager(tid)->CreateRecipientAgreement(respHdr,
+                                                originator,
+                                                reqHdr->GetStartingSequence(),
+                                                htSupported,
+                                                m_rxMiddle);
+
+    auto agreement = GetBaManager(tid)->GetAgreementAsRecipient(originator, tid);
+    NS_ASSERT(agreement);
+    if (respHdr.GetTimeout() != 0)
+    {
+        Time timeout = MicroSeconds(1024 * agreement->get().GetTimeout());
+
+        agreement->get().m_inactivityEvent =
+            Simulator::Schedule(timeout,
+                                &HtFrameExchangeManager::SendDelbaFrame,
+                                this,
+                                originator,
+                                tid,
+                                false);
+    }
 
     auto mpdu = Create<WifiMpdu>(packet, hdr);
 
@@ -253,15 +278,12 @@
      * an ADDBA_RESPONSE frame, but is not able to successfully transmit it before the timer to
      * wait for ADDBA_RESPONSE expires at the originator. The latter may then send another
      * ADDBA_REQUEST frame, which triggers the creation of another ADDBA_RESPONSE frame.
-     * The first ADDBA_RESPONSE frame that is acknowledged causes the Block Ack agreement to be
-     * moved from the queue of pending agreements to the queue of establish agreements; the next
-     * one will find no pending agreement, thus causing the simulation to abort.
-     * As a solution, we keep track of the previously enqueued ADDBA_RESPONSE frame (if any),
-     * dequeue it and replace it with the new ADDBA_RESPONSE frame.
+     * To avoid sending unnecessary ADDBA_RESPONSE frames, we keep track of the previously enqueued
+     * ADDBA_RESPONSE frame (if any), dequeue it and replace it with the new ADDBA_RESPONSE frame.
      */
 
     // remove any pending ADDBA_RESPONSE frame
-    AgreementKey key(originator, reqHdr->GetTid());
+    AgreementKey key(originator, tid);
     if (auto it = m_pendingAddBaResp.find(key); it != m_pendingAddBaResp.end())
     {
         NS_ASSERT_MSG(it->second, "The pointer to the pending ADDBA_RESPONSE cannot be null");
@@ -275,7 +297,7 @@
     // bung it into the queue corresponding to the TID for which we are
     // establishing an agreement, and push it to the head.
     //  Wifi MAC queue scheduler is expected to prioritize management frames
-    m_mac->GetQosTxop(reqHdr->GetTid())->Queue(mpdu);
+    m_mac->GetQosTxop(tid)->Queue(mpdu);
 }
 
 uint16_t
@@ -290,7 +312,8 @@
     NS_LOG_FUNCTION(this << addr << +tid << byOriginator);
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_MGT_ACTION);
-    hdr.SetAddr1(addr);
+    // use the remote link address if addr is an MLD address
+    hdr.SetAddr1(GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr).value_or(addr));
     hdr.SetAddr2(m_self);
     hdr.SetAddr3(m_bssid);
     hdr.SetDsNotTo();
@@ -301,12 +324,12 @@
     if (byOriginator)
     {
         delbaHdr.SetByOriginator();
-        GetBaManager(tid)->DestroyAgreement(addr, tid);
+        GetBaManager(tid)->DestroyOriginatorAgreement(addr, tid);
     }
     else
     {
         delbaHdr.SetByRecipient();
-        DestroyBlockAckAgreement(addr, tid);
+        GetBaManager(tid)->DestroyRecipientAgreement(addr, tid);
     }
 
     WifiActionHeader actionHdr;
@@ -321,69 +344,14 @@
     m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(packet, hdr));
 }
 
-void
-HtFrameExchangeManager::CreateBlockAckAgreement(const MgtAddBaResponseHeader* respHdr,
-                                                Mac48Address originator,
-                                                uint16_t startingSeq)
-{
-    NS_LOG_FUNCTION(this << *respHdr << originator << startingSeq);
-    uint8_t tid = respHdr->GetTid();
-
-    RecipientBlockAckAgreement agreement(
-        originator,
-        respHdr->IsAmsduSupported(),
-        tid,
-        respHdr->GetBufferSize(),
-        respHdr->GetTimeout(),
-        startingSeq,
-        GetWifiRemoteStationManager()->GetHtSupported() &&
-            GetWifiRemoteStationManager()->GetHtSupported(originator));
-    agreement.SetMacRxMiddle(m_rxMiddle);
-    if (respHdr->IsImmediateBlockAck())
-    {
-        agreement.SetImmediateBlockAck();
-    }
-    else
-    {
-        agreement.SetDelayedBlockAck();
-    }
-
-    if (respHdr->GetTimeout() != 0)
-    {
-        Time timeout = MicroSeconds(1024 * agreement.GetTimeout());
-
-        agreement.m_inactivityEvent = Simulator::Schedule(timeout,
-                                                          &HtFrameExchangeManager::SendDelbaFrame,
-                                                          this,
-                                                          originator,
-                                                          tid,
-                                                          false);
-    }
-
-    m_pendingAgreements.insert_or_assign({originator, tid}, agreement);
-}
-
-void
-HtFrameExchangeManager::DestroyBlockAckAgreement(Mac48Address originator, uint8_t tid)
-{
-    NS_LOG_FUNCTION(this << originator << +tid);
-
-    auto agreementIt = m_agreements.find({originator, tid});
-    if (agreementIt != m_agreements.end())
-    {
-        // forward up the buffered MPDUs before destroying the agreement
-        agreementIt->second.Flush();
-        m_agreements.erase(agreementIt);
-    }
-}
-
 bool
 HtFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
 {
     NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
 
     // First, check if there is a BAR to be transmitted
-    if (SendMpduFromBaManager(edca, availableTime, initialFrame))
+    if (auto mpdu = GetBar(edca->GetAccessCategory());
+        mpdu && SendMpduFromBaManager(mpdu, availableTime, initialFrame))
     {
         return true;
     }
@@ -421,7 +389,8 @@
 
     // Use SendDataFrame if we can try aggregation
     if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() && !peekedItem->IsFragment() &&
-        !GetWifiRemoteStationManager()->NeedFragmentation(peekedItem))
+        !GetWifiRemoteStationManager()->NeedFragmentation(peekedItem =
+                                                              CreateAliasIfNeeded(peekedItem)))
     {
         return SendDataFrame(peekedItem, availableTime, initialFrame);
     }
@@ -434,33 +403,182 @@
     return QosFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
 }
 
+Ptr<WifiMpdu>
+HtFrameExchangeManager::GetBar(AcIndex ac,
+                               std::optional<uint8_t> optTid,
+                               std::optional<Mac48Address> optAddress)
+{
+    NS_LOG_FUNCTION(this << +ac << optTid.has_value() << optAddress.has_value());
+    NS_ASSERT_MSG(optTid.has_value() == optAddress.has_value(),
+                  "Either both or none of TID and address must be provided");
+
+    // remove all expired MPDUs from the MAC queue, so that
+    // BlockAckRequest frames (if needed) are scheduled
+    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> selectedBar;
+    uint8_t selectedTid = 0;
+
+    for (const auto& queueId : queueIds)
+    {
+        Ptr<WifiMpdu> bar;
+        Ptr<WifiMpdu> prevBar;
+
+        while ((bar = queue->PeekByQueueId(queueId, prevBar)))
+        {
+            if (bar->GetHeader().IsBlockAckReq())
+            {
+                CtrlBAckRequestHeader reqHdr;
+                bar->GetPacket()->PeekHeader(reqHdr);
+                auto tid = reqHdr.GetTidInfo();
+                Mac48Address recipient = bar->GetHeader().GetAddr1();
+
+                if (selectedBar)
+                {
+                    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)
+                    {
+                        queue->Remove(bar);
+                        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;
+        }
+    }
+
+    if (!selectedBar)
+    {
+        // check if we can send a BAR to a recipient to which a BAR can only be sent if data queued
+        auto baManager = m_mac->GetQosTxop(ac)->GetBaManager();
+        for (const auto& [recipient, tid] : baManager->GetSendBarIfDataQueuedList())
+        {
+            if (queue->PeekByTidAndAddress(tid, recipient))
+            {
+                selectedBar = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
+                baManager->RemoveFromSendBarIfDataQueuedList(recipient, tid);
+                queue->Enqueue(selectedBar);
+                break;
+            }
+        }
+    }
+
+    if (selectedBar && selectedBar->GetHeader().GetAddr2() != m_self)
+    {
+        // the selected BAR has MLD addresses in Addr1/Addr2, replace them with link addresses
+        // and move to the appropriate container queue
+        NS_ASSERT(selectedBar->GetHeader().GetAddr2() == m_mac->GetAddress());
+        DequeueMpdu(selectedBar);
+        const auto currAddr1 = selectedBar->GetHeader().GetAddr1();
+        auto addr1 =
+            GetWifiRemoteStationManager()->GetAffiliatedStaAddress(currAddr1).value_or(currAddr1);
+        selectedBar->GetHeader().SetAddr1(addr1);
+        selectedBar->GetHeader().SetAddr2(m_self);
+        queue->Enqueue(selectedBar);
+    }
+
+    return selectedBar;
+}
+
 bool
-HtFrameExchangeManager::SendMpduFromBaManager(Ptr<QosTxop> edca,
+HtFrameExchangeManager::SendMpduFromBaManager(Ptr<WifiMpdu> mpdu,
                                               Time availableTime,
                                               bool initialFrame)
 {
-    NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
+    NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
 
     // First, check if there is a BAR to be transmitted
-    Ptr<const WifiMpdu> peekedItem = edca->GetBaManager()->GetBar(false);
-
-    if (!peekedItem)
+    if (!mpdu->GetHeader().IsBlockAckReq())
     {
         NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
         return false;
     }
 
-    NS_ASSERT(peekedItem->GetHeader().IsBlockAckReq());
-
     // Prepare the TX parameters. Note that the default ack manager expects the
     // data TxVector in the m_txVector field to compute the BlockAck TxVector.
     // The m_txVector field of the TX parameters is set to the BlockAckReq TxVector
     // a few lines below.
     WifiTxParameters txParams;
     txParams.m_txVector =
-        GetWifiRemoteStationManager()->GetDataTxVector(peekedItem->GetHeader(), m_allowedWidth);
+        GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
     txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
-    txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(peekedItem, txParams);
+    txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
 
     NS_ABORT_IF(txParams.m_acknowledgment->method != WifiAcknowledgment::BLOCK_ACK);
 
@@ -469,7 +587,7 @@
     // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
     txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
 
-    Time barTxDuration = m_phy->CalculateTxDuration(peekedItem->GetSize(),
+    Time barTxDuration = m_phy->CalculateTxDuration(mpdu->GetSize(),
                                                     blockAcknowledgment->blockAckTxVector,
                                                     m_phy->GetPhyBand());
 
@@ -484,8 +602,7 @@
     }
 
     // we can transmit the BlockAckReq frame
-    Ptr<const WifiMpdu> mpdu = edca->GetBaManager()->GetBar();
-    SendPsduWithProtection(GetWifiPsdu(Copy(mpdu), txParams.m_txVector), txParams);
+    SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
     return true;
 }
 
@@ -592,14 +709,16 @@
         uint8_t tid = mpdu->GetHeader().GetQosTid();
         Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
 
-        if (edca->GetBaAgreementEstablished(mpdu->GetHeader().GetAddr1(), tid))
+        if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
         {
             // notify the BA manager that the MPDU was acknowledged
-            edca->GetBaManager()->NotifyGotAck(mpdu);
+            edca->GetBaManager()->NotifyGotAck(m_linkId, mpdu);
         }
     }
     else if (mpdu->GetHeader().IsAction())
     {
+        auto addr1 = mpdu->GetHeader().GetAddr1();
+        auto address = GetWifiRemoteStationManager()->GetMldAddress(addr1).value_or(addr1);
         WifiActionHeader actionHdr;
         Ptr<Packet> p = mpdu->GetPacket()->Copy();
         p->RemoveHeader(actionHdr);
@@ -609,14 +728,14 @@
             {
                 MgtDelBaHeader delBa;
                 p->PeekHeader(delBa);
+                auto tid = delBa.GetTid();
                 if (delBa.IsByOriginator())
                 {
-                    GetBaManager(delBa.GetTid())
-                        ->DestroyAgreement(mpdu->GetHeader().GetAddr1(), delBa.GetTid());
+                    GetBaManager(tid)->DestroyOriginatorAgreement(address, tid);
                 }
                 else
                 {
-                    DestroyBlockAckAgreement(mpdu->GetHeader().GetAddr1(), delBa.GetTid());
+                    GetBaManager(tid)->DestroyRecipientAgreement(address, tid);
                 }
             }
             else if (actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST)
@@ -628,23 +747,19 @@
                 Simulator::Schedule(edca->GetAddBaResponseTimeout(),
                                     &QosTxop::AddBaResponseTimeout,
                                     edca,
-                                    mpdu->GetHeader().GetAddr1(),
+                                    address,
                                     addBa.GetTid());
             }
             else if (actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE)
             {
-                // The recipient Block Ack agreement can be moved from the pending queue
-                // to the queue of established Block Ack agreements
+                // A recipient Block Ack agreement must exist
                 MgtAddBaResponseHeader addBa;
                 p->PeekHeader(addBa);
-                AgreementKey key(mpdu->GetHeader().GetAddr1(), addBa.GetTid());
-                auto nh = m_pendingAgreements.extract(key);
-                NS_ASSERT_MSG(!nh.empty(),
-                              "Pending agreement {" << key.first << ", " << +key.second
-                                                    << "} not found");
-                m_agreements.erase(nh.key());
-                m_agreements.insert(std::move(nh));
-                m_pendingAddBaResp.erase(key);
+                auto tid = addBa.GetTid();
+                NS_ASSERT_MSG(GetBaManager(tid)->GetAgreementAsRecipient(address, tid),
+                              "Recipient BA agreement {" << address << ", " << +tid
+                                                         << "} not found");
+                m_pendingAddBaResp.erase({address, tid});
             }
         }
     }
@@ -656,7 +771,7 @@
 {
     NS_LOG_DEBUG(this);
 
-    if (m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() && m_edca->GetBaManager()->GetBar(false))
+    if (m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() && GetBar(m_edca->GetAccessCategory()))
     {
         // A TXOP limit of 0 indicates that the TXOP holder may transmit or cause to
         // be transmitted (as responses) the following within the current TXOP:
@@ -688,20 +803,26 @@
     {
         WifiActionHeader actionHdr;
         mpdu->GetPacket()->PeekHeader(actionHdr);
-        if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK)
+        if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
+            actionHdr.GetAction().blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST)
         {
             uint8_t tid = GetTid(mpdu->GetPacket(), mpdu->GetHeader());
-            if (GetBaManager(tid)->ExistsAgreementInState(mpdu->GetHeader().GetAddr1(),
-                                                          tid,
-                                                          OriginatorBlockAckAgreement::PENDING))
+            auto recipient = mpdu->GetHeader().GetAddr1();
+            // if the recipient is an MLD, use its MLD address
+            if (auto mldAddr = GetWifiRemoteStationManager()->GetMldAddress(recipient))
+            {
+                recipient = *mldAddr;
+            }
+            if (auto agreement = GetBaManager(tid)->GetAgreementAsOriginator(recipient, tid);
+                agreement && agreement->get().IsPending())
             {
                 NS_LOG_DEBUG("No ACK after ADDBA request");
-                GetBaManager(tid)->NotifyAgreementNoReply(mpdu->GetHeader().GetAddr1(), tid);
                 Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
+                qosTxop->NotifyOriginatorAgreementNoReply(recipient, tid);
                 Simulator::Schedule(qosTxop->GetFailedAddBaTimeout(),
                                     &QosTxop::ResetBa,
                                     qosTxop,
-                                    mpdu->GetHeader().GetAddr1(),
+                                    recipient,
                                     tid);
             }
         }
@@ -719,10 +840,10 @@
         uint8_t tid = mpdu->GetHeader().GetQosTid();
         Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
 
-        if (edca->GetBaAgreementEstablished(mpdu->GetHeader().GetAddr1(), tid))
+        if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
         {
             // notify the BA manager that the MPDU was not acknowledged
-            edca->GetBaManager()->NotifyMissedAck(mpdu);
+            edca->GetBaManager()->NotifyMissedAck(m_linkId, mpdu);
             return;
         }
     }
@@ -730,35 +851,53 @@
 }
 
 void
-HtFrameExchangeManager::ReleaseSequenceNumber(Ptr<WifiMpdu> mpdu) const
+HtFrameExchangeManager::ReleaseSequenceNumbers(Ptr<const WifiPsdu> psdu) const
 {
-    NS_LOG_FUNCTION(this << *mpdu);
+    NS_LOG_FUNCTION(this << *psdu);
 
-    // the MPDU should be still in the queue, unless it expired.
-    const WifiMacHeader& hdr = mpdu->GetHeader();
-    if (hdr.IsQosData())
+    auto tids = psdu->GetTids();
+
+    if (tids.empty() || // no QoS data frames included
+        !m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), *tids.begin()))
     {
-        uint8_t tid = hdr.GetQosTid();
-        Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
+        QosFrameExchangeManager::ReleaseSequenceNumbers(psdu);
+        return;
+    }
+
+    // iterate over MPDUs in reverse order (to process them in decreasing order of sequence number)
+    auto mpduIt = psdu->end();
+
+    do
+    {
+        std::advance(mpduIt, -1);
 
-        if (edca->GetBaAgreementEstablished(hdr.GetAddr1(), tid) && !hdr.IsRetry())
+        const WifiMacHeader& hdr = (*mpduIt)->GetOriginal()->GetHeader();
+        if (hdr.IsQosData())
         {
-            // The MPDU has never been transmitted, so we can make its sequence
-            // number available again if it is lower than the sequence number
-            // maintained by the MAC TX middle
-            uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
-            uint16_t startingSeq = edca->GetBaStartingSequence(hdr.GetAddr1(), tid);
+            uint8_t tid = hdr.GetQosTid();
+            NS_ASSERT(m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr1(), tid));
 
-            if (BlockAckAgreement::GetDistance(hdr.GetSequenceNumber(), startingSeq) <
-                BlockAckAgreement::GetDistance(currentNextSeq, startingSeq))
+            if (!hdr.IsRetry() && !(*mpduIt)->IsInFlight())
             {
-                m_txMiddle->SetSequenceNumberFor(&hdr);
-            }
+                // The MPDU has never been transmitted, so we can make its sequence
+                // number available again if it is the highest sequence number
+                // assigned by the MAC TX middle
+                uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
 
-            return;
+                if ((hdr.GetSequenceNumber() + 1) % SEQNO_SPACE_SIZE == currentNextSeq)
+                {
+                    (*mpduIt)->UnassignSeqNo();
+                    m_txMiddle->SetSequenceNumberFor(&hdr);
+
+                    NS_LOG_DEBUG("Released " << hdr.GetSequenceNumber()
+                                             << ", next sequence "
+                                                "number for dest="
+                                             << hdr.GetAddr1() << ",tid=" << +tid << " is "
+                                             << m_txMiddle->PeekNextSequenceNumberFor(&hdr));
+                }
+            }
         }
-    }
-    QosFrameExchangeManager::ReleaseSequenceNumber(mpdu);
+    } while (mpduIt != psdu->begin());
 }
 
 Time
@@ -814,6 +953,14 @@
     // Set QoS Ack policy
     WifiAckManager::SetQosAckPolicy(m_psdu, m_txParams.m_acknowledgment.get());
 
+    for (const auto& mpdu : *PeekPointer(m_psdu))
+    {
+        if (mpdu->IsQueued())
+        {
+            mpdu->SetInFlight(m_linkId);
+        }
+    }
+
     if (m_txParams.m_protection->method == WifiProtection::RTS_CTS)
     {
         SendRts(m_txParams);
@@ -866,7 +1013,7 @@
         std::set<uint8_t> tids = m_psdu->GetTids();
         NS_ASSERT_MSG(tids.size() <= 1, "Multi-TID A-MPDUs are not supported");
 
-        if (tids.size() == 0 || m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK)
+        if (tids.empty() || m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK)
         {
             // No acknowledgment, hence dequeue the PSDU if it is stored in a queue
             DequeuePsdu(m_psdu);
@@ -905,7 +1052,7 @@
         uint8_t tid = *tids.begin();
 
         Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
-        edca->ScheduleBar(edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid));
+        GetBaManager(tid)->ScheduleBar(edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid));
 
         Simulator::Schedule(txDuration, &HtFrameExchangeManager::TransmissionSucceeded, this);
     }
@@ -1217,7 +1364,8 @@
 {
     NS_LOG_FUNCTION(this << psdu << txVector << resetCw);
 
-    Mac48Address recipient = psdu->GetAddr1();
+    auto recipient = psdu->GetAddr1();
+    auto recipientMld = GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient);
     bool isBar;
     uint8_t tid;
 
@@ -1244,18 +1392,27 @@
     if (edca->UseExplicitBarAfterMissedBlockAck() || isBar)
     {
         // we have to send a BlockAckReq, if needed
-        if (GetBaManager(tid)->NeedBarRetransmission(tid, recipient))
+        if (GetBaManager(tid)->NeedBarRetransmission(tid, recipientMld))
         {
             NS_LOG_DEBUG("Missed Block Ack, transmit a BlockAckReq");
+            /**
+             * The BlockAckReq must be sent on the same link as the data frames to avoid issues.
+             * As an example, assume that an A-MPDU is sent on link 0, the BlockAck timer
+             * expires and the BlockAckReq is sent on another link (e.g., on link 1). When the
+             * originator processes the BlockAck response, it will not interpret a '0' in the
+             * bitmap corresponding to the transmitted MPDUs as a negative acknowledgment,
+             * because the BlockAck is received on a different link than the one on which the
+             * MPDUs are (still) inflight. Hence, such MPDUs stay inflight and are not
+             * retransmitted.
+             */
             if (isBar)
             {
                 psdu->GetHeader(0).SetRetry();
-                edca->ScheduleBar(*psdu->begin());
             }
             else
             {
                 // missed block ack after data frame with Implicit BAR Ack policy
-                edca->ScheduleBar(edca->PrepareBlockAckRequest(recipient, tid));
+                GetBaManager(tid)->ScheduleBar(edca->PrepareBlockAckRequest(recipient, tid));
             }
             resetCw = false;
         }
@@ -1265,13 +1422,15 @@
             // if a BA agreement exists, we can get here if there is no outstanding
             // MPDU whose lifetime has not expired yet.
             GetWifiRemoteStationManager()->ReportFinalDataFailed(*psdu->begin());
-            if (GetBaManager(tid)->ExistsAgreementInState(recipient,
-                                                          tid,
-                                                          OriginatorBlockAckAgreement::ESTABLISHED))
+            if (isBar)
+            {
+                DequeuePsdu(psdu);
+            }
+            if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
             {
-                // schedule a BlockAckRequest with skipIfNoDataQueued set to true, so that the
-                // BlockAckRequest is only sent if there are data frames queued for this recipient.
-                edca->ScheduleBar(edca->PrepareBlockAckRequest(recipient, tid), true);
+                // schedule a BlockAckRequest to be sent only if there are data frames queued
+                // for this recipient
+                GetBaManager(tid)->AddToSendBarIfDataQueuedList(recipientMld, tid);
             }
             resetCw = true;
         }
@@ -1293,7 +1452,7 @@
         else
         {
             NS_LOG_DEBUG("Missed Block Ack, retransmit data frames");
-            GetBaManager(tid)->NotifyMissedBlockAck(recipient, tid);
+            GetBaManager(tid)->NotifyMissedBlockAck(m_linkId, recipientMld, tid);
             resetCw = false;
         }
     }
@@ -1309,7 +1468,12 @@
 
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_CTL_BACKRESP);
-    hdr.SetAddr1(agreement.GetPeer());
+    auto addr1 = agreement.GetPeer();
+    if (auto originator = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr1))
+    {
+        addr1 = *originator;
+    }
+    hdr.SetAddr1(addr1);
     hdr.SetAddr2(m_self);
     hdr.SetDsNotFrom();
     hdr.SetDsNotTo();
@@ -1345,20 +1509,6 @@
     ForwardPsduDown(psdu, blockAckTxVector);
 }
 
-bool
-HtFrameExchangeManager::GetBaAgreementEstablished(Mac48Address originator, uint8_t tid) const
-{
-    return (m_agreements.find({originator, tid}) != m_agreements.end());
-}
-
-BlockAckType
-HtFrameExchangeManager::GetBlockAckType(Mac48Address originator, uint8_t tid) const
-{
-    auto it = m_agreements.find({originator, tid});
-    NS_ABORT_MSG_IF(it == m_agreements.end(), "No established Block Ack agreement");
-    return it->second.GetBlockAckType();
-}
-
 void
 HtFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
                                     RxSignalInfo rxSignalInfo,
@@ -1408,8 +1558,11 @@
             mpdu->GetPacket()->PeekHeader(blockAck);
             uint8_t tid = blockAck.GetTidInfo();
             std::pair<uint16_t, uint16_t> ret =
-                GetBaManager(tid)->NotifyGotBlockAck(blockAck, hdr.GetAddr2(), {tid});
-            GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
+                GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
+                                                     blockAck,
+                                                     m_mac->GetMldAddress(sender).value_or(sender),
+                                                     {tid});
+            GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
                                                                ret.first,
                                                                ret.second,
                                                                rxSnr,
@@ -1423,6 +1576,11 @@
             // Reset the CW
             m_edca->ResetCw(m_linkId);
 
+            // if this BlockAck was sent in response to a BlockAckReq, dequeue the blockAckReq
+            if (m_psdu && m_psdu->GetNMpdus() == 1 && m_psdu->GetHeader(0).IsBlockAckReq())
+            {
+                DequeuePsdu(m_psdu);
+            }
             m_psdu = nullptr;
             TransmissionSucceeded();
         }
@@ -1439,22 +1597,25 @@
             NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
             uint8_t tid = blockAckReq.GetTidInfo();
 
-            auto agreementIt = m_agreements.find({sender, tid});
+            auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(sender, tid);
 
-            if (agreementIt == m_agreements.end())
+            if (!agreement)
             {
                 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
                 return;
             }
 
-            agreementIt->second.NotifyReceivedBar(blockAckReq.GetStartingSequence());
+            GetBaManager(tid)->NotifyGotBlockAckRequest(
+                m_mac->GetMldAddress(sender).value_or(sender),
+                tid,
+                blockAckReq.GetStartingSequence());
 
             NS_LOG_DEBUG("Schedule Block Ack");
             Simulator::Schedule(
                 m_phy->GetSifs(),
                 &HtFrameExchangeManager::SendBlockAck,
                 this,
-                agreementIt->second,
+                *agreement,
                 hdr.GetDuration(),
                 GetWifiRemoteStationManager()->GetBlockAckTxVector(sender, txVector),
                 rxSnr);
@@ -1471,13 +1632,12 @@
     {
         uint8_t tid = hdr.GetQosTid();
 
-        auto agreementIt = m_agreements.find({hdr.GetAddr2(), tid});
-        if (agreementIt != m_agreements.end())
+        if (m_mac->GetBaAgreementEstablishedAsRecipient(hdr.GetAddr2(), tid))
         {
             // a Block Ack agreement has been established
             NS_LOG_DEBUG("Received from=" << hdr.GetAddr2() << " (" << *mpdu << ")");
 
-            agreementIt->second.NotifyReceivedMpdu(mpdu);
+            GetBaManager(tid)->NotifyGotMpdu(mpdu);
 
             if (!inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
             {
@@ -1517,14 +1677,14 @@
         {
             // Normal Ack or Implicit Block Ack Request
             NS_LOG_DEBUG("Schedule Block Ack");
-            auto agreementIt = m_agreements.find({psdu->GetAddr2(), tid});
-            NS_ASSERT(agreementIt != m_agreements.end());
+            auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(psdu->GetAddr2(), tid);
+            NS_ASSERT(agreement);
 
             Simulator::Schedule(
                 m_phy->GetSifs(),
                 &HtFrameExchangeManager::SendBlockAck,
                 this,
-                agreementIt->second,
+                *agreement,
                 psdu->GetDuration(),
                 GetWifiRemoteStationManager()->GetBlockAckTxVector(psdu->GetAddr2(), txVector),
                 rxSignalInfo.snr);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ht/ht-frame-exchange-manager.h ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.h
--- ns-3.37/src/wifi/model/ht/ht-frame-exchange-manager.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
@@ -141,31 +141,6 @@
                                                Time ppduDurationLimit) const;
 
     /**
-     * \param respHdr Add block ack response from originator (action
-     *        frame).
-     * \param originator Address of peer station involved in block ack
-     *        mechanism.
-     * \param startingSeq Sequence number of the first MPDU of all
-     *        packets for which block ack was negotiated.
-     *
-     * This function is typically invoked only by ns3::WifiMac
-     * when the STA (which may be non-AP in ESS, or in an IBSS) has
-     * received an ADDBA Request frame and is transmitting an ADDBA
-     * Response frame. At this point the frame exchange manager must
-     * allocate buffers to collect all correctly received packets belonging
-     * to the category for which block ack was negotiated.
-     */
-    void CreateBlockAckAgreement(const MgtAddBaResponseHeader* respHdr,
-                                 Mac48Address originator,
-                                 uint16_t startingSeq);
-    /**
-     * Destroy a Block Ack agreement.
-     *
-     * \param originator the originator MAC address
-     * \param tid the TID associated with the Block Ack agreement
-     */
-    void DestroyBlockAckAgreement(Mac48Address originator, uint8_t tid);
-    /**
      * This method can be called to accept a received ADDBA Request. An
      * ADDBA Response will be constructed and queued for transmission.
      *
@@ -182,26 +157,6 @@
     virtual uint16_t GetSupportedBaBufferSize() const;
 
     /**
-     * Return true if a Block Ack agreement has been established with the given
-     * originator for the given TID.
-     *
-     * \param originator the MAC address of the given originator
-     * \param tid the Traffic ID
-     * \return true if a Block Ack agreement has been established with the given
-     *         originator for the given TID
-     */
-    bool GetBaAgreementEstablished(Mac48Address originator, uint8_t tid) const;
-
-    /**
-     * Get the type of BlockAck frames sent to the given originator.
-     *
-     * \param originator the MAC address of the given originator
-     * \param tid the Traffic ID
-     * \return the type of BlockAck frames sent to the given originator
-     */
-    BlockAckType GetBlockAckType(Mac48Address originator, uint8_t tid) const;
-
-    /**
      * Sends DELBA frame to cancel a block ack agreement with STA
      * addressed by <i>addr</i> for TID <i>tid</i>.
      *
@@ -211,6 +166,21 @@
      */
     void SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator);
 
+    /**
+     * Get the next BlockAckRequest or MU-BAR Trigger Frame to send, if any. If TID and recipient
+     * address are given, then only return a BlockAckRequest, if any, addressed to that recipient
+     * and for the given TID.
+     *
+     * \param ac the AC whose queue is searched for BlockAckRequest or Trigger Frames
+     * \param optTid the TID (optional)
+     * \param optAddress the recipient of the BAR (optional)
+     *
+     * \return the next BAR or Trigger Frame to be sent, if any
+     */
+    Ptr<WifiMpdu> GetBar(AcIndex ac,
+                         std::optional<uint8_t> optTid = std::nullopt,
+                         std::optional<Mac48Address> optAddress = std::nullopt);
+
   protected:
     void DoDispose() override;
 
@@ -225,7 +195,7 @@
     void NotifyReceivedNormalAck(Ptr<WifiMpdu> mpdu) override;
     void NotifyPacketDiscarded(Ptr<const WifiMpdu> mpdu) override;
     void RetransmitMpduAfterMissedAck(Ptr<WifiMpdu> mpdu) const override;
-    void ReleaseSequenceNumber(Ptr<WifiMpdu> mpdu) const override;
+    void ReleaseSequenceNumbers(Ptr<const WifiPsdu> psdu) const override;
     void ForwardMpduDown(Ptr<WifiMpdu> mpdu, WifiTxVector& txVector) override;
     void CtsTimeout(Ptr<WifiMpdu> rts, const WifiTxVector& txVector) override;
     void TransmissionSucceeded() override;
@@ -292,12 +262,11 @@
     void DequeuePsdu(Ptr<const WifiPsdu> psdu);
 
     /**
-     * If the Block Ack Manager associated with the given EDCA has a BlockAckReq frame
-     * to transmit (the duration of which plus the response fits within the given
-     * available time, if the latter is not Time::Min() and this is not the initial
-     * frame of a TXOP), transmit the frame and return true. Otherwise, return false.
+     * If the given MPDU contains a BlockAckReq frame (the duration of which plus the response
+     * fits within the given available time, if the latter is not Time::Min() and this is not
+     * the initial frame of a TXOP), transmit the frame and return true. Otherwise, return false.
      *
-     * \param edca the EDCAF which has been granted the opportunity to transmit
+     * \param mpdu the given MPDU
      * \param availableTime the amount of time allowed for the frame exchange. Equals
      *                      Time::Min() in case the TXOP limit is null
      * \param initialFrame true if the frame being transmitted is the initial frame
@@ -305,7 +274,7 @@
      *                     limit can be exceeded
      * \return true if frame is transmitted, false otherwise
      */
-    virtual bool SendMpduFromBaManager(Ptr<QosTxop> edca, Time availableTime, bool initialFrame);
+    virtual bool SendMpduFromBaManager(Ptr<WifiMpdu> mpdu, Time availableTime, bool initialFrame);
 
     /**
      * Given a non-broadcast QoS data frame, prepare the PSDU to transmit by attempting
@@ -387,14 +356,8 @@
     /// agreement key typedef (MAC address and TID)
     typedef std::pair<Mac48Address, uint8_t> AgreementKey;
 
-    /// typedef for map of recipient Block Ack agreements
-    using RecipientBlockAckAgreementMap = std::map<AgreementKey, RecipientBlockAckAgreement>;
-
-    RecipientBlockAckAgreementMap m_agreements;        //!< Block Ack agreements
-    RecipientBlockAckAgreementMap m_pendingAgreements; //!< pending Block Ack agreements (waiting
-                                                       //!< for Ack in response to ADDBA_RESPONSE)
-    Ptr<MsduAggregator> m_msduAggregator;              //!< A-MSDU aggregator
-    Ptr<MpduAggregator> m_mpduAggregator;              //!< A-MPDU aggregator
+    Ptr<MsduAggregator> m_msduAggregator; //!< A-MSDU aggregator
+    Ptr<MpduAggregator> m_mpduAggregator; //!< A-MPDU aggregator
 
     /// pending ADDBA_RESPONSE frames indexed by agreement key
     std::map<AgreementKey, Ptr<WifiMpdu>> m_pendingAddBaResp;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ht/ht-phy.cc ns-3.38/src/wifi/model/ht/ht-phy.cc
--- ns-3.37/src/wifi/model/ht/ht-phy.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/ht/ht-phy.cc	2023-03-16 20:54:23.856869200 -0700
@@ -459,10 +459,9 @@
 }
 
 Ptr<SpectrumValue>
-HtPhy::GetTxPowerSpectralDensity(double txPowerW,
-                                 Ptr<const WifiPpdu> /* ppdu */,
-                                 const WifiTxVector& txVector) const
+HtPhy::GetTxPowerSpectralDensity(double txPowerW, Ptr<const WifiPpdu> ppdu) const
 {
+    const auto& txVector = ppdu->GetTxVector();
     uint16_t centerFrequency = GetCenterFrequencyForChannelWidth(txVector);
     uint16_t channelWidth = txVector.GetChannelWidth();
     NS_LOG_FUNCTION(this << centerFrequency << channelWidth << txPowerW);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ht/ht-phy.h ns-3.38/src/wifi/model/ht/ht-phy.h
--- ns-3.37/src/wifi/model/ht/ht-phy.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/ht/ht-phy.h	2023-03-16 20:54:23.856869200 -0700
@@ -464,8 +464,7 @@
     bool IsAllConfigSupported(WifiPpduField field, Ptr<const WifiPpdu> ppdu) const override;
     bool IsConfigSupported(Ptr<const WifiPpdu> ppdu) const override;
     Ptr<SpectrumValue> GetTxPowerSpectralDensity(double txPowerW,
-                                                 Ptr<const WifiPpdu> ppdu,
-                                                 const WifiTxVector& txVector) const override;
+                                                 Ptr<const WifiPpdu> ppdu) const override;
     uint32_t GetMaxPsduSize() const override;
     CcaIndication GetCcaIndication(const Ptr<const WifiPpdu> ppdu) override;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ht/ht-ppdu.cc ns-3.38/src/wifi/model/ht/ht-ppdu.cc
--- ns-3.37/src/wifi/model/ht/ht-ppdu.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/ht/ht-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
@@ -47,6 +47,32 @@
                false) // don't instantiate LSigHeader of OfdmPpdu
 {
     NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << ppduDuration << band << uid);
+    SetPhyHeaders(txVector, ppduDuration, psdu->GetSize());
+}
+
+void
+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)
     {
@@ -58,16 +84,17 @@
                              4.0) *
                         3) -
                        3);
-    m_lSig.SetLength(length);
-    m_htSig.SetMcs(txVector.GetMode().GetMcsValue());
-    m_htSig.SetChannelWidth(m_channelWidth);
-    m_htSig.SetHtLength(psdu->GetSize());
-    m_htSig.SetAggregation(txVector.IsAggregation());
-    m_htSig.SetShortGuardInterval(txVector.GetGuardInterval() == 400);
+    lSig.SetLength(length);
 }
 
-HtPpdu::~HtPpdu()
+void
+HtPpdu::SetHtSigHeader(HtSigHeader& htSig, const WifiTxVector& txVector, std::size_t psduSize) const
 {
+    htSig.SetMcs(txVector.GetMode().GetMcsValue());
+    htSig.SetChannelWidth(txVector.GetChannelWidth());
+    htSig.SetHtLength(psduSize);
+    htSig.SetAggregation(txVector.IsAggregation());
+    htSig.SetShortGuardInterval(txVector.GetGuardInterval() == 400);
 }
 
 WifiTxVector
@@ -75,27 +102,70 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-    txVector.SetMode(HtPhy::GetHtMcs(m_htSig.GetMcs()));
-    txVector.SetChannelWidth(m_htSig.GetChannelWidth());
-    txVector.SetNss(1 + (m_htSig.GetMcs() / 8));
-    txVector.SetGuardInterval(m_htSig.GetShortGuardInterval() ? 400 : 800);
-    txVector.SetAggregation(m_htSig.GetAggregation());
+
+#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;
 }
 
+void
+HtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
+                                  const LSigHeader& lSig,
+                                  const HtSigHeader& htSig) const
+{
+    txVector.SetMode(HtPhy::GetHtMcs(htSig.GetMcs()));
+    txVector.SetChannelWidth(htSig.GetChannelWidth());
+    txVector.SetNss(1 + (htSig.GetMcs() / 8));
+    txVector.SetGuardInterval(htSig.GetShortGuardInterval() ? 400 : 800);
+    txVector.SetAggregation(htSig.GetAggregation());
+}
+
 Time
 HtPpdu::GetTxDuration() const
 {
     Time ppduDuration = Seconds(0);
     const WifiTxVector& txVector = GetTxVector();
-    ppduDuration = WifiPhy::CalculateTxDuration(m_htSig.GetHtLength(), txVector, m_band);
+
+    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;
 }
 
 Ptr<WifiPpdu>
 HtPpdu::Copy() const
 {
-    return Create<HtPpdu>(GetPsdu(), GetTxVector(), m_txCenterFreq, GetTxDuration(), m_band, m_uid);
+    return Ptr<WifiPpdu>(new HtPpdu(*this), false);
 }
 
 HtPpdu::HtSigHeader::HtSigHeader()
@@ -107,10 +177,6 @@
 {
 }
 
-HtPpdu::HtSigHeader::~HtSigHeader()
-{
-}
-
 TypeId
 HtPpdu::HtSigHeader::GetTypeId()
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/ht/ht-ppdu.h ns-3.38/src/wifi/model/ht/ht-ppdu.h
--- ns-3.37/src/wifi/model/ht/ht-ppdu.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/ht/ht-ppdu.h	2023-03-16 20:54:23.860869400 -0700
@@ -52,7 +52,6 @@
     {
       public:
         HtSigHeader();
-        ~HtSigHeader() override;
 
         /**
          * \brief Get the type ID.
@@ -151,10 +150,6 @@
            Time ppduDuration,
            WifiPhyBand band,
            uint64_t uid);
-    /**
-     * Destructor for HtPpdu.
-     */
-    ~HtPpdu() override;
 
     Time GetTxDuration() const override;
     Ptr<WifiPpdu> Copy() const override;
@@ -162,8 +157,49 @@
   private:
     WifiTxVector DoGetTxVector() 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
+     * \param psduSize the size duration of the PHY payload (PSDU)
+     */
+    void SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration, std::size_t psduSize);
+
+    /**
+     * 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;
+
+    /**
+     * Fill in the HT-SIG header.
+     *
+     * \param htSig the HT-SIG header to fill in
+     * \param txVector the TXVECTOR that was used for this PPDU
+     * \param psduSize the size duration of the PHY payload (PSDU)
+     */
+    void SetHtSigHeader(HtSigHeader& htSig,
+                        const WifiTxVector& txVector,
+                        std::size_t psduSize) const;
+
+    /**
+     * Fill in the TXVECTOR from PHY headers.
+     *
+     * \param txVector the TXVECTOR to fill in
+     * \param lSig the L-SIG header
+     * \param htSig the HT-SIG header
+     */
+    void SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
+                                   const LSigHeader& lSig,
+                                   const HtSigHeader& htSig) const;
+
+#ifndef NS3_BUILD_PROFILE_DEBUG
     HtSigHeader m_htSig; //!< the HT-SIG PHY header
-};                       // class HtPpdu
+#endif
+}; // class HtPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/interference-helper.cc ns-3.38/src/wifi/model/interference-helper.cc
--- ns-3.37/src/wifi/model/interference-helper.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/interference-helper.cc	2023-03-16 20:54:23.860869400 -0700
@@ -88,7 +88,7 @@
 double
 Event::GetRxPowerW() const
 {
-    NS_ASSERT(m_rxPowerW.size() > 0);
+    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(),
@@ -628,7 +628,7 @@
     }
 
     double psr = 1.0;
-    if (!sections.empty() > 0)
+    if (!sections.empty())
     {
         psr = CalculatePhyHeaderSectionPsr(event, nis, channelWidth, band, sections);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/mac-rx-middle.cc ns-3.38/src/wifi/model/mac-rx-middle.cc
--- ns-3.37/src/wifi/model/mac-rx-middle.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/mac-rx-middle.cc	2023-03-16 20:54:23.860869400 -0700
@@ -301,7 +301,8 @@
 MacRxMiddle::Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
 {
     NS_LOG_FUNCTION(*mpdu << +linkId);
-    const WifiMacHeader* hdr = &mpdu->GetHeader();
+    // consider the MAC header of the original MPDU (makes a difference for data frames only)
+    const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
     NS_ASSERT(hdr->IsData() || hdr->IsMgt());
 
     OriginatorRxStatus* originator = Lookup(hdr);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/mgt-headers.cc ns-3.38/src/wifi/model/mgt-headers.cc
--- ns-3.37/src/wifi/model/mgt-headers.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/mgt-headers.cc	2023-03-16 20:54:23.860869400 -0700
@@ -282,7 +282,10 @@
     i = WifiInformationElement::DeserializeIfPresent(m_htCapability, i);
     i = WifiInformationElement::DeserializeIfPresent(m_vhtCapability, i);
     i = WifiInformationElement::DeserializeIfPresent(m_heCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, 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);
 }
 
@@ -301,7 +304,7 @@
 }
 
 uint64_t
-MgtProbeResponseHeader::GetTimestamp()
+MgtProbeResponseHeader::GetTimestamp() const
 {
     return m_timestamp;
 }
@@ -487,6 +490,24 @@
 }
 
 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;
@@ -709,6 +730,10 @@
     {
         size += m_ehtCapability->GetSerializedSize();
     }
+    if (m_ehtOperation.has_value())
+    {
+        size += m_ehtOperation->GetSerializedSize();
+    }
     return size;
 }
 
@@ -753,6 +778,10 @@
     {
         os << "EHT Capabilities=" << *m_ehtCapability;
     }
+    if (m_ehtOperation.has_value())
+    {
+        os << "EHT Operation=" << *m_ehtOperation;
+    }
 }
 
 void
@@ -824,6 +853,10 @@
     {
         i = m_ehtCapability->Serialize(i);
     }
+    if (m_ehtOperation.has_value())
+    {
+        i = m_ehtOperation->Serialize(i);
+    }
 }
 
 uint32_t
@@ -851,7 +884,11 @@
     i = WifiInformationElement::DeserializeIfPresent(m_heOperation, i);
     i = WifiInformationElement::DeserializeIfPresent(m_muEdcaParameterSet, i);
     i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement, i, WIFI_MAC_MGT_BEACON);
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, 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);
+    i = WifiInformationElement::DeserializeIfPresent(m_ehtOperation, i);
 
     return i.GetDistanceFrom(start);
 }
@@ -1199,7 +1236,10 @@
     i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement,
                                                      i,
                                                      WIFI_MAC_MGT_ASSOCIATION_REQUEST);
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, 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);
 }
 
@@ -1539,7 +1579,10 @@
     i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement,
                                                      i,
                                                      WIFI_MAC_MGT_REASSOCIATION_REQUEST);
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, 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);
 }
 
@@ -1751,6 +1794,24 @@
 }
 
 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;
@@ -1910,6 +1971,10 @@
     {
         size += m_ehtCapability->GetSerializedSize();
     }
+    if (m_ehtOperation.has_value())
+    {
+        size += m_ehtOperation->GetSerializedSize();
+    }
     return size;
 }
 
@@ -1955,6 +2020,10 @@
     {
         os << "EHT Capabilities=" << *m_ehtCapability;
     }
+    if (m_ehtOperation.has_value())
+    {
+        os << "EHT Operation=" << *m_ehtOperation;
+    }
 }
 
 void
@@ -2017,6 +2086,10 @@
     {
         i = m_ehtCapability->Serialize(i);
     }
+    if (m_ehtOperation.has_value())
+    {
+        i = m_ehtOperation->Serialize(i);
+    }
 }
 
 uint32_t
@@ -2042,7 +2115,11 @@
     i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement,
                                                      i,
                                                      WIFI_MAC_MGT_ASSOCIATION_RESPONSE);
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, 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);
+    i = WifiInformationElement::DeserializeIfPresent(m_ehtOperation, i);
     return i.GetDistanceFrom(start);
 }
 
@@ -2111,7 +2188,7 @@
 }
 
 WifiActionHeader::CategoryValue
-WifiActionHeader::GetCategory()
+WifiActionHeader::GetCategory() const
 {
     switch (m_category)
     {
@@ -2144,7 +2221,7 @@
 }
 
 WifiActionHeader::ActionValue
-WifiActionHeader::GetAction()
+WifiActionHeader::GetAction() const
 {
     ActionValue retval;
     retval.selfProtectedAction =
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/mgt-headers.h ns-3.38/src/wifi/model/mgt-headers.h
--- ns-3.37/src/wifi/model/mgt-headers.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/mgt-headers.h	2023-03-16 20:54:23.860869400 -0700
@@ -32,6 +32,7 @@
 
 #include "ns3/dsss-parameter-set.h"
 #include "ns3/eht-capabilities.h"
+#include "ns3/eht-operation.h"
 #include "ns3/erp-information.h"
 #include "ns3/he-capabilities.h"
 #include "ns3/he-operation.h"
@@ -515,6 +516,12 @@
      */
     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 Multi-Link Element information element, if present.
      *
      * \return the Multi-Link Element information element, if present
@@ -689,6 +696,16 @@
     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
@@ -725,6 +742,7 @@
     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
 };
 
@@ -966,6 +984,12 @@
      */
     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
@@ -1087,6 +1111,16 @@
     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
@@ -1178,7 +1212,7 @@
      *
      * \return time stamp
      */
-    uint64_t GetTimestamp();
+    uint64_t GetTimestamp() const;
 
     /**
      * Register this type.
@@ -1209,6 +1243,7 @@
     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
@@ -1432,13 +1467,13 @@
      *
      * \return CategoryValue
      */
-    CategoryValue GetCategory();
+    CategoryValue GetCategory() const;
     /**
      * Return the action value.
      *
      * \return ActionValue
      */
-    ActionValue GetAction();
+    ActionValue GetAction() const;
 
     /**
      * Register this type.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/mpdu-aggregator.cc ns-3.38/src/wifi/model/mpdu-aggregator.cc
--- ns-3.37/src/wifi/model/mpdu-aggregator.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/mpdu-aggregator.cc	2023-03-16 20:54:23.860869400 -0700
@@ -35,6 +35,7 @@
 
 #include "ns3/he-capabilities.h"
 #include "ns3/ht-capabilities.h"
+#include "ns3/ht-frame-exchange-manager.h"
 #include "ns3/log.h"
 #include "ns3/packet.h"
 #include "ns3/vht-capabilities.h"
@@ -56,18 +57,11 @@
     return tid;
 }
 
-MpduAggregator::MpduAggregator()
-{
-}
-
-MpduAggregator::~MpduAggregator()
-{
-}
-
 void
 MpduAggregator::DoDispose()
 {
     m_mac = nullptr;
+    m_htFem = nullptr;
     Object::DoDispose();
 }
 
@@ -76,6 +70,18 @@
 {
     NS_LOG_FUNCTION(this << mac);
     m_mac = mac;
+    m_htFem = DynamicCast<HtFrameExchangeManager>(m_mac->GetFrameExchangeManager(m_linkId));
+}
+
+void
+MpduAggregator::SetLinkId(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << +linkId);
+    m_linkId = linkId;
+    if (m_mac)
+    {
+        m_htFem = DynamicCast<HtFrameExchangeManager>(m_mac->GetFrameExchangeManager(m_linkId));
+    }
 }
 
 void
@@ -137,7 +143,7 @@
         return 0;
     }
 
-    Ptr<WifiRemoteStationManager> stationManager = m_mac->GetWifiRemoteStationManager();
+    Ptr<WifiRemoteStationManager> stationManager = m_mac->GetWifiRemoteStationManager(m_linkId);
     NS_ASSERT(stationManager);
 
     // Retrieve the Capabilities elements advertised by the recipient
@@ -206,12 +212,13 @@
     Mac48Address recipient = mpdu->GetHeader().GetAddr1();
     NS_ASSERT(mpdu->GetHeader().IsQosData() && !recipient.IsBroadcast());
     uint8_t tid = mpdu->GetHeader().GetQosTid();
+    auto origRecipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
 
     Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
     NS_ASSERT(qosTxop);
 
     // Have to make sure that the block ack agreement is established and A-MPDU is enabled
-    if (qosTxop->GetBaAgreementEstablished(recipient, tid) &&
+    if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid) &&
         GetMaxAmpduSize(recipient, tid, txParams.m_txVector.GetModulationClass()) > 0)
     {
         /* here is performed MPDU aggregation */
@@ -228,25 +235,24 @@
             mpduList.push_back(nextMpdu);
 
             // If allowed by the BA agreement, get the next MPDU
-            auto peekedMpdu = qosTxop->PeekNextMpdu(SINGLE_LINK_OP_ID, tid, recipient, nextMpdu);
+            auto peekedMpdu =
+                qosTxop->PeekNextMpdu(m_linkId, tid, origRecipient, nextMpdu->GetOriginal());
             nextMpdu = nullptr;
 
             if (peekedMpdu)
             {
                 // PeekNextMpdu() does not return an MPDU that is beyond the transmit window
                 NS_ASSERT(IsInWindow(peekedMpdu->GetHeader().GetSequenceNumber(),
-                                     qosTxop->GetBaStartingSequence(recipient, tid),
-                                     qosTxop->GetBaBufferSize(recipient, tid)));
+                                     qosTxop->GetBaStartingSequence(origRecipient, tid),
+                                     qosTxop->GetBaBufferSize(origRecipient, tid)));
 
+                peekedMpdu = m_htFem->CreateAliasIfNeeded(peekedMpdu);
                 // get the next MPDU to aggregate, provided that the constraints on size
                 // and duration limit are met. Note that the returned MPDU differs from
                 // the peeked MPDU if A-MSDU aggregation is enabled.
                 NS_LOG_DEBUG("Trying to aggregate another MPDU");
-                nextMpdu = qosTxop->GetNextMpdu(SINGLE_LINK_OP_ID,
-                                                peekedMpdu,
-                                                txParams,
-                                                availableTime,
-                                                false);
+                nextMpdu =
+                    qosTxop->GetNextMpdu(m_linkId, peekedMpdu, txParams, availableTime, false);
             }
         }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/mpdu-aggregator.h ns-3.38/src/wifi/model/mpdu-aggregator.h
--- ns-3.37/src/wifi/model/mpdu-aggregator.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/mpdu-aggregator.h	2023-03-16 20:54:23.860869400 -0700
@@ -38,6 +38,7 @@
 class WifiMac;
 class WifiMpdu;
 class WifiTxParameters;
+class HtFrameExchangeManager;
 
 /**
  * \brief Aggregator used to construct A-MPDUs
@@ -57,8 +58,8 @@
      */
     static TypeId GetTypeId();
 
-    MpduAggregator();
-    ~MpduAggregator() override;
+    MpduAggregator() = default;
+    ~MpduAggregator() override = default;
 
     /**
      * Aggregate an MPDU to an A-MPDU.
@@ -70,6 +71,13 @@
     static void Aggregate(Ptr<const WifiMpdu> mpdu, Ptr<Packet> ampdu, bool isSingle);
 
     /**
+     * Set the ID of the link this MPDU aggregator is associated with.
+     *
+     * \param linkId the ID of the link this MPDU aggregator is associated with
+     */
+    void SetLinkId(uint8_t linkId);
+
+    /**
      * Compute the size of the A-MPDU resulting from the aggregation of an MPDU of
      * size <i>mpduSize</i> and an A-MPDU of size <i>ampduSize</i>.
      *
@@ -158,7 +166,9 @@
     void DoDispose() override;
 
   private:
-    Ptr<WifiMac> m_mac; //!< the MAC of this station
+    Ptr<WifiMac> m_mac;                  //!< the MAC of this station
+    Ptr<HtFrameExchangeManager> m_htFem; //!< the HT Frame Exchange Manager of this station
+    uint8_t m_linkId{0};                 //!< ID of the link this object is connected to
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/msdu-aggregator.cc ns-3.38/src/wifi/model/msdu-aggregator.cc
--- ns-3.37/src/wifi/model/msdu-aggregator.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/msdu-aggregator.cc	2023-03-16 20:54:23.860869400 -0700
@@ -51,14 +51,6 @@
     return tid;
 }
 
-MsduAggregator::MsduAggregator()
-{
-}
-
-MsduAggregator::~MsduAggregator()
-{
-}
-
 void
 MsduAggregator::DoDispose()
 {
@@ -72,7 +64,18 @@
 {
     NS_LOG_FUNCTION(this << mac);
     m_mac = mac;
-    m_htFem = DynamicCast<HtFrameExchangeManager>(m_mac->GetFrameExchangeManager());
+    m_htFem = DynamicCast<HtFrameExchangeManager>(m_mac->GetFrameExchangeManager(m_linkId));
+}
+
+void
+MsduAggregator::SetLinkId(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << +linkId);
+    m_linkId = linkId;
+    if (m_mac)
+    {
+        m_htFem = DynamicCast<HtFrameExchangeManager>(m_mac->GetFrameExchangeManager(m_linkId));
+    }
 }
 
 uint16_t
@@ -94,7 +97,7 @@
     Ptr<WifiMacQueue> queue = m_mac->GetTxopQueue(peekedItem->GetQueueAc());
 
     uint8_t tid = peekedItem->GetHeader().GetQosTid();
-    Mac48Address recipient = peekedItem->GetHeader().GetAddr1();
+    auto recipient = peekedItem->GetOriginal()->GetHeader().GetAddr1();
 
     /* "The Address 1 field of an MPDU carrying an A-MSDU shall be set to an
      * individual address or to the GCR concealment address" (Section 10.12
@@ -120,15 +123,18 @@
         return nullptr;
     }
 
-    Ptr<WifiMpdu> amsdu = peekedItem;
+    Ptr<WifiMpdu> amsdu = queue->GetOriginal(peekedItem);
     uint8_t nMsdu = 1;
-    peekedItem = queue->PeekByTidAndAddress(tid, recipient, peekedItem);
+    peekedItem = queue->PeekByTidAndAddress(tid, recipient, peekedItem->GetOriginal());
 
-    while (peekedItem && m_htFem->TryAggregateMsdu(peekedItem, txParams, availableTime))
+    while (peekedItem &&
+           m_htFem->TryAggregateMsdu(peekedItem = m_htFem->CreateAliasIfNeeded(peekedItem),
+                                     txParams,
+                                     availableTime))
     {
         // find the next MPDU before dequeuing the current one
-        Ptr<const WifiMpdu> msdu = peekedItem;
-        peekedItem = queue->PeekByTidAndAddress(tid, recipient, peekedItem);
+        Ptr<const WifiMpdu> msdu = peekedItem->GetOriginal();
+        peekedItem = queue->PeekByTidAndAddress(tid, recipient, msdu);
         queue->DequeueIfQueued({amsdu});
         // perform A-MSDU aggregation
         amsdu->Aggregate(msdu);
@@ -144,7 +150,7 @@
     }
 
     // Aggregation succeeded
-    return amsdu;
+    return m_htFem->CreateAliasIfNeeded(amsdu);
 }
 
 uint8_t
@@ -171,13 +177,27 @@
         return 0;
     }
 
-    Ptr<WifiRemoteStationManager> stationManager = m_mac->GetWifiRemoteStationManager();
+    Ptr<WifiRemoteStationManager> stationManager = m_mac->GetWifiRemoteStationManager(m_linkId);
     NS_ASSERT(stationManager);
 
     // Retrieve the Capabilities elements advertised by the recipient
-    Ptr<const VhtCapabilities> vhtCapabilities =
-        stationManager->GetStationVhtCapabilities(recipient);
-    Ptr<const HtCapabilities> htCapabilities = stationManager->GetStationHtCapabilities(recipient);
+    auto ehtCapabilities = stationManager->GetStationEhtCapabilities(recipient);
+    auto vhtCapabilities = stationManager->GetStationVhtCapabilities(recipient);
+    auto htCapabilities = stationManager->GetStationHtCapabilities(recipient);
+
+    // Determine the maximum MPDU size, which is used to indirectly constrain the maximum
+    // A-MSDU size in some cases (see below). The maximum MPDU size is advertised
+    // in the EHT Capabilities element, for the 2.4 GHz band, or in the VHT Capabilities
+    // element, otherwise.
+    uint16_t maxMpduSize = 0;
+    if (ehtCapabilities && m_mac->GetWifiPhy(m_linkId)->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
+    {
+        maxMpduSize = ehtCapabilities->GetMaxMpduLength();
+    }
+    else if (vhtCapabilities && m_mac->GetWifiPhy(m_linkId)->GetPhyBand() != WIFI_PHY_BAND_2_4GHZ)
+    {
+        maxMpduSize = vhtCapabilities->GetMaxMpduLength();
+    }
 
     if (!htCapabilities)
     {
@@ -192,29 +212,49 @@
 
     // Determine the constraint imposed by the recipient based on the PPDU
     // format used to transmit the A-MSDU
-    if (modulation >= WIFI_MOD_CLASS_VHT)
+    if (modulation >= WIFI_MOD_CLASS_EHT)
+    {
+        // the maximum A-MSDU size is indirectly constrained by the maximum MPDU size
+        // supported by the recipient (see Table 9-34 of 802.11be D2.0)
+        NS_ABORT_MSG_IF(maxMpduSize == 0, "Max MPDU size not advertised");
+        maxAmsduSize = std::min(maxAmsduSize, static_cast<uint16_t>(maxMpduSize - 56));
+    }
+    else if (modulation == WIFI_MOD_CLASS_HE)
+    {
+        // for a non-EHT STA operating in the 2.4 GHz band, the maximum A-MSDU size is
+        // advertised in the HT Capabilities element. Otherwise, the maximum A-MSDU size is
+        // indirectly constrained by the maximum MPDU size supported by the recipient
+        // (see Table 9-34 of 802.11be D2.0)
+        if (m_mac->GetWifiPhy(m_linkId)->GetStandard() < WIFI_STANDARD_80211be &&
+            m_mac->GetWifiPhy(m_linkId)->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
+        {
+            maxAmsduSize = std::min(maxAmsduSize, htCapabilities->GetMaxAmsduLength());
+        }
+        else
+        {
+            NS_ABORT_MSG_IF(maxMpduSize == 0, "Max MPDU size not advertised");
+            maxAmsduSize = std::min(maxAmsduSize, static_cast<uint16_t>(maxMpduSize - 56));
+        }
+    }
+    else if (modulation == WIFI_MOD_CLASS_VHT)
     {
         // the maximum A-MSDU size is indirectly constrained by the maximum MPDU
         // size supported by the recipient and advertised in the VHT Capabilities
-        // element (see Table 9-19 of 802.11-2016 as amended by 802.11ax)
-        NS_ABORT_MSG_IF(!vhtCapabilities, "VHT Capabilities element not received");
-
-        maxAmsduSize =
-            std::min(maxAmsduSize, static_cast<uint16_t>(vhtCapabilities->GetMaxMpduLength() - 56));
+        // element (see Table 9-25 of 802.11-2020)
+        NS_ABORT_MSG_IF(maxMpduSize == 0, "Max MPDU size not advertised");
+        maxAmsduSize = std::min(maxAmsduSize, static_cast<uint16_t>(maxMpduSize - 56));
     }
-    else if (modulation == WIFI_MOD_CLASS_HT)
+    else if (modulation >= WIFI_MOD_CLASS_HT)
     {
         // the maximum A-MSDU size is constrained by the maximum A-MSDU size
         // supported by the recipient and advertised in the HT Capabilities
         // element (see Table 9-19 of 802.11-2016)
-
         maxAmsduSize = std::min(maxAmsduSize, htCapabilities->GetMaxAmsduLength());
     }
     else // non-HT PPDU
     {
         // the maximum A-MSDU size is indirectly constrained by the maximum PSDU size
         // supported by the recipient (see Table 9-19 of 802.11-2016)
-
         maxAmsduSize = std::min(maxAmsduSize, static_cast<uint16_t>(3839));
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/msdu-aggregator.h ns-3.38/src/wifi/model/msdu-aggregator.h
--- ns-3.37/src/wifi/model/msdu-aggregator.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/msdu-aggregator.h	2023-03-16 20:54:23.860869400 -0700
@@ -55,8 +55,15 @@
      */
     static TypeId GetTypeId();
 
-    MsduAggregator();
-    ~MsduAggregator() override;
+    MsduAggregator() = default;
+    ~MsduAggregator() override = default;
+
+    /**
+     * Set the ID of the link this MSDU aggregator is associated with.
+     *
+     * \param linkId the ID of the link this MSDU aggregator is associated with
+     */
+    void SetLinkId(uint8_t linkId);
 
     /**
      * Compute the size of the A-MSDU resulting from the aggregation of an MSDU of
@@ -148,6 +155,7 @@
   private:
     Ptr<WifiMac> m_mac;                  //!< the MAC of this station
     Ptr<HtFrameExchangeManager> m_htFem; //!< the HT Frame Exchange Manager of this station
+    uint8_t m_linkId{0};                 //!< ID of the link this object is connected to
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/dsss-error-rate-model.cc ns-3.38/src/wifi/model/non-ht/dsss-error-rate-model.cc
--- ns-3.37/src/wifi/model/non-ht/dsss-error-rate-model.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/dsss-error-rate-model.cc	2023-03-16 20:54:23.860869400 -0700
@@ -23,6 +23,13 @@
 
 #include <cmath>
 
+#ifdef HAVE_GSL
+#include <gsl/gsl_cdf.h>
+#include <gsl/gsl_integration.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_sf_bessel.h>
+#endif
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/dsss-error-rate-model.h ns-3.38/src/wifi/model/non-ht/dsss-error-rate-model.h
--- ns-3.37/src/wifi/model/non-ht/dsss-error-rate-model.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/dsss-error-rate-model.h	2023-03-16 20:54:23.860869400 -0700
@@ -20,13 +20,6 @@
 #ifndef DSSS_ERROR_RATE_MODEL_H
 #define DSSS_ERROR_RATE_MODEL_H
 
-#ifdef HAVE_GSL
-#include <gsl/gsl_cdf.h>
-#include <gsl/gsl_integration.h>
-#include <gsl/gsl_math.h>
-#include <gsl/gsl_sf_bessel.h>
-#endif
-
 #include <cstdint>
 
 namespace ns3
@@ -36,11 +29,11 @@
 /**
  * Structure for integral function parameters
  */
-typedef struct FunctionParameterType
+struct FunctionParameters
 {
     double beta; ///< Beta parameter
     double n;    ///< n parameter
-} FunctionParameters;
+};
 
 /**
  * Integral function using GSL library
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/dsss-phy.cc ns-3.38/src/wifi/model/non-ht/dsss-phy.cc
--- ns-3.37/src/wifi/model/non-ht/dsss-phy.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/dsss-phy.cc	2023-03-16 20:54:23.860869400 -0700
@@ -49,7 +49,7 @@
                              WIFI_PPDU_FIELD_DATA } },
     { WIFI_PREAMBLE_SHORT, { WIFI_PPDU_FIELD_PREAMBLE,      // Short PHY preamble
                              WIFI_PPDU_FIELD_NON_HT_HEADER, // Short PHY header
-                             WIFI_PPDU_FIELD_DATA } }
+                             WIFI_PPDU_FIELD_DATA } },
 };
 
 const PhyEntity::ModulationLookupTable DsssPhy::m_dsssModulationLookupTable {
@@ -57,7 +57,7 @@
   { "DsssRate1Mbps",   { WIFI_CODE_RATE_UNDEFINED, 2 } },
   { "DsssRate2Mbps",   { WIFI_CODE_RATE_UNDEFINED, 4 } },
   { "DsssRate5_5Mbps", { WIFI_CODE_RATE_UNDEFINED, 16 } },
-  { "DsssRate11Mbps",  { WIFI_CODE_RATE_UNDEFINED, 256 } }
+  { "DsssRate11Mbps",  { WIFI_CODE_RATE_UNDEFINED, 256 } },
 };
 
 // clang-format on
@@ -265,10 +265,9 @@
 }
 
 Ptr<SpectrumValue>
-DsssPhy::GetTxPowerSpectralDensity(double txPowerW,
-                                   Ptr<const WifiPpdu> /* ppdu */,
-                                   const WifiTxVector& txVector) const
+DsssPhy::GetTxPowerSpectralDensity(double txPowerW, Ptr<const WifiPpdu> ppdu) const
 {
+    const auto& txVector = ppdu->GetTxVector();
     uint16_t centerFrequency = GetCenterFrequencyForChannelWidth(txVector);
     uint16_t channelWidth = txVector.GetChannelWidth();
     NS_LOG_FUNCTION(this << centerFrequency << channelWidth << txPowerW);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/dsss-phy.h ns-3.38/src/wifi/model/non-ht/dsss-phy.h
--- ns-3.37/src/wifi/model/non-ht/dsss-phy.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/dsss-phy.h	2023-03-16 20:54:23.860869400 -0700
@@ -161,8 +161,7 @@
   private:
     PhyFieldRxStatus DoEndReceiveField(WifiPpduField field, Ptr<Event> event) override;
     Ptr<SpectrumValue> GetTxPowerSpectralDensity(double txPowerW,
-                                                 Ptr<const WifiPpdu> ppdu,
-                                                 const WifiTxVector& txVector) const override;
+                                                 Ptr<const WifiPpdu> ppdu) const override;
     uint16_t GetRxChannelWidth(const WifiTxVector& txVector) const override;
     uint16_t GetMeasurementChannelWidth(const Ptr<const WifiPpdu> ppdu) const override;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/dsss-ppdu.cc ns-3.38/src/wifi/model/non-ht/dsss-ppdu.cc
--- ns-3.37/src/wifi/model/non-ht/dsss-ppdu.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/dsss-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
@@ -40,13 +40,31 @@
     : WifiPpdu(psdu, txVector, txCenterFreq, uid)
 {
     NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << ppduDuration << uid);
-    m_dsssSig.SetRate(txVector.GetMode().GetDataRate(22));
-    Time psduDuration = ppduDuration - WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
-    m_dsssSig.SetLength(psduDuration.GetMicroSeconds());
+    SetPhyHeaders(txVector, ppduDuration);
 }
 
-DsssPpdu::~DsssPpdu()
+void
+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
+DsssPpdu::SetDsssHeader(DsssSigHeader& dsssSig,
+                        const WifiTxVector& txVector,
+                        Time ppduDuration) const
+{
+    dsssSig.SetRate(txVector.GetMode().GetDataRate(22));
+    Time psduDuration = ppduDuration - WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
+    dsssSig.SetLength(psduDuration.GetMicroSeconds());
 }
 
 WifiTxVector
@@ -54,25 +72,50 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-    txVector.SetMode(DsssPhy::GetDsssRate(m_dsssSig.GetRate()));
     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;
 }
 
+void
+DsssPpdu::SetTxVectorFromDsssHeader(WifiTxVector& txVector, const DsssSigHeader& dsssSig) const
+{
+    txVector.SetMode(DsssPhy::GetDsssRate(dsssSig.GetRate()));
+}
+
 Time
 DsssPpdu::GetTxDuration() const
 {
     Time ppduDuration = Seconds(0);
     const WifiTxVector& txVector = GetTxVector();
-    ppduDuration = MicroSeconds(m_dsssSig.GetLength()) +
-                   WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
+    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;
 }
 
 Ptr<WifiPpdu>
 DsssPpdu::Copy() const
 {
-    return Create<DsssPpdu>(GetPsdu(), GetTxVector(), m_txCenterFreq, GetTxDuration(), m_uid);
+    return Ptr<WifiPpdu>(new DsssPpdu(*this), false);
 }
 
 DsssPpdu::DsssSigHeader::DsssSigHeader()
@@ -81,10 +124,6 @@
 {
 }
 
-DsssPpdu::DsssSigHeader::~DsssSigHeader()
-{
-}
-
 TypeId
 DsssPpdu::DsssSigHeader::GetTypeId()
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/dsss-ppdu.h ns-3.38/src/wifi/model/non-ht/dsss-ppdu.h
--- ns-3.37/src/wifi/model/non-ht/dsss-ppdu.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/dsss-ppdu.h	2023-03-16 20:54:23.860869400 -0700
@@ -53,7 +53,6 @@
     {
       public:
         DsssSigHeader();
-        ~DsssSigHeader() override;
 
         /**
          * \brief Get the type ID.
@@ -111,10 +110,6 @@
              uint16_t txCenterFreq,
              Time ppduDuration,
              uint64_t uid);
-    /**
-     * Destructor for DsssPpdu.
-     */
-    ~DsssPpdu() override;
 
     Time GetTxDuration() const override;
     Ptr<WifiPpdu> Copy() const override;
@@ -122,8 +117,38 @@
   private:
     WifiTxVector DoGetTxVector() 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 DSSS header.
+     *
+     * \param dsssSig the DSSS header to fill in
+     * \param txVector the TXVECTOR that was used for this PPDU
+     * \param ppduDuration the transmission duration of this PPDU
+     */
+    void SetDsssHeader(DsssSigHeader& dsssSig,
+                       const WifiTxVector& txVector,
+                       Time ppduDuration) const;
+
+    /**
+     * Fill in the TXVECTOR from DSSS header.
+     *
+     * \param txVector the TXVECTOR to fill in
+     * \param dsssSig the DSSS header
+     */
+    virtual void SetTxVectorFromDsssHeader(WifiTxVector& txVector,
+                                           const DsssSigHeader& dsssSig) const;
+
+#ifndef NS3_BUILD_PROFILE_DEBUG
     DsssSigHeader m_dsssSig; //!< the DSSS SIG PHY header
-};                           // class DsssPpdu
+#endif
+}; // class DsssPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/erp-ofdm-phy.cc ns-3.38/src/wifi/model/non-ht/erp-ofdm-phy.cc
--- ns-3.37/src/wifi/model/non-ht/erp-ofdm-phy.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/erp-ofdm-phy.cc	2023-03-16 20:54:23.860869400 -0700
@@ -113,12 +113,14 @@
                       Time /* ppduDuration */)
 {
     NS_LOG_FUNCTION(this << psdus << txVector);
-    return Create<ErpOfdmPpdu>(psdus.begin()->second,
-                               txVector,
-                               m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                                   txVector.GetChannelWidth()),
-                               m_wifiPhy->GetPhyBand(),
-                               ObtainNextUid(txVector));
+    return Create<ErpOfdmPpdu>(
+        psdus.begin()->second,
+        txVector,
+        m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
+            txVector.GetChannelWidth()),
+        m_wifiPhy->GetPhyBand(),
+        m_wifiPhy->GetLatestPhyEntity()->ObtainNextUid(
+            txVector)); // use latest PHY entity to handle MU-RTS sent with non-HT rate
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/erp-ofdm-ppdu.cc ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.cc
--- ns-3.37/src/wifi/model/non-ht/erp-ofdm-ppdu.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
@@ -35,30 +35,22 @@
                          uint16_t txCenterFreq,
                          WifiPhyBand band,
                          uint64_t uid)
-    : OfdmPpdu(psdu, txVector, txCenterFreq, band, uid, true) // instantiate LSigHeader of OfdmPpdu
+    : OfdmPpdu(psdu, txVector, txCenterFreq, band, uid, true) // add LSigHeader of OfdmPpdu
 {
     NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << band << uid);
 }
 
-ErpOfdmPpdu::~ErpOfdmPpdu()
+void
+ErpOfdmPpdu::SetTxVectorFromLSigHeader(WifiTxVector& txVector, const LSigHeader& lSig) const
 {
-}
-
-WifiTxVector
-ErpOfdmPpdu::DoGetTxVector() const
-{
-    WifiTxVector txVector;
-    txVector.SetPreambleType(m_preamble);
-    NS_ASSERT(m_channelWidth == 20);
-    txVector.SetMode(ErpOfdmPhy::GetErpOfdmRate(m_lSig.GetRate()));
-    txVector.SetChannelWidth(m_channelWidth);
-    return txVector;
+    txVector.SetMode(ErpOfdmPhy::GetErpOfdmRate(lSig.GetRate()));
+    txVector.SetChannelWidth(20);
 }
 
 Ptr<WifiPpdu>
 ErpOfdmPpdu::Copy() const
 {
-    return Create<ErpOfdmPpdu>(GetPsdu(), GetTxVector(), m_txCenterFreq, m_band, m_uid);
+    return Ptr<WifiPpdu>(new ErpOfdmPpdu(*this), false);
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/erp-ofdm-ppdu.h ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.h
--- ns-3.37/src/wifi/model/non-ht/erp-ofdm-ppdu.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.h	2023-03-16 20:54:23.860869400 -0700
@@ -58,15 +58,11 @@
                 uint16_t txCenterFreq,
                 WifiPhyBand band,
                 uint64_t uid);
-    /**
-     * Destructor for ErpOfdmPpdu.
-     */
-    ~ErpOfdmPpdu() override;
 
     Ptr<WifiPpdu> Copy() const override;
 
   private:
-    WifiTxVector DoGetTxVector() const override;
+    void SetTxVectorFromLSigHeader(WifiTxVector& txVector, const LSigHeader& lSig) const override;
 }; // class ErpOfdmPpdu
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/ofdm-phy.cc ns-3.38/src/wifi/model/non-ht/ofdm-phy.cc
--- ns-3.37/src/wifi/model/non-ht/ofdm-phy.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/ofdm-phy.cc	2023-03-16 20:54:23.860869400 -0700
@@ -87,7 +87,8 @@
        12000000, 18000000, 24000000, 27000000 }},
    { 5, // MHz
      {  1500000,  2250000,  3000000,  4500000,
-        6000000,  9000000, 12000000, 13500000 }}};
+        6000000,  9000000, 12000000, 13500000 }},
+};
 
 // clang-format on
 
@@ -294,12 +295,14 @@
                    Time /* ppduDuration */)
 {
     NS_LOG_FUNCTION(this << psdus << txVector);
-    return Create<OfdmPpdu>(psdus.begin()->second,
-                            txVector,
-                            m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                                txVector.GetChannelWidth()),
-                            m_wifiPhy->GetPhyBand(),
-                            ObtainNextUid(txVector));
+    return Create<OfdmPpdu>(
+        psdus.begin()->second,
+        txVector,
+        m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
+            txVector.GetChannelWidth()),
+        m_wifiPhy->GetPhyBand(),
+        m_wifiPhy->GetLatestPhyEntity()->ObtainNextUid(
+            txVector)); // use latest PHY entity to handle MU-RTS sent with non-HT rate
 }
 
 PhyEntity::PhyFieldRxStatus
@@ -361,22 +364,36 @@
 }
 
 Ptr<SpectrumValue>
-OfdmPhy::GetTxPowerSpectralDensity(double txPowerW,
-                                   Ptr<const WifiPpdu> /* ppdu */,
-                                   const WifiTxVector& txVector) const
+OfdmPhy::GetTxPowerSpectralDensity(double txPowerW, Ptr<const WifiPpdu> ppdu) const
 {
+    const auto& txVector = ppdu->GetTxVector();
     uint16_t centerFrequency = GetCenterFrequencyForChannelWidth(txVector);
     uint16_t channelWidth = txVector.GetChannelWidth();
     NS_LOG_FUNCTION(this << centerFrequency << channelWidth << txPowerW);
     const auto& txMaskRejectionParams = GetTxMaskRejectionParams();
-    Ptr<SpectrumValue> v = WifiSpectrumValueHelper::CreateOfdmTxPowerSpectralDensity(
-        centerFrequency,
-        channelWidth,
-        txPowerW,
-        GetGuardBandwidth(channelWidth),
-        std::get<0>(txMaskRejectionParams),
-        std::get<1>(txMaskRejectionParams),
-        std::get<2>(txMaskRejectionParams));
+    Ptr<SpectrumValue> v;
+    if (txVector.IsNonHtDuplicate())
+    {
+        v = WifiSpectrumValueHelper::CreateDuplicated20MhzTxPowerSpectralDensity(
+            centerFrequency,
+            channelWidth,
+            txPowerW,
+            GetGuardBandwidth(channelWidth),
+            std::get<0>(txMaskRejectionParams),
+            std::get<1>(txMaskRejectionParams),
+            std::get<2>(txMaskRejectionParams));
+    }
+    else
+    {
+        v = WifiSpectrumValueHelper::CreateOfdmTxPowerSpectralDensity(
+            centerFrequency,
+            channelWidth,
+            txPowerW,
+            GetGuardBandwidth(channelWidth),
+            std::get<0>(txMaskRejectionParams),
+            std::get<1>(txMaskRejectionParams),
+            std::get<2>(txMaskRejectionParams));
+    }
     return v;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/ofdm-phy.h ns-3.38/src/wifi/model/non-ht/ofdm-phy.h
--- ns-3.37/src/wifi/model/non-ht/ofdm-phy.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/ofdm-phy.h	2023-03-16 20:54:23.860869400 -0700
@@ -324,8 +324,7 @@
   protected:
     PhyFieldRxStatus DoEndReceiveField(WifiPpduField field, Ptr<Event> event) override;
     Ptr<SpectrumValue> GetTxPowerSpectralDensity(double txPowerW,
-                                                 Ptr<const WifiPpdu> ppdu,
-                                                 const WifiTxVector& txVector) const override;
+                                                 Ptr<const WifiPpdu> ppdu) const override;
     uint32_t GetMaxPsduSize() const override;
     uint16_t GetMeasurementChannelWidth(const Ptr<const WifiPpdu> ppdu) const override;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/ofdm-ppdu.cc ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.cc
--- ns-3.37/src/wifi/model/non-ht/ofdm-ppdu.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
@@ -40,18 +40,34 @@
                    bool instantiateLSig /* = true */)
     : WifiPpdu(psdu, txVector, txCenterFreq, uid),
       m_band(band),
-      m_channelWidth(txVector.GetChannelWidth())
+      m_channelWidth(txVector.IsNonHtDuplicate() ? 20 : txVector.GetChannelWidth())
 {
     NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << band << uid);
     if (instantiateLSig)
     {
-        m_lSig.SetRate(txVector.GetMode().GetDataRate(txVector), m_channelWidth);
-        m_lSig.SetLength(psdu->GetSize());
+        SetPhyHeaders(txVector, psdu->GetSize());
     }
 }
 
-OfdmPpdu::~OfdmPpdu()
+void
+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
+OfdmPpdu::SetLSigHeader(LSigHeader& lSig, const WifiTxVector& txVector, std::size_t psduSize) const
+{
+    lSig.SetRate(txVector.GetMode().GetDataRate(txVector), m_channelWidth);
+    lSig.SetLength(psduSize);
 }
 
 WifiTxVector
@@ -59,26 +75,50 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-    // OFDM uses 20 MHz, unless PHY channel width is 5 MHz or 10 MHz
-    uint16_t channelWidth = m_channelWidth < 20 ? m_channelWidth : 20;
-    txVector.SetMode(OfdmPhy::GetOfdmRate(m_lSig.GetRate(m_channelWidth), channelWidth));
-    txVector.SetChannelWidth(channelWidth);
+
+#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;
 }
 
+void
+OfdmPpdu::SetTxVectorFromLSigHeader(WifiTxVector& txVector, const LSigHeader& lSig) const
+{
+    NS_ASSERT(m_channelWidth <= 20);
+    // OFDM uses 20 MHz, unless PHY channel width is 5 MHz or 10 MHz
+    txVector.SetMode(OfdmPhy::GetOfdmRate(lSig.GetRate(m_channelWidth), m_channelWidth));
+    txVector.SetChannelWidth(m_channelWidth);
+}
+
 Time
 OfdmPpdu::GetTxDuration() const
 {
-    Time ppduDuration = Seconds(0);
     const WifiTxVector& txVector = GetTxVector();
-    ppduDuration = WifiPhy::CalculateTxDuration(m_lSig.GetLength(), txVector, m_band);
-    return ppduDuration;
+    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);
 }
 
 Ptr<WifiPpdu>
 OfdmPpdu::Copy() const
 {
-    return Create<OfdmPpdu>(GetPsdu(), GetTxVector(), m_txCenterFreq, m_band, m_uid);
+    return Ptr<WifiPpdu>(new OfdmPpdu(*this), false);
 }
 
 OfdmPpdu::LSigHeader::LSigHeader()
@@ -87,10 +127,6 @@
 {
 }
 
-OfdmPpdu::LSigHeader::~LSigHeader()
-{
-}
-
 TypeId
 OfdmPpdu::LSigHeader::GetTypeId()
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/non-ht/ofdm-ppdu.h ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.h
--- ns-3.37/src/wifi/model/non-ht/ofdm-ppdu.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.h	2023-03-16 20:54:23.860869400 -0700
@@ -55,7 +55,6 @@
     {
       public:
         LSigHeader();
-        ~LSigHeader() override;
 
         /**
          * \brief Get the type ID.
@@ -118,22 +117,47 @@
              WifiPhyBand band,
              uint64_t uid,
              bool instantiateLSig = true);
-    /**
-     * Destructor for OfdmPpdu.
-     */
-    ~OfdmPpdu() override;
 
     Time GetTxDuration() const override;
     Ptr<WifiPpdu> Copy() const override;
 
   protected:
-    WifiPhyBand m_band;      //!< the WifiPhyBand used to transmit that PPDU
-    uint16_t m_channelWidth; //!< the channel width used to transmit that PPDU in MHz
-    LSigHeader m_lSig;       //!< the L-SIG PHY header
+    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;
-}; // class OfdmPpdu
+
+    /**
+     * Fill in the PHY headers.
+     *
+     * \param txVector the TXVECTOR that was used for this PPDU
+     * \param psduSize the size duration of the PHY payload (PSDU)
+     */
+    void SetPhyHeaders(const WifiTxVector& txVector, std::size_t psduSize);
+
+    /**
+     * Fill in the L-SIG header.
+     *
+     * \param lSig the L-SIG header to fill in
+     * \param txVector the TXVECTOR that was used for this PPDU
+     * \param psduSize the size duration of the PHY payload (PSDU)
+     */
+    void SetLSigHeader(LSigHeader& lSig, const WifiTxVector& txVector, std::size_t psduSize) const;
+
+    /**
+     * Fill in the TXVECTOR from L-SIG header.
+     *
+     * \param txVector the TXVECTOR to fill in
+     * \param lSig the L-SIG header
+     */
+    virtual void SetTxVectorFromLSigHeader(WifiTxVector& txVector, const LSigHeader& lSig) const;
+
+    uint16_t m_channelWidth; //!< the channel width used to transmit that PPDU in MHz (needed to
+                             //!< distinguish 5 MHz, 10 MHz or 20 MHz PPDUs)
+};                           // class OfdmPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/phy-entity.cc ns-3.38/src/wifi/model/phy-entity.cc
--- ns-3.37/src/wifi/model/phy-entity.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/phy-entity.cc	2023-03-16 20:54:23.860869400 -0700
@@ -239,11 +239,9 @@
 }
 
 Ptr<WifiPpdu>
-PhyEntity::BuildPpdu(const WifiConstPsduMap& psdus,
-                     const WifiTxVector& txVector,
-                     Time /* ppduDuration */)
+PhyEntity::BuildPpdu(const WifiConstPsduMap& psdus, const WifiTxVector& txVector, Time ppduDuration)
 {
-    NS_LOG_FUNCTION(this << psdus << txVector);
+    NS_LOG_FUNCTION(this << psdus << txVector << ppduDuration);
     NS_FATAL_ERROR("This method is unsupported for the base PhyEntity class. Use the overloaded "
                    "version in the amendment-specific subclasses instead!");
     return Create<WifiPpdu>(psdus.begin()->second,
@@ -410,7 +408,9 @@
            const std::pair<WifiSpectrumBand, double>& p2) { return p1.second < p2.second; });
     NS_LOG_FUNCTION(this << ppdu << it->second);
 
-    Ptr<Event> event = DoGetEvent(ppdu, rxPowersW);
+    Ptr<Event> event = m_wifiPhy->GetPhyEntityForPpdu(ppdu)->DoGetEvent(
+        ppdu,
+        rxPowersW); // use latest PHY entity to handle MU-RTS sent with non-HT rate
     if (!event)
     {
         // PPDU should be simply considered as interference (once it has been accounted for in
@@ -1279,30 +1279,31 @@
 }
 
 void
-PhyEntity::StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector)
+PhyEntity::StartTx(Ptr<const WifiPpdu> ppdu)
 {
-    NS_LOG_FUNCTION(this << ppdu << txVector);
-    Transmit(ppdu->GetTxDuration(), ppdu, txVector, "transmission");
+    NS_LOG_FUNCTION(this << ppdu);
+    auto txPowerDbm = m_wifiPhy->GetTxPowerForTransmission(ppdu) + m_wifiPhy->GetTxGain();
+    auto txVector = ppdu->GetTxVector();
+    auto txPowerSpectrum = GetTxPowerSpectralDensity(DbmToW(txPowerDbm), ppdu);
+    Transmit(ppdu->GetTxDuration(), ppdu, txPowerDbm, txPowerSpectrum, "transmission");
 }
 
 void
 PhyEntity::Transmit(Time txDuration,
                     Ptr<const WifiPpdu> ppdu,
-                    const WifiTxVector& txVector,
-                    std::string type)
-{
-    NS_LOG_FUNCTION(this << txDuration << ppdu << txVector << type);
-    double txPowerWatts =
-        DbmToW(m_wifiPhy->GetTxPowerForTransmission(ppdu) + m_wifiPhy->GetTxGain());
-    NS_LOG_DEBUG("Start " << type << ": signal power before antenna gain=" << WToDbm(txPowerWatts)
-                          << "dBm");
-    Ptr<SpectrumValue> txPowerSpectrum = GetTxPowerSpectralDensity(txPowerWatts, ppdu, txVector);
-    Ptr<WifiSpectrumSignalParameters> txParams = Create<WifiSpectrumSignalParameters>();
+                    double txPowerDbm,
+                    Ptr<SpectrumValue> txPowerSpectrum,
+                    const std::string& type)
+{
+    NS_LOG_FUNCTION(this << txDuration << ppdu << txPowerDbm << type);
+    NS_LOG_DEBUG("Start " << type << ": signal power before antenna gain=" << txPowerDbm << "dBm");
+    auto txParams = Create<WifiSpectrumSignalParameters>();
     txParams->duration = txDuration;
     txParams->psd = txPowerSpectrum;
     txParams->ppdu = ppdu;
-    txParams->txCenterFreq = GetCenterFrequencyForChannelWidth(txVector);
-    NS_LOG_DEBUG("Starting " << type << " with power " << WToDbm(txPowerWatts) << " dBm on channel "
+    txParams->txWidth = ppdu->GetTxVector().GetChannelWidth();
+    ;
+    NS_LOG_DEBUG("Starting " << type << " with power " << txPowerDbm << " dBm on channel "
                              << +m_wifiPhy->GetChannelNumber() << " for "
                              << txParams->duration.As(Time::MS));
     NS_LOG_DEBUG("Starting " << type << " with integrated spectrum power "
@@ -1335,4 +1336,34 @@
     return WifiPhy::CalculateTxDuration(it->second->GetSize(), txVector, band, it->first);
 }
 
+bool
+PhyEntity::CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txChannelWidth) const
+{
+    // The PHY shall not issue a PHY-RXSTART.indication primitive in response to a PPDU that does
+    // not overlap the primary channel
+    const auto channelWidth = m_wifiPhy->GetChannelWidth();
+    const auto primaryWidth =
+        ((channelWidth % 20 == 0) ? 20
+                                  : channelWidth); // if the channel width is a multiple of 20 MHz,
+                                                   // then we consider the primary20 channel
+    const auto p20CenterFreq =
+        m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(primaryWidth);
+    const auto p20MinFreq = p20CenterFreq - (primaryWidth / 2);
+    const auto p20MaxFreq = p20CenterFreq + (primaryWidth / 2);
+    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;
+}
+
+Ptr<const WifiPpdu>
+PhyEntity::GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu)
+{
+    return ppdu;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/phy-entity.h ns-3.38/src/wifi/model/phy-entity.h
--- ns-3.37/src/wifi/model/phy-entity.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/phy-entity.h	2023-03-16 20:54:23.860869400 -0700
@@ -454,6 +454,17 @@
     virtual uint16_t GetStaId(const Ptr<const WifiPpdu> ppdu) const;
 
     /**
+     * Determine whether the PHY shall issue a PHY-RXSTART.indication primitive in response to a
+     * given PPDU.
+     *
+     * \param ppdu the PPDU
+     * \param txChannelWidth the channel width (MHz) used to transmit the PPDU
+     * \return true if the PHY shall issue a PHY-RXSTART.indication primitive in response to a PPDU,
+     * false otherwise
+     */
+    virtual bool CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txChannelWidth) const;
+
+    /**
      * Check if PHY state should move to CCA busy state based on current
      * state of interference tracker.
      *
@@ -476,9 +487,8 @@
      * \see SpectrumWifiPhy::StartTx
      *
      * \param ppdu the PPDU to send
-     * \param txVector the TXVECTOR used for the transmission of the PPDU
      */
-    virtual void StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector);
+    virtual void StartTx(Ptr<const WifiPpdu> ppdu);
 
     /**
      * This function prepares most of the WifiSpectrumSignalParameters
@@ -487,13 +497,15 @@
      *
      * \param txDuration the duration of the transmission
      * \param ppdu the PPDU to send
-     * \param txVector the TXVECTOR used for the transmission of the PPDU
+     * \param txPowerDbm the total TX power in dBm
+     * \param txPowerSpectrum the TX PSD
      * \param type the type of transmission (for logging)
      */
     void Transmit(Time txDuration,
                   Ptr<const WifiPpdu> ppdu,
-                  const WifiTxVector& txVector,
-                  std::string type);
+                  double txPowerDbm,
+                  Ptr<SpectrumValue> txPowerSpectrum,
+                  const std::string& type);
 
     /**
      * \param psduMap the PSDU(s) to transmit indexed by STA-ID
@@ -516,6 +528,26 @@
     virtual double GetCcaThreshold(const Ptr<const WifiPpdu> ppdu,
                                    WifiChannelListType channelType) const;
 
+    /**
+     * The WifiPpdu from the TX PHY is received by each RX PHY attached to the same channel.
+     * By default and for performance reasons, all RX PHYs will work on the same WifiPpdu instance
+     * from TX instead of a copy of it. Child classes can change that behavior and do a copy and/or
+     * change the content of the parameters stored in WifiPpdu.
+     *
+     * \param ppdu the WifiPpdu transmitted by the TX PHY
+     * \return the WifiPpdu to be used by the RX PHY
+     */
+    virtual Ptr<const WifiPpdu> GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu);
+
+    /**
+     * Obtain the next UID for the PPDU to transmit.
+     * Note that the global UID counter could be incremented.
+     *
+     * \param txVector the transmission parameters
+     * \return the UID to use for the PPDU to transmit
+     */
+    virtual uint64_t ObtainNextUid(const WifiTxVector& txVector);
+
   protected:
     /**
      * A map of PPDU field elements per preamble type.
@@ -709,7 +741,7 @@
                                     uint16_t staId,
                                     const std::vector<bool>& statusPerMpdu);
     /**
-     * Perform amendment-specific actions when the payload is unsuccessfully received.
+     * Perform amendment-specific actions when the payload is unsuccessfuly received.
      *
      * \param psdu the PSDU that we failed to received
      * \param snr the SNR of the received PSDU in linear scale
@@ -818,26 +850,15 @@
     void NotifyInterferenceRxEndAndClear(bool reset);
 
     /**
-     * Obtain the next UID for the PPDU to transmit.
-     * Note that the global UID counter could be incremented.
-     *
-     * \param txVector the transmission parameters
-     * \return the UID to use for the PPDU to transmit
-     */
-    virtual uint64_t ObtainNextUid(const WifiTxVector& txVector);
-
-    /**
      * \param txPowerW power in W to spread across the bands
      * \param ppdu the PPDU that will be transmitted
-     * \param txVector the transmission parameters
      * \return Pointer to SpectrumValue
      *
      * This is a helper function to create the right TX PSD corresponding
      * to the amendment of this PHY.
      */
     virtual Ptr<SpectrumValue> GetTxPowerSpectralDensity(double txPowerW,
-                                                         Ptr<const WifiPpdu> ppdu,
-                                                         const WifiTxVector& txVector) const = 0;
+                                                         Ptr<const WifiPpdu> ppdu) const = 0;
 
     /**
      * Get the center frequency of the channel corresponding the current TxVector rather than
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/qos-frame-exchange-manager.cc ns-3.38/src/wifi/model/qos-frame-exchange-manager.cc
--- ns-3.37/src/wifi/model/qos-frame-exchange-manager.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/qos-frame-exchange-manager.cc	2023-03-16 20:54:23.860869400 -0700
@@ -280,6 +280,7 @@
         return false;
     }
 
+    mpdu = CreateAliasIfNeeded(mpdu);
     WifiTxParameters txParams;
     txParams.m_txVector =
         GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
@@ -312,6 +313,12 @@
     return true;
 }
 
+Ptr<WifiMpdu>
+QosFrameExchangeManager::CreateAliasIfNeeded(Ptr<WifiMpdu> mpdu) const
+{
+    return mpdu;
+}
+
 bool
 QosFrameExchangeManager::TryAddMpdu(Ptr<const WifiMpdu> mpdu,
                                     WifiTxParameters& txParams,
@@ -637,8 +644,6 @@
 {
     NS_LOG_FUNCTION(this << psdu << txVector);
 
-    SetTxopHolder(psdu, txVector);
-
     // APs store buffer size report of associated stations
     if (m_mac->GetTypeOfStation() == AP && psdu->GetAddr1() == m_self)
     {
@@ -651,34 +656,71 @@
                 NS_LOG_DEBUG("Station " << hdr.GetAddr2() << " reported a buffer status of "
                                         << +hdr.GetQosQueueSize()
                                         << " for tid=" << +hdr.GetQosTid());
-                StaticCast<ApWifiMac>(m_mac)->SetBufferStatus(hdr.GetQosTid(),
-                                                              hdr.GetAddr2(),
-                                                              hdr.GetQosQueueSize());
+                StaticCast<ApWifiMac>(m_mac)->SetBufferStatus(
+                    hdr.GetQosTid(),
+                    mpdu->GetOriginal()->GetHeader().GetAddr2(),
+                    hdr.GetQosQueueSize());
             }
         }
     }
 
+    // before updating the NAV, check if the NAV counted down to zero. In such a
+    // case, clear the saved TXOP holder address.
+    ClearTxopHolderIfNeeded();
+
     FrameExchangeManager::PreProcessFrame(psdu, txVector);
 }
 
 void
+QosFrameExchangeManager::PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << psdu << txVector);
+
+    SetTxopHolder(psdu, txVector);
+    FrameExchangeManager::PostProcessFrame(psdu, txVector);
+}
+
+void
 QosFrameExchangeManager::SetTxopHolder(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
 {
     NS_LOG_FUNCTION(this << psdu << txVector);
 
     const WifiMacHeader& hdr = psdu->GetHeader(0);
 
-    if (hdr.IsQosData() || hdr.IsMgt() || hdr.IsRts())
+    // A STA shall save the TXOP holder address for the BSS in which it is associated.
+    // The TXOP holder address is the MAC address from the Address 2 field of the frame
+    // that initiated a frame exchange sequence, except if this is a CTS frame, in which
+    // case the TXOP holder address is the Address 1 field. (Sec. 10.23.2.4 of 802.11-2020)
+    if ((hdr.IsQosData() || hdr.IsMgt() || hdr.IsRts()) &&
+        (hdr.GetAddr1() == m_bssid || hdr.GetAddr2() == m_bssid))
     {
         m_txopHolder = psdu->GetAddr2();
     }
-    else if (hdr.IsCts() || hdr.IsAck())
+    else if (hdr.IsCts() && hdr.GetAddr1() == m_bssid)
     {
         m_txopHolder = psdu->GetAddr1();
     }
 }
 
 void
+QosFrameExchangeManager::ClearTxopHolderIfNeeded()
+{
+    NS_LOG_FUNCTION(this);
+    if (m_navEnd <= Simulator::Now())
+    {
+        m_txopHolder.reset();
+    }
+}
+
+void
+QosFrameExchangeManager::NavResetTimeout()
+{
+    NS_LOG_FUNCTION(this);
+    FrameExchangeManager::NavResetTimeout();
+    ClearTxopHolderIfNeeded();
+}
+
+void
 QosFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
                                      RxSignalInfo rxSignalInfo,
                                      const WifiTxVector& txVector,
@@ -706,7 +748,7 @@
         // frame matches the saved TXOP holder address, then the STA shall send the
         // CTS frame after SIFS, without regard for, and without resetting, its NAV.
         // (sec. 10.22.2.4 of 802.11-2016)
-        if (hdr.GetAddr2() == m_txopHolder || m_navEnd <= Simulator::Now())
+        if (hdr.GetAddr2() == m_txopHolder || VirtualCsMediumIdle())
         {
             NS_LOG_DEBUG("Received RTS from=" << hdr.GetAddr2() << ", schedule CTS");
             Simulator::Schedule(m_phy->GetSifs(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/qos-frame-exchange-manager.h ns-3.38/src/wifi/model/qos-frame-exchange-manager.h
--- ns-3.37/src/wifi/model/qos-frame-exchange-manager.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/qos-frame-exchange-manager.h	2023-03-16 20:54:23.860869400 -0700
@@ -22,6 +22,8 @@
 
 #include "frame-exchange-manager.h"
 
+#include <optional>
+
 namespace ns3
 {
 
@@ -92,6 +94,16 @@
                                            const WifiTxParameters& txParams,
                                            Time ppduDurationLimit) const;
 
+    /**
+     * Create an alias of the given MPDU for transmission by this Frame Exchange Manager.
+     * This is required by 11be MLDs to support translation of MAC addresses. For single
+     * link devices, the given MPDU is simply returned.
+     *
+     * \param mpdu the given MPDU
+     * \return the alias of the given MPDU for transmission on this link
+     */
+    virtual Ptr<WifiMpdu> CreateAliasIfNeeded(Ptr<WifiMpdu> mpdu) const;
+
   protected:
     void DoDispose() override;
 
@@ -100,6 +112,8 @@
                      const WifiTxVector& txVector,
                      bool inAmpdu) override;
     void PreProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
+    void PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
+    void NavResetTimeout() override;
     Time GetFrameDurationId(const WifiMacHeader& header,
                             uint32_t size,
                             const WifiTxParameters& txParams,
@@ -163,10 +177,15 @@
      */
     virtual void SetTxopHolder(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector);
 
-    Ptr<QosTxop> m_edca;       //!< the EDCAF that gained channel access
-    Mac48Address m_txopHolder; //!< MAC address of the TXOP holder
-    bool m_setQosQueueSize;    /**< whether to set the Queue Size subfield of the
-                                    QoS Control field of QoS data frames */
+    /**
+     * Clear the TXOP holder if the NAV counted down to zero (includes the case of NAV reset).
+     */
+    virtual void ClearTxopHolderIfNeeded();
+
+    Ptr<QosTxop> m_edca;                      //!< the EDCAF that gained channel access
+    std::optional<Mac48Address> m_txopHolder; //!< MAC address of the TXOP holder
+    bool m_setQosQueueSize;                   /**< whether to set the Queue Size subfield of the
+                                                   QoS Control field of QoS data frames */
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/qos-txop.cc ns-3.38/src/wifi/model/qos-txop.cc
--- ns-3.37/src/wifi/model/qos-txop.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/qos-txop.cc	2023-03-16 20:54:23.860869400 -0700
@@ -90,6 +90,12 @@
                           PointerValue(),
                           MakePointerAccessor(&QosTxop::m_baManager),
                           MakePointerChecker<BlockAckManager>())
+            .AddAttribute("NMaxInflights",
+                          "The maximum number of links (in the range 1-15) on which an MPDU can be "
+                          "simultaneously in-flight.",
+                          UintegerValue(1),
+                          MakeUintegerAccessor(&QosTxop::m_nMaxInflights),
+                          MakeUintegerChecker<uint8_t>(1, 15))
             .AddTraceSource("TxopTrace",
                             "Trace source for TXOP start and duration times",
                             MakeTraceSourceAccessor(&QosTxop::m_txopTrace),
@@ -257,14 +263,6 @@
     return m_baManager;
 }
 
-bool
-QosTxop::GetBaAgreementEstablished(Mac48Address address, uint8_t tid) const
-{
-    return m_baManager->ExistsAgreementInState(address,
-                                               tid,
-                                               OriginatorBlockAckAgreement::ESTABLISHED);
-}
-
 uint16_t
 QosTxop::GetBaBufferSize(Mac48Address address, uint8_t tid) const
 {
@@ -277,32 +275,29 @@
     return m_baManager->GetOriginatorStartingSequence(address, tid);
 }
 
-Ptr<const WifiMpdu>
+Ptr<WifiMpdu>
 QosTxop::PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
     NS_ASSERT(QosUtilsMapTidToAc(tid) == m_ac);
 
-    CtrlBAckRequestHeader reqHdr = m_baManager->GetBlockAckReqHeader(recipient, tid);
+    auto recipientMld = m_mac->GetMldAddress(recipient);
+
+    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);
     hdr.SetAddr1(recipient);
-    hdr.SetAddr2(m_mac->GetAddress());
+    hdr.SetAddr2(m_mac->GetLocalAddress(recipient));
     hdr.SetDsNotTo();
     hdr.SetDsNotFrom();
     hdr.SetNoRetry();
     hdr.SetNoMoreFragments();
 
-    return Create<const WifiMpdu>(bar, hdr);
-}
-
-void
-QosTxop::ScheduleBar(Ptr<const WifiMpdu> bar, bool skipIfNoDataQueued)
-{
-    m_baManager->ScheduleBar(bar, skipIfNoDataQueued);
+    return Create<WifiMpdu>(bar, hdr);
 }
 
 bool
@@ -314,16 +309,12 @@
 bool
 QosTxop::HasFramesToTransmit(uint8_t linkId)
 {
-    // check if the BA manager has anything to send, so that expired
-    // frames (if any) are removed and a BlockAckRequest is scheduled to advance
-    // the starting sequence number of the transmit (and receiver) window
-    bool baManagerHasPackets{m_baManager->GetBar(false)};
     // remove MSDUs with expired lifetime starting from the head of the queue
     m_queue->WipeAllExpiredMpdus();
     bool queueIsNotEmpty = (bool)(m_queue->PeekFirstAvailable(linkId, m_qosBlockedDestinations));
 
-    NS_LOG_FUNCTION(this << baManagerHasPackets << queueIsNotEmpty);
-    return baManagerHasPackets || queueIsNotEmpty;
+    NS_LOG_FUNCTION(this << queueIsNotEmpty);
+    return queueIsNotEmpty;
 }
 
 uint16_t
@@ -351,7 +342,7 @@
     Mac48Address recipient = mpdu->GetHeader().GetAddr1();
     uint8_t tid = mpdu->GetHeader().GetQosTid();
 
-    if (!GetBaAgreementEstablished(recipient, tid))
+    if (!m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
     {
         return false;
     }
@@ -365,89 +356,118 @@
 }
 
 Ptr<WifiMpdu>
-QosTxop::PeekNextMpdu(uint8_t linkId, uint8_t tid, Mac48Address recipient, Ptr<WifiMpdu> item)
+QosTxop::PeekNextMpdu(uint8_t linkId, uint8_t tid, Mac48Address recipient, Ptr<const WifiMpdu> mpdu)
 {
-    NS_LOG_FUNCTION(this << +linkId << +tid << recipient << item);
+    NS_LOG_FUNCTION(this << +linkId << +tid << recipient << mpdu);
 
     // lambda to peek the next frame
-    auto peek = [this, &linkId, &tid, &recipient, &item]() -> Ptr<WifiMpdu> {
+    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, item);
+            return m_queue->PeekFirstAvailable(linkId, m_qosBlockedDestinations, mpdu);
         }
         if (m_qosBlockedDestinations->IsBlocked(recipient, tid))
         {
             return nullptr;
         }
-        return m_queue->PeekByTidAndAddress(tid, recipient, item);
+        return m_queue->PeekByTidAndAddress(tid, recipient, mpdu);
     };
 
-    item = peek();
+    auto item = peek();
     // remove old packets (must be retransmissions or in flight, otherwise they did
     // not get a sequence number assigned)
     while (item && !item->IsFragment())
     {
-        if ((item->GetHeader().IsRetry() || item->IsInFlight()) && IsQosOldPacket(item))
+        if (item->GetHeader().IsCtl())
+        {
+            NS_LOG_DEBUG("Skipping control frame: " << *item);
+            mpdu = item;
+            item = peek();
+            continue;
+        }
+
+        if (item->HasSeqNoAssigned() && IsQosOldPacket(item))
         {
             NS_LOG_DEBUG("Removing an old packet from EDCA queue: " << *item);
             if (!m_droppedMpduCallback.IsNull())
             {
                 m_droppedMpduCallback(WIFI_MAC_DROP_QOS_OLD_PACKET, item);
             }
-            auto oldItem = item;
+            mpdu = item;
             item = peek();
-            m_queue->Remove(oldItem);
+            m_queue->Remove(mpdu);
+            continue;
         }
-        else if (item->IsInFlight())
+
+        if (auto linkIds = item->GetInFlightLinkIds(); !linkIds.empty()) // MPDU is in-flight
         {
+            // if the MPDU is not already in-flight on the link for which we are requesting an
+            // MPDU and the number of links on which the MPDU is in-flight is less than the
+            // maximum number, then we can transmit this MPDU
+            if (linkIds.count(linkId) == 0 && linkIds.size() < m_nMaxInflights)
+            {
+                break;
+            }
+
+            // if no BA agreement, we cannot have multiple MPDUs in-flight
+            if (item->GetHeader().IsQosData() &&
+                !m_mac->GetBaAgreementEstablishedAsOriginator(item->GetHeader().GetAddr1(),
+                                                              item->GetHeader().GetQosTid()))
+            {
+                NS_LOG_DEBUG("No BA agreement and an MPDU is already in-flight");
+                return nullptr;
+            }
+
             NS_LOG_DEBUG("Skipping in flight MPDU: " << *item);
+            mpdu = item;
             item = peek();
+            continue;
         }
-        else if (item->GetHeader().HasData() &&
-                 !m_mac->CanForwardPacketsTo(item->GetHeader().GetAddr1()))
+
+        if (item->GetHeader().HasData() &&
+            !m_mac->CanForwardPacketsTo(item->GetHeader().GetAddr1()))
         {
             NS_LOG_DEBUG("Skipping frame that cannot be forwarded: " << *item);
+            mpdu = item;
             item = peek();
+            continue;
         }
-        else
-        {
-            break;
-        }
+        break;
     }
-    if (item)
-    {
-        NS_ASSERT(!item->IsInFlight());
-        WifiMacHeader& hdr = item->GetHeader();
 
-        // peek the next sequence number and check if it is within the transmit window
-        // in case of QoS data frame
-        uint16_t sequence =
-            (hdr.IsRetry() ? hdr.GetSequenceNumber() : m_txMiddle->PeekNextSequenceNumberFor(&hdr));
-        if (hdr.IsQosData())
-        {
-            Mac48Address recipient = hdr.GetAddr1();
-            uint8_t tid = hdr.GetQosTid();
+    if (!item)
+    {
+        return nullptr;
+    }
 
-            if (GetBaAgreementEstablished(recipient, tid) &&
-                !IsInWindow(sequence,
-                            GetBaStartingSequence(recipient, tid),
-                            GetBaBufferSize(recipient, tid)))
-            {
-                NS_LOG_DEBUG("Packet beyond the end of the current transmit window");
-                return nullptr;
-            }
-        }
+    WifiMacHeader& hdr = item->GetHeader();
 
-        // Assign a sequence number if this is not a fragment nor a retransmission
-        if (!item->IsFragment() && !hdr.IsRetry())
+    // peek the next sequence number and check if it is within the transmit window
+    // in case of QoS data frame
+    uint16_t sequence = item->HasSeqNoAssigned() ? hdr.GetSequenceNumber()
+                                                 : m_txMiddle->PeekNextSequenceNumberFor(&hdr);
+    if (hdr.IsQosData())
+    {
+        Mac48Address recipient = hdr.GetAddr1();
+        uint8_t tid = hdr.GetQosTid();
+
+        if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid) &&
+            !IsInWindow(sequence,
+                        GetBaStartingSequence(recipient, tid),
+                        GetBaBufferSize(recipient, tid)))
         {
-            hdr.SetSequenceNumber(sequence);
+            NS_LOG_DEBUG("Packet beyond the end of the current transmit window");
+            return nullptr;
         }
-        NS_LOG_DEBUG("Packet peeked from EDCA queue: " << *item);
-        return item;
     }
 
-    return nullptr;
+    // Assign a sequence number if this is not a fragment nor it already has one assigned
+    if (!item->IsFragment() && !item->HasSeqNoAssigned())
+    {
+        hdr.SetSequenceNumber(sequence);
+    }
+    NS_LOG_DEBUG("Packet peeked from EDCA queue: " << *item);
+    return item;
 }
 
 Ptr<WifiMpdu>
@@ -486,15 +506,15 @@
         // we should not be asked to dequeue an MPDU that is beyond the transmit window.
         // Note that PeekNextMpdu() temporarily assigns the next available sequence number
         // to the peeked frame
-        NS_ASSERT(!GetBaAgreementEstablished(recipient, tid) ||
-                  IsInWindow(peekedItem->GetHeader().GetSequenceNumber(),
-                             GetBaStartingSequence(recipient, tid),
-                             GetBaBufferSize(recipient, tid)));
+        NS_ASSERT(!m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid) ||
+                  IsInWindow(
+                      peekedItem->GetHeader().GetSequenceNumber(),
+                      GetBaStartingSequence(peekedItem->GetOriginal()->GetHeader().GetAddr1(), tid),
+                      GetBaBufferSize(peekedItem->GetOriginal()->GetHeader().GetAddr1(), tid)));
 
         // try A-MSDU aggregation
         if (m_mac->GetHtSupported() && !recipient.IsBroadcast() &&
-            !peekedItem->GetHeader().IsRetry() && !peekedItem->IsFragment() &&
-            !peekedItem->IsInFlight())
+            !peekedItem->HasSeqNoAssigned() && !peekedItem->IsFragment())
         {
             auto htFem = StaticCast<HtFrameExchangeManager>(qosFem);
             mpdu = htFem->GetMsduAggregator()->GetNextAmsdu(peekedItem, txParams, availableTime);
@@ -524,25 +544,15 @@
 {
     NS_LOG_FUNCTION(this << *mpdu);
 
-    if (!mpdu->IsFragment() && !mpdu->GetHeader().IsRetry() && !mpdu->IsInFlight())
+    if (!mpdu->IsFragment() && !mpdu->HasSeqNoAssigned())
     {
-        uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
-        mpdu->GetHeader().SetSequenceNumber(sequence);
+        // in case of 11be MLDs, sequence numbers refer to the MLD address
+        auto origMpdu = m_queue->GetOriginal(mpdu);
+        uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&origMpdu->GetHeader());
+        mpdu->AssignSeqNo(sequence);
     }
 }
 
-BlockAckReqType
-QosTxop::GetBlockAckReqType(Mac48Address recipient, uint8_t tid) const
-{
-    return m_baManager->GetBlockAckReqType(recipient, tid);
-}
-
-BlockAckType
-QosTxop::GetBlockAckType(Mac48Address recipient, uint8_t tid) const
-{
-    return m_baManager->GetBlockAckType(recipient, tid);
-}
-
 void
 QosTxop::NotifyChannelAccessed(uint8_t linkId, Time txopDuration)
 {
@@ -594,11 +604,11 @@
 }
 
 void
-QosTxop::GotAddBaResponse(const MgtAddBaResponseHeader* respHdr, Mac48Address recipient)
+QosTxop::GotAddBaResponse(const MgtAddBaResponseHeader& respHdr, Mac48Address recipient)
 {
     NS_LOG_FUNCTION(this << respHdr << recipient);
-    uint8_t tid = respHdr->GetTid();
-    if (respHdr->GetStatusCode().IsSuccess())
+    uint8_t tid = respHdr.GetTid();
+    if (respHdr.GetStatusCode().IsSuccess())
     {
         NS_LOG_DEBUG("block ack agreement established with " << recipient << " tid " << +tid);
         // A (destination, TID) pair is "blocked" (i.e., no more packets are sent)
@@ -614,18 +624,17 @@
         {
             startingSeq = peekedItem->GetHeader().GetSequenceNumber();
         }
-        m_baManager->UpdateAgreement(respHdr, recipient, startingSeq);
+        m_baManager->UpdateOriginatorAgreement(respHdr, recipient, startingSeq);
     }
     else
     {
         NS_LOG_DEBUG("discard ADDBA response" << recipient);
-        m_baManager->NotifyAgreementRejected(recipient, tid);
+        m_baManager->NotifyOriginatorAgreementRejected(recipient, tid);
     }
 
-    if (HasFramesToTransmit(SINGLE_LINK_OP_ID) &&
-        GetLink(SINGLE_LINK_OP_ID).access == NOT_REQUESTED)
+    for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
     {
-        m_mac->GetChannelAccessManager(SINGLE_LINK_OP_ID)->RequestAccess(this);
+        StartAccessIfNeeded(linkId);
     }
 }
 
@@ -634,7 +643,21 @@
 {
     NS_LOG_FUNCTION(this << delBaHdr << recipient);
     NS_LOG_DEBUG("received DELBA frame from=" << recipient);
-    m_baManager->DestroyAgreement(recipient, delBaHdr->GetTid());
+    m_baManager->DestroyOriginatorAgreement(recipient, delBaHdr->GetTid());
+}
+
+void
+QosTxop::NotifyOriginatorAgreementNoReply(const Mac48Address& recipient, uint8_t tid)
+{
+    NS_LOG_FUNCTION(this << recipient << tid);
+
+    m_baManager->NotifyOriginatorAgreementNoReply(recipient, tid);
+    // the recipient has been "unblocked" and transmissions can resume using normal
+    // acknowledgment, hence start access (if needed) on all the links
+    for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
+    {
+        StartAccessIfNeeded(linkId);
+    }
 }
 
 void
@@ -642,9 +665,12 @@
 {
     NS_ASSERT(mpdu->GetHeader().IsQosData());
     // If there is an established BA agreement, store the packet in the queue of outstanding packets
-    if (GetBaAgreementEstablished(mpdu->GetHeader().GetAddr1(), mpdu->GetHeader().GetQosTid()))
+    if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(),
+                                                     mpdu->GetHeader().GetQosTid()))
     {
-        m_baManager->StorePacket(mpdu);
+        NS_ASSERT(mpdu->IsQueued());
+        NS_ASSERT(m_queue->GetAc() == mpdu->GetQueueAc());
+        m_baManager->StorePacket(m_queue->GetOriginal(mpdu));
     }
 }
 
@@ -681,16 +707,11 @@
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
     // If agreement is still pending, ADDBA response is not received
-    if (m_baManager->ExistsAgreementInState(recipient, tid, OriginatorBlockAckAgreement::PENDING))
+    if (auto agreement = m_baManager->GetAgreementAsOriginator(recipient, tid);
+        agreement && agreement->get().IsPending())
     {
-        m_baManager->NotifyAgreementNoReply(recipient, tid);
+        NotifyOriginatorAgreementNoReply(recipient, tid);
         Simulator::Schedule(m_failedAddBaTimeout, &QosTxop::ResetBa, this, recipient, tid);
-        GenerateBackoff(SINGLE_LINK_OP_ID);
-        if (HasFramesToTransmit(SINGLE_LINK_OP_ID) &&
-            GetLink(SINGLE_LINK_OP_ID).access == NOT_REQUESTED)
-        {
-            m_mac->GetChannelAccessManager(SINGLE_LINK_OP_ID)->RequestAccess(this);
-        }
     }
 }
 
@@ -702,12 +723,10 @@
     // before this function is called, a DELBA request may arrive, which causes
     // the agreement to be deleted. Hence, check if an agreement exists before
     // notifying that the agreement has to be reset.
-    if (m_baManager->ExistsAgreement(recipient, tid) &&
-        !m_baManager->ExistsAgreementInState(recipient,
-                                             tid,
-                                             OriginatorBlockAckAgreement::ESTABLISHED))
+    if (auto agreement = m_baManager->GetAgreementAsOriginator(recipient, tid);
+        agreement && !agreement->get().IsEstablished())
     {
-        m_baManager->NotifyAgreementReset(recipient, tid);
+        m_baManager->NotifyOriginatorAgreementReset(recipient, tid);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/qos-txop.h ns-3.38/src/wifi/model/qos-txop.h
--- ns-3.37/src/wifi/model/qos-txop.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/qos-txop.h	2023-03-16 20:54:23.860869400 -0700
@@ -117,16 +117,6 @@
      * \param address recipient address of the peer station
      * \param tid traffic ID.
      *
-     * \return true if a block ack agreement is established, false otherwise.
-     *
-     * Checks if a block ack agreement is established with station addressed by
-     * <i>recipient</i> for TID <i>tid</i>.
-     */
-    bool GetBaAgreementEstablished(Mac48Address address, uint8_t tid) const;
-    /**
-     * \param address recipient address of the peer station
-     * \param tid traffic ID.
-     *
      * \return the negotiated buffer size during ADDBA handshake.
      *
      * Returns the negotiated buffer size during ADDBA handshake with station addressed by
@@ -145,24 +135,6 @@
      */
     uint16_t GetBaStartingSequence(Mac48Address address, uint8_t tid) const;
     /**
-     * \param recipient MAC address of recipient
-     * \param tid traffic ID
-     *
-     * \return the type of Block Ack Requests sent to the recipient
-     *
-     * This function returns the type of Block Ack Requests sent to the recipient.
-     */
-    BlockAckReqType GetBlockAckReqType(Mac48Address recipient, uint8_t tid) const;
-    /**
-     * \param recipient MAC address
-     * \param tid traffic ID
-     *
-     * \return the type of Block Acks sent by the recipient
-     *
-     * This function returns the type of Block Acks sent by the recipient.
-     */
-    BlockAckType GetBlockAckType(Mac48Address recipient, uint8_t tid) const;
-    /**
      * \param recipient Address of recipient.
      * \param tid traffic ID.
      * \return the BlockAckRequest to send
@@ -172,15 +144,7 @@
      * 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<const WifiMpdu> PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const;
-    /**
-     * \param bar the BlockAckRequest to schedule
-     * \param skipIfNoDataQueued do not send if there is no data queued
-     *
-     * Request the block ack manager to schedule the transmission of the given
-     * BlockAckRequest.
-     */
-    void ScheduleBar(Ptr<const WifiMpdu> bar, bool skipIfNoDataQueued = false);
+    Ptr<WifiMpdu> PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const;
 
     /* Event handlers */
     /**
@@ -189,7 +153,7 @@
      * \param respHdr ADDBA response header.
      * \param recipient address of the recipient.
      */
-    void GotAddBaResponse(const MgtAddBaResponseHeader* respHdr, Mac48Address recipient);
+    void GotAddBaResponse(const MgtAddBaResponseHeader& respHdr, Mac48Address recipient);
     /**
      * Event handler when a DELBA frame is received.
      *
@@ -198,6 +162,14 @@
      */
     void GotDelBaFrame(const MgtDelBaHeader* delBaHdr, Mac48Address recipient);
     /**
+     * Take action upon notification of ADDBA_REQUEST frame being discarded
+     * (likely due to exceeded max retry limit).
+     *
+     * \param recipient the intended recipient of the ADDBA_REQUEST frame
+     * \param tid the TID
+     */
+    void NotifyOriginatorAgreementNoReply(const Mac48Address& recipient, uint8_t tid);
+    /**
      * Callback when ADDBA response is not received after timeout.
      *
      * \param recipient MAC address of recipient
@@ -291,8 +263,8 @@
     /**
      * Peek the next frame to transmit on the given link to the given receiver and of the given TID
      * from the EDCA queue. If <i>tid</i> is equal to 8 (invalid value) and <i>recipient</i>
-     * is the broadcast address, the first available frame is returned. If <i>item</i>
-     * is not a null pointer, the search starts from the packet following <i>item</i>
+     * is the broadcast address, the first available frame is returned. If <i>mpdu</i>
+     * is not a null pointer, the search starts from the MPDU following <i>mpdu</i>
      * in the queue; otherwise, the search starts from the head of the queue.
      * Note that A-MSDU aggregation is never attempted. If the frame has never been
      * transmitted, it is assigned a sequence number peeked from MacTxMiddle.
@@ -301,13 +273,13 @@
      * \param linkId the ID of the given link
      * \param tid traffic ID.
      * \param recipient the receiver station address.
-     * \param item the item after which the search starts from
+     * \param mpdu the MPDU after which the search starts from
      * \returns the peeked frame.
      */
     Ptr<WifiMpdu> PeekNextMpdu(uint8_t linkId,
                                uint8_t tid = 8,
                                Mac48Address recipient = Mac48Address::GetBroadcast(),
-                               Ptr<WifiMpdu> item = nullptr);
+                               Ptr<const WifiMpdu> mpdu = nullptr);
     /**
      * Prepare the frame to transmit on the given link starting from the MPDU that has been
      * previously peeked by calling PeekNextMpdu. A frame is only returned if it meets the
@@ -505,6 +477,9 @@
     Time m_failedAddBaTimeout;            //!< timeout after failed BA agreement
     bool m_useExplicitBarAfterMissedBlockAck; //!< flag whether explicit BlockAckRequest should be
                                               //!< sent upon missed BlockAck Response
+    uint8_t m_nMaxInflights;                  //!< the maximum number of links on which
+                                              //!< an MPDU can be in-flight at the same
+                                              //!< time
 
     /// TracedCallback for TXOP trace typedef
     typedef TracedCallback<Time /* start time */, Time /* duration */, uint8_t /* link ID*/>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/qos-utils.cc ns-3.38/src/wifi/model/qos-utils.cc
--- ns-3.37/src/wifi/model/qos-utils.cc	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/qos-utils.cc	2023-03-16 20:54:23.860869400 -0700
@@ -84,7 +84,7 @@
 }
 
 bool
-operator>(enum AcIndex left, enum AcIndex right)
+operator>(AcIndex left, AcIndex right)
 {
     NS_ABORT_MSG_IF(left > 3 || right > 3, "Cannot compare non-QoS ACs");
 
@@ -104,7 +104,7 @@
 }
 
 bool
-operator>=(enum AcIndex left, enum AcIndex right)
+operator>=(AcIndex left, AcIndex right)
 {
     NS_ABORT_MSG_IF(left > 3 || right > 3, "Cannot compare non-QoS ACs");
 
@@ -112,21 +112,23 @@
 }
 
 bool
-operator<(enum AcIndex left, enum AcIndex right)
+operator<(AcIndex left, AcIndex right)
 {
     return !(left >= right);
 }
 
 bool
-operator<=(enum AcIndex left, enum AcIndex right)
+operator<=(AcIndex left, AcIndex right)
 {
     return !(left > right);
 }
 
-const std::map<AcIndex, WifiAc> wifiAcList = {{AC_BE, {0, 3}},
-                                              {AC_BK, {1, 2}},
-                                              {AC_VI, {4, 5}},
-                                              {AC_VO, {6, 7}}};
+const std::map<AcIndex, WifiAc> wifiAcList = {
+    {AC_BE, {0, 3}},
+    {AC_BK, {1, 2}},
+    {AC_VI, {4, 5}},
+    {AC_VO, {6, 7}},
+};
 
 AcIndex
 QosUtilsMapTidToAc(uint8_t tid)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/qos-utils.h ns-3.38/src/wifi/model/qos-utils.h
--- ns-3.37/src/wifi/model/qos-utils.h	2022-11-01 17:37:24.747395800 -0700
+++ ns-3.38/src/wifi/model/qos-utils.h	2023-03-16 20:54:23.860869400 -0700
@@ -34,8 +34,6 @@
 
 typedef std::pair<Mac48Address, uint8_t> WifiAddressTidPair; //!< (MAC address, TID) pair
 
-#define WIFI_TID_UNDEFINED 0xff
-
 /**
  * Function object to compute the hash of a (MAC address, TID) pair
  */
@@ -136,7 +134,7 @@
  * \param right the AC on the right of operator>
  * \return true if the AC on the left has higher priority than the AC on the right
  */
-bool operator>(enum AcIndex left, enum AcIndex right);
+bool operator>(AcIndex left, AcIndex right);
 
 /**
  * \ingroup wifi
@@ -147,7 +145,7 @@
  * \param right the AC on the right of operator>=
  * \return true if the AC on the left has higher or the same priority than the AC on the right
  */
-bool operator>=(enum AcIndex left, enum AcIndex right);
+bool operator>=(AcIndex left, AcIndex right);
 
 /**
  * \ingroup wifi
@@ -158,7 +156,7 @@
  * \param right the AC on the right of operator<
  * \return true if the AC on the left has lower priority than the AC on the right
  */
-bool operator<(enum AcIndex left, enum AcIndex right);
+bool operator<(AcIndex left, AcIndex right);
 
 /**
  * \ingroup wifi
@@ -169,7 +167,7 @@
  * \param right the AC on the right of operator<=
  * \return true if the AC on the left has lower or the same priority than the AC on the right
  */
-bool operator<=(enum AcIndex left, enum AcIndex right);
+bool operator<=(AcIndex left, AcIndex right);
 
 /**
  * Map containing the four ACs in increasing order of priority (according to
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/constant-rate-wifi-manager.cc ns-3.38/src/wifi/model/rate-control/constant-rate-wifi-manager.cc
--- ns-3.37/src/wifi/model/rate-control/constant-rate-wifi-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/constant-rate-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
@@ -25,7 +25,7 @@
 #include "ns3/wifi-tx-vector.h"
 #include "ns3/wifi-utils.h"
 
-#define Min(a, b) ((a < b) ? a : b)
+#include <algorithm>
 
 namespace ns3
 {
@@ -127,7 +127,7 @@
 ConstantRateWifiManager::DoGetDataTxVector(WifiRemoteStation* st, uint16_t allowedWidth)
 {
     NS_LOG_FUNCTION(this << st << allowedWidth);
-    uint8_t nss = Min(GetMaxNumberOfTransmitStreams(), GetNumberOfSupportedStreams(st));
+    uint8_t nss = std::min(GetMaxNumberOfTransmitStreams(), GetNumberOfSupportedStreams(st));
     if (m_dataMode.GetModulationClass() == WIFI_MOD_CLASS_HT)
     {
         nss = 1 + (m_dataMode.GetMcsValue() / 8);
@@ -142,7 +142,7 @@
         GetNumberOfAntennas(),
         nss,
         0,
-        GetChannelWidthForTransmission(m_dataMode, allowedWidth, GetChannelWidth(st)),
+        GetPhy()->GetTxBandwidth(m_dataMode, std::min(allowedWidth, GetChannelWidth(st))),
         GetAggregation(st));
 }
 
@@ -160,7 +160,7 @@
         1,
         1,
         0,
-        GetChannelWidthForTransmission(m_ctlMode, GetPhy()->GetChannelWidth(), GetChannelWidth(st)),
+        GetPhy()->GetTxBandwidth(m_ctlMode, GetChannelWidth(st)),
         GetAggregation(st));
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/ideal-wifi-manager.cc ns-3.38/src/wifi/model/rate-control/ideal-wifi-manager.cc
--- ns-3.37/src/wifi/model/rate-control/ideal-wifi-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/ideal-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
@@ -571,7 +571,7 @@
         GetNumberOfAntennas(),
         selectedNss,
         0,
-        GetChannelWidthForTransmission(maxMode, channelWidth),
+        GetPhy()->GetTxBandwidth(maxMode, channelWidth),
         GetAggregation(station)};
     uint64_t maxDataRate = maxMode.GetDataRate(bestTxVector);
     if (m_currentRate != maxDataRate)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc ns-3.38/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc
--- ns-3.37/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
@@ -1091,7 +1091,7 @@
             GetNumberOfAntennas(),
             group.streams,
             GetNess(station),
-            GetChannelWidthForTransmission(mode, group.chWidth),
+            GetPhy()->GetTxBandwidth(mode, group.chWidth),
             GetAggregation(station) && !station->m_isSampling};
         uint64_t dataRate = mode.GetDataRate(txVector);
         if (m_currentRate != dataRate && !station->m_isSampling)
@@ -1181,9 +1181,7 @@
             1,
             1,
             0,
-            GetChannelWidthForTransmission(rtsRate,
-                                           GetPhy()->GetChannelWidth(),
-                                           GetChannelWidth(station)),
+            GetPhy()->GetTxBandwidth(rtsRate, GetChannelWidth(station)),
             GetAggregation(station));
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/minstrel-wifi-manager.cc ns-3.38/src/wifi/model/rate-control/minstrel-wifi-manager.cc
--- ns-3.37/src/wifi/model/rate-control/minstrel-wifi-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/minstrel-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
@@ -1105,7 +1105,7 @@
 }
 
 void
-MinstrelWifiManager::PrintSampleTable(MinstrelWifiRemoteStation* station)
+MinstrelWifiManager::PrintSampleTable(MinstrelWifiRemoteStation* station) const
 {
     uint8_t numSampleRates = station->m_nModes;
     std::stringstream table;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/minstrel-wifi-manager.h ns-3.38/src/wifi/model/rate-control/minstrel-wifi-manager.h
--- ns-3.37/src/wifi/model/rate-control/minstrel-wifi-manager.h	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/minstrel-wifi-manager.h	2023-03-16 20:54:23.864869400 -0700
@@ -328,7 +328,7 @@
      *
      * \param station the station object
      */
-    void PrintSampleTable(MinstrelWifiRemoteStation* station);
+    void PrintSampleTable(MinstrelWifiRemoteStation* station) const;
 
     /**
      * Print Minstrel Table.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/rraa-wifi-manager.cc ns-3.38/src/wifi/model/rate-control/rraa-wifi-manager.cc
--- ns-3.37/src/wifi/model/rate-control/rraa-wifi-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/rraa-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
@@ -191,7 +191,7 @@
 RraaWifiManager::GetThresholds(RraaWifiRemoteStation* station, WifiMode mode) const
 {
     NS_LOG_FUNCTION(this << station << mode);
-    struct WifiRraaThresholds threshold;
+    WifiRraaThresholds threshold;
     for (RraaThresholdsTable::const_iterator i = station->m_thresholds.begin();
          i != station->m_thresholds.end();
          i++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/rrpaa-wifi-manager.cc ns-3.38/src/wifi/model/rate-control/rrpaa-wifi-manager.cc
--- ns-3.37/src/wifi/model/rate-control/rrpaa-wifi-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/rrpaa-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
@@ -220,7 +220,7 @@
 RrpaaWifiManager::GetThresholds(RrpaaWifiRemoteStation* station, WifiMode mode) const
 {
     NS_LOG_FUNCTION(this << station << mode);
-    struct WifiRrpaaThresholds threshold;
+    WifiRrpaaThresholds threshold;
     for (RrpaaThresholdsTable::const_iterator i = station->m_thresholds.begin();
          i != station->m_thresholds.end();
          i++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/rrpaa-wifi-manager.h ns-3.38/src/wifi/model/rate-control/rrpaa-wifi-manager.h
--- ns-3.37/src/wifi/model/rate-control/rrpaa-wifi-manager.h	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/rrpaa-wifi-manager.h	2023-03-16 20:54:23.864869400 -0700
@@ -37,7 +37,7 @@
  * Threshold Coordinated Management for High-Density IEEE 802.11 Networks</i>
  * by Matías Richart; Jorge Visca and Javier Baliosian in Integrated Network Management (IM),
  * 2015 IFIP/IEEE International Symposium on (pp. 139-146). IEEE.
- * http://dl.ifip.org/db/conf/im/im2015/137430.pdf
+ * https://ieeexplore.ieee.org/document/7140286
  *
  * RRPAA adds power control to the RRAA mechanism. RRAA is described in
  * <i>Robust rate adaptation for 802.11 wireless networks</i> by Starsky H. Y. Wong;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/rate-control/thompson-sampling-wifi-manager.cc ns-3.38/src/wifi/model/rate-control/thompson-sampling-wifi-manager.cc
--- ns-3.37/src/wifi/model/rate-control/thompson-sampling-wifi-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/rate-control/thompson-sampling-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
@@ -350,7 +350,7 @@
         GetNumberOfAntennas(),
         nss,
         0, // NESS
-        GetChannelWidthForTransmission(mode, channelWidth),
+        GetPhy()->GetTxBandwidth(mode, channelWidth),
         GetAggregation(station),
         false);
 }
@@ -365,7 +365,6 @@
     // Use the most robust MCS for the control channel.
     auto& stats = station->m_mcsStats.at(0);
     WifiMode mode = stats.mode;
-    uint16_t channelWidth = std::min(stats.channelWidth, GetPhy()->GetChannelWidth());
     uint8_t nss = stats.nss;
 
     // Make sure control frames are sent using 1 spatial stream.
@@ -379,7 +378,7 @@
         GetNumberOfAntennas(),
         nss,
         0, // NESS
-        GetChannelWidthForTransmission(mode, channelWidth),
+        GetPhy()->GetTxBandwidth(mode, stats.channelWidth),
         GetAggregation(station),
         false);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/reduced-neighbor-report.cc ns-3.38/src/wifi/model/reduced-neighbor-report.cc
--- ns-3.37/src/wifi/model/reduced-neighbor-report.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/reduced-neighbor-report.cc	2023-03-16 20:54:23.864869400 -0700
@@ -179,7 +179,7 @@
         startingFreq = 5000;
         break;
     case WIFI_PHY_BAND_6GHZ:
-        startingFreq = 5940;
+        startingFreq = 5950;
         break;
     case WIFI_PHY_BAND_UNSPECIFIED:
     default:
@@ -267,7 +267,7 @@
         startingFreq = 5000;
         break;
     case WIFI_PHY_BAND_6GHZ:
-        startingFreq = 5940;
+        startingFreq = 5950;
         break;
     case WIFI_PHY_BAND_UNSPECIFIED:
     default:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/spectrum-wifi-phy.cc ns-3.38/src/wifi/model/spectrum-wifi-phy.cc
--- ns-3.37/src/wifi/model/spectrum-wifi-phy.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/spectrum-wifi-phy.cc	2023-03-16 20:54:23.864869400 -0700
@@ -210,8 +210,9 @@
                                               (primary80IsLower80 && phyIndex <= nRus / 2) ||
                                               (!primary80IsLower80 && phyIndex > nRus / 2));
                             HeRu::RuSpec ru(ruType, index, primary80);
-                            ru.SetPhyIndex(bw, GetOperatingChannel().GetPrimaryChannelIndex(20));
-                            NS_ABORT_IF(ru.GetPhyIndex() != phyIndex);
+                            NS_ABORT_IF(ru.GetPhyIndex(bw,
+                                                       GetOperatingChannel().GetPrimaryChannelIndex(
+                                                           20)) != phyIndex);
                             m_ruBands[channelWidth].insert({band, ru});
                         }
                     }
@@ -266,6 +267,12 @@
     }
 }
 
+bool
+SpectrumWifiPhy::CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txWidth) const
+{
+    return GetLatestPhyEntity()->CanStartRx(ppdu, txWidth);
+}
+
 void
 SpectrumWifiPhy::StartRx(Ptr<SpectrumSignalParameters> rxParams)
 {
@@ -385,46 +392,38 @@
     // 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
-    uint16_t txWidth = wifiRxParams->ppdu->GetTransmissionChannelWidth();
+    const auto txWidth = wifiRxParams->txWidth;
+    const auto& ppdu = GetRxPpduFromTxPpdu(wifiRxParams->ppdu);
+    const auto& txVector = ppdu->GetTxVector();
     if (totalRxPowerW < DbmToW(GetRxSensitivity()) * (txWidth / 20.0))
     {
         NS_LOG_INFO("Received signal too weak to process: " << WToDbm(totalRxPowerW) << " dBm");
-        m_interference->Add(wifiRxParams->ppdu,
-                            wifiRxParams->ppdu->GetTxVector(),
-                            rxDuration,
-                            rxPowerW);
+        m_interference->Add(ppdu, txVector, rxDuration, rxPowerW);
         SwitchMaybeToCcaBusy(nullptr);
         return;
     }
 
-    // Unless we are receiving a TB PPDU, do not sync with this signal if the PPDU
-    // does not overlap with the receiver's primary20 channel
     if (wifiRxParams->txPhy)
     {
-        // if the channel width is a multiple of 20 MHz, then we consider the primary20 channel
-        uint16_t width = (GetChannelWidth() % 20 == 0 ? 20 : GetChannelWidth());
-        uint16_t p20MinFreq =
-            GetOperatingChannel().GetPrimaryChannelCenterFrequency(width) - width / 2;
-        uint16_t p20MaxFreq =
-            GetOperatingChannel().GetPrimaryChannelCenterFrequency(width) + width / 2;
-
-        if (!wifiRxParams->ppdu->CanBeReceived(p20MinFreq, p20MaxFreq))
+        if (!CanStartRx(ppdu, txWidth))
         {
-            NS_LOG_INFO("Cannot receive the PPDU, consider it as interference");
-            m_interference->Add(wifiRxParams->ppdu,
-                                wifiRxParams->ppdu->GetTxVector(),
-                                rxDuration,
-                                rxPowerW);
-            SwitchMaybeToCcaBusy(wifiRxParams->ppdu);
+            NS_LOG_INFO("Cannot start reception of the PPDU, consider it as interference");
+            m_interference->Add(ppdu, txVector, rxDuration, rxPowerW);
+            SwitchMaybeToCcaBusy(ppdu);
             return;
         }
     }
 
     NS_LOG_INFO("Received Wi-Fi signal");
-    Ptr<WifiPpdu> ppdu = wifiRxParams->ppdu->Copy();
     StartReceivePreamble(ppdu, rxPowerW, rxDuration);
 }
 
+Ptr<const WifiPpdu>
+SpectrumWifiPhy::GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu)
+{
+    return GetPhyEntityForPpdu(ppdu)->GetRxPpduFromTxPpdu(ppdu);
+}
+
 Ptr<Object>
 SpectrumWifiPhy::GetAntenna() const
 {
@@ -448,10 +447,10 @@
 }
 
 void
-SpectrumWifiPhy::StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector)
+SpectrumWifiPhy::StartTx(Ptr<const WifiPpdu> ppdu)
 {
     NS_LOG_FUNCTION(this << ppdu);
-    GetPhyEntity(ppdu->GetModulation())->StartTx(ppdu, txVector);
+    GetPhyEntity(ppdu->GetModulation())->StartTx(ppdu);
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/spectrum-wifi-phy.h ns-3.38/src/wifi/model/spectrum-wifi-phy.h
--- ns-3.37/src/wifi/model/spectrum-wifi-phy.h	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/spectrum-wifi-phy.h	2023-03-16 20:54:23.864869400 -0700
@@ -69,7 +69,7 @@
     ~SpectrumWifiPhy() override;
 
     // Implementation of pure virtual method.
-    void StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector) 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;
@@ -152,6 +152,14 @@
      */
     void Transmit(Ptr<WifiSpectrumSignalParameters> txParams);
 
+    /**
+     * Determine the WifiPpdu to be used by the RX PHY based on the WifiPpdu sent by the TX PHY.
+     *
+     * \param ppdu the WifiPpdu transmitted by the TX PHY
+     * \return the WifiPpdu to be used by the RX PHY
+     */
+    Ptr<const WifiPpdu> GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu);
+
   protected:
     void DoDispose() override;
     void DoInitialize() override;
@@ -192,6 +200,17 @@
      */
     void UpdateInterferenceHelperBands();
 
+    /**
+     * Determine whether the PHY shall issue a PHY-RXSTART.indication primitive in response to a
+     * given PPDU.
+     *
+     * \param ppdu the PPDU
+     * \param txChannelWidth the channel width (MHz) used to transmit the PPDU
+     * \return true if the PHY shall issue a PHY-RXSTART.indication primitive in response to a PPDU,
+     * false otherwise
+     */
+    bool CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txChannelWidth) const;
+
     Ptr<SpectrumChannel> m_channel; //!< SpectrumChannel that this SpectrumWifiPhy is connected to
 
     Ptr<WifiSpectrumPhyInterface> m_wifiSpectrumPhyInterface; //!< Spectrum PHY interface
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/sta-wifi-mac.cc ns-3.38/src/wifi/model/sta-wifi-mac.cc
--- ns-3.37/src/wifi/model/sta-wifi-mac.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/sta-wifi-mac.cc	2023-03-16 20:54:23.864869400 -0700
@@ -714,6 +714,34 @@
     return m_state == WAIT_ASSOC_RESP;
 }
 
+std::set<uint8_t>
+StaWifiMac::GetSetupLinkIds() const
+{
+    if (!IsAssociated())
+    {
+        return {};
+    }
+
+    std::set<uint8_t> linkIds;
+    for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
+    {
+        if (GetLink(linkId).bssid)
+        {
+            linkIds.insert(linkId);
+        }
+    }
+    return linkIds;
+}
+
+Mac48Address
+StaWifiMac::DoGetLocalAddress(const Mac48Address& remoteAddr) const
+{
+    auto linkIds = GetSetupLinkIds();
+    NS_ASSERT_MSG(!linkIds.empty(), "Not associated");
+    uint8_t linkId = *linkIds.begin();
+    return GetFrameExchangeManager(linkId)->GetAddress();
+}
+
 bool
 StaWifiMac::CanForwardPacketsTo(Mac48Address to) const
 {
@@ -773,8 +801,22 @@
                           // yet implemented (set it to 1 when implemented)
     }
 
-    hdr.SetAddr1(GetBssid(0)); // TODO use appropriate linkId
-    hdr.SetAddr2(GetAddress());
+    // the Receiver Address (RA) and the Transmitter Address (TA) are the MLD addresses only for
+    // non-broadcast data frames exchanged between two MLDs
+    auto linkIds = GetSetupLinkIds();
+    NS_ASSERT(!linkIds.empty());
+    uint8_t linkId = *linkIds.begin();
+    if (const auto apMldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(GetBssid(linkId)))
+    {
+        hdr.SetAddr1(*apMldAddr);
+        hdr.SetAddr2(GetAddress());
+    }
+    else
+    {
+        hdr.SetAddr1(GetBssid(linkId));
+        hdr.SetAddr2(GetFrameExchangeManager(linkId)->GetAddress());
+    }
+
     hdr.SetAddr3(to);
     hdr.SetDsNotFrom();
     hdr.SetDsTo();
@@ -795,11 +837,12 @@
 StaWifiMac::Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
 {
     NS_LOG_FUNCTION(this << *mpdu << +linkId);
-    const WifiMacHeader* hdr = &mpdu->GetHeader();
+    // consider the MAC header of the original MPDU (makes a difference for data frames only)
+    const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
     Ptr<const Packet> packet = mpdu->GetPacket();
     NS_ASSERT(!hdr->IsCtl());
-    Mac48Address myAddr =
-        hdr->IsData() ? GetAddress() : GetFrameExchangeManager(linkId)->GetAddress();
+    Mac48Address myAddr = hdr->IsData() ? Mac48Address::ConvertFrom(GetDevice()->GetAddress())
+                                        : GetFrameExchangeManager(linkId)->GetAddress();
     if (hdr->GetAddr3() == myAddr)
     {
         NS_LOG_LOGIC("packet sent by us.");
@@ -825,7 +868,12 @@
             NotifyRxDrop(packet);
             return;
         }
-        if (hdr->GetAddr2() != GetBssid(0)) // TODO use appropriate linkId
+        std::set<Mac48Address> apAddresses; // link addresses of AP
+        for (auto id : GetSetupLinkIds())
+        {
+            apAddresses.insert(GetBssid(id));
+        }
+        if (apAddresses.count(mpdu->GetHeader().GetAddr2()) == 0)
         {
             NS_LOG_LOGIC("Received data frame not from the BSS we are associated with: ignore");
             NotifyRxDrop(packet);
@@ -835,7 +883,7 @@
         {
             if (hdr->IsQosAmsdu())
             {
-                NS_ASSERT(hdr->GetAddr3() == GetBssid(0)); // TODO use appropriate linkId
+                NS_ASSERT(apAddresses.count(mpdu->GetHeader().GetAddr3()) != 0);
                 DeaggregateAmsduAndForward(mpdu);
                 packet = nullptr;
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/sta-wifi-mac.h ns-3.38/src/wifi/model/sta-wifi-mac.h
--- ns-3.37/src/wifi/model/sta-wifi-mac.h	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/sta-wifi-mac.h	2023-03-16 20:54:23.864869400 -0700
@@ -25,11 +25,13 @@
 #include "mgt-headers.h"
 #include "wifi-mac.h"
 
+#include <set>
 #include <variant>
 
 class TwoLevelAggregationTest;
 class AmpduAggregationTest;
 class HeAggregationTest;
+class MultiLinkOperationsTestBase;
 
 namespace ns3
 {
@@ -127,6 +129,8 @@
     friend class ::AmpduAggregationTest;
     /// Allow test cases to access private members
     friend class ::HeAggregationTest;
+    /// Allow test cases to access private members
+    friend class ::MultiLinkOperationsTestBase;
 
     /// type of the management frames used to get info about APs
     using MgtFrameType =
@@ -203,6 +207,13 @@
     bool IsAssociated() const;
 
     /**
+     * Get the IDs of the setup links (if any).
+     *
+     * \return the IDs of the setup links
+     */
+    std::set<uint8_t> GetSetupLinkIds() const;
+
+    /**
      * Return the association ID.
      *
      * \return the association ID
@@ -290,6 +301,7 @@
 
     void Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId) override;
     std::unique_ptr<LinkEntity> CreateLinkEntity() const override;
+    Mac48Address DoGetLocalAddress(const Mac48Address& remoteAddr) const override;
 
     /**
      * Process the Beacon frame received on the given link.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/table-based-error-rate-model.cc ns-3.38/src/wifi/model/table-based-error-rate-model.cc
--- ns-3.37/src/wifi/model/table-based-error-rate-model.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/table-based-error-rate-model.cc	2023-03-16 20:54:23.864869400 -0700
@@ -166,7 +166,8 @@
     else
     {
         NS_LOG_DEBUG("No MCS found for mode " << mode << ": use fallback error rate model");
-        return m_fallbackErrorModel->GetChunkSuccessRate(mode, txVector, snr, nbits, staId);
+        return m_fallbackErrorModel
+            ->GetChunkSuccessRate(mode, txVector, snr, nbits, numRxAntennas, field, staId);
     }
     bool ldpc = txVector.IsLdpc();
     NS_LOG_FUNCTION(this << +mcs << roundedSnr << size << ldpc);
@@ -181,7 +182,8 @@
     {
         NS_LOG_WARN("Table missing for MCS: "
                     << +mcs << " in TableBasedErrorRateModel: use fallback error rate model");
-        return m_fallbackErrorModel->GetChunkSuccessRate(mode, txVector, snr, nbits, staId);
+        return m_fallbackErrorModel
+            ->GetChunkSuccessRate(mode, txVector, snr, nbits, numRxAntennas, field, staId);
     }
 
     auto errorTable = (ldpc ? AwgnErrorTableLdpc1458
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/threshold-preamble-detection-model.cc ns-3.38/src/wifi/model/threshold-preamble-detection-model.cc
--- ns-3.37/src/wifi/model/threshold-preamble-detection-model.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/threshold-preamble-detection-model.cc	2023-03-16 20:54:23.864869400 -0700
@@ -40,7 +40,7 @@
             .SetGroupName("Wifi")
             .AddConstructor<ThresholdPreambleDetectionModel>()
             .AddAttribute("Threshold",
-                          "Preamble is successfully detection if the SNR is at or above this value "
+                          "Preamble is successfully detected if the SNR is at or above this value "
                           "(expressed in dB).",
                           DoubleValue(4),
                           MakeDoubleAccessor(&ThresholdPreambleDetectionModel::m_threshold),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/txop.cc ns-3.38/src/wifi/model/txop.cc
--- ns-3.37/src/wifi/model/txop.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/txop.cc	2023-03-16 20:54:23.864869400 -0700
@@ -68,8 +68,8 @@
                 "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<IntegerValue, std::list>(&Txop::SetMinCws,
-                                                                        &Txop::GetMinCws),
+                MakeAttributeContainerAccessor<UintegerValue, std::list>(&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 +84,8 @@
                 "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<IntegerValue, std::list>(&Txop::SetMaxCws,
-                                                                        &Txop::GetMaxCws),
+                MakeAttributeContainerAccessor<UintegerValue, std::list>(&Txop::SetMaxCws,
+                                                                         &Txop::GetMaxCws),
                 MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint32_t>()))
             .AddAttribute(
                 "Aifsn",
@@ -101,8 +101,8 @@
                 "The values of AIFSN for all the links",
                 TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
                 AttributeContainerValue<UintegerValue>(),
-                MakeAttributeContainerAccessor<IntegerValue, std::list>(&Txop::SetAifsns,
-                                                                        &Txop::GetAifsns),
+                MakeAttributeContainerAccessor<UintegerValue, std::list>(&Txop::SetAifsns,
+                                                                         &Txop::GetAifsns),
                 MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint8_t>()))
             .AddAttribute("TxopLimit",
                           "The TXOP limit: the default value conforms to non-QoS "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/vht/vht-phy.cc ns-3.38/src/wifi/model/vht/vht-phy.cc
--- ns-3.37/src/wifi/model/vht/vht-phy.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/vht/vht-phy.cc	2023-03-16 20:54:23.864869400 -0700
@@ -552,7 +552,7 @@
         {
         case WIFI_CHANLIST_PRIMARY: {
             // Start of a PPDU for which its power measured within the primary 20 MHz channel is at
-            // or above the CCA sensitivy threshold.
+            // or above the CCA sensitivity threshold.
             return m_wifiPhy->GetCcaSensitivityThreshold();
         }
         case WIFI_CHANLIST_SECONDARY:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/vht/vht-ppdu.cc ns-3.38/src/wifi/model/vht/vht-ppdu.cc
--- ns-3.37/src/wifi/model/vht/vht-ppdu.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/vht/vht-ppdu.cc	2023-03-16 20:54:23.864869400 -0700
@@ -46,14 +46,47 @@
                false) // don't instantiate LSigHeader of OfdmPpdu
 {
     NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << ppduDuration << band << uid);
+    SetPhyHeaders(txVector, ppduDuration);
+}
+
+void
+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
+VhtPpdu::SetLSigHeader(LSigHeader& lSig, Time ppduDuration) const
+{
     uint16_t length =
         ((ceil((static_cast<double>(ppduDuration.GetNanoSeconds() - (20 * 1000)) / 1000) / 4.0) *
           3) -
          3);
-    m_lSig.SetLength(length);
-    m_vhtSig.SetMuFlag(m_preamble == WIFI_PREAMBLE_VHT_MU);
-    m_vhtSig.SetChannelWidth(m_channelWidth);
-    m_vhtSig.SetShortGuardInterval(txVector.GetGuardInterval() == 400);
+    lSig.SetLength(length);
+}
+
+void
+VhtPpdu::SetVhtSigHeader(VhtSigHeader& vhtSig,
+                         const WifiTxVector& txVector,
+                         Time ppduDuration) const
+{
+    vhtSig.SetMuFlag(m_preamble == WIFI_PREAMBLE_VHT_MU);
+    vhtSig.SetChannelWidth(txVector.GetChannelWidth());
+    vhtSig.SetShortGuardInterval(txVector.GetGuardInterval() == 400);
     uint32_t nSymbols =
         (static_cast<double>(
              (ppduDuration - WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector))
@@ -61,14 +94,10 @@
          (3200 + txVector.GetGuardInterval()));
     if (txVector.GetGuardInterval() == 400)
     {
-        m_vhtSig.SetShortGuardIntervalDisambiguation((nSymbols % 10) == 9);
+        vhtSig.SetShortGuardIntervalDisambiguation((nSymbols % 10) == 9);
     }
-    m_vhtSig.SetSuMcs(txVector.GetMode().GetMcsValue());
-    m_vhtSig.SetNStreams(txVector.GetNss());
-}
-
-VhtPpdu::~VhtPpdu()
-{
+    vhtSig.SetSuMcs(txVector.GetMode().GetMcsValue());
+    vhtSig.SetNStreams(txVector.GetNss());
 }
 
 WifiTxVector
@@ -76,27 +105,75 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-    txVector.SetMode(VhtPhy::GetVhtMcs(m_vhtSig.GetSuMcs()));
-    txVector.SetChannelWidth(m_vhtSig.GetChannelWidth());
-    txVector.SetNss(m_vhtSig.GetNStreams());
-    txVector.SetGuardInterval(m_vhtSig.GetShortGuardInterval() ? 400 : 800);
-    txVector.SetAggregation(GetPsdu()->IsAggregate());
+
+#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;
 }
 
+void
+VhtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
+                                   const LSigHeader& lSig,
+                                   const VhtSigHeader& vhtSig) const
+{
+    txVector.SetMode(VhtPhy::GetVhtMcs(vhtSig.GetSuMcs()));
+    txVector.SetChannelWidth(vhtSig.GetChannelWidth());
+    txVector.SetNss(vhtSig.GetNStreams());
+    txVector.SetGuardInterval(vhtSig.GetShortGuardInterval() ? 400 : 800);
+    txVector.SetAggregation(GetPsdu()->IsAggregate());
+}
+
 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>(m_lSig.GetLength() + 3) / 3)) * 4) + 20);
+    Time calculatedDuration = MicroSeconds(((ceil(static_cast<double>(length + 3) / 3)) * 4) + 20);
     uint32_t nSymbols =
         floor(static_cast<double>((calculatedDuration - preambleDuration).GetNanoSeconds()) /
               tSymbol.GetNanoSeconds());
-    if (m_vhtSig.GetShortGuardInterval() && m_vhtSig.GetShortGuardIntervalDisambiguation())
+    if (sgi && sgiDisambiguation)
     {
         nSymbols--;
     }
@@ -107,12 +184,7 @@
 Ptr<WifiPpdu>
 VhtPpdu::Copy() const
 {
-    return Create<VhtPpdu>(GetPsdu(),
-                           GetTxVector(),
-                           m_txCenterFreq,
-                           GetTxDuration(),
-                           m_band,
-                           m_uid);
+    return Ptr<WifiPpdu>(new VhtPpdu(*this), false);
 }
 
 WifiPpduType
@@ -131,10 +203,6 @@
 {
 }
 
-VhtPpdu::VhtSigHeader::~VhtSigHeader()
-{
-}
-
 TypeId
 VhtPpdu::VhtSigHeader::GetTypeId()
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/vht/vht-ppdu.h ns-3.38/src/wifi/model/vht/vht-ppdu.h
--- ns-3.37/src/wifi/model/vht/vht-ppdu.h	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/vht/vht-ppdu.h	2023-03-16 20:54:23.864869400 -0700
@@ -52,7 +52,6 @@
     {
       public:
         VhtSigHeader();
-        ~VhtSigHeader() override;
 
         /**
          * \brief Get the type ID.
@@ -165,10 +164,6 @@
             Time ppduDuration,
             WifiPhyBand band,
             uint64_t uid);
-    /**
-     * Destructor for VhtPpdu.
-     */
-    ~VhtPpdu() override;
 
     Time GetTxDuration() const override;
     Ptr<WifiPpdu> Copy() const override;
@@ -177,8 +172,48 @@
   private:
     WifiTxVector DoGetTxVector() 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
+     */
+    virtual 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;
+
+    /**
+     * Fill in the VHT-SIG header.
+     *
+     * \param vhtSig the VHT-SIG header to fill in
+     * \param txVector the TXVECTOR that was used for this PPDU
+     * \param ppduDuration the transmission duration of this PPDU
+     */
+    void SetVhtSigHeader(VhtSigHeader& vhtSig,
+                         const WifiTxVector& txVector,
+                         Time ppduDuration) const;
+
+    /**
+     * Fill in the TXVECTOR from PHY headers.
+     *
+     * \param txVector the TXVECTOR to fill in
+     * \param lSig the L-SIG header
+     * \param vhtSig the VHT-SIG header
+     */
+    void SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
+                                   const LSigHeader& lSig,
+                                   const VhtSigHeader& vhtSig) const;
+
+#ifndef NS3_BUILD_PROFILE_DEBUG
     VhtSigHeader m_vhtSig; //!< the VHT-SIG PHY header
-};                         // class VhtPpdu
+#endif
+}; // class VhtPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-assoc-manager.cc ns-3.38/src/wifi/model/wifi-assoc-manager.cc
--- ns-3.37/src/wifi/model/wifi-assoc-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/wifi-assoc-manager.cc	2023-03-16 20:54:23.864869400 -0700
@@ -51,16 +51,15 @@
         return true;
     }
 
-    // the Compare method implemented by subclass may be such that the two ApInfo objects
-    // compare equal; in such a case, use the BSSID as tie breaker
     bool rhsBefore = m_manager.Compare(rhs, lhs);
     if (rhsBefore)
     {
         return false;
     }
 
-    WifiAddressHash hash;
-    return hash(lhs.m_bssid) > hash(rhs.m_bssid);
+    // the Compare method implemented by subclass may be such that the two ApInfo objects
+    // compare equal; in such a case, use the BSSID as tie breaker
+    return lhs.m_bssid < rhs.m_bssid;
 }
 
 TypeId
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-default-ack-manager.cc ns-3.38/src/wifi/model/wifi-default-ack-manager.cc
--- ns-3.37/src/wifi/model/wifi-default-ack-manager.cc	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/wifi-default-ack-manager.cc	2023-03-16 20:54:23.864869400 -0700
@@ -28,7 +28,6 @@
 #include "wifi-tx-parameters.h"
 
 #include "ns3/he-frame-exchange-manager.h"
-#include "ns3/he-phy.h"
 #include "ns3/log.h"
 
 namespace ns3
@@ -97,15 +96,15 @@
 {
     NS_LOG_FUNCTION(this << *mpdu << &txParams);
 
-    const WifiMacHeader& hdr = mpdu->GetHeader();
-    Mac48Address receiver = hdr.GetAddr1();
+    auto receiver = mpdu->GetHeader().GetAddr1();
+    auto origReceiver = mpdu->GetOriginal()->GetHeader().GetAddr1();
 
-    uint8_t tid = hdr.GetQosTid();
+    uint8_t tid = mpdu->GetHeader().GetQosTid();
     Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
-    NS_ABORT_MSG_IF(!edca->GetBaAgreementEstablished(receiver, tid),
+    NS_ABORT_MSG_IF(!m_mac->GetBaAgreementEstablishedAsOriginator(origReceiver, tid),
                     "An established Block Ack agreement is required");
 
-    uint16_t startingSeq = edca->GetBaStartingSequence(receiver, tid);
+    uint16_t startingSeq = edca->GetBaStartingSequence(origReceiver, tid);
     uint16_t maxDistFromStartingSeq =
         (mpdu->GetHeader().GetSequenceNumber() - startingSeq + SEQNO_SPACE_SIZE) % SEQNO_SPACE_SIZE;
     NS_ABORT_MSG_IF(maxDistFromStartingSeq >= SEQNO_SPACE_HALF_SIZE,
@@ -144,7 +143,7 @@
     NS_LOG_FUNCTION(this << *mpdu << &txParams);
 
     uint8_t tid = mpdu->GetHeader().GetQosTid();
-    Mac48Address receiver = mpdu->GetHeader().GetAddr1();
+    Mac48Address receiver = mpdu->GetOriginal()->GetHeader().GetAddr1();
     Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
 
     // An immediate response (Ack or Block Ack) is needed if any of the following holds:
@@ -171,6 +170,43 @@
     return true;
 }
 
+bool
+WifiDefaultAckManager::ExistInflightOnSameLink(Ptr<const WifiMpdu> mpdu) const
+{
+    NS_ASSERT(mpdu->GetHeader().IsQosData());
+    auto tid = mpdu->GetHeader().GetQosTid();
+    NS_ASSERT(mpdu->IsQueued());
+    auto queue = m_mac->GetTxopQueue(mpdu->GetQueueAc());
+    auto origReceiver = mpdu->GetOriginal()->GetHeader().GetAddr1();
+    auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(origReceiver, tid);
+    NS_ASSERT(agreement);
+    auto mpduDist = agreement->get().GetDistance(mpdu->GetHeader().GetSequenceNumber());
+
+    Ptr<WifiMpdu> item = queue->PeekByTidAndAddress(tid, origReceiver);
+
+    while (item)
+    {
+        auto itemDist = agreement->get().GetDistance(item->GetHeader().GetSequenceNumber());
+        if (itemDist == mpduDist)
+        {
+            NS_LOG_DEBUG("No previous MPDU in-flight on the same link");
+            return false;
+        }
+        NS_ABORT_MSG_IF(itemDist > mpduDist,
+                        "While searching for given MPDU ("
+                            << *mpdu << "), found first another one (" << *item
+                            << ") with higher sequence number");
+        if (auto linkIds = item->GetInFlightLinkIds(); linkIds.count(m_linkId) > 0)
+        {
+            NS_LOG_DEBUG("Found MPDU inflight on the same link");
+            return true;
+        }
+        item = queue->PeekByTidAndAddress(tid, origReceiver, item);
+    }
+    NS_ABORT_MSG("Should not get here");
+    return false;
+}
+
 std::unique_ptr<WifiAcknowledgment>
 WifiDefaultAckManager::TryAddMpdu(Ptr<const WifiMpdu> mpdu, const WifiTxParameters& txParams)
 {
@@ -238,11 +274,10 @@
         return TryUlMuTransmission(mpdu, txParams);
     }
 
-    // if the current protection method (if any) is already BLOCK_ACK or BAR_BLOCK_ACK,
-    // it will not change by adding an MPDU
+    // if the current acknowledgment method (if any) is already BLOCK_ACK, it will not
+    // change by adding an MPDU
     if (txParams.m_acknowledgment &&
-        (txParams.m_acknowledgment->method == WifiAcknowledgment::BLOCK_ACK ||
-         txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK))
+        txParams.m_acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
     {
         return nullptr;
     }
@@ -258,8 +293,8 @@
         return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
     }
 
-    if ((!hdr.IsQosData() || !m_mac->GetQosTxop(hdr.GetQosTid())
-                                  ->GetBaAgreementEstablished(receiver, hdr.GetQosTid())) &&
+    if ((!hdr.IsQosData() ||
+         !m_mac->GetBaAgreementEstablishedAsOriginator(receiver, hdr.GetQosTid())) &&
         !hdr.IsBlockAckReq())
     {
         NS_LOG_DEBUG(
@@ -296,8 +331,7 @@
 
     // we get here if a response is needed
     uint8_t tid = GetTid(mpdu->GetPacket(), hdr);
-    if (!hdr.IsBlockAckReq() && txParams.GetSize(receiver) == 0 &&
-        hdr.GetSequenceNumber() == m_mac->GetQosTxop(tid)->GetBaStartingSequence(receiver, tid))
+    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;
@@ -319,8 +353,8 @@
         acknowledgment->blockAckReqTxVector =
             GetWifiRemoteStationManager()->GetBlockAckTxVector(receiver, txParams.m_txVector);
         acknowledgment->blockAckTxVector = acknowledgment->blockAckReqTxVector;
-        acknowledgment->barType = m_mac->GetQosTxop(tid)->GetBlockAckReqType(receiver, tid);
-        acknowledgment->baType = m_mac->GetQosTxop(tid)->GetBlockAckType(receiver, tid);
+        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);
     }
@@ -330,7 +364,7 @@
     WifiBlockAck* acknowledgment = new WifiBlockAck;
     acknowledgment->blockAckTxVector =
         GetWifiRemoteStationManager()->GetBlockAckTxVector(receiver, txParams.m_txVector);
-    acknowledgment->baType = m_mac->GetQosTxop(tid)->GetBlockAckType(receiver, tid);
+    acknowledgment->baType = m_mac->GetBaTypeAsOriginator(receiver, tid);
     acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::NORMAL_ACK);
     return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
 }
@@ -405,14 +439,16 @@
             receiver,
             WifiDlMuBarBaSequence::BlockAckInfo{
                 GetWifiRemoteStationManager()->GetBlockAckTxVector(receiver, txParams.m_txVector),
-                edca->GetBlockAckType(receiver, tid)});
+                m_mac->GetBaTypeAsOriginator(receiver, tid)});
         return std::unique_ptr<WifiDlMuBarBaSequence>(acknowledgment);
     }
 
     // we get here if this is the first MPDU for this receiver
-    if (edca->GetBaManager()->GetBar(true, tid, receiver) ||
-        (acknowledgment && (!acknowledgment->stationsReplyingWithNormalAck.empty() ||
-                            !acknowledgment->stationsReplyingWithBlockAck.empty())))
+    auto htFem = DynamicCast<HtFrameExchangeManager>(m_mac->GetFrameExchangeManager(m_linkId));
+    NS_ASSERT(htFem);
+    if (auto bar = htFem->GetBar(QosUtilsMapTidToAc(tid), tid, receiver);
+        bar || (acknowledgment && (!acknowledgment->stationsReplyingWithNormalAck.empty() ||
+                                   !acknowledgment->stationsReplyingWithBlockAck.empty())))
     {
         // there is a pending BlockAckReq for this receiver or another receiver
         // was selected for immediate response.
@@ -435,9 +471,9 @@
             receiver,
             WifiDlMuBarBaSequence::BlockAckReqInfo{
                 GetWifiRemoteStationManager()->GetBlockAckTxVector(receiver, txParams.m_txVector),
-                edca->GetBlockAckReqType(receiver, tid),
+                m_mac->GetBarTypeAsOriginator(receiver, tid),
                 GetWifiRemoteStationManager()->GetBlockAckTxVector(receiver, txParams.m_txVector),
-                edca->GetBlockAckType(receiver, tid)});
+                m_mac->GetBaTypeAsOriginator(receiver, tid)});
 
         acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::BLOCK_ACK);
         return std::unique_ptr<WifiDlMuBarBaSequence>(acknowledgment);
@@ -516,15 +552,17 @@
 
         // determine the TX vector used to send the BlockAck frame
         WifiTxVector blockAckTxVector;
-        blockAckTxVector.SetPreambleType(WifiPreamble::WIFI_PREAMBLE_HE_TB);
+        auto preamble = IsEht(txParams.m_txVector.GetPreambleType()) ? WIFI_PREAMBLE_EHT_TB
+                                                                     : WIFI_PREAMBLE_HE_TB;
+        blockAckTxVector.SetPreambleType(preamble);
         blockAckTxVector.SetChannelWidth(txParams.m_txVector.GetChannelWidth());
-        blockAckTxVector.SetGuardInterval(txParams.m_txVector.GetGuardInterval());
+        // 800ns GI is not allowed for HE TB
+        blockAckTxVector.SetGuardInterval(
+            std::max<uint16_t>(txParams.m_txVector.GetGuardInterval(), 1600));
         const auto& userInfo = txParams.m_txVector.GetHeMuUserInfo(staId);
         blockAckTxVector.SetHeMuUserInfo(
             staId,
-            {userInfo.ru,
-             HePhy::GetHeMcs(std::min(userInfo.mcs.GetMcsValue(), m_maxMcsForBlockAckInTbPpdu)),
-             userInfo.nss});
+            {userInfo.ru, std::min(userInfo.mcs, m_maxMcsForBlockAckInTbPpdu), userInfo.nss});
 
         NS_LOG_DEBUG("Adding STA "
                      << receiver
@@ -532,11 +570,13 @@
         Ptr<QosTxop> edca = m_mac->GetQosTxop(QosUtilsMapTidToAc(tid));
         acknowledgment->stationsReplyingWithBlockAck.emplace(
             receiver,
-            WifiDlMuTfMuBar::BlockAckInfo{edca->GetBaManager()->GetBlockAckReqHeader(receiver, tid),
+            WifiDlMuTfMuBar::BlockAckInfo{edca->GetBaManager()->GetBlockAckReqHeader(
+                                              mpdu->GetOriginal()->GetHeader().GetAddr1(),
+                                              tid),
                                           blockAckTxVector,
-                                          edca->GetBlockAckType(receiver, tid)});
+                                          m_mac->GetBaTypeAsOriginator(receiver, tid)});
 
-        acknowledgment->barTypes.push_back(edca->GetBlockAckReqType(receiver, tid));
+        acknowledgment->barTypes.push_back(m_mac->GetBarTypeAsOriginator(receiver, tid));
         acknowledgment->muBarTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(receiver);
         acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::BLOCK_ACK);
         return std::unique_ptr<WifiDlMuTfMuBar>(acknowledgment);
@@ -600,15 +640,17 @@
 
         // determine the TX vector used to send the BlockAck frame
         WifiTxVector blockAckTxVector;
-        blockAckTxVector.SetPreambleType(WifiPreamble::WIFI_PREAMBLE_HE_TB);
+        auto preamble = IsEht(txParams.m_txVector.GetPreambleType()) ? WIFI_PREAMBLE_EHT_TB
+                                                                     : WIFI_PREAMBLE_HE_TB;
+        blockAckTxVector.SetPreambleType(preamble);
         blockAckTxVector.SetChannelWidth(txParams.m_txVector.GetChannelWidth());
-        blockAckTxVector.SetGuardInterval(txParams.m_txVector.GetGuardInterval());
+        // 800ns GI is not allowed for HE TB
+        blockAckTxVector.SetGuardInterval(
+            std::max<uint16_t>(txParams.m_txVector.GetGuardInterval(), 1600));
         const auto& userInfo = txParams.m_txVector.GetHeMuUserInfo(staId);
         blockAckTxVector.SetHeMuUserInfo(
             staId,
-            {userInfo.ru,
-             HePhy::GetHeMcs(std::min(userInfo.mcs.GetMcsValue(), m_maxMcsForBlockAckInTbPpdu)),
-             userInfo.nss});
+            {userInfo.ru, std::min(userInfo.mcs, m_maxMcsForBlockAckInTbPpdu), userInfo.nss});
 
         NS_LOG_DEBUG("Adding STA " << receiver
                                    << " to the list of stations that will reply with a Block Ack");
@@ -616,10 +658,12 @@
         acknowledgment->stationsReplyingWithBlockAck.emplace(
             receiver,
             WifiDlMuAggregateTf::BlockAckInfo{
-                GetMuBarSize({edca->GetBlockAckReqType(receiver, tid)}),
-                edca->GetBaManager()->GetBlockAckReqHeader(receiver, tid),
+                GetMuBarSize({m_mac->GetBarTypeAsOriginator(receiver, tid)}),
+                edca->GetBaManager()->GetBlockAckReqHeader(
+                    mpdu->GetOriginal()->GetHeader().GetAddr1(),
+                    tid),
                 blockAckTxVector,
-                edca->GetBlockAckType(receiver, tid)});
+                m_mac->GetBaTypeAsOriginator(receiver, tid)});
 
         acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::NO_EXPLICIT_ACK);
         return std::unique_ptr<WifiDlMuAggregateTf>(acknowledgment);
@@ -667,12 +711,12 @@
             }
             NS_ABORT_MSG_IF(aid12 == 0 || aid12 > 2007, "Allocation of RA-RUs is not supported");
 
-            NS_ASSERT(apMac->GetStaList().find(aid12) != apMac->GetStaList().end());
-            Mac48Address staAddress = apMac->GetStaList().find(aid12)->second;
+            NS_ASSERT(apMac->GetStaList(m_linkId).find(aid12) != apMac->GetStaList(m_linkId).end());
+            Mac48Address staAddress = apMac->GetStaList(m_linkId).find(aid12)->second;
 
             // find a TID for which a BA agreement exists with the given originator
             uint8_t tid = 0;
-            while (tid < 8 && !heFem->GetBaAgreementEstablished(staAddress, tid))
+            while (tid < 8 && !m_mac->GetBaAgreementEstablishedAsRecipient(staAddress, tid))
             {
                 tid++;
             }
@@ -686,13 +730,13 @@
             // we assume the Block Acknowledgment context is used for the multi-STA BlockAck frame
             // (since it requires the longest TX time due to the presence of a bitmap)
             acknowledgment->baType.m_bitmapLen.push_back(
-                heFem->GetBlockAckType(staAddress, tid).m_bitmapLen.at(0));
+                m_mac->GetBaTypeAsRecipient(staAddress, tid).m_bitmapLen.at(0));
         }
 
         uint16_t staId = trigger.begin()->GetAid12();
         acknowledgment->tbPpduTxVector = trigger.GetHeTbTxVector(staId);
         acknowledgment->multiStaBaTxVector = GetWifiRemoteStationManager()->GetBlockAckTxVector(
-            apMac->GetStaList().find(staId)->second,
+            apMac->GetStaList(m_linkId).find(staId)->second,
             acknowledgment->tbPpduTxVector);
         return std::unique_ptr<WifiUlMuMultiStaBa>(acknowledgment);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-default-ack-manager.h ns-3.38/src/wifi/model/wifi-default-ack-manager.h
--- ns-3.37/src/wifi/model/wifi-default-ack-manager.h	2022-11-01 17:37:24.751396000 -0700
+++ ns-3.38/src/wifi/model/wifi-default-ack-manager.h	2023-03-16 20:54:23.864869400 -0700
@@ -78,6 +78,13 @@
      */
     bool IsResponseNeeded(Ptr<const WifiMpdu> mpdu, const WifiTxParameters& txParams) const;
 
+    /**
+     * \param mpdu the given MPDU
+     * \return whether there exist MPDUs with lower sequence number than the given MPDU that are
+     * inflight on the same link as the given MPDU
+     */
+    bool ExistInflightOnSameLink(Ptr<const WifiMpdu> mpdu) const;
+
   private:
     /**
      * Compute the information about the acknowledgment of the current multi-user frame
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-default-protection-manager.cc ns-3.38/src/wifi/model/wifi-default-protection-manager.cc
--- ns-3.37/src/wifi/model/wifi-default-protection-manager.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-default-protection-manager.cc	2023-03-16 20:54:23.864869400 -0700
@@ -19,12 +19,16 @@
 
 #include "wifi-default-protection-manager.h"
 
-#include "wifi-mac.h"
+#include "ap-wifi-mac.h"
 #include "wifi-mpdu.h"
 #include "wifi-tx-parameters.h"
 
+#include "ns3/boolean.h"
+#include "ns3/erp-ofdm-phy.h"
 #include "ns3/log.h"
 
+#include <type_traits>
+
 namespace ns3
 {
 
@@ -35,10 +39,16 @@
 TypeId
 WifiDefaultProtectionManager::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::WifiDefaultProtectionManager")
-                            .SetParent<WifiProtectionManager>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<WifiDefaultProtectionManager>();
+    static TypeId tid =
+        TypeId("ns3::WifiDefaultProtectionManager")
+            .SetParent<WifiProtectionManager>()
+            .SetGroupName("Wifi")
+            .AddConstructor<WifiDefaultProtectionManager>()
+            .AddAttribute("EnableMuRts",
+                          "If enabled, always protect a DL/UL MU frame exchange with MU-RTS/CTS.",
+                          BooleanValue(false),
+                          MakeBooleanAccessor(&WifiDefaultProtectionManager::m_sendMuRts),
+                          MakeBooleanChecker());
     return tid;
 }
 
@@ -57,12 +67,20 @@
 {
     NS_LOG_FUNCTION(this << *mpdu << &txParams);
 
-    // TB PPDUs need no protection (the soliciting Trigger Frame can be protected
-    // by an MU-RTS). Until MU-RTS is implemented, we disable protection also for:
-    // - Trigger Frames
-    // - DL MU PPDUs containing more than one PSDU
-    if (txParams.m_txVector.IsUlMu() || mpdu->GetHeader().IsTrigger() ||
-        (txParams.m_txVector.IsDlMu() && txParams.GetPsduInfoMap().size() > 1))
+    // For a DL MU PPDU containing more than one PSDU, call a separate method.
+    // 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();
+        txParams.m_txVector.IsDlMu() &&
+        (psduInfoMap.size() > 1 ||
+         (psduInfoMap.size() == 1 && psduInfoMap.begin()->first != mpdu->GetHeader().GetAddr1())))
+    {
+        return TryAddMpduToMuPpdu(mpdu, txParams);
+    }
+
+    // No protection for TB PPDUs (the soliciting Trigger Frame can be protected by an MU-RTS)
+    if (txParams.m_txVector.IsUlMu())
     {
         if (txParams.m_protection)
         {
@@ -72,12 +90,10 @@
         return std::unique_ptr<WifiProtection>(new WifiNoProtection);
     }
 
-    // If we are adding a second PSDU to a DL MU PPDU, switch to no protection
-    // (until MU-RTS is implemented)
-    if (txParams.m_txVector.IsDlMu() && txParams.GetPsduInfoMap().size() == 1 &&
-        !txParams.GetPsduInfo(mpdu->GetHeader().GetAddr1()))
+    // if this is a Trigger Frame, call a separate method
+    if (mpdu->GetHeader().IsTrigger())
     {
-        return std::unique_ptr<WifiProtection>(new WifiNoProtection);
+        return TryUlMuTransmission(mpdu, txParams);
     }
 
     // if the current protection method (if any) is already RTS/CTS or CTS-to-Self,
@@ -110,11 +126,12 @@
 {
     NS_LOG_FUNCTION(this << *msdu << &txParams);
 
-    // if the current protection method is already RTS/CTS or CTS-to-Self,
+    // if the current protection method is already RTS/CTS, CTS-to-Self or MU-RTS/CTS,
     // it will not change by aggregating an MSDU
     NS_ASSERT(txParams.m_protection);
     if (txParams.m_protection->method == WifiProtection::RTS_CTS ||
-        txParams.m_protection->method == WifiProtection::CTS_TO_SELF)
+        txParams.m_protection->method == WifiProtection::CTS_TO_SELF ||
+        txParams.m_protection->method == WifiProtection::MU_RTS_CTS)
     {
         return nullptr;
     }
@@ -179,4 +196,146 @@
     return std::unique_ptr<WifiProtection>(new WifiNoProtection());
 }
 
+std::unique_ptr<WifiProtection>
+WifiDefaultProtectionManager::TryAddMpduToMuPpdu(Ptr<const WifiMpdu> mpdu,
+                                                 const WifiTxParameters& txParams)
+{
+    NS_LOG_FUNCTION(this << *mpdu << &txParams);
+    NS_ASSERT(txParams.m_txVector.IsDlMu());
+
+    if (!m_sendMuRts)
+    {
+        // No protection because sending MU-RTS is disabled
+        if (txParams.m_protection && txParams.m_protection->method == WifiProtection::NONE)
+        {
+            return nullptr;
+        }
+        return std::unique_ptr<WifiProtection>(new WifiNoProtection());
+    }
+
+    WifiMuRtsCtsProtection* protection = nullptr;
+    if (txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS)
+    {
+        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();
+
+        if (protection != nullptr)
+        {
+            // txParams.m_protection points to an existing WifiMuRtsCtsProtection object.
+            // We have to return a copy of this object including the needed changes
+            protection = new WifiMuRtsCtsProtection(*protection);
+        }
+        else
+        {
+            // we have to create a new WifiMuRtsCtsProtection object
+            protection = new 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,
+            // PE Disambiguity, UL Spatial Reuse, Doppler and UL HE-SIG-A2 Reserved subfields in
+            // the Common Info field are reserved. (Sec. 9.3.1.22.5 of 802.11ax)
+            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);
+
+            // compute the TXVECTOR to use to send the MU-RTS Trigger Frame
+            protection->muRtsTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(receiver);
+            // The transmitter of an MU-RTS Trigger frame shall not request a non-AP STA to send
+            // a CTS frame response in a 20 MHz channel that is not occupied by the PPDU that
+            // contains the MU-RTS Trigger frame. (Sec. 26.2.6.2 of 802.11ax)
+            protection->muRtsTxVector.SetChannelWidth(txWidth);
+        }
+
+        // Add a User Info field for the new receiver
+        // The UL HE-MCS, UL FEC Coding Type, UL DCM, SS Allocation and UL Target RSSI fields
+        // in the User Info field are reserved (Sec. 9.3.1.22.5 of 802.11ax)
+        AddUserInfoToMuRts(protection->muRts, txWidth, receiver);
+
+        return std::unique_ptr<WifiMuRtsCtsProtection>(protection);
+    }
+
+    // an MPDU addressed to the same receiver has been already added
+    NS_ASSERT(protection != nullptr);
+
+    // no change is needed
+    return nullptr;
+}
+
+std::unique_ptr<WifiProtection>
+WifiDefaultProtectionManager::TryUlMuTransmission(Ptr<const WifiMpdu> mpdu,
+                                                  const WifiTxParameters& txParams)
+{
+    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;
+    // 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,
+    // PE Disambiguity, UL Spatial Reuse, Doppler and UL HE-SIG-A2 Reserved subfields in
+    // the Common Info field are reserved. (Sec. 9.3.1.22.5 of 802.11ax)
+    protection->muRts.SetType(TriggerFrameType::MU_RTS_TRIGGER);
+    protection->muRts.SetUlBandwidth(txWidth);
+
+    NS_ABORT_MSG_IF(m_mac->GetTypeOfStation() != AP, "HE APs only can send DL MU PPDUs");
+    const auto& staList = StaticCast<ApWifiMac>(m_mac)->GetStaList(m_linkId);
+    std::remove_reference_t<decltype(staList)>::const_iterator staIt;
+
+    for (const auto& userInfo : trigger)
+    {
+        // Add a User Info field to the MU-RTS for this solicited station
+        // The UL HE-MCS, UL FEC Coding Type, UL DCM, SS Allocation and UL Target RSSI fields
+        // in the User Info field are reserved (Sec. 9.3.1.22.5 of 802.11ax)
+        staIt = staList.find(userInfo.GetAid12());
+        NS_ASSERT(staIt != staList.cend());
+        AddUserInfoToMuRts(protection->muRts, txWidth, staIt->second);
+    }
+
+    // compute the TXVECTOR to use to send the MU-RTS Trigger Frame
+    protection->muRtsTxVector =
+        GetWifiRemoteStationManager()->GetRtsTxVector(mpdu->GetHeader().GetAddr1());
+    // The transmitter of an MU-RTS Trigger frame shall not request a non-AP STA to send
+    // a CTS frame response in a 20 MHz channel that is not occupied by the PPDU that
+    // contains the MU-RTS Trigger frame. (Sec. 26.2.6.2 of 802.11ax)
+    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());
+    }
+
+    return std::unique_ptr<WifiMuRtsCtsProtection>(protection);
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-default-protection-manager.h ns-3.38/src/wifi/model/wifi-default-protection-manager.h
--- ns-3.37/src/wifi/model/wifi-default-protection-manager.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-default-protection-manager.h	2023-03-16 20:54:23.864869400 -0700
@@ -64,6 +64,42 @@
     virtual std::unique_ptr<WifiProtection> GetPsduProtection(const WifiMacHeader& hdr,
                                                               uint32_t size,
                                                               const WifiTxVector& txVector) const;
+
+  private:
+    /**
+     * Calculate the protection method to use if the given MPDU is added to the
+     * current DL MU PPDU (represented by the given TX parameters). If the computed
+     * protection method is the same as the current one, a null pointer is
+     * returned. Otherwise, the computed protection method is returned.
+     * The TX width of the PPDU containing the MU-RTS is the same as the DL MU PPDU
+     * being protected. Each non-AP station is solicited to transmit a CTS occupying a
+     * bandwidth equal to the minimum between the TX width of the DL MU PPDU and the
+     * maximum channel width supported by the non-AP station.
+     *
+     * \param mpdu the given MPDU
+     * \param txParams the TX parameters describing the current DL MU PPDU
+     * \return the new protection method or a null pointer if the protection method
+     *         is unchanged
+     */
+    virtual std::unique_ptr<WifiProtection> TryAddMpduToMuPpdu(Ptr<const WifiMpdu> mpdu,
+                                                               const WifiTxParameters& txParams);
+
+    /**
+     * Calculate the protection method for the UL MU transmission solicited by the given
+     * Trigger Frame.
+     * The TX width of the PPDU containing the MU-RTS is the same as the TB PPDUs being
+     * solicited by the given Trigger Frame. Each non-AP station is solicited to transmit a CTS
+     * occupying a bandwidth equal to the minimum between the TX width of the PPDU containing
+     * the MU-RTS and the maximum channel width supported by the non-AP station.
+     *
+     * \param mpdu the given Trigger Frame
+     * \param txParams the current TX parameters (just the TXVECTOR needs to be set)
+     * \return the protection method for the UL MU transmission solicited by the given Trigger Frame
+     */
+    virtual std::unique_ptr<WifiProtection> TryUlMuTransmission(Ptr<const WifiMpdu> mpdu,
+                                                                const WifiTxParameters& txParams);
+
+    bool m_sendMuRts; //!< true for sending an MU-RTS to protect DL MU PPDUs
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-information-element.h ns-3.38/src/wifi/model/wifi-information-element.h
--- ns-3.37/src/wifi/model/wifi-information-element.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-information-element.h	2023-03-16 20:54:23.864869400 -0700
@@ -27,6 +27,9 @@
 namespace ns3
 {
 
+/// Size in bytes of the Element ID Extension field (IEEE 802.11-2020 9.4.2.1 General)
+constexpr uint8_t WIFI_IE_ELEMENT_ID_EXT_SIZE = 1;
+
 /**
  * This type is used to represent an Information Element ID. An
  * enumeration would be tidier, but doesn't provide for the
@@ -230,8 +233,10 @@
 #define IE_EXT_UORA_PARAMETER_SET ((WifiInformationElementId)37)
 #define IE_EXT_MU_EDCA_PARAMETER_SET ((WifiInformationElementId)38)
 
+#define IE_EXT_EHT_OPERATION ((WifiInformationElementId)106)
 #define IE_EXT_MULTI_LINK_ELEMENT ((WifiInformationElementId)107)
 #define IE_EXT_EHT_CAPABILITIES ((WifiInformationElementId)108)
+#define IE_EXT_TID_TO_LINK_MAPPING_ELEMENT ((WifiInformationElementId)109)
 
 /**
  * \brief Information element, as defined in 802.11-2007 standard
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac.cc ns-3.38/src/wifi/model/wifi-mac.cc
--- ns-3.37/src/wifi/model/wifi-mac.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac.cc	2023-03-16 20:54:23.868869300 -0700
@@ -31,8 +31,8 @@
 #include "wifi-net-device.h"
 
 #include "ns3/eht-configuration.h"
+#include "ns3/eht-frame-exchange-manager.h"
 #include "ns3/he-configuration.h"
-#include "ns3/he-frame-exchange-manager.h"
 #include "ns3/ht-configuration.h"
 #include "ns3/log.h"
 #include "ns3/packet.h"
@@ -295,7 +295,7 @@
                             TypeId::OBSOLETE,
                             "Use the AckedMpdu trace instead.")
             .AddTraceSource("TxErrHeader",
-                            "The header of unsuccessfully transmitted packet.",
+                            "The header of unsuccessfuly transmitted packet.",
                             MakeTraceSourceAccessor(&WifiMac::m_txErrCallback),
                             "ns3::WifiMacHeader::TracedCallback",
                             TypeId::OBSOLETE,
@@ -320,8 +320,9 @@
                 "An MPDU whose response was not received before the timeout, along with "
                 "an identifier of the type of timeout (see WifiTxTimer::Reason) and the "
                 "TXVECTOR used to transmit the MPDU. This trace source is fired when a "
-                "CTS is missing after an RTS or a Normal Ack is missing after an MPDU "
-                "or after a DL MU PPDU acknowledged in SU format.",
+                "CTS is missing after an RTS, when all CTS frames are missing after an MU-RTS, "
+                "or when a Normal Ack is missing after an MPDU or after a DL MU PPDU "
+                "acknowledged in SU format.",
                 MakeTraceSourceAccessor(&WifiMac::m_mpduResponseTimeoutCallback),
                 "ns3::WifiMac::MpduResponseTimeoutCallback")
             .AddTraceSource(
@@ -791,7 +792,11 @@
     NS_ABORT_MSG_IF(standard == WIFI_STANDARD_UNSPECIFIED, "Wifi standard not set");
     Ptr<FrameExchangeManager> feManager;
 
-    if (standard >= WIFI_STANDARD_80211ax)
+    if (standard >= WIFI_STANDARD_80211be)
+    {
+        feManager = CreateObject<EhtFrameExchangeManager>();
+    }
+    else if (standard >= WIFI_STANDARD_80211ax)
     {
         feManager = CreateObject<HeFrameExchangeManager>();
     }
@@ -856,7 +861,7 @@
 {
     NS_LOG_FUNCTION(this);
 
-    NS_ABORT_MSG_UNLESS(m_links.size() == 0 || m_links.size() == stationManagers.size(),
+    NS_ABORT_MSG_UNLESS(m_links.empty() || m_links.size() == stationManagers.size(),
                         "If links have been already created, the number of provided "
                         "Remote Manager objects ("
                             << stationManagers.size()
@@ -922,7 +927,7 @@
     NS_LOG_FUNCTION(this);
     ResetWifiPhys();
 
-    NS_ABORT_MSG_UNLESS(m_links.size() == 0 || m_links.size() == phys.size(),
+    NS_ABORT_MSG_UNLESS(m_links.empty() || m_links.size() == phys.size(),
                         "If links have been already created, the number of provided "
                         "PHY objects ("
                             << phys.size()
@@ -994,16 +999,6 @@
         SetupEdcaQueue(AC_VI);
         SetupEdcaQueue(AC_BE);
         SetupEdcaQueue(AC_BK);
-
-        std::map<AcIndex, Ptr<BlockAckManager>> bamMap;
-        for (const auto& ac : {AC_BE, AC_BK, AC_VI, AC_VO})
-        {
-            bamMap[ac] = GetQosTxop(ac)->GetBaManager();
-        }
-        for (const auto& ac : {AC_BE, AC_BK, AC_VI, AC_VO})
-        {
-            GetQosTxop(ac)->GetBaManager()->SetBlockAckManagerMap(bamMap);
-        }
     }
 }
 
@@ -1113,11 +1108,11 @@
 {
     NS_LOG_FUNCTION(this << *mpdu << linkId);
 
-    const WifiMacHeader* hdr = &mpdu->GetHeader();
-    Ptr<Packet> packet = mpdu->GetPacket()->Copy();
+    const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
     Mac48Address to = hdr->GetAddr1();
     Mac48Address from = hdr->GetAddr2();
-    auto& link = GetLink(SINGLE_LINK_OP_ID);
+    auto myAddr = hdr->IsData() ? Mac48Address::ConvertFrom(GetDevice()->GetAddress())
+                                : GetFrameExchangeManager(linkId)->GetAddress();
 
     // We don't know how to deal with any frame that is not addressed to
     // us (and odds are there is nothing sensible we could do anyway),
@@ -1125,7 +1120,7 @@
     //
     // The derived class may also do some such filtering, but it doesn't
     // hurt to have it here too as a backstop.
-    if (to != GetAddress())
+    if (to != myAddr)
     {
         return;
     }
@@ -1136,7 +1131,9 @@
         // frames to be flying about if we are a QoS STA.
         NS_ASSERT(GetQosSupported());
 
+        auto& link = GetLink(linkId);
         WifiActionHeader actionHdr;
+        Ptr<Packet> packet = mpdu->GetPacket()->Copy();
         packet->RemoveHeader(actionHdr);
 
         switch (actionHdr.GetCategory())
@@ -1171,7 +1168,9 @@
                 // seems a waste given the level of the current model)
                 // and act by locally establishing the agreement on
                 // the appropriate queue.
-                GetQosTxop(respHdr.GetTid())->GotAddBaResponse(&respHdr, from);
+                auto recipientMld = link.stationManager->GetMldAddress(from);
+                auto recipient = (recipientMld ? *recipientMld : from);
+                GetQosTxop(respHdr.GetTid())->GotAddBaResponse(respHdr, recipient);
                 auto htFem = DynamicCast<HtFrameExchangeManager>(link.feManager);
                 if (htFem)
                 {
@@ -1186,26 +1185,25 @@
             case WifiActionHeader::BLOCK_ACK_DELBA: {
                 MgtDelBaHeader delBaHdr;
                 packet->RemoveHeader(delBaHdr);
+                auto recipientMld = link.stationManager->GetMldAddress(from);
+                auto recipient = (recipientMld ? *recipientMld : from);
 
                 if (delBaHdr.IsByOriginator())
                 {
                     // This DELBA frame was sent by the originator, so
                     // this means that an ingoing established
-                    // agreement exists in HtFrameExchangeManager and we need to
+                    // agreement exists in BlockAckManager and we need to
                     // destroy it.
-                    NS_ASSERT(link.feManager);
-                    auto htFem = DynamicCast<HtFrameExchangeManager>(link.feManager);
-                    if (htFem)
-                    {
-                        htFem->DestroyBlockAckAgreement(from, delBaHdr.GetTid());
-                    }
+                    GetQosTxop(delBaHdr.GetTid())
+                        ->GetBaManager()
+                        ->DestroyRecipientAgreement(recipient, delBaHdr.GetTid());
                 }
                 else
                 {
                     // We must have been the originator. We need to
                     // tell the correct queue that the agreement has
                     // been torn down
-                    GetQosTxop(delBaHdr.GetTid())->GotDelBaFrame(&delBaHdr, from);
+                    GetQosTxop(delBaHdr.GetTid())->GotDelBaFrame(&delBaHdr, recipient);
                 }
                 // This frame is now completely dealt with, so we're done.
                 return;
@@ -1234,6 +1232,111 @@
     }
 }
 
+std::optional<Mac48Address>
+WifiMac::GetMldAddress(const Mac48Address& remoteAddr) const
+{
+    for (std::size_t linkId = 0; linkId < m_links.size(); linkId++)
+    {
+        if (auto mldAddress = m_links[linkId]->stationManager->GetMldAddress(remoteAddr))
+        {
+            return *mldAddress;
+        }
+    }
+    return std::nullopt;
+}
+
+Mac48Address
+WifiMac::GetLocalAddress(const Mac48Address& remoteAddr) const
+{
+    for (const auto& link : m_links)
+    {
+        if (auto mldAddress = link->stationManager->GetMldAddress(remoteAddr))
+        {
+            // this is a link setup with remote MLD
+            if (mldAddress != remoteAddr)
+            {
+                // the remote address is the address of a STA affiliated with the remote MLD
+                return link->feManager->GetAddress();
+            }
+            // we have to return our MLD address
+            return m_address;
+        }
+    }
+    // we get here if no ML setup was established between this device and the remote device,
+    // i.e., they are not both multi-link devices
+    if (GetNLinks() == 1)
+    {
+        // this is a single link device
+        return m_address;
+    }
+    // this is an MLD (hence the remote device is single link)
+    return DoGetLocalAddress(remoteAddr);
+}
+
+Mac48Address
+WifiMac::DoGetLocalAddress(const Mac48Address& remoteAddr [[maybe_unused]]) const
+{
+    return m_address;
+}
+
+WifiMac::OriginatorAgreementOptConstRef
+WifiMac::GetBaAgreementEstablishedAsOriginator(Mac48Address recipient, uint8_t tid) const
+{
+    // BA agreements are indexed by the MLD address if ML setup was performed
+    recipient = GetMldAddress(recipient).value_or(recipient);
+
+    auto agreement = GetQosTxop(tid)->GetBaManager()->GetAgreementAsOriginator(recipient, tid);
+    if (!agreement || !agreement->get().IsEstablished())
+    {
+        return std::nullopt;
+    }
+    return agreement;
+}
+
+WifiMac::RecipientAgreementOptConstRef
+WifiMac::GetBaAgreementEstablishedAsRecipient(Mac48Address originator, uint8_t tid) const
+{
+    // BA agreements are indexed by the MLD address if ML setup was performed
+    originator = GetMldAddress(originator).value_or(originator);
+    return GetQosTxop(tid)->GetBaManager()->GetAgreementAsRecipient(originator, tid);
+}
+
+BlockAckType
+WifiMac::GetBaTypeAsOriginator(const Mac48Address& recipient, uint8_t tid) const
+{
+    auto agreement = GetBaAgreementEstablishedAsOriginator(recipient, tid);
+    NS_ABORT_MSG_IF(!agreement,
+                    "No existing Block Ack agreement with " << recipient << " TID: " << +tid);
+    return agreement->get().GetBlockAckType();
+}
+
+BlockAckReqType
+WifiMac::GetBarTypeAsOriginator(const Mac48Address& recipient, uint8_t tid) const
+{
+    auto agreement = GetBaAgreementEstablishedAsOriginator(recipient, tid);
+    NS_ABORT_MSG_IF(!agreement,
+                    "No existing Block Ack agreement with " << recipient << " TID: " << +tid);
+    return agreement->get().GetBlockAckReqType();
+}
+
+BlockAckType
+WifiMac::GetBaTypeAsRecipient(Mac48Address originator, uint8_t tid) const
+{
+    auto agreement = GetBaAgreementEstablishedAsRecipient(originator, tid);
+    NS_ABORT_MSG_IF(!agreement,
+                    "No existing Block Ack agreement with " << originator << " TID: " << +tid);
+    return agreement->get().GetBlockAckType();
+}
+
+BlockAckReqType
+WifiMac::GetBarTypeAsRecipient(Mac48Address originator, uint8_t tid) const
+{
+    auto agreement = GetBaAgreementEstablishedAsRecipient(originator, tid);
+    NS_ABORT_MSG_IF(!agreement,
+                    "No existing Block Ack agreement with " << originator << " TID: " << +tid);
+    return agreement->get().GetBlockAckReqType();
+}
+
 Ptr<HtConfiguration>
 WifiMac::GetHtConfiguration() const
 {
@@ -1283,6 +1386,58 @@
     return bool(GetDevice()->GetEhtConfiguration());
 }
 
+bool
+WifiMac::GetHtSupported(const Mac48Address& address) const
+{
+    for (const auto& link : m_links)
+    {
+        if (link->stationManager->GetHtSupported(address))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool
+WifiMac::GetVhtSupported(const Mac48Address& address) const
+{
+    for (const auto& link : m_links)
+    {
+        if (link->stationManager->GetVhtSupported(address))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool
+WifiMac::GetHeSupported(const Mac48Address& address) const
+{
+    for (const auto& link : m_links)
+    {
+        if (link->stationManager->GetHeSupported(address))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool
+WifiMac::GetEhtSupported(const Mac48Address& address) const
+{
+    for (const auto& link : m_links)
+    {
+        if (link->stationManager->GetEhtSupported(address))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 void
 WifiMac::SetVoBlockAckThreshold(uint8_t threshold)
 {
@@ -1527,7 +1682,7 @@
     {
         channelWidthSet |= 0x01;
     }
-    if ((phy->GetChannelWidth() >= 80) &&
+    if (((phy->GetChannelWidth() >= 80) || GetEhtSupported()) &&
         ((phy->GetPhyBand() == WIFI_PHY_BAND_5GHZ) || (phy->GetPhyBand() == WIFI_PHY_BAND_6GHZ)))
     {
         channelWidthSet |= 0x02;
@@ -1574,7 +1729,90 @@
     NS_LOG_FUNCTION(this << +linkId);
     NS_ASSERT(GetEhtSupported());
     EhtCapabilities capabilities;
-    // TODO: fill in EHT capabilities
+
+    Ptr<WifiPhy> phy = GetLink(linkId).phy;
+
+    // Set Maximum MPDU Length subfield (Reserved when transmitted in 5 GHz or 6 GHz band)
+    if (phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
+    {
+        uint16_t maxAmsduSize =
+            std::max({m_voMaxAmsduSize, m_viMaxAmsduSize, m_beMaxAmsduSize, m_bkMaxAmsduSize});
+        // Table 9-34—Maximum data unit sizes (in octets) and durations (in microseconds)
+        if (maxAmsduSize <= 3839)
+        {
+            capabilities.SetMaxMpduLength(3895);
+        }
+        else if (maxAmsduSize <= 7935)
+        {
+            capabilities.SetMaxMpduLength(7991);
+        }
+        else
+        {
+            capabilities.SetMaxMpduLength(11454);
+        }
+    }
+
+    // Set Maximum A-MPDU Length Exponent Extension subfield
+    uint32_t maxAmpduLength =
+        std::max({m_voMaxAmpduSize, m_viMaxAmpduSize, m_beMaxAmpduSize, m_bkMaxAmpduSize});
+    // round to the next power of two minus one
+    maxAmpduLength = (1UL << static_cast<uint32_t>(std::ceil(std::log2(maxAmpduLength + 1)))) - 1;
+    // The maximum A-MPDU length in EHT capabilities elements ranges from 2^23-1 to 2^24-1
+    capabilities.SetMaxAmpduLength(std::min(std::max(maxAmpduLength, 8388607U), 16777215U));
+
+    // Set the PHY capabilities
+    const bool support4096Qam = phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, 12);
+    capabilities.m_phyCapabilities.supportTx1024And4096QamForRuSmallerThan242Tones =
+        support4096Qam ? 1 : 0;
+    capabilities.m_phyCapabilities.supportRx1024And4096QamForRuSmallerThan242Tones =
+        support4096Qam ? 1 : 0;
+
+    const uint8_t maxTxNss = phy->GetMaxSupportedTxSpatialStreams();
+    const uint8_t maxRxNss = phy->GetMaxSupportedRxSpatialStreams();
+    if (phy->GetChannelWidth() == 20)
+    {
+        for (auto maxMcs : {7, 9, 11, 13})
+        {
+            capabilities.SetSupportedRxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY,
+                maxMcs,
+                phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, maxMcs) ? maxRxNss : 0);
+            capabilities.SetSupportedTxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY,
+                maxMcs,
+                phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, maxMcs) ? maxTxNss : 0);
+        }
+    }
+    else
+    {
+        for (auto maxMcs : {9, 11, 13})
+        {
+            capabilities.SetSupportedRxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ,
+                maxMcs,
+                phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, maxMcs) ? maxRxNss : 0);
+            capabilities.SetSupportedTxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ,
+                maxMcs,
+                phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, maxMcs) ? maxTxNss : 0);
+        }
+    }
+    if (phy->GetChannelWidth() >= 160)
+    {
+        for (auto maxMcs : {9, 11, 13})
+        {
+            capabilities.SetSupportedRxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_160_MHZ,
+                maxMcs,
+                phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, maxMcs) ? maxRxNss : 0);
+            capabilities.SetSupportedTxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_160_MHZ,
+                maxMcs,
+                phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, maxMcs) ? maxTxNss : 0);
+        }
+    }
+    // 320 MHz not supported yet
+
     return capabilities;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac.h ns-3.38/src/wifi/model/wifi-mac.h
--- ns-3.37/src/wifi/model/wifi-mac.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac.h	2023-03-16 20:54:23.868869300 -0700
@@ -51,6 +51,8 @@
 class ChannelAccessManager;
 class ExtendedCapabilities;
 class WifiMacQueueScheduler;
+class OriginatorBlockAckAgreement;
+class RecipientBlockAckAgreement;
 
 /**
  * Enumeration for type of station
@@ -148,6 +150,29 @@
     virtual std::optional<uint8_t> GetLinkIdByAddress(const Mac48Address& address) const;
 
     /**
+     * \param remoteAddr the (MLD or link) address of a remote device
+     * \return the MLD address of the remote device having the given (MLD or link) address, if
+     *         the remote device is an MLD.
+     */
+    std::optional<Mac48Address> GetMldAddress(const Mac48Address& remoteAddr) const;
+
+    /**
+     * Get the local MAC address used to communicate with a remote STA. Specifically:
+     * - If the given remote address is the address of a STA affiliated with a remote MLD
+     * and operating on a setup link, the address of the local STA operating on such a link
+     * is returned.
+     * - If the given remote address is the MLD address of a remote MLD (with which some link
+     * has been setup), the MLD address of this device is returned.
+     * - If this is a single link device, the unique MAC address of this device is returned.
+     * - Otherwise, return the MAC address of the affiliated STA (which must exists) that
+     * can be used to communicate with the remote device.
+     *
+     * \param remoteAddr the MAC address of the remote device
+     * \return the local MAC address used to communicate with the remote device
+     */
+    Mac48Address GetLocalAddress(const Mac48Address& remoteAddr) const;
+
+    /**
      * Accessor for the Txop object
      *
      * \return a smart pointer to Txop
@@ -494,6 +519,27 @@
     bool GetEhtSupported() const;
 
     /**
+     * \param address the (link or MLD) address of a remote station
+     * \return true if the remote station with the given address supports HT
+     */
+    bool GetHtSupported(const Mac48Address& address) const;
+    /**
+     * \param address the (link or MLD) address of a remote station
+     * \return true if the remote station with the given address supports VHT
+     */
+    bool GetVhtSupported(const Mac48Address& address) const;
+    /**
+     * \param address the (link or MLD) address of a remote station
+     * \return true if the remote station with the given address supports HE
+     */
+    bool GetHeSupported(const Mac48Address& address) const;
+    /**
+     * \param address the (link or MLD) address of a remote station
+     * \return true if the remote station with the given address supports EHT
+     */
+    bool GetEhtSupported(const Mac48Address& address) const;
+
+    /**
      * Return the maximum A-MPDU size of the given Access Category.
      *
      * \param ac Access Category index
@@ -508,6 +554,73 @@
      */
     uint16_t GetMaxAmsduSize(AcIndex ac) const;
 
+    /// optional const reference to OriginatorBlockAckAgreement
+    using OriginatorAgreementOptConstRef =
+        std::optional<std::reference_wrapper<const OriginatorBlockAckAgreement>>;
+    /// optional const reference to RecipientBlockAckAgreement
+    using RecipientAgreementOptConstRef =
+        std::optional<std::reference_wrapper<const RecipientBlockAckAgreement>>;
+
+    /**
+     * \param recipient (link or device) MAC address of the recipient
+     * \param tid traffic ID.
+     *
+     * \return the originator block ack agreement, if one has been established
+     *
+     * Checks if an originator block ack agreement is established with station addressed by
+     * <i>recipient</i> for TID <i>tid</i>.
+     */
+    OriginatorAgreementOptConstRef GetBaAgreementEstablishedAsOriginator(Mac48Address recipient,
+                                                                         uint8_t tid) const;
+    /**
+     * \param originator (link or device) MAC address of the originator
+     * \param tid traffic ID.
+     *
+     * \return the recipient block ack agreement, if one has been established
+     *
+     * Checks if a recipient block ack agreement is established with station addressed by
+     * <i>originator</i> for TID <i>tid</i>.
+     */
+    RecipientAgreementOptConstRef GetBaAgreementEstablishedAsRecipient(Mac48Address originator,
+                                                                       uint8_t tid) const;
+
+    /**
+     * \param recipient MAC address
+     * \param tid traffic ID
+     *
+     * \return the type of Block Acks sent by the recipient
+     *
+     * This function returns the type of Block Acks sent by the recipient.
+     */
+    BlockAckType GetBaTypeAsOriginator(const Mac48Address& recipient, uint8_t tid) const;
+    /**
+     * \param recipient MAC address of recipient
+     * \param tid traffic ID
+     *
+     * \return the type of Block Ack Requests sent to the recipient
+     *
+     * This function returns the type of Block Ack Requests sent to the recipient.
+     */
+    BlockAckReqType GetBarTypeAsOriginator(const Mac48Address& recipient, uint8_t tid) const;
+    /**
+     * \param originator MAC address of originator
+     * \param tid traffic ID
+     *
+     * \return the type of Block Acks sent to the originator
+     *
+     * This function returns the type of Block Acks sent to the originator.
+     */
+    BlockAckType GetBaTypeAsRecipient(Mac48Address originator, uint8_t tid) const;
+    /**
+     * \param originator MAC address of originator
+     * \param tid traffic ID
+     *
+     * \return the type of Block Ack Requests sent by the originator
+     *
+     * This function returns the type of Block Ack Requests sent by the originator.
+     */
+    BlockAckReqType GetBarTypeAsRecipient(Mac48Address originator, uint8_t tid) const;
+
   protected:
     void DoInitialize() override;
     void DoDispose() override;
@@ -691,6 +804,17 @@
     virtual std::unique_ptr<LinkEntity> CreateLinkEntity() const;
 
     /**
+     * This method is called if this device is an MLD to determine the MAC address of
+     * the affiliated STA used to communicate with the single link device having the
+     * given MAC address. This method is overridden because its implementation depends
+     * on the type of station.
+     *
+     * \param remoteAddr the MAC address of the remote single link device
+     * \return the MAC address of the affiliated STA used to communicate with the remote device
+     */
+    virtual Mac48Address DoGetLocalAddress(const Mac48Address& remoteAddr) const;
+
+    /**
      * Enable or disable ERP support for the given link.
      *
      * \param enable whether ERP is supported
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-queue.cc ns-3.38/src/wifi/model/wifi-mac-queue.cc
--- ns-3.37/src/wifi/model/wifi-mac-queue.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-queue.cc	2023-03-16 20:54:23.868869300 -0700
@@ -94,6 +94,12 @@
     return mpdu->GetQueueIt(WmqIteratorTag());
 }
 
+Ptr<WifiMpdu>
+WifiMacQueue::GetOriginal(Ptr<WifiMpdu> mpdu)
+{
+    return GetIt(mpdu)->mpdu;
+}
+
 void
 WifiMacQueue::ExtractExpiredMpdus(const WifiContainerQueueId& queueId) const
 {
@@ -105,11 +111,17 @@
     for (auto it = first; it != last; it++)
     {
         mpdus.push_back(it->mpdu);
+    }
+    for (const auto& mpdu : mpdus)
+    {
         // fire the Expired trace
-        m_traceExpired(it->mpdu);
+        Simulator::ScheduleNow(&WifiMacQueue::m_traceExpired, this, mpdu);
     }
     // notify the scheduler
-    m_scheduler->NotifyRemove(m_ac, mpdus);
+    if (!mpdus.empty())
+    {
+        m_scheduler->NotifyRemove(m_ac, mpdus);
+    }
 }
 
 void
@@ -123,11 +135,17 @@
     for (auto it = first; it != last; it++)
     {
         mpdus.push_back(it->mpdu);
+    }
+    for (const auto& mpdu : mpdus)
+    {
         // fire the Expired trace
-        m_traceExpired(it->mpdu);
+        Simulator::ScheduleNow(&WifiMacQueue::m_traceExpired, this, mpdu);
     }
     // notify the scheduler
-    m_scheduler->NotifyRemove(m_ac, mpdus);
+    if (!mpdus.empty())
+    {
+        m_scheduler->NotifyRemove(m_ac, mpdus);
+    }
 }
 
 void
@@ -248,7 +266,7 @@
         {
             auto it = GetIt(mpdu);
             NS_ASSERT(it->ac == m_ac);
-            NS_ASSERT(it->mpdu == mpdu);
+            NS_ASSERT(it->mpdu == mpdu->GetOriginal());
             iterators.emplace_back(it);
         }
     }
@@ -345,11 +363,17 @@
         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())))
+           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())
@@ -374,7 +398,7 @@
     NS_ASSERT(mpdu && mpdu->IsQueued());
     auto it = GetIt(mpdu);
     NS_ASSERT(it->ac == m_ac);
-    NS_ASSERT(it->mpdu == mpdu);
+    NS_ASSERT(it->mpdu == mpdu->GetOriginal());
 
     return DoRemove(it);
 }
@@ -386,7 +410,7 @@
     NS_ASSERT(currentItem->IsQueued());
     auto currentIt = GetIt(currentItem);
     NS_ASSERT(currentIt->ac == m_ac);
-    NS_ASSERT(currentIt->mpdu == currentItem);
+    NS_ASSERT(currentIt->mpdu == currentItem->GetOriginal());
     NS_ASSERT(!newItem->IsQueued());
 
     Time expiryTime = currentIt->expiryTime;
@@ -416,21 +440,29 @@
 {
     NS_LOG_FUNCTION(this << *item);
 
+    auto currSize = GetMaxSize();
+    // control frames should not consume room in the MAC queue, so increase queue size
+    // if we are trying to enqueue a control frame
+    if (item->GetHeader().IsCtl())
+    {
+        SetMaxSize(currSize + item);
+    }
     auto mpdu = m_scheduler->HasToDropBeforeEnqueue(m_ac, item);
 
     if (mpdu == item)
     {
         // the given item must be dropped
+        SetMaxSize(currSize);
         return false;
     }
 
     auto queueId = WifiMacQueueContainer::GetQueueId(item);
-    if (pos != GetContainer().GetQueue(queueId).cend() && pos->mpdu == mpdu)
+    if (pos != GetContainer().GetQueue(queueId).cend() && mpdu && pos->mpdu == mpdu->GetOriginal())
     {
         // the element pointed to by pos must be dropped; update insert position
         pos = std::next(pos);
     }
-    if (mpdu != nullptr)
+    if (mpdu)
     {
         DoRemove(GetIt(mpdu));
     }
@@ -441,13 +473,14 @@
         // set item's information about its position in the queue
         item->SetQueueIt(ret, {});
         ret->ac = m_ac;
-        ret->expiryTime = Simulator::Now() + m_maxDelay;
+        ret->expiryTime = item->GetHeader().IsCtl() ? Time::Max() : Simulator::Now() + m_maxDelay;
         WmqIteratorTag tag;
         ret->deleter = [tag](auto mpdu) { mpdu->SetQueueIt(std::nullopt, tag); };
 
         m_scheduler->NotifyEnqueue(m_ac, item);
         return true;
     }
+    SetMaxSize(currSize);
     return false;
 }
 
@@ -461,9 +494,13 @@
     // First, dequeue all the items
     for (auto& it : iterators)
     {
-        if (auto item = Queue<WifiMpdu, WifiMacQueueContainer>::DoDequeue(it); item)
+        if (auto item = Queue<WifiMpdu, WifiMacQueueContainer>::DoDequeue(it))
         {
             items.push_back(item);
+            if (item->GetHeader().IsCtl())
+            {
+                SetMaxSize(GetMaxSize() - item);
+            }
         }
     }
 
@@ -483,6 +520,10 @@
 
     if (item)
     {
+        if (item->GetHeader().IsCtl())
+        {
+            SetMaxSize(GetMaxSize() - item);
+        }
         m_scheduler->NotifyRemove(m_ac, {item});
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-queue-container.cc ns-3.38/src/wifi/model/wifi-mac-queue-container.cc
--- ns-3.37/src/wifi/model/wifi-mac-queue-container.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-queue-container.cc	2023-03-16 20:54:23.864869400 -0700
@@ -19,6 +19,7 @@
 
 #include "wifi-mac-queue-container.h"
 
+#include "ctrl-headers.h"
 #include "wifi-mpdu.h"
 
 #include "ns3/mac48-address.h"
@@ -42,6 +43,7 @@
 
     NS_ABORT_MSG_UNLESS(pos == m_queues[queueId].cend() || GetQueueId(pos->mpdu) == queueId,
                         "pos iterator does not point to the correct container queue");
+    NS_ABORT_MSG_IF(!item->IsOriginal(), "Only the original copy of an MPDU can be inserted");
 
     auto [it, ret] = m_nBytesPerQueue.insert({queueId, 0});
     it->second += item->GetSize();
@@ -76,11 +78,14 @@
 WifiMacQueueContainer::GetQueueId(Ptr<const WifiMpdu> mpdu)
 {
     const WifiMacHeader& hdr = mpdu->GetHeader();
-    NS_ABORT_IF(hdr.IsCtl());
 
+    if (hdr.IsCtl())
+    {
+        return {WIFI_CTL_QUEUE, hdr.GetAddr2(), std::nullopt};
+    }
     if (hdr.IsMgt())
     {
-        return {WIFI_MGT_QUEUE, hdr.GetAddr2(), WIFI_TID_UNDEFINED};
+        return {WIFI_MGT_QUEUE, hdr.GetAddr2(), std::nullopt};
     }
     if (hdr.IsQosData())
     {
@@ -90,7 +95,7 @@
         }
         return {WIFI_QOSDATA_UNICAST_QUEUE, hdr.GetAddr1(), hdr.GetQosTid()};
     }
-    return {WIFI_DATA_QUEUE, hdr.GetAddr1(), WIFI_TID_UNDEFINED};
+    return {WIFI_DATA_QUEUE, hdr.GetAddr1(), std::nullopt};
 }
 
 const WifiMacQueueContainer::ContainerQueue&
@@ -118,52 +123,73 @@
 std::pair<WifiMacQueueContainer::iterator, WifiMacQueueContainer::iterator>
 WifiMacQueueContainer::DoExtractExpiredMpdus(ContainerQueue& queue) const
 {
+    std::optional<std::pair<WifiMacQueueContainer::iterator, WifiMacQueueContainer::iterator>> ret;
     iterator firstExpiredIt = queue.begin();
     iterator lastExpiredIt = firstExpiredIt;
     Time now = Simulator::Now();
 
-    while (lastExpiredIt != queue.end() && lastExpiredIt->expiryTime <= now)
+    do
     {
-        lastExpiredIt->expired = true;
-        // this MPDU is no longer queued
-        lastExpiredIt->ac = AC_UNDEF;
-        lastExpiredIt->deleter(lastExpiredIt->mpdu);
+        // advance firstExpiredIt and lastExpiredIt to skip all inflight MPDUs
+        for (firstExpiredIt = lastExpiredIt;
+             firstExpiredIt != queue.end() && !firstExpiredIt->inflights.empty();
+             ++firstExpiredIt, ++lastExpiredIt)
+        {
+        }
 
-        WifiContainerQueueId queueId = GetQueueId(lastExpiredIt->mpdu);
-        auto it = m_nBytesPerQueue.find(queueId);
-        NS_ASSERT(it != m_nBytesPerQueue.end());
-        NS_ASSERT(it->second >= lastExpiredIt->mpdu->GetSize());
-        it->second -= lastExpiredIt->mpdu->GetSize();
+        if (!ret)
+        {
+            // we get here in the first iteration only
+            ret = std::make_pair(firstExpiredIt, lastExpiredIt);
+        }
 
-        ++lastExpiredIt;
-    }
+        // advance lastExpiredIt as we encounter MPDUs with expired lifetime that are not inflight
+        while (lastExpiredIt != queue.end() && lastExpiredIt->expiryTime <= now &&
+               lastExpiredIt->inflights.empty())
+        {
+            lastExpiredIt->expired = true;
+            // this MPDU is no longer queued
+            lastExpiredIt->ac = AC_UNDEF;
+            lastExpiredIt->deleter(lastExpiredIt->mpdu);
+
+            WifiContainerQueueId queueId = GetQueueId(lastExpiredIt->mpdu);
+            auto it = m_nBytesPerQueue.find(queueId);
+            NS_ASSERT(it != m_nBytesPerQueue.end());
+            NS_ASSERT(it->second >= lastExpiredIt->mpdu->GetSize());
+            it->second -= lastExpiredIt->mpdu->GetSize();
 
-    if (lastExpiredIt != firstExpiredIt)
-    {
-        // transfer MPDUs with expired lifetime to the tail of m_expiredQueue
-        m_expiredQueue.splice(m_expiredQueue.end(), queue, firstExpiredIt, lastExpiredIt);
-        return {firstExpiredIt, m_expiredQueue.end()};
-    }
+            ++lastExpiredIt;
+        }
+
+        if (lastExpiredIt != firstExpiredIt)
+        {
+            // transfer non-inflight MPDUs with expired lifetime to the tail of m_expiredQueue
+            m_expiredQueue.splice(m_expiredQueue.end(), queue, firstExpiredIt, lastExpiredIt);
+            ret->second = m_expiredQueue.end();
+        }
+
+    } while (lastExpiredIt != firstExpiredIt);
 
-    return {m_expiredQueue.end(), m_expiredQueue.end()};
+    return *ret;
 }
 
 std::pair<WifiMacQueueContainer::iterator, WifiMacQueueContainer::iterator>
 WifiMacQueueContainer::ExtractAllExpiredMpdus() const
 {
-    iterator firstExpiredIt = m_expiredQueue.end();
+    std::optional<WifiMacQueueContainer::iterator> firstExpiredIt;
 
     for (auto& queue : m_queues)
     {
         auto [firstIt, lastIt] = DoExtractExpiredMpdus(queue.second);
 
-        if (firstIt != lastIt && firstExpiredIt == m_expiredQueue.end())
+        if (firstIt != lastIt && !firstExpiredIt)
         {
             // this is the first queue with MPDUs with expired lifetime
             firstExpiredIt = firstIt;
         }
     }
-    return {firstExpiredIt, m_expiredQueue.end()};
+    return std::make_pair(firstExpiredIt ? *firstExpiredIt : m_expiredQueue.end(),
+                          m_expiredQueue.end());
 }
 
 std::pair<WifiMacQueueContainer::iterator, WifiMacQueueContainer::iterator>
@@ -182,12 +208,16 @@
 std::hash<ns3::WifiContainerQueueId>::operator()(ns3::WifiContainerQueueId queueId) const
 {
     auto [type, address, tid] = queueId;
+    const std::size_t size = tid.has_value() ? 8 : 7;
 
-    uint8_t buffer[8];
+    uint8_t buffer[size];
     buffer[0] = type;
     address.CopyTo(buffer + 1);
-    buffer[7] = tid;
+    if (tid.has_value())
+    {
+        buffer[7] = *tid;
+    }
 
-    std::string s(buffer, buffer + 8);
+    std::string s(buffer, buffer + size);
     return std::hash<std::string>{}(s);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-queue-container.h ns-3.38/src/wifi/model/wifi-mac-queue-container.h
--- ns-3.37/src/wifi/model/wifi-mac-queue-container.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-queue-container.h	2023-03-16 20:54:23.864869400 -0700
@@ -25,6 +25,7 @@
 #include "ns3/mac48-address.h"
 
 #include <list>
+#include <optional>
 #include <tuple>
 #include <unordered_map>
 
@@ -34,16 +35,22 @@
 /// enumeration of container queue types
 enum WifiContainerQueueType
 {
-    WIFI_MGT_QUEUE = 0,
-    WIFI_QOSDATA_UNICAST_QUEUE = 1,
-    WIFI_QOSDATA_BROADCAST_QUEUE = 2,
-    WIFI_DATA_QUEUE = 3
+    WIFI_CTL_QUEUE = 0,
+    WIFI_MGT_QUEUE = 1,
+    WIFI_QOSDATA_UNICAST_QUEUE = 2,
+    WIFI_QOSDATA_BROADCAST_QUEUE = 3,
+    WIFI_DATA_QUEUE = 4
 };
 
 /**
  * Tuple (queue 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
@@ -59,7 +66,8 @@
  * Transmitter Address (TA) because 11be stations are QoS stations and hence do
  * not send non-QoS Data frames. TID is ignored.
  */
-using WifiContainerQueueId = std::tuple<WifiContainerQueueType, Mac48Address, uint8_t>;
+using WifiContainerQueueId =
+    std::tuple<WifiContainerQueueType, Mac48Address, std::optional<uint8_t>>;
 
 } // namespace ns3
 
@@ -162,7 +170,7 @@
     uint32_t GetNBytes(const WifiContainerQueueId& queueId) const;
 
     /**
-     * Transfer MPDUs with expired lifetime in the container queue identified by
+     * Transfer non-inflight MPDUs with expired lifetime in the container queue identified by
      * the given QueueId to the container queue storing MPDUs with expired lifetime.
      *
      * \param queueId the QueueId identifying the container queue
@@ -171,8 +179,8 @@
      */
     std::pair<iterator, iterator> ExtractExpiredMpdus(const WifiContainerQueueId& queueId) const;
     /**
-     * Transfer MPDUs with expired lifetime in all the container queues to the container
-     * queue storing MPDUs with expired lifetime.
+     * Transfer non-inflight MPDUs with expired lifetime in all the container queues to the
+     * container queue storing MPDUs with expired lifetime.
      *
      * \return the range [first, last) of iterators pointing to the MPDUs transferred
      *         to the container queue storing MPDUs with expired lifetime
@@ -189,7 +197,7 @@
 
   private:
     /**
-     * Transfer MPDUs with expired lifetime in the given container queue to the
+     * Transfer non-inflight MPDUs with expired lifetime in the given container queue to the
      * container queue storing MPDUs with expired lifetime.
      *
      * \param queue the given container queue
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-queue-elem.cc ns-3.38/src/wifi/model/wifi-mac-queue-elem.cc
--- ns-3.37/src/wifi/model/wifi-mac-queue-elem.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-queue-elem.cc	2023-03-16 20:54:23.864869400 -0700
@@ -35,6 +35,7 @@
 WifiMacQueueElem::~WifiMacQueueElem()
 {
     deleter(mpdu);
+    inflights.clear();
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-queue-elem.h ns-3.38/src/wifi/model/wifi-mac-queue-elem.h
--- ns-3.37/src/wifi/model/wifi-mac-queue-elem.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-queue-elem.h	2023-03-16 20:54:23.864869400 -0700
@@ -25,6 +25,8 @@
 #include "ns3/callback.h"
 #include "ns3/nstime.h"
 
+#include <map>
+
 namespace ns3
 {
 
@@ -36,15 +38,24 @@
  *
  * Such elements can be accessed by the WifiMacQueue (via iterators) and
  * by the WifiMpdu itself (via the iterator it stores).
+ *
+ * Data frames transmitted by an 11be MLD must include link addresses as (RA, TA)
+ * which are different than the MLD addresses seen by the upper layer. In order
+ * to keep the original version of the data frame, we create an alias when a data
+ * frame is sent over a link. Aliases are stored in the map of in-flight MPDUs, which
+ * is indexed by the ID of the link over which the alias is in-flight.
+ * For consistency, also data frame transmitted by non-MLDs have an alias, which is
+ * simply a pointer to the original version of the data frame.
  */
 struct WifiMacQueueElem
 {
-    Ptr<WifiMpdu> mpdu;                    ///< MPDU stored by this element
-    Time expiryTime;                       ///< expiry time of the MPDU (set by WifiMacQueue)
-    AcIndex ac;                            ///< the Access Category associated with the queue
-                                           ///< storing this element (set by WifiMacQueue)
-    bool expired;                          ///< whether this MPDU has been marked as expired
-    Callback<void, Ptr<WifiMpdu>> deleter; ///< reset the iterator stored by the MPDU
+    Ptr<WifiMpdu> mpdu;                         ///< MPDU stored by this element
+    Time expiryTime{0};                         ///< expiry time of the MPDU (set by WifiMacQueue)
+    AcIndex ac{AC_UNDEF};                       ///< the Access Category associated with the queue
+                                                ///< storing this element (set by WifiMacQueue)
+    bool expired{false};                        ///< whether this MPDU has been marked as expired
+    std::map<uint8_t, Ptr<WifiMpdu>> inflights; ///< map of MPDUs in-flight on each link
+    Callback<void, Ptr<WifiMpdu>> deleter;      ///< reset the iterator stored by the MPDU
 
     /**
      * Constructor.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-queue.h ns-3.38/src/wifi/model/wifi-mac-queue.h
--- ns-3.37/src/wifi/model/wifi-mac-queue.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-queue.h	2023-03-16 20:54:23.868869300 -0700
@@ -281,6 +281,15 @@
      */
     void WipeAllExpiredMpdus();
 
+    /**
+     * Unlike the GetOriginal() method of WifiMpdu, this method returns a non-const
+     * pointer to the original copy of the given MPDU.
+     *
+     * \param mpdu the given MPDU
+     * \return the original copy of the given MPDU
+     */
+    Ptr<WifiMpdu> GetOriginal(Ptr<WifiMpdu> mpdu);
+
   protected:
     using Queue<WifiMpdu, WifiMacQueueContainer>::GetContainer;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-queue-scheduler-impl.h ns-3.38/src/wifi/model/wifi-mac-queue-scheduler-impl.h
--- ns-3.37/src/wifi/model/wifi-mac-queue-scheduler-impl.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-queue-scheduler-impl.h	2023-03-16 20:54:23.864869400 -0700
@@ -306,12 +306,15 @@
         // 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_QOSDATA_BROADCAST_QUEUE)
+        if (queueType == WIFI_MGT_QUEUE ||
+            (queueType == WIFI_CTL_QUEUE && GetMac() && address != GetMac()->GetAddress()) ||
+            queueType == WIFI_QOSDATA_BROADCAST_QUEUE)
         {
             // these queue types are associated with just one link
             NS_ASSERT(GetMac());
-            auto linkId = GetMac()->GetLinkIdByAddress(std::get<Mac48Address>(queueId));
+            auto linkId = GetMac()->GetLinkIdByAddress(address);
             NS_ASSERT(linkId.has_value());
             queueInfoIt->second.linkIds = {*linkId};
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mac-trailer.h ns-3.38/src/wifi/model/wifi-mac-trailer.h
--- ns-3.37/src/wifi/model/wifi-mac-trailer.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mac-trailer.h	2023-03-16 20:54:23.868869300 -0700
@@ -26,7 +26,7 @@
 {
 
 /**
- * The length in octects of the IEEE 802.11 MAC FCS field
+ * The length in octets of the IEEE 802.11 MAC FCS field
  */
 static const uint16_t WIFI_MAC_FCS_LENGTH = 4;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mpdu.cc ns-3.38/src/wifi/model/wifi-mpdu.cc
--- ns-3.37/src/wifi/model/wifi-mpdu.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mpdu.cc	2023-03-16 20:54:23.868869300 -0700
@@ -36,24 +36,83 @@
 NS_LOG_COMPONENT_DEFINE("WifiMpdu");
 
 WifiMpdu::WifiMpdu(Ptr<const Packet> p, const WifiMacHeader& header)
-    : m_packet(p),
-      m_header(header)
+    : m_header(header)
 {
+    auto& original = std::get<OriginalInfo>(m_instanceInfo);
+    original.m_packet = p;
+
     if (header.IsQosData() && header.IsQosAmsdu())
     {
-        m_msduList = MsduAggregator::Deaggregate(p->Copy());
+        original.m_msduList = MsduAggregator::Deaggregate(p->Copy());
     }
-    m_inFlight = false;
 }
 
 WifiMpdu::~WifiMpdu()
 {
+    // Aliases can be queued (i.e., the original copy is queued) when destroyed
+    NS_ASSERT(std::holds_alternative<Ptr<WifiMpdu>>(m_instanceInfo) || !IsQueued());
+}
+
+bool
+WifiMpdu::IsOriginal() const
+{
+    return std::holds_alternative<OriginalInfo>(m_instanceInfo);
+}
+
+Ptr<const WifiMpdu>
+WifiMpdu::GetOriginal() const
+{
+    if (std::holds_alternative<OriginalInfo>(m_instanceInfo))
+    {
+        return this;
+    }
+    return std::get<ALIAS>(m_instanceInfo);
+}
+
+Ptr<WifiMpdu>
+WifiMpdu::CreateAlias(uint8_t linkId) const
+{
+    NS_LOG_FUNCTION(this << +linkId);
+    NS_ABORT_MSG_IF(!std::holds_alternative<OriginalInfo>(m_instanceInfo),
+                    "This method can only be called on the original version of the MPDU");
+    NS_ABORT_MSG_IF(!IsQueued(),
+                    "This method can only be called if the MPDU is stored in a MAC queue");
+
+    auto alias = Ptr<WifiMpdu>(new WifiMpdu, false);
+
+    alias->m_header = m_header; // copy the MAC header
+    alias->m_instanceInfo = Ptr(const_cast<WifiMpdu*>(this));
+    NS_ASSERT(alias->m_instanceInfo.index() == ALIAS);
+
+    return alias;
+}
+
+WifiMpdu::OriginalInfo&
+WifiMpdu::GetOriginalInfo()
+{
+    if (auto original = std::get_if<ORIGINAL>(&m_instanceInfo))
+    {
+        return *original;
+    }
+    auto& origInstanceInfo = std::get<Ptr<WifiMpdu>>(m_instanceInfo)->m_instanceInfo;
+    return std::get<OriginalInfo>(origInstanceInfo);
+}
+
+const WifiMpdu::OriginalInfo&
+WifiMpdu::GetOriginalInfo() const
+{
+    if (auto original = std::get_if<ORIGINAL>(&m_instanceInfo))
+    {
+        return *original;
+    }
+    const auto& origInstanceInfo = std::get<Ptr<WifiMpdu>>(m_instanceInfo)->m_instanceInfo;
+    return std::get<OriginalInfo>(origInstanceInfo);
 }
 
 Ptr<const Packet>
 WifiMpdu::GetPacket() const
 {
-    return m_packet;
+    return GetOriginalInfo().m_packet;
 }
 
 const WifiMacHeader&
@@ -77,7 +136,7 @@
 uint32_t
 WifiMpdu::GetPacketSize() const
 {
-    return m_packet->GetSize();
+    return GetPacket()->GetSize();
 }
 
 uint32_t
@@ -95,7 +154,7 @@
 Ptr<Packet>
 WifiMpdu::GetProtocolDataUnit() const
 {
-    Ptr<Packet> mpdu = m_packet->Copy();
+    Ptr<Packet> mpdu = GetPacket()->Copy();
     mpdu->AddHeader(m_header);
     AddWifiMacTrailer(mpdu);
     return mpdu;
@@ -108,12 +167,16 @@
     NS_LOG_FUNCTION(this << *msdu);
     NS_ABORT_MSG_IF(!msdu->GetHeader().IsQosData() || msdu->GetHeader().IsQosAmsdu(),
                     "Only QoS data frames that do not contain an A-MSDU can be aggregated");
+    NS_ABORT_MSG_IF(!std::holds_alternative<OriginalInfo>(m_instanceInfo),
+                    "This method can only be called on the original version of the MPDU");
 
-    if (m_msduList.empty())
+    auto& original = std::get<OriginalInfo>(m_instanceInfo);
+
+    if (original.m_msduList.empty())
     {
         // An MSDU is going to be aggregated to this MPDU, hence this has to be an A-MSDU now
         Ptr<const WifiMpdu> firstMsdu = Create<const WifiMpdu>(*this);
-        m_packet = Create<Packet>();
+        original.m_packet = Create<Packet>();
         DoAggregate(firstMsdu);
 
         m_header.SetQosAmsdu();
@@ -159,16 +222,18 @@
                                                          : msdu->GetHeader().GetAddr4()));
     hdr.SetLength(static_cast<uint16_t>(msdu->GetPacket()->GetSize()));
 
-    m_msduList.emplace_back(msdu->GetPacket(), hdr);
+    auto& original = std::get<OriginalInfo>(m_instanceInfo);
+
+    original.m_msduList.emplace_back(msdu->GetPacket(), hdr);
 
     // build the A-MSDU
-    NS_ASSERT(m_packet);
-    Ptr<Packet> amsdu = m_packet->Copy();
+    NS_ASSERT(original.m_packet);
+    Ptr<Packet> amsdu = original.m_packet->Copy();
 
     // pad the previous A-MSDU subframe if the A-MSDU is not empty
-    if (m_packet->GetSize() > 0)
+    if (original.m_packet->GetSize() > 0)
     {
-        uint8_t padding = MsduAggregator::CalculatePadding(m_packet->GetSize());
+        uint8_t padding = MsduAggregator::CalculatePadding(original.m_packet->GetSize());
 
         if (padding)
         {
@@ -180,70 +245,119 @@
     Ptr<Packet> amsduSubframe = msdu->GetPacket()->Copy();
     amsduSubframe->AddHeader(hdr);
     amsdu->AddAtEnd(amsduSubframe);
-    m_packet = amsdu;
+    original.m_packet = amsdu;
 }
 
 bool
 WifiMpdu::IsQueued() const
 {
-    return m_queueIt.has_value();
+    return GetOriginalInfo().m_queueIt.has_value();
 }
 
 void
 WifiMpdu::SetQueueIt(std::optional<Iterator> queueIt, WmqIteratorTag tag)
 {
-    m_queueIt = queueIt;
+    NS_ABORT_MSG_IF(!std::holds_alternative<OriginalInfo>(m_instanceInfo),
+                    "This method can only be called on the original version of the MPDU");
+
+    auto& original = std::get<OriginalInfo>(m_instanceInfo);
+    original.m_queueIt = queueIt;
 }
 
 WifiMpdu::Iterator
 WifiMpdu::GetQueueIt(WmqIteratorTag tag) const
 {
+    return GetQueueIt();
+}
+
+WifiMpdu::Iterator
+WifiMpdu::GetQueueIt() const
+{
     NS_ASSERT(IsQueued());
-    return m_queueIt.value();
+    return GetOriginalInfo().m_queueIt.value();
 }
 
 AcIndex
 WifiMpdu::GetQueueAc() const
 {
-    NS_ASSERT(IsQueued());
-    return (*m_queueIt)->ac;
+    return GetQueueIt()->ac;
 }
 
 Time
 WifiMpdu::GetExpiryTime() const
 {
-    NS_ASSERT(IsQueued());
-    return (*m_queueIt)->expiryTime;
+    return GetQueueIt()->expiryTime;
 }
 
 void
-WifiMpdu::SetInFlight()
+WifiMpdu::SetInFlight(uint8_t linkId) const
 {
-    m_inFlight = true;
+    GetQueueIt()->inflights[linkId] = Ptr(const_cast<WifiMpdu*>(this));
 }
 
 void
-WifiMpdu::ResetInFlight()
+WifiMpdu::ResetInFlight(uint8_t linkId) const
+{
+    GetQueueIt()->inflights.erase(linkId);
+}
+
+std::set<uint8_t>
+WifiMpdu::GetInFlightLinkIds() const
 {
-    m_inFlight = false;
+    if (!IsQueued())
+    {
+        return {};
+    }
+    std::set<uint8_t> linkIds;
+    for (const auto& [linkId, mpdu] : GetQueueIt()->inflights)
+    {
+        linkIds.insert(linkId);
+    }
+    return linkIds;
 }
 
 bool
 WifiMpdu::IsInFlight() const
 {
-    return m_inFlight;
+    return IsQueued() && !GetQueueIt()->inflights.empty();
+}
+
+void
+WifiMpdu::AssignSeqNo(uint16_t seqNo)
+{
+    NS_LOG_FUNCTION(this << seqNo);
+
+    m_header.SetSequenceNumber(seqNo);
+    // if this is an alias, set the sequence number on the original copy, too
+    if (auto originalPtr = std::get_if<ALIAS>(&m_instanceInfo))
+    {
+        (*originalPtr)->m_header.SetSequenceNumber(seqNo);
+    }
+    GetOriginalInfo().m_seqNoAssigned = true;
+}
+
+bool
+WifiMpdu::HasSeqNoAssigned() const
+{
+    return GetOriginalInfo().m_seqNoAssigned;
+}
+
+void
+WifiMpdu::UnassignSeqNo()
+{
+    GetOriginalInfo().m_seqNoAssigned = false;
 }
 
 WifiMpdu::DeaggregatedMsdusCI
 WifiMpdu::begin() const
 {
-    return m_msduList.cbegin();
+    return GetOriginalInfo().m_msduList.cbegin();
 }
 
 WifiMpdu::DeaggregatedMsdusCI
 WifiMpdu::end() const
 {
-    return m_msduList.cend();
+    return GetOriginalInfo().m_msduList.cend();
 }
 
 void
@@ -274,7 +388,7 @@
         os << ", residualLifetime=" << (GetExpiryTime() - Simulator::Now()).As(Time::US)
            << ", inflight=" << IsInFlight();
     }
-    os << ", packet=" << m_packet;
+    os << ", packet=" << GetPacket();
 }
 
 std::ostream&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-mpdu.h ns-3.38/src/wifi/model/wifi-mpdu.h
--- ns-3.37/src/wifi/model/wifi-mpdu.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-mpdu.h	2023-03-16 20:54:23.868869300 -0700
@@ -31,6 +31,8 @@
 
 #include <list>
 #include <optional>
+#include <set>
+#include <variant>
 
 namespace ns3
 {
@@ -50,8 +52,10 @@
 /**
  * \ingroup wifi
  *
- * WifiMpdu stores (const) packets along with their Wifi MAC headers
- * and the time when they were enqueued.
+ * WifiMpdu stores a (const) packet along with a MAC header. To support 802.11be
+ * Multi-Link Operation (MLO), a WifiMpdu variant, referred to as WifiMpdu alias,
+ * is added. A WifiMpdu alias stores its own MAC header and a pointer to the original
+ * copy of the WifiMpdu.
  */
 class WifiMpdu : public SimpleRefCount<WifiMpdu>
 {
@@ -66,6 +70,16 @@
     virtual ~WifiMpdu();
 
     /**
+     * \return whether this is the original version of the MPDU
+     */
+    bool IsOriginal() const;
+
+    /**
+     * \return the original version of this MPDU
+     */
+    Ptr<const WifiMpdu> GetOriginal() const;
+
+    /**
      * \brief Get the packet stored in this item
      * \return the packet stored in this item.
      */
@@ -182,21 +196,53 @@
     Ptr<Packet> GetProtocolDataUnit() const;
 
     /**
-     * Mark this MPDU as being in flight (only used if Block Ack agreement established).
+     * Mark this MPDU as being in flight on the given link.
+     *
+     * \param linkId the ID of the given link
      */
-    void SetInFlight();
+    void SetInFlight(uint8_t linkId) const;
     /**
-     * Mark this MPDU as not being in flight (only used if Block Ack agreement established).
+     * Mark this MPDU as not being in flight on the given link.
+     *
+     * \param linkId the ID of the given link
      */
-    void ResetInFlight();
+    void ResetInFlight(uint8_t linkId) const;
     /**
-     * Return true if this MPDU is in flight, false otherwise.
-     *
-     * \return true if this MPDU is in flight, false otherwise
+     * \return the set of IDs of the links on which this MPDU is currently in flight
+     */
+    std::set<uint8_t> GetInFlightLinkIds() const;
+    /**
+     * \return true if this MPDU is in flight on any link, false otherwise
      */
     bool IsInFlight() const;
 
     /**
+     * Set the sequence number of this MPDU (and of the original copy, if this is an alias)
+     * to the given value. Also, record that a sequence number has been assigned to this MPDU.
+     *
+     * \param seqNo the given sequence number
+     */
+    void AssignSeqNo(uint16_t seqNo);
+    /**
+     * \return whether a sequence number has been assigned to this MPDU
+     */
+    bool HasSeqNoAssigned() const;
+    /**
+     * Record that a sequence number is no (longer) assigned to this MPDU.
+     */
+    void UnassignSeqNo();
+
+    /**
+     * Create an alias for this MPDU (which must be an original copy) for transmission
+     * on the link with the given ID. Aliases have their own copy of the MAC header and
+     * cannot be used to perform non-const operations on the frame body.
+     *
+     * \param linkId the ID of the given link
+     * \return an alias for this MPDU
+     */
+    Ptr<WifiMpdu> CreateAlias(uint8_t linkId) const;
+
+    /**
      * \brief Print the item contents.
      * \param os output stream in which the data should be printed.
      */
@@ -211,11 +257,48 @@
      */
     void DoAggregate(Ptr<const WifiMpdu> msdu);
 
-    Ptr<const Packet> m_packet;        //!< The packet (MSDU or A-MSDU) contained in this queue item
-    WifiMacHeader m_header;            //!< Wifi MAC header associated with the packet
-    DeaggregatedMsdus m_msduList;      //!< The list of aggregated MSDUs included in this MPDU
-    std::optional<Iterator> m_queueIt; //!< Queue iterator pointing to this MPDU, if queued
-    bool m_inFlight;                   //!< whether the MPDU is in flight
+    /**
+     * \return the queue iterator stored by this object
+     */
+    Iterator GetQueueIt() const;
+
+    /**
+     * Private default constructor (used to construct aliases).
+     */
+    WifiMpdu() = default;
+
+    /**
+     * Information stored by both the original copy and the aliases
+     */
+    WifiMacHeader m_header; //!< Wifi MAC header associated with the packet
+
+    /**
+     * Information stored by the original copy only.
+     */
+    struct OriginalInfo
+    {
+        Ptr<const Packet> m_packet;        //!< MSDU or A-MSDU contained in this queue item
+        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
+    };
+
+    /**
+     * \return a reference to the information held by the original copy of the MPDU.
+     */
+    OriginalInfo& GetOriginalInfo();
+    /**
+     * \return a const reference to the information held by the original copy of the MPDU.
+     */
+    const OriginalInfo& GetOriginalInfo() const;
+
+    /// Information stored by the original copy and an alias, respectively
+    using InstanceInfo = std::variant<OriginalInfo, Ptr<WifiMpdu>>;
+
+    InstanceInfo m_instanceInfo; //!< information associated with the instance type
+    static constexpr std::size_t ORIGINAL =
+        0;                                  //!< index of original copy in the InstanceInfo variant
+    static constexpr std::size_t ALIAS = 1; //!< index of an alias in the InstanceInfo variant
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-net-device.cc ns-3.38/src/wifi/model/wifi-net-device.cc
--- ns-3.37/src/wifi/model/wifi-net-device.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-net-device.cc	2023-03-16 20:54:23.868869300 -0700
@@ -19,7 +19,8 @@
 
 #include "wifi-net-device.h"
 
-#include "wifi-mac.h"
+#include "frame-exchange-manager.h"
+#include "sta-wifi-mac.h"
 #include "wifi-phy.h"
 
 #include "ns3/channel.h"
@@ -60,7 +61,8 @@
                           MakePointerAccessor(&WifiNetDevice::GetChannel),
                           MakePointerChecker<Channel>(),
                           TypeId::DEPRECATED,
-                          "Use the Channel attribute of WifiPhy")
+                          "class WifiNetDevice; use the Channel "
+                          "attribute of WifiPhy")
             .AddAttribute("Phy",
                           "The PHY layer attached to this device.",
                           PointerValue(),
@@ -93,7 +95,7 @@
                           ObjectVectorValue(),
                           MakeObjectVectorAccessor(&WifiNetDevice::GetRemoteStationManager,
                                                    &WifiNetDevice::GetNRemoteStationManagers),
-                          MakeObjectVectorChecker<WifiPhy>())
+                          MakeObjectVectorChecker<WifiRemoteStationManager>())
             .AddAttribute("HtConfiguration",
                           "The HtConfiguration object.",
                           PointerValue(),
@@ -374,6 +376,30 @@
 Address
 WifiNetDevice::GetAddress() const
 {
+    Ptr<StaWifiMac> staMac;
+    std::set<uint8_t> linkIds;
+
+    /**
+     * Normally, the MAC address that the network device has to advertise to upper layers is
+     * the MLD address, if this device is an MLD, or the unique MAC address, otherwise.
+     * Advertising the MAC address returned by WifiMac::GetAddress() is therefore the right
+     * thing to do in both cases. However, there is an exception: if this device is a non-AP MLD
+     * associated with a single link AP (hence, no ML setup was done), we need to advertise the
+     * MAC address of the link used to communicate with the AP. In fact, if we advertised the
+     * MLD address, the AP could not forward a frame to us because it would not recognize our
+     * MLD address as the MAC address of an associated station.
+     */
+
+    // Handle the exception first
+    if (m_mac->GetTypeOfStation() == STA &&
+        (staMac = StaticCast<StaWifiMac>(m_mac))->IsAssociated() && m_mac->GetNLinks() > 1 &&
+        (linkIds = staMac->GetSetupLinkIds()).size() == 1 &&
+        !GetRemoteStationManager(*linkIds.begin())
+             ->GetMldAddress(m_mac->GetBssid(*linkIds.begin())))
+    {
+        return m_mac->GetFrameExchangeManager(*linkIds.begin())->GetAddress();
+    }
+
     return m_mac->GetAddress();
 }
 
@@ -504,7 +530,7 @@
     {
         type = NetDevice::PACKET_MULTICAST;
     }
-    else if (to == m_mac->GetAddress())
+    else if (to == GetAddress())
     {
         type = NetDevice::PACKET_HOST;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy.cc ns-3.38/src/wifi/model/wifi-phy.cc
--- ns-3.37/src/wifi/model/wifi-phy.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy.cc	2023-03-16 20:54:23.868869300 -0700
@@ -364,6 +364,31 @@
 }
 
 void
+WifiPhy::DoInitialize()
+{
+    NS_LOG_FUNCTION(this);
+
+    // This method ensures that the local mobility model pointer holds
+    // a pointer to the Node's aggregated mobility model (if one exists)
+    // in the case that the user has not directly called SetMobility()
+    // on this WifiPhy during simulation setup.  If the mobility model
+    // needs to be added or changed during simulation runtime, users must
+    // call SetMobility() on this object.
+
+    if (!m_mobility)
+    {
+        NS_ABORT_MSG_UNLESS(m_device && m_device->GetNode(),
+                            "Either install a MobilityModel on this object or ensure that this "
+                            "object is part of a Node and NetDevice");
+        m_mobility = m_device->GetNode()->GetObject<MobilityModel>();
+        if (!m_mobility)
+        {
+            NS_LOG_WARN("Mobility not found, propagation models might not work properly");
+        }
+    }
+}
+
+void
 WifiPhy::DoDispose()
 {
     NS_LOG_FUNCTION(this);
@@ -592,14 +617,7 @@
 Ptr<MobilityModel>
 WifiPhy::GetMobility() const
 {
-    if (m_mobility)
-    {
-        return m_mobility;
-    }
-    else
-    {
-        return m_device->GetNode()->GetObject<MobilityModel>();
-    }
+    return m_mobility;
 }
 
 void
@@ -677,7 +695,8 @@
 WifiPhy::GetStaticPhyEntity(WifiModulationClass modulation)
 {
     const auto it = GetStaticPhyEntities().find(modulation);
-    NS_ABORT_MSG_IF(it == GetStaticPhyEntities().end(), "Unimplemented Wi-Fi modulation class");
+    NS_ABORT_MSG_IF(it == GetStaticPhyEntities().cend(),
+                    "Unimplemented Wi-Fi modulation class " << modulation);
     return it->second;
 }
 
@@ -685,7 +704,8 @@
 WifiPhy::GetPhyEntity(WifiModulationClass modulation) const
 {
     const auto it = m_phyEntities.find(modulation);
-    NS_ABORT_MSG_IF(it == m_phyEntities.end(), "Unsupported Wi-Fi modulation class " << modulation);
+    NS_ABORT_MSG_IF(it == m_phyEntities.cend(),
+                    "Unsupported Wi-Fi modulation class " << modulation);
     return it->second;
 }
 
@@ -695,6 +715,32 @@
     return GetPhyEntity(GetModulationClassForStandard(standard));
 }
 
+Ptr<PhyEntity>
+WifiPhy::GetLatestPhyEntity() const
+{
+    return GetPhyEntity(m_standard);
+}
+
+Ptr<PhyEntity>
+WifiPhy::GetPhyEntityForPpdu(const Ptr<const WifiPpdu> ppdu) const
+{
+    NS_ABORT_IF(!ppdu);
+    const auto modulation = ppdu->GetModulation();
+    if (modulation > m_phyEntities.rbegin()->first)
+    {
+        // unsupported modulation: start reception process with latest PHY entity
+        return GetLatestPhyEntity();
+    }
+    if (modulation < WIFI_MOD_CLASS_HT)
+    {
+        // for non-HT (duplicate), call the latest PHY entity since some extra processing can be
+        // done in PHYs implemented in HT and later (e.g. channel width selection for non-HT
+        // duplicates)
+        return GetLatestPhyEntity();
+    }
+    return GetPhyEntity(modulation);
+}
+
 void
 WifiPhy::AddStaticPhyEntity(WifiModulationClass modulation, Ptr<PhyEntity> phyEntity)
 {
@@ -1000,6 +1046,18 @@
     return m_fixedPhyBand;
 }
 
+uint16_t
+WifiPhy::GetTxBandwidth(WifiMode mode, uint16_t maxAllowedWidth) const
+{
+    auto modulation = mode.GetModulationClass();
+    if (modulation == WIFI_MOD_CLASS_DSSS || modulation == WIFI_MOD_CLASS_HR_DSSS)
+    {
+        return 22;
+    }
+
+    return std::min({GetChannelWidth(), GetMaximumChannelWidth(modulation), maxAllowedWidth});
+}
+
 void
 WifiPhy::SetOperatingChannel(const ChannelTuple& channelTuple)
 {
@@ -1727,7 +1785,8 @@
     m_endTxEvent =
         Simulator::Schedule(txDuration, &WifiPhy::NotifyTxEnd, this, psdus); // TODO: fix for MU
 
-    StartTx(ppdu, txVector);
+    StartTx(ppdu);
+    ppdu->ResetTxVector();
 
     m_channelAccessRequested = false;
     m_powerRestricted = false;
@@ -1759,7 +1818,8 @@
                               RxPowerWattPerChannelBand& rxPowersW,
                               Time rxDuration)
 {
-    WifiModulationClass modulation = ppdu->GetTxVector().GetModulationClass();
+    NS_LOG_FUNCTION(this << ppdu << rxDuration);
+    WifiModulationClass modulation = ppdu->GetModulation();
     auto it = m_phyEntities.find(modulation);
     if (it != m_phyEntities.end())
     {
@@ -1998,14 +2058,14 @@
 WifiPhy::SwitchMaybeToCcaBusy(const Ptr<const WifiPpdu> ppdu)
 {
     NS_LOG_FUNCTION(this);
-    GetPhyEntity(m_standard)->SwitchMaybeToCcaBusy(ppdu);
+    GetLatestPhyEntity()->SwitchMaybeToCcaBusy(ppdu);
 }
 
 void
 WifiPhy::NotifyCcaBusy(const Ptr<const WifiPpdu> ppdu, Time duration)
 {
     NS_LOG_FUNCTION(this << duration);
-    GetPhyEntity(m_standard)->NotifyCcaBusy(ppdu, duration, WIFI_CHANLIST_PRIMARY);
+    GetLatestPhyEntity()->NotifyCcaBusy(ppdu, duration, WIFI_CHANLIST_PRIMARY);
 }
 
 void
@@ -2072,7 +2132,7 @@
 WifiPhy::GetTxPowerForTransmission(Ptr<const WifiPpdu> ppdu) const
 {
     NS_LOG_FUNCTION(this << m_powerRestricted << ppdu);
-    const WifiTxVector& txVector = ppdu->GetTxVector();
+    const auto& txVector = ppdu->GetTxVector();
     // Get transmit power before antenna gain
     double txPowerDbm;
     if (!m_powerRestricted)
@@ -2108,7 +2168,7 @@
 WifiPhy::GetAddressedPsduInPpdu(Ptr<const WifiPpdu> ppdu) const
 {
     // TODO: wrapper. See if still needed
-    return GetPhyEntity(ppdu->GetModulation())->GetAddressedPsduInPpdu(ppdu);
+    return GetPhyEntityForPpdu(ppdu)->GetAddressedPsduInPpdu(ppdu);
 }
 
 WifiSpectrumBand
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy-common.cc ns-3.38/src/wifi/model/wifi-phy-common.cc
--- ns-3.37/src/wifi/model/wifi-phy-common.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy-common.cc	2023-03-16 20:54:23.868869300 -0700
@@ -70,38 +70,6 @@
     return gi;
 }
 
-uint16_t
-GetChannelWidthForTransmission(WifiMode mode, uint16_t maxAllowedChannelWidth)
-{
-    WifiModulationClass modulationClass = mode.GetModulationClass();
-    if (maxAllowedChannelWidth > 20 &&
-        (modulationClass == WifiModulationClass::WIFI_MOD_CLASS_OFDM // all non-HT OFDM control and
-                                                                     // management frames
-         || modulationClass ==
-                WifiModulationClass::WIFI_MOD_CLASS_ERP_OFDM)) // special case of beacons at 2.4 GHz
-    {
-        return 20;
-    }
-    // at 2.4 GHz basic rate can be non-ERP DSSS
-    if (modulationClass == WifiModulationClass::WIFI_MOD_CLASS_DSSS ||
-        modulationClass == WifiModulationClass::WIFI_MOD_CLASS_HR_DSSS)
-    {
-        return 22;
-    }
-    return maxAllowedChannelWidth;
-}
-
-uint16_t
-GetChannelWidthForTransmission(WifiMode mode,
-                               uint16_t operatingChannelWidth,
-                               uint16_t maxSupportedChannelWidth)
-{
-    return GetChannelWidthForTransmission(mode,
-                                          (operatingChannelWidth < maxSupportedChannelWidth)
-                                              ? operatingChannelWidth
-                                              : maxSupportedChannelWidth);
-}
-
 WifiPreamble
 GetPreambleForTransmission(WifiModulationClass modulation, bool useShortPreamble)
 {
@@ -145,6 +113,30 @@
     }
 }
 
+WifiModulationClass
+GetModulationClassForPreamble(WifiPreamble preamble)
+{
+    switch (preamble)
+    {
+    case WIFI_PREAMBLE_HT_MF:
+        return WIFI_MOD_CLASS_HT;
+    case WIFI_PREAMBLE_VHT_SU:
+    case WIFI_PREAMBLE_VHT_MU:
+        return WIFI_MOD_CLASS_VHT;
+    case WIFI_PREAMBLE_HE_SU:
+    case WIFI_PREAMBLE_HE_ER_SU:
+    case WIFI_PREAMBLE_HE_MU:
+    case WIFI_PREAMBLE_HE_TB:
+        return WIFI_MOD_CLASS_HE;
+    case WIFI_PREAMBLE_EHT_MU:
+    case WIFI_PREAMBLE_EHT_TB:
+        return WIFI_MOD_CLASS_EHT;
+    default:
+        NS_ABORT_MSG("Unsupported preamble type: " << preamble);
+    }
+    return WIFI_MOD_CLASS_UNKNOWN;
+}
+
 bool
 IsAllowedControlAnswerModulationClass(WifiModulationClass modClassReq,
                                       WifiModulationClass modClassAnswer)
@@ -252,4 +244,34 @@
     return modulationClass;
 }
 
+uint16_t
+GetMaximumChannelWidth(WifiModulationClass modulation)
+{
+    switch (modulation)
+    {
+    case WIFI_MOD_CLASS_DSSS:
+    case WIFI_MOD_CLASS_HR_DSSS:
+        return 22;
+    case WIFI_MOD_CLASS_OFDM:
+    case WIFI_MOD_CLASS_ERP_OFDM:
+        return 20;
+    case WIFI_MOD_CLASS_HT:
+        return 40;
+    case WIFI_MOD_CLASS_VHT:
+    case WIFI_MOD_CLASS_HE:
+        return 160;
+    case WIFI_MOD_CLASS_EHT:
+        return 160; // TODO update when 320 MHz channels are supported
+    default:
+        NS_ABORT_MSG("Unknown modulation class: " << modulation);
+        return 0;
+    }
+}
+
+bool
+IsEht(WifiPreamble preamble)
+{
+    return ((preamble == WIFI_PREAMBLE_EHT_MU) || (preamble == WIFI_PREAMBLE_EHT_TB));
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy-common.h ns-3.38/src/wifi/model/wifi-phy-common.h
--- ns-3.37/src/wifi/model/wifi-phy-common.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy-common.h	2023-03-16 20:54:23.868869300 -0700
@@ -476,29 +476,13 @@
 WifiPreamble GetPreambleForTransmission(WifiModulationClass modulation, bool useShortPreamble);
 
 /**
- * Return the channel width that is allowed based on the selected mode and the given
- * maximum channel width. This is especially useful when using non-HT modes with
- * HT/VHT/HE capable stations (with default width above 20 MHz).
- *
- * \param mode selected WifiMode
- * \param maxAllowedChannelWidth maximum channel width allowed for the transmission
- * \return channel width adapted to the selected mode
- */
-uint16_t GetChannelWidthForTransmission(WifiMode mode, uint16_t maxAllowedChannelWidth);
-/**
- * Return the channel width that is allowed based on the selected mode, the current
- * width of the operating channel and the maximum channel width supported by the
- * receiver. This is especially useful when using non-HT modes with HT/VHT/HE
- * capable stations (with default width above 20 MHz).
- *
- * \param mode selected WifiMode
- * \param operatingChannelWidth operating channel width
- * \param maxSupportedChannelWidth maximum channel width supported by the receiver
- * \return channel width adapted to the selected mode
- */
-uint16_t GetChannelWidthForTransmission(WifiMode mode,
-                                        uint16_t operatingChannelWidth,
-                                        uint16_t maxSupportedChannelWidth);
+ * Return the modulation class corresponding to the given preamble type.
+ * Only preamble types used by HT/VHT/HE/EHT can be passed to this function.
+ *
+ * \param preamble the given preamble type (must be one defined by HT standard or later)
+ * \return the modulation class corresponding to the given preamble type
+ */
+WifiModulationClass GetModulationClassForPreamble(WifiPreamble preamble);
 
 /**
  * Return whether the modulation class of the selected mode for the
@@ -556,6 +540,22 @@
  */
 WifiModulationClass GetModulationClassForStandard(WifiStandard standard);
 
+/**
+ * Get the maximum channel width in MHz allowed for the given modulation class.
+ *
+ * \param modulation the modulation class
+ * \return the maximum channel width in MHz allowed for the given modulation class
+ */
+uint16_t GetMaximumChannelWidth(WifiModulationClass modulation);
+
+/**
+ * Return true if a preamble corresponds to an EHT transmission.
+ *
+ * \param preamble the preamble
+ * \return true if the provided preamble corresponds to an EHT transmission
+ */
+bool IsEht(WifiPreamble preamble);
+
 } // namespace ns3
 
 #endif /* WIFI_PHY_COMMON_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy.h ns-3.38/src/wifi/model/wifi-phy.h
--- ns-3.37/src/wifi/model/wifi-phy.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy.h	2023-03-16 20:54:23.868869300 -0700
@@ -28,6 +28,8 @@
 
 #include "ns3/error-model.h"
 
+#include <limits>
+
 namespace ns3
 {
 
@@ -151,9 +153,8 @@
 
     /**
      * \param ppdu the PPDU to send
-     * \param txVector the TXVECTOR to use to send the PPDU
      */
-    virtual void StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector) = 0;
+    virtual void StartTx(Ptr<const WifiPpdu> ppdu) = 0;
 
     /**
      * Put in sleep mode.
@@ -898,6 +899,18 @@
      */
     uint8_t GetPrimary20Index() const;
     /**
+     * Get the bandwidth for a transmission occurring on the current operating channel and
+     * using the given WifiMode, subject to the constraint that the TX bandwidth cannot exceed
+     * the given maximum allowed value.
+     *
+     * \param mode the given WifiMode
+     * \param maxAllowedBandWidth the maximum allowed TX bandwidth
+     * \return the bandwidth for the transmission
+     */
+    uint16_t GetTxBandwidth(
+        WifiMode mode,
+        uint16_t maxAllowedBandWidth = std::numeric_limits<uint16_t>::max()) const;
+    /**
      * \param antennas the number of antennas on this node.
      */
     void SetNumberOfAntennas(uint8_t antennas);
@@ -1058,6 +1071,19 @@
     static const Ptr<const PhyEntity> GetStaticPhyEntity(WifiModulationClass modulation);
 
     /**
+     * Get the supported PHY entity to use for a received PPDU.
+     * This typically returns the entity corresponding to the modulation class used to transmit the
+     * PPDU. If the modulation class used to transmit the PPDU is not supported by the PHY, the
+     * latest PHY entity corresponding to the configured standard is returned. If the modulation
+     * used to transmit the PPDU is non-HT (duplicate), the latest PHY entity corresponding to the
+     * configured standard is also returned.
+     *
+     * \param ppdu the received PPDU
+     * \return the pointer to the supported PHY entity
+     */
+    Ptr<PhyEntity> GetPhyEntityForPpdu(const Ptr<const WifiPpdu> ppdu) const;
+
+    /**
      * Get the supported PHY entity corresponding to the modulation class.
      *
      * \param modulation the modulation class
@@ -1071,6 +1097,12 @@
      * \return the pointer to the supported PHY entity
      */
     Ptr<PhyEntity> GetPhyEntity(WifiStandard standard) const;
+    /**
+     * Get the latest PHY entity supported by this PHY instance.
+     *
+     * \return the latest PHY entity supported by this PHY instance
+     */
+    Ptr<PhyEntity> GetLatestPhyEntity() const;
 
     /**
      * \return the UID of the previously received PPDU (reset to UINT64_MAX upon transmission)
@@ -1120,6 +1152,7 @@
     virtual WifiSpectrumBand GetBand(uint16_t bandWidth, uint8_t bandIndex = 0);
 
   protected:
+    void DoInitialize() override;
     void DoDispose() override;
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy-operating-channel.cc ns-3.38/src/wifi/model/wifi-phy-operating-channel.cc
--- ns-3.37/src/wifi/model/wifi-phy-operating-channel.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy-operating-channel.cc	2023-03-16 20:54:23.868869300 -0700
@@ -20,6 +20,8 @@
 
 #include "wifi-phy-operating-channel.h"
 
+#include "wifi-phy-common.h"
+
 #include "ns3/assert.h"
 #include "ns3/log.h"
 
@@ -152,118 +154,119 @@
 
     // Now the 6 GHz channels for 802.11ax/be
     // 20 MHz channels
-    {std::make_tuple(1, 5945, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(5, 5965, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(9, 5985, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(13, 6005, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(17, 6025, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(21, 6045, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(25, 6065, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(29, 6085, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(33, 6105, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(37, 6125, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(41, 6145, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(45, 6165, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(49, 6185, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(53, 6205, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(57, 6225, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(61, 6245, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(65, 6265, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(69, 6285, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(73, 6305, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(77, 6325, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(81, 6345, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(85, 6365, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(89, 6385, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(93, 6405, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(97, 6425, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(101, 6445, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(105, 6465, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(109, 6485, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(113, 6505, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(117, 6525, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(121, 6545, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(125, 6565, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(129, 6585, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(133, 6605, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(137, 6625, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(141, 6645, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(145, 6665, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(149, 6685, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(153, 6705, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(157, 6725, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(161, 6745, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(165, 6765, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(169, 6785, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(173, 6805, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(177, 6825, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(181, 6845, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(185, 6865, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(189, 6885, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(193, 6905, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(197, 6925, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(201, 6945, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(205, 6965, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(209, 6985, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(213, 7005, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(217, 7025, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(221, 7045, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(225, 7065, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(229, 7085, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(233, 7105, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(1, 5955, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(5, 5975, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(9, 5995, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(13, 6015, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(17, 6035, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(21, 6055, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(25, 6075, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(29, 6095, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(33, 6115, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(37, 6135, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(41, 6155, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(45, 6175, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(49, 6195, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(53, 6215, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(57, 6235, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(61, 6255, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(65, 6275, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(69, 6295, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(73, 6315, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(77, 6335, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(81, 6355, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(85, 6375, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(89, 6395, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(93, 6415, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(97, 6435, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(101, 6455, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(105, 6475, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(109, 6495, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(113, 6515, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(117, 6535, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(121, 6555, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(125, 6575, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(129, 6595, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(133, 6615, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(137, 6635, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(141, 6655, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(145, 6675, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(149, 6695, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(153, 6715, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(157, 6735, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(161, 6755, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(165, 6775, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(169, 6795, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(173, 6815, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(177, 6835, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(181, 6855, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(185, 6875, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(189, 6895, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(193, 6915, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(197, 6935, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(201, 6955, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(205, 6975, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(209, 6995, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(213, 7015, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(217, 7035, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(221, 7055, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(225, 7075, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(229, 7095, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(233, 7115, 20, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
     // 40 MHz channels
-    {std::make_tuple(3, 5955, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(11, 5995, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(19, 6035, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(27, 6075, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(35, 6115, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(43, 6155, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(51, 6195, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(59, 6235, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(67, 6275, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(75, 6315, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(83, 6355, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(91, 6395, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(99, 6435, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(107, 6475, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(115, 6515, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(123, 6555, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(131, 6595, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(139, 6635, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(147, 6675, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(155, 6715, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(163, 6755, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(171, 6795, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(179, 6835, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(187, 6875, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(195, 6915, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(203, 6955, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(211, 6995, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(219, 7035, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(227, 7075, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(3, 5965, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(11, 6005, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(19, 6045, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(27, 6085, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(35, 6125, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(43, 6165, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(51, 6205, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(59, 6245, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(67, 6285, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(75, 6325, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(83, 6365, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(91, 6405, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(99, 6445, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(107, 6485, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(115, 6525, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(123, 6565, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(131, 6605, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(139, 6645, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(147, 6685, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(155, 6725, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(163, 6765, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(171, 6805, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(179, 6845, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(187, 6885, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(195, 6925, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(203, 6965, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(211, 7005, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(219, 7045, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(227, 7085, 40, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
     // 80 MHz channels
-    {std::make_tuple(7, 5975, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(23, 6055, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(39, 6135, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(55, 6215, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(71, 6295, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(87, 6375, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(103, 6455, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(119, 6535, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(135, 6615, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(151, 6695, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(167, 6775, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(183, 6855, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(199, 6935, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(215, 7015, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(7, 5985, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(23, 6065, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(39, 6145, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(55, 6225, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(71, 6305, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(87, 6385, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(103, 6465, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(119, 6585, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(135, 6625, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(151, 6705, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(167, 6785, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(183, 6865, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(199, 6945, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(215, 7025, 80, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
     // 160 MHz channels
-    {std::make_tuple(15, 6015, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(47, 6175, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(79, 6335, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(111, 6495, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(143, 6655, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(175, 6815, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
-    {std::make_tuple(207, 6975, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)}};
+    {std::make_tuple(15, 6025, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(47, 6185, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(79, 6345, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(111, 6505, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(143, 6665, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(175, 6825, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+    {std::make_tuple(207, 6985, 160, WIFI_PHY_OFDM_CHANNEL, WIFI_PHY_BAND_6GHZ)},
+};
 
 WifiPhyOperatingChannel::WifiPhyOperatingChannel()
     : WifiPhyOperatingChannel(m_frequencyChannels.end())
@@ -381,7 +384,7 @@
         standardIt != wifiStandards.cend() &&
         (std::find(standardIt->second.cbegin(), standardIt->second.cend(), band) ==
              standardIt->second.cend() ||
-         width > GetMaximumChannelWidth(standard)))
+         width > GetMaximumChannelWidth(GetModulationClassForStandard(standard))))
     {
         return m_frequencyChannels.cend();
     }
@@ -509,8 +512,10 @@
                                                  WifiStandard standard) const
 {
     auto frequency = GetPrimaryChannelCenterFrequency(primaryChannelWidth);
+    NS_ASSERT_MSG(IsSet(), "No channel set");
     auto& [chanNumber, centerFreq, channelWidth, channelType, band] = *m_channelIt;
     auto primaryChanIt = FindFirst(0, frequency, primaryChannelWidth, standard, band);
+    NS_ASSERT_MSG(primaryChanIt != m_frequencyChannels.end(), "Primary channel number not found");
     return std::get<0>(*primaryChanIt);
 }
 
@@ -577,4 +582,117 @@
     return secondaryIndices;
 }
 
+std::set<uint8_t>
+WifiPhyOperatingChannel::Get20MHzIndicesCoveringRu(HeRu::RuSpec ru, uint16_t width) const
+{
+    auto ruType = ru.GetRuType();
+
+    NS_ASSERT_MSG(HeRu::GetBandwidth(ruType) <= width,
+                  "No RU of type " << ruType << " is contained in a " << width << " MHz channel");
+    NS_ASSERT_MSG(width <= GetWidth(),
+                  "The given width (" << width << " MHz) exceeds the operational width ("
+                                      << GetWidth() << " MHz)");
+
+    // trivial case: 2x996-tone RU
+    if (ruType == HeRu::RU_2x996_TONE)
+    {
+        return {0, 1, 2, 3, 4, 5, 6, 7};
+    }
+
+    // handle first the special case of center 26-tone RUs
+    if (ruType == HeRu::RU_26_TONE && ru.GetIndex() == 19)
+    {
+        NS_ASSERT_MSG(width >= 80,
+                      "26-tone RU with index 19 is only present in channels of at least 80 MHz");
+        // the center 26-tone RU in an 80 MHz channel is not fully covered by
+        // any 20 MHz channel, but by the two central 20 MHz channels in the 80 MHz channel
+        auto indices = ru.GetPrimary80MHz() ? GetAll20MHzChannelIndicesInPrimary(80)
+                                            : GetAll20MHzChannelIndicesInSecondary(80);
+        indices.erase(indices.begin());
+        indices.erase(std::prev(indices.end()));
+        return indices;
+    }
+
+    auto ruIndex = ru.GetIndex();
+
+    if (ruType == HeRu::RU_26_TONE && ruIndex > 19)
+    {
+        // "ignore" the center 26-tone RU in an 80 MHz channel
+        ruIndex--;
+    }
+
+    // if the RU refers to a 160 MHz channel, we have to update the RU index (which
+    // refers to an 80 MHz channel) if the RU is not in the lower 80 MHz channel
+    if (width == 160)
+    {
+        bool primary80IsLower80 = (m_primary20Index < 4);
+        if (primary80IsLower80 != ru.GetPrimary80MHz())
+        {
+            auto nRusIn80MHz = HeRu::GetNRus(80, ruType);
+            // "ignore" the center 26-tone RU in an 80 MHz channel
+            if (ruType == HeRu::RU_26_TONE)
+            {
+                nRusIn80MHz--;
+            }
+            ruIndex += nRusIn80MHz;
+        }
+    }
+
+    uint8_t n20MHzChannels; // number of 20 MHz channels in the channel covering the RU
+
+    switch (ruType)
+    {
+    case HeRu::RU_26_TONE:
+    case HeRu::RU_52_TONE:
+    case HeRu::RU_106_TONE:
+    case HeRu::RU_242_TONE:
+        n20MHzChannels = 1;
+        break;
+    case HeRu::RU_484_TONE:
+        n20MHzChannels = 2;
+        break;
+    case HeRu::RU_996_TONE:
+        n20MHzChannels = 4;
+        break;
+    default:
+        NS_ABORT_MSG("Unhandled RU type: " << ruType);
+    }
+
+    auto nRusInCoveringChannel = HeRu::GetNRus(n20MHzChannels * 20, ruType);
+    // compute the index (starting at 0) of the covering channel within the given width
+    std::size_t indexOfCoveringChannelInGivenWidth = (ruIndex - 1) / nRusInCoveringChannel;
+
+    // expand the index of the covering channel in the indices of its constituent
+    // 20 MHz channels (within the given width)
+    NS_ASSERT(indexOfCoveringChannelInGivenWidth < 8); // max number of 20 MHz channels
+    std::set<uint8_t> indices({static_cast<uint8_t>(indexOfCoveringChannelInGivenWidth)});
+
+    while (n20MHzChannels > 1)
+    {
+        std::set<uint8_t> updatedIndices;
+        for (const auto& idx : indices)
+        {
+            updatedIndices.insert(idx * 2);
+            updatedIndices.insert(idx * 2 + 1);
+        }
+        indices.swap(updatedIndices);
+        n20MHzChannels /= 2;
+    }
+
+    // finally, add the appropriate offset if width is less than the operational channel width
+    auto offset = GetPrimaryChannelIndex(width) * width / 20;
+
+    if (offset > 0)
+    {
+        std::set<uint8_t> updatedIndices;
+        for (const auto& idx : indices)
+        {
+            updatedIndices.insert(idx + offset);
+        }
+        indices.swap(updatedIndices);
+    }
+
+    return indices;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy-operating-channel.h ns-3.38/src/wifi/model/wifi-phy-operating-channel.h
--- ns-3.37/src/wifi/model/wifi-phy-operating-channel.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy-operating-channel.h	2023-03-16 20:54:23.868869300 -0700
@@ -24,6 +24,8 @@
 #include "wifi-phy-band.h"
 #include "wifi-standards.h"
 
+#include "ns3/he-ru.h"
+
 #include <set>
 #include <tuple>
 
@@ -263,6 +265,16 @@
      */
     uint8_t GetPrimaryChannelNumber(uint16_t primaryChannelWidth, WifiStandard standard) const;
 
+    /**
+     * Get the channel indices of the minimum subset of 20 MHz channels containing the given RU.
+     *
+     * \param ru the given RU
+     * \param width the width in MHz of the channel to which the given RU refers to; normally,
+     *              it is the width in MHz of the PPDU for which the RU is allocated
+     * \return the channel indices of the minimum subset of 20 MHz channels containing the given RU
+     */
+    std::set<uint8_t> Get20MHzIndicesCoveringRu(HeRu::RuSpec ru, uint16_t width) const;
+
   private:
     ConstIterator m_channelIt; //!< const iterator pointing to the configured frequency channel
     uint8_t m_primary20Index;  /**< index of the primary20 channel (0 indicates the 20 MHz
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy-state-helper.cc ns-3.38/src/wifi/model/wifi-phy-state-helper.cc
--- ns-3.37/src/wifi/model/wifi-phy-state-helper.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy-state-helper.cc	2023-03-16 20:54:23.868869300 -0700
@@ -55,7 +55,7 @@
                             MakeTraceSourceAccessor(&WifiPhyStateHelper::m_rxOkTrace),
                             "ns3::WifiPhyStateHelper::RxOkTracedCallback")
             .AddTraceSource("RxError",
-                            "A packet has been received unsuccessfully.",
+                            "A packet has been received unsuccessfuly.",
                             MakeTraceSourceAccessor(&WifiPhyStateHelper::m_rxErrorTrace),
                             "ns3::WifiPhyStateHelper::RxEndErrorTracedCallback")
             .AddTraceSource("Tx",
@@ -487,7 +487,7 @@
                          << std::all_of(statusPerMpdu.begin(), statusPerMpdu.end(), [](bool v) {
                                 return v;
                             })); // returns true if all true
-    NS_ASSERT(statusPerMpdu.size() != 0);
+    NS_ASSERT(!statusPerMpdu.empty());
     if (!m_rxOkTrace.IsEmpty())
     {
         m_rxOkTrace(psdu->GetPacket(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-phy-state-helper.h ns-3.38/src/wifi/model/wifi-phy-state-helper.h
--- ns-3.37/src/wifi/model/wifi-phy-state-helper.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-phy-state-helper.h	2023-03-16 20:54:23.868869300 -0700
@@ -54,9 +54,9 @@
 typedef Callback<void, Ptr<const WifiPsdu>, RxSignalInfo, WifiTxVector, std::vector<bool>>
     RxOkCallback;
 /**
- * Callback if PSDU unsuccessfully received
+ * Callback if PSDU unsuccessfuly received
  *
- * arg1: PSDU received unsuccessfully
+ * arg1: PSDU received unsuccessfuly
  */
 typedef Callback<void, Ptr<const WifiPsdu>> RxErrorCallback;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-ppdu.cc ns-3.38/src/wifi/model/wifi-ppdu.cc
--- ns-3.37/src/wifi/model/wifi-ppdu.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-ppdu.cc	2023-03-16 20:54:23.868869300 -0700
@@ -37,6 +37,10 @@
       m_modulation(txVector.IsValid() ? txVector.GetModulationClass() : WIFI_MOD_CLASS_UNKNOWN),
       m_txCenterFreq(txCenterFreq),
       m_uid(uid),
+      m_txVector(txVector),
+#ifdef NS3_BUILD_PROFILE_DEBUG
+      m_phyHeaders(Create<Packet>()),
+#endif
       m_truncatedTx(false),
       m_txPowerLevel(txVector.GetTxPowerLevel())
 {
@@ -53,6 +57,10 @@
                                       : WIFI_MOD_CLASS_UNKNOWN),
       m_txCenterFreq(txCenterFreq),
       m_uid(uid),
+      m_txVector(txVector),
+#ifdef NS3_BUILD_PROFILE_DEBUG
+      m_phyHeaders(Create<Packet>()),
+#endif
       m_truncatedTx(false),
       m_txPowerLevel(txVector.GetTxPowerLevel()),
       m_txAntennas(txVector.GetNTx())
@@ -70,13 +78,16 @@
     m_psdus.clear();
 }
 
-WifiTxVector
+const WifiTxVector&
 WifiPpdu::GetTxVector() const
 {
-    WifiTxVector txVector = DoGetTxVector();
-    txVector.SetTxPowerLevel(m_txPowerLevel);
-    txVector.SetNTx(m_txAntennas);
-    return txVector;
+    if (!m_txVector.has_value())
+    {
+        m_txVector = DoGetTxVector();
+        m_txVector->SetTxPowerLevel(m_txPowerLevel);
+        m_txVector->SetNTx(m_txAntennas);
+    }
+    return m_txVector.value();
 }
 
 WifiTxVector
@@ -87,6 +98,21 @@
     return WifiTxVector(); // should be overloaded
 }
 
+void
+WifiPpdu::ResetTxVector() const
+{
+    NS_LOG_FUNCTION(this);
+    m_txVector.reset();
+}
+
+void
+WifiPpdu::UpdateTxVector(const WifiTxVector& updatedTxVector) const
+{
+    NS_LOG_FUNCTION(this << updatedTxVector);
+    ResetTxVector();
+    m_txVector = updatedTxVector;
+}
+
 Ptr<const WifiPsdu>
 WifiPpdu::GetPsdu() const
 {
@@ -118,6 +144,12 @@
     return GetTxVector().GetChannelWidth();
 }
 
+uint16_t
+WifiPpdu::GetTxCenterFreq() const
+{
+    return m_txCenterFreq;
+}
+
 bool
 WifiPpdu::DoesOverlapChannel(uint16_t minFreq, uint16_t maxFreq) const
 {
@@ -161,19 +193,6 @@
     return true;
 }
 
-bool
-WifiPpdu::CanBeReceived(uint16_t p20MinFreq, uint16_t p20MaxFreq) const
-{
-    NS_LOG_FUNCTION(this << p20MinFreq << p20MaxFreq);
-    const bool overlap = DoesOverlapChannel(p20MinFreq, p20MaxFreq);
-    if (!overlap)
-    {
-        NS_LOG_INFO("Received PPDU does not overlap with the primary20 channel");
-        return false;
-    }
-    return true;
-}
-
 uint64_t
 WifiPpdu::GetUid() const
 {
@@ -227,7 +246,7 @@
 {
     NS_FATAL_ERROR("This method should not be called for the base WifiPpdu class. Use the "
                    "overloaded version in the amendment-specific PPDU subclasses instead!");
-    return Create<WifiPpdu>(GetPsdu(), GetTxVector(), m_txCenterFreq);
+    return Ptr<WifiPpdu>(new WifiPpdu(*this), false);
 }
 
 std::ostream&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-ppdu.h ns-3.38/src/wifi/model/wifi-ppdu.h
--- ns-3.37/src/wifi/model/wifi-ppdu.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-ppdu.h	2023-03-16 20:54:23.868869300 -0700
@@ -25,6 +25,7 @@
 #include "ns3/nstime.h"
 
 #include <list>
+#include <optional>
 #include <unordered_map>
 
 /**
@@ -37,6 +38,7 @@
 namespace ns3
 {
 
+class Packet;
 class WifiPsdu;
 
 /**
@@ -87,7 +89,19 @@
      *
      * \return the TXVECTOR of the PPDU.
      */
-    WifiTxVector GetTxVector() const;
+    const WifiTxVector& GetTxVector() const;
+
+    /**
+     * Reset the TXVECTOR.
+     */
+    void ResetTxVector() const;
+
+    /**
+     * Update the TXVECTOR based on some information known at the receiver.
+     *
+     * \param updatedTxVector the updated TXVECTOR.
+     */
+    void UpdateTxVector(const WifiTxVector& updatedTxVector) const;
 
     /**
      * Get the payload of the PPDU.
@@ -122,6 +136,11 @@
     virtual uint16_t GetTransmissionChannelWidth() const;
 
     /**
+     * \return the center frequency (MHz) used for the transmission of this PPDU
+     */
+    uint16_t GetTxCenterFreq() const;
+
+    /**
      * Check whether the given PPDU overlaps a given channel.
      *
      * \param minFreq the minimum frequency (MHz) of the channel
@@ -131,17 +150,6 @@
     bool DoesOverlapChannel(uint16_t minFreq, uint16_t maxFreq) const;
 
     /**
-     * Check whether the given PPDU can be received on the specified primary
-     * channel. Normally, a PPDU can be received if it is transmitted over a
-     * channel that overlaps the primary20 channel of a PHY entity.
-     *
-     * \param p20MinFreq the minimum frequency (MHz) of the primary channel
-     * \param p20MaxFreq the maximum frequency (MHz) of the primary channel
-     * \return true if this PPDU can be received, false otherwise
-     */
-    virtual bool CanBeReceived(uint16_t p20MinFreq, uint16_t p20MaxFreq) const;
-
-    /**
      * Get the modulation used for the PPDU.
      * \return the modulation used for the PPDU
      */
@@ -195,6 +203,13 @@
     WifiConstPsduMap m_psdus;         //!< the PSDUs contained in this PPDU
     uint16_t m_txCenterFreq; //!< the center frequency (MHz) used for the transmission of this PPDU
     uint64_t m_uid;          //!< the unique ID of this PPDU
+    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
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-protection.cc ns-3.38/src/wifi/model/wifi-protection.cc
--- ns-3.37/src/wifi/model/wifi-protection.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-protection.cc	2023-03-16 20:54:23.868869300 -0700
@@ -102,6 +102,27 @@
     os << "CTS_TO_SELF";
 }
 
+/*
+ * WifiMuRtsCtsProtection
+ */
+
+WifiMuRtsCtsProtection::WifiMuRtsCtsProtection()
+    : WifiProtection(MU_RTS_CTS)
+{
+}
+
+std::unique_ptr<WifiProtection>
+WifiMuRtsCtsProtection::Copy() const
+{
+    return std::unique_ptr<WifiProtection>(new WifiMuRtsCtsProtection(*this));
+}
+
+void
+WifiMuRtsCtsProtection::Print(std::ostream& os) const
+{
+    os << "MU_RTS_CTS";
+}
+
 std::ostream&
 operator<<(std::ostream& os, const WifiProtection* protection)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-protection.h ns-3.38/src/wifi/model/wifi-protection.h
--- ns-3.37/src/wifi/model/wifi-protection.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-protection.h	2023-03-16 20:54:23.868869300 -0700
@@ -47,7 +47,8 @@
     {
         NONE = 0,
         RTS_CTS,
-        CTS_TO_SELF
+        CTS_TO_SELF,
+        MU_RTS_CTS
     };
 
     /**
@@ -118,6 +119,23 @@
 };
 
 /**
+ * \ingroup wifi
+ *
+ * WifiMuRtsCtsProtection specifies that MU-RTS/CTS protection method is used.
+ */
+struct WifiMuRtsCtsProtection : public WifiProtection
+{
+    WifiMuRtsCtsProtection();
+
+    // Overridden from WifiProtection
+    std::unique_ptr<WifiProtection> Copy() const override;
+    void Print(std::ostream& os) const override;
+
+    CtrlTriggerHeader muRts;    //!< MU-RTS
+    WifiTxVector muRtsTxVector; //!< MU-RTS TXVECTOR
+};
+
+/**
  * \brief Stream insertion operator.
  *
  * \param os the output stream
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-protection-manager.cc ns-3.38/src/wifi/model/wifi-protection-manager.cc
--- ns-3.37/src/wifi/model/wifi-protection-manager.cc	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-protection-manager.cc	2023-03-16 20:54:23.868869300 -0700
@@ -19,7 +19,8 @@
 
 #include "wifi-protection-manager.h"
 
-#include "wifi-mac.h"
+#include "ap-wifi-mac.h"
+#include "wifi-phy.h"
 
 #include "ns3/log.h"
 
@@ -77,4 +78,45 @@
     m_linkId = linkId;
 }
 
+void
+WifiProtectionManager::AddUserInfoToMuRts(CtrlTriggerHeader& muRts,
+                                          uint16_t txWidth,
+                                          const Mac48Address& receiver) const
+{
+    NS_LOG_FUNCTION(this << muRts << txWidth << receiver);
+
+    CtrlTriggerUserInfoField& ui = muRts.AddUserInfoField();
+
+    NS_ABORT_MSG_IF(m_mac->GetTypeOfStation() != AP, "HE APs only can send MU-RTS");
+    auto apMac = StaticCast<ApWifiMac>(m_mac);
+    ui.SetAid12(apMac->GetAssociationId(receiver, m_linkId));
+
+    const uint16_t ctsTxWidth =
+        std::min(txWidth, GetWifiRemoteStationManager()->GetChannelWidthSupported(receiver));
+    auto phy = m_mac->GetWifiPhy(m_linkId);
+    std::size_t primaryIdx = phy->GetOperatingChannel().GetPrimaryChannelIndex(ctsTxWidth);
+    if (phy->GetChannelWidth() == 160 && ctsTxWidth <= 40 && primaryIdx >= 80 / ctsTxWidth)
+    {
+        // the primary80 is in the higher part of the 160 MHz channel
+        primaryIdx -= 80 / ctsTxWidth;
+    }
+    switch (ctsTxWidth)
+    {
+    case 20:
+        ui.SetMuRtsRuAllocation(61 + primaryIdx);
+        break;
+    case 40:
+        ui.SetMuRtsRuAllocation(65 + primaryIdx);
+        break;
+    case 80:
+        ui.SetMuRtsRuAllocation(67);
+        break;
+    case 160:
+        ui.SetMuRtsRuAllocation(68);
+        break;
+    default:
+        NS_ABORT_MSG("Unhandled TX width: " << ctsTxWidth << " MHz");
+    }
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-protection-manager.h ns-3.38/src/wifi/model/wifi-protection-manager.h
--- ns-3.37/src/wifi/model/wifi-protection-manager.h	2022-11-01 17:37:24.755396000 -0700
+++ ns-3.38/src/wifi/model/wifi-protection-manager.h	2023-03-16 20:54:23.868869300 -0700
@@ -98,6 +98,21 @@
      */
     Ptr<WifiRemoteStationManager> GetWifiRemoteStationManager() const;
 
+    /**
+     * Add a User Info field to the given MU-RTS Trigger Frame to solicit a CTS from
+     * the station with the given MAC address. The MU-RTS is intended to protect a data
+     * frame having the given TX width. The TX width of the solicited CTS is the minimum
+     * between the TX width of the protected data frame and the maximum width supported
+     * by the solicited station.
+     *
+     * \param muRts the MU-RTS Trigger Frame
+     * \param txWidth the TX width of the protected data frame
+     * \param receiver the MAC address of the solicited station
+     */
+    void AddUserInfoToMuRts(CtrlTriggerHeader& muRts,
+                            uint16_t txWidth,
+                            const Mac48Address& receiver) const;
+
     Ptr<WifiMac> m_mac; //!< MAC which is using this Protection Manager
     uint8_t m_linkId;   //!< ID of the link this Protection Manager is operating on
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-psdu.cc ns-3.38/src/wifi/model/wifi-psdu.cc
--- ns-3.37/src/wifi/model/wifi-psdu.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-psdu.cc	2023-03-16 20:54:23.868869300 -0700
@@ -139,6 +139,16 @@
     return ta;
 }
 
+bool
+WifiPsdu::HasNav() const
+{
+    // When the contents of a received Duration/ID field, treated as an unsigned integer,
+    // are greater than 32 768, the contents are interpreted as appropriate for the frame
+    // type and subtype or ignored if the receiving MAC entity does not have a defined
+    // interpretation for that type and subtype (IEEE 802.11-2016 sec. 10.27.3)
+    return (m_mpduList.at(0)->GetHeader().GetRawDuration() & 0x8000) == 0;
+}
+
 Time
 WifiPsdu::GetDuration() const
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-psdu.h ns-3.38/src/wifi/model/wifi-psdu.h
--- ns-3.37/src/wifi/model/wifi-psdu.h	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-psdu.h	2023-03-16 20:54:23.868869300 -0700
@@ -145,6 +145,11 @@
     Mac48Address GetAddr2() const;
 
     /**
+     * \return true if the Duration/ID field contains a value for setting the NAV
+     */
+    bool HasNav() const;
+
+    /**
      * Get the duration from the Duration/ID field, which is common to all the MPDUs
      * \return the duration from the Duration/ID field.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-remote-station-manager.cc ns-3.38/src/wifi/model/wifi-remote-station-manager.cc
--- ns-3.37/src/wifi/model/wifi-remote-station-manager.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-remote-station-manager.cc	2023-03-16 20:54:23.868869300 -0700
@@ -574,14 +574,13 @@
 {
     auto stateIt = m_states.find(mldAddress);
 
-    if (stateIt == m_states.end())
+    if (stateIt == m_states.end() || !stateIt->second->m_mldAddress)
     {
         // MLD address not found
-        return std::optional<Mac48Address>();
+        return std::nullopt;
     }
 
-    NS_ASSERT(stateIt->second->m_mldAddress.has_value() &&
-              *stateIt->second->m_mldAddress == mldAddress);
+    NS_ASSERT(*stateIt->second->m_mldAddress == mldAddress);
     return stateIt->second->m_address;
 }
 
@@ -598,7 +597,7 @@
         v.SetPreambleType(
             GetPreambleForTransmission(mode.GetModulationClass(), GetShortPreambleEnabled()));
         v.SetTxPowerLevel(m_defaultTxPowerLevel);
-        v.SetChannelWidth(GetChannelWidthForTransmission(mode, allowedWidth));
+        v.SetChannelWidth(m_wifiPhy->GetTxBandwidth(mode, allowedWidth));
         v.SetGuardInterval(ConvertGuardIntervalToNanoSeconds(mode, m_wifiPhy->GetDevice()));
         v.SetNTx(GetNumberOfAntennas());
         v.SetNss(1);
@@ -632,7 +631,7 @@
             }
         }
 
-        txVector.SetChannelWidth(GetChannelWidthForTransmission(mgtMode, channelWidth));
+        txVector.SetChannelWidth(m_wifiPhy->GetTxBandwidth(mgtMode, channelWidth));
         txVector.SetGuardInterval(
             ConvertGuardIntervalToNanoSeconds(mgtMode, m_wifiPhy->GetDevice()));
     }
@@ -691,7 +690,7 @@
                         GetNumberOfAntennas(),
                         1,
                         0,
-                        GetChannelWidthForTransmission(defaultMode, m_wifiPhy->GetChannelWidth()),
+                        m_wifiPhy->GetTxBandwidth(defaultMode),
                         false);
 }
 
@@ -707,7 +706,7 @@
         v.SetPreambleType(
             GetPreambleForTransmission(mode.GetModulationClass(), GetShortPreambleEnabled()));
         v.SetTxPowerLevel(m_defaultTxPowerLevel);
-        v.SetChannelWidth(GetChannelWidthForTransmission(mode, m_wifiPhy->GetChannelWidth()));
+        v.SetChannelWidth(m_wifiPhy->GetTxBandwidth(mode));
         v.SetGuardInterval(ConvertGuardIntervalToNanoSeconds(mode, m_wifiPhy->GetDevice()));
         v.SetNTx(GetNumberOfAntennas());
         v.SetNss(1);
@@ -727,7 +726,7 @@
     v.SetPreambleType(
         GetPreambleForTransmission(ctsMode.GetModulationClass(), GetShortPreambleEnabled()));
     v.SetTxPowerLevel(GetDefaultTxPowerLevel());
-    v.SetChannelWidth(GetChannelWidthForTransmission(ctsMode, m_wifiPhy->GetChannelWidth()));
+    v.SetChannelWidth(m_wifiPhy->GetTxBandwidth(ctsMode));
     uint16_t ctsTxGuardInterval =
         ConvertGuardIntervalToNanoSeconds(ctsMode, m_wifiPhy->GetDevice());
     v.SetGuardInterval(ctsTxGuardInterval);
@@ -745,7 +744,7 @@
     v.SetPreambleType(
         GetPreambleForTransmission(ackMode.GetModulationClass(), GetShortPreambleEnabled()));
     v.SetTxPowerLevel(GetDefaultTxPowerLevel());
-    v.SetChannelWidth(GetChannelWidthForTransmission(ackMode, m_wifiPhy->GetChannelWidth()));
+    v.SetChannelWidth(m_wifiPhy->GetTxBandwidth(ackMode));
     uint16_t ackTxGuardInterval =
         ConvertGuardIntervalToNanoSeconds(ackMode, m_wifiPhy->GetDevice());
     v.SetGuardInterval(ackTxGuardInterval);
@@ -764,7 +763,7 @@
     v.SetPreambleType(
         GetPreambleForTransmission(blockAckMode.GetModulationClass(), GetShortPreambleEnabled()));
     v.SetTxPowerLevel(GetDefaultTxPowerLevel());
-    v.SetChannelWidth(GetChannelWidthForTransmission(blockAckMode, m_wifiPhy->GetChannelWidth()));
+    v.SetChannelWidth(m_wifiPhy->GetTxBandwidth(blockAckMode));
     uint16_t blockAckTxGuardInterval =
         ConvertGuardIntervalToNanoSeconds(blockAckMode, m_wifiPhy->GetDevice());
     v.SetGuardInterval(blockAckTxGuardInterval);
@@ -1339,16 +1338,17 @@
     return LookupState(address)->m_info;
 }
 
-double
+std::optional<double>
 WifiRemoteStationManager::GetMostRecentRssi(Mac48Address address) const
 {
-    auto stationIt = m_stations.find(address);
-    NS_ASSERT_MSG(stationIt != m_stations.end(), "Address: " << address << " not found");
-    auto station = stationIt->second;
+    auto station = Lookup(address);
     auto rssi = station->m_rssiAndUpdateTimePair.first;
     auto ts = station->m_rssiAndUpdateTimePair.second;
-    NS_ASSERT_MSG(ts.IsStrictlyPositive(), "address: " << address << " ts:" << ts);
-    return rssi;
+    if (ts.IsStrictlyPositive())
+    {
+        return rssi;
+    }
+    return std::nullopt;
 }
 
 std::shared_ptr<WifiRemoteStationState>
@@ -1510,15 +1510,11 @@
         // todo: Using 3200ns, default value for HeConfiguration::GuardInterval
         state->m_guardInterval = 3200;
     }
-    for (uint8_t i = 1; i <= m_wifiPhy->GetMaxSupportedTxSpatialStreams(); i++)
+    for (const auto& mcs : m_wifiPhy->GetMcsList(WIFI_MOD_CLASS_HE))
     {
-        for (const auto& mcs : m_wifiPhy->GetMcsList(WIFI_MOD_CLASS_HE))
+        if (heCapabilities.GetHighestMcsSupported() >= mcs.GetMcsValue())
         {
-            if (heCapabilities.GetHighestNssSupported() >= i &&
-                heCapabilities.GetHighestMcsSupported() >= mcs.GetMcsValue())
-            {
-                AddSupportedMcs(from, mcs);
-            }
+            AddSupportedMcs(from, mcs);
         }
     }
     state->m_heCapabilities = Create<const HeCapabilities>(heCapabilities);
@@ -1532,7 +1528,17 @@
     // Used by all stations to record EHT capabilities of remote stations
     NS_LOG_FUNCTION(this << from << ehtCapabilities);
     auto state = LookupState(from);
-    // TODO: to be completed
+    for (const auto& mcs : m_wifiPhy->GetMcsList(WIFI_MOD_CLASS_EHT))
+    {
+        for (uint8_t mapType = 0; mapType < EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_MAX; ++mapType)
+        {
+            if (ehtCapabilities.GetHighestSupportedRxMcs(
+                    static_cast<EhtMcsAndNssSet::EhtMcsMapType>(mapType)) >= mcs.GetMcsValue())
+            {
+                AddSupportedMcs(from, mcs);
+            }
+        }
+    }
     state->m_ehtCapabilities = Create<const EhtCapabilities>(ehtCapabilities);
     SetQosSupport(from, true);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-remote-station-manager.h ns-3.38/src/wifi/model/wifi-remote-station-manager.h
--- ns-3.37/src/wifi/model/wifi-remote-station-manager.h	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-remote-station-manager.h	2023-03-16 20:54:23.868869300 -0700
@@ -878,7 +878,7 @@
      * \param address the address of the receiver
      * \param nSuccessfulMpdus number of successfully transmitted MPDUs
      * A value of 0 means that the Block ACK was missed.
-     * \param nFailedMpdus number of unsuccessfully transmitted MPDUs
+     * \param nFailedMpdus number of unsuccessfuly transmitted MPDUs
      * \param rxSnr received SNR of the block ack frame itself
      * \param dataSnr data SNR reported by remote station
      * \param dataTxVector the TXVECTOR of the MPDUs we sent
@@ -973,7 +973,7 @@
      * to estimate the target UL RSSI info to put in the
      * Trigger frame to send to the remote station.
      */
-    double GetMostRecentRssi(Mac48Address address) const;
+    std::optional<double> GetMostRecentRssi(Mac48Address address) const;
     /**
      * Set the default transmission power level
      *
@@ -1342,7 +1342,7 @@
      * \param station the station that sent the DATA to us
      * \param nSuccessfulMpdus number of successfully transmitted MPDUs.
      *        A value of 0 means that the Block ACK was missed.
-     * \param nFailedMpdus number of unsuccessfully transmitted MPDUs.
+     * \param nFailedMpdus number of unsuccessfuly transmitted MPDUs.
      * \param rxSnr received SNR of the block ack frame itself
      * \param dataSnr data SNR reported by remote station
      * \param dataChannelWidth the channel width (in MHz) of the A-MPDU we sent
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-spectrum-phy-interface.cc ns-3.38/src/wifi/model/wifi-spectrum-phy-interface.cc
--- ns-3.37/src/wifi/model/wifi-spectrum-phy-interface.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-spectrum-phy-interface.cc	2023-03-16 20:54:23.868869300 -0700
@@ -101,7 +101,6 @@
 Ptr<Object>
 WifiSpectrumPhyInterface::GetAntenna() const
 {
-    NS_LOG_FUNCTION(this);
     return m_spectrumWifiPhy->GetAntenna();
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-spectrum-signal-parameters.cc ns-3.38/src/wifi/model/wifi-spectrum-signal-parameters.cc
--- ns-3.37/src/wifi/model/wifi-spectrum-signal-parameters.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-spectrum-signal-parameters.cc	2023-03-16 20:54:23.868869300 -0700
@@ -30,17 +30,19 @@
 NS_LOG_COMPONENT_DEFINE("WifiSpectrumSignalParameters");
 
 WifiSpectrumSignalParameters::WifiSpectrumSignalParameters()
-    : txCenterFreq(0)
+    : SpectrumSignalParameters(),
+      ppdu(nullptr),
+      txWidth(0)
 {
     NS_LOG_FUNCTION(this);
 }
 
 WifiSpectrumSignalParameters::WifiSpectrumSignalParameters(const WifiSpectrumSignalParameters& p)
-    : SpectrumSignalParameters(p)
+    : SpectrumSignalParameters(p),
+      ppdu(p.ppdu),
+      txWidth(p.txWidth)
 {
     NS_LOG_FUNCTION(this << &p);
-    ppdu = p.ppdu;
-    txCenterFreq = p.txCenterFreq;
 }
 
 Ptr<SpectrumSignalParameters>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-spectrum-signal-parameters.h ns-3.38/src/wifi/model/wifi-spectrum-signal-parameters.h
--- ns-3.37/src/wifi/model/wifi-spectrum-signal-parameters.h	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-spectrum-signal-parameters.h	2023-03-16 20:54:23.868869300 -0700
@@ -50,7 +50,7 @@
     WifiSpectrumSignalParameters(const WifiSpectrumSignalParameters& p);
 
     Ptr<const WifiPpdu> ppdu; ///< The PPDU being transmitted
-    uint16_t txCenterFreq;    ///< the center frequency of the transmitted signal in MHz
+    uint16_t txWidth;         ///< The TX width in MHz
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-standards.h ns-3.38/src/wifi/model/wifi-standards.h
--- ns-3.37/src/wifi/model/wifi-standards.h	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-standards.h	2023-03-16 20:54:23.868869300 -0700
@@ -95,7 +95,8 @@
     {WIFI_STANDARD_80211ac, {WIFI_PHY_BAND_5GHZ}},
     {WIFI_STANDARD_80211ad, {WIFI_PHY_BAND_60GHZ}},
     {WIFI_STANDARD_80211ax, {WIFI_PHY_BAND_2_4GHZ, WIFI_PHY_BAND_5GHZ, WIFI_PHY_BAND_6GHZ}},
-    {WIFI_STANDARD_80211be, {WIFI_PHY_BAND_2_4GHZ, WIFI_PHY_BAND_5GHZ, WIFI_PHY_BAND_6GHZ}}};
+    {WIFI_STANDARD_80211be, {WIFI_PHY_BAND_2_4GHZ, WIFI_PHY_BAND_5GHZ, WIFI_PHY_BAND_6GHZ}},
+};
 
 /**
  * \ingroup wifi
@@ -128,38 +129,6 @@
     }
 }
 
-/**
- * Get the maximum channel width in MHz allowed for the given standard.
- *
- * \param standard the standard
- * \return the maximum channel width in MHz allowed for the given standard
- */
-inline uint16_t
-GetMaximumChannelWidth(WifiStandard standard)
-{
-    switch (standard)
-    {
-    case WIFI_STANDARD_80211b:
-        return 22;
-    case WIFI_STANDARD_80211p:
-        return 10;
-    case WIFI_STANDARD_80211a:
-    case WIFI_STANDARD_80211g:
-        return 20;
-    case WIFI_STANDARD_80211n:
-        return 40;
-    case WIFI_STANDARD_80211ac:
-    case WIFI_STANDARD_80211ax:
-    case WIFI_STANDARD_80211be:
-        return 160;
-    case WIFI_STANDARD_80211ad:
-        return 2160;
-    default:
-        NS_ABORT_MSG("Unknown standard: " << standard);
-        return 0;
-    }
-}
-
 /**
  * Get the default channel width for the given PHY standard and band.
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-tx-timer.cc ns-3.38/src/wifi/model/wifi-tx-timer.cc
--- ns-3.37/src/wifi/model/wifi-tx-timer.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-tx-timer.cc	2023-03-16 20:54:23.868869300 -0700
@@ -107,6 +107,7 @@
         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);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-tx-timer.h ns-3.38/src/wifi/model/wifi-tx-timer.h
--- ns-3.37/src/wifi/model/wifi-tx-timer.h	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-tx-timer.h	2023-03-16 20:54:23.868869300 -0700
@@ -58,6 +58,7 @@
         WAIT_CTS,
         WAIT_NORMAL_ACK,
         WAIT_BLOCK_ACK,
+        WAIT_CTS_AFTER_MU_RTS,
         WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU,
         WAIT_BLOCK_ACKS_IN_TB_PPDU,
         WAIT_TB_PPDU_AFTER_BASIC_TF,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-tx-vector.cc ns-3.38/src/wifi/model/wifi-tx-vector.cc
--- ns-3.37/src/wifi/model/wifi-tx-vector.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-tx-vector.cc	2023-03-16 20:54:23.868869300 -0700
@@ -23,6 +23,7 @@
 #include "wifi-phy-common.h"
 
 #include "ns3/abort.h"
+#include "ns3/eht-phy.h"
 
 #include <algorithm>
 #include <iterator>
@@ -31,7 +32,8 @@
 {
 
 WifiTxVector::WifiTxVector()
-    : m_preamble(WIFI_PREAMBLE_LONG),
+    : m_txPowerLevel(1),
+      m_preamble(WIFI_PREAMBLE_LONG),
       m_channelWidth(20),
       m_guardInterval(800),
       m_nTx(1),
@@ -42,9 +44,11 @@
       m_ldpc(false),
       m_bssColor(0),
       m_length(0),
+      m_triggerResponding(false),
       m_modeInitialized(false),
       m_inactiveSubchannels(),
-      m_ruAllocation()
+      m_ruAllocation(),
+      m_ehtPpduType(1) // SU transmission by default
 {
 }
 
@@ -60,7 +64,8 @@
                            bool stbc,
                            bool ldpc,
                            uint8_t bssColor,
-                           uint16_t length)
+                           uint16_t length,
+                           bool triggerResponding)
     : m_mode(mode),
       m_txPowerLevel(powerLevel),
       m_preamble(preamble),
@@ -74,9 +79,11 @@
       m_ldpc(ldpc),
       m_bssColor(bssColor),
       m_length(length),
+      m_triggerResponding(triggerResponding),
       m_modeInitialized(true),
       m_inactiveSubchannels(),
-      m_ruAllocation()
+      m_ruAllocation(),
+      m_ehtPpduType(1) // SU transmission by default
 {
 }
 
@@ -94,10 +101,12 @@
       m_ldpc(txVector.m_ldpc),
       m_bssColor(txVector.m_bssColor),
       m_length(txVector.m_length),
+      m_triggerResponding(txVector.m_triggerResponding),
       m_modeInitialized(txVector.m_modeInitialized),
       m_inactiveSubchannels(txVector.m_inactiveSubchannels),
       m_sigBMcs(txVector.m_sigBMcs),
-      m_ruAllocation(txVector.m_ruAllocation)
+      m_ruAllocation(txVector.m_ruAllocation),
+      m_ehtPpduType(txVector.m_ehtPpduType)
 {
     m_muUserInfos.clear();
     if (!txVector.m_muUserInfos.empty()) // avoids crashing for loop
@@ -127,13 +136,23 @@
     {
         NS_FATAL_ERROR("WifiTxVector mode must be set before using");
     }
-    if (IsMu())
+    if (!IsMu())
     {
-        NS_ABORT_MSG_IF(staId > 2048, "STA-ID should be correctly set for MU (" << staId << ")");
-        NS_ASSERT(m_muUserInfos.find(staId) != m_muUserInfos.end());
-        return m_muUserInfos.at(staId).mcs;
+        return m_mode;
+    }
+    NS_ABORT_MSG_IF(staId > 2048, "STA-ID should be correctly set for MU (" << staId << ")");
+    const auto userInfoIt = m_muUserInfos.find(staId);
+    NS_ASSERT(userInfoIt != m_muUserInfos.cend());
+    switch (GetModulationClassForPreamble(m_preamble))
+    {
+    case WIFI_MOD_CLASS_EHT:
+        return EhtPhy::GetEhtMcs(userInfoIt->second.mcs);
+    case WIFI_MOD_CLASS_HE:
+        return HePhy::GetHeMcs(userInfoIt->second.mcs);
+    default:
+        NS_ABORT_MSG("Unsupported modulation class: " << GetModulationClassForPreamble(m_preamble));
     }
-    return m_mode;
+    return WifiMode(); // invalid WifiMode
 }
 
 WifiModulationClass
@@ -145,7 +164,7 @@
     {
         NS_ASSERT(!m_muUserInfos.empty());
         // all the modes belong to the same modulation class
-        return m_muUserInfos.begin()->second.mcs.GetModulationClass();
+        return GetModulationClassForPreamble(m_preamble);
     }
     return m_mode.GetModulationClass();
 }
@@ -234,6 +253,13 @@
     return m_ldpc;
 }
 
+bool
+WifiTxVector::IsNonHtDuplicate() const
+{
+    return ((m_channelWidth >= 40) && !IsMu() &&
+            (GetMode().GetModulationClass() < WIFI_MOD_CLASS_HT));
+}
+
 void
 WifiTxVector::SetMode(WifiMode mode)
 {
@@ -246,7 +272,7 @@
 {
     NS_ABORT_MSG_IF(!IsMu(), "Not a MU transmission");
     NS_ABORT_MSG_IF(staId > 2048, "STA-ID should be correctly set for MU");
-    m_muUserInfos[staId].mcs = mode;
+    m_muUserInfos[staId].mcs = mode.GetMcsValue();
     m_modeInitialized = true;
 }
 
@@ -342,6 +368,18 @@
     return m_length;
 }
 
+bool
+WifiTxVector::IsTriggerResponding() const
+{
+    return m_triggerResponding;
+}
+
+void
+WifiTxVector::SetTriggerResponding(bool triggerResponding)
+{
+    m_triggerResponding = triggerResponding;
+}
+
 void
 WifiTxVector::SetSigBMode(const WifiMode& mode)
 {
@@ -357,7 +395,7 @@
 void
 WifiTxVector::SetRuAllocation(const RuAllocation& ruAlloc)
 {
-    if (IsDlMu() && !m_muUserInfos.empty())
+    if (ns3::IsDlMu(m_preamble) && !m_muUserInfos.empty())
     {
         NS_ASSERT(ruAlloc == DeriveRuAllocation());
     }
@@ -367,13 +405,26 @@
 const RuAllocation&
 WifiTxVector::GetRuAllocation() const
 {
-    if (IsDlMu() && m_ruAllocation.empty())
+    if (ns3::IsDlMu(m_preamble) && m_ruAllocation.empty())
     {
         m_ruAllocation = DeriveRuAllocation();
     }
     return m_ruAllocation;
 }
 
+void
+WifiTxVector::SetEhtPpduType(uint8_t type)
+{
+    NS_ASSERT(IsEht(m_preamble));
+    m_ehtPpduType = type;
+}
+
+uint8_t
+WifiTxVector::GetEhtPpduType() const
+{
+    return m_ehtPpduType;
+}
+
 bool
 WifiTxVector::IsValid() const
 {
@@ -413,13 +464,13 @@
 bool
 WifiTxVector::IsMu() const
 {
-    return ns3::IsMu(m_preamble);
+    return IsDlMu() || IsUlMu();
 }
 
 bool
 WifiTxVector::IsDlMu() const
 {
-    return ns3::IsDlMu(m_preamble);
+    return ns3::IsDlMu(m_preamble) && !(IsEht(m_preamble) && m_ehtPpduType == 1);
 }
 
 bool
@@ -456,8 +507,6 @@
 {
     NS_ABORT_MSG_IF(!IsMu(), "HE MU user info only available for MU");
     NS_ABORT_MSG_IF(staId > 2048, "STA-ID should be correctly set for MU");
-    NS_ABORT_MSG_IF(userInfo.mcs.GetModulationClass() < WIFI_MOD_CLASS_HE,
-                    "Only HE (or newer) modes authorized for MU");
     m_muUserInfos[staId] = userInfo;
     m_modeInitialized = true;
     m_ruAllocation.clear();
@@ -481,6 +530,11 @@
 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");
@@ -563,13 +617,17 @@
     {
         os << " Length: " << v.GetLength();
     }
+    if (ns3::IsDlMu(v.GetPreambleType()))
+    {
+        os << " SIG-B mode: " << v.GetSigBMode();
+    }
     if (v.IsMu())
     {
         WifiTxVector::HeMuUserInfoMap userInfoMap = v.GetHeMuUserInfoMap();
         os << " num User Infos: " << userInfoMap.size();
         for (auto& ui : userInfoMap)
         {
-            os << ", {STA-ID: " << ui.first << ", " << ui.second.ru << ", MCS: " << ui.second.mcs
+            os << ", {STA-ID: " << ui.first << ", " << ui.second.ru << ", MCS: " << +ui.second.mcs
                << ", Nss: " << +ui.second.nss << "}";
         }
     }
@@ -585,13 +643,17 @@
                   puncturedSubchannels.cend(),
                   std::ostream_iterator<bool>(os, ", "));
     }
+    if (IsEht(v.GetPreambleType()))
+    {
+        os << " EHT PPDU type: " << +v.GetEhtPpduType();
+    }
     return os;
 }
 
 bool
 HeMuUserInfo::operator==(const HeMuUserInfo& other) const
 {
-    return ru == other.ru && mcs.GetMcsValue() == other.mcs.GetMcsValue() && nss == other.nss;
+    return ru == other.ru && mcs == other.mcs && nss == other.nss;
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-tx-vector.h ns-3.38/src/wifi/model/wifi-tx-vector.h
--- ns-3.37/src/wifi/model/wifi-tx-vector.h	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-tx-vector.h	2023-03-16 20:54:23.868869300 -0700
@@ -36,7 +36,7 @@
 struct HeMuUserInfo
 {
     HeRu::RuSpec ru; ///< RU specification
-    WifiMode mcs;    ///< MCS
+    uint8_t mcs;     ///< MCS index
     uint8_t nss;     ///< number of spatial streams
 
     /**
@@ -100,7 +100,7 @@
 class WifiTxVector
 {
   public:
-    /// map of HE MU specific user info paramters indexed by STA-ID
+    /// map of HE MU specific user info parameters indexed by STA-ID
     typedef std::map<uint16_t /* staId */, HeMuUserInfo /* HE MU specific user info */>
         HeMuUserInfoMap;
 
@@ -122,6 +122,7 @@
      * \param ldpc enable or disable LDPC (BCC is used otherwise)
      * \param bssColor the BSS color
      * \param length the LENGTH field of the L-SIG
+     * \param triggerResponding the Trigger Responding parameter
      */
     WifiTxVector(WifiMode mode,
                  uint8_t powerLevel,
@@ -135,7 +136,8 @@
                  bool stbc = false,
                  bool ldpc = false,
                  uint8_t bssColor = 0,
-                 uint16_t length = 0);
+                 uint16_t length = 0,
+                 bool triggerResponding = false);
     /**
      * Copy constructor
      * \param txVector the TXVECTOR to copy
@@ -267,13 +269,13 @@
     /**
      * Checks whether the PSDU contains A-MPDU.
      * \returns true if this PSDU has A-MPDU aggregation,
-     *          false otherwise.
+     *          false otherwise
      */
     bool IsAggregation() const;
     /**
      * Sets if PSDU contains A-MPDU.
      *
-     * \param aggregation whether the PSDU contains A-MPDU or not.
+     * \param aggregation whether the PSDU contains A-MPDU or not
      */
     void SetAggregation(bool aggregation);
     /**
@@ -303,6 +305,12 @@
      */
     void SetLdpc(bool ldpc);
     /**
+     * Checks whether this TXVECTOR corresponds to a non-HT duplicate.
+     * \returns true if this TXVECTOR corresponds to a non-HT duplicate,
+     *          false otherwise.
+     */
+    bool IsNonHtDuplicate() const;
+    /**
      * Set the BSS color
      * \param color the BSS color
      */
@@ -323,6 +331,16 @@
      */
     uint16_t GetLength() const;
     /**
+     * Return true if the Trigger Responding parameter is set to true, false otherwise.
+     * \return true if the Trigger Responding parameter is set to true, false otherwise
+     */
+    bool IsTriggerResponding() const;
+    /**
+     * Set the Trigger Responding parameter to the given value
+     * \param triggerResponding the value for the Trigger Responding parameter
+     */
+    void SetTriggerResponding(bool triggerResponding);
+    /**
      * The standard disallows certain combinations of WifiMode, number of
      * spatial streams, and channel widths.  This method can be used to
      * check whether this WifiTxVector contains an invalid combination.
@@ -331,21 +349,17 @@
      */
     bool IsValid() const;
     /**
-     * Return true if this TX vector is used for a multi-user transmission.
-     *
-     * \return true if this TX vector is used for a multi-user transmission
+     * \return true if this TX vector is used for a multi-user (OFDMA and/or MU-MIMO) transmission
      */
     bool IsMu() const;
     /**
-     * Return true if this TX vector is used for a downlink multi-user transmission.
-     *
-     * \return true if this TX vector is used for a downlink multi-user transmission
+     * \return true if this TX vector is used for a downlink multi-user (OFDMA and/or MU-MIMO)
+     * transmission
      */
     bool IsDlMu() const;
     /**
-     * Return true if this TX vector is used for an uplink multi-user transmission.
-     *
-     * \return true if this TX vector is used for an uplink multi-user transmission
+     * \return true if this TX vector is used for an uplink multi-user (OFDMA and/or MU-MIMO)
+     * transmission
      */
     bool IsUlMu() const;
     /**
@@ -452,6 +466,17 @@
      */
     ContentChannelAllocation GetContentChannelAllocation() const;
 
+    /**
+     * Set the EHT_PPDU_TYPE parameter
+     * \param type the EHT_PPDU_TYPE parameter
+     */
+    void SetEhtPpduType(uint8_t type);
+    /**
+     * Get the EHT_PPDU_TYPE parameter
+     * \return the EHT_PPDU_TYPE parameter
+     */
+    uint8_t GetEhtPpduType() const;
+
   private:
     /**
      * Derive the RU allocation from the TXVECTOR for which its RU allocation has not been set yet.
@@ -478,6 +503,7 @@
     bool m_ldpc;              /**< LDPC FEC coding if true, BCC otherwise*/
     uint8_t m_bssColor;       /**< BSS color */
     uint16_t m_length;        /**< LENGTH field of the L-SIG */
+    bool m_triggerResponding; /**< The Trigger Responding parameter */
 
     bool m_modeInitialized; /**< Internal initialization flag */
 
@@ -493,6 +519,7 @@
 
     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 */
 };
 
 /**
@@ -501,7 +528,7 @@
  * \param os the output stream
  * \param v the WifiTxVector to stringify
  *
- * \return ouput stream
+ * \return output stream
  */
 std::ostream& operator<<(std::ostream& os, const WifiTxVector& v);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/wifi-utils.cc ns-3.38/src/wifi/model/wifi-utils.cc
--- ns-3.37/src/wifi/model/wifi-utils.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/wifi-utils.cc	2023-03-16 20:54:23.868869300 -0700
@@ -88,7 +88,7 @@
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_CTL_TRIGGER);
     CtrlTriggerHeader trigger;
-    trigger.SetType(MU_BAR_TRIGGER);
+    trigger.SetType(TriggerFrameType::MU_BAR_TRIGGER);
     for (auto& t : types)
     {
         auto userInfo = trigger.AddUserInfoField();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/yans-error-rate-model.cc ns-3.38/src/wifi/model/yans-error-rate-model.cc
--- ns-3.37/src/wifi/model/yans-error-rate-model.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/yans-error-rate-model.cc	2023-03-16 20:54:23.868869300 -0700
@@ -385,7 +385,7 @@
                 return GetFecQamBer(snr,
                                     nbits,
                                     txVector.GetChannelWidth() * 1000000, // signal spread
-                                    mode.GetPhyRate(txVector),            // PHY rate
+                                    phyRate,                              // PHY rate
                                     4096,                                 // m
                                     4,                                    // dFree
                                     14,                                   // adFree
@@ -397,7 +397,7 @@
                 return GetFecQamBer(snr,
                                     nbits,
                                     txVector.GetChannelWidth() * 1000000, // signal spread
-                                    mode.GetPhyRate(txVector),            // PHY rate
+                                    phyRate,                              // PHY rate
                                     4096,                                 // m
                                     5,                                    // dFree
                                     8,                                    // adFree
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/yans-wifi-phy.cc ns-3.38/src/wifi/model/yans-wifi-phy.cc
--- ns-3.37/src/wifi/model/yans-wifi-phy.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/yans-wifi-phy.cc	2023-03-16 20:54:23.868869300 -0700
@@ -87,11 +87,11 @@
 }
 
 void
-YansWifiPhy::StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector)
+YansWifiPhy::StartTx(Ptr<const WifiPpdu> ppdu)
 {
     NS_LOG_FUNCTION(this << ppdu);
     NS_LOG_DEBUG("Start transmission: signal power before antenna gain="
-                 << GetPowerDbm(txVector.GetTxPowerLevel()) << "dBm");
+                 << GetPowerDbm(ppdu->GetTxVector().GetTxPowerLevel()) << "dBm");
     m_channel->Send(this, ppdu, GetTxPowerForTransmission(ppdu) + GetTxGain());
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/model/yans-wifi-phy.h ns-3.38/src/wifi/model/yans-wifi-phy.h
--- ns-3.37/src/wifi/model/yans-wifi-phy.h	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/model/yans-wifi-phy.h	2023-03-16 20:54:23.868869300 -0700
@@ -57,7 +57,7 @@
     ~YansWifiPhy() override;
 
     void SetInterferenceHelper(const Ptr<InterferenceHelper> helper) override;
-    void StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector) 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;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/channel-access-manager-test.cc ns-3.38/src/wifi/test/channel-access-manager-test.cc
--- ns-3.37/src/wifi/test/channel-access-manager-test.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/test/channel-access-manager-test.cc	2023-03-16 20:54:23.868869300 -0700
@@ -367,11 +367,11 @@
      * \param ackDelay the delay of the Ack after txEnd
      * \param from the index of the requesting Txop
      */
-    void AddAccessRequestWithSuccessfullAck(uint64_t at,
-                                            uint64_t txTime,
-                                            uint64_t expectedGrantTime,
-                                            uint32_t ackDelay,
-                                            uint32_t from);
+    void AddAccessRequestWithSuccessfulAck(uint64_t at,
+                                           uint64_t txTime,
+                                           uint64_t expectedGrantTime,
+                                           uint32_t ackDelay,
+                                           uint32_t from);
     /**
      * Add access request with successful Ack
      * \param txTime the transmit time
@@ -759,7 +759,7 @@
                                                      uint64_t expectedGrantTime,
                                                      uint32_t from)
 {
-    AddAccessRequestWithSuccessfullAck(at, txTime, expectedGrantTime, 0, from);
+    AddAccessRequestWithSuccessfulAck(at, txTime, expectedGrantTime, 0, from);
 }
 
 template <typename TxopType>
@@ -779,11 +779,11 @@
 
 template <typename TxopType>
 void
-ChannelAccessManagerTest<TxopType>::AddAccessRequestWithSuccessfullAck(uint64_t at,
-                                                                       uint64_t txTime,
-                                                                       uint64_t expectedGrantTime,
-                                                                       uint32_t ackDelay,
-                                                                       uint32_t from)
+ChannelAccessManagerTest<TxopType>::AddAccessRequestWithSuccessfulAck(uint64_t at,
+                                                                      uint64_t txTime,
+                                                                      uint64_t expectedGrantTime,
+                                                                      uint32_t ackDelay,
+                                                                      uint32_t from)
 {
     NS_ASSERT(ackDelay < m_ackTimeoutValue);
     Simulator::Schedule(MicroSeconds(at) - Now(),
@@ -1043,7 +1043,7 @@
     StartTest(4, 6, 10);
     AddTxop(0); // high priority DCF
     AddTxop(2); // low priority DCF
-    AddAccessRequestWithSuccessfullAck(20, 20, 34, 2, 1);
+    AddAccessRequestWithSuccessfulAck(20, 20, 34, 2, 1);
     AddAccessRequest(55, 10, 62, 0);
     EndTest();
 
@@ -1381,7 +1381,7 @@
                         m_cam,
                         ccaBusyDuration,
                         busyChannel,
-                        std::vector<Time>(chWidth / 20, Seconds(0)));
+                        std::vector<Time>(chWidth == 20 ? 0 : chWidth / 20, Seconds(0)));
 
     // During any interval ending within CCA_BUSY period, the idle channel is the
     // primary channel contiguous to the busy secondary channel, if the busy channel
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/examples-to-run.py ns-3.38/src/wifi/test/examples-to-run.py
--- ns-3.37/src/wifi/test/examples-to-run.py	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/test/examples-to-run.py	2023-03-16 20:54:23.872869300 -0700
@@ -463,11 +463,11 @@
     ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
     ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
     ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessfull=0 --expectRxBSuccessfull=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=17 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessfull=0 --expectRxBSuccessfull=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=20 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessfull=0 --expectRxBSuccessfull=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=27 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessfull=0 --expectRxBSuccessfull=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=1 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessfull=0 --expectRxBSuccessfull=1", "True", "False"),
+    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
+    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=17 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
+    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=20 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
+    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=27 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
+    ("wifi-test-interference-helper --enableCapture=1 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=1", "True", "False"),
     ("wifi-bianchi --validate --phyMode=OfdmRate54Mbps --nMinStas=5 --nMaxStas=10 --duration=5", "False", "False"), # TODO: run from N=5 to N=50 for 100s (TAKES_FOREVER) when issue #170 is fixed
     ("wifi-bianchi --validate --phyMode=OfdmRate6Mbps --nMinStas=5 --nMaxStas=10 --duration=15", "True", "False"), # TODO: run from N=5 to N=50 for 400s (TAKES_FOREVER) when issue #170 is fixed
     ("wifi-bianchi --validate --phyMode=OfdmRate54Mbps --nMinStas=5 --nMaxStas=10 --duration=5 --infra", "False", "False"), # TODO: run from N=5 to N=50 for 100s (TAKES_FOREVER) when issue #170 is fixed
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/spectrum-wifi-phy-test.cc ns-3.38/src/wifi/test/spectrum-wifi-phy-test.cc
--- ns-3.37/src/wifi/test/spectrum-wifi-phy-test.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/test/spectrum-wifi-phy-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -147,6 +147,7 @@
     txParams->txPhy = nullptr;
     txParams->duration = txDuration;
     txParams->ppdu = ppdu;
+    txParams->txWidth = CHANNEL_WIDTH;
 
     return txParams;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/tx-duration-test.cc ns-3.38/src/wifi/test/tx-duration-test.cc
--- ns-3.37/src/wifi/test/tx-duration-test.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/test/tx-duration-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -274,6 +274,10 @@
     txVector.SetGuardInterval(guardInterval);
     txVector.SetStbc(0);
     txVector.SetNess(0);
+    if (IsEht(preamble))
+    {
+        txVector.SetEhtPpduType(0);
+    }
     std::list<uint16_t> staIds;
 
     uint16_t staId = 1;
@@ -1236,59 +1240,53 @@
     retval = retval &&
              CheckMuTxDuration(
                  std::list<uint32_t>{1536, 1536},
-                 std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, HePhy::GetHeMcs0(), 1},
-                                         {{HeRu::RU_242_TONE, 2, true}, HePhy::GetHeMcs0(), 1}},
+                 std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, 0, 1},
+                                         {{HeRu::RU_242_TONE, 2, true}, 0, 1}},
                  40,
                  800,
                  WIFI_PREAMBLE_HE_MU,
                  NanoSeconds(
                      1493600)) // equivalent to HE_SU for 20 MHz with 2 extra HE-SIG-B (i.e. 8 us)
-             && CheckMuTxDuration(
-                    std::list<uint32_t>{1536, 1536},
-                    std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, HePhy::GetHeMcs1(), 1},
-                                            {{HeRu::RU_242_TONE, 2, true}, HePhy::GetHeMcs0(), 1}},
-                    40,
-                    800,
-                    WIFI_PREAMBLE_HE_MU,
-                    NanoSeconds(1493600)) // shouldn't change if first PSDU is shorter
-             && CheckMuTxDuration(
-                    std::list<uint32_t>{1536, 76},
-                    std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, HePhy::GetHeMcs0(), 1},
-                                            {{HeRu::RU_242_TONE, 2, true}, HePhy::GetHeMcs0(), 1}},
-                    40,
-                    800,
-                    WIFI_PREAMBLE_HE_MU,
-                    NanoSeconds(1493600));
+             && CheckMuTxDuration(std::list<uint32_t>{1536, 1536},
+                                  std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, 1, 1},
+                                                          {{HeRu::RU_242_TONE, 2, true}, 0, 1}},
+                                  40,
+                                  800,
+                                  WIFI_PREAMBLE_HE_MU,
+                                  NanoSeconds(1493600)) // shouldn't change if first PSDU is shorter
+             && CheckMuTxDuration(std::list<uint32_t>{1536, 76},
+                                  std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, 0, 1},
+                                                          {{HeRu::RU_242_TONE, 2, true}, 0, 1}},
+                                  40,
+                                  800,
+                                  WIFI_PREAMBLE_HE_MU,
+                                  NanoSeconds(1493600));
 
     NS_TEST_EXPECT_MSG_EQ(retval, true, "an 802.11ax MU duration failed");
 
     // 802.11be MU durations
-    retval =
-        retval &&
-        CheckMuTxDuration(
-            std::list<uint32_t>{1536, 1536},
-            std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, EhtPhy::GetEhtMcs0(), 1},
-                                    {{HeRu::RU_242_TONE, 2, true}, EhtPhy::GetEhtMcs0(), 1}},
-            40,
-            800,
-            WIFI_PREAMBLE_EHT_MU,
-            NanoSeconds(1493600)) // equivalent to 802.11ax MU
-        && CheckMuTxDuration(
-               std::list<uint32_t>{1536, 1536},
-               std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, EhtPhy::GetEhtMcs1(), 1},
-                                       {{HeRu::RU_242_TONE, 2, true}, EhtPhy::GetEhtMcs0(), 1}},
-               40,
-               800,
-               WIFI_PREAMBLE_EHT_MU,
-               NanoSeconds(1493600)) // shouldn't change if first PSDU is shorter
-        && CheckMuTxDuration(
-               std::list<uint32_t>{1536, 76},
-               std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, EhtPhy::GetEhtMcs0(), 1},
-                                       {{HeRu::RU_242_TONE, 2, true}, EhtPhy::GetEhtMcs0(), 1}},
-               40,
-               800,
-               WIFI_PREAMBLE_EHT_MU,
-               NanoSeconds(1493600));
+    retval = retval &&
+             CheckMuTxDuration(std::list<uint32_t>{1536, 1536},
+                               std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, 0, 1},
+                                                       {{HeRu::RU_242_TONE, 2, true}, 0, 1}},
+                               40,
+                               800,
+                               WIFI_PREAMBLE_EHT_MU,
+                               NanoSeconds(1493600)) // equivalent to 802.11ax MU
+             && CheckMuTxDuration(std::list<uint32_t>{1536, 1536},
+                                  std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, 1, 1},
+                                                          {{HeRu::RU_242_TONE, 2, true}, 0, 1}},
+                                  40,
+                                  800,
+                                  WIFI_PREAMBLE_EHT_MU,
+                                  NanoSeconds(1493600)) // shouldn't change if first PSDU is shorter
+             && CheckMuTxDuration(std::list<uint32_t>{1536, 76},
+                                  std::list<HeMuUserInfo>{{{HeRu::RU_242_TONE, 1, true}, 0, 1},
+                                                          {{HeRu::RU_242_TONE, 2, true}, 0, 1}},
+                                  40,
+                                  800,
+                                  WIFI_PREAMBLE_EHT_MU,
+                                  NanoSeconds(1493600));
 
     NS_TEST_EXPECT_MSG_EQ(retval, true, "an 802.11be MU duration failed");
 
@@ -1310,7 +1308,7 @@
 
   private:
     /**
-     * Build a TXVECTOR for HE MU with the given bandwith and user informations.
+     * 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
@@ -1355,8 +1353,8 @@
 
     // 20 MHz band
     std::list<HeMuUserInfo> userInfos;
-    userInfos.push_back({{HeRu::RU_106_TONE, 1, true}, HePhy::GetHeMcs11(), 1});
-    userInfos.push_back({{HeRu::RU_106_TONE, 2, true}, HePhy::GetHeMcs10(), 4});
+    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});
@@ -1375,10 +1373,10 @@
                           "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}, HePhy::GetHeMcs4(), 1});
-    userInfos.push_back({{HeRu::RU_52_TONE, 6, true}, HePhy::GetHeMcs6(), 2});
-    userInfos.push_back({{HeRu::RU_52_TONE, 7, true}, HePhy::GetHeMcs5(), 3});
-    userInfos.push_back({{HeRu::RU_52_TONE, 8, true}, HePhy::GetHeMcs6(), 2});
+    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});
@@ -1397,7 +1395,7 @@
                           "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}, HePhy::GetHeMcs3(), 1});
+    userInfos.push_back({{HeRu::RU_26_TONE, 14, true}, 3, 1});
     txVector = BuildTxVector(40, userInfos);
     txVector.SetSigBMode(VhtPhy::GetVhtMcs3());
     txVector.SetRuAllocation({96, 15});
@@ -1416,8 +1414,8 @@
                           "HE-SIG-B should last two OFDM symbols");
 
     // 80 MHz band
-    userInfos.push_back({{HeRu::RU_242_TONE, 3, true}, HePhy::GetHeMcs1(), 1});
-    userInfos.push_back({{HeRu::RU_242_TONE, 4, true}, HePhy::GetHeMcs4(), 1});
+    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});
@@ -1436,7 +1434,7 @@
                           "HE-SIG-B should last four OFDM symbols");
 
     // 160 MHz band
-    userInfos.push_back({{HeRu::RU_996_TONE, 1, false}, HePhy::GetHeMcs1(), 1});
+    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});
@@ -1550,9 +1548,11 @@
     // -> long PPDU format
     txVector.SetPreambleType(WIFI_PREAMBLE_LONG);
     nonHtMode = DsssPhy::GetDsssRate1Mbps();
-    sections = {{WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(144)}, nonHtMode}},
-                {WIFI_PPDU_FIELD_NON_HT_HEADER,
-                 {{ppduStart + MicroSeconds(144), ppduStart + MicroSeconds(192)}, nonHtMode}}};
+    sections = {
+        {WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(144)}, nonHtMode}},
+        {WIFI_PPDU_FIELD_NON_HT_HEADER,
+         {{ppduStart + MicroSeconds(144), ppduStart + MicroSeconds(192)}, nonHtMode}},
+    };
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
 
     // -> long PPDU format if data rate is 1 Mbps (even if preamble is tagged short)
@@ -1563,9 +1563,11 @@
     txVector.SetMode(DsssPhy::GetDsssRate11Mbps());
     nonHtMode = DsssPhy::GetDsssRate2Mbps();
     txVector.SetPreambleType(WIFI_PREAMBLE_SHORT);
-    sections = {{WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(72)}, nonHtMode}},
-                {WIFI_PPDU_FIELD_NON_HT_HEADER,
-                 {{ppduStart + MicroSeconds(72), ppduStart + MicroSeconds(96)}, nonHtMode}}};
+    sections = {
+        {WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(72)}, nonHtMode}},
+        {WIFI_PPDU_FIELD_NON_HT_HEADER,
+         {{ppduStart + MicroSeconds(72), ppduStart + MicroSeconds(96)}, nonHtMode}},
+    };
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
 
     // ==================================================================================
@@ -1577,7 +1579,8 @@
         // number to use to deduce rate and BW info for each variant
         {OFDM_PHY_DEFAULT, 1},
         {OFDM_PHY_10_MHZ, 2},
-        {OFDM_PHY_5_MHZ, 4}};
+        {OFDM_PHY_5_MHZ, 4},
+    };
     for (auto variant : variants)
     {
         phyEntity = Create<OfdmPhy>(variant.first);
@@ -1586,11 +1589,13 @@
         txVector.SetChannelWidth(bw);
         txVector.SetMode(OfdmPhy::GetOfdmRate(12000000 / ratio, bw));
         nonHtMode = OfdmPhy::GetOfdmRate(6000000 / ratio, bw);
-        sections = {{WIFI_PPDU_FIELD_PREAMBLE,
-                     {{ppduStart, ppduStart + MicroSeconds(16 * ratio)}, nonHtMode}},
-                    {WIFI_PPDU_FIELD_NON_HT_HEADER,
-                     {{ppduStart + MicroSeconds(16 * ratio), ppduStart + MicroSeconds(20 * ratio)},
-                      nonHtMode}}};
+        sections = {
+            {WIFI_PPDU_FIELD_PREAMBLE,
+             {{ppduStart, ppduStart + MicroSeconds(16 * ratio)}, nonHtMode}},
+            {WIFI_PPDU_FIELD_NON_HT_HEADER,
+             {{ppduStart + MicroSeconds(16 * ratio), ppduStart + MicroSeconds(20 * ratio)},
+              nonHtMode}},
+        };
         CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
     }
 
@@ -1600,9 +1605,11 @@
     txVector.SetChannelWidth(20);
     txVector.SetMode(ErpOfdmPhy::GetErpOfdmRate(54000000));
     nonHtMode = ErpOfdmPhy::GetErpOfdmRate6Mbps();
-    sections = {{WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(16)}, nonHtMode}},
-                {WIFI_PPDU_FIELD_NON_HT_HEADER,
-                 {{ppduStart + MicroSeconds(16), ppduStart + MicroSeconds(20)}, nonHtMode}}};
+    sections = {
+        {WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(16)}, nonHtMode}},
+        {WIFI_PPDU_FIELD_NON_HT_HEADER,
+         {{ppduStart + MicroSeconds(16), ppduStart + MicroSeconds(20)}, nonHtMode}},
+    };
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
 
     // ==================================================================================
@@ -1625,7 +1632,8 @@
          {{ppduStart + MicroSeconds(20), ppduStart + MicroSeconds(28)}, htSigMode}},
         {WIFI_PPDU_FIELD_TRAINING,
          {{ppduStart + MicroSeconds(28), ppduStart + MicroSeconds(40)}, // 1 HT-STF + 2 HT-LTFs
-          htSigMode}}};
+          htSigMode}},
+    };
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
     txVector.SetChannelWidth(20); // shouldn't have any impact
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
@@ -1659,7 +1667,8 @@
          {{ppduStart + MicroSeconds(20), ppduStart + MicroSeconds(28)}, sigAMode}},
         {WIFI_PPDU_FIELD_TRAINING,
          {{ppduStart + MicroSeconds(28), ppduStart + MicroSeconds(56)}, // 1 VHT-STF + 6 VHT-LTFs
-          sigAMode}}};
+          sigAMode}},
+    };
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
 
     // -> VHT SU format for 7 SS
@@ -1688,24 +1697,25 @@
     txVector.SetNss(2); // HE-LTF duration assumed to be always 8 us for the time being (see note in
                         // HePhy::GetTrainingDuration)
     txVector.SetMode(HePhy::GetHeMcs9());
-    std::map<uint16_t, HeMuUserInfo> userInfoMap = {
-        {1, {{HeRu::RU_106_TONE, 1, true}, HePhy::GetHeMcs4(), 2}},
-        {2, {{HeRu::RU_106_TONE, 1, true}, HePhy::GetHeMcs9(), 1}}};
+    std::map<uint16_t, HeMuUserInfo> userInfoMap = {{1, {{HeRu::RU_106_TONE, 1, true}, 4, 2}},
+                                                    {2, {{HeRu::RU_106_TONE, 1, true}, 9, 1}}};
     sigAMode = HePhy::GetVhtMcs0();
     sigBMode = HePhy::GetVhtMcs4(); // because of first user info map
 
     // -> HE SU format
     txVector.SetPreambleType(WIFI_PREAMBLE_HE_SU);
-    sections = {{WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(16)}, nonHtMode}},
-                {WIFI_PPDU_FIELD_NON_HT_HEADER,
-                 {{ppduStart + MicroSeconds(16), ppduStart + MicroSeconds(24)}, // L-SIG + RL-SIG
-                  nonHtMode}},
-                {WIFI_PPDU_FIELD_SIG_A,
-                 {{ppduStart + MicroSeconds(24), ppduStart + MicroSeconds(32)}, sigAMode}},
-                {WIFI_PPDU_FIELD_TRAINING,
-                 {{ppduStart + MicroSeconds(32),
-                   ppduStart + MicroSeconds(52)}, // 1 HE-STF (@ 4 us) + 2 HE-LTFs (@ 8 us)
-                  sigAMode}}};
+    sections = {
+        {WIFI_PPDU_FIELD_PREAMBLE, {{ppduStart, ppduStart + MicroSeconds(16)}, nonHtMode}},
+        {WIFI_PPDU_FIELD_NON_HT_HEADER,
+         {{ppduStart + MicroSeconds(16), ppduStart + MicroSeconds(24)}, // L-SIG + RL-SIG
+          nonHtMode}},
+        {WIFI_PPDU_FIELD_SIG_A,
+         {{ppduStart + MicroSeconds(24), ppduStart + MicroSeconds(32)}, sigAMode}},
+        {WIFI_PPDU_FIELD_TRAINING,
+         {{ppduStart + MicroSeconds(32),
+           ppduStart + MicroSeconds(52)}, // 1 HE-STF (@ 4 us) + 2 HE-LTFs (@ 8 us)
+          sigAMode}},
+    };
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
 
     // -> HE ER SU format
@@ -1752,15 +1762,15 @@
 
     // ==================================================================================
     // 11be (EHT)
-    sections.erase(WIFI_PPDU_FIELD_SIG_A); // FIXME: do we keep using seperate type for 11be?
-    sections.erase(WIFI_PPDU_FIELD_SIG_B); // FIXME: do we keep using seperate type for 11be?
+    sections.erase(WIFI_PPDU_FIELD_SIG_A); // FIXME: do we keep using separate type for 11be?
+    sections.erase(WIFI_PPDU_FIELD_SIG_B); // FIXME: do we keep using separate type for 11be?
     phyEntity = Create<EhtPhy>();
     txVector.SetChannelWidth(20);
     txVector.SetNss(2); // EHT-LTF duration assumed to be always 8 us for the time being (see note
                         // in HePhy::GetTrainingDuration)
     txVector.SetMode(EhtPhy::GetEhtMcs9());
-    userInfoMap = {{1, {{HeRu::RU_106_TONE, 1, true}, EhtPhy::GetEhtMcs4(), 2}},
-                   {2, {{HeRu::RU_106_TONE, 1, true}, EhtPhy::GetEhtMcs9(), 1}}};
+    userInfoMap = {{1, {{HeRu::RU_106_TONE, 1, true}, 4, 2}},
+                   {2, {{HeRu::RU_106_TONE, 1, true}, 9, 1}}};
     WifiMode uSigMode = EhtPhy::GetVhtMcs0();
     WifiMode ehtSigMode = EhtPhy::GetVhtMcs4(); // because of first user info map
 
@@ -1778,6 +1788,7 @@
 
     // -> EHT MU format
     txVector.SetPreambleType(WIFI_PREAMBLE_EHT_MU);
+    txVector.SetEhtPpduType(0); // EHT MU transmission
     txVector.SetRuAllocation({96});
     sections[WIFI_PPDU_FIELD_U_SIG] = {{ppduStart + MicroSeconds(24), ppduStart + MicroSeconds(32)},
                                        uSigMode};
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-aggregation-test.cc ns-3.38/src/wifi/test/wifi-aggregation-test.cc
--- ns-3.37/src/wifi/test/wifi-aggregation-test.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/test/wifi-aggregation-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -173,7 +173,7 @@
     reqHdr.SetBufferSize(64);
     reqHdr.SetTimeout(0);
     reqHdr.SetStartingSequence(0);
-    m_mac->GetBEQueue()->GetBaManager()->CreateAgreement(&reqHdr, hdr.GetAddr1());
+    m_mac->GetBEQueue()->GetBaManager()->CreateOriginatorAgreement(reqHdr, hdr.GetAddr1());
 
     MgtAddBaResponseHeader respHdr;
     StatusCode code;
@@ -184,7 +184,7 @@
     respHdr.SetTid(reqHdr.GetTid());
     respHdr.SetBufferSize(64);
     respHdr.SetTimeout(reqHdr.GetTimeout());
-    m_mac->GetBEQueue()->GetBaManager()->UpdateAgreement(&respHdr, hdr.GetAddr1(), 0);
+    m_mac->GetBEQueue()->GetBaManager()->UpdateOriginatorAgreement(respHdr, hdr.GetAddr1(), 0);
 
     //-----------------------------------------------------------------------------------------------------
 
@@ -210,6 +210,7 @@
 
     // the packet has not been "transmitted", release its sequence number
     m_mac->m_txMiddle->SetSequenceNumberFor(&item->GetHeader());
+    item->UnassignSeqNo();
 
     //-----------------------------------------------------------------------------------------------------
 
@@ -501,7 +502,7 @@
     reqHdr.SetBufferSize(64);
     reqHdr.SetTimeout(0);
     reqHdr.SetStartingSequence(0);
-    m_mac->GetVIQueue()->GetBaManager()->CreateAgreement(&reqHdr, hdr.GetAddr1());
+    m_mac->GetVIQueue()->GetBaManager()->CreateOriginatorAgreement(reqHdr, hdr.GetAddr1());
 
     MgtAddBaResponseHeader respHdr;
     StatusCode code;
@@ -512,7 +513,7 @@
     respHdr.SetTid(reqHdr.GetTid());
     respHdr.SetBufferSize(64);
     respHdr.SetTimeout(reqHdr.GetTimeout());
-    m_mac->GetVIQueue()->GetBaManager()->UpdateAgreement(&respHdr, hdr.GetAddr1(), 0);
+    m_mac->GetVIQueue()->GetBaManager()->UpdateOriginatorAgreement(respHdr, hdr.GetAddr1(), 0);
 
     m_mac->SetAttribute("VI_MaxAmsduSize", UintegerValue(3050)); // max 2 MSDUs per A-MSDU
     m_mac->SetAttribute("VI_MaxAmpduSize", UintegerValue(65535));
@@ -584,7 +585,7 @@
  * \ingroup tests
  *
  * \brief 802.11ax aggregation test which permits 64 or 256 MPDUs in A-MPDU according to the
- * negociated buffer size.
+ * negotiated buffer size.
  */
 class HeAggregationTest : public TestCase
 {
@@ -694,7 +695,7 @@
     reqHdr.SetBufferSize(bufferSize);
     reqHdr.SetTimeout(0);
     reqHdr.SetStartingSequence(0);
-    m_mac->GetBEQueue()->GetBaManager()->CreateAgreement(&reqHdr, hdr.GetAddr1());
+    m_mac->GetBEQueue()->GetBaManager()->CreateOriginatorAgreement(reqHdr, hdr.GetAddr1());
 
     MgtAddBaResponseHeader respHdr;
     StatusCode code;
@@ -705,10 +706,10 @@
     respHdr.SetTid(reqHdr.GetTid());
     respHdr.SetBufferSize(bufferSize);
     respHdr.SetTimeout(reqHdr.GetTimeout());
-    m_mac->GetBEQueue()->GetBaManager()->UpdateAgreement(&respHdr, hdr.GetAddr1(), 0);
+    m_mac->GetBEQueue()->GetBaManager()->UpdateOriginatorAgreement(respHdr, hdr.GetAddr1(), 0);
 
     /*
-     * Test behavior when 300 packets are ready for transmission but negociated buffer size is 64
+     * Test behavior when 300 packets are ready for transmission but negotiated buffer size is 64
      */
     Ptr<HtFrameExchangeManager> htFem = DynamicCast<HtFrameExchangeManager>(fem);
     Ptr<MpduAggregator> mpduAggregator = htFem->GetMpduAggregator();
@@ -741,7 +742,7 @@
     NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), false, "MPDU aggregation failed");
     NS_TEST_EXPECT_MSG_EQ(mpduList.size(),
                           bufferSize,
-                          "A-MPDU should countain " << bufferSize << " MPDUs");
+                          "A-MPDU should contain " << bufferSize << " MPDUs");
     uint16_t expectedRemainingPacketsInQueue = 300 - bufferSize;
     NS_TEST_EXPECT_MSG_EQ(m_mac->GetBEQueue()->GetWifiMacQueue()->GetNPackets(),
                           expectedRemainingPacketsInQueue,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-eht-info-elems-test.cc ns-3.38/src/wifi/test/wifi-eht-info-elems-test.cc
--- ns-3.37/src/wifi/test/wifi-eht-info-elems-test.cc	2022-11-01 17:37:24.759395800 -0700
+++ ns-3.38/src/wifi/test/wifi-eht-info-elems-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -22,6 +22,7 @@
 #include "ns3/mgt-headers.h"
 #include "ns3/multi-link-element.h"
 #include "ns3/reduced-neighbor-report.h"
+#include "ns3/tid-to-link-mapping-element.h"
 #include "ns3/wifi-phy-operating-channel.h"
 
 #include <sstream>
@@ -124,7 +125,7 @@
 
     commonInfo.m_bssParamsChangeCount = 1;
 
-    // Adding BSS Paraemeters Change Count
+    // Adding BSS Parameters Change Count
     TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}), m_frameType);
 
     commonInfo.m_mediumSyncDelayInfo =
@@ -344,6 +345,581 @@
  * \ingroup wifi-test
  * \ingroup tests
  *
+ * \brief Test serialization and deserialization of EHT capabilities IE
+ */
+class WifiEhtCapabilitiesIeTest : public HeaderSerializationTestCase
+{
+  public:
+    /**
+     * Constructor
+     * \param is2_4Ghz whether the PHY is operating in 2.4 GHz
+     * \param channelWidth the supported channel width in MHz
+     */
+    WifiEhtCapabilitiesIeTest(bool is2_4Ghz, uint16_t channelWidth);
+    ~WifiEhtCapabilitiesIeTest() override = default;
+
+    /**
+     * Generate the HE capabilities IE.
+     *
+     * \return the generated HE capabilities IE
+     */
+    HeCapabilities GetHeCapabilities() const;
+
+    /**
+     * Generate the EHT capabilities IE.
+     *
+     * \param maxMpduLength the maximum MPDU length in bytes
+     * \param maxAmpduSize the maximum A-MPDU size in bytes
+     * \param maxSupportedMcs the maximum EHT MCS supported by the PHY
+     * \return the generated EHT capabilities IE
+     */
+    EhtCapabilities GetEhtCapabilities(uint16_t maxMpduLength,
+                                       uint32_t maxAmpduSize,
+                                       uint8_t maxSupportedMcs) const;
+
+    /**
+     * Serialize the EHT capabilities in a buffer.
+     *
+     * \param ehtCapabilities the EHT capabilities
+     * \return the buffer in which the EHT capabilities has been serialized
+     */
+    Buffer SerializeIntoBuffer(const EhtCapabilities& ehtCapabilities);
+
+    /**
+     * Check that the given buffer contains the given value at the given position.
+     *
+     * \param buffer the given buffer
+     * \param position the given position (starting at 0)
+     * \param value the given value
+     */
+    void CheckSerializedByte(const Buffer& buffer, uint32_t position, uint8_t value);
+
+    /**
+     * Check the content of the EHT MAC Capabilities Information subfield.
+     *
+     * \param buffer the buffer containing the serialized EHT capabilities
+     * \param expectedValueFirstByte the expected value for the first byte
+     */
+    void CheckEhtMacCapabilitiesInformation(const Buffer& buffer, uint8_t expectedValueFirstByte);
+
+    /**
+     * Check the content of the EHT PHY Capabilities Information subfield.
+     *
+     * \param buffer the buffer containing the serialized EHT capabilities
+     * \param expectedValueSixthByte the expected value for the sixth byte
+     */
+    void CheckEhtPhyCapabilitiesInformation(const Buffer& buffer, uint8_t expectedValueSixthByte);
+
+    /**
+     * Check the content of the Supported EHT-MCS And NSS Set subfield.
+     * \param maxSupportedMcs the maximum EHT MCS supported by the PHY
+     *
+     * \param buffer the buffer containing the serialized EHT capabilities
+     */
+    void CheckSupportedEhtMcsAndNssSet(const Buffer& buffer, uint8_t maxSupportedMcs);
+
+  private:
+    void DoRun() override;
+
+    bool m_is2_4Ghz;         //!< whether the PHY is operating in 2.4 GHz
+    uint16_t m_channelWidth; //!< Supported channel width by the PHY (in MHz)
+};
+
+WifiEhtCapabilitiesIeTest ::WifiEhtCapabilitiesIeTest(bool is2_4Ghz, uint16_t channelWidth)
+    : HeaderSerializationTestCase{"Check serialization and deserialization of EHT capabilities IE"},
+      m_is2_4Ghz{is2_4Ghz},
+      m_channelWidth{channelWidth}
+{
+}
+
+HeCapabilities
+WifiEhtCapabilitiesIeTest::GetHeCapabilities() const
+{
+    HeCapabilities capabilities;
+    uint8_t channelWidthSet = 0;
+    if ((m_channelWidth >= 40) && m_is2_4Ghz)
+    {
+        channelWidthSet |= 0x01;
+    }
+    if ((m_channelWidth >= 80) && !m_is2_4Ghz)
+    {
+        channelWidthSet |= 0x02;
+    }
+    if ((m_channelWidth >= 160) && !m_is2_4Ghz)
+    {
+        channelWidthSet |= 0x04;
+    }
+    capabilities.SetChannelWidthSet(channelWidthSet);
+    return capabilities;
+}
+
+EhtCapabilities
+WifiEhtCapabilitiesIeTest::GetEhtCapabilities(uint16_t maxMpduLength,
+                                              uint32_t maxAmpduSize,
+                                              uint8_t maxSupportedMcs) const
+{
+    EhtCapabilities capabilities;
+
+    if (m_is2_4Ghz)
+    {
+        capabilities.SetMaxMpduLength(maxMpduLength);
+    }
+    // round to the next power of two minus one
+    maxAmpduSize = (1UL << static_cast<uint32_t>(std::ceil(std::log2(maxAmpduSize + 1)))) - 1;
+    // The maximum A-MPDU length in EHT capabilities elements ranges from 2^23-1 to 2^24-1
+    capabilities.SetMaxAmpduLength(std::min(std::max(maxAmpduSize, 8388607U), 16777215U));
+
+    capabilities.m_phyCapabilities.supportTx1024And4096QamForRuSmallerThan242Tones =
+        (maxSupportedMcs >= 12) ? 1 : 0;
+    capabilities.m_phyCapabilities.supportRx1024And4096QamForRuSmallerThan242Tones =
+        (maxSupportedMcs >= 12) ? 1 : 0;
+    if (m_channelWidth == 20)
+    {
+        for (auto maxMcs : {7, 9, 11, 13})
+        {
+            capabilities.SetSupportedRxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY,
+                                                    maxMcs,
+                                                    maxMcs <= maxSupportedMcs ? 1 : 0);
+            capabilities.SetSupportedTxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY,
+                                                    maxMcs,
+                                                    maxMcs <= maxSupportedMcs ? 2 : 0);
+        }
+    }
+    else
+    {
+        for (auto maxMcs : {9, 11, 13})
+        {
+            capabilities.SetSupportedRxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ,
+                maxMcs,
+                maxMcs <= maxSupportedMcs ? 3 : 0);
+            capabilities.SetSupportedTxEhtMcsAndNss(
+                EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ,
+                maxMcs,
+                maxMcs <= maxSupportedMcs ? 4 : 0);
+        }
+    }
+    if (m_channelWidth >= 160)
+    {
+        for (auto maxMcs : {9, 11, 13})
+        {
+            capabilities.SetSupportedRxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_160_MHZ,
+                                                    maxMcs,
+                                                    maxMcs <= maxSupportedMcs ? 2 : 0);
+            capabilities.SetSupportedTxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_160_MHZ,
+                                                    maxMcs,
+                                                    maxMcs <= maxSupportedMcs ? 1 : 0);
+        }
+    }
+    if (m_channelWidth == 320)
+    {
+        capabilities.m_phyCapabilities.support320MhzIn6Ghz = 1;
+        for (auto maxMcs : {9, 11, 13})
+        {
+            capabilities.SetSupportedRxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_320_MHZ,
+                                                    maxMcs,
+                                                    maxMcs <= maxSupportedMcs ? 4 : 0);
+            capabilities.SetSupportedTxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_320_MHZ,
+                                                    maxMcs,
+                                                    maxMcs <= maxSupportedMcs ? 3 : 0);
+        }
+    }
+    else
+    {
+        capabilities.m_phyCapabilities.support320MhzIn6Ghz = 0;
+    }
+
+    return capabilities;
+}
+
+Buffer
+WifiEhtCapabilitiesIeTest::SerializeIntoBuffer(const EhtCapabilities& ehtCapabilities)
+{
+    Buffer buffer;
+    buffer.AddAtStart(ehtCapabilities.GetSerializedSize());
+    ehtCapabilities.Serialize(buffer.Begin());
+    return buffer;
+}
+
+void
+WifiEhtCapabilitiesIeTest::CheckSerializedByte(const Buffer& buffer,
+                                               uint32_t position,
+                                               uint8_t value)
+{
+    Buffer::Iterator it = buffer.Begin();
+    it.Next(position);
+    uint8_t byte = it.ReadU8();
+    NS_TEST_EXPECT_MSG_EQ(+byte, +value, "Unexpected byte at pos=" << position);
+}
+
+void
+WifiEhtCapabilitiesIeTest::CheckEhtMacCapabilitiesInformation(const Buffer& buffer,
+                                                              uint8_t expectedValueFirstByte)
+{
+    CheckSerializedByte(buffer, 3, expectedValueFirstByte);
+    CheckSerializedByte(buffer, 4, 0x00);
+}
+
+void
+WifiEhtCapabilitiesIeTest::CheckEhtPhyCapabilitiesInformation(const Buffer& buffer,
+                                                              uint8_t expectedValueSixthByte)
+{
+    CheckSerializedByte(buffer, 5, (m_channelWidth == 320) ? 0x02 : 0x00);
+    CheckSerializedByte(buffer, 6, 0x00);
+    CheckSerializedByte(buffer, 7, 0x00);
+    CheckSerializedByte(buffer, 8, 0x00);
+    CheckSerializedByte(buffer, 9, 0x00);
+    CheckSerializedByte(buffer, 10, expectedValueSixthByte);
+    CheckSerializedByte(buffer, 11, 0x00);
+    CheckSerializedByte(buffer, 12, 0x00);
+    CheckSerializedByte(buffer, 13, 0x00);
+}
+
+void
+WifiEhtCapabilitiesIeTest::CheckSupportedEhtMcsAndNssSet(const Buffer& buffer,
+                                                         uint8_t maxSupportedMcs)
+{
+    if (m_channelWidth == 20)
+    {
+        CheckSerializedByte(buffer, 14, 0x21); // first byte of Supported EHT-MCS And NSS Set
+        CheckSerializedByte(
+            buffer,
+            15,
+            maxSupportedMcs >= 8 ? 0x21 : 0x00); // second byte of Supported EHT-MCS And NSS Set
+        CheckSerializedByte(
+            buffer,
+            16,
+            maxSupportedMcs >= 10 ? 0x21 : 0x00); // third byte of Supported EHT-MCS And NSS Set
+        CheckSerializedByte(
+            buffer,
+            17,
+            maxSupportedMcs >= 12 ? 0x21 : 0x00); // fourth byte of Supported EHT-MCS And NSS Set
+    }
+    else
+    {
+        CheckSerializedByte(buffer, 14, 0x43); // first byte of Supported EHT-MCS And NSS Set
+        CheckSerializedByte(
+            buffer,
+            15,
+            maxSupportedMcs >= 10 ? 0x43 : 0x00); // second byte of Supported EHT-MCS And NSS Set
+        CheckSerializedByte(
+            buffer,
+            16,
+            maxSupportedMcs >= 12 ? 0x43 : 0x00); // third byte of Supported EHT-MCS And NSS Set
+    }
+    if (m_channelWidth >= 160)
+    {
+        CheckSerializedByte(buffer, 17, 0x12); // first byte of EHT-MCS Map (BW = 160 MHz)
+        CheckSerializedByte(
+            buffer,
+            18,
+            maxSupportedMcs >= 10 ? 0x12 : 0x00); // second byte of EHT-MCS Map (BW = 160 MHz)
+        CheckSerializedByte(
+            buffer,
+            19,
+            maxSupportedMcs >= 12 ? 0x12 : 0x00); // third byte of EHT-MCS Map (BW = 160 MHz)
+    }
+    if (m_channelWidth == 320)
+    {
+        CheckSerializedByte(buffer, 20, 0x34); // first byte of EHT-MCS Map (BW = 320 MHz)
+        CheckSerializedByte(
+            buffer,
+            21,
+            maxSupportedMcs >= 10 ? 0x34 : 0x00); // second byte of EHT-MCS Map (BW = 320 MHz)
+        CheckSerializedByte(
+            buffer,
+            22,
+            maxSupportedMcs >= 12 ? 0x34 : 0x00); // third byte of EHT-MCS Map (BW = 320 MHz)
+    }
+}
+
+void
+WifiEhtCapabilitiesIeTest::DoRun()
+{
+    uint8_t maxMcs = 0;
+    uint16_t expectedEhtMcsAndNssSetSize = 0;
+    switch (m_channelWidth)
+    {
+    case 20:
+        expectedEhtMcsAndNssSetSize = 4;
+        break;
+    case 40:
+    case 80:
+        expectedEhtMcsAndNssSetSize = 3;
+        break;
+    case 160:
+        expectedEhtMcsAndNssSetSize = (2 * 3);
+        break;
+    case 320:
+        expectedEhtMcsAndNssSetSize = (3 * 3);
+        break;
+    default:
+        NS_ASSERT_MSG(false, "Invalid upper channel width " << m_channelWidth);
+    }
+
+    uint16_t expectedSize = 1 +                          // Element ID
+                            1 +                          // Length
+                            1 +                          // Element ID Extension
+                            2 +                          // EHT MAC Capabilities Information
+                            9 +                          // EHT PHY Capabilities Information
+                            expectedEhtMcsAndNssSetSize; // Supported EHT-MCS And NSS Set
+
+    auto mapType = m_channelWidth == 20 ? EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY
+                                        : EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_NOT_LARGER_THAN_80_MHZ;
+
+    {
+        maxMcs = 11;
+        HeCapabilities heCapabilities = GetHeCapabilities();
+        EhtCapabilities ehtCapabilities = GetEhtCapabilities(3895, 65535, maxMcs);
+
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedRxMcs(mapType) == maxMcs);
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedTxMcs(mapType) == maxMcs);
+
+        NS_TEST_EXPECT_MSG_EQ(ehtCapabilities.GetSerializedSize(),
+                              expectedSize,
+                              "Unexpected header size");
+
+        Buffer buffer = SerializeIntoBuffer(ehtCapabilities);
+
+        CheckEhtMacCapabilitiesInformation(buffer, 0x00);
+
+        CheckEhtPhyCapabilitiesInformation(buffer, 0x00);
+
+        CheckSupportedEhtMcsAndNssSet(buffer, maxMcs);
+
+        TestHeaderSerialization(ehtCapabilities, m_is2_4Ghz, heCapabilities);
+    }
+
+    {
+        maxMcs = 11;
+        HeCapabilities heCapabilities = GetHeCapabilities();
+        EhtCapabilities ehtCapabilities = GetEhtCapabilities(11454, 65535, maxMcs);
+
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedRxMcs(mapType) == maxMcs);
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedTxMcs(mapType) == maxMcs);
+
+        NS_TEST_EXPECT_MSG_EQ(ehtCapabilities.GetSerializedSize(),
+                              expectedSize,
+                              "Unexpected header size");
+
+        Buffer buffer = SerializeIntoBuffer(ehtCapabilities);
+
+        CheckEhtMacCapabilitiesInformation(buffer, m_is2_4Ghz ? 0x80 : 0x00);
+
+        CheckEhtPhyCapabilitiesInformation(buffer, 0x00);
+
+        CheckSupportedEhtMcsAndNssSet(buffer, maxMcs);
+
+        TestHeaderSerialization(ehtCapabilities, m_is2_4Ghz, heCapabilities);
+    }
+
+    {
+        maxMcs = 13;
+        HeCapabilities heCapabilities = GetHeCapabilities();
+        EhtCapabilities ehtCapabilities = GetEhtCapabilities(3895, 65535, maxMcs);
+
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedRxMcs(mapType) == maxMcs);
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedTxMcs(mapType) == maxMcs);
+
+        NS_TEST_EXPECT_MSG_EQ(ehtCapabilities.GetSerializedSize(),
+                              expectedSize,
+                              "Unexpected header size");
+
+        Buffer buffer = SerializeIntoBuffer(ehtCapabilities);
+
+        CheckEhtMacCapabilitiesInformation(buffer, 0x00);
+
+        CheckEhtPhyCapabilitiesInformation(buffer, 0x06);
+
+        CheckSupportedEhtMcsAndNssSet(buffer, maxMcs);
+
+        TestHeaderSerialization(ehtCapabilities, m_is2_4Ghz, heCapabilities);
+    }
+
+    {
+        maxMcs = 11;
+        HeCapabilities heCapabilities = GetHeCapabilities();
+        EhtCapabilities ehtCapabilities = GetEhtCapabilities(3895, 65535, maxMcs);
+
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedRxMcs(mapType) == maxMcs);
+        NS_ASSERT(ehtCapabilities.GetHighestSupportedTxMcs(mapType) == maxMcs);
+
+        std::vector<std::pair<uint8_t, uint8_t>> ppeThresholds;
+        ppeThresholds.emplace_back(1, 2); // NSS1 242-tones RU
+        ppeThresholds.emplace_back(2, 3); // NSS1 484-tones RU
+        ppeThresholds.emplace_back(3, 4); // NSS2 242-tones RU
+        ppeThresholds.emplace_back(4, 3); // NSS2 484-tones RU
+        ppeThresholds.emplace_back(3, 2); // NSS3 242-tones RU
+        ppeThresholds.emplace_back(2, 1); // NSS3 484-tones RU
+        ehtCapabilities.SetPpeThresholds(2, 0x03, ppeThresholds);
+
+        expectedSize += 6;
+
+        NS_TEST_EXPECT_MSG_EQ(ehtCapabilities.GetSerializedSize(),
+                              expectedSize,
+                              "Unexpected header size");
+
+        Buffer buffer = SerializeIntoBuffer(ehtCapabilities);
+
+        CheckEhtMacCapabilitiesInformation(buffer, 0x00);
+
+        CheckEhtPhyCapabilitiesInformation(buffer, 0x08);
+
+        CheckSupportedEhtMcsAndNssSet(buffer, maxMcs);
+
+        TestHeaderSerialization(ehtCapabilities, m_is2_4Ghz, heCapabilities);
+    }
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test TID-To-Link Mapping information element serialization and deserialization
+ */
+class TidToLinkMappingElementTest : public HeaderSerializationTestCase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \tparam Args \deduced Template type parameter pack for the sequence of TID-Link mapping pairs
+     * \param direction The direction for the TID-to-link mapping
+     * \param args A sequence of TID-Link mapping pairs
+     */
+    template <typename... Args>
+    TidToLinkMappingElementTest(TidLinkMapDir direction, Args&&... args);
+
+    ~TidToLinkMappingElementTest() override = default;
+
+  private:
+    /**
+     * Set the given TID-Link mapping and recursively call itself to set the remaining pairs.
+     *
+     * \tparam Args \deduced Template type parameter pack for the sequence of TID-Link mapping pairs
+     * \param tid the TID
+     * \param linkIds the IDs of the links on which the given TID is mapped
+     * \param args A sequence of TID-Link mapping pairs
+     */
+    template <typename... Args>
+    void SetLinkMapping(uint8_t tid, const std::list<uint8_t>& linkIds, Args&&... args);
+
+    /**
+     * Base case to stop the recursion performed by the templated version of this method.
+     */
+    void SetLinkMapping()
+    {
+    }
+
+    void DoRun() override;
+
+    TidToLinkMapping m_tidToLinkMapping; ///< TID-To-Link Mapping element
+};
+
+template <typename... Args>
+TidToLinkMappingElementTest::TidToLinkMappingElementTest(TidLinkMapDir direction, Args&&... args)
+    : HeaderSerializationTestCase(
+          "Check serialization and deserialization of TID-To-Link Mapping elements")
+{
+    m_tidToLinkMapping.m_control.direction = direction;
+    m_tidToLinkMapping.m_control.defaultMapping = true;
+    SetLinkMapping(args...);
+}
+
+template <typename... Args>
+void
+TidToLinkMappingElementTest::SetLinkMapping(uint8_t tid,
+                                            const std::list<uint8_t>& linkIds,
+                                            Args&&... args)
+{
+    m_tidToLinkMapping.m_control.defaultMapping = false;
+    m_tidToLinkMapping.SetLinkMappingOfTid(tid, linkIds);
+    SetLinkMapping(args...);
+}
+
+void
+TidToLinkMappingElementTest::DoRun()
+{
+    TestHeaderSerialization(m_tidToLinkMapping);
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test EHT Operation information element serialization and deserialization
+ */
+class EhtOperationElementTest : public HeaderSerializationTestCase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param params the EHT Operation Parameters field
+     * \param rxMaxNss0_7 RX max NSS that supports EHT MCS 0-7
+     * \param txMaxNss0_7 TX max NSS that supports EHT MCS 0-7
+     * \param rxMaxNss8_9 RX max NSS that supports EHT MCS 8-9
+     * \param txMaxNss8_9 TX max NSS that supports EHT MCS 8-9
+     * \param rxMaxNss10_11 RX max NSS that supports EHT MCS 10-11
+     * \param txMaxNss10_11 TX max NSS that supports EHT MCS 10-11
+     * \param rxMaxNss12_13 RX max NSS that supports EHT MCS 12-13
+     * \param txMaxNss12_13 TX max NSS that supports EHT MCS 12-13
+     * \param opInfo the EHT Operation Information field
+     */
+    EhtOperationElementTest(const EhtOperation::EhtOpParams& params,
+                            uint8_t rxMaxNss0_7,
+                            uint8_t txMaxNss0_7,
+                            uint8_t rxMaxNss8_9,
+                            uint8_t txMaxNss8_9,
+                            uint8_t rxMaxNss10_11,
+                            uint8_t txMaxNss10_11,
+                            uint8_t rxMaxNss12_13,
+                            uint8_t txMaxNss12_13,
+                            std::optional<EhtOperation::EhtOpInfo> opInfo);
+
+    ~EhtOperationElementTest() override = default;
+
+  private:
+    void DoRun() override;
+
+    EhtOperation m_ehtOperation; ///< EHT Operation element
+};
+
+EhtOperationElementTest::EhtOperationElementTest(const EhtOperation::EhtOpParams& params,
+                                                 uint8_t rxMaxNss0_7,
+                                                 uint8_t txMaxNss0_7,
+                                                 uint8_t rxMaxNss8_9,
+                                                 uint8_t txMaxNss8_9,
+                                                 uint8_t rxMaxNss10_11,
+                                                 uint8_t txMaxNss10_11,
+                                                 uint8_t rxMaxNss12_13,
+                                                 uint8_t txMaxNss12_13,
+                                                 std::optional<EhtOperation::EhtOpInfo> opInfo)
+    : HeaderSerializationTestCase(
+          "Check serialization and deserialization of EHT Operation elements")
+{
+    m_ehtOperation.m_params = params;
+    m_ehtOperation.SetMaxRxNss(rxMaxNss0_7, 0, 7);
+    m_ehtOperation.SetMaxTxNss(txMaxNss0_7, 0, 7);
+    m_ehtOperation.SetMaxRxNss(rxMaxNss8_9, 8, 9);
+    m_ehtOperation.SetMaxTxNss(txMaxNss8_9, 8, 9);
+    m_ehtOperation.SetMaxRxNss(rxMaxNss10_11, 10, 11);
+    m_ehtOperation.SetMaxTxNss(txMaxNss10_11, 10, 11);
+    m_ehtOperation.SetMaxRxNss(rxMaxNss12_13, 12, 13);
+    m_ehtOperation.SetMaxTxNss(txMaxNss12_13, 12, 13);
+    m_ehtOperation.m_opInfo = opInfo;
+}
+
+void
+EhtOperationElementTest::DoRun()
+{
+    TestHeaderSerialization(m_ehtOperation);
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
  * \brief wifi EHT Information Elements Test Suite
  */
 class WifiEhtInfoElemsTestSuite : public TestSuite
@@ -357,6 +933,65 @@
 {
     AddTestCase(new BasicMultiLinkElementTest(), TestCase::QUICK);
     AddTestCase(new ReducedNeighborReportTest(), TestCase::QUICK);
+    AddTestCase(new WifiEhtCapabilitiesIeTest(false, 20), TestCase::QUICK);
+    AddTestCase(new WifiEhtCapabilitiesIeTest(true, 20), TestCase::QUICK);
+    AddTestCase(new WifiEhtCapabilitiesIeTest(false, 80), TestCase::QUICK);
+    AddTestCase(new WifiEhtCapabilitiesIeTest(true, 40), TestCase::QUICK);
+    AddTestCase(new WifiEhtCapabilitiesIeTest(true, 80), TestCase::QUICK);
+    AddTestCase(new WifiEhtCapabilitiesIeTest(false, 160), TestCase::QUICK);
+    AddTestCase(new WifiEhtCapabilitiesIeTest(false, 320), TestCase::QUICK);
+    AddTestCase(new TidToLinkMappingElementTest(TidLinkMapDir::DOWNLINK), TestCase::QUICK);
+    AddTestCase(
+        new TidToLinkMappingElementTest(TidLinkMapDir::UPLINK, 3, std::list<uint8_t>{0, 4, 6}),
+        TestCase::QUICK);
+    AddTestCase(new TidToLinkMappingElementTest(TidLinkMapDir::BOTH_DIRECTIONS,
+                                                3,
+                                                std::list<uint8_t>{0, 4, 6},
+                                                6,
+                                                std::list<uint8_t>{3, 7, 11, 14}),
+                TestCase::QUICK);
+    AddTestCase(new TidToLinkMappingElementTest(TidLinkMapDir::DOWNLINK,
+                                                0,
+                                                std::list<uint8_t>{0, 1, 2},
+                                                1,
+                                                std::list<uint8_t>{3, 4, 5},
+                                                2,
+                                                std::list<uint8_t>{6, 7},
+                                                3,
+                                                std::list<uint8_t>{8, 9, 10},
+                                                4,
+                                                std::list<uint8_t>{11, 12, 13},
+                                                5,
+                                                std::list<uint8_t>{14},
+                                                6,
+                                                std::list<uint8_t>{1, 3, 6},
+                                                7,
+                                                std::list<uint8_t>{11, 14}),
+                TestCase::QUICK);
+    AddTestCase(new EhtOperationElementTest({0, 0, 0, 0, 0}, 1, 2, 3, 4, 5, 6, 7, 8, std::nullopt),
+                TestCase::QUICK);
+    AddTestCase(new EhtOperationElementTest({1, 0, 0, 1, 0},
+                                            1,
+                                            2,
+                                            3,
+                                            4,
+                                            5,
+                                            6,
+                                            7,
+                                            8,
+                                            EhtOperation::EhtOpInfo{{1}, 3, 5}),
+                TestCase::QUICK);
+    AddTestCase(new EhtOperationElementTest({1, 1, 1, 1, 2},
+                                            1,
+                                            2,
+                                            3,
+                                            4,
+                                            5,
+                                            6,
+                                            7,
+                                            8,
+                                            EhtOperation::EhtOpInfo{{2}, 4, 6, 3000}),
+                TestCase::QUICK);
 }
 
 static WifiEhtInfoElemsTestSuite g_wifiEhtInfoElemsTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-error-rate-models-test.cc ns-3.38/src/wifi/test/wifi-error-rate-models-test.cc
--- ns-3.37/src/wifi/test/wifi-error-rate-models-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-error-rate-models-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -18,6 +18,13 @@
  *          Sébastien Deronne (sebastien.deronne@gmail.com)
  */
 
+#ifdef HAVE_GSL
+#include <gsl/gsl_cdf.h>
+#include <gsl/gsl_integration.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_sf_bessel.h>
+#endif
+
 #include "ns3/dsss-error-rate-model.h"
 #include "ns3/he-phy.h" //includes HT and VHT
 #include "ns3/interference-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-mac-ofdma-test.cc ns-3.38/src/wifi/test/wifi-mac-ofdma-test.cc
--- ns-3.37/src/wifi/test/wifi-mac-ofdma-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-mac-ofdma-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -40,6 +40,8 @@
 #include "ns3/wifi-protection.h"
 #include "ns3/wifi-psdu.h"
 
+#include <iomanip>
+
 using namespace ns3;
 
 NS_LOG_COMPONENT_DEFINE("WifiMacOfdmaTestSuite");
@@ -85,6 +87,7 @@
     WifiTxVector m_txVector;          //!< the TX vector for MU PPDUs
     WifiTxParameters m_txParams;      //!< TX parameters
     WifiPsduMap m_psduMap;            //!< the DL MU PPDU to transmit
+    WifiModulationClass m_modClass;   //!< modulation class for DL MU PPDUs and TB PPDUs
 };
 
 NS_OBJECT_ENSURE_REGISTERED(TestMultiUserScheduler);
@@ -92,10 +95,16 @@
 TypeId
 TestMultiUserScheduler::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::TestMultiUserScheduler")
-                            .SetParent<MultiUserScheduler>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<TestMultiUserScheduler>();
+    static TypeId tid =
+        TypeId("ns3::TestMultiUserScheduler")
+            .SetParent<MultiUserScheduler>()
+            .SetGroupName("Wifi")
+            .AddConstructor<TestMultiUserScheduler>()
+            .AddAttribute("ModulationClass",
+                          "Modulation class for DL MU PPDUs and TB PPDUs.",
+                          EnumValue(WIFI_MOD_CLASS_HE),
+                          MakeEnumAccessor(&TestMultiUserScheduler::m_modClass),
+                          MakeEnumChecker(WIFI_MOD_CLASS_HE, "HE", WIFI_MOD_CLASS_EHT, "EHT"));
     return tid;
 }
 
@@ -133,19 +142,22 @@
             (m_txFormat == SU_TX || m_txFormat == DL_MU_TX ? TriggerFrameType::BSRP_TRIGGER
                                                            : TriggerFrameType::BASIC_TRIGGER);
 
-        m_trigger = CtrlTriggerHeader(ulTriggerType, m_txVector);
-
         WifiTxVector txVector = m_txVector;
+        txVector.SetPreambleType(m_modClass == WIFI_MOD_CLASS_HE ? WIFI_PREAMBLE_HE_TB
+                                                                 : WIFI_PREAMBLE_EHT_TB);
+        m_trigger = CtrlTriggerHeader(ulTriggerType, txVector);
+
         txVector.SetGuardInterval(m_trigger.GetGuardInterval());
 
         uint32_t ampduSize = (ulTriggerType == TriggerFrameType::BSRP_TRIGGER)
                                  ? 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().begin()->first);
+        Time duration =
+            WifiPhy::CalculateTxDuration(ampduSize,
+                                         txVector,
+                                         m_apMac->GetWifiPhy()->GetPhyBand(),
+                                         m_apMac->GetStaList(SINGLE_LINK_OP_ID).begin()->first);
 
         uint16_t length;
         std::tie(length, duration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
@@ -170,7 +182,7 @@
         m_txParams.m_txVector =
             m_apMac->GetWifiRemoteStationManager()->GetRtsTxVector(m_triggerHdr.GetAddr1());
 
-        if (!m_heFem->TryAddMpdu(item, m_txParams, m_availableTime) ||
+        if (!GetHeFem(SINGLE_LINK_OP_ID)->TryAddMpdu(item, m_txParams, m_availableTime) ||
             (m_availableTime != Time::Min() &&
              m_txParams.m_protection->protectionTime + m_txParams.m_txDuration // TF tx time
                      + m_apMac->GetWifiPhy()->GetSifs() + duration +
@@ -188,7 +200,7 @@
     {
         // try to send a DL MU PPDU
         m_psduMap.clear();
-        const std::map<uint16_t, Mac48Address>& staList = m_apMac->GetStaList();
+        const std::map<uint16_t, Mac48Address>& staList = m_apMac->GetStaList(SINGLE_LINK_OP_ID);
         NS_ABORT_MSG_IF(staList.size() != 4, "There must be 4 associated stations");
 
         /* Initialize TX params */
@@ -227,8 +239,9 @@
             }
 
             std::vector<Ptr<WifiMpdu>> mpduList;
-            mpduList =
-                m_heFem->GetMpduAggregator()->GetNextAmpdu(mpdu, m_txParams, m_availableTime);
+            mpduList = GetHeFem(SINGLE_LINK_OP_ID)
+                           ->GetMpduAggregator()
+                           ->GetNextAmpdu(mpdu, m_txParams, m_availableTime);
 
             if (mpduList.size() > 1)
             {
@@ -260,7 +273,7 @@
 void
 TestMultiUserScheduler::ComputeWifiTxVector()
 {
-    if (m_txVector.GetPreambleType() == WIFI_PREAMBLE_HE_MU)
+    if (m_txVector.IsDlMu())
     {
         // the TX vector has been already computed
         return;
@@ -268,12 +281,18 @@
 
     uint16_t bw = m_apMac->GetWifiPhy()->GetChannelWidth();
 
-    m_txVector.SetPreambleType(WIFI_PREAMBLE_HE_MU);
+    m_txVector.SetPreambleType(m_modClass == WIFI_MOD_CLASS_HE ? WIFI_PREAMBLE_HE_MU
+                                                               : WIFI_PREAMBLE_EHT_MU);
+    if (IsEht(m_txVector.GetPreambleType()))
+    {
+        m_txVector.SetEhtPpduType(0);
+    }
     m_txVector.SetChannelWidth(bw);
     m_txVector.SetGuardInterval(m_apMac->GetHeConfiguration()->GetGuardInterval().GetNanoSeconds());
-    m_txVector.SetTxPowerLevel(GetWifiRemoteStationManager()->GetDefaultTxPowerLevel());
+    m_txVector.SetTxPowerLevel(
+        GetWifiRemoteStationManager(SINGLE_LINK_OP_ID)->GetDefaultTxPowerLevel());
 
-    const std::map<uint16_t, Mac48Address>& staList = m_apMac->GetStaList();
+    const std::map<uint16_t, Mac48Address>& staList = m_apMac->GetStaList(SINGLE_LINK_OP_ID);
     NS_ABORT_MSG_IF(staList.size() != 4, "There must be 4 associated stations");
 
     HeRu::RuType ruType;
@@ -309,8 +328,7 @@
             ruIndex = 1;
             primary80 = false;
         }
-        m_txVector.SetHeMuUserInfo(sta.first,
-                                   {{ruType, ruIndex++, primary80}, WifiMode("HeMcs11"), 1});
+        m_txVector.SetHeMuUserInfo(sta.first, {{ruType, ruIndex++, primary80}, 11, 1});
     }
     m_txVector.SetSigBMode(VhtPhy::GetVhtMcs5());
 }
@@ -332,6 +350,18 @@
 /**
  * \ingroup wifi-test
  * \ingroup tests
+ * The scenarios
+ */
+enum class WifiOfdmaScenario : uint8_t
+{
+    HE = 0, // HE AP and HE non-AP STAs
+    HE_EHT, // EHT AP, some EHT non-AP STAs and some non-EHT HE non-AP STAs
+    EHT     // EHT AP and EHT non-AP STAs
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
  *
  * \brief Test OFDMA acknowledgment sequences
  *
@@ -367,13 +397,15 @@
      * \param txopLimit the TXOP limit in microseconds
      * \param nPktsPerSta number of packets to send to/receive from each station
      * \param muEdcaParameterSet the MU EDCA Parameter Set
+     * \param scenario the OFDMA scenario to test
      */
     OfdmaAckSequenceTest(uint16_t width,
                          WifiAcknowledgment::Method dlType,
                          uint32_t maxAmpduSize,
                          uint16_t txopLimit,
                          uint16_t nPktsPerSta,
-                         MuEdcaParameterSet muEdcaParameterSet);
+                         MuEdcaParameterSet muEdcaParameterSet,
+                         WifiOfdmaScenario scenario);
     ~OfdmaAckSequenceTest() override;
 
     /**
@@ -427,12 +459,16 @@
     Ptr<WifiNetDevice> m_apDevice;              ///< AP's device
     std::vector<PacketSocketAddress> m_sockets; ///< packet socket addresses for STAs
     uint16_t m_channelWidth;                    ///< PHY channel bandwidth in MHz
+    uint8_t m_muRtsRuAllocation;                ///< B7-B1 of RU Allocation subfield of MU-RTS
     std::vector<FrameInfo> m_txPsdus;           ///< transmitted PSDUs
     WifiAcknowledgment::Method m_dlMuAckType;   ///< DL MU ack sequence type
     uint32_t m_maxAmpduSize;                    ///< maximum A-MPDU size in bytes
     uint16_t m_txopLimit;                       ///< TXOP limit in microseconds
     uint16_t m_nPktsPerSta;                     ///< number of packets to send to each station
     MuEdcaParameterSet m_muEdcaParameterSet;    ///< MU EDCA Parameter Set
+    WifiOfdmaScenario m_scenario;               ///< OFDMA scenario to test
+    WifiPreamble m_dlMuPreamble;                ///< expected preamble type for DL MU PPDUs
+    WifiPreamble m_tbPreamble;                  ///< expected preamble type for TB PPDUs
     bool m_ulPktsGenerated;           ///< whether UL packets for HE TB PPDUs have been generated
     uint16_t m_received;              ///< number of packets received by the stations
     uint16_t m_flushed;               ///< number of DL packets flushed after DL MU PPDU
@@ -445,7 +481,8 @@
                                            uint32_t maxAmpduSize,
                                            uint16_t txopLimit,
                                            uint16_t nPktsPerSta,
-                                           MuEdcaParameterSet muEdcaParameterSet)
+                                           MuEdcaParameterSet muEdcaParameterSet,
+                                           WifiOfdmaScenario scenario)
     : TestCase("Check correct operation of DL OFDMA acknowledgment sequences"),
       m_nStations(4),
       m_sockets(m_nStations),
@@ -455,12 +492,43 @@
       m_txopLimit(txopLimit),
       m_nPktsPerSta(nPktsPerSta),
       m_muEdcaParameterSet(muEdcaParameterSet),
+      m_scenario(scenario),
       m_ulPktsGenerated(false),
       m_received(0),
       m_flushed(0),
       m_edcaDisabledStartTime(Seconds(0)),
       m_cwValues(std::vector<uint32_t>(m_nStations, 2)) // 2 is an invalid CW value
 {
+    switch (m_scenario)
+    {
+    case WifiOfdmaScenario::HE:
+    case WifiOfdmaScenario::HE_EHT:
+        m_dlMuPreamble = WIFI_PREAMBLE_HE_MU;
+        m_tbPreamble = WIFI_PREAMBLE_HE_TB;
+        break;
+    case WifiOfdmaScenario::EHT:
+        m_dlMuPreamble = WIFI_PREAMBLE_EHT_MU;
+        m_tbPreamble = WIFI_PREAMBLE_EHT_TB;
+        break;
+    }
+
+    switch (m_channelWidth)
+    {
+    case 20:
+        m_muRtsRuAllocation = 61; // p20 index is 0
+        break;
+    case 40:
+        m_muRtsRuAllocation = 65; // p20 index is 0
+        break;
+    case 80:
+        m_muRtsRuAllocation = 67;
+        break;
+    case 160:
+        m_muRtsRuAllocation = 68;
+        break;
+    default:
+        NS_ABORT_MSG("Unhandled channel width (" << m_channelWidth << " MHz)");
+    }
 }
 
 OfdmaAckSequenceTest::~OfdmaAckSequenceTest()
@@ -507,14 +575,16 @@
                         << (psdu->GetHeader(0).IsQosData()
                                 ? " TID " + std::to_string(*psdu->GetTids().begin())
                                 : "")
-                        << " txDuration " << txDuration << " duration/ID "
-                        << psdu->GetHeader(0).GetDuration() << " #TX PSDUs = " << m_txPsdus.size());
+                        << std::setprecision(10) << " txDuration " << txDuration << " duration/ID "
+                        << psdu->GetHeader(0).GetDuration() << " #TX PSDUs = " << m_txPsdus.size()
+                        << " size=" << (*psdu->begin())->GetSize() << "\n"
+                        << "TXVECTOR: " << txVector << "\n");
         }
     }
 
     // Flush the MAC queue of the AP after sending a DL MU PPDU (no need for
     // further transmissions)
-    if (txVector.GetPreambleType() == WIFI_PREAMBLE_HE_MU)
+    if (txVector.GetPreambleType() == m_dlMuPreamble)
     {
         m_flushed = 0;
         for (uint32_t i = 0; i < m_staDevices.GetN(); i++)
@@ -541,7 +611,7 @@
             }
         }
     }
-    else if (txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
+    else if (txVector.GetPreambleType() == m_tbPreamble &&
              psduMap.begin()->second->GetHeader(0).HasData())
     {
         Mac48Address sender = psduMap.begin()->second->GetAddr2();
@@ -598,6 +668,7 @@
         {
             // the AP is starting the transmission of the Basic Trigger frame, so generate
             // the configured number of packets at STAs, which are sent in HE TB PPDUs
+            Time txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, WIFI_PHY_BAND_5GHZ);
             for (uint16_t i = 0; i < m_nStations; i++)
             {
                 Ptr<PacketSocketClient> client = CreateObject<PacketSocketClient>();
@@ -607,7 +678,7 @@
                 client->SetAttribute("Priority", UintegerValue(i * 2)); // 0, 2, 4 and 6
                 client->SetRemote(m_sockets[i]);
                 m_staDevices.Get(i)->GetNode()->AddApplication(client);
-                client->SetStartTime(Seconds(0));  // start now
+                client->SetStartTime(txDuration);  // start when TX ends
                 client->SetStopTime(Seconds(1.0)); // stop in a second
                 client->Initialize();
             }
@@ -622,48 +693,165 @@
     CtrlTriggerHeader trigger;
     CtrlBAckResponseHeader blockAck;
     Time tEnd;                         // TX end for a frame
-    Time tStart;                       // TX start fot the next frame
+    Time tStart;                       // TX start for the next frame
     Time tolerance = NanoSeconds(500); // due to propagation delay
     Time ifs = (m_txopLimit > 0 ? sifs : sifs + aifsn * slotTime);
     Time navEnd;
 
     /*
-     *                                                  |--------------NAV------------------>|
-     *                |------NAV------->|               |                 |------NAV-------->|
-     *      |---------|      |----------|     |---------|      |----------|      |-----------|
-     *      |         |      |QoS Null 1|     |         |      |QoS Data 1|      |           |
-     *      |  BSRP   |      |----------|     |  Basic  |      |----------|      | Multi-STA |
-     *      | Trigger |      |QoS Null 2|     | Trigger |      |QoS Data 2|      | Block Ack |
-     *      |  Frame  |<SIFS>|----------|<IFS>|  Frame  |<SIFS>|----------|<SIFS>|           |
-     *      |         |      |QoS Null 3|     |         |      |QoS Data 3|      |           |
-     *      |         |      |----------|     |         |      |----------|      |           |
-     *      |         |      |QoS Null 4|     |         |      |QoS Data 4|      |           |
-     * -----------------------------------------------------------------------------------------
-     * From:     AP                                AP                                 AP
-     *   To:    all               AP              all               AP               all
+     *        |-------------NAV----------->|         |-----------------NAV------------------->|
+     *                 |---------NAV------>|                  |--------------NAV------------->|
+     *                           |---NAV-->|                             |--------NAV-------->|
+     *    ┌───┐    ┌───┐    ┌────┐    ┌────┐     ┌───┐    ┌───┐    ┌─────┐    ┌────┐    ┌─────┐
+     *    │   │    │   │    │    │    │QoS │     │   │    │   │    │     │    │QoS │    │     │
+     *    │   │    │   │    │    │    │Null│     │   │    │   │    │     │    │Data│    │     │
+     *    │   │    │   │    │    │    ├────┤     │   │    │   │    │     │    ├────┤    │     │
+     *    │   │    │   │    │    │    │QoS │     │   │    │   │    │     │    │QoS │    │Multi│
+     *    │MU-│    │CTS│    │BSRP│    │Null│     │MU-│    │CTS│    │Basic│    │Data│    │-STA │
+     *    │RTS│SIFS│   │SIFS│ TF │SIFS├────┤<IFS>│RTS│SIFS│   │SIFS│ TF  │SIFS├────┤SIFS│Block│
+     *    │TF │    │x4 │    │    │    │QoS │     │TF │    │x4 │    │     │    │QoS │    │ Ack │
+     *    │   │    │   │    │    │    │Null│     │   │    │   │    │     │    │Data│    │     │
+     *    │   │    │   │    │    │    ├────┤     │   │    │   │    │     │    ├────┤    │     │
+     *    │   │    │   │    │    │    │QoS │     │   │    │   │    │     │    │QoS │    │     │
+     *    │   │    │   │    │    │    │Null│     │   │    │   │    │     │    │Data│    │     │
+     * ───┴───┴────┴───┴────┴────┴────┴────┴─────┴───┴────┴───┴────┴─────┴────┴────┴────┴─────┴──
+     * From: AP     all       AP        all       AP       all       AP         all       AP
+     *   To: all    AP        all       AP        all      AP        all        AP        all
      */
 
-    // the first packet sent after 1.5s is a BSRP Trigger Frame
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 5, "Expected at least 5 transmitted packet");
+    // the first packet sent after 1.5s is an MU-RTS Trigger Frame
+    NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 5, "Expected at least 5 transmitted packet");
     NS_TEST_EXPECT_MSG_EQ((m_txPsdus[0].psduMap.size() == 1 &&
                            m_txPsdus[0].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
                            m_txPsdus[0].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
                           true,
                           "Expected a Trigger Frame");
     m_txPsdus[0].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[0].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[0].endTx;
+    navEnd = tEnd + m_txPsdus[0].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[1].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+         m_txPsdus[1].psduMap.size() == 1 &&
+         m_txPsdus[1].psduMap.begin()->second->GetNMpdus() == 1 &&
+         m_txPsdus[1].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+        true,
+        "Expected a CTS frame");
+    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[1].txVector.GetChannelWidth(),
+                          m_channelWidth,
+                          "Expected the CTS to occupy the entire channel width");
+
+    tStart = m_txPsdus[1].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");
+    Time ctsNavEnd = m_txPsdus[1].endTx + m_txPsdus[1].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[2].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+         m_txPsdus[2].psduMap.size() == 1 &&
+         m_txPsdus[2].psduMap.begin()->second->GetNMpdus() == 1 &&
+         m_txPsdus[2].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+        true,
+        "Expected a CTS frame");
+    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[2].txVector.GetChannelWidth(),
+                          m_channelWidth,
+                          "Expected the CTS to occupy the entire channel width");
+
+    tStart = m_txPsdus[2].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[2].endTx + m_txPsdus[2].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[3].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+         m_txPsdus[3].psduMap.size() == 1 &&
+         m_txPsdus[3].psduMap.begin()->second->GetNMpdus() == 1 &&
+         m_txPsdus[3].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+        true,
+        "Expected a CTS frame");
+    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[3].txVector.GetChannelWidth(),
+                          m_channelWidth,
+                          "Expected the CTS to occupy the entire channel width");
+
+    tStart = m_txPsdus[3].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[3].endTx + m_txPsdus[3].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[4].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+         m_txPsdus[4].psduMap.size() == 1 &&
+         m_txPsdus[4].psduMap.begin()->second->GetNMpdus() == 1 &&
+         m_txPsdus[4].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+        true,
+        "Expected a CTS frame");
+    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[4].txVector.GetChannelWidth(),
+                          m_channelWidth,
+                          "Expected the CTS to occupy the entire channel width");
+
+    tStart = m_txPsdus[4].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[4].endTx + m_txPsdus[4].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 a BSRP Trigger Frame
+    NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 10, "Expected at least 10 transmitted packet");
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[5].psduMap.size() == 1 &&
+                           m_txPsdus[5].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
+                           m_txPsdus[5].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
+                          true,
+                          "Expected a Trigger Frame");
+    m_txPsdus[5].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(trigger);
     NS_TEST_EXPECT_MSG_EQ(trigger.IsBsrp(), true, "Expected a BSRP Trigger Frame");
     NS_TEST_EXPECT_MSG_EQ(trigger.GetNUserInfoFields(),
                           4,
                           "Expected one User Info field per station");
+    tEnd = m_txPsdus[4].endTx;
+    tStart = m_txPsdus[5].startTx;
+    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "BSRP Trigger Frame sent too early");
+    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "BSRP Trigger Frame sent too late");
+    Time bsrpNavEnd = m_txPsdus[5].endTx + m_txPsdus[5].psduMap[SU_STA_ID]->GetDuration();
+    // navEnd <= bsrpNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, bsrpNavEnd, "Duration/ID in BSRP TF is too short");
+    NS_TEST_EXPECT_MSG_LT(bsrpNavEnd, navEnd + tolerance, "Duration/ID in BSRP TF is too long");
 
-    // A first STA sends a QoS Null frame in an HE TB PPDU a SIFS after the reception of the BSRP TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[1].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[1].psduMap.size() == 1 &&
-                           m_txPsdus[1].psduMap.begin()->second->GetNMpdus() == 1),
+    // A first STA sends a QoS Null frame in a TB PPDU a SIFS after the reception of the BSRP TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[6].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[6].psduMap.size() == 1 &&
+                           m_txPsdus[6].psduMap.begin()->second->GetNMpdus() == 1),
                           true,
-                          "Expected a QoS Null frame in an HE TB PPDU");
+                          "Expected a QoS Null frame in a TB PPDU");
     {
-        const WifiMacHeader& hdr = m_txPsdus[1].psduMap.begin()->second->GetHeader(0);
+        const WifiMacHeader& hdr = m_txPsdus[6].psduMap.begin()->second->GetHeader(0);
         NS_TEST_EXPECT_MSG_EQ(hdr.GetType(), WIFI_MAC_QOSDATA_NULL, "Expected a QoS Null frame");
         uint16_t staId;
         for (staId = 0; staId < m_nStations; staId++)
@@ -677,26 +865,31 @@
         uint8_t tid = staId * 2;
         NS_TEST_EXPECT_MSG_EQ(+hdr.GetQosTid(), +tid, "Expected a TID equal to " << +tid);
     }
-    tEnd = m_txPsdus[0].endTx;
-    navEnd = tEnd + m_txPsdus[0].psduMap[SU_STA_ID]->GetDuration();
-    tStart = m_txPsdus[1].startTx;
+    tEnd = m_txPsdus[5].endTx;
+    tStart = m_txPsdus[6].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS Null frame in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS Null frame in HE TB PPDU sent too late");
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[1].endTx,
-                                "Duration/ID in BSRP Trigger Frame is too short");
-
-    // A second STA sends a QoS Null frame in an HE TB PPDU a SIFS after the reception of the BSRP
-    // TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[2].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[2].psduMap.size() == 1 &&
-                           m_txPsdus[2].psduMap.begin()->second->GetNMpdus() == 1),
+    Time qosNullNavEnd = m_txPsdus[6].endTx + m_txPsdus[6].psduMap.begin()->second->GetDuration();
+    if (m_txopLimit == 0)
+    {
+        NS_TEST_EXPECT_MSG_EQ(qosNullNavEnd,
+                              m_txPsdus[6].endTx,
+                              "Expected null Duration/ID for QoS Null frame in HE TB PPDU");
+    }
+    // navEnd <= qosNullNavEnd < navEnd + tolerance
+    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");
+
+    // A second STA sends a QoS Null frame in a TB PPDU a SIFS after the reception of the BSRP TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[7].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[7].psduMap.size() == 1 &&
+                           m_txPsdus[7].psduMap.begin()->second->GetNMpdus() == 1),
                           true,
-                          "Expected a QoS Null frame in an HE TB PPDU");
+                          "Expected a QoS Null frame in a TB PPDU");
     {
-        const WifiMacHeader& hdr = m_txPsdus[2].psduMap.begin()->second->GetHeader(0);
+        const WifiMacHeader& hdr = m_txPsdus[7].psduMap.begin()->second->GetHeader(0);
         NS_TEST_EXPECT_MSG_EQ(hdr.GetType(), WIFI_MAC_QOSDATA_NULL, "Expected a QoS Null frame");
         uint16_t staId;
         for (staId = 0; staId < m_nStations; staId++)
@@ -710,23 +903,30 @@
         uint8_t tid = staId * 2;
         NS_TEST_EXPECT_MSG_EQ(+hdr.GetQosTid(), +tid, "Expected a TID equal to " << +tid);
     }
-    tStart = m_txPsdus[2].startTx;
+    tStart = m_txPsdus[7].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS Null frame in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS Null frame in HE TB PPDU sent too late");
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[2].endTx,
-                                "Duration/ID in BSRP Trigger Frame is too short");
-
-    // A third STA sends a QoS Null frame in an HE TB PPDU a SIFS after the reception of the BSRP TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[3].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[3].psduMap.size() == 1 &&
-                           m_txPsdus[3].psduMap.begin()->second->GetNMpdus() == 1),
+    qosNullNavEnd = m_txPsdus[7].endTx + m_txPsdus[7].psduMap.begin()->second->GetDuration();
+    if (m_txopLimit == 0)
+    {
+        NS_TEST_EXPECT_MSG_EQ(qosNullNavEnd,
+                              m_txPsdus[7].endTx,
+                              "Expected null Duration/ID for QoS Null frame in HE TB PPDU");
+    }
+    // navEnd <= qosNullNavEnd < navEnd + tolerance
+    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");
+
+    // A third STA sends a QoS Null frame in a TB PPDU a SIFS after the reception of the BSRP TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[8].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[8].psduMap.size() == 1 &&
+                           m_txPsdus[8].psduMap.begin()->second->GetNMpdus() == 1),
                           true,
                           "Expected a QoS Null frame in an HE TB PPDU");
     {
-        const WifiMacHeader& hdr = m_txPsdus[3].psduMap.begin()->second->GetHeader(0);
+        const WifiMacHeader& hdr = m_txPsdus[8].psduMap.begin()->second->GetHeader(0);
         NS_TEST_EXPECT_MSG_EQ(hdr.GetType(), WIFI_MAC_QOSDATA_NULL, "Expected a QoS Null frame");
         uint16_t staId;
         for (staId = 0; staId < m_nStations; staId++)
@@ -740,24 +940,30 @@
         uint8_t tid = staId * 2;
         NS_TEST_EXPECT_MSG_EQ(+hdr.GetQosTid(), +tid, "Expected a TID equal to " << +tid);
     }
-    tStart = m_txPsdus[3].startTx;
+    tStart = m_txPsdus[8].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS Null frame in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS Null frame in HE TB PPDU sent too late");
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[3].endTx,
-                                "Duration/ID in BSRP Trigger Frame is too short");
-
-    // A fourth STA sends a QoS Null frame in an HE TB PPDU a SIFS after the reception of the BSRP
-    // TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[4].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[4].psduMap.size() == 1 &&
-                           m_txPsdus[4].psduMap.begin()->second->GetNMpdus() == 1),
+    qosNullNavEnd = m_txPsdus[8].endTx + m_txPsdus[8].psduMap.begin()->second->GetDuration();
+    if (m_txopLimit == 0)
+    {
+        NS_TEST_EXPECT_MSG_EQ(qosNullNavEnd,
+                              m_txPsdus[8].endTx,
+                              "Expected null Duration/ID for QoS Null frame in HE TB PPDU");
+    }
+    // navEnd <= qosNullNavEnd < navEnd + tolerance
+    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");
+
+    // A fourth STA sends a QoS Null frame in a TB PPDU a SIFS after the reception of the BSRP TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[9].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[9].psduMap.size() == 1 &&
+                           m_txPsdus[9].psduMap.begin()->second->GetNMpdus() == 1),
                           true,
                           "Expected a QoS Null frame in an HE TB PPDU");
     {
-        const WifiMacHeader& hdr = m_txPsdus[4].psduMap.begin()->second->GetHeader(0);
+        const WifiMacHeader& hdr = m_txPsdus[9].psduMap.begin()->second->GetHeader(0);
         NS_TEST_EXPECT_MSG_EQ(hdr.GetType(), WIFI_MAC_QOSDATA_NULL, "Expected a QoS Null frame");
         uint16_t staId;
         for (staId = 0; staId < m_nStations; staId++)
@@ -771,104 +977,241 @@
         uint8_t tid = staId * 2;
         NS_TEST_EXPECT_MSG_EQ(+hdr.GetQosTid(), +tid, "Expected a TID equal to " << +tid);
     }
-    tStart = m_txPsdus[4].startTx;
+    tStart = m_txPsdus[9].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS Null frame in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS Null frame in HE TB PPDU sent too late");
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[4].endTx,
-                                "Duration/ID in BSRP Trigger Frame is too short");
+    qosNullNavEnd = m_txPsdus[9].endTx + m_txPsdus[9].psduMap.begin()->second->GetDuration();
+    if (m_txopLimit == 0)
+    {
+        NS_TEST_EXPECT_MSG_EQ(qosNullNavEnd,
+                              m_txPsdus[9].endTx,
+                              "Expected null Duration/ID for QoS Null frame in HE TB PPDU");
+    }
+    // navEnd <= qosNullNavEnd < navEnd + tolerance
+    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 a Basic Trigger Frame to solicit QoS data frames
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 11, "Expected at least 11 transmitted packets");
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[5].psduMap.size() == 1 &&
-                           m_txPsdus[5].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
-                           m_txPsdus[5].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
+    // 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[5].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(trigger);
-    NS_TEST_EXPECT_MSG_EQ(trigger.IsBasic(), true, "Expected a Basic 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");
-    tEnd = m_txPsdus[1].endTx;
-    tStart = m_txPsdus[5].startTx;
+    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");
     if (m_txopLimit > 0)
     {
         NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Basic Trigger Frame sent too late");
+        // Duration/ID still protects until the end of the TXOP
+        auto muRtsNavEnd = m_txPsdus[10].endTx + m_txPsdus[10].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= muRtsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, muRtsNavEnd, "Duration/ID in MU-RTS is too short");
+        NS_TEST_EXPECT_MSG_LT(muRtsNavEnd, navEnd + tolerance, "Duration/ID in MU-RTS is too long");
     }
 
-    // A first STA sends QoS data frames in an HE TB PPDU a SIFS after the reception of the Basic TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[6].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[6].psduMap.size() == 1 &&
-                           m_txPsdus[6].psduMap.begin()->second->GetNMpdus() == 2 &&
-                           m_txPsdus[6].psduMap.begin()->second->GetHeader(0).IsQosData() &&
-                           m_txPsdus[6].psduMap.begin()->second->GetHeader(1).IsQosData()),
+    // 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");
+
+    // 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");
+
+    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");
+
+    // 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");
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[15].psduMap.size() == 1 &&
+                           m_txPsdus[15].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
+                           m_txPsdus[15].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
+                          true,
+                          "Expected a Trigger Frame");
+    m_txPsdus[15].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(trigger);
+    NS_TEST_EXPECT_MSG_EQ(trigger.IsBasic(), true, "Expected a Basic Trigger Frame");
+    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");
+    Time basicNavEnd = m_txPsdus[15].endTx + m_txPsdus[15].psduMap[SU_STA_ID]->GetDuration();
+    // navEnd <= basicNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, basicNavEnd, "Duration/ID in Basic TF is too short");
+    NS_TEST_EXPECT_MSG_LT(basicNavEnd, navEnd + tolerance, "Duration/ID in Basic TF is too long");
+
+    // A first STA sends QoS data frames in a TB PPDU a SIFS after the reception of the Basic TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[16].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[16].psduMap.size() == 1 &&
+                           m_txPsdus[16].psduMap.begin()->second->GetNMpdus() == 2 &&
+                           m_txPsdus[16].psduMap.begin()->second->GetHeader(0).IsQosData() &&
+                           m_txPsdus[16].psduMap.begin()->second->GetHeader(1).IsQosData()),
                           true,
                           "Expected 2 QoS data frames in an HE TB PPDU");
-    tEnd = m_txPsdus[5].endTx;
-    tStart = m_txPsdus[6].startTx;
+    tEnd = m_txPsdus[15].endTx;
+    tStart = m_txPsdus[16].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS data frames in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS data frames in HE TB PPDU sent too late");
-
-    // A second STA sends QoS data frames in an HE TB PPDU a SIFS after the reception of the Basic
-    // TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[7].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[7].psduMap.size() == 1 &&
-                           m_txPsdus[7].psduMap.begin()->second->GetNMpdus() == 2 &&
-                           m_txPsdus[7].psduMap.begin()->second->GetHeader(0).IsQosData() &&
-                           m_txPsdus[7].psduMap.begin()->second->GetHeader(1).IsQosData()),
+    Time qosDataNavEnd = m_txPsdus[16].endTx + m_txPsdus[16].psduMap.begin()->second->GetDuration();
+    // navEnd <= qosDataNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, qosDataNavEnd, "Duration/ID in QoS Data is too short");
+    NS_TEST_EXPECT_MSG_LT(qosDataNavEnd, navEnd + tolerance, "Duration/ID in QoS Data is too long");
+
+    // A second STA sends QoS data frames in a TB PPDU a SIFS after the reception of the Basic TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[17].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[17].psduMap.size() == 1 &&
+                           m_txPsdus[17].psduMap.begin()->second->GetNMpdus() == 2 &&
+                           m_txPsdus[17].psduMap.begin()->second->GetHeader(0).IsQosData() &&
+                           m_txPsdus[17].psduMap.begin()->second->GetHeader(1).IsQosData()),
                           true,
                           "Expected 2 QoS data frames in an HE TB PPDU");
-    tEnd = m_txPsdus[5].endTx;
-    tStart = m_txPsdus[7].startTx;
+    tStart = m_txPsdus[17].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS data frames in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS data frames in HE TB PPDU sent too late");
-
-    // A third STA sends QoS data frames in an HE TB PPDU a SIFS after the reception of the Basic TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[8].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[8].psduMap.size() == 1 &&
-                           m_txPsdus[8].psduMap.begin()->second->GetNMpdus() == 2 &&
-                           m_txPsdus[8].psduMap.begin()->second->GetHeader(0).IsQosData() &&
-                           m_txPsdus[8].psduMap.begin()->second->GetHeader(1).IsQosData()),
+    qosDataNavEnd = m_txPsdus[17].endTx + m_txPsdus[17].psduMap.begin()->second->GetDuration();
+    // navEnd <= qosDataNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, qosDataNavEnd, "Duration/ID in QoS Data is too short");
+    NS_TEST_EXPECT_MSG_LT(qosDataNavEnd, navEnd + tolerance, "Duration/ID in QoS Data is too long");
+
+    // A third STA sends QoS data frames in a TB PPDU a SIFS after the reception of the Basic TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[18].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[18].psduMap.size() == 1 &&
+                           m_txPsdus[18].psduMap.begin()->second->GetNMpdus() == 2 &&
+                           m_txPsdus[18].psduMap.begin()->second->GetHeader(0).IsQosData() &&
+                           m_txPsdus[18].psduMap.begin()->second->GetHeader(1).IsQosData()),
                           true,
                           "Expected 2 QoS data frames in an HE TB PPDU");
-    tEnd = m_txPsdus[5].endTx;
-    tStart = m_txPsdus[8].startTx;
+    tStart = m_txPsdus[18].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS data frames in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS data frames in HE TB PPDU sent too late");
-
-    // A fourth STA sends QoS data frames in an HE TB PPDU a SIFS after the reception of the Basic
-    // TF
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[9].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                           m_txPsdus[9].psduMap.size() == 1 &&
-                           m_txPsdus[9].psduMap.begin()->second->GetNMpdus() == 2 &&
-                           m_txPsdus[9].psduMap.begin()->second->GetHeader(0).IsQosData() &&
-                           m_txPsdus[9].psduMap.begin()->second->GetHeader(1).IsQosData()),
+    qosDataNavEnd = m_txPsdus[18].endTx + m_txPsdus[18].psduMap.begin()->second->GetDuration();
+    // navEnd <= qosDataNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, qosDataNavEnd, "Duration/ID in QoS Data is too short");
+    NS_TEST_EXPECT_MSG_LT(qosDataNavEnd, navEnd + tolerance, "Duration/ID in QoS Data is too long");
+
+    // A fourth STA sends QoS data frames in a TB PPDU a SIFS after the reception of the Basic TF
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[19].txVector.GetPreambleType() == m_tbPreamble &&
+                           m_txPsdus[19].psduMap.size() == 1 &&
+                           m_txPsdus[19].psduMap.begin()->second->GetNMpdus() == 2 &&
+                           m_txPsdus[19].psduMap.begin()->second->GetHeader(0).IsQosData() &&
+                           m_txPsdus[19].psduMap.begin()->second->GetHeader(1).IsQosData()),
                           true,
                           "Expected 2 QoS data frames in an HE TB PPDU");
-    tEnd = m_txPsdus[5].endTx;
-    tStart = m_txPsdus[9].startTx;
+    tStart = m_txPsdus[19].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "QoS data frames in HE TB PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart,
                           tEnd + sifs + tolerance,
                           "QoS data frames in HE TB PPDU sent too late");
+    qosDataNavEnd = m_txPsdus[19].endTx + m_txPsdus[19].psduMap.begin()->second->GetDuration();
+    // navEnd <= qosDataNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, qosDataNavEnd, "Duration/ID in QoS Data is too short");
+    NS_TEST_EXPECT_MSG_LT(qosDataNavEnd, navEnd + tolerance, "Duration/ID in QoS Data is too long");
 
     // the AP sends a Multi-STA Block Ack
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[10].psduMap.size() == 1 &&
-                           m_txPsdus[10].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck() &&
-                           m_txPsdus[10].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
+    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[20].psduMap.size() == 1 &&
+                           m_txPsdus[20].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck() &&
+                           m_txPsdus[20].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
                           true,
                           "Expected a Block Ack");
-    m_txPsdus[10].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(blockAck);
+    m_txPsdus[20].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(blockAck);
     NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA Block Ack");
     NS_TEST_EXPECT_MSG_EQ(blockAck.GetNPerAidTidInfoSubfields(),
                           4,
@@ -878,415 +1221,645 @@
         NS_TEST_EXPECT_MSG_EQ(blockAck.GetAckType(i), true, "Expected All-ack context");
         NS_TEST_EXPECT_MSG_EQ(+blockAck.GetTidInfo(i), 14, "Expected All-ack context");
     }
-    tEnd = m_txPsdus[9].endTx;
-    tStart = m_txPsdus[10].startTx;
+    tEnd = m_txPsdus[19].endTx;
+    tStart = m_txPsdus[20].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Multi-STA Block Ack sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Multi-STA Block Ack sent too late");
-
-    navEnd = m_txPsdus[5].endTx + m_txPsdus[5].psduMap[SU_STA_ID]->GetDuration(); // Basic TF's NAV
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[10].endTx,
-                                "Duration/ID in Basic Trigger Frame is too short");
-    navEnd = m_txPsdus[6].endTx +
-             m_txPsdus[6].psduMap.begin()->second->GetDuration(); // 1st QoS Data frame's NAV
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[10].endTx,
-                                "Duration/ID in 1st QoS Data frame is too short");
-    navEnd = m_txPsdus[7].endTx +
-             m_txPsdus[7].psduMap.begin()->second->GetDuration(); // 2nd QoS Data frame's NAV
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[10].endTx,
-                                "Duration/ID in 2nd QoS Data frame is too short");
-    navEnd = m_txPsdus[8].endTx +
-             m_txPsdus[8].psduMap.begin()->second->GetDuration(); // 3rd QoS Data frame's NAV
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[10].endTx,
-                                "Duration/ID in 3rd QoS Data frame is too short");
-    navEnd = m_txPsdus[9].endTx +
-             m_txPsdus[9].psduMap.begin()->second->GetDuration(); // 4th QoS Data frame's NAV
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                m_txPsdus[10].endTx,
-                                "Duration/ID in 4th QoS Data frame is too short");
+    auto multiStaBaNavEnd = m_txPsdus[20].endTx + m_txPsdus[20].psduMap[SU_STA_ID]->GetDuration();
+    // navEnd <= multiStaBaNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd,
+                                multiStaBaNavEnd,
+                                "Duration/ID in Multi-STA BlockAck is too short");
+    NS_TEST_EXPECT_MSG_LT(multiStaBaNavEnd,
+                          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)
+    {
+        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");
+
+    // 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");
+
+    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");
 
     // the AP sends a DL MU PPDU
-    NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 12, "Expected at least 12 transmitted packet");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].txVector.GetPreambleType(),
-                          WIFI_PREAMBLE_HE_MU,
+    NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 27, "Expected at least 27 transmitted packet");
+    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[26].txVector.GetPreambleType(),
+                          m_dlMuPreamble,
                           "Expected a DL MU PPDU");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].psduMap.size(),
+    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[26].psduMap.size(),
                           4,
                           "Expected 4 PSDUs within the DL MU PPDU");
     // the TX duration cannot exceed the maximum PPDU duration
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(m_txPsdus[11].endTx - m_txPsdus[11].startTx,
-                                GetPpduMaxTime(m_txPsdus[11].txVector.GetPreambleType()),
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(m_txPsdus[26].endTx - m_txPsdus[26].startTx,
+                                GetPpduMaxTime(m_txPsdus[26].txVector.GetPreambleType()),
                                 "TX duration cannot exceed max PPDU duration");
-    for (auto& psdu : m_txPsdus[11].psduMap)
+    for (auto& psdu : m_txPsdus[26].psduMap)
     {
         NS_TEST_EXPECT_MSG_LT_OR_EQ(psdu.second->GetSize(),
                                     m_maxAmpduSize,
                                     "Max A-MPDU size exceeded");
     }
-    tEnd = m_txPsdus[10].endTx;
-    tStart = m_txPsdus[11].startTx;
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(tEnd + ifs, tStart, "DL MU PPDU sent too early");
+    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(tStart, tEnd + sifs + tolerance, "DL MU PPDU sent too late");
 
     // The Duration/ID field is the same for all the PSDUs
-    navEnd = m_txPsdus[11].endTx;
-    for (auto& psdu : m_txPsdus[11].psduMap)
+    auto dlMuNavEnd = m_txPsdus[26].endTx;
+    for (auto& psdu : m_txPsdus[26].psduMap)
     {
-        if (navEnd == m_txPsdus[11].endTx)
+        if (dlMuNavEnd == m_txPsdus[26].endTx)
         {
-            navEnd += psdu.second->GetDuration();
+            dlMuNavEnd += psdu.second->GetDuration();
         }
         else
         {
-            NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].endTx + psdu.second->GetDuration(),
-                                  navEnd,
+            NS_TEST_EXPECT_MSG_EQ(m_txPsdus[26].endTx + psdu.second->GetDuration(),
+                                  dlMuNavEnd,
                                   "Duration/ID must be the same for all PSDUs");
         }
     }
-    NS_TEST_EXPECT_MSG_GT(navEnd,
-                          m_txPsdus[11].endTx,
-                          "Duration/ID of the DL MU PPDU cannot be zero");
+    // navEnd <= dlMuNavEnd < navEnd + tolerance
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, dlMuNavEnd, "Duration/ID in DL MU PPDU is too short");
+    NS_TEST_EXPECT_MSG_LT(dlMuNavEnd, navEnd + tolerance, "Duration/ID in DL MU PPDU is too long");
 
     std::size_t nTxPsdus = 0;
 
     if (m_dlMuAckType == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
     {
         /*
-         *        |----------------------------------------NAV---------------------------------------------->|
-         *        | |--------------------------NAV---------------------------------->| | |
-         * |-------------------NAV--------------->| |                         | | |----NAV---->|
-         * |------|      |-----|      |-----|      |-----|      |-----|      |-----|      |-----|
-         * |-----| |PSDU 1|      |     |      |     |      |     |      |     |      |     |      |
-         * |      |     |
-         * |------|      |Block|      |Block|      |Block|      |Block|      |Block|      |Block|
-         * |Block| |PSDU 2|      | Ack |      | Ack |      | Ack |      | Ack |      | Ack |      |
-         * Ack |      | Ack |
-         * |------|<SIFS>|     |<SIFS>| Req |<SIFS>|     |<SIFS>| Req |<SIFS>|     |<SIFS>| Req
-         * |<SIFS>|     | |PSDU 3|      |     |      |     |      |     |      |     |      |     |
-         * |     |      |     |
-         * |------|      |     |      |     |      |     |      |     |      |     |      |     | |
-         * | |PSDU 4|      |     |      |     |      |     |      |     |      |     |      |     |
-         * |     |
-         * ---------------------------------------------------------------------------------------------------
-         * From: AP       STA 1         AP          STA 2         AP          STA 3         AP STA 4
-         *   To:           AP          STA 2         AP          STA 3         AP          STA 4 AP
+         *        |-----------------------------------------NAV-------------------------------->|
+         *                 |----------------------------------NAV------------------------------>|
+         *                           |-----------------------------NAV------------------------->|
+         *                                   |-------------------------NAV--------------------->|
+         *                                            |--NAV->|        |--NAV->|        |--NAV->|
+         *    ┌───┐    ┌───┐    ┌────┐    ┌──┐    ┌───┐    ┌──┐    ┌───┐    ┌──┐    ┌───┐    ┌──┐
+         *    │   │    │   │    │PSDU│    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │   │    │   │    │  1 │    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │   │    │   │    ├────┤    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │   │    │   │    │PSDU│    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │MU-│    │CTS│    │  2 │    │BA│    │BAR│    │BA│    │BAR│    │BA│    │BAR│    │BA│
+         *    │RTS│SIFS│   │SIFS├────┤SIFS│  │SIFS│   │SIFS│  │SIFS│   │SIFS│  │SIFS│   │SIFS│  │
+         *    │TF │    │x4 │    │PSDU│    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │   │    │   │    │  3 │    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │   │    │   │    ├────┤    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │   │    │   │    │PSDU│    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         *    │   │    │   │    │  4 │    │  │    │   │    │  │    │   │    │  │    │   │    │  │
+         * ───┴───┴────┴───┴────┴────┴────┴──┴────┴───┴────┴──┴────┴───┴────┴──┴────┴───┴────┴──┴──
+         * From: AP     all       AP      STA 1    AP     STA 2     AP      STA 3    AP      STA 4
+         *   To: all    AP        all      AP     STA 2     AP     STA 3     AP     STA 4     AP
          */
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 19, "Expected at least 19 packets");
+        NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 34, "Expected at least 34 packets");
 
         // A first STA sends a Block Ack a SIFS after the reception of the DL MU PPDU
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[12].psduMap.size() == 1 &&
-                               m_txPsdus[12].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[27].psduMap.size() == 1 &&
+                               m_txPsdus[27].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[11].endTx;
-        tStart = m_txPsdus[12].startTx;
+        tEnd = m_txPsdus[26].endTx;
+        tStart = m_txPsdus[27].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "First Block Ack sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "First Block Ack sent too late");
+        Time baNavEnd = m_txPsdus[27].endTx + m_txPsdus[27].psduMap[SU_STA_ID]->GetDuration();
+        // The NAV of the first BlockAck, being a response to a QoS Data frame, matches the NAV
+        // set by the MU-RTS TF.
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd,
+                                    baNavEnd,
+                                    "Duration/ID in 1st BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in 1st BlockAck is too long");
 
         // the AP transmits a Block Ack Request an IFS after the reception of the Block Ack
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[13].psduMap.size() == 1 &&
-                               m_txPsdus[13].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAckReq()),
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[28].psduMap.size() == 1 &&
+                               m_txPsdus[28].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAckReq()),
                               true,
                               "Expected a Block Ack Request");
-        tEnd = m_txPsdus[12].endTx;
-        tStart = m_txPsdus[13].startTx;
+        tEnd = m_txPsdus[27].endTx;
+        tStart = m_txPsdus[28].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "First Block Ack Request sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "First Block Ack Request sent too late");
+        // under single protection setting (TXOP limit equal to zero), the NAV of the BlockAckReq
+        // only covers the following BlockAck response; under multiple protection setting, the
+        // NAV of the BlockAckReq matches the NAV set by the MU-RTS TF
+        Time barNavEnd = m_txPsdus[28].endTx + m_txPsdus[28].psduMap[SU_STA_ID]->GetDuration();
+        if (m_txopLimit > 0)
+        {
+            // navEnd <= barNavEnd < navEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd,
+                                        barNavEnd,
+                                        "Duration/ID in BlockAckReq is too short");
+            NS_TEST_EXPECT_MSG_LT(barNavEnd,
+                                  navEnd + tolerance,
+                                  "Duration/ID in BlockAckReq is too long");
+        }
 
         // A second STA sends a Block Ack a SIFS after the reception of the Block Ack Request
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[14].psduMap.size() == 1 &&
-                               m_txPsdus[14].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[29].psduMap.size() == 1 &&
+                               m_txPsdus[29].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[13].endTx;
-        tStart = m_txPsdus[14].startTx;
+        tEnd = m_txPsdus[28].endTx;
+        tStart = m_txPsdus[29].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Second Block Ack sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Second Block Ack sent too late");
+        baNavEnd = m_txPsdus[29].endTx + m_txPsdus[29].psduMap[SU_STA_ID]->GetDuration();
+        if (m_txopLimit > 0)
+        {
+            // navEnd <= baNavEnd < navEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck is too short");
+            NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                                  navEnd + tolerance,
+                                  "Duration/ID in BlockAck is too long");
+        }
+        else
+        {
+            // barNavEnd <= baNavEnd < barNavEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(barNavEnd,
+                                        baNavEnd,
+                                        "Duration/ID in BlockAck is too short");
+            NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                                  barNavEnd + tolerance,
+                                  "Duration/ID in BlockAck is too long");
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[29].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
 
         // the AP transmits a Block Ack Request an IFS after the reception of the Block Ack
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[15].psduMap.size() == 1 &&
-                               m_txPsdus[15].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAckReq()),
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[30].psduMap.size() == 1 &&
+                               m_txPsdus[30].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAckReq()),
                               true,
                               "Expected a Block Ack Request");
-        tEnd = m_txPsdus[14].endTx;
-        tStart = m_txPsdus[15].startTx;
+        tEnd = m_txPsdus[29].endTx;
+        tStart = m_txPsdus[30].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Second Block Ack Request sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Second Block Ack Request sent too late");
+        // under single protection setting (TXOP limit equal to zero), the NAV of the BlockAckReq
+        // only covers the following BlockAck response; under multiple protection setting, the
+        // NAV of the BlockAckReq matches the NAV set by the MU-RTS TF
+        barNavEnd = m_txPsdus[30].endTx + m_txPsdus[30].psduMap[SU_STA_ID]->GetDuration();
+        if (m_txopLimit > 0)
+        {
+            // navEnd <= barNavEnd < navEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd,
+                                        barNavEnd,
+                                        "Duration/ID in BlockAckReq is too short");
+            NS_TEST_EXPECT_MSG_LT(barNavEnd,
+                                  navEnd + tolerance,
+                                  "Duration/ID in BlockAckReq is too long");
+        }
 
         // A third STA sends a Block Ack a SIFS after the reception of the Block Ack Request
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[16].psduMap.size() == 1 &&
-                               m_txPsdus[16].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[31].psduMap.size() == 1 &&
+                               m_txPsdus[31].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[15].endTx;
-        tStart = m_txPsdus[16].startTx;
+        tEnd = m_txPsdus[30].endTx;
+        tStart = m_txPsdus[31].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Third Block Ack sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Third Block Ack sent too late");
+        baNavEnd = m_txPsdus[31].endTx + m_txPsdus[31].psduMap[SU_STA_ID]->GetDuration();
+        if (m_txopLimit > 0)
+        {
+            // navEnd <= baNavEnd < navEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck is too short");
+            NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                                  navEnd + tolerance,
+                                  "Duration/ID in BlockAck is too long");
+        }
+        else
+        {
+            // barNavEnd <= baNavEnd < barNavEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(barNavEnd,
+                                        baNavEnd,
+                                        "Duration/ID in BlockAck is too short");
+            NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                                  barNavEnd + tolerance,
+                                  "Duration/ID in BlockAck is too long");
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[31].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
 
         // the AP transmits a Block Ack Request an IFS after the reception of the Block Ack
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[17].psduMap.size() == 1 &&
-                               m_txPsdus[17].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAckReq()),
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[32].psduMap.size() == 1 &&
+                               m_txPsdus[32].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAckReq()),
                               true,
                               "Expected a Block Ack Request");
-        tEnd = m_txPsdus[16].endTx;
-        tStart = m_txPsdus[17].startTx;
+        tEnd = m_txPsdus[31].endTx;
+        tStart = m_txPsdus[32].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Third Block Ack Request sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Third Block Ack Request sent too late");
+        // under single protection setting (TXOP limit equal to zero), the NAV of the BlockAckReq
+        // only covers the following BlockAck response; under multiple protection setting, the
+        // NAV of the BlockAckReq matches the NAV set by the MU-RTS TF
+        barNavEnd = m_txPsdus[32].endTx + m_txPsdus[32].psduMap[SU_STA_ID]->GetDuration();
+        if (m_txopLimit > 0)
+        {
+            // navEnd <= barNavEnd < navEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd,
+                                        barNavEnd,
+                                        "Duration/ID in BlockAckReq is too short");
+            NS_TEST_EXPECT_MSG_LT(barNavEnd,
+                                  navEnd + tolerance,
+                                  "Duration/ID in BlockAckReq is too long");
+        }
 
         // A fourth STA sends a Block Ack a SIFS after the reception of the Block Ack Request
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[18].psduMap.size() == 1 &&
-                               m_txPsdus[18].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[33].psduMap.size() == 1 &&
+                               m_txPsdus[33].psduMap[SU_STA_ID]->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[17].endTx;
-        tStart = m_txPsdus[18].startTx;
+        tEnd = m_txPsdus[32].endTx;
+        tStart = m_txPsdus[33].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Fourth Block Ack sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Fourth Block Ack sent too late");
-
+        baNavEnd = m_txPsdus[33].endTx + m_txPsdus[33].psduMap[SU_STA_ID]->GetDuration();
         if (m_txopLimit > 0)
         {
-            // DL MU PPDU's NAV
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[18].endTx,
-                                        "Duration/ID in the DL MU PPDU is too short");
-            // 1st BlockAckReq's NAV
-            navEnd = m_txPsdus[13].endTx + m_txPsdus[13].psduMap[SU_STA_ID]->GetDuration();
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[18].endTx,
-                                        "Duration/ID in the 1st BlockAckReq is too short");
-            // 2nd BlockAckReq's NAV
-            navEnd = m_txPsdus[15].endTx + m_txPsdus[15].psduMap[SU_STA_ID]->GetDuration();
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[18].endTx,
-                                        "Duration/ID in the 2nd BlockAckReq is too short");
-            // 3rd BlockAckReq's NAV
-            navEnd = m_txPsdus[17].endTx + m_txPsdus[17].psduMap[SU_STA_ID]->GetDuration();
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[18].endTx,
-                                        "Duration/ID in the 3rd BlockAckReq is too short");
+            // navEnd <= baNavEnd < navEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck is too short");
+            NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                                  navEnd + tolerance,
+                                  "Duration/ID in BlockAck is too long");
         }
-        nTxPsdus = 19;
+        else
+        {
+            // barNavEnd <= baNavEnd < barNavEnd + tolerance
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(barNavEnd,
+                                        baNavEnd,
+                                        "Duration/ID in BlockAck is too short");
+            NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                                  barNavEnd + tolerance,
+                                  "Duration/ID in BlockAck is too long");
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[33].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
+
+        nTxPsdus = 34;
     }
     else if (m_dlMuAckType == WifiAcknowledgment::DL_MU_TF_MU_BAR)
     {
         /*
-         *             |----------------NAV--------------->|
-         *             |                |--------NAV------>|
-         *      |------|      |---------|      |-----------|
-         *      |PSDU 1|      |         |      |Block Ack 1|
-         *      |------|      | MU-BAR  |      |-----------|
-         *      |PSDU 2|      | Trigger |      |Block Ack 2|
-         *      |------|<SIFS>|  Frame  |<SIFS>|-----------|
-         *      |PSDU 3|      |         |      |Block Ack 3|
-         *      |------|      |         |      |-----------|
-         *      |PSDU 4|      |         |      |Block Ack 4|
-         * --------------------------------------------------
-         * From:   AP            AP
-         *   To:                all                 AP
+         *          |---------------------NAV------------------------>|
+         *                   |-------------------NAV----------------->|
+         *                               |---------------NAV--------->|
+         *                                            |------NAV----->|
+         *      ┌───┐    ┌───┐    ┌──────┐    ┌───────┐    ┌──────────┐
+         *      │   │    │   │    │PSDU 1│    │       │    │BlockAck 1│
+         *      │   │    │   │    ├──────┤    │MU-BAR │    ├──────────┤
+         *      │MU-│    │CTS│    │PSDU 2│    │Trigger│    │BlockAck 2│
+         *      │RTS│SIFS│   │SIFS├──────┤SIFS│ Frame │SIFS├──────────┤
+         *      │TF │    │x4 │    │PSDU 3│    │       │    │BlockAck 3│
+         *      │   │    │   │    ├──────┤    │       │    ├──────────┤
+         *      │   │    │   │    │PSDU 4│    │       │    │BlockAck 4│
+         * -----┴───┴────┴───┴────┴──────┴────┴───────┴────┴──────────┴───
+         * From: AP       all        AP          AP            all
+         *   To: all      AP         all         all           AP
          */
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 17, "Expected at least 17 packets");
+        NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 32, "Expected at least 32 packets");
 
-        // the AP transmits a MU-BAR Trigger Frame an IFS after the transmission of the DL MU PPDU
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[12].psduMap.size() == 1 &&
-                               m_txPsdus[12].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger()),
+        // the AP transmits a MU-BAR Trigger Frame a SIFS after the transmission of the DL MU PPDU
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[27].psduMap.size() == 1 &&
+                               m_txPsdus[27].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger()),
                               true,
                               "Expected a MU-BAR Trigger Frame");
-        tEnd = m_txPsdus[11].endTx;
-        tStart = m_txPsdus[12].startTx;
+        tEnd = m_txPsdus[26].endTx;
+        tStart = m_txPsdus[27].startTx;
         NS_TEST_EXPECT_MSG_EQ(tStart, tEnd + sifs, "MU-BAR Trigger Frame sent at wrong time");
-
-        // A first STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        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->GetHeader(0).IsBlockAck()),
+        auto muBarNavEnd = m_txPsdus[27].endTx + m_txPsdus[27].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= muBarNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd,
+                                    muBarNavEnd,
+                                    "Duration/ID in MU-BAR Trigger Frame is too short");
+        NS_TEST_EXPECT_MSG_LT(muBarNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in MU-BAR Trigger Frame is too long");
+
+        // A first STA sends a Block Ack in a TB PPDU a SIFS after the reception of the MU-BAR
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[28].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[28].psduMap.size() == 1 &&
+                               m_txPsdus[28].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[12].endTx;
-        tStart = m_txPsdus[13].startTx;
+        tEnd = m_txPsdus[27].endTx;
+        tStart = m_txPsdus[28].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
+        Time baNavEnd = m_txPsdus[28].endTx + m_txPsdus[28].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd, navEnd + tolerance, "Duration/ID in BlockAck is too long");
+        if (m_txopLimit == 0)
+        {
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[28].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
 
-        // A second STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        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->GetHeader(0).IsBlockAck()),
+        // A second STA sends a Block Ack in a TB PPDU a SIFS after the reception of the MU-BAR
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[29].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[29].psduMap.size() == 1 &&
+                               m_txPsdus[29].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[12].endTx;
-        tStart = m_txPsdus[14].startTx;
+        tStart = m_txPsdus[29].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
+        baNavEnd = m_txPsdus[29].endTx + m_txPsdus[29].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in 1st BlockAck is too long");
+        if (m_txopLimit == 0)
+        {
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[29].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
 
-        // A third STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[15].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                               m_txPsdus[15].psduMap.size() == 1 &&
-                               m_txPsdus[15].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
+        // A third STA sends a Block Ack in a TB PPDU a SIFS after the reception of the MU-BAR
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[30].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[30].psduMap.size() == 1 &&
+                               m_txPsdus[30].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[12].endTx;
-        tStart = m_txPsdus[15].startTx;
+        tStart = m_txPsdus[30].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
+        baNavEnd = m_txPsdus[30].endTx + m_txPsdus[30].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in 1st BlockAck is too long");
+        if (m_txopLimit == 0)
+        {
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[30].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
 
-        // A fourth STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[16].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                               m_txPsdus[16].psduMap.size() == 1 &&
-                               m_txPsdus[16].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
+        // A fourth STA sends a Block Ack in a TB PPDU a SIFS after the reception of the MU-BAR
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[31].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[31].psduMap.size() == 1 &&
+                               m_txPsdus[31].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[12].endTx;
-        tStart = m_txPsdus[16].startTx;
+        tStart = m_txPsdus[31].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
-
-        if (m_txopLimit > 0)
+        baNavEnd = m_txPsdus[31].endTx + m_txPsdus[31].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in 1st BlockAck is too long");
+        if (m_txopLimit == 0)
         {
-            // DL MU PPDU's NAV
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[13].endTx,
-                                        "Duration/ID in the DL MU PPDU is too short");
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[14].endTx,
-                                        "Duration/ID in the DL MU PPDU is too short");
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[15].endTx,
-                                        "Duration/ID in the DL MU PPDU is too short");
-            NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                        m_txPsdus[16].endTx,
-                                        "Duration/ID in the DL MU PPDU is too short");
-        }
-        // MU-BAR Trigger Frame's NAV
-        navEnd = m_txPsdus[12].endTx + m_txPsdus[12].psduMap[SU_STA_ID]->GetDuration();
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[13].endTx,
-                                    "Duration/ID in the MU-BAR Trigger Frame is too short");
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[14].endTx,
-                                    "Duration/ID in the MU-BAR Trigger Frame is too short");
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[15].endTx,
-                                    "Duration/ID in the MU-BAR Trigger Frame is too short");
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[16].endTx,
-                                    "Duration/ID in the MU-BAR Trigger Frame is too short");
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[31].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
 
-        nTxPsdus = 17;
+        nTxPsdus = 32;
     }
     else if (m_dlMuAckType == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
     {
         /*
-         *                         |--------NAV------>|
-         *      |------|-----------|      |-----------|
-         *      |PSDU 1|MU-BAR TF 1|      |Block Ack 1|
-         *      |------------------|      |-----------|
-         *      |PSDU 2|MU-BAR TF 2|      |Block Ack 2|
-         *      |------------------|<SIFS>|-----------|
-         *      |PSDU 3|MU-BAR TF 3|      |Block Ack 3|
-         *      |------------------|      |-----------|
-         *      |PSDU 4|MU-BAR TF 4|      |Block Ack 4|
-         * --------------------------------------------------
-         * From:         AP
-         *   To:                               AP
+         *          |---------------------NAV----------------------->|
+         *                   |-------------------NAV---------------->|
+         *                                           |------NAV----->|
+         *      ┌───┐    ┌───┐    ┌──────┬───────────┐    ┌──────────┐
+         *      │   │    │   │    │PSDU 1│MU-BAR TF 1│    │BlockAck 1│
+         *      │   │    │   │    ├──────┼───────────┤    ├──────────┤
+         *      │MU-│    │CTS│    │PSDU 2│MU-BAR TF 2│    │BlockAck 2│
+         *      │RTS│SIFS│   │SIFS├──────┼───────────┤SIFS├──────────┤
+         *      │TF │    │x4 │    │PSDU 3│MU-BAR TF 3│    │BlockAck 3│
+         *      │   │    │   │    ├──────┼───────────┤    ├──────────┤
+         *      │   │    │   │    │PSDU 4│MU-BAR TF 4│    │BlockAck 4│
+         * -----┴───┴────┴───┴────┴──────┴───────────┴────┴──────────┴───
+         * From: AP       all            AP                    all
+         *   To: all      AP             all                   AP
          */
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(m_txPsdus.size(), 16, "Expected at least 16 packets");
+        NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 31, "Expected at least 31 packets");
 
         // The last MPDU in each PSDU is a MU-BAR Trigger Frame
-        for (auto& psdu : m_txPsdus[11].psduMap)
+        for (auto& psdu : m_txPsdus[26].psduMap)
         {
-            NS_TEST_EXPECT_MSG_EQ((*(--psdu.second->end()))->GetHeader().IsTrigger(),
+            NS_TEST_EXPECT_MSG_EQ((*std::prev(psdu.second->end()))->GetHeader().IsTrigger(),
                                   true,
                                   "Expected an aggregated MU-BAR Trigger Frame");
         }
 
-        // A first STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        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->GetHeader(0).IsBlockAck()),
+        // A first STA sends a Block Ack in a TB PPDU a SIFS after the reception of the DL MU PPDU
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[27].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[27].psduMap.size() == 1 &&
+                               m_txPsdus[27].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[11].endTx;
-        tStart = m_txPsdus[12].startTx;
+        tEnd = m_txPsdus[26].endTx;
+        tStart = m_txPsdus[27].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[12].endTx,
-                                    "Duration/ID in the DL MU PPDU is too short");
-
-        // A second STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        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->GetHeader(0).IsBlockAck()),
+        Time baNavEnd = m_txPsdus[27].endTx + m_txPsdus[27].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd, navEnd + tolerance, "Duration/ID in BlockAck is too long");
+        if (m_txopLimit == 0)
+        {
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[27].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
+
+        // A second STA sends a Block Ack in a TB PPDU a SIFS after the reception of the DL MU PPDU
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[28].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[28].psduMap.size() == 1 &&
+                               m_txPsdus[28].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[11].endTx;
-        tStart = m_txPsdus[13].startTx;
+        tStart = m_txPsdus[28].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[13].endTx,
-                                    "Duration/ID in the DL MU PPDU is too short");
-
-        // A third STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        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->GetHeader(0).IsBlockAck()),
+        baNavEnd = m_txPsdus[28].endTx + m_txPsdus[28].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd, navEnd + tolerance, "Duration/ID in BlockAck is too long");
+        if (m_txopLimit == 0)
+        {
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[28].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
+
+        // A third STA sends a Block Ack in a TB PPDU a SIFS after the reception of the DL MU PPDU
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[29].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[29].psduMap.size() == 1 &&
+                               m_txPsdus[29].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[11].endTx;
-        tStart = m_txPsdus[14].startTx;
+        tStart = m_txPsdus[29].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[14].endTx,
-                                    "Duration/ID in the DL MU PPDU is too short");
-
-        // A fourth STA sends a Block Ack in an HE TB PPDU a SIFS after the reception of the DL MU
-        // PPDU
-        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[15].txVector.GetPreambleType() == WIFI_PREAMBLE_HE_TB &&
-                               m_txPsdus[15].psduMap.size() == 1 &&
-                               m_txPsdus[15].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
+        baNavEnd = m_txPsdus[29].endTx + m_txPsdus[29].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd, navEnd + tolerance, "Duration/ID in BlockAck is too long");
+        if (m_txopLimit == 0)
+        {
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[29].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
+
+        // A fourth STA sends a Block Ack in a TB PPDU a SIFS after the reception of the DL MU PPDU
+        NS_TEST_EXPECT_MSG_EQ((m_txPsdus[30].txVector.GetPreambleType() == m_tbPreamble &&
+                               m_txPsdus[30].psduMap.size() == 1 &&
+                               m_txPsdus[30].psduMap.begin()->second->GetHeader(0).IsBlockAck()),
                               true,
                               "Expected a Block Ack");
-        tEnd = m_txPsdus[11].endTx;
-        tStart = m_txPsdus[15].startTx;
+        tStart = m_txPsdus[30].startTx;
         NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Block Ack in HE TB PPDU sent too early");
         NS_TEST_EXPECT_MSG_LT(tStart,
                               tEnd + sifs + tolerance,
                               "Block Ack in HE TB PPDU sent too late");
-        NS_TEST_EXPECT_MSG_GT_OR_EQ(navEnd + tolerance,
-                                    m_txPsdus[15].endTx,
-                                    "Duration/ID in the DL MU PPDU is too short");
+        baNavEnd = m_txPsdus[30].endTx + m_txPsdus[30].psduMap.begin()->second->GetDuration();
+        // navEnd <= baNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, baNavEnd, "Duration/ID in BlockAck frame is too short");
+        NS_TEST_EXPECT_MSG_LT(baNavEnd, navEnd + tolerance, "Duration/ID in BlockAck is too long");
+        if (m_txopLimit == 0)
+        {
+            NS_TEST_EXPECT_MSG_EQ(baNavEnd,
+                                  m_txPsdus[30].endTx,
+                                  "Expected null Duration/ID for BlockAck");
+        }
 
-        nTxPsdus = 16;
+        nTxPsdus = 31;
     }
 
     NS_TEST_EXPECT_MSG_EQ(m_received,
@@ -1301,6 +1874,7 @@
         for (std::size_t i = nTxPsdus; i < m_txPsdus.size(); ++i)
         {
             if (m_txPsdus[i].psduMap.size() == 1 &&
+                !m_txPsdus[i].psduMap.begin()->second->GetHeader(0).IsCts() &&
                 m_txPsdus[i].psduMap.begin()->second->GetHeader(0).GetAddr2() !=
                     m_apDevice->GetAddress() &&
                 !m_txPsdus[i].txVector.IsUlMu())
@@ -1335,13 +1909,16 @@
     uint64_t previousRun = RngSeedManager::GetRun();
     Config::SetGlobal("RngSeed", UintegerValue(1));
     Config::SetGlobal("RngRun", UintegerValue(1));
-    int64_t streamNumber = 20;
+    int64_t streamNumber = 10;
 
     NodeContainer wifiApNode;
     wifiApNode.Create(1);
 
-    NodeContainer wifiStaNodes;
-    wifiStaNodes.Create(m_nStations);
+    NodeContainer wifiOldStaNodes;
+    NodeContainer wifiNewStaNodes;
+    wifiOldStaNodes.Create(m_nStations / 2);
+    wifiNewStaNodes.Create(m_nStations - m_nStations / 2);
+    NodeContainer wifiStaNodes(wifiOldStaNodes, wifiNewStaNodes);
 
     Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
     Ptr<FriisPropagationLossModel> lossModel = CreateObject<FriisPropagationLossModel>();
@@ -1372,6 +1949,7 @@
         NS_ABORT_MSG("Invalid channel bandwidth (must be 20, 40, 80 or 160)");
     }
 
+    Config::SetDefault("ns3::WifiDefaultProtectionManager::EnableMuRts", BooleanValue(true));
     Config::SetDefault("ns3::HeConfiguration::MuBeAifsn",
                        UintegerValue(m_muEdcaParameterSet.muAifsn));
     Config::SetDefault("ns3::HeConfiguration::MuBeCwMin",
@@ -1412,8 +1990,11 @@
     Config::SetDefault("ns3::WifiMacQueue::MaxDelay", TimeValue(Seconds(2)));
 
     WifiHelper wifi;
-    wifi.SetStandard(WIFI_STANDARD_80211ax);
-    wifi.SetRemoteStationManager("ns3::IdealWifiManager");
+    wifi.SetStandard(m_scenario == WifiOfdmaScenario::EHT ? WIFI_STANDARD_80211be
+                                                          : WIFI_STANDARD_80211ax);
+    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
+                                 "DataMode",
+                                 StringValue("HeMcs11"));
     wifi.ConfigHeOptions("MuBeAifsn",
                          UintegerValue(m_muEdcaParameterSet.muAifsn),
                          "MuBeCwMin",
@@ -1466,15 +2047,22 @@
                 "ActiveProbing",
                 BooleanValue(false));
 
-    m_staDevices = wifi.Install(phy, mac, wifiStaNodes);
+    m_staDevices = wifi.Install(phy, mac, wifiOldStaNodes);
+
+    wifi.SetStandard(m_scenario == WifiOfdmaScenario::HE ? WIFI_STANDARD_80211ax
+                                                         : WIFI_STANDARD_80211be);
+    m_staDevices = NetDeviceContainer(m_staDevices, wifi.Install(phy, mac, wifiNewStaNodes));
 
     mac.SetType("ns3::ApWifiMac", "BeaconGeneration", BooleanValue(true));
-    mac.SetMultiUserScheduler("ns3::TestMultiUserScheduler",
-                              // request channel access at 1.5s
-                              "AccessReqInterval",
-                              TimeValue(Seconds(1.5)),
-                              "DelayAccessReqUponAccess",
-                              BooleanValue(false));
+    mac.SetMultiUserScheduler(
+        "ns3::TestMultiUserScheduler",
+        "ModulationClass",
+        EnumValue(m_scenario == WifiOfdmaScenario::EHT ? WIFI_MOD_CLASS_EHT : WIFI_MOD_CLASS_HE),
+        // request channel access at 1.5s
+        "AccessReqInterval",
+        TimeValue(Seconds(1.5)),
+        "DelayAccessReqUponAccess",
+        BooleanValue(false));
     mac.SetAckManager("ns3::WifiDefaultAckManager",
                       "DlMuAckSequenceType",
                       EnumValue(m_dlMuAckType));
@@ -1628,48 +2216,58 @@
                                                  {0, 127, 2047, 100} /* EDCA disabled */,
                                                  {10, 127, 2047, 100} /* worse parameters */})
     {
-        AddTestCase(new OfdmaAckSequenceTest(20,
-                                             WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE,
-                                             10000,
-                                             5440,
-                                             15,
-                                             muEdcaParameterSet),
-                    TestCase::QUICK);
-        AddTestCase(new OfdmaAckSequenceTest(20,
-                                             WifiAcknowledgment::DL_MU_AGGREGATE_TF,
-                                             10000,
-                                             5440,
-                                             15,
-                                             muEdcaParameterSet),
-                    TestCase::QUICK);
-        AddTestCase(new OfdmaAckSequenceTest(20,
-                                             WifiAcknowledgment::DL_MU_TF_MU_BAR,
-                                             10000,
-                                             5440,
-                                             15,
-                                             muEdcaParameterSet),
-                    TestCase::QUICK);
-        AddTestCase(new OfdmaAckSequenceTest(40,
-                                             WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE,
-                                             10000,
-                                             0,
-                                             15,
-                                             muEdcaParameterSet),
-                    TestCase::QUICK);
-        AddTestCase(new OfdmaAckSequenceTest(40,
-                                             WifiAcknowledgment::DL_MU_AGGREGATE_TF,
-                                             10000,
-                                             0,
-                                             15,
-                                             muEdcaParameterSet),
-                    TestCase::QUICK);
-        AddTestCase(new OfdmaAckSequenceTest(40,
-                                             WifiAcknowledgment::DL_MU_TF_MU_BAR,
-                                             10000,
-                                             0,
-                                             15,
-                                             muEdcaParameterSet),
-                    TestCase::QUICK);
+        for (const auto scenario :
+             {WifiOfdmaScenario::HE, WifiOfdmaScenario::HE_EHT, WifiOfdmaScenario::EHT})
+        {
+            AddTestCase(new OfdmaAckSequenceTest(20,
+                                                 WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE,
+                                                 10000,
+                                                 5504,
+                                                 15,
+                                                 muEdcaParameterSet,
+                                                 scenario),
+                        TestCase::QUICK);
+            AddTestCase(new OfdmaAckSequenceTest(20,
+                                                 WifiAcknowledgment::DL_MU_AGGREGATE_TF,
+                                                 10000,
+                                                 5504,
+                                                 15,
+                                                 muEdcaParameterSet,
+                                                 scenario),
+                        TestCase::QUICK);
+            AddTestCase(new OfdmaAckSequenceTest(20,
+                                                 WifiAcknowledgment::DL_MU_TF_MU_BAR,
+                                                 10000,
+                                                 5504,
+                                                 15,
+                                                 muEdcaParameterSet,
+                                                 scenario),
+                        TestCase::QUICK);
+            AddTestCase(new OfdmaAckSequenceTest(40,
+                                                 WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE,
+                                                 10000,
+                                                 0,
+                                                 15,
+                                                 muEdcaParameterSet,
+                                                 scenario),
+                        TestCase::QUICK);
+            AddTestCase(new OfdmaAckSequenceTest(40,
+                                                 WifiAcknowledgment::DL_MU_AGGREGATE_TF,
+                                                 10000,
+                                                 0,
+                                                 15,
+                                                 muEdcaParameterSet,
+                                                 scenario),
+                        TestCase::QUICK);
+            AddTestCase(new OfdmaAckSequenceTest(40,
+                                                 WifiAcknowledgment::DL_MU_TF_MU_BAR,
+                                                 10000,
+                                                 0,
+                                                 15,
+                                                 muEdcaParameterSet,
+                                                 scenario),
+                        TestCase::QUICK);
+        }
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-mac-queue-test.cc ns-3.38/src/wifi/test/wifi-mac-queue-test.cc
--- ns-3.37/src/wifi/test/wifi-mac-queue-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-mac-queue-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -22,6 +22,8 @@
 #include "ns3/test.h"
 #include "ns3/wifi-mac-queue.h"
 
+#include <algorithm>
+
 using namespace ns3;
 
 /**
@@ -124,6 +126,312 @@
  * \ingroup wifi-test
  * \ingroup tests
  *
+ * \brief Test extraction of expired MPDUs from MAC queue container
+ *
+ * This test verifies the correctness of the WifiMacQueueContainer methods
+ * (ExtractExpiredMpdus and ExtractAllExpiredMpdus) that extract MPDUs with
+ * expired lifetime from the MAC queue container.
+ */
+class WifiExtractExpiredMpdusTest : public TestCase
+{
+  public:
+    WifiExtractExpiredMpdusTest();
+
+  private:
+    void DoRun() override;
+
+    /**
+     * Enqueue a new MPDU into the container.
+     *
+     * \param rxAddr Receiver Address of the MPDU
+     * \param inflight whether the MPDU is inflight
+     * \param expiryTime the expity time for the MPDU
+     */
+    void Enqueue(Mac48Address rxAddr, bool inflight, Time expiryTime);
+
+    WifiMacQueueContainer m_container; //!< MAC queue container
+    uint16_t m_currentSeqNo{0};        //!< sequence number of current MPDU
+    Mac48Address m_txAddr;             //!< Transmitter Address of MPDUs
+};
+
+WifiExtractExpiredMpdusTest::WifiExtractExpiredMpdusTest()
+    : TestCase("Test extraction of expired MPDUs from MAC queue container")
+{
+}
+
+void
+WifiExtractExpiredMpdusTest::Enqueue(Mac48Address rxAddr, bool inflight, Time expiryTime)
+{
+    WifiMacHeader header(WIFI_MAC_QOSDATA);
+    header.SetAddr1(rxAddr);
+    header.SetAddr2(m_txAddr);
+    header.SetQosTid(0);
+    header.SetSequenceNumber(m_currentSeqNo++);
+    auto mpdu = Create<WifiMpdu>(Create<Packet>(), header);
+
+    auto queueId = WifiMacQueueContainer::GetQueueId(mpdu);
+    auto elemIt = m_container.insert(m_container.GetQueue(queueId).cend(), mpdu);
+    elemIt->expiryTime = expiryTime;
+    if (inflight)
+    {
+        elemIt->inflights.emplace(0, mpdu);
+    }
+    elemIt->deleter = [](auto mpdu) {};
+}
+
+void
+WifiExtractExpiredMpdusTest::DoRun()
+{
+    m_txAddr = Mac48Address::Allocate();
+    auto rxAddr1 = Mac48Address::Allocate();
+    auto rxAddr2 = Mac48Address::Allocate();
+
+    /**
+     * At simulation time 25ms:
+     *
+     * Container queue for rxAddr1
+     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
+     * │Exp│Exp│Exp│Exp│   │   │   │   │   │   │   │
+     * │Inf│   │Inf│   │Inf│   │Inf│   │   │   │   │
+     * │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │10 │
+     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
+     *
+     * Container queue for rxAddr2
+     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
+     * │Exp│Exp│Exp│   │   │   │   │   │   │
+     * │   │Inf│Inf│   │Inf│Inf│   │   │   │
+     * │11 │12 │13 │14 │15 │16 │17 │18 │19 │
+     * └───┴───┴───┴───┴───┴───┴───┴───┴───┘
+     */
+    Enqueue(rxAddr1, true, MilliSeconds(10));
+    Enqueue(rxAddr1, false, MilliSeconds(10));
+    Enqueue(rxAddr1, true, MilliSeconds(12));
+    Enqueue(rxAddr1, false, MilliSeconds(15));
+    Enqueue(rxAddr1, true, MilliSeconds(30));
+    Enqueue(rxAddr1, false, MilliSeconds(30));
+    Enqueue(rxAddr1, true, MilliSeconds(35));
+    Enqueue(rxAddr1, false, MilliSeconds(35));
+    Enqueue(rxAddr1, false, MilliSeconds(40));
+    Enqueue(rxAddr1, false, MilliSeconds(75));
+    Enqueue(rxAddr1, false, MilliSeconds(75));
+
+    Enqueue(rxAddr2, false, MilliSeconds(11));
+    Enqueue(rxAddr2, true, MilliSeconds(11));
+    Enqueue(rxAddr2, true, MilliSeconds(13));
+    Enqueue(rxAddr2, false, MilliSeconds(30));
+    Enqueue(rxAddr2, true, MilliSeconds(35));
+    Enqueue(rxAddr2, true, MilliSeconds(40));
+    Enqueue(rxAddr2, false, MilliSeconds(40));
+    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};
+
+    Simulator::Schedule(MilliSeconds(25), [&]() {
+        /**
+         * Extract expired MPDUs from container queue 1
+         */
+        auto [first1, last1] = m_container.ExtractExpiredMpdus(queueId1);
+        // MPDU 0 not extracted because inflight, MPDU 1 extracted
+        NS_TEST_EXPECT_MSG_EQ((first1 != last1), true, "Expected one MPDU extracted");
+        NS_TEST_EXPECT_MSG_EQ(first1->mpdu->GetHeader().GetSequenceNumber(),
+                              1,
+                              "Unexpected extracted MPDU");
+        first1++;
+        // MPDU 2 not extracted because inflight, MPDU 3 extracted
+        NS_TEST_EXPECT_MSG_EQ((first1 != last1), true, "Expected two MPDUs extracted");
+        NS_TEST_EXPECT_MSG_EQ(first1->mpdu->GetHeader().GetSequenceNumber(),
+                              3,
+                              "Unexpected extracted MPDU");
+        first1++;
+        // No other expired MPDU
+        NS_TEST_EXPECT_MSG_EQ((first1 == last1), true, "Did not expect other expired MPDUs");
+
+        // If we try to extract expired MPDUs again, the returned set is empty
+        {
+            auto [first, last] = m_container.ExtractExpiredMpdus(queueId1);
+            NS_TEST_EXPECT_MSG_EQ((first == last), true, "Did not expect other expired MPDUs");
+        }
+
+        /**
+         * Extract expired MPDUs from container queue 2
+         */
+        auto [first2, last2] = m_container.ExtractExpiredMpdus(queueId2);
+        // MPDU 11 extracted
+        NS_TEST_EXPECT_MSG_EQ((first2 != last2), true, "Expected one MPDU extracted");
+        NS_TEST_EXPECT_MSG_EQ(first2->mpdu->GetHeader().GetSequenceNumber(),
+                              11,
+                              "Unexpected extracted MPDU");
+        first2++;
+        // MPDU 12 and 13 not extracted because inflight, no other expired MPDU
+        NS_TEST_EXPECT_MSG_EQ((first2 == last2), true, "Did not expect other expired MPDUs");
+
+        // If we try to extract expired MPDUs again, the returned set is empty
+        {
+            auto [first, last] = m_container.ExtractExpiredMpdus(queueId2);
+            NS_TEST_EXPECT_MSG_EQ((first == last), true, "Did not expect other expired MPDUs");
+        }
+    });
+
+    /**
+     * At simulation time 50ms:
+     *
+     * Container queue for rxAddr1
+     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
+     * │Exp│Exp│Exp│Exp│Exp│Exp│Exp│   │   │
+     * │Inf│Inf│Inf│   │Inf│   │   │   │   │
+     * │ 0 │ 2 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │10 │
+     * └───┴───┴───┴───┴───┴───┴───┴───┴───┘
+     *
+     * Container queue for rxAddr2
+     * ┌───┬───┬───┬───┬───┬───┬───┬───┐
+     * │Exp│Exp│Exp│Exp│Exp│Exp│   │   │
+     * │Inf│Inf│   │Inf│Inf│   │   │   │
+     * │12 │13 │14 │15 │16 │17 │18 │19 │
+     * └───┴───┴───┴───┴───┴───┴───┴───┘
+     */
+    Simulator::Schedule(MilliSeconds(50), [&]() {
+        /**
+         * Extract all expired MPDUs (from container queue 1 and 2)
+         */
+        auto [first, last] = m_container.ExtractAllExpiredMpdus();
+
+        std::set<uint16_t> expectedSeqNo{5, 7, 8, 14, 17};
+        std::set<uint16_t> actualSeqNo;
+
+        std::transform(first, last, std::inserter(actualSeqNo, actualSeqNo.end()), [](auto& elem) {
+            return elem.mpdu->GetHeader().GetSequenceNumber();
+        });
+
+        NS_TEST_EXPECT_MSG_EQ(expectedSeqNo.size(),
+                              actualSeqNo.size(),
+                              "Unexpected number of MPDUs extracted");
+
+        for (auto expectedIt = expectedSeqNo.begin(), actualIt = actualSeqNo.begin();
+             expectedIt != expectedSeqNo.end();
+             ++expectedIt, ++actualIt)
+        {
+            NS_TEST_EXPECT_MSG_EQ(*expectedIt, *actualIt, "Unexpected extracted MPDU");
+        }
+
+        // If we try to extract expired MPDUs again, the returned set is empty
+        {
+            auto [first, last] = m_container.ExtractAllExpiredMpdus();
+            NS_TEST_EXPECT_MSG_EQ((first == last), true, "Did not expect other expired MPDUs");
+        }
+
+        /**
+         * Check MPDUs remaining in container queue 1
+         */
+        auto elemIt = m_container.GetQueue(queueId1).begin();
+        auto endIt = m_container.GetQueue(queueId1).end();
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 1");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              0,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 1");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              2,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 1");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              4,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 1");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              6,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 1");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              9,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 1");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              10,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt == endIt),
+                              true,
+                              "There should be no other MPDU in container queue 1");
+
+        /**
+         * Check MPDUs remaining in container queue 2
+         */
+        elemIt = m_container.GetQueue(queueId2).begin();
+        endIt = m_container.GetQueue(queueId2).end();
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 2");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              12,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 2");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              13,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 2");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              15,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 2");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              16,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 2");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              18,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt != endIt),
+                              true,
+                              "There should be other MPDU(s) in container queue 2");
+        NS_TEST_EXPECT_MSG_EQ(elemIt->mpdu->GetHeader().GetSequenceNumber(),
+                              19,
+                              "Unexpected queued MPDU");
+        elemIt++;
+        NS_TEST_EXPECT_MSG_EQ((elemIt == endIt),
+                              true,
+                              "There should be no other MPDU in container queue 2");
+    });
+
+    Simulator::Run();
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
  * \brief Wifi MAC Queue Test Suite
  */
 class WifiMacQueueTestSuite : public TestSuite
@@ -136,6 +444,7 @@
     : TestSuite("wifi-mac-queue", UNIT)
 {
     AddTestCase(new WifiMacQueueDropOldestTest, TestCase::QUICK);
+    AddTestCase(new WifiExtractExpiredMpdusTest, TestCase::QUICK);
 }
 
 static WifiMacQueueTestSuite g_wifiMacQueueTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-mlo-test.cc ns-3.38/src/wifi/test/wifi-mlo-test.cc
--- ns-3.37/src/wifi/test/wifi-mlo-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-mlo-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -27,6 +27,7 @@
 #include "ns3/mobility-helper.h"
 #include "ns3/multi-link-element.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"
@@ -34,17 +35,22 @@
 #include "ns3/pointer.h"
 #include "ns3/qos-utils.h"
 #include "ns3/rng-seed-manager.h"
+#include "ns3/rr-multi-user-scheduler.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/test.h"
 #include "ns3/wifi-acknowledgment.h"
 #include "ns3/wifi-assoc-manager.h"
 #include "ns3/wifi-mac-header.h"
+#include "ns3/wifi-mac-queue.h"
 #include "ns3/wifi-net-device.h"
 #include "ns3/wifi-protection.h"
 #include "ns3/wifi-psdu.h"
 
+#include <algorithm>
+#include <array>
 #include <iomanip>
 #include <sstream>
 #include <vector>
@@ -186,49 +192,32 @@
  * \ingroup wifi-test
  * \ingroup tests
  *
- * \brief Test Multi-Link Discovery & Setup
+ * \brief Base class for Multi-Link Operations tests
  *
  * Three spectrum channels are created, one for each band (2.4 GHz, 5 GHz and 6 GHz).
  * Each PHY object is attached to the spectrum channel corresponding to the PHY band
  * in which it is operating.
  */
-class MultiLinkSetupTest : public TestCase
+class MultiLinkOperationsTestBase : public TestCase
 {
   public:
     /**
      * Constructor
      *
+     * \param name The name of the new TestCase created
+     * \param nStations the number of stations to create
      * \param staChannels the strings specifying the operating channels for the STA
      * \param apChannels the strings specifying the operating channels for the AP
-     * \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::initializer_list<std::string> staChannels,
-                       std::initializer_list<std::string> apChannels,
-                       std::initializer_list<std::pair<uint8_t, uint8_t>> setupLinks,
-                       std::initializer_list<uint8_t> fixedPhyBands = {});
-    ~MultiLinkSetupTest() override;
-
-  private:
-    /**
-     * Reset the given PHY helper, use the given strings to set the ChannelSettings
-     * attribute of the PHY objects to create, and attach them to the given spectrum
-     * channels appropriately.
-     *
-     * \param helper the given PHY helper
-     * \param channels the strings specifying the operating channels to configure
-     * \param channelMap the created spectrum channels
-     */
-    void SetChannels(SpectrumWifiPhyHelper& helper,
-                     const std::vector<std::string>& channels,
-                     const std::map<WifiPhyBand, Ptr<MultiModelSpectrumChannel>>& channelMap);
-
-    /**
-     * \param str the given channel string
-     * \return the PHY band specified in the given channel string
-     */
-    WifiPhyBand GetPhyBandFromChannelStr(const std::string& str);
+    MultiLinkOperationsTestBase(const std::string& name,
+                                uint8_t nStations,
+                                std::vector<std::string> staChannels,
+                                std::vector<std::string> apChannels,
+                                std::vector<uint8_t> fixedPhyBands = {});
+    ~MultiLinkOperationsTestBase() override = default;
 
+  protected:
     /**
      * Callback invoked when a FEM passes PSDUs to the PHY.
      *
@@ -238,46 +227,31 @@
      * \param txVector the TX vector
      * \param txPowerW the tx power in Watts
      */
-    void Transmit(uint8_t linkId,
-                  std::string context,
-                  WifiConstPsduMap psduMap,
-                  WifiTxVector txVector,
-                  double txPowerW);
-    /**
-     * Check correctness of Multi-Link Setup procedure.
-     */
-    void CheckMlSetup();
+    virtual void Transmit(uint8_t linkId,
+                          std::string context,
+                          WifiConstPsduMap psduMap,
+                          WifiTxVector txVector,
+                          double txPowerW);
 
-    /**
-     * Check that links that are not setup on the non-AP MLD are disabled.
-     */
-    void CheckDisabledLinks();
+    void DoSetup() override;
 
     /**
-     * Check correctness of the given Beacon frame.
-     *
-     * \param mpdu the given Beacon frame
-     * \param linkId the ID of the link on which the Beacon frame was transmitted
+     * Uplink or Downlink direction
      */
-    void CheckBeacon(Ptr<WifiMpdu> mpdu, uint8_t linkId);
-
-    /**
-     * Check correctness of the given Association Request frame.
-     *
-     * \param mpdu the given Association Request frame
-     * \param linkId the ID of the link on which the Association Request frame was transmitted
-     */
-    void CheckAssocRequest(Ptr<WifiMpdu> mpdu, uint8_t linkId);
+    enum Direction
+    {
+        DL = 0,
+        UL
+    };
 
     /**
-     * Check correctness of the given Association Response frame.
+     * Check that the Address 1 and Address 2 fields of the given PSDU contain device MAC addresses.
      *
-     * \param mpdu the given Association Response frame
-     * \param linkId the ID of the link on which the Association Response frame was transmitted
+     * \param psdu the given PSDU
+     * \param direction indicates direction for management frames (DL or UL)
      */
-    void CheckAssocResponse(Ptr<WifiMpdu> mpdu, uint8_t linkId);
-
-    void DoRun() override;
+    void CheckAddresses(Ptr<const WifiPsdu> psdu,
+                        std::optional<Direction> direction = std::nullopt);
 
     /// Information about transmitted frames
     struct FrameInfo
@@ -288,62 +262,170 @@
         uint8_t linkId;           ///< link ID
     };
 
-    std::vector<FrameInfo> m_txPsdus;       ///< transmitted PSDUs
-    std::vector<std::string> m_staChannels; ///< strings specifying channels for STA
-    std::vector<std::string> m_apChannels;  ///< strings specifying channels for AP
-    std::vector<std::pair<uint8_t, uint8_t>>
-        m_setupLinks;                     ///< expected links to setup (STA link ID, AP link ID)
-    std::vector<uint8_t> m_fixedPhyBands; ///< links on non-AP MLD with fixed PHY band
-    Ptr<ApWifiMac> m_apMac;               ///< AP wifi MAC
-    Ptr<StaWifiMac> m_staMac;             ///< STA wifi MAC
-};
-
-MultiLinkSetupTest::MultiLinkSetupTest(
-    std::initializer_list<std::string> staChannels,
-    std::initializer_list<std::string> apChannels,
-    std::initializer_list<std::pair<uint8_t, uint8_t>> setupLinks,
-    std::initializer_list<uint8_t> fixedPhyBands)
-    : TestCase("Check correctness of Multi-Link Setup"),
+    std::vector<FrameInfo> m_txPsdus;             ///< transmitted PSDUs
+    const std::vector<std::string> m_staChannels; ///< strings specifying channels for STA
+    const std::vector<std::string> m_apChannels;  ///< strings specifying channels for AP
+    const std::vector<uint8_t> m_fixedPhyBands;   ///< links on non-AP MLD with fixed PHY band
+    Ptr<ApWifiMac> m_apMac;                       ///< AP wifi MAC
+    std::vector<Ptr<StaWifiMac>> m_staMacs;       ///< STA wifi MACs
+    uint8_t m_nStations;                          ///< number of stations to create
+    uint16_t m_lastAid;                           ///< AID of last associated station
+
+  private:
+    /**
+     * Reset the given PHY helper, use the given strings to set the ChannelSettings
+     * attribute of the PHY objects to create, and attach them to the given spectrum
+     * channels appropriately.
+     *
+     * \param helper the given PHY helper
+     * \param channels the strings specifying the operating channels to configure
+     * \param channel the created spectrum channel
+     */
+    void SetChannels(SpectrumWifiPhyHelper& helper,
+                     const std::vector<std::string>& channels,
+                     Ptr<MultiModelSpectrumChannel> channel);
+
+    /**
+     * 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()
+    {
+    }
+};
+
+MultiLinkOperationsTestBase::MultiLinkOperationsTestBase(const std::string& name,
+                                                         uint8_t nStations,
+                                                         std::vector<std::string> staChannels,
+                                                         std::vector<std::string> apChannels,
+                                                         std::vector<uint8_t> fixedPhyBands)
+    : TestCase(name),
       m_staChannels(staChannels),
       m_apChannels(apChannels),
-      m_setupLinks(setupLinks),
-      m_fixedPhyBands(fixedPhyBands)
+      m_fixedPhyBands(fixedPhyBands),
+      m_staMacs(nStations),
+      m_nStations(nStations),
+      m_lastAid(0)
 {
 }
 
-MultiLinkSetupTest::~MultiLinkSetupTest()
+void
+MultiLinkOperationsTestBase::CheckAddresses(Ptr<const WifiPsdu> psdu,
+                                            std::optional<Direction> direction)
 {
+    std::optional<Mac48Address> apAddr;
+    std::optional<Mac48Address> staAddr;
+
+    // direction for Data frames is derived from ToDS/FromDS flags
+    if (psdu->GetHeader(0).IsQosData())
+    {
+        direction = (!psdu->GetHeader(0).IsToDs() && psdu->GetHeader(0).IsFromDs()) ? DL : UL;
+    }
+    NS_ASSERT(direction);
+
+    if (direction == DL)
+    {
+        if (!psdu->GetAddr1().IsGroup())
+        {
+            staAddr = psdu->GetAddr1();
+        }
+        apAddr = psdu->GetAddr2();
+    }
+    else
+    {
+        if (!psdu->GetAddr1().IsGroup())
+        {
+            apAddr = psdu->GetAddr1();
+        }
+        staAddr = psdu->GetAddr2();
+    }
+
+    if (apAddr)
+    {
+        bool found = false;
+        for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+        {
+            if (m_apMac->GetFrameExchangeManager(linkId)->GetAddress() == *apAddr)
+            {
+                found = true;
+                break;
+            }
+        }
+        NS_TEST_EXPECT_MSG_EQ(found,
+                              true,
+                              "Address " << *apAddr << " is not an AP device address. "
+                                         << "PSDU: " << *psdu);
+    }
+
+    if (staAddr)
+    {
+        bool found = false;
+        for (uint8_t i = 0; i < m_nStations; i++)
+        {
+            for (uint8_t linkId = 0; linkId < m_staMacs[i]->GetNLinks(); linkId++)
+            {
+                if (m_staMacs[i]->GetFrameExchangeManager(linkId)->GetAddress() == *staAddr)
+                {
+                    found = true;
+                    break;
+                }
+            }
+            if (found)
+            {
+                break;
+            }
+        }
+        NS_TEST_EXPECT_MSG_EQ(found,
+                              true,
+                              "Address " << *staAddr << " is not a STA device address. "
+                                         << "PSDU: " << *psdu);
+    }
 }
 
 void
-MultiLinkSetupTest::Transmit(uint8_t linkId,
-                             std::string context,
-                             WifiConstPsduMap psduMap,
-                             WifiTxVector txVector,
-                             double txPowerW)
+MultiLinkOperationsTestBase::Transmit(uint8_t linkId,
+                                      std::string context,
+                                      WifiConstPsduMap psduMap,
+                                      WifiTxVector txVector,
+                                      double txPowerW)
 {
     m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, linkId});
 
-    std::stringstream ss;
-    ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID " << +linkId << " "
-       << psduMap.begin()->second->GetHeader(0).GetTypeString() << " #MPDUs "
-       << psduMap.begin()->second->GetNMpdus() << " duration/ID "
-       << psduMap.begin()->second->GetHeader(0).GetDuration()
-       << " RA = " << psduMap.begin()->second->GetAddr1()
-       << " TA = " << psduMap.begin()->second->GetAddr2();
-    if (psduMap.begin()->second->GetHeader(0).IsQosData())
+    for (const auto& [aid, psdu] : psduMap)
     {
-        ss << " TID = " << +psduMap.begin()->second->GetHeader(0).GetQosTid();
+        std::stringstream ss;
+        ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID " << +linkId << " "
+           << psdu->GetHeader(0).GetTypeString() << " #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(ss.str());
     NS_LOG_INFO("TXVECTOR = " << txVector << "\n");
 }
 
 void
-MultiLinkSetupTest::SetChannels(
-    SpectrumWifiPhyHelper& helper,
-    const std::vector<std::string>& channels,
-    const std::map<WifiPhyBand, Ptr<MultiModelSpectrumChannel>>& channelMap)
+MultiLinkOperationsTestBase::SetChannels(SpectrumWifiPhyHelper& helper,
+                                         const std::vector<std::string>& channels,
+                                         Ptr<MultiModelSpectrumChannel> channel)
 {
     helper = SpectrumWifiPhyHelper(channels.size());
     helper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
@@ -351,34 +433,14 @@
     uint8_t linkId = 0;
     for (const auto& str : channels)
     {
-        helper.Set(linkId, "ChannelSettings", StringValue(str));
-        helper.SetChannel(linkId, channelMap.at(GetPhyBandFromChannelStr(str)));
-
-        linkId++;
+        helper.Set(linkId++, "ChannelSettings", StringValue(str));
     }
-}
 
-WifiPhyBand
-MultiLinkSetupTest::GetPhyBandFromChannelStr(const std::string& str)
-{
-    if (str.find("2_4GHZ") != std::string::npos)
-    {
-        return WIFI_PHY_BAND_2_4GHZ;
-    }
-    if (str.find("5GHZ") != std::string::npos)
-    {
-        return WIFI_PHY_BAND_5GHZ;
-    }
-    if (str.find("6GHZ") != std::string::npos)
-    {
-        return WIFI_PHY_BAND_6GHZ;
-    }
-    NS_ABORT_MSG("Band in channel settings must be specified");
-    return WIFI_PHY_BAND_UNSPECIFIED;
+    helper.SetChannel(channel);
 }
 
 void
-MultiLinkSetupTest::DoRun()
+MultiLinkOperationsTestBase::DoSetup()
 {
     RngSeedManager::SetSeed(1);
     RngSeedManager::SetRun(2);
@@ -387,25 +449,24 @@
     NodeContainer wifiApNode;
     wifiApNode.Create(1);
 
-    NodeContainer wifiStaNode;
-    wifiStaNode.Create(1);
+    NodeContainer wifiStaNodes;
+    wifiStaNodes.Create(m_nStations);
 
     WifiHelper wifi;
     // wifi.EnableLogComponents ();
     wifi.SetStandard(WIFI_STANDARD_80211be);
     wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
                                  "DataMode",
-                                 StringValue("EhtMcs0"));
+                                 StringValue("EhtMcs0"),
+                                 "ControlMode",
+                                 StringValue("HtMcs0"));
 
-    std::map<WifiPhyBand, Ptr<MultiModelSpectrumChannel>> channelMap = {
-        {WIFI_PHY_BAND_2_4GHZ, CreateObject<MultiModelSpectrumChannel>()},
-        {WIFI_PHY_BAND_5GHZ, CreateObject<MultiModelSpectrumChannel>()},
-        {WIFI_PHY_BAND_6GHZ, CreateObject<MultiModelSpectrumChannel>()}};
+    auto channel = CreateObject<MultiModelSpectrumChannel>();
 
     SpectrumWifiPhyHelper staPhyHelper;
     SpectrumWifiPhyHelper apPhyHelper;
-    SetChannels(staPhyHelper, m_staChannels, channelMap);
-    SetChannels(apPhyHelper, m_apChannels, channelMap);
+    SetChannels(staPhyHelper, m_staChannels, channel);
+    SetChannels(apPhyHelper, m_apChannels, channel);
 
     for (const auto& linkId : m_fixedPhyBands)
     {
@@ -413,12 +474,17 @@
     }
 
     WifiMacHelper mac;
-    Ssid ssid = Ssid("ns-3-ssid");
-    mac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid), "ActiveProbing", BooleanValue(false));
-
-    NetDeviceContainer staDevices = wifi.Install(staPhyHelper, mac, wifiStaNode);
-
-    mac.SetType("ns3::ApWifiMac", "Ssid", SsidValue(ssid), "BeaconGeneration", BooleanValue(true));
+    mac.SetType("ns3::StaWifiMac", // default SSID
+                "ActiveProbing",
+                BooleanValue(false));
+
+    NetDeviceContainer staDevices = wifi.Install(staPhyHelper, mac, wifiStaNodes);
+
+    mac.SetType("ns3::ApWifiMac",
+                "Ssid",
+                SsidValue(Ssid("ns-3-ssid")),
+                "BeaconGeneration",
+                BooleanValue(true));
 
     NetDeviceContainer apDevices = wifi.Install(apPhyHelper, mac, wifiApNode);
 
@@ -439,10 +505,14 @@
 
     mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
     mobility.Install(wifiApNode);
-    mobility.Install(wifiStaNode);
+    mobility.Install(wifiStaNodes);
 
     m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevices.Get(0))->GetMac());
-    m_staMac = DynamicCast<StaWifiMac>(DynamicCast<WifiNetDevice>(staDevices.Get(0))->GetMac());
+    for (uint8_t i = 0; i < m_nStations; i++)
+    {
+        m_staMacs[i] =
+            DynamicCast<StaWifiMac>(DynamicCast<WifiNetDevice>(staDevices.Get(i))->GetMac());
+    }
 
     // Trace PSDUs passed to the PHY on all devices
     for (uint8_t linkId = 0; linkId < StaticCast<WifiNetDevice>(apDevices.Get(0))->GetNPhys();
@@ -450,16 +520,134 @@
     {
         Config::Connect("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
                             std::to_string(linkId) + "/PhyTxPsduBegin",
-                        MakeCallback(&MultiLinkSetupTest::Transmit, this).Bind(linkId));
+                        MakeCallback(&MultiLinkOperationsTestBase::Transmit, this).Bind(linkId));
     }
-    for (uint8_t linkId = 0; linkId < StaticCast<WifiNetDevice>(staDevices.Get(0))->GetNPhys();
-         linkId++)
+    for (uint8_t i = 0; i < m_nStations; i++)
     {
-        Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
-                            std::to_string(linkId) + "/PhyTxPsduBegin",
-                        MakeCallback(&MultiLinkSetupTest::Transmit, this).Bind(linkId));
+        for (uint8_t linkId = 0; linkId < StaticCast<WifiNetDevice>(staDevices.Get(i))->GetNPhys();
+             linkId++)
+        {
+            Config::Connect(
+                "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
+                    std::to_string(linkId) + "/PhyTxPsduBegin",
+                MakeCallback(&MultiLinkOperationsTestBase::Transmit, this).Bind(linkId));
+        }
     }
 
+    // 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")); });
+}
+
+void
+MultiLinkOperationsTestBase::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;
+
+    // make the next STA to start ML discovery & setup
+    if (aid < m_nStations)
+    {
+        m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
+        return;
+    }
+    // wait some time (5ms) to allow the completion of association before generating traffic
+    Simulator::Schedule(MilliSeconds(5), &MultiLinkOperationsTestBase::StartTraffic, this);
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Multi-Link Discovery & Setup test.
+ *
+ * This test sets up an AP MLD and a non-AP MLD having a variable number of links.
+ * The RF channels to set each link to are provided as input parameters through the test
+ * case constructor, along with the identifiers (starting at 0) of the links that cannot
+ * switch PHY band (if any). The links that are expected to be setup are also provided as input
+ * parameters. This test verifies that the management frames exchanged during ML discovery
+ * and ML setup contain the expected values and that the two MLDs setup the expected links.
+ */
+class MultiLinkSetupTest : public MultiLinkOperationsTestBase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param staChannels the strings specifying the operating channels for the STA
+     * \param apChannels the strings specifying the operating channels for the AP
+     * \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,
+                       std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
+                       std::vector<uint8_t> fixedPhyBands = {});
+    ~MultiLinkSetupTest() override = default;
+
+  protected:
+    void DoRun() override;
+
+  private:
+    /**
+     * Check correctness of Multi-Link Setup procedure.
+     */
+    void CheckMlSetup();
+
+    /**
+     * Check that links that are not setup on the non-AP MLD are disabled.
+     */
+    void CheckDisabledLinks();
+
+    /**
+     * Check correctness of the given Beacon frame.
+     *
+     * \param mpdu the given Beacon frame
+     * \param linkId the ID of the link on which the Beacon frame was transmitted
+     */
+    void CheckBeacon(Ptr<WifiMpdu> mpdu, uint8_t linkId);
+
+    /**
+     * Check correctness of the given Association Request frame.
+     *
+     * \param mpdu the given Association Request frame
+     * \param linkId the ID of the link on which the Association Request frame was transmitted
+     */
+    void CheckAssocRequest(Ptr<WifiMpdu> mpdu, uint8_t linkId);
+
+    /**
+     * Check correctness of the given Association Response frame.
+     *
+     * \param mpdu the given Association Response frame
+     * \param linkId the ID of the link on which the Association Response frame was transmitted
+     */
+    void CheckAssocResponse(Ptr<WifiMpdu> mpdu, uint8_t linkId);
+
+    /// expected links to setup (STA link ID, AP link ID)
+    const std::vector<std::pair<uint8_t, uint8_t>> m_setupLinks;
+};
+
+MultiLinkSetupTest::MultiLinkSetupTest(std::vector<std::string> staChannels,
+                                       std::vector<std::string> apChannels,
+                                       std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
+                                       std::vector<uint8_t> fixedPhyBands)
+    : MultiLinkOperationsTestBase("Check correctness of Multi-Link Setup",
+                                  1,
+                                  staChannels,
+                                  apChannels,
+                                  fixedPhyBands),
+      m_setupLinks(setupLinks)
+{
+}
+
+void
+MultiLinkSetupTest::DoRun()
+{
     Simulator::Schedule(MilliSeconds(500), &MultiLinkSetupTest::CheckMlSetup, this);
 
     Simulator::Stop(Seconds(1.5));
@@ -502,6 +690,8 @@
 {
     NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_BEACON);
 
+    CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
+
     NS_TEST_EXPECT_MSG_EQ(m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
                           mpdu->GetHeader().GetAddr2(),
                           "TA of Beacon frame is not the address of the link it is transmitted on");
@@ -558,15 +748,17 @@
 {
     NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_REQUEST);
 
+    CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::UL);
+
     NS_TEST_EXPECT_MSG_EQ(
-        m_staMac->GetFrameExchangeManager(linkId)->GetAddress(),
+        m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress(),
         mpdu->GetHeader().GetAddr2(),
         "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();
 
-    if (m_apMac->GetNLinks() == 1 || m_staMac->GetNLinks() == 1)
+    if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
     {
         NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
                               false,
@@ -576,7 +768,7 @@
 
     NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Assoc Request frame");
     NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
-                          m_staMac->GetAddress(),
+                          m_staMacs[0]->GetAddress(),
                           "Incorrect MLD Address advertised in Multi-Link Element");
     NS_TEST_EXPECT_MSG_EQ(mle->GetNPerStaProfileSubelements(),
                           m_setupLinks.size() - 1,
@@ -588,7 +780,7 @@
                               true,
                               "Per-STA Profile must contain STA MAC address");
         // find ID of the local link corresponding to this subelement
-        auto staLinkId = m_staMac->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
+        auto staLinkId = m_staMacs[0]->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
         NS_TEST_EXPECT_MSG_EQ(
             staLinkId.has_value(),
             true,
@@ -618,6 +810,8 @@
 {
     NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_RESPONSE);
 
+    CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
+
     NS_TEST_EXPECT_MSG_EQ(
         m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
         mpdu->GetHeader().GetAddr2(),
@@ -626,7 +820,7 @@
     mpdu->GetPacket()->PeekHeader(assoc);
     const auto& mle = assoc.GetMultiLinkElement();
 
-    if (m_apMac->GetNLinks() == 1 || m_staMac->GetNLinks() == 1)
+    if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
     {
         NS_TEST_EXPECT_MSG_EQ(
             mle.has_value(),
@@ -679,21 +873,21 @@
     /**
      * Check outcome of Multi-Link Setup
      */
-    NS_TEST_EXPECT_MSG_EQ(m_staMac->IsAssociated(), true, "Expected the STA to be associated");
+    NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->IsAssociated(), true, "Expected the STA to be associated");
 
     for (const auto& [staLinkId, apLinkId] : m_setupLinks)
     {
-        auto staAddr = m_staMac->GetFrameExchangeManager(staLinkId)->GetAddress();
+        auto staAddr = m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetAddress();
         auto apAddr = m_apMac->GetFrameExchangeManager(apLinkId)->GetAddress();
 
-        auto staRemoteMgr = m_staMac->GetWifiRemoteStationManager(staLinkId);
+        auto staRemoteMgr = m_staMacs[0]->GetWifiRemoteStationManager(staLinkId);
         auto apRemoteMgr = m_apMac->GetWifiRemoteStationManager(apLinkId);
 
         // STA side
-        NS_TEST_EXPECT_MSG_EQ(m_staMac->GetFrameExchangeManager(staLinkId)->GetBssid(),
+        NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetBssid(),
                               apAddr,
                               "Unexpected BSSID for STA link ID " << +staLinkId);
-        if (m_apMac->GetNLinks() > 1 && m_staMac->GetNLinks() > 1)
+        if (m_apMac->GetNLinks() > 1 && m_staMacs[0]->GetNLinks() > 1)
         {
             NS_TEST_EXPECT_MSG_EQ((staRemoteMgr->GetMldAddress(apAddr) == m_apMac->GetAddress()),
                                   true,
@@ -709,13 +903,14 @@
                               true,
                               "Expecting STA " << staAddr << " to be associated on link "
                                                << +apLinkId);
-        if (m_apMac->GetNLinks() > 1 && m_staMac->GetNLinks() > 1)
+        if (m_apMac->GetNLinks() > 1 && m_staMacs[0]->GetNLinks() > 1)
         {
-            NS_TEST_EXPECT_MSG_EQ((apRemoteMgr->GetMldAddress(staAddr) == m_staMac->GetAddress()),
-                                  true,
-                                  "Incorrect MLD address stored by AP on link ID " << +apLinkId);
             NS_TEST_EXPECT_MSG_EQ(
-                (apRemoteMgr->GetAffiliatedStaAddress(m_staMac->GetAddress()) == staAddr),
+                (apRemoteMgr->GetMldAddress(staAddr) == m_staMacs[0]->GetAddress()),
+                true,
+                "Incorrect MLD address stored by AP on link ID " << +apLinkId);
+            NS_TEST_EXPECT_MSG_EQ(
+                (apRemoteMgr->GetAffiliatedStaAddress(m_staMacs[0]->GetAddress()) == staAddr),
                 true,
                 "Incorrect affiliated address stored by AP on link ID " << +apLinkId);
         }
@@ -726,21 +921,23 @@
                               "STA " << staAddr << " not found in list of associated STAs");
 
         // STA of non-AP MLD operate on the same channel as the AP
-        NS_TEST_EXPECT_MSG_EQ(+m_staMac->GetWifiPhy(staLinkId)->GetOperatingChannel().GetNumber(),
-                              +m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetNumber(),
-                              "Incorrect operating channel number for STA on link " << +staLinkId);
-        NS_TEST_EXPECT_MSG_EQ(m_staMac->GetWifiPhy(staLinkId)->GetOperatingChannel().GetFrequency(),
-                              m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetFrequency(),
-                              "Incorrect operating channel frequency for STA on link "
-                                  << +staLinkId);
-        NS_TEST_EXPECT_MSG_EQ(m_staMac->GetWifiPhy(staLinkId)->GetOperatingChannel().GetWidth(),
+        NS_TEST_EXPECT_MSG_EQ(
+            +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetNumber(),
+            +m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetNumber(),
+            "Incorrect operating channel number for STA on link " << +staLinkId);
+        NS_TEST_EXPECT_MSG_EQ(
+            m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetFrequency(),
+            m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetFrequency(),
+            "Incorrect operating channel frequency for STA on link " << +staLinkId);
+        NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetWidth(),
                               m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetWidth(),
                               "Incorrect operating channel width for STA on link " << +staLinkId);
-        NS_TEST_EXPECT_MSG_EQ(+m_staMac->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPhyBand(),
-                              +m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetPhyBand(),
-                              "Incorrect operating PHY band for STA on link " << +staLinkId);
         NS_TEST_EXPECT_MSG_EQ(
-            +m_staMac->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPrimaryChannelIndex(20),
+            +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPhyBand(),
+            +m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetPhyBand(),
+            "Incorrect operating PHY band for STA on link " << +staLinkId);
+        NS_TEST_EXPECT_MSG_EQ(
+            +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPrimaryChannelIndex(20),
             +m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel().GetPrimaryChannelIndex(20),
             "Incorrect operating primary channel index for STA on link " << +staLinkId);
     }
@@ -757,31 +954,1540 @@
         if (it == m_setupLinks.end())
         {
             // the link has not been setup
-            NS_TEST_EXPECT_MSG_EQ(m_staMac->GetWifiPhy(linkId)->GetState()->IsStateOff(),
+            NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
                                   true,
                                   "Link " << +linkId << " has not been setup but is not disabled");
             continue;
         }
 
-        if (GetPhyBandFromChannelStr(m_staChannels.at(it->first)) !=
-            GetPhyBandFromChannelStr(m_apChannels.at(it->second)))
+        // the link has been setup and must be active
+        NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
+                              false,
+                              "Expecting link " << +linkId << " to be active");
+    }
+}
+
+/**
+ * Tested traffic patterns.
+ */
+enum class WifiTrafficPattern : uint8_t
+{
+    STA_TO_STA = 0,
+    STA_TO_AP,
+    AP_TO_STA,
+    AP_TO_BCAST,
+    STA_TO_BCAST
+};
+
+/**
+ * Block Ack agreement enabled/disabled
+ */
+enum class WifiBaEnabled : uint8_t
+{
+    NO = 0,
+    YES
+};
+
+/**
+ * Whether to send a BlockAckReq after a missed BlockAck
+ */
+enum class WifiUseBarAfterMissedBa : uint8_t
+{
+    NO = 0,
+    YES
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test data transmission between two MLDs.
+ *
+ * This test sets up an AP MLD and two non-AP MLDs having a variable number of links.
+ * The RF channels to set each link to are provided as input parameters through the test
+ * case constructor, along with the identifiers (starting at 0) of the links that cannot
+ * switch PHY band (if any). This test aims at veryfing the successful transmission of both
+ * unicast QoS data frames (from one station to another, from one station to the AP, from
+ * the AP to the station) and broadcast QoS data frames (from the AP or from one station).
+ * In the scenarios in which the AP forwards frames (i.e., from one station to another and
+ * from one station to broadcast) the client application generates only 4 packets, in order
+ * to limit the probability of collisions. In the other scenarios, 8 packets are generated.
+ * When BlockAck agreements are enabled, the maximum A-MSDU size is set such that two
+ * packets can be aggregated in an A-MSDU. The MPDU with sequence number equal to 1 is
+ * corrupted (once, by using a post reception error model) to test its successful
+ * re-transmission, unless the traffic scenario is from the AP to broadcast (broadcast frames
+ * are not retransmitted) or is a scenario where the AP forwards frame (to limit the
+ * probability of collisions).
+ *
+ * When BlockAck agreements are enabled, we also corrupt a BlockAck frame, so as to simulate
+ * the case of BlockAck timeout. Both the case where a BlockAckReq is sent and the case where
+ * data frame are retransmitted are tested. Finally, when BlockAck agreements are enabled, we
+ * also enable the concurrent transmission of data frames over two links and check that at
+ * least one MPDU is concurrently transmitted over two links.
+ */
+class MultiLinkTxTest : public MultiLinkOperationsTestBase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param trafficPattern the pattern of traffic to generate
+     * \param baEnabled whether BA agreement is enabled or disabled
+     * \param useBarAfterMissedBa whether a BAR or Data frames are sent after missed BlockAck
+     * \param nMaxInflight the max number of links on which an MPDU can be simultaneously inflight
+     *                     (unused if Block Ack agreements are not established)
+     * \param staChannels the strings specifying the operating channels for the STA
+     * \param apChannels the strings specifying the operating channels for the AP
+     * \param fixedPhyBands list of IDs of STA links that cannot switch PHY band
+     */
+    MultiLinkTxTest(WifiTrafficPattern trafficPattern,
+                    WifiBaEnabled baEnabled,
+                    WifiUseBarAfterMissedBa useBarAfterMissedBa,
+                    uint8_t nMaxInflight,
+                    const std::vector<std::string>& staChannels,
+                    const std::vector<std::string>& apChannels,
+                    const std::vector<uint8_t>& fixedPhyBands = {});
+    ~MultiLinkTxTest() override = default;
+
+  protected:
+    /**
+     * Function to trace packets received by the server application
+     * \param nodeId the ID of the node that received the packet
+     * \param p the packet
+     * \param addr the address
+     */
+    void L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr);
+
+    /**
+     * Check the content of a received BlockAck frame when the max number of links on which
+     * an MPDU can be inflight is one.
+     *
+     * \param psdu the PSDU containing the BlockAck
+     * \param txVector the TXVECTOR used to transmit the BlockAck
+     * \param linkId the ID of the link on which the BlockAck was transmitted
+     */
+    void CheckBlockAck(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector, uint8_t linkId);
+
+    void Transmit(uint8_t linkId,
+                  std::string context,
+                  WifiConstPsduMap psduMap,
+                  WifiTxVector txVector,
+                  double txPowerW) override;
+    void DoSetup() override;
+    void DoRun() override;
+
+  private:
+    void StartTraffic() override;
+
+    /// Receiver address-indexed map of list error models
+    using RxErrorModelMap = std::unordered_map<Mac48Address, Ptr<ListErrorModel>, WifiAddressHash>;
+
+    RxErrorModelMap m_errorModels;         ///< error rate models to corrupt packets
+    std::list<uint64_t> m_uidList;         ///< list of UIDs of packets to corrupt
+    bool m_dataCorrupted{false};           ///< whether second data frame has been already corrupted
+    WifiTrafficPattern m_trafficPattern;   ///< the pattern of traffic to generate
+    bool m_baEnabled;                      ///< whether BA agreement is enabled or disabled
+    bool m_useBarAfterMissedBa;            ///< whether to send BAR after missed BlockAck
+    std::size_t m_nMaxInflight;            ///< max number of links on which an MPDU can be inflight
+    std::size_t m_nPackets;                ///< number of application packets to generate
+    std::size_t m_blockAckCount{0};        ///< transmitted BlockAck counter
+    std::size_t m_blockAckReqCount{0};     ///< transmitted BlockAckReq counter
+    std::array<std::size_t, 3> m_rxPkts{}; ///< number of packets received at application layer
+                                           ///< by each node (AP, STA 0, STA 1)
+    std::map<uint16_t, std::size_t> m_inflightCount; ///< seqNo-indexed max number of simultaneous
+                                                     ///< transmissions of a data frame
+    Ptr<WifiMac> m_sourceMac; ///< MAC of the node sending application packets
+};
+
+MultiLinkTxTest::MultiLinkTxTest(WifiTrafficPattern trafficPattern,
+                                 WifiBaEnabled baEnabled,
+                                 WifiUseBarAfterMissedBa useBarAfterMissedBa,
+                                 uint8_t nMaxInflight,
+                                 const std::vector<std::string>& staChannels,
+                                 const std::vector<std::string>& apChannels,
+                                 const std::vector<uint8_t>& fixedPhyBands)
+    : MultiLinkOperationsTestBase(
+          std::string("Check data transmission between MLDs ") +
+              (baEnabled == WifiBaEnabled::YES
+                   ? (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
+                          ? "with BA agreement, send BAR after BlockAck timeout"
+                          : "with BA agreement, send Data frames after BlockAck timeout")
+                   : "without BA agreement") +
+              " (Traffic pattern: " + std::to_string(static_cast<uint8_t>(trafficPattern)) +
+              (baEnabled == WifiBaEnabled::YES ? ", nMaxInflight=" + std::to_string(nMaxInflight)
+                                               : "") +
+              ")",
+          2,
+          staChannels,
+          apChannels,
+          fixedPhyBands),
+      m_trafficPattern(trafficPattern),
+      m_baEnabled(baEnabled == WifiBaEnabled::YES),
+      m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
+      m_nMaxInflight(nMaxInflight),
+      m_nPackets(trafficPattern == WifiTrafficPattern::STA_TO_BCAST ||
+                         trafficPattern == WifiTrafficPattern::STA_TO_STA
+                     ? 4
+                     : 8)
+{
+}
+
+void
+MultiLinkTxTest::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_ACTION:
+        // a management frame is a DL frame if TA equals BSSID
+        CheckAddresses(psdu,
+                       psdu->GetHeader(0).GetAddr2() == psdu->GetHeader(0).GetAddr3() ? DL : UL);
+        if (!m_baEnabled)
+        {
+            // corrupt all management action frames (ADDBA Request frames) to prevent
+            // the establishment of a BA agreement
+            m_uidList.push_front(psdu->GetPacket()->GetUid());
+            m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
+            NS_LOG_INFO("CORRUPTED");
+        }
+        break;
+    case WIFI_MAC_QOSDATA:
+        CheckAddresses(psdu);
+
+        for (const auto& mpdu : *psdu)
+        {
+            // determine the max number of simultaneous transmissions for this MPDU
+            // (only if sent by the traffic source and this is not a broadcast frame)
+            if (m_baEnabled && linkId < m_sourceMac->GetNLinks() &&
+                m_sourceMac->GetFrameExchangeManager(linkId)->GetAddress() ==
+                    mpdu->GetHeader().GetAddr2() &&
+                !mpdu->GetHeader().GetAddr1().IsGroup())
+            {
+                auto seqNo = mpdu->GetHeader().GetSequenceNumber();
+                auto [it, success] =
+                    m_inflightCount.insert({seqNo, mpdu->GetInFlightLinkIds().size()});
+                if (!success)
+                {
+                    it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
+                }
+            }
+        }
+        for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
+        {
+            // corrupt QoS data frame with sequence number equal to 1 (only once) if we are
+            // not in the AP to broadcast traffic pattern (broadcast frames are not retransmitted)
+            // nor in the STA to broadcast or STA to STA traffic patterns (retransmissions from
+            // STA 1 could collide with frames forwarded by the AP)
+            if (psdu->GetHeader(i).GetSequenceNumber() != 1 ||
+                m_trafficPattern == WifiTrafficPattern::AP_TO_BCAST ||
+                m_trafficPattern == WifiTrafficPattern::STA_TO_BCAST ||
+                m_trafficPattern == WifiTrafficPattern::STA_TO_STA)
+            {
+                continue;
+            }
+            auto uid = psdu->GetPayload(i)->GetUid();
+            if (!m_dataCorrupted)
+            {
+                m_uidList.push_front(uid);
+                m_dataCorrupted = true;
+                NS_LOG_INFO("CORRUPTED");
+                m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
+            }
+            else
+            {
+                // do not corrupt the QoS data frame anymore
+                if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
+                    it != m_uidList.cend())
+                {
+                    m_uidList.erase(it);
+                }
+                m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
+            }
+            break;
+        }
+        break;
+    case WIFI_MAC_CTL_BACKRESP: {
+        // ignore BlockAck frames not addressed to the source of the application packets
+        if (!m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr1()))
+        {
+            break;
+        }
+        if (m_nMaxInflight > 1)
+        {
+            // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
+            break;
+        }
+        CheckBlockAck(psdu, txVector, linkId);
+        m_blockAckCount++;
+        if (m_blockAckCount == 2)
+        {
+            // corrupt the second BlockAck frame to simulate a missed BlockAck
+            m_uidList.push_front(psdu->GetPacket()->GetUid());
+            NS_LOG_INFO("CORRUPTED");
+            m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
+        }
+        break;
+    case WIFI_MAC_CTL_BACKREQ:
+        // ignore BlockAckReq frames not transmitted by the source of the application packets
+        if (m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr2()))
+        {
+            m_blockAckReqCount++;
+        }
+        break;
+    }
+    default:;
+    }
+
+    MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
+}
+
+void
+MultiLinkTxTest::CheckBlockAck(Ptr<const WifiPsdu> psdu,
+                               const WifiTxVector& txVector,
+                               uint8_t linkId)
+{
+    NS_TEST_ASSERT_MSG_EQ(m_baEnabled, true, "No BlockAck expected without BA agreement");
+    NS_TEST_ASSERT_MSG_EQ((m_trafficPattern != WifiTrafficPattern::AP_TO_BCAST),
+                          true,
+                          "No BlockAck expected in AP to broadcast traffic pattern");
+
+    /*
+     *         ┌───────┬───────X        ┌───────┐
+     *  link 0 │   0   │   1   │        │   1   │
+     *  ───────┴───────┴───────┴┬──┬────┴───────┴┬───┬────────────────────────
+     *                          │BA│             │ACK│
+     *                          └──┘             └───┘
+     *                      ┌───────┬───────┐       ┌───────┬───────┐
+     *  link 1              │   2   │   3   │       │   2   │   3   │
+     *  ────────────────────┴───────┴───────┴┬──X───┴───────┴───────┴┬──┬─────
+     *                                       │BA│                    │BA│
+     *                                       └──┘                    └──┘
+     */
+    auto mpdu = *psdu->begin();
+    CtrlBAckResponseHeader blockAck;
+    mpdu->GetPacket()->PeekHeader(blockAck);
+    bool isMpdu1corrupted = (m_trafficPattern == WifiTrafficPattern::STA_TO_AP ||
+                             m_trafficPattern == WifiTrafficPattern::AP_TO_STA);
+
+    switch (m_blockAckCount)
+    {
+    case 0: // first BlockAck frame (all traffic patterns)
+        NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(0),
+                              true,
+                              "MPDU 0 expected to be successfully received");
+        NS_TEST_EXPECT_MSG_EQ(
+            blockAck.IsPacketReceived(1),
+            !isMpdu1corrupted,
+            "MPDU 1 expected to be received only in STA_TO_STA/STA_TO_BCAST scenarios");
+        // if there are at least two links setup, we expect all MPDUs to be inflight
+        // (on distinct links)
+        if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
+        {
+            auto queue = m_sourceMac->GetTxopQueue(AC_BE);
+            auto rcvMac = m_sourceMac == m_staMacs[0] ? StaticCast<WifiMac>(m_apMac)
+                                                      : StaticCast<WifiMac>(m_staMacs[1]);
+            auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
+            std::size_t nQueuedPkt = 0;
+            auto delay = WifiPhy::CalculateTxDuration(psdu,
+                                                      txVector,
+                                                      rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
+                         MicroSeconds(1); // to account for propagation delay
+
+            while (item)
+            {
+                auto seqNo = item->GetHeader().GetSequenceNumber();
+                NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
+                                      true,
+                                      "MPDU with seqNo=" << seqNo << " is not in flight");
+                auto linkIds = item->GetInFlightLinkIds();
+                NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
+                                      1,
+                                      "MPDU with seqNo=" << seqNo
+                                                         << " is in flight on multiple links");
+                // The first two MPDUs are in flight on the same link on which the BlockAck
+                // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
+                // in flight on a different link.
+                auto srcLinkId = m_sourceMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
+                NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
+                                      true,
+                                      "Addr1 of BlockAck is not an originator's link address");
+                NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
+                                      (seqNo <= 1),
+                                      "MPDU with seqNo=" << seqNo
+                                                         << " in flight on unexpected link");
+                // check the Retry subfield and whether this MPDU is still queued
+                // after the originator has processed this BlockAck
+
+                // MPDUs acknowledged via this BlockAck are no longer queued
+                bool isQueued = (seqNo > (isMpdu1corrupted ? 0 : 1));
+                // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
+                // is still queued) and has been transmitted on the same link as the BlockAck
+                // (i.e., its sequence number is less than or equal to 1)
+                bool isRetry = isQueued && seqNo <= 1;
+
+                Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
+                    NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
+                                          isQueued,
+                                          "MPDU with seqNo="
+                                              << item->GetHeader().GetSequenceNumber() << " should "
+                                              << (isQueued ? "" : "not") << " be queued");
+                    NS_TEST_EXPECT_MSG_EQ(
+                        item->GetHeader().IsRetry(),
+                        isRetry,
+                        "Unexpected value for the Retry subfield of the MPDU with seqNo="
+                            << item->GetHeader().GetSequenceNumber());
+                });
+
+                nQueuedPkt++;
+                item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
+            }
+            // Each MPDU contains an A-MSDU consisting of two MSDUs
+            NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
+        }
+        break;
+    case 1: // second BlockAck frame (STA to AP and AP to STA traffic patterns only)
+    case 2: // third BlockAck frame (STA to AP and AP to STA traffic patterns only)
+        NS_TEST_EXPECT_MSG_EQ((m_trafficPattern == WifiTrafficPattern::AP_TO_STA ||
+                               m_trafficPattern == WifiTrafficPattern::STA_TO_AP),
+                              true,
+                              "Did not expect to receive a second BlockAck");
+        // the second BlockAck is corrupted, but the data frames have been received successfully
+        std::pair<uint16_t, uint16_t> seqNos;
+        // if multiple links were setup, the transmission of the second A-MPDU started before
+        // the end of the first one, so the second A-MPDU includes MPDUs with sequence numbers
+        // 2 and 3. Otherwise, MPDU with sequence number 1 is retransmitted along with the MPDU
+        // with sequence number 2.
+        if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
+        {
+            seqNos = {2, 3};
+        }
+        else
+        {
+            seqNos = {1, 2};
+        }
+        NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.first),
+                              true,
+                              "MPDU " << seqNos.first << " expected to be successfully received");
+        NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.second),
+                              true,
+                              "MPDU " << seqNos.second << " expected to be successfully received");
+        break;
+    }
+}
+
+void
+MultiLinkTxTest::L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr)
+{
+    NS_LOG_INFO("Packet received by NODE " << +nodeId << "\n");
+    m_rxPkts[nodeId]++;
+}
+
+void
+MultiLinkTxTest::DoSetup()
+{
+    MultiLinkOperationsTestBase::DoSetup();
+
+    if (m_baEnabled)
+    {
+        // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
+        for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
+        {
+            mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(2100));
+            mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
+                                                 BooleanValue(m_useBarAfterMissedBa));
+            mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
+        }
+    }
+
+    // install post reception error model on all devices
+    for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+    {
+        auto errorModel = CreateObject<ListErrorModel>();
+        m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
+        m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
+    }
+    for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
+    {
+        auto errorModel = CreateObject<ListErrorModel>();
+        m_errorModels[m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
+        m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
+
+        errorModel = CreateObject<ListErrorModel>();
+        m_errorModels[m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
+        m_staMacs[1]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
+    }
+}
+
+void
+MultiLinkTxTest::StartTraffic()
+{
+    const Time duration = Seconds(1);
+    Address destAddr;
+
+    switch (m_trafficPattern)
+    {
+    case WifiTrafficPattern::STA_TO_STA:
+        m_sourceMac = m_staMacs[0];
+        destAddr = m_staMacs[1]->GetDevice()->GetAddress();
+        break;
+    case WifiTrafficPattern::STA_TO_AP:
+        m_sourceMac = m_staMacs[0];
+        destAddr = m_apMac->GetDevice()->GetAddress();
+        break;
+    case WifiTrafficPattern::AP_TO_STA:
+        m_sourceMac = m_apMac;
+        destAddr = m_staMacs[1]->GetDevice()->GetAddress();
+        break;
+    case WifiTrafficPattern::AP_TO_BCAST:
+        m_sourceMac = m_apMac;
+        destAddr = Mac48Address::GetBroadcast();
+        break;
+    case WifiTrafficPattern::STA_TO_BCAST:
+        m_sourceMac = m_staMacs[0];
+        destAddr = Mac48Address::GetBroadcast();
+        break;
+    }
+
+    PacketSocketHelper packetSocket;
+    packetSocket.Install(m_apMac->GetDevice()->GetNode());
+    packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
+    packetSocket.Install(m_staMacs[1]->GetDevice()->GetNode());
+
+    PacketSocketAddress socket;
+    socket.SetSingleDevice(m_sourceMac->GetDevice()->GetIfIndex());
+    socket.SetPhysicalAddress(destAddr);
+    socket.SetProtocol(1);
+
+    // install first client application generating at most 4 packets
+    auto client1 = CreateObject<PacketSocketClient>();
+    client1->SetAttribute("PacketSize", UintegerValue(1000));
+    client1->SetAttribute("MaxPackets", UintegerValue(std::min<std::size_t>(m_nPackets, 4)));
+    client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+    client1->SetRemote(socket);
+    m_sourceMac->GetDevice()->GetNode()->AddApplication(client1);
+    client1->SetStartTime(Seconds(0)); // now
+    client1->SetStopTime(duration);
+
+    if (m_nPackets > 4)
+    {
+        // install a second client application generating the remaining packets
+        auto client2 = CreateObject<PacketSocketClient>();
+        client2->SetAttribute("PacketSize", UintegerValue(1000));
+        client2->SetAttribute("MaxPackets", UintegerValue(m_nPackets - 4));
+        client2->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+        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->SetStopTime(duration);
+    }
+
+    // install a server on all nodes
+    for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
+    {
+        Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
+        server->SetLocal(socket);
+        (*nodeIt)->AddApplication(server);
+        server->SetStartTime(Seconds(0)); // now
+        server->SetStopTime(duration);
+    }
+
+    for (std::size_t nodeId = 0; nodeId < NodeList::GetNNodes(); nodeId++)
+    {
+        Config::ConnectWithoutContext("/NodeList/" + std::to_string(nodeId) +
+                                          "/ApplicationList/*/$ns3::PacketSocketServer/Rx",
+                                      MakeCallback(&MultiLinkTxTest::L7Receive, this).Bind(nodeId));
+    }
+
+    Simulator::Stop(duration);
+}
+
+void
+MultiLinkTxTest::DoRun()
+{
+    Simulator::Run();
+
+    // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
+    std::array<std::size_t, 3> expectedRxPkts{};
+
+    switch (m_trafficPattern)
+    {
+    case WifiTrafficPattern::STA_TO_STA:
+    case WifiTrafficPattern::AP_TO_STA:
+        // only STA 1 receives the m_nPackets packets that have been transmitted
+        expectedRxPkts[2] = m_nPackets;
+        break;
+    case WifiTrafficPattern::STA_TO_AP:
+        // only the AP receives the m_nPackets packets that have been transmitted
+        expectedRxPkts[0] = m_nPackets;
+        break;
+    case WifiTrafficPattern::AP_TO_BCAST:
+        // the AP replicates the broadcast frames on all the links, hence each station
+        // receives the m_nPackets packets N times, where N is the number of setup link
+        expectedRxPkts[1] = m_nPackets * m_staMacs[0]->GetSetupLinkIds().size();
+        expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
+        break;
+    case WifiTrafficPattern::STA_TO_BCAST:
+        // the AP receives the m_nPackets packets and then replicates them on all the links,
+        // hence STA 1 receives m_nPackets packets N times, where N is the number of setup link
+        expectedRxPkts[0] = m_nPackets;
+        expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
+        break;
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(+m_rxPkts[0],
+                          +expectedRxPkts[0],
+                          "Unexpected number of packets received by the AP");
+    NS_TEST_EXPECT_MSG_EQ(+m_rxPkts[1],
+                          +expectedRxPkts[1],
+                          "Unexpected number of packets received by STA 0");
+    NS_TEST_EXPECT_MSG_EQ(+m_rxPkts[2],
+                          +expectedRxPkts[2],
+                          "Unexpected number of packets received by STA 1");
+
+    // check that the expected number of BlockAck frames are transmitted
+    if (m_baEnabled && m_nMaxInflight == 1)
+    {
+        std::size_t expectedBaCount = 0;
+        std::size_t expectedBarCount = 0;
+
+        switch (m_trafficPattern)
+        {
+        case WifiTrafficPattern::STA_TO_AP:
+        case WifiTrafficPattern::AP_TO_STA:
+            // two A-MPDUs are transmitted and one BlockAck is corrupted
+            expectedBaCount = 3;
+            // one BlockAckReq is sent if m_useBarAfterMissedBa is true
+            expectedBarCount = m_useBarAfterMissedBa ? 1 : 0;
+            break;
+        case WifiTrafficPattern::STA_TO_STA:
+        case WifiTrafficPattern::STA_TO_BCAST:
+            // only one A-MPDU is transmitted and the BlockAck is not corrupted
+            expectedBaCount = 1;
+            break;
+        default:;
+        }
+        NS_TEST_EXPECT_MSG_EQ(m_blockAckCount,
+                              expectedBaCount,
+                              "Unexpected number of BlockAck frames");
+        NS_TEST_EXPECT_MSG_EQ(m_blockAckReqCount,
+                              expectedBarCount,
+                              "Unexpected number of BlockAckReq frames");
+    }
+
+    // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
+    // We do not support sending an MPDU multiple times concurrently without Block Ack
+    // agreement. Also, broadcast frames are already duplicated and sent on all links.
+    if (m_baEnabled && m_trafficPattern != WifiTrafficPattern::AP_TO_BCAST)
+    {
+        NS_TEST_EXPECT_MSG_EQ(
+            m_inflightCount.size(),
+            m_nPackets / 2,
+            "Did not collect number of simultaneous transmissions for all data frames");
+
+        auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
+        std::size_t maxCount = 0;
+        for (const auto& [seqNo, count] : m_inflightCount)
+        {
+            NS_TEST_EXPECT_MSG_LT_OR_EQ(
+                count,
+                nMaxInflight,
+                "MPDU with seqNo=" << seqNo
+                                   << " transmitted simultaneously more times than allowed");
+            maxCount = std::max(maxCount, count);
+        }
+
+        NS_TEST_EXPECT_MSG_EQ(
+            maxCount,
+            nMaxInflight,
+            "Expected that at least one data frame was transmitted simultaneously a number of "
+            "times equal to the NMaxInflights attribute");
+    }
+
+    Simulator::Destroy();
+}
+
+/**
+ * Tested MU traffic patterns.
+ */
+enum class WifiMuTrafficPattern : uint8_t
+{
+    DL_MU_BAR_BA_SEQUENCE = 0,
+    DL_MU_MU_BAR,
+    DL_MU_AGGR_MU_BAR,
+    UL_MU
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test data transmission between MLDs using OFDMA MU transmissions
+ *
+ * This test sets up an AP MLD and two non-AP MLDs having a variable number of links.
+ * The RF channels to set each link to are provided as input parameters through the test
+ * case constructor, along with the identifiers (starting at 0) of the links that cannot
+ * switch PHY band (if any). This test aims at veryfing the successful transmission of both
+ * DL MU and UL MU frames. In the DL MU scenarios, the client applications installed on the
+ * AP generate 8 packets addressed to each of the stations (plus 3 packets to trigger the
+ * establishment of BlockAck agreements). In the UL MU scenario, client applications
+ * installed on the stations generate 4 packets each (plus 3 packets to trigger the
+ * establishment of BlockAck agreements).
+ *
+ * The maximum A-MSDU size is set such that two packets can be aggregated in an A-MSDU.
+ * The MPDU with sequence number equal to 3 is corrupted (by using a post reception error
+ * model) once and for a single station, to test its successful re-transmission.
+ *
+ * Also, we enable the concurrent transmission of data frames over two links and check that at
+ * least one MPDU is concurrently transmitted over two links.
+ */
+class MultiLinkMuTxTest : public MultiLinkOperationsTestBase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param muTrafficPattern the pattern of traffic to generate
+     * \param useBarAfterMissedBa whether a BAR or Data frames are sent after missed BlockAck
+     * \param nMaxInflight the max number of links on which an MPDU can be simultaneously inflight
+     *                     (unused if Block Ack agreements are not established)
+     * \param staChannels the strings specifying the operating channels for the STA
+     * \param apChannels the strings specifying the operating channels for the AP
+     * \param fixedPhyBands list of IDs of STA links that cannot switch PHY band
+     */
+    MultiLinkMuTxTest(WifiMuTrafficPattern muTrafficPattern,
+                      WifiUseBarAfterMissedBa useBarAfterMissedBa,
+                      uint8_t nMaxInflight,
+                      const std::vector<std::string>& staChannels,
+                      const std::vector<std::string>& apChannels,
+                      const std::vector<uint8_t>& fixedPhyBands = {});
+    ~MultiLinkMuTxTest() override = default;
+
+  protected:
+    /**
+     * Function to trace packets received by the server application
+     * \param nodeId the ID of the node that received the packet
+     * \param p the packet
+     * \param addr the address
+     */
+    void L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr);
+
+    /**
+     * Check the content of a received BlockAck frame when the max number of links on which
+     * an MPDU can be inflight is one.
+     *
+     * \param psdu the PSDU containing the BlockAck
+     * \param txVector the TXVECTOR used to transmit the BlockAck
+     * \param linkId the ID of the link on which the BlockAck was transmitted
+     */
+    void CheckBlockAck(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector, uint8_t linkId);
+
+    void Transmit(uint8_t linkId,
+                  std::string context,
+                  WifiConstPsduMap psduMap,
+                  WifiTxVector txVector,
+                  double txPowerW) override;
+    void DoSetup() override;
+    void DoRun() override;
+
+  private:
+    void StartTraffic() override;
+
+    /// Receiver address-indexed map of list error models
+    using RxErrorModelMap = std::unordered_map<Mac48Address, Ptr<ListErrorModel>, WifiAddressHash>;
+
+    /// A pair of a MAC address (the address of the receiver for DL frames and the address of
+    /// the sender for UL frames) and a sequence number identifying a transmitted QoS data frame
+    using AddrSeqNoPair = std::pair<Mac48Address, uint16_t>;
+
+    RxErrorModelMap m_errorModels;                  ///< error rate models to corrupt packets
+    std::list<uint64_t> m_uidList;                  ///< list of UIDs of packets to corrupt
+    std::optional<Mac48Address> m_dataCorruptedSta; ///< MAC address of the station that received
+                                                    ///< MPDU with SeqNo=2 corrupted
+    bool m_waitFirstTf{true}; ///< whether we are waiting for the first Basic Trigger Frame
+    WifiMuTrafficPattern m_muTrafficPattern; ///< the pattern of traffic to generate
+    bool m_useBarAfterMissedBa;              ///< whether to send BAR after missed BlockAck
+    std::size_t m_nMaxInflight; ///< max number of links on which an MPDU can be inflight
+    std::vector<PacketSocketAddress> m_sockets; ///< packet socket addresses for STAs
+    std::size_t m_nPackets;                     ///< number of application packets to generate
+    std::size_t m_blockAckCount{0};             ///< transmitted BlockAck counter
+    // std::size_t m_blockAckReqCount{0};     ///< transmitted BlockAckReq counter
+    std::array<std::size_t, 3> m_rxPkts{}; ///< number of packets received at application layer
+                                           ///< by each node (AP, STA 0, STA 1)
+    std::map<AddrSeqNoPair, std::size_t> m_inflightCount; ///< max number of simultaneous
+                                                          ///< transmissions of each data frame
+    Ptr<WifiMac> m_sourceMac; ///< MAC of the node sending application packets
+};
+
+MultiLinkMuTxTest::MultiLinkMuTxTest(WifiMuTrafficPattern muTrafficPattern,
+                                     WifiUseBarAfterMissedBa useBarAfterMissedBa,
+                                     uint8_t nMaxInflight,
+                                     const std::vector<std::string>& staChannels,
+                                     const std::vector<std::string>& apChannels,
+                                     const std::vector<uint8_t>& fixedPhyBands)
+    : MultiLinkOperationsTestBase(
+          std::string("Check MU data transmission between MLDs ") +
+              (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
+                   ? "(send BAR after BlockAck timeout,"
+                   : "(send Data frames after BlockAck timeout,") +
+              " MU Traffic pattern: " + std::to_string(static_cast<uint8_t>(muTrafficPattern)) +
+              ", nMaxInflight=" + std::to_string(nMaxInflight) + ")",
+          2,
+          staChannels,
+          apChannels,
+          fixedPhyBands),
+      m_muTrafficPattern(muTrafficPattern),
+      m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
+      m_nMaxInflight(nMaxInflight),
+      m_sockets(m_nStations),
+      m_nPackets(muTrafficPattern == WifiMuTrafficPattern::UL_MU ? 4 : 8)
+{
+}
+
+void
+MultiLinkMuTxTest::Transmit(uint8_t linkId,
+                            std::string context,
+                            WifiConstPsduMap psduMap,
+                            WifiTxVector txVector,
+                            double txPowerW)
+{
+    CtrlTriggerHeader trigger;
+
+    for (const auto& [staId, psdu] : psduMap)
+    {
+        switch (psdu->GetHeader(0).GetType())
+        {
+        case WIFI_MAC_QOSDATA:
+            CheckAddresses(psdu);
+            if (psdu->GetHeader(0).HasData())
+            {
+                bool isDl = psdu->GetHeader(0).IsFromDs();
+                auto linkAddress =
+                    isDl ? psdu->GetHeader(0).GetAddr1() : psdu->GetHeader(0).GetAddr2();
+                auto address = m_apMac->GetMldAddress(linkAddress).value_or(linkAddress);
+
+                for (const auto& mpdu : *psdu)
+                {
+                    // determine the max number of simultaneous transmissions for this MPDU
+                    auto seqNo = mpdu->GetHeader().GetSequenceNumber();
+                    auto [it, success] = m_inflightCount.insert(
+                        {{address, seqNo}, mpdu->GetInFlightLinkIds().size()});
+                    if (!success)
+                    {
+                        it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
+                    }
+                }
+                for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
+                {
+                    // MPDUs with seqNo=2 are always transmitted in an MU PPDU
+                    if (psdu->GetHeader(i).GetSequenceNumber() == 2)
+                    {
+                        if (m_muTrafficPattern == WifiMuTrafficPattern::UL_MU)
+                        {
+                            NS_TEST_EXPECT_MSG_EQ(txVector.IsUlMu(),
+                                                  true,
+                                                  "MPDU " << **std::next(psdu->begin(), i)
+                                                          << " not transmitted in a TB PPDU");
+                        }
+                        else
+                        {
+                            NS_TEST_EXPECT_MSG_EQ(txVector.GetHeMuUserInfoMap().size(),
+                                                  2,
+                                                  "MPDU " << **std::next(psdu->begin(), i)
+                                                          << " not transmitted in a DL MU PPDU");
+                        }
+                    }
+                    // corrupt QoS data frame with sequence number equal to 3 (only once)
+                    if (psdu->GetHeader(i).GetSequenceNumber() != 3)
+                    {
+                        continue;
+                    }
+                    auto uid = psdu->GetPayload(i)->GetUid();
+                    if (!m_dataCorruptedSta)
+                    {
+                        m_uidList.push_front(uid);
+                        m_dataCorruptedSta = isDl ? psdu->GetAddr1() : psdu->GetAddr2();
+                        NS_LOG_INFO("CORRUPTED");
+                        m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
+                    }
+                    else if ((isDl && m_dataCorruptedSta == psdu->GetAddr1()) ||
+                             (!isDl && m_dataCorruptedSta == psdu->GetAddr2()))
+                    {
+                        // do not corrupt the QoS data frame anymore
+                        if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
+                            it != m_uidList.cend())
+                        {
+                            m_uidList.erase(it);
+                        }
+                        m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
+                    }
+                    break;
+                }
+            }
+            break;
+        case WIFI_MAC_CTL_BACKRESP:
+            if (m_nMaxInflight > 1)
+            {
+                // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
+                break;
+            }
+            CheckBlockAck(psdu, txVector, linkId);
+            m_blockAckCount++;
+            // to simulate a missed BlockAck, corrupt the fifth BlockAck frame (the first
+            // two BlockAck frames are sent to acknowledge the QoS data frames that triggered
+            // the establishment of Block Ack agreements)
+            if (m_blockAckCount == 5)
+            {
+                // corrupt the third BlockAck frame to simulate a missed BlockAck
+                m_uidList.push_front(psdu->GetPacket()->GetUid());
+                NS_LOG_INFO("CORRUPTED");
+                m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
+            }
+            break;
+        case WIFI_MAC_CTL_TRIGGER:
+            psdu->GetPayload(0)->PeekHeader(trigger);
+            // the MU scheduler requests channel access on all links but we have to perform the
+            // following actions only once (hence why we only consider TF transmitted on link 0)
+            if (trigger.IsBasic() && m_waitFirstTf)
+            {
+                m_waitFirstTf = false;
+                // the AP is starting the transmission of the Basic Trigger frame, so generate
+                // the configured number of packets at STAs, which are sent in TB PPDUs
+                auto band = m_apMac->GetWifiPhy(linkId)->GetPhyBand();
+                Time txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, band);
+                for (uint8_t i = 0; i < m_nStations; i++)
+                {
+                    Ptr<PacketSocketClient> client = CreateObject<PacketSocketClient>();
+                    client->SetAttribute("PacketSize", UintegerValue(450));
+                    client->SetAttribute("MaxPackets", UintegerValue(m_nPackets));
+                    client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+                    client->SetAttribute("Priority", UintegerValue(i * 4)); // 0 and 4
+                    client->SetRemote(m_sockets[i]);
+                    m_staMacs[i]->GetDevice()->GetNode()->AddApplication(client);
+                    client->SetStartTime(txDuration);  // start when TX ends
+                    client->SetStopTime(Seconds(1.0)); // stop in a second
+                    client->Initialize();
+                }
+            }
+            break;
+        default:;
+        }
+    }
+
+    MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
+}
+
+void
+MultiLinkMuTxTest::CheckBlockAck(Ptr<const WifiPsdu> psdu,
+                                 const WifiTxVector& txVector,
+                                 uint8_t linkId)
+{
+    /*
+     * Example sequence with DL_MU_BAR_BA_SEQUENCE
+     *                  ┌───────┬───────X
+     *           (To:1) │   2   │   3   │
+     *                  ├───────┼───────┤   ┌───┐             ┌───────┐
+     *  [link 0] (To:0) │   2   │   3   │   │BAR│      (To:1) │   3   │
+     *  ────────────────┴───────┴───────┴┬──┼───┼──┬──────────┴───────┴┬───┬────────
+     *                                   │BA│   │BA│                   │ACK│
+     *                                   └──┘   └──┘                   └───┘
+     *                              ┌───────┬───────┐
+     *                       (To:1) │   4   │   5   │
+     *                              ├───────┼───────┤        ┌───┐   ┌───┐
+     *  [link 1]             (To:0) │   4   │   5   │        │BAR│   │BAR│
+     *  ────────────────────────────┴───────┴───────┴┬──X────┴───┴┬──┼───┼──┬───────
+     *                                               │BA│         │BA│   │BA│
+     *                                               └──┘         └──┘   └──┘
+     *
+     * Example sequence with UL_MU
+     *
+     *           ┌──┐                     ┌────┐                      ┌───┐
+     *  [link 0] │TF│                     │M-BA│                      │ACK│
+     *  ─────────┴──┴──┬───────┬───────┬──┴────┴────────────┬───────┬─┴───┴─────────
+     *        (From:0) │   2   │   3   │           (From:1) │   3   │
+     *                 ├───────┼───────┤                    └───────┘
+     *        (From:1) │   2   │   3   │
+     *                 └───────┴───────X
+     *           ┌──┐
+     *  [link 1] │TF│
+     *  ─────────┴──┴──┬───────────────┬────────────────────────────────────────────
+     *        (From:0) │   QoS Null    │
+     *                 ├───────────────┤
+     *        (From:1) │   QoS Null    │
+     *                 └───────────────┘
+     */
+    auto mpdu = *psdu->begin();
+    CtrlBAckResponseHeader blockAck;
+    mpdu->GetPacket()->PeekHeader(blockAck);
+    bool isMpdu3corrupted;
+
+    switch (m_blockAckCount)
+    {
+    case 0:
+    case 1: // Ignore the first two BlockAck frames that acknowledged frames sent to establish BA
+        break;
+    case 2:
+        if (m_muTrafficPattern == WifiMuTrafficPattern::UL_MU)
+        {
+            NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA BlockAck");
+            for (uint8_t i = 0; i < m_nStations; i++)
+            {
+                auto indices = blockAck.FindPerAidTidInfoWithAid(m_staMacs[i]->GetAssociationId());
+                NS_TEST_ASSERT_MSG_EQ(indices.size(), 1, "Expected one Per AID TID Info per STA");
+                auto index = indices.front();
+                NS_TEST_ASSERT_MSG_EQ(m_dataCorruptedSta.has_value(),
+                                      true,
+                                      "Expected that a QoS data frame was corrupted");
+                isMpdu3corrupted =
+                    m_staMacs[i]->GetLinkIdByAddress(*m_dataCorruptedSta).has_value();
+                NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(2, index),
+                                      true,
+                                      "MPDU 2 expected to be successfully received");
+                NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(3, index),
+                                      !isMpdu3corrupted,
+                                      "Unexpected reception status for MPDU 3");
+            }
+
+            break;
+        }
+    case 3:
+        // BlockAck frames in response to the first DL MU PPDU
+        isMpdu3corrupted = (mpdu->GetHeader().GetAddr2() == m_dataCorruptedSta);
+        NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(2),
+                              true,
+                              "MPDU 2 expected to be successfully received");
+        NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(3),
+                              !isMpdu3corrupted,
+                              "Unexpected reception status for MPDU 3");
+        // in case of DL MU, if there are at least two links setup, we expect all MPDUs to
+        // be inflight (on distinct links)
+        if (m_muTrafficPattern != WifiMuTrafficPattern::UL_MU &&
+            m_staMacs[0]->GetSetupLinkIds().size() > 1)
+        {
+            auto queue = m_apMac->GetTxopQueue(AC_BE);
+            Ptr<StaWifiMac> rcvMac;
+            if (m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress() ==
+                mpdu->GetHeader().GetAddr2())
+            {
+                rcvMac = m_staMacs[0];
+            }
+            else if (m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress() ==
+                     mpdu->GetHeader().GetAddr2())
+            {
+                rcvMac = m_staMacs[1];
+            }
+            else
+            {
+                NS_ABORT_MSG("BlockAck frame not sent by a station in DL scenario");
+            }
+            auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
+            std::size_t nQueuedPkt = 0;
+            auto delay = WifiPhy::CalculateTxDuration(psdu,
+                                                      txVector,
+                                                      rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
+                         MicroSeconds(1); // to account for propagation delay
+
+            while (item)
+            {
+                auto seqNo = item->GetHeader().GetSequenceNumber();
+                NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
+                                      true,
+                                      "MPDU with seqNo=" << seqNo << " is not in flight");
+                auto linkIds = item->GetInFlightLinkIds();
+                NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
+                                      1,
+                                      "MPDU with seqNo=" << seqNo
+                                                         << " is in flight on multiple links");
+                // The first two MPDUs are in flight on the same link on which the BlockAck
+                // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
+                // in flight on a different link.
+                auto srcLinkId = m_apMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
+                NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
+                                      true,
+                                      "Addr1 of BlockAck is not an originator's link address");
+                NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
+                                      (seqNo <= 3),
+                                      "MPDU with seqNo=" << seqNo
+                                                         << " in flight on unexpected link");
+                // check the Retry subfield and whether this MPDU is still queued
+                // after the originator has processed this BlockAck
+
+                // MPDUs acknowledged via this BlockAck are no longer queued
+                bool isQueued = (seqNo > (isMpdu3corrupted ? 2 : 3));
+                // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
+                // is still queued) and has been transmitted on the same link as the BlockAck
+                // (i.e., its sequence number is less than or equal to 2)
+                bool isRetry = isQueued && seqNo <= 3;
+
+                Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
+                    NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
+                                          isQueued,
+                                          "MPDU with seqNo="
+                                              << item->GetHeader().GetSequenceNumber() << " should "
+                                              << (isQueued ? "" : "not") << " be queued");
+                    NS_TEST_EXPECT_MSG_EQ(
+                        item->GetHeader().IsRetry(),
+                        isRetry,
+                        "Unexpected value for the Retry subfield of the MPDU with seqNo="
+                            << item->GetHeader().GetSequenceNumber());
+                });
+
+                nQueuedPkt++;
+                item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
+            }
+            // Each MPDU contains an A-MSDU consisting of two MSDUs
+            NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
+        }
+        break;
+    }
+}
+
+void
+MultiLinkMuTxTest::L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr)
+{
+    NS_LOG_INFO("Packet received by NODE " << +nodeId << "\n");
+    m_rxPkts[nodeId]++;
+}
+
+void
+MultiLinkMuTxTest::DoSetup()
+{
+    switch (m_muTrafficPattern)
+    {
+    case WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE:
+        Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
+                           EnumValue(WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE));
+        break;
+    case WifiMuTrafficPattern::DL_MU_MU_BAR:
+        Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
+                           EnumValue(WifiAcknowledgment::DL_MU_TF_MU_BAR));
+        break;
+    case WifiMuTrafficPattern::DL_MU_AGGR_MU_BAR:
+        Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
+                           EnumValue(WifiAcknowledgment::DL_MU_AGGREGATE_TF));
+        break;
+    default:;
+    }
+
+    MultiLinkOperationsTestBase::DoSetup();
+
+    // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
+    for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
+    {
+        mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(1050));
+        mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
+                                             BooleanValue(m_useBarAfterMissedBa));
+        mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
+
+        mac->SetAttribute("VI_MaxAmsduSize", UintegerValue(1050));
+        mac->GetQosTxop(AC_VI)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
+                                             BooleanValue(m_useBarAfterMissedBa));
+        mac->GetQosTxop(AC_VI)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
+    }
+
+    // aggregate MU scheduler
+    auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>(
+        "EnableUlOfdma",
+        BooleanValue(m_muTrafficPattern == WifiMuTrafficPattern::UL_MU),
+        "EnableBsrp",
+        BooleanValue(false),
+        "UlPsduSize",
+        UintegerValue(2000));
+    m_apMac->AggregateObject(muScheduler);
+
+    // install post reception error model on all devices
+    for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+    {
+        auto errorModel = CreateObject<ListErrorModel>();
+        m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
+        m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
+    }
+    for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
+    {
+        auto errorModel = CreateObject<ListErrorModel>();
+        m_errorModels[m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
+        m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
+
+        errorModel = CreateObject<ListErrorModel>();
+        m_errorModels[m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
+        m_staMacs[1]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
+    }
+}
+
+void
+MultiLinkMuTxTest::StartTraffic()
+{
+    const Time duration = Seconds(1);
+    Address destAddr;
+
+    PacketSocketHelper packetSocket;
+    packetSocket.Install(m_apMac->GetDevice()->GetNode());
+    packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
+    packetSocket.Install(m_staMacs[1]->GetDevice()->GetNode());
+
+    if (m_muTrafficPattern < WifiMuTrafficPattern::UL_MU)
+    {
+        // DL Traffic
+        for (uint8_t i = 0; i < m_nStations; i++)
+        {
+            PacketSocketAddress socket;
+            socket.SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
+            socket.SetPhysicalAddress(m_staMacs[i]->GetDevice()->GetAddress());
+            socket.SetProtocol(1);
+
+            // the first client application generates three packets in order
+            // to trigger the establishment of a Block Ack agreement
+            auto client1 = CreateObject<PacketSocketClient>();
+            client1->SetAttribute("PacketSize", UintegerValue(450));
+            client1->SetAttribute("MaxPackets", UintegerValue(3));
+            client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+            client1->SetRemote(socket);
+            m_apMac->GetDevice()->GetNode()->AddApplication(client1);
+            client1->SetStartTime(i * MilliSeconds(50));
+            client1->SetStopTime(duration);
+
+            // the second client application generates the first half of the selected number
+            // of packets, which are sent in DL MU PPDUs.
+            auto client2 = CreateObject<PacketSocketClient>();
+            client2->SetAttribute("PacketSize", UintegerValue(450));
+            client2->SetAttribute("MaxPackets", UintegerValue(m_nPackets / 2));
+            client2->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+            client2->SetRemote(socket);
+            m_apMac->GetDevice()->GetNode()->AddApplication(client2);
+            // start after all BA agreements are established
+            client2->SetStartTime(m_nStations * MilliSeconds(50));
+            client2->SetStopTime(duration);
+
+            // the third client application generates the second half of the selected number
+            // of packets, which are sent in DL MU PPDUs.
+            auto client3 = CreateObject<PacketSocketClient>();
+            client3->SetAttribute("PacketSize", UintegerValue(450));
+            client3->SetAttribute("MaxPackets", UintegerValue(m_nPackets / 2));
+            client3->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+            client3->SetRemote(socket);
+            m_apMac->GetDevice()->GetNode()->AddApplication(client3);
+            // start during transmission of first A-MPDU, if multiple links are setup
+            client3->SetStartTime(m_nStations * MilliSeconds(50) + MilliSeconds(3));
+            client3->SetStopTime(duration);
+        }
+    }
+    else
+    {
+        // UL Traffic
+        for (uint8_t i = 0; i < m_nStations; i++)
+        {
+            m_sockets[i].SetSingleDevice(m_staMacs[i]->GetDevice()->GetIfIndex());
+            m_sockets[i].SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
+            m_sockets[i].SetProtocol(1);
+
+            // the first client application generates three packets in order
+            // to trigger the establishment of a Block Ack agreement
+            Ptr<PacketSocketClient> client1 = CreateObject<PacketSocketClient>();
+            client1->SetAttribute("PacketSize", UintegerValue(450));
+            client1->SetAttribute("MaxPackets", UintegerValue(3));
+            client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+            client1->SetAttribute("Priority", UintegerValue(i * 4)); // 0 and 4
+            client1->SetRemote(m_sockets[i]);
+            m_staMacs[i]->GetDevice()->GetNode()->AddApplication(client1);
+            client1->SetStartTime(i * MilliSeconds(50));
+            client1->SetStopTime(duration);
+
+            // packets to be included in TB PPDUs are generated (by Transmit()) when
+            // the first Basic Trigger Frame is sent by the AP
+        }
+
+        // MU scheduler starts requesting channel access when we are done with BA agreements
+        Simulator::Schedule(m_nStations * MilliSeconds(50), [this]() {
+            auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
+            NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "Expected an aggregated MU scheduler");
+            muScheduler->SetAccessReqInterval(MilliSeconds(3));
+            // channel access is requested only once
+            muScheduler->SetAccessReqInterval(Seconds(0));
+        });
+    }
+
+    // install a server on all nodes and connect traced callback
+    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);
+
+        Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
+        server->SetLocal(srvAddr);
+        (*nodeIt)->AddApplication(server);
+        server->SetStartTime(Seconds(0)); // now
+        server->SetStopTime(duration);
+        server->TraceConnectWithoutContext(
+            "Rx",
+            MakeCallback(&MultiLinkMuTxTest::L7Receive, this).Bind(device->GetNode()->GetId()));
+    }
+
+    Simulator::Stop(duration);
+}
+
+void
+MultiLinkMuTxTest::DoRun()
+{
+    Simulator::Run();
+
+    // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
+    std::array<std::size_t, 3> expectedRxPkts{};
+
+    switch (m_muTrafficPattern)
+    {
+    case WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE:
+    case WifiMuTrafficPattern::DL_MU_MU_BAR:
+    case WifiMuTrafficPattern::DL_MU_AGGR_MU_BAR:
+        // both STA 0 and STA 1 receive m_nPackets + 3 (sent to trigger BA establishment) packets
+        expectedRxPkts[1] = m_nPackets + 3;
+        expectedRxPkts[2] = m_nPackets + 3;
+        break;
+    case WifiMuTrafficPattern::UL_MU:
+        // AP receives m_nPackets + 3 (sent to trigger BA establishment) packets from each station
+        expectedRxPkts[0] = 2 * (m_nPackets + 3);
+        break;
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(+m_rxPkts[0],
+                          +expectedRxPkts[0],
+                          "Unexpected number of packets received by the AP");
+    NS_TEST_EXPECT_MSG_EQ(+m_rxPkts[1],
+                          +expectedRxPkts[1],
+                          "Unexpected number of packets received by STA 0");
+    NS_TEST_EXPECT_MSG_EQ(+m_rxPkts[2],
+                          +expectedRxPkts[2],
+                          "Unexpected number of packets received by STA 1");
+
+    // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
+    // For DL, for each station we send 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
+    // For UL, each station sends 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
+    NS_TEST_EXPECT_MSG_EQ(
+        m_inflightCount.size(),
+        2 * (2 + m_nPackets / 2),
+        "Did not collect number of simultaneous transmissions for all data frames");
+
+    auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
+    std::size_t maxCount = 0;
+    for (const auto& [txSeqNoPair, count] : m_inflightCount)
+    {
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(count,
+                                    nMaxInflight,
+                                    "MPDU with seqNo="
+                                        << txSeqNoPair.second
+                                        << " transmitted simultaneously more times than allowed");
+        maxCount = std::max(maxCount, count);
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(
+        maxCount,
+        nMaxInflight,
+        "Expected that at least one data frame was transmitted simultaneously a number of "
+        "times equal to the NMaxInflights attribute");
+
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test release of sequence numbers upon CTS timeout in multi-link operations
+ *
+ * In this test, an AP MLD and a non-AP MLD setup 3 links. Usage of RTS/CTS protection is
+ * enabled for frames whose length is at least 1000 bytes. The AP MLD receives a first set
+ * of 4 packets from the upper layer and sends an RTS frame, which is corrupted at the
+ * receiver, on a first link. When the RTS frame is transmitted, the AP MLD receives another
+ * set of 4 packets, which are transmitted after a successful RTS/CTS exchange on a second
+ * link. In the meantime, a new RTS/CTS exchange is successfully carried out (on the first
+ * link or on the third link) to transmit the first set of 4 packets. When the transmission
+ * of the first set of 4 packets starts, the AP MLD receives the third set of 4 packets from
+ * the upper layer, which are transmitted after a successful RTS/CTS exchange.
+ *
+ * This test checks that sequence numbers are correctly assigned to all the MPDUs carrying data.
+ */
+class ReleaseSeqNoAfterCtsTimeoutTest : public MultiLinkOperationsTestBase
+{
+  public:
+    ReleaseSeqNoAfterCtsTimeoutTest();
+    ~ReleaseSeqNoAfterCtsTimeoutTest() override = default;
+
+  protected:
+    void DoSetup() override;
+    void DoRun() override;
+    void Transmit(uint8_t linkId,
+                  std::string context,
+                  WifiConstPsduMap psduMap,
+                  WifiTxVector txVector,
+                  double txPowerW) override;
+
+  private:
+    void StartTraffic() override;
+
+    /**
+     * \return the client application generating 4 packets addressed to the non-AP MLD
+     */
+    Ptr<PacketSocketClient> GetApplication() const;
+
+    PacketSocketAddress m_socket;     //!< packet socket address
+    std::size_t m_nQosDataFrames;     //!< counter for transmitted QoS data frames
+    Ptr<ListErrorModel> m_errorModel; //!< error rate model to corrupt first RTS frame
+    bool m_rtsCorrupted;              //!< whether the first RTS frame has been corrupted
+};
+
+ReleaseSeqNoAfterCtsTimeoutTest::ReleaseSeqNoAfterCtsTimeoutTest()
+    : MultiLinkOperationsTestBase(
+          "Check sequence numbers after CTS timeout",
+          1,
+          {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
+          {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}),
+      m_nQosDataFrames(0),
+      m_errorModel(CreateObject<ListErrorModel>()),
+      m_rtsCorrupted(false)
+{
+}
+
+void
+ReleaseSeqNoAfterCtsTimeoutTest::DoSetup()
+{
+    // Enable RTS/CTS
+    Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("1000"));
+
+    MultiLinkOperationsTestBase::DoSetup();
+
+    // install post reception error model on all STAs affiliated with non-AP MLD
+    for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
+    {
+        m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
+    }
+}
+
+Ptr<PacketSocketClient>
+ReleaseSeqNoAfterCtsTimeoutTest::GetApplication() const
+{
+    const Time duration = Seconds(1);
+
+    auto client = CreateObject<PacketSocketClient>();
+    client->SetAttribute("PacketSize", UintegerValue(1000));
+    client->SetAttribute("MaxPackets", UintegerValue(4));
+    client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+    client->SetRemote(m_socket);
+    client->SetStartTime(Seconds(0)); // now
+    client->SetStopTime(duration);
+
+    return client;
+}
+
+void
+ReleaseSeqNoAfterCtsTimeoutTest::StartTraffic()
+{
+    const Time duration = Seconds(1);
+
+    PacketSocketHelper packetSocket;
+    packetSocket.Install(m_apMac->GetDevice()->GetNode());
+    packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
+
+    m_socket.SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
+    m_socket.SetPhysicalAddress(m_staMacs[0]->GetAddress());
+    m_socket.SetProtocol(1);
+
+    // install client application generating 4 packets
+    m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication());
+
+    // install a server on all nodes
+    for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
+    {
+        Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
+        server->SetLocal(m_socket);
+        (*nodeIt)->AddApplication(server);
+        server->SetStartTime(Seconds(0)); // now
+        server->SetStopTime(duration);
+    }
+}
+
+void
+ReleaseSeqNoAfterCtsTimeoutTest::Transmit(uint8_t linkId,
+                                          std::string context,
+                                          WifiConstPsduMap psduMap,
+                                          WifiTxVector txVector,
+                                          double txPowerW)
+{
+    auto psdu = psduMap.begin()->second;
+
+    if (psdu->GetHeader(0).IsRts() && !m_rtsCorrupted)
+    {
+        m_errorModel->SetList({psdu->GetPacket()->GetUid()});
+        m_rtsCorrupted = true;
+        // generate other packets when the first RTS is transmitted
+        m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication());
+    }
+    else if (psdu->GetHeader(0).IsQosData())
+    {
+        m_nQosDataFrames++;
+
+        if (m_nQosDataFrames == 2)
+        {
+            // generate other packets when the second QoS data frame is transmitted
+            m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication());
+        }
+    }
+
+    MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
+}
+
+void
+ReleaseSeqNoAfterCtsTimeoutTest::DoRun()
+{
+    Simulator::Stop(Seconds(1.0));
+    Simulator::Run();
+
+    NS_TEST_EXPECT_MSG_EQ(m_nQosDataFrames, 3, "Unexpected number of transmitted QoS data frames");
+
+    std::size_t count{};
+
+    for (const auto& txPsdu : m_txPsdus)
+    {
+        auto psdu = txPsdu.psduMap.begin()->second;
+
+        if (!psdu->GetHeader(0).IsQosData())
         {
-            // STA had to switch PHY band to match AP's operating band. Given that we
-            // are using three distinct spectrum channels (one per band), a STA will
-            // not receive Beacon frames after switching PHY band. After a number of
-            // missed Beacon frames, the link is disabled
-            NS_TEST_EXPECT_MSG_EQ(m_staMac->GetWifiPhy(linkId)->GetState()->IsStateOff(),
-                                  true,
-                                  "Expecting link " << +linkId
-                                                    << " to be disabled due to switching PHY band");
             continue;
         }
 
-        // the link has been setup and must be active
-        NS_TEST_EXPECT_MSG_EQ(m_staMac->GetWifiPhy(linkId)->GetState()->IsStateOff(),
-                              false,
-                              "Expecting link " << +linkId << " to be active");
+        NS_TEST_EXPECT_MSG_EQ(psdu->GetNMpdus(), 4, "Unexpected number of MPDUs in A-MPDU");
+
+        count++;
+        uint16_t expectedSeqNo{};
+
+        switch (count)
+        {
+        case 1:
+            expectedSeqNo = 4;
+            break;
+        case 2:
+            expectedSeqNo = 0;
+            break;
+        case 3:
+            expectedSeqNo = 8;
+            break;
+        }
+
+        for (const auto& mpdu : *PeekPointer(psdu))
+        {
+            NS_TEST_EXPECT_MSG_EQ(mpdu->GetHeader().GetSequenceNumber(),
+                                  expectedSeqNo++,
+                                  "Unexpected sequence number");
+        }
     }
+
+    Simulator::Destroy();
 }
 
 /**
@@ -799,71 +2505,149 @@
 WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite()
     : TestSuite("wifi-mlo", UNIT)
 {
+    using ParamsTuple =
+        std::tuple<std::vector<std::string>,                 // non-AP MLD channels
+                   std::vector<std::string>,                 // AP MLD channels
+                   std::vector<std::pair<uint8_t, uint8_t>>, // (STA link ID, AP link ID) of setup
+                                                             // links
+                   std::vector<uint8_t>>; // IDs of link that cannot change PHY band
+
     AddTestCase(new GetRnrLinkInfoTest(), TestCase::QUICK);
-    // matching channels: setup all links
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
-                    {{0, 0}, {1, 1}, {2, 2}}),
-                TestCase::QUICK);
-    // non-matching channels, matching PHY bands: setup all links
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{108, 0, BAND_5GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{120, 0, BAND_5GHZ, 0}", "{5, 0, BAND_6GHZ, 0}"},
-                    {{1, 0}, {0, 1}, {2, 2}}),
-                TestCase::QUICK);
-    // non-AP MLD switches band on some links to setup 3 links
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{9, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
-                    {{2, 0}, {0, 1}, {1, 2}}),
-                TestCase::QUICK);
-    // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
-    // that band, hence only 2 links are setup
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
-                    {{1, 0}, {2, 1}},
-                    {0}),
-                TestCase::QUICK);
-    // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
-    // that band; the second link of the non-AP MLD cannot change PHY band and there is
-    // an AP operating on the same channel; hence 2 links are setup
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
-                    {{1, 0}, {2, 1}},
-                    {0, 1}),
-                TestCase::QUICK);
-    // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
-    // that band; the second link of the non-AP MLD cannot change PHY band and there is
-    // an AP operating on the same channel; the third link of the non-AP MLD cannot
-    // change PHY band and there is an AP operating on the same band (different channel);
-    // hence 2 links are setup by switching channel (not band) on the third link
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{60, 0, BAND_5GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
-                    {{1, 0}, {2, 2}},
-                    {0, 1, 2}),
-                TestCase::QUICK);
-    // non-AP MLD has only two STAs and setups two links
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
-                    {{0, 1}, {1, 0}}),
-                TestCase::QUICK);
-    // single link non-AP STA associates with an AP affiliated with an AP MLD
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{120, 0, BAND_5GHZ, 0}"},
-                    {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
-                    {{0, 2}}),
-                TestCase::QUICK);
-    // a STA affiliated with a non-AP MLD associates with a single link AP
-    AddTestCase(new MultiLinkSetupTest(
-                    {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
-                    {"{120, 0, BAND_5GHZ, 0}"},
-                    {{2, 0}}),
-                TestCase::QUICK);
+
+    for (const auto& [staChannels, apChannels, setupLinks, fixedPhyBands] :
+         {// matching channels: setup all links
+          ParamsTuple({"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
+                      {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
+                      {{0, 0}, {1, 1}, {2, 2}},
+                      {}),
+          // non-matching channels, matching PHY bands: setup all links
+          ParamsTuple({"{108, 0, BAND_5GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
+                      {"{36, 0, BAND_5GHZ, 0}", "{120, 0, BAND_5GHZ, 0}", "{5, 0, BAND_6GHZ, 0}"},
+                      {{1, 0}, {0, 1}, {2, 2}},
+                      {}),
+          // non-AP MLD switches band on some links to setup 3 links
+          ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
+                      {"{36, 0, BAND_5GHZ, 0}", "{9, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+                      {{2, 0}, {0, 1}, {1, 2}},
+                      {}),
+          // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
+          // that band, hence only 2 links are setup
+          ParamsTuple(
+              {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
+              {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+              {{1, 0}, {2, 1}},
+              {0}),
+          // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
+          // that band; the second link of the non-AP MLD cannot change PHY band and there is
+          // an AP operating on the same channel; hence 2 links are setup
+          ParamsTuple(
+              {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
+              {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+              {{1, 0}, {2, 1}},
+              {0, 1}),
+          // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
+          // that band; the second link of the non-AP MLD cannot change PHY band and there is
+          // an AP operating on the same channel; the third link of the non-AP MLD cannot
+          // change PHY band and there is an AP operating on the same band (different channel);
+          // hence 2 links are setup by switching channel (not band) on the third link
+          ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{60, 0, BAND_5GHZ, 0}"},
+                      {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+                      {{1, 0}, {2, 2}},
+                      {0, 1, 2}),
+          // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
+          // that band; the second link of the non-AP MLD cannot change PHY band and there is
+          // an AP operating on the same channel; hence one link only is setup
+          ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
+                      {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+                      {{1, 0}},
+                      {0, 1}),
+          // non-AP MLD has only two STAs and setups two links
+          ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
+                      {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+                      {{0, 1}, {1, 0}},
+                      {}),
+          // single link non-AP STA associates with an AP affiliated with an AP MLD
+          ParamsTuple({"{120, 0, BAND_5GHZ, 0}"},
+                      {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+                      {{0, 2}},
+                      {}),
+          // a STA affiliated with a non-AP MLD associates with a single link AP
+          ParamsTuple({"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
+                      {"{120, 0, BAND_5GHZ, 0}"},
+                      {{2, 0}},
+                      {})})
+    {
+        AddTestCase(new MultiLinkSetupTest(staChannels, apChannels, setupLinks, fixedPhyBands),
+                    TestCase::QUICK);
+
+        for (const auto& trafficPattern : {WifiTrafficPattern::STA_TO_STA,
+                                           WifiTrafficPattern::STA_TO_AP,
+                                           WifiTrafficPattern::AP_TO_STA,
+                                           WifiTrafficPattern::AP_TO_BCAST,
+                                           WifiTrafficPattern::STA_TO_BCAST})
+        {
+            // No Block Ack agreement
+            AddTestCase(new MultiLinkTxTest(trafficPattern,
+                                            WifiBaEnabled::NO,
+                                            WifiUseBarAfterMissedBa::NO,
+                                            1,
+                                            staChannels,
+                                            apChannels,
+                                            fixedPhyBands),
+                        TestCase::QUICK);
+            for (const auto& useBarAfterMissedBa :
+                 {WifiUseBarAfterMissedBa::YES, WifiUseBarAfterMissedBa::NO})
+            {
+                // Block Ack agreement with nMaxInflight=1
+                AddTestCase(new MultiLinkTxTest(trafficPattern,
+                                                WifiBaEnabled::YES,
+                                                useBarAfterMissedBa,
+                                                1,
+                                                staChannels,
+                                                apChannels,
+                                                fixedPhyBands),
+                            TestCase::QUICK);
+                // Block Ack agreement with nMaxInflight=2
+                AddTestCase(new MultiLinkTxTest(trafficPattern,
+                                                WifiBaEnabled::YES,
+                                                useBarAfterMissedBa,
+                                                2,
+                                                staChannels,
+                                                apChannels,
+                                                fixedPhyBands),
+                            TestCase::QUICK);
+            }
+        }
+
+        for (const auto& muTrafficPattern : {WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE,
+                                             WifiMuTrafficPattern::DL_MU_MU_BAR,
+                                             WifiMuTrafficPattern::DL_MU_AGGR_MU_BAR,
+                                             WifiMuTrafficPattern::UL_MU})
+        {
+            for (const auto& useBarAfterMissedBa :
+                 {WifiUseBarAfterMissedBa::YES, WifiUseBarAfterMissedBa::NO})
+            {
+                // Block Ack agreement with nMaxInflight=1
+                AddTestCase(new MultiLinkMuTxTest(muTrafficPattern,
+                                                  useBarAfterMissedBa,
+                                                  1,
+                                                  staChannels,
+                                                  apChannels,
+                                                  fixedPhyBands),
+                            TestCase::QUICK);
+                // Block Ack agreement with nMaxInflight=2
+                AddTestCase(new MultiLinkMuTxTest(muTrafficPattern,
+                                                  useBarAfterMissedBa,
+                                                  2,
+                                                  staChannels,
+                                                  apChannels,
+                                                  fixedPhyBands),
+                            TestCase::QUICK);
+            }
+        }
+    }
+
+    AddTestCase(new ReleaseSeqNoAfterCtsTimeoutTest(), TestCase::QUICK);
 }
 
 static WifiMultiLinkOperationsTestSuite g_wifiMultiLinkOperationsTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-non-ht-dup-test.cc ns-3.38/src/wifi/test/wifi-non-ht-dup-test.cc
--- ns-3.37/src/wifi/test/wifi-non-ht-dup-test.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/test/wifi-non-ht-dup-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -0,0 +1,993 @@
+/*
+ * Copyright (c) 2022
+ *
+ * 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: Sébastien Deronne <sebastien.deronne@gmail.com>
+ */
+
+#include "ns3/ap-wifi-mac.h"
+#include "ns3/boolean.h"
+#include "ns3/constant-position-mobility-model.h"
+#include "ns3/he-configuration.h"
+#include "ns3/he-phy.h"
+#include "ns3/interference-helper.h"
+#include "ns3/log.h"
+#include "ns3/mobility-helper.h"
+#include "ns3/multi-model-spectrum-channel.h"
+#include "ns3/nist-error-rate-model.h"
+#include "ns3/node.h"
+#include "ns3/non-communicating-net-device.h"
+#include "ns3/rng-seed-manager.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/test.h"
+#include "ns3/waveform-generator.h"
+#include "ns3/wifi-mac-header.h"
+#include "ns3/wifi-net-device.h"
+#include "ns3/wifi-psdu.h"
+#include "ns3/wifi-spectrum-signal-parameters.h"
+#include "ns3/wifi-spectrum-value-helper.h"
+#include "ns3/wifi-utils.h"
+
+#include <vector>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("WifiNonHtDuplicateTest");
+
+constexpr uint32_t DEFAULT_FREQUENCY = 5180; // MHz
+
+/**
+ * HE PHY used for testing MU-RTS/CTS.
+ */
+class MuRtsCtsHePhy : public HePhy
+{
+  public:
+    MuRtsCtsHePhy();
+    ~MuRtsCtsHePhy() override;
+
+    /**
+     * Set the previous TX PPDU UID counter.
+     *
+     * \param uid the value to which the previous TX PPDU UID counter should be set
+     */
+    void SetPreviousTxPpduUid(uint64_t uid);
+
+    /**
+     * Set the TXVECTOR of the previously transmitted MU-RTS.
+     *
+     * \param muRtsTxVector the TXVECTOR used to transmit MU-RTS trigger frame
+     */
+    void SetMuRtsTxVector(const WifiTxVector& muRtsTxVector);
+}; // class MuRtsCtsHePhy
+
+MuRtsCtsHePhy::MuRtsCtsHePhy()
+    : HePhy()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+MuRtsCtsHePhy::~MuRtsCtsHePhy()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+void
+MuRtsCtsHePhy::SetPreviousTxPpduUid(uint64_t uid)
+{
+    NS_LOG_FUNCTION(this << uid);
+    m_previouslyTxPpduUid = uid;
+}
+
+void
+MuRtsCtsHePhy::SetMuRtsTxVector(const WifiTxVector& muRtsTxVector)
+{
+    NS_LOG_FUNCTION(this << muRtsTxVector);
+    m_currentTxVector = muRtsTxVector;
+}
+
+/**
+ * Spectrum PHY used for testing MU-RTS/CTS.
+ */
+class MuRtsCtsSpectrumWifiPhy : public SpectrumWifiPhy
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    MuRtsCtsSpectrumWifiPhy();
+    ~MuRtsCtsSpectrumWifiPhy() override;
+
+    void DoInitialize() override;
+    void DoDispose() override;
+
+    /**
+     * Set the global PPDU UID counter.
+     *
+     * \param uid the value to which the global PPDU UID counter should be set
+     */
+    void SetPpduUid(uint64_t uid);
+
+    /**
+     * Set the TXVECTOR of the previously transmitted MU-RTS.
+     *
+     * \param muRtsTxVector the TXVECTOR used to transmit MU-RTS trigger frame
+     */
+    void SetMuRtsTxVector(const WifiTxVector& muRtsTxVector);
+
+  private:
+    Ptr<MuRtsCtsHePhy> m_muRtsCtsHePhy; ///< Pointer to HE PHY instance used for MU-RTS/CTS PHY test
+};                                      // class MuRtsCtsSpectrumWifiPhy
+
+TypeId
+MuRtsCtsSpectrumWifiPhy::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::MuRtsCtsSpectrumWifiPhy").SetParent<SpectrumWifiPhy>().SetGroupName("Wifi");
+    return tid;
+}
+
+MuRtsCtsSpectrumWifiPhy::MuRtsCtsSpectrumWifiPhy()
+    : SpectrumWifiPhy()
+{
+    NS_LOG_FUNCTION(this);
+    m_muRtsCtsHePhy = Create<MuRtsCtsHePhy>();
+    m_muRtsCtsHePhy->SetOwner(this);
+}
+
+MuRtsCtsSpectrumWifiPhy::~MuRtsCtsSpectrumWifiPhy()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+void
+MuRtsCtsSpectrumWifiPhy::DoInitialize()
+{
+    // Replace HE PHY instance with test instance
+    m_phyEntities[WIFI_MOD_CLASS_HE] = m_muRtsCtsHePhy;
+    SpectrumWifiPhy::DoInitialize();
+}
+
+void
+MuRtsCtsSpectrumWifiPhy::DoDispose()
+{
+    m_muRtsCtsHePhy = nullptr;
+    SpectrumWifiPhy::DoDispose();
+}
+
+void
+MuRtsCtsSpectrumWifiPhy::SetPpduUid(uint64_t uid)
+{
+    NS_LOG_FUNCTION(this << uid);
+    m_muRtsCtsHePhy->SetPreviousTxPpduUid(uid);
+    m_previouslyRxPpduUid = uid;
+}
+
+void
+MuRtsCtsSpectrumWifiPhy::SetMuRtsTxVector(const WifiTxVector& muRtsTxVector)
+{
+    NS_LOG_FUNCTION(this << muRtsTxVector);
+    m_muRtsCtsHePhy->SetMuRtsTxVector(muRtsTxVector);
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief non-HT duplicate PHY reception test
+ * The test consists in an AP sending a single non-HT duplicate PPDU
+ * of a given channel width (multiple of 20 MHz) over a spectrum
+ * channel and it checks whether the STAs attached to the channel
+ * receive the PPDU. If an interference is injected on a given 20 MHz
+ * subchannel, the payload reception should fail, otherwise it should succeed.
+ */
+class TestNonHtDuplicatePhyReception : public TestCase
+{
+  public:
+    /// A vector containing parameters per STA: the standard, the center frequency and the P20 index
+    using StasParams = std::vector<std::tuple<WifiStandard, uint16_t, uint8_t>>;
+
+    /**
+     * Constructor
+     * \param apStandard the standard to use for the AP
+     * \param apFrequency the center frequency of the AP (in MHz)
+     * \param apP20Index the index of the primary 20 MHz channel of the AP
+     * \param stasParams the parameters of the STAs (\see StasParams)
+     * \param per20MhzInterference flags per 20 MHz subchannel whether an interference should be
+     * generated on that subchannel. An empty vector means that the test will not generate any
+     * interference.
+     */
+    TestNonHtDuplicatePhyReception(WifiStandard apStandard,
+                                   uint16_t apFrequency,
+                                   uint8_t apP20Index,
+                                   StasParams stasParams,
+                                   std::vector<bool> per20MhzInterference = {});
+
+  private:
+    void DoSetup() override;
+    void DoTeardown() override;
+    void DoRun() override;
+
+    /**
+     * Receive success function
+     * \param index index of the RX STA
+     * \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(std::size_t index,
+                   Ptr<const WifiPsdu> psdu,
+                   RxSignalInfo rxSignalInfo,
+                   WifiTxVector txVector,
+                   std::vector<bool> statusPerMpdu);
+
+    /**
+     * Receive failure function
+     * \param index index of the RX STA
+     * \param psdu the PSDU
+     */
+    void RxFailure(std::size_t index, Ptr<const WifiPsdu> psdu);
+
+    /**
+     * Check the results
+     * \param index index of the RX STA
+     * \param expectedRxSuccess the expected number of RX success
+     * \param expectedRxFailure the expected number of RX failures
+     */
+    void CheckResults(std::size_t index, uint32_t expectedRxSuccess, uint32_t expectedRxFailure);
+
+    /**
+     * Reset the results
+     */
+    void ResetResults();
+
+    /**
+     * Send non-HT duplicate PPDU function
+     * \param channelWidth the channel width to use to transmit the non-HT PPDU (in MHz)
+     */
+    void SendNonHtDuplicatePpdu(uint16_t channelWidth);
+
+    /**
+     * Generate interference function
+     * \param interferer the PHY of the interferer to use to generate the signal
+     * \param interferencePsd the PSD of the interference to be generated
+     * \param duration the duration of the interference
+     */
+    void GenerateInterference(Ptr<WaveformGenerator> interferer,
+                              Ptr<SpectrumValue> interferencePsd,
+                              Time duration);
+    /**
+     * Stop interference function
+     * \param interferer the PHY of the interferer that was used to generate the signal
+     */
+    void StopInterference(Ptr<WaveformGenerator> interferer);
+
+    WifiStandard m_apStandard; ///< the standard to use for the AP
+    uint16_t m_apFrequency;    ///< the center frequency of the AP (in MHz)
+    uint8_t m_apP20Index;      ///< the index of the primary 20 MHz channel of the AP
+    StasParams m_stasParams;   ///< the parameters of the STAs
+    std::vector<bool>
+        m_per20MhzInterference; ///< flags per 20 MHz subchannel whether an interference should be
+                                ///< generated on that subchannel
+
+    std::vector<uint32_t> m_countRxSuccessStas; ///< count RX success for STAs
+    std::vector<uint32_t> m_countRxFailureStas; ///< count RX failure for STAs
+
+    Ptr<SpectrumWifiPhy> m_phyAp;                ///< PHY of AP
+    std::vector<Ptr<SpectrumWifiPhy>> m_phyStas; ///< PHYs of STAs
+
+    std::vector<Ptr<WaveformGenerator>>
+        m_phyInterferers; ///< PHYs of interferers (1 interferer per 20 MHz subchannel)
+};
+
+TestNonHtDuplicatePhyReception::TestNonHtDuplicatePhyReception(
+    WifiStandard apStandard,
+    uint16_t apFrequency,
+    uint8_t apP20Index,
+    StasParams stasParams,
+    std::vector<bool> per20MhzInterference)
+    : TestCase{"non-HT duplicate PHY reception test"},
+      m_apStandard{apStandard},
+      m_apFrequency{apFrequency},
+      m_apP20Index{apP20Index},
+      m_stasParams{stasParams},
+      m_per20MhzInterference{per20MhzInterference},
+      m_countRxSuccessStas{},
+      m_countRxFailureStas{},
+      m_phyStas{}
+{
+}
+
+void
+TestNonHtDuplicatePhyReception::ResetResults()
+{
+    for (auto& countRxSuccess : m_countRxSuccessStas)
+    {
+        countRxSuccess = 0;
+    }
+    for (auto& countRxFailure : m_countRxFailureStas)
+    {
+        countRxFailure = 0;
+    }
+}
+
+void
+TestNonHtDuplicatePhyReception::SendNonHtDuplicatePpdu(uint16_t channelWidth)
+{
+    NS_LOG_FUNCTION(this << channelWidth);
+    WifiTxVector txVector = WifiTxVector(OfdmPhy::GetOfdmRate24Mbps(),
+                                         0,
+                                         WIFI_PREAMBLE_LONG,
+                                         800,
+                                         1,
+                                         1,
+                                         0,
+                                         channelWidth,
+                                         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_phyAp->CalculateTxDuration(psdu->GetSize(), txVector, m_phyAp->GetPhyBand());
+
+    m_phyAp->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
+}
+
+void
+TestNonHtDuplicatePhyReception::GenerateInterference(Ptr<WaveformGenerator> interferer,
+                                                     Ptr<SpectrumValue> interferencePsd,
+                                                     Time duration)
+{
+    NS_LOG_FUNCTION(this << interferer << duration);
+    interferer->SetTxPowerSpectralDensity(interferencePsd);
+    interferer->SetPeriod(duration);
+    interferer->Start();
+    Simulator::Schedule(duration,
+                        &TestNonHtDuplicatePhyReception::StopInterference,
+                        this,
+                        interferer);
+}
+
+void
+TestNonHtDuplicatePhyReception::StopInterference(Ptr<WaveformGenerator> interferer)
+{
+    NS_LOG_FUNCTION(this << interferer);
+    interferer->Stop();
+}
+
+void
+TestNonHtDuplicatePhyReception::RxSuccess(std::size_t index,
+                                          Ptr<const WifiPsdu> psdu,
+                                          RxSignalInfo rxSignalInfo,
+                                          WifiTxVector txVector,
+                                          std::vector<bool> /*statusPerMpdu*/)
+{
+    NS_LOG_FUNCTION(this << index << *psdu << rxSignalInfo << txVector);
+    m_countRxSuccessStas.at(index)++;
+}
+
+void
+TestNonHtDuplicatePhyReception::RxFailure(std::size_t index, Ptr<const WifiPsdu> psdu)
+{
+    NS_LOG_FUNCTION(this << index << *psdu);
+    m_countRxFailureStas.at(index)++;
+}
+
+void
+TestNonHtDuplicatePhyReception::CheckResults(std::size_t index,
+                                             uint32_t expectedRxSuccess,
+                                             uint32_t expectedRxFailure)
+{
+    NS_LOG_FUNCTION(this << index << expectedRxSuccess << expectedRxFailure);
+    NS_TEST_ASSERT_MSG_EQ(m_countRxSuccessStas.at(index),
+                          expectedRxSuccess,
+                          "The number of successfully received packets by STA "
+                              << index << " is not correct!");
+    NS_TEST_ASSERT_MSG_EQ(m_countRxFailureStas.at(index),
+                          expectedRxFailure,
+                          "The number of unsuccessfully received packets by STA "
+                              << index << " is not correct!");
+}
+
+void
+TestNonHtDuplicatePhyReception::DoSetup()
+{
+    auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    auto lossModel = CreateObject<FriisPropagationLossModel>();
+    lossModel->SetFrequency(m_apFrequency * 1e6);
+    spectrumChannel->AddPropagationLossModel(lossModel);
+    auto delayModel = CreateObject<ConstantSpeedPropagationDelayModel>();
+    spectrumChannel->SetPropagationDelayModel(delayModel);
+
+    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->ConfigureStandard(WIFI_STANDARD_80211ax);
+    auto apMobility = CreateObject<ConstantPositionMobilityModel>();
+    m_phyAp->SetMobility(apMobility);
+    apDev->SetPhy(m_phyAp);
+    apNode->AggregateObject(apMobility);
+    apNode->AddDevice(apDev);
+
+    for (const auto& staParams : m_stasParams)
+    {
+        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->ConfigureStandard(std::get<0>(staParams));
+        staPhy->SetReceiveOkCallback(
+            MakeCallback(&TestNonHtDuplicatePhyReception::RxSuccess, this).Bind(m_phyStas.size()));
+        staPhy->SetReceiveErrorCallback(
+            MakeCallback(&TestNonHtDuplicatePhyReception::RxFailure, this).Bind(m_phyStas.size()));
+        auto staMobility = CreateObject<ConstantPositionMobilityModel>();
+        staPhy->SetMobility(staMobility);
+        staDev->SetPhy(staPhy);
+        staNode->AggregateObject(staMobility);
+        staNode->AddDevice(staDev);
+        m_phyStas.push_back(staPhy);
+        m_countRxSuccessStas.push_back(0);
+        m_countRxFailureStas.push_back(0);
+    }
+
+    if (!m_per20MhzInterference.empty())
+    {
+        [[maybe_unused]] auto [channelNum, centerFreq, apChannelWidth, type, phyBand] =
+            (*WifiPhyOperatingChannel::FindFirst(0,
+                                                 m_apFrequency,
+                                                 0,
+                                                 m_apStandard,
+                                                 WIFI_PHY_BAND_5GHZ));
+        NS_ASSERT(m_per20MhzInterference.size() == (apChannelWidth / 20));
+        for (std::size_t i = 0; i < m_per20MhzInterference.size(); ++i)
+        {
+            auto interfererNode = CreateObject<Node>();
+            auto interfererDev = CreateObject<NonCommunicatingNetDevice>();
+            auto phyInterferer = CreateObject<WaveformGenerator>();
+            phyInterferer->SetDevice(interfererDev);
+            phyInterferer->SetChannel(spectrumChannel);
+            phyInterferer->SetDutyCycle(1);
+            interfererNode->AddDevice(interfererDev);
+            m_phyInterferers.push_back(phyInterferer);
+        }
+    }
+}
+
+void
+TestNonHtDuplicatePhyReception::DoTeardown()
+{
+    m_phyAp->Dispose();
+    m_phyAp = nullptr;
+    for (auto phySta : m_phyStas)
+    {
+        phySta->Dispose();
+        phySta = nullptr;
+    }
+    for (auto phyInterferer : m_phyInterferers)
+    {
+        phyInterferer->Dispose();
+        phyInterferer = nullptr;
+    }
+}
+
+void
+TestNonHtDuplicatePhyReception::DoRun()
+{
+    RngSeedManager::SetSeed(1);
+    RngSeedManager::SetRun(1);
+    int64_t streamNumber = 0;
+    m_phyAp->AssignStreams(streamNumber);
+    for (auto phySta : m_phyStas)
+    {
+        phySta->AssignStreams(streamNumber);
+    }
+
+    [[maybe_unused]] auto [apChannelNum, centerFreq, apChannelWidth, type, phyBand] =
+        (*WifiPhyOperatingChannel::FindFirst(0,
+                                             m_apFrequency,
+                                             0,
+                                             m_apStandard,
+                                             WIFI_PHY_BAND_5GHZ));
+    m_phyAp->SetOperatingChannel(
+        WifiPhy::ChannelTuple{apChannelNum, apChannelWidth, WIFI_PHY_BAND_5GHZ, m_apP20Index});
+
+    auto index = 0;
+    for (const auto& [staStandard, staFrequency, staP20Index] : m_stasParams)
+    {
+        [[maybe_unused]] auto [staChannelNum, centerFreq, staChannelWidth, type, phyBand] =
+            (*WifiPhyOperatingChannel::FindFirst(0,
+                                                 staFrequency,
+                                                 0,
+                                                 staStandard,
+                                                 WIFI_PHY_BAND_5GHZ));
+        m_phyStas.at(index++)->SetOperatingChannel(
+            WifiPhy::ChannelTuple{staChannelNum, staChannelWidth, WIFI_PHY_BAND_5GHZ, staP20Index});
+    }
+
+    index = 0;
+    const auto minApCenterFrequency =
+        m_phyAp->GetFrequency() - (m_phyAp->GetChannelWidth() / 2) + (20 / 2);
+    for (auto channelWidth = 20; channelWidth <= apChannelWidth; channelWidth *= 2, ++index)
+    {
+        if (!m_phyInterferers.empty())
+        {
+            for (std::size_t i = 0; i < m_phyInterferers.size(); ++i)
+            {
+                if (!m_per20MhzInterference.at(i))
+                {
+                    continue;
+                }
+                BandInfo bandInfo;
+                bandInfo.fc = (minApCenterFrequency + (i * 20)) * 1e6;
+                bandInfo.fl = bandInfo.fc - (5 * 1e6);
+                bandInfo.fh = bandInfo.fc + (5 * 1e6);
+                Bands bands;
+                bands.push_back(bandInfo);
+                auto spectrumInterference = Create<SpectrumModel>(bands);
+                auto interferencePsd = Create<SpectrumValue>(spectrumInterference);
+                auto interferencePower = 0.005; // in watts (designed to make PHY headers reception
+                                                // successful but payload reception fail)
+                *interferencePsd = interferencePower / 10e6;
+                Simulator::Schedule(Seconds(index),
+                                    &TestNonHtDuplicatePhyReception::GenerateInterference,
+                                    this,
+                                    m_phyInterferers.at(i),
+                                    interferencePsd,
+                                    Seconds(0.5));
+            }
+        }
+        const auto apCenterFreq =
+            m_phyAp->GetOperatingChannel().GetPrimaryChannelCenterFrequency(channelWidth);
+        const auto apMinFreq = apCenterFreq - (channelWidth / 2);
+        const auto apMaxFreq = apCenterFreq + (channelWidth / 2);
+        Simulator::Schedule(Seconds(index + 0.1),
+                            &TestNonHtDuplicatePhyReception::SendNonHtDuplicatePpdu,
+                            this,
+                            channelWidth);
+        for (std::size_t i = 0; i < m_stasParams.size(); ++i)
+        {
+            const auto p20Width = 20;
+            const auto staP20Freq =
+                m_phyStas.at(i)->GetOperatingChannel().GetPrimaryChannelCenterFrequency(p20Width);
+            const auto staP20MinFreq = staP20Freq - (p20Width / 2);
+            const auto staP20MaxFreq = staP20Freq + (p20Width / 2);
+            bool expectRx = (staP20MinFreq >= apMinFreq && staP20MaxFreq <= apMaxFreq);
+            bool expectSuccess = true;
+            if (!m_per20MhzInterference.empty())
+            {
+                const auto index20MhzSubBand = ((staP20Freq - minApCenterFrequency) / 20);
+                expectSuccess = !m_per20MhzInterference.at(index20MhzSubBand);
+            }
+            Simulator::Schedule(Seconds(index + 0.5),
+                                &TestNonHtDuplicatePhyReception::CheckResults,
+                                this,
+                                i,
+                                expectRx ? expectSuccess : 0,
+                                expectRx ? !expectSuccess : 0);
+        }
+        Simulator::Schedule(Seconds(index + 0.5),
+                            &TestNonHtDuplicatePhyReception::ResetResults,
+                            this);
+    }
+
+    Simulator::Run();
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief test PHY reception of multiple CTS frames as a response to a MU-RTS frame.
+ * The test is checking whether the reception of multiple identical CTS frames as a response to a
+ * MU-RTS frame is successfully received by the AP PHY and that only a single CTS frame is forwarded
+ * up to the MAC. Since the test is focusing on the PHY reception of multiple CTS response, the
+ * transmission of the MU-RTS frame is faked.
+ */
+class TestMultipleCtsResponsesFromMuRts : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     * \param bwPerSta the bandwidth to use for each STA (in MHz)
+     */
+    TestMultipleCtsResponsesFromMuRts(const std::vector<uint16_t>& bwPerSta);
+
+  private:
+    void DoSetup() override;
+    void DoTeardown() override;
+    void DoRun() override;
+
+    /**
+     * Function called to fake the transmission of a MU-RTS.
+     *
+     * \param bw the bandwidth to use for the transmission in MHz
+     */
+    void FakePreviousMuRts(uint16_t bw);
+
+    /**
+     * Function called to trigger a CTS frame sent by a STA using non-HT duplicate.
+     *
+     * \param phy the PHY of the STA
+     * \param bw the bandwidth to use for the transmission in MHz
+     */
+    void TxNonHtDuplicateCts(Ptr<SpectrumWifiPhy> phy, uint16_t bw);
+
+    /**
+     * CTS RX 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 RxCtsSuccess(Ptr<const WifiPsdu> psdu,
+                      RxSignalInfo rxSignalInfo,
+                      WifiTxVector txVector,
+                      std::vector<bool> statusPerMpdu);
+
+    /**
+     * CTS RX failure function
+     * \param psdu the PSDU
+     */
+    void RxCtsFailure(Ptr<const WifiPsdu> psdu);
+
+    /**
+     * Check the results
+     * \param expectedRxCtsSuccess the expected number of CTS RX success
+     * \param expectedRxCtsFailure the expected number of CTS RX failures
+     */
+    void CheckResults(std::size_t expectedRxCtsSuccess, std::size_t expectedRxCtsFailure);
+
+    Ptr<MuRtsCtsSpectrumWifiPhy> m_phyAp;                ///< AP PHY
+    std::vector<Ptr<MuRtsCtsSpectrumWifiPhy>> m_phyStas; ///< STAs PHYs
+
+    std::vector<uint16_t> m_bwPerSta; ///< Bandwidth per STA in MHz
+
+    std::size_t m_countRxCtsSuccess; ///< count the number of successfully received CTS frames
+    std::size_t m_countRxCtsFailure; ///< count the number of unsuccessfully received CTS frames
+
+    double m_stasTxPowerDbm; ///< TX power in dBm configured for the STAs
+};
+
+TestMultipleCtsResponsesFromMuRts::TestMultipleCtsResponsesFromMuRts(
+    const std::vector<uint16_t>& bwPerSta)
+    : TestCase{"test PHY reception of multiple CTS frames following a MU-RTS frame"},
+      m_bwPerSta{bwPerSta},
+      m_countRxCtsSuccess{0},
+      m_countRxCtsFailure{0},
+      m_stasTxPowerDbm(10.0)
+{
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::FakePreviousMuRts(uint16_t bw)
+{
+    NS_LOG_FUNCTION(this << bw);
+
+    WifiTxVector txVector;
+    txVector.SetChannelWidth(bw); // only the channel width matters for this test
+
+    // set the TXVECTOR and the UID of the previously transmitted MU-RTS in the AP PHY
+    m_phyAp->SetMuRtsTxVector(txVector);
+    m_phyAp->SetPpduUid(0);
+
+    // set the UID of the previously received MU-RTS in the STAs PHYs
+    for (auto& phySta : m_phyStas)
+    {
+        phySta->SetPpduUid(0);
+    }
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::TxNonHtDuplicateCts(Ptr<SpectrumWifiPhy> phy, uint16_t bw)
+{
+    NS_LOG_FUNCTION(this << phy << bw);
+
+    WifiTxVector txVector =
+        WifiTxVector(OfdmPhy::GetOfdmRate54Mbps(), // use less robust modulation for test purpose
+                     0,
+                     WIFI_PREAMBLE_LONG,
+                     800,
+                     1,
+                     1,
+                     0,
+                     bw,
+                     false,
+                     false);
+    txVector.SetTriggerResponding(true);
+
+    WifiMacHeader hdr;
+    hdr.SetType(WIFI_MAC_CTL_CTS);
+    hdr.SetDsNotFrom();
+    hdr.SetDsNotTo();
+    hdr.SetNoMoreFragments();
+    hdr.SetNoRetry();
+
+    auto pkt = Create<Packet>();
+    auto mpdu = Create<WifiMpdu>(pkt, hdr);
+    auto psdu = Create<WifiPsdu>(mpdu, false);
+
+    phy->Send(psdu, txVector);
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::RxCtsSuccess(Ptr<const WifiPsdu> psdu,
+                                                RxSignalInfo rxSignalInfo,
+                                                WifiTxVector txVector,
+                                                std::vector<bool> /*statusPerMpdu*/)
+{
+    NS_LOG_FUNCTION(this << *psdu << rxSignalInfo << txVector);
+    NS_TEST_EXPECT_MSG_EQ_TOL(rxSignalInfo.rssi,
+                              WToDbm(DbmToW(m_stasTxPowerDbm) * m_bwPerSta.size()),
+                              0.1,
+                              "RX power is not correct!");
+    m_countRxCtsSuccess++;
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::RxCtsFailure(Ptr<const WifiPsdu> psdu)
+{
+    NS_LOG_FUNCTION(this << *psdu);
+    m_countRxCtsFailure++;
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::CheckResults(std::size_t expectedRxCtsSuccess,
+                                                std::size_t expectedRxCtsFailure)
+{
+    NS_TEST_ASSERT_MSG_EQ(m_countRxCtsSuccess,
+                          expectedRxCtsSuccess,
+                          "The number of successfully received CTS frames by AP is not correct!");
+    NS_TEST_ASSERT_MSG_EQ(m_countRxCtsFailure,
+                          expectedRxCtsFailure,
+                          "The number of unsuccessfully received CTS frames by AP is not correct!");
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::DoSetup()
+{
+    RngSeedManager::SetSeed(1);
+    RngSeedManager::SetRun(1);
+    int64_t streamNumber = 0;
+
+    auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    auto lossModel = CreateObject<FriisPropagationLossModel>();
+    lossModel->SetFrequency(DEFAULT_FREQUENCY * 1e6);
+    spectrumChannel->AddPropagationLossModel(lossModel);
+    auto delayModel = CreateObject<ConstantSpeedPropagationDelayModel>();
+    spectrumChannel->SetPropagationDelayModel(delayModel);
+
+    auto apNode = CreateObject<Node>();
+    auto apDev = CreateObject<WifiNetDevice>();
+    auto apMac = CreateObject<ApWifiMac>();
+    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->ConfigureStandard(WIFI_STANDARD_80211ax);
+    m_phyAp->AssignStreams(streamNumber);
+
+    m_phyAp->SetReceiveOkCallback(
+        MakeCallback(&TestMultipleCtsResponsesFromMuRts::RxCtsSuccess, this));
+    m_phyAp->SetReceiveErrorCallback(
+        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));
+
+    m_phyAp->SetOperatingChannel(WifiPhy::ChannelTuple{channelNum, apBw, WIFI_PHY_BAND_5GHZ, 0});
+
+    auto apMobility = CreateObject<ConstantPositionMobilityModel>();
+    m_phyAp->SetMobility(apMobility);
+    apDev->SetPhy(m_phyAp);
+    apDev->SetStandard(WIFI_STANDARD_80211ax);
+    apDev->SetHeConfiguration(CreateObject<HeConfiguration>());
+    apMac->SetWifiPhys({m_phyAp});
+    apNode->AggregateObject(apMobility);
+    apNode->AddDevice(apDev);
+
+    for (std::size_t i = 0; i < m_bwPerSta.size(); ++i)
+    {
+        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->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));
+
+        phySta->SetOperatingChannel(
+            WifiPhy::ChannelTuple{channelNum, m_bwPerSta.at(i), WIFI_PHY_BAND_5GHZ, 0});
+
+        auto staMobility = CreateObject<ConstantPositionMobilityModel>();
+        phySta->SetMobility(staMobility);
+        staDev->SetPhy(phySta);
+        staDev->SetStandard(WIFI_STANDARD_80211ax);
+        staDev->SetHeConfiguration(CreateObject<HeConfiguration>());
+        staNode->AggregateObject(staMobility);
+        staNode->AddDevice(staDev);
+        m_phyStas.push_back(phySta);
+    }
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::DoTeardown()
+{
+    m_phyAp->Dispose();
+    m_phyAp = nullptr;
+    for (auto& phySta : m_phyStas)
+    {
+        phySta->Dispose();
+        phySta = nullptr;
+    }
+    m_phyStas.clear();
+}
+
+void
+TestMultipleCtsResponsesFromMuRts::DoRun()
+{
+    // Fake transmission of a MU-RTS frame preceding the CTS responses
+    Simulator::Schedule(Seconds(0.0),
+                        &TestMultipleCtsResponsesFromMuRts::FakePreviousMuRts,
+                        this,
+                        *std::max_element(m_bwPerSta.cbegin(), m_bwPerSta.cend()));
+
+    std::size_t index = 1;
+    for (auto& phySta : m_phyStas)
+    {
+        // Transmit CTS responses over their operating bandwidth with 1 nanosecond delay between
+        // each other
+        const auto delay = index * NanoSeconds(1.0);
+        Simulator::Schedule(Seconds(0.0) + delay,
+                            &TestMultipleCtsResponsesFromMuRts::TxNonHtDuplicateCts,
+                            this,
+                            phySta,
+                            m_bwPerSta.at(index - 1));
+        index++;
+    }
+
+    // Verify successful reception of the CTS frames: since multiple copies are sent
+    // simultaneously, a single CTS frame should be forwarded up to the MAC.
+    Simulator::Schedule(Seconds(1.0), &TestMultipleCtsResponsesFromMuRts::CheckResults, this, 1, 0);
+
+    Simulator::Run();
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief wifi non-HT duplicate Test Suite
+ */
+class WifiNonHtDuplicateTestSuite : public TestSuite
+{
+  public:
+    WifiNonHtDuplicateTestSuite();
+};
+
+WifiNonHtDuplicateTestSuite::WifiNonHtDuplicateTestSuite()
+    : TestSuite("wifi-non-ht-dup", UNIT)
+{
+    /**
+     * Channel map:
+     *
+     *                | 20MHz  | 20MHz  | 20MHz  | 20MHz  |
+     *
+     *                ┌────────┬────────┬────────┬────────┐
+     *  AP 802.11ax   │CH 36(P)│ CH 40  │ CH 44  │ CH 48  │
+     *                └────────┴────────┴────────┴────────┘
+     *
+     *                ┌────────┐
+     *  STA1 802.11a  │ CH 36  │
+     *                └────────┘
+     *
+     *                         ┌────────┐
+     *  STA2 802.11n           │ CH 40  │
+     *                         └────────┘
+     *
+     *                                  ┌────────┬────────┐
+     *  STA3 802.11ac                   │CH 44(P)│ CH 48  │
+     *                                  └────────┴────────┘
+     *
+     * Test scenario:
+     *                ┌────────┐       ┌──────────────────────┐
+     *                │        │       │RX non-HT PPDU @ STA 1│
+     *                │ 80 MHz │       └──────────────────────┘
+     *                │ non-HT │       ┌──────────────────────┐
+     *                │  PPDU  │       │RX non-HT PPDU @ STA 2│
+     *                │  sent  │       └──────────────────────┘
+     *                │  from  │       ┌──────────────────────┐
+     *                │   AP   │       │                      │
+     *                │        │       │RX non-HT PPDU @ STA 3│
+     *                │        │       │                      │
+     *                └────────┘       └──────────────────────┘
+     */
+    AddTestCase(new TestNonHtDuplicatePhyReception(WIFI_STANDARD_80211ax,
+                                                   5210,
+                                                   0,
+                                                   {{WIFI_STANDARD_80211a, 5180, 0},
+                                                    {WIFI_STANDARD_80211n, 5200, 0},
+                                                    {WIFI_STANDARD_80211ac, 5230, 0}}),
+                TestCase::QUICK);
+    /* same channel map and test scenario as previously but inject interference on channel 40 */
+    AddTestCase(new TestNonHtDuplicatePhyReception(WIFI_STANDARD_80211ax,
+                                                   5210,
+                                                   0,
+                                                   {{WIFI_STANDARD_80211a, 5180, 0},
+                                                    {WIFI_STANDARD_80211n, 5200, 0},
+                                                    {WIFI_STANDARD_80211ac, 5230, 0}},
+                                                   {false, true, false, false}),
+                TestCase::QUICK);
+
+    /* test PHY reception of multiple CTS responses following a MU-RTS */
+    /* 4 STAs operating on 20 MHz */
+    AddTestCase(new TestMultipleCtsResponsesFromMuRts({20, 20, 20, 20}), TestCase::QUICK);
+    /* 4 STAs operating on 40 MHz */
+    AddTestCase(new TestMultipleCtsResponsesFromMuRts({40, 40, 40, 40}), TestCase::QUICK);
+    /* 4 STAs operating on 80 MHz */
+    AddTestCase(new TestMultipleCtsResponsesFromMuRts({80, 80, 80, 80}), TestCase::QUICK);
+    /* 4 STAs operating on 160 MHz */
+    AddTestCase(new TestMultipleCtsResponsesFromMuRts({160, 160, 160, 160}), TestCase::QUICK);
+    /* 4 STAs operating on different bandwidths: 160, 80, 40 and 20 MHz */
+    AddTestCase(new TestMultipleCtsResponsesFromMuRts({160, 80, 40, 20}), TestCase::QUICK);
+}
+
+static WifiNonHtDuplicateTestSuite wifiNonHtDuplicateTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-phy-cca-test.cc ns-3.38/src/wifi/test/wifi-phy-cca-test.cc
--- ns-3.37/src/wifi/test/wifi-phy-cca-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-phy-cca-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -60,9 +60,11 @@
 const Time smallDelta = NanoSeconds(1);
 // add small delta to be right after aCCATime, since test checks are scheduled before wifi events
 const Time aCcaTime = MicroSeconds(4) + smallDelta;
-const std::map<uint16_t, Time> PpduDurations = {{20, NanoSeconds(1009600)},
-                                                {40, NanoSeconds(533600)},
-                                                {80, NanoSeconds(275200)}};
+const std::map<uint16_t, Time> PpduDurations = {
+    {20, NanoSeconds(1009600)},
+    {40, NanoSeconds(533600)},
+    {80, NanoSeconds(275200)},
+};
 
 /**
  * \ingroup wifi-test
@@ -287,7 +289,7 @@
 
     //-----------------------------------------------------------------------------------------------------------------------------------
 
-    // OFDM PHY: 20 MHz non-HT PPDU in primary channel (20 MHz) if power above CCA sensitivty
+    // 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(),
@@ -310,14 +312,14 @@
 
     //-----------------------------------------------------------------------------------------------------------------------------------
 
-    // HT PHY: 20 MHz HT PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
+    // 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),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
-    // HT PHY: 40 MHz HT PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
+    // 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),
@@ -352,29 +354,29 @@
 
     //-----------------------------------------------------------------------------------------------------------------------------------
 
-    // VHT PHY: 20 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
-    // threshold
+    // 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),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
-    // VHT PHY: 40 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
-    // threshold
+    // 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),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
-    // VHT PHY: 80 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
-    // threshold
+    // 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),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // VHT PHY: 160 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA
-    // sensitivty threshold
+    // sensitivity threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
                        CreateDummyVhtPpdu(160),
                        WIFI_CHANLIST_PRIMARY,
@@ -452,28 +454,28 @@
 
     //-----------------------------------------------------------------------------------------------------------------------------------
 
-    // HE PHY: 20 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
+    // 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),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
-    // HE PHY: 40 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
+    // 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),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
-    // HE PHY: 80 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
+    // 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),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
-    // HE PHY: 160 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivty
+    // 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),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-phy-ofdma-test.cc ns-3.38/src/wifi/test/wifi-phy-ofdma-test.cc
--- ns-3.37/src/wifi/test/wifi-phy-ofdma-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-phy-ofdma-test.cc	2023-03-16 20:54:23.872869300 -0700
@@ -149,6 +149,7 @@
     void DoDispose() override;
 
     using WifiPhy::Reset;
+    void StartTx(Ptr<const WifiPpdu> ppdu) override;
 
     /**
      * TracedCallback signature for UID of transmitted PPDU.
@@ -158,12 +159,6 @@
     typedef void (*TxPpduUidCallback)(uint64_t uid);
 
     /**
-     * \param ppdu the PPDU to send
-     * \param txVector the TXVECTOR used for the transmission of the PPDU
-     */
-    void StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector) override;
-
-    /**
      * Set the global PPDU UID counter.
      *
      * \param uid the value to which the global PPDU UID counter should be set
@@ -263,10 +258,10 @@
 }
 
 void
-OfdmaSpectrumWifiPhy::StartTx(Ptr<const WifiPpdu> ppdu, const WifiTxVector& txVector)
+OfdmaSpectrumWifiPhy::StartTx(Ptr<const WifiPpdu> ppdu)
 {
     m_phyTxPpduUidTrace(ppdu->GetUid());
-    SpectrumWifiPhy::StartTx(ppdu, txVector);
+    SpectrumWifiPhy::StartTx(ppdu);
 }
 
 std::map<std::pair<uint64_t, WifiPreamble>, Ptr<Event>>&
@@ -290,7 +285,7 @@
 Ptr<const HePhy>
 OfdmaSpectrumWifiPhy::GetHePhy() const
 {
-    return DynamicCast<const HePhy>(GetPhyEntity(WIFI_MOD_CLASS_HE));
+    return DynamicCast<const HePhy>(GetLatestPhyEntity());
 }
 
 /**
@@ -638,7 +633,7 @@
                           "The number of successfully received packets by STA 1 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxFailureSta1,
                           expectedRxFailure,
-                          "The number of unsuccessfully received packets by STA 1 is not correct!");
+                          "The number of unsuccessfuly received packets by STA 1 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesSta1,
                           expectedRxBytes,
                           "The number of bytes received by STA 1 is not correct!");
@@ -654,7 +649,7 @@
                           "The number of successfully received packets by STA 2 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxFailureSta2,
                           expectedRxFailure,
-                          "The number of unsuccessfully received packets by STA 2 is not correct!");
+                          "The number of unsuccessfuly received packets by STA 2 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesSta2,
                           expectedRxBytes,
                           "The number of bytes received by STA 2 is not correct!");
@@ -670,7 +665,7 @@
                           "The number of successfully received packets by STA 3 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxFailureSta3,
                           expectedRxFailure,
-                          "The number of unsuccessfully received packets by STA 3 is not correct!");
+                          "The number of unsuccessfuly received packets by STA 3 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesSta3,
                           expectedRxBytes,
                           "The number of bytes received by STA 3 is not correct!");
@@ -680,7 +675,7 @@
 TestDlOfdmaPhyTransmission::CheckPhyState(Ptr<OfdmaSpectrumWifiPhy> phy, WifiPhyState expectedState)
 {
     // This is needed to make sure PHY state will be checked as the last event if a state change
-    // occured at the exact same time as the check
+    // occurred at the exact same time as the check
     Simulator::ScheduleNow(&TestDlOfdmaPhyTransmission::DoCheckPhyState, this, phy, expectedState);
 }
 
@@ -1011,7 +1006,7 @@
                         m_phySta3,
                         WifiPhyState::CCA_BUSY);
 
-    // One PSDU of 1000 bytes should have been unsuccessfully received by STA 1 (since interference
+    // One PSDU of 1000 bytes should have been unsuccessfuly received by STA 1 (since interference
     // occupies RU 1)
     Simulator::Schedule(Seconds(3.1), &TestDlOfdmaPhyTransmission::CheckResultsSta1, this, 0, 1, 0);
     // One PSDU of 1500 bytes should have been successfully received by STA 2
@@ -1087,7 +1082,7 @@
                         1,
                         0,
                         1000);
-    // One PSDU of 1500 bytes should have been unsuccessfully received by STA 2 (since interference
+    // One PSDU of 1500 bytes should have been unsuccessfuly received by STA 2 (since interference
     // occupies RU 2)
     Simulator::Schedule(Seconds(4.1), &TestDlOfdmaPhyTransmission::CheckResultsSta2, this, 0, 1, 0);
     // No PSDU should have been received by STA3
@@ -1149,10 +1144,10 @@
                         m_phySta3,
                         WifiPhyState::CCA_BUSY);
 
-    // One PSDU of 1000 bytes should have been unsuccessfully received by STA 1 (since interference
+    // One PSDU of 1000 bytes should have been unsuccessfuly received by STA 1 (since interference
     // occupies RU 1)
     Simulator::Schedule(Seconds(5.1), &TestDlOfdmaPhyTransmission::CheckResultsSta1, this, 0, 1, 0);
-    // One PSDU of 1500 bytes should have been unsuccessfully received by STA 2 (since interference
+    // One PSDU of 1500 bytes should have been unsuccessfuly received by STA 2 (since interference
     // occupies RU 2)
     Simulator::Schedule(Seconds(5.1), &TestDlOfdmaPhyTransmission::CheckResultsSta2, this, 0, 1, 0);
     // No PSDU should have been received by STA3
@@ -1501,7 +1496,7 @@
                           "The number of successfully received packets by STA 1 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxFailureSta1,
                           expectedRxFailure,
-                          "The number of unsuccessfully received packets by STA 1 is not correct!");
+                          "The number of unsuccessfuly received packets by STA 1 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesSta1,
                           expectedRxBytes,
                           "The number of bytes received by STA 1 is not correct!");
@@ -1517,7 +1512,7 @@
                           "The number of successfully received packets by STA 2 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxFailureSta2,
                           expectedRxFailure,
-                          "The number of unsuccessfully received packets by STA 2 is not correct!");
+                          "The number of unsuccessfuly received packets by STA 2 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesSta2,
                           expectedRxBytes,
                           "The number of bytes received by STA 2 is not correct!");
@@ -1527,7 +1522,7 @@
 TestDlOfdmaPhyPuncturing::CheckPhyState(Ptr<OfdmaSpectrumWifiPhy> phy, WifiPhyState expectedState)
 {
     // This is needed to make sure PHY state will be checked as the last event if a state change
-    // occured at the exact same time as the check
+    // occurred at the exact same time as the check
     Simulator::ScheduleNow(&TestDlOfdmaPhyPuncturing::DoCheckPhyState, this, phy, expectedState);
 }
 
@@ -1715,7 +1710,7 @@
 
     if (m_indexSubchannel < 2) // interference in RU 1
     {
-        // One PSDU of 1000 bytes should have been unsuccessfully received by STA 1
+        // One PSDU of 1000 bytes should have been unsuccessfuly received by STA 1
         Simulator::Schedule(Seconds(1.1),
                             &TestDlOfdmaPhyPuncturing::CheckResultsSta1,
                             this,
@@ -1739,7 +1734,7 @@
                             1,
                             0,
                             1000);
-        // One PSDU of 1500 bytes should have been unsuccessfully received by STA 2
+        // One PSDU of 1500 bytes should have been unsuccessfuly received by STA 2
         Simulator::Schedule(Seconds(1.1),
                             &TestDlOfdmaPhyPuncturing::CheckResultsSta2,
                             this,
@@ -2111,10 +2106,11 @@
 {
     WifiConstPsduMap psdus1;
     WifiConstPsduMap psdus2;
+
     WifiTxVector txVector1 = WifiTxVector(HePhy::GetHeMcs7(),
                                           0,
                                           WIFI_PREAMBLE_HE_TB,
-                                          800,
+                                          1600,
                                           1,
                                           1,
                                           0,
@@ -2122,12 +2118,16 @@
                                           false,
                                           false);
     WifiTxVector txVector2 = txVector1;
+    WifiTxVector trigVector = txVector2;
 
     uint16_t rxStaId1 = 1;
     HeRu::RuSpec ru1(HeRu::RU_106_TONE, 1, false);
     txVector1.SetRu(ru1, rxStaId1);
     txVector1.SetMode(HePhy::GetHeMcs7(), rxStaId1);
     txVector1.SetNss(1, rxStaId1);
+    trigVector.SetRu(ru1, rxStaId1);
+    trigVector.SetMode(HePhy::GetHeMcs7(), rxStaId1);
+    trigVector.SetNss(1, rxStaId1);
 
     Ptr<Packet> pkt1 = Create<Packet>(1000);
     WifiMacHeader hdr1;
@@ -2143,6 +2143,9 @@
     txVector2.SetRu(ru2, rxStaId2);
     txVector2.SetMode(HePhy::GetHeMcs9(), rxStaId2);
     txVector2.SetNss(1, rxStaId2);
+    trigVector.SetRu(ru2, rxStaId2);
+    trigVector.SetMode(HePhy::GetHeMcs9(), rxStaId2);
+    trigVector.SetNss(1, rxStaId2);
 
     Ptr<Packet> pkt2 = Create<Packet>(1500);
     WifiMacHeader hdr2;
@@ -2170,6 +2173,9 @@
         HePhy::ConvertHeTbPpduDurationToLSigLength(txDuration, txVector2, m_phySta2->GetPhyBand())
             .first);
 
+    auto hePhyAp = DynamicCast<HePhy>(m_phyAp->GetPhyEntity(WIFI_MOD_CLASS_HE));
+    hePhyAp->SetTrigVector(trigVector, txDuration);
+
     m_phySta1->Send(psdus1, txVector1);
     m_phySta2->Send(psdus2, txVector2);
 }
@@ -2334,7 +2340,7 @@
       m_trigVector(HePhy::GetHeMcs7(),
                    0,
                    WIFI_PREAMBLE_HE_TB,
-                   800,
+                   1600,
                    1,
                    1,
                    0,
@@ -2399,7 +2405,7 @@
     WifiTxVector txVector = WifiTxVector(HePhy::GetHeMcs7(),
                                          0,
                                          WIFI_PREAMBLE_HE_TB,
-                                         800,
+                                         1600,
                                          1,
                                          1,
                                          0,
@@ -2408,12 +2414,11 @@
                                          false);
 
     HeRu::RuSpec ru(HeRu::RU_106_TONE, staId, false);
-    ru.SetPhyIndex(DEFAULT_CHANNEL_WIDTH, 0);
     txVector.SetRu(ru, staId);
     txVector.SetMode(HePhy::GetHeMcs7(), staId);
     txVector.SetNss(1, staId);
 
-    m_trigVector.SetHeMuUserInfo(staId, {ru, HePhy::GetHeMcs7(), 1});
+    m_trigVector.SetHeMuUserInfo(staId, {ru, 7, 1});
 
     Ptr<Packet> pkt = Create<Packet>(payloadSize);
     WifiMacHeader hdr;
@@ -2432,8 +2437,7 @@
                                       ppduDuration,
                                       WIFI_PHY_BAND_5GHZ,
                                       uid,
-                                      HePpdu::PSD_NON_HE_PORTION,
-                                      0);
+                                      HePpdu::PSD_NON_HE_PORTION);
 
     // Send non-OFDMA part
     Time nonOfdmaDuration = m_phy->GetHePhy()->CalculateNonOfdmaDurationForHeTb(txVector);
@@ -2451,14 +2455,16 @@
     rxParams->txPhy = nullptr;
     rxParams->duration = nonOfdmaDuration;
     rxParams->ppdu = ppdu;
+    rxParams->txWidth = channelWidth;
 
     uint16_t length;
     std::tie(length, ppduDuration) =
         HePhy::ConvertHeTbPpduDurationToLSigLength(ppduDuration, txVector, m_phy->GetPhyBand());
     txVector.SetLength(length);
     m_trigVector.SetLength(length);
-    auto hePhy = DynamicCast<HePhy>(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
+    auto hePhy = DynamicCast<HePhy>(m_phy->GetLatestPhyEntity());
     hePhy->SetTrigVector(m_trigVector, ppduDuration);
+    ppdu->ResetTxVector();
     m_phy->StartRx(rxParams);
 
     // Schedule OFDMA part
@@ -2476,6 +2482,7 @@
     rxParamsOfdma->txPhy = nullptr;
     rxParamsOfdma->duration = ppduDuration - nonOfdmaDuration;
     rxParamsOfdma->ppdu = ppduOfdma;
+    rxParamsOfdma->txWidth = DEFAULT_CHANNEL_WIDTH;
     Simulator::Schedule(nonOfdmaDuration,
                         &TestMultipleHeTbPreambles::RxHeTbPpduOfdmaPart,
                         this,
@@ -2911,8 +2918,8 @@
     }
 
     /**
-     * Return whether last RX has been successfull.
-     * \return true if last RX has been successfull, false otherwise
+     * Return whether last RX has been successful.
+     * \return true if last RX has been successful, false otherwise
      */
     bool IsLastRxSuccess() const
     {
@@ -2924,7 +2931,7 @@
     uint32_t m_notifyRxEnd{0};      ///< count number of RX end notifications
     Time m_lastRxStart{Seconds(0)}; ///< last time a RX start notification has been received
     Time m_lastRxEnd{Seconds(0)};   ///< last time a RX end notification has been received
-    bool m_lastRxSuccess{false};    ///< flag whether last RX has been successfull
+    bool m_lastRxSuccess{false};    ///< flag whether last RX has been successful
 };
 
 /**
@@ -2945,8 +2952,6 @@
         CHANNEL_WIDTH,
         UL_LENGTH,
         AID,
-        RU_TYPE,
-        MCS
     };
 
     TestUlOfdmaPhyTransmission();
@@ -3277,7 +3282,7 @@
     WifiTxVector txVector = WifiTxVector(HePhy::GetHeMcs7(),
                                          0,
                                          WIFI_PREAMBLE_HE_TB,
-                                         800,
+                                         1600,
                                          1,
                                          1,
                                          0,
@@ -3316,7 +3321,6 @@
         index = 1;
     }
     HeRu::RuSpec ru(ruType, index, primary80MHz);
-    ru.SetPhyIndex(m_channelWidth, 0);
     txVector.SetRu(ru, txStaId);
     txVector.SetMode(HePhy::GetHeMcs7(), txStaId);
     txVector.SetNss(1, txStaId);
@@ -3335,7 +3339,7 @@
     WifiTxVector txVector(HePhy::GetHeMcs7(),
                           0,
                           WIFI_PREAMBLE_HE_TB,
-                          800,
+                          1600,
                           1,
                           1,
                           0,
@@ -3348,19 +3352,19 @@
     HeRu::RuType ruType = HeRu::RU_106_TONE;
     if (channelWidth == 20)
     {
-        ruType = (error == RU_TYPE ? HeRu::RU_52_TONE : HeRu::RU_106_TONE);
+        ruType = HeRu::RU_106_TONE;
     }
     else if (channelWidth == 40)
     {
-        ruType = (error == RU_TYPE ? HeRu::RU_106_TONE : HeRu::RU_242_TONE);
+        ruType = HeRu::RU_242_TONE;
     }
     else if (channelWidth == 80)
     {
-        ruType = (error == RU_TYPE ? HeRu::RU_242_TONE : HeRu::RU_484_TONE);
+        ruType = HeRu::RU_484_TONE;
     }
     else if (channelWidth == 160)
     {
-        ruType = (error == RU_TYPE ? HeRu::RU_484_TONE : HeRu::RU_996_TONE);
+        ruType = HeRu::RU_996_TONE;
     }
     else
     {
@@ -3371,15 +3375,13 @@
     uint16_t aid2 = (error == AID ? 4 : 2);
 
     HeRu::RuSpec ru1(ruType, 1, true);
-    ru1.SetPhyIndex(channelWidth, 0);
     txVector.SetRu(ru1, aid1);
-    txVector.SetMode((error == MCS ? HePhy::GetHeMcs5() : HePhy::GetHeMcs7()), aid1);
+    txVector.SetMode(HePhy::GetHeMcs7(), aid1);
     txVector.SetNss(1, aid1);
 
     HeRu::RuSpec ru2(ruType, (channelWidth == 160 ? 1 : 2), (channelWidth == 160 ? false : true));
-    ru2.SetPhyIndex(channelWidth, 0);
     txVector.SetRu(ru2, aid2);
-    txVector.SetMode((error == MCS ? HePhy::GetHeMcs5() : HePhy::GetHeMcs7()), aid2);
+    txVector.SetMode(HePhy::GetHeMcs7(), aid2);
     txVector.SetNss(1, aid2);
 
     uint16_t length;
@@ -3392,7 +3394,7 @@
         ++length;
     }
     txVector.SetLength(length);
-    auto hePhyAp = DynamicCast<HePhy>(m_phyAp->GetPhyEntity(WIFI_MOD_CLASS_HE));
+    auto hePhyAp = DynamicCast<HePhy>(m_phyAp->GetLatestPhyEntity());
     hePhyAp->SetTrigVector(txVector, m_expectedPpduDuration);
 }
 
@@ -3512,7 +3514,7 @@
     NS_TEST_ASSERT_MSG_EQ(
         m_countRxFailureFromSta1,
         expectedFailures,
-        "The number of unsuccessfully received packets from STA 1 is not correct!");
+        "The number of unsuccessfuly received packets from STA 1 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesFromSta1,
                           expectedBytes,
                           "The number of bytes received from STA 1 is not correct!");
@@ -3529,7 +3531,7 @@
     NS_TEST_ASSERT_MSG_EQ(
         m_countRxFailureFromSta2,
         expectedFailures,
-        "The number of unsuccessfully received packets from STA 2 is not correct!");
+        "The number of unsuccessfuly received packets from STA 2 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesFromSta2,
                           expectedBytes,
                           "The number of bytes received from STA 2 is not correct!");
@@ -3880,7 +3882,7 @@
                         0);
     if (!solicited)
     {
-        // UID of TB PPDUs will be different than the one of the preceeding frame
+        // UID of TB PPDUs will be different than the one of the preceding frame
         ++uid;
     }
     else
@@ -4151,7 +4153,7 @@
      * are sent on RU 1 for STA 1 and RU 2 for STA 2.
      * The difference between solicited and unsolicited lies in that their PPDU
      * ID correspond to the one of the immediately preceding HE SU PPDU (thus
-     * mimicing trigger frame reception).
+     * mimicking trigger frame reception).
      */
 
     //---------------------------------------------------------------------------
@@ -4276,48 +4278,6 @@
     delay += Seconds(1.0);
 
     //---------------------------------------------------------------------------
-    // Verify that HE TB PPDUs with RU type differing from TRIGVECTOR are discarded
-    Simulator::Schedule(delay,
-                        &TestUlOfdmaPhyTransmission::LogScenario,
-                        this,
-                        "Dropping of HE TB PPDUs with RU type differing from TRIGVECTOR");
-    ScheduleTest(delay,
-                 true,
-                 WifiPhyState::IDLE,
-                 0,
-                 0,
-                 0, // PSDU from STA 1 is not received (no TRIGVECTOR)
-                 0,
-                 0,
-                 0, // PSDU from STA 2 is not received (no TRIGVECTOR)
-                 true,
-                 Seconds(0),
-                 WifiPhyState::CCA_BUSY,
-                 RU_TYPE);
-    delay += Seconds(1.0);
-
-    //---------------------------------------------------------------------------
-    // Verify that HE TB PPDUs with MCS differing from TRIGVECTOR are discarded
-    Simulator::Schedule(delay,
-                        &TestUlOfdmaPhyTransmission::LogScenario,
-                        this,
-                        "Dropping of HE TB PPDUs with MCS differing from TRIGVECTOR");
-    ScheduleTest(delay,
-                 true,
-                 WifiPhyState::IDLE,
-                 0,
-                 0,
-                 0, // PSDU from STA 1 is not received (no TRIGVECTOR)
-                 0,
-                 0,
-                 0, // PSDU from STA 2 is not received (no TRIGVECTOR)
-                 true,
-                 Seconds(0),
-                 WifiPhyState::CCA_BUSY,
-                 MCS);
-    delay += Seconds(1.0);
-
-    //---------------------------------------------------------------------------
     // Generate an interference on RU 1 and verify that only STA 1's solicited HE TB PPDU has been
     // impacted
     Simulator::Schedule(
@@ -4764,25 +4724,25 @@
 {
     m_frequency = 5180;
     m_channelWidth = 20;
-    m_expectedPpduDuration = NanoSeconds(279200);
+    m_expectedPpduDuration = NanoSeconds(292800);
     NS_LOG_DEBUG("Run UL OFDMA PHY transmission test for " << m_channelWidth << " MHz");
     RunOne();
 
     m_frequency = 5190;
     m_channelWidth = 40;
-    m_expectedPpduDuration = NanoSeconds(156800);
+    m_expectedPpduDuration = NanoSeconds(163200);
     NS_LOG_DEBUG("Run UL OFDMA PHY transmission test for " << m_channelWidth << " MHz");
     RunOne();
 
     m_frequency = 5210;
     m_channelWidth = 80;
-    m_expectedPpduDuration = NanoSeconds(102400);
+    m_expectedPpduDuration = NanoSeconds(105600);
     NS_LOG_DEBUG("Run UL OFDMA PHY transmission test for " << m_channelWidth << " MHz");
     RunOne();
 
     m_frequency = 5250;
     m_channelWidth = 160;
-    m_expectedPpduDuration = NanoSeconds(75200);
+    m_expectedPpduDuration = NanoSeconds(76800);
     NS_LOG_DEBUG("Run UL OFDMA PHY transmission test for " << m_channelWidth << " MHz");
     RunOne();
 
@@ -4933,7 +4893,7 @@
     WifiTxVector txVector = WifiTxVector(HePhy::GetHeMcs7(),
                                          0,
                                          WIFI_PREAMBLE_HE_TB,
-                                         800,
+                                         1600,
                                          1,
                                          1,
                                          0,
@@ -5039,7 +4999,7 @@
     NS_TEST_ASSERT_MSG_EQ(
         m_countRxFailureFromSta1,
         expectedFailures,
-        "The number of unsuccessfully received packets from STA 1 is not correct!");
+        "The number of unsuccessfuly received packets from STA 1 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesFromSta1,
                           expectedBytes,
                           "The number of bytes received from STA 1 is not correct!");
@@ -5056,7 +5016,7 @@
     NS_TEST_ASSERT_MSG_EQ(
         m_countRxFailureFromSta2,
         expectedFailures,
-        "The number of unsuccessfully received packets from STA 2 is not correct!");
+        "The number of unsuccessfuly received packets from STA 2 is not correct!");
     NS_TEST_ASSERT_MSG_EQ(m_countRxBytesFromSta2,
                           expectedBytes,
                           "The number of bytes received from STA 2 is not correct!");
@@ -5232,7 +5192,7 @@
     WifiTxVector trigVector(HePhy::GetHeMcs7(),
                             0,
                             WIFI_PREAMBLE_HE_TB,
-                            800,
+                            1600,
                             1,
                             1,
                             0,
@@ -5250,16 +5210,16 @@
     std::tie(length, ppduDuration) =
         HePhy::ConvertHeTbPpduDurationToLSigLength(ppduDuration, trigVector, m_phyAp->GetPhyBand());
     trigVector.SetLength(length);
-    auto hePhyAp = DynamicCast<HePhy>(m_phyAp->GetPhyEntity(WIFI_MOD_CLASS_HE));
+    auto hePhyAp = DynamicCast<HePhy>(m_phyAp->GetLatestPhyEntity());
     hePhyAp->SetTrigVector(trigVector, ppduDuration);
 }
 
 void
 TestPhyPaddingExclusion::DoRun()
 {
-    Time expectedPpduDuration = NanoSeconds(279200);
+    Time expectedPpduDuration = NanoSeconds(292800);
     Time ppduWithPaddingDuration =
-        expectedPpduDuration + 10 * NanoSeconds(12800 + 800 /* GI */); // add 10 extra OFDM symbols
+        expectedPpduDuration + 10 * NanoSeconds(12800 + 1600 /* GI */); // add 10 extra OFDM symbols
 
     Simulator::Schedule(Seconds(0.0), &TestPhyPaddingExclusion::Reset, this);
 
@@ -5495,7 +5455,7 @@
 
     // Build MU-BAR trigger frame
     CtrlTriggerHeader muBar;
-    muBar.SetType(MU_BAR_TRIGGER);
+    muBar.SetType(TriggerFrameType::MU_BAR_TRIGGER);
     muBar.SetMoreTF(true);
     muBar.SetCsRequired(true);
     muBar.SetUlBandwidth(DEFAULT_CHANNEL_WIDTH);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-phy-reception-test.cc ns-3.38/src/wifi/test/wifi-phy-reception-test.cc
--- ns-3.37/src/wifi/test/wifi-phy-reception-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-phy-reception-test.cc	2023-03-16 20:54:23.876869400 -0700
@@ -49,6 +49,8 @@
 #include "ns3/wifi-spectrum-value-helper.h"
 #include "ns3/wifi-utils.h"
 
+#include <optional>
+
 using namespace ns3;
 
 NS_LOG_COMPONENT_DEFINE("WifiPhyReceptionTest");
@@ -115,7 +117,7 @@
     /**
      * Check the number of received packets
      * \param expectedSuccessCount the number of successfully received packets
-     * \param expectedFailureCount the number of unsuccessfully received packets
+     * \param expectedFailureCount the number of unsuccessfuly received packets
      */
     void CheckRxPacketCount(uint32_t expectedSuccessCount, uint32_t expectedFailureCount);
 
@@ -160,6 +162,7 @@
     txParams->txPhy = nullptr;
     txParams->duration = txDuration;
     txParams->ppdu = ppdu;
+    txParams->txWidth = CHANNEL_WIDTH;
 
     m_phy->StartRx(txParams);
 }
@@ -168,7 +171,7 @@
 TestThresholdPreambleDetectionWithoutFrameCapture::CheckPhyState(WifiPhyState expectedState)
 {
     // This is needed to make sure PHY state will be checked as the last event if a state change
-    // occured at the exact same time as the check
+    // occurred at the exact same time as the check
     Simulator::ScheduleNow(&TestThresholdPreambleDetectionWithoutFrameCapture::DoCheckPhyState,
                            this,
                            expectedState);
@@ -788,7 +791,7 @@
     /**
      * Check the number of received packets
      * \param expectedSuccessCount the number of successfully received packets
-     * \param expectedFailureCount the number of unsuccessfully received packets
+     * \param expectedFailureCount the number of unsuccessfuly received packets
      */
     void CheckRxPacketCount(uint32_t expectedSuccessCount, uint32_t expectedFailureCount);
 
@@ -841,7 +844,7 @@
 TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState(WifiPhyState expectedState)
 {
     // This is needed to make sure PHY state will be checked as the last event if a state change
-    // occured at the exact same time as the check
+    // occurred at the exact same time as the check
     Simulator::ScheduleNow(&TestThresholdPreambleDetectionWithFrameCapture::DoCheckPhyState,
                            this,
                            expectedState);
@@ -2154,7 +2157,7 @@
 TestPhyHeadersReception::CheckPhyState(WifiPhyState expectedState)
 {
     // This is needed to make sure PHY state will be checked as the last event if a state change
-    // occured at the exact same time as the check
+    // occurred at the exact same time as the check
     Simulator::ScheduleNow(&TestPhyHeadersReception::DoCheckPhyState, this, expectedState);
 }
 
@@ -2611,8 +2614,8 @@
     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; ///< bitmap of unsuccessfully received MPDUs in A-MPDU #1
-    uint8_t m_rxFailureBitmapAmpdu2; ///< 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
@@ -2870,6 +2873,7 @@
     txParams->txPhy = nullptr;
     txParams->duration = txDuration;
     txParams->ppdu = ppdu;
+    txParams->txWidth = CHANNEL_WIDTH;
 
     m_phy->StartRx(txParams);
 }
@@ -4293,6 +4297,458 @@
  * \ingroup wifi-test
  * \ingroup tests
  *
+ * \brief Unsupported Bandwidth Reception Test
+ * This test checks whether a PHY receiving a PPDU sent over a channel width
+ * larger than the one supported by the PHY is getting dropped at the expected
+ * time. The expected time corresponds to the moment the PHY header indicating
+ * the channel width used to transmit the PPDU is received. Since we are considering
+ * 802.11ax for this test, this corresponds to the time HE-SIG-A is received.
+ */
+class TestUnsupportedBandwidthReception : public TestCase
+{
+  public:
+    TestUnsupportedBandwidthReception();
+
+  private:
+    void DoSetup() override;
+    void DoTeardown() override;
+    void DoRun() override;
+
+    /**
+     * 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);
+
+    /**
+     * Function called upon a PSDU received successfully
+     * \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);
+
+    /**
+     * Function called upon a PSDU received unsuccessfuly
+     * \param psdu the PSDU
+     */
+    void RxFailure(Ptr<const WifiPsdu> psdu);
+
+    /**
+     * Function called upon a PSDU dropped by the PHY
+     * \param packet the packet that was dropped
+     * \param reason the reason the packet was dropped
+     */
+    void RxDropped(Ptr<const Packet> packet, WifiPhyRxfailureReason reason);
+
+    /**
+     * Check the reception results
+     * \param expectedCountRxSuccess the expected number of RX success
+     * \param expectedCountRxFailure the expected number of RX failure
+     * \param expectedCountRxDropped the expected number of RX drop
+     * \param expectedLastRxSucceeded the expected time the last RX success occurred or std::nullopt
+     * if the expected number of RX success is not strictly positive \param expectedLastRxFailed the
+     * expected time the last RX failure occurred or std::nullopt if the expected number of RX
+     * failure is not strictly positive \param expectedLastRxDropped the expected time the last RX
+     * drop occurred or std::nullopt if the expected number of RX drop is not strictly positive
+     */
+    void CheckRx(uint32_t expectedCountRxSuccess,
+                 uint32_t expectedCountRxFailure,
+                 uint32_t expectedCountRxDropped,
+                 std::optional<Time> expectedLastRxSucceeded,
+                 std::optional<Time> expectedLastRxFailed,
+                 std::optional<Time> expectedLastRxDropped);
+
+    uint32_t m_countRxSuccess; ///< count RX success
+    uint32_t m_countRxFailure; ///< count RX failure
+    uint32_t m_countRxDropped; ///< count RX drop
+
+    std::optional<Time> m_lastRxSucceeded; ///< time of last RX success, if any
+    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
+};
+
+TestUnsupportedBandwidthReception::TestUnsupportedBandwidthReception()
+    : TestCase("Check correct behavior when a STA is receiving a transmission using an unsupported "
+               "bandwidth"),
+      m_countRxSuccess(0),
+      m_countRxFailure(0),
+      m_countRxDropped(0),
+      m_lastRxSucceeded(std::nullopt),
+      m_lastRxFailed(std::nullopt),
+      m_lastRxDropped(std::nullopt)
+{
+}
+
+void
+TestUnsupportedBandwidthReception::SendPpdu(WifiPhyBand band,
+                                            uint16_t centerFreqMhz,
+                                            uint16_t bandwidthMhz)
+{
+    auto txVector =
+        WifiTxVector(HePhy::GetHeMcs0(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, bandwidthMhz, false);
+
+    auto 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());
+
+    auto ppdu = Create<HePpdu>(psdu, txVector, centerFreqMhz, txDuration, WIFI_PHY_BAND_5GHZ, 0);
+
+    auto txPowerSpectrum =
+        WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(centerFreqMhz,
+                                                                    bandwidthMhz,
+                                                                    DbmToW(-50),
+                                                                    bandwidthMhz);
+
+    auto txParams = Create<WifiSpectrumSignalParameters>();
+    txParams->psd = txPowerSpectrum;
+    txParams->txPhy = nullptr;
+    txParams->duration = txDuration;
+    txParams->ppdu = ppdu;
+    txParams->txWidth = bandwidthMhz;
+
+    m_phy->StartRx(txParams);
+}
+
+void
+TestUnsupportedBandwidthReception::RxSuccess(Ptr<const WifiPsdu> psdu,
+                                             RxSignalInfo rxSignalInfo,
+                                             WifiTxVector txVector,
+                                             std::vector<bool> statusPerMpdu)
+{
+    NS_LOG_FUNCTION(this << *psdu << rxSignalInfo << txVector);
+    m_countRxSuccess++;
+    m_lastRxSucceeded = Simulator::Now();
+}
+
+void
+TestUnsupportedBandwidthReception::RxFailure(Ptr<const WifiPsdu> psdu)
+{
+    NS_LOG_FUNCTION(this << *psdu);
+    m_countRxFailure++;
+    m_lastRxFailed = Simulator::Now();
+}
+
+void
+TestUnsupportedBandwidthReception::RxDropped(Ptr<const Packet> p, WifiPhyRxfailureReason reason)
+{
+    NS_LOG_FUNCTION(this << p << reason);
+    NS_ASSERT(reason == UNSUPPORTED_SETTINGS);
+    m_countRxDropped++;
+    m_lastRxDropped = Simulator::Now();
+}
+
+void
+TestUnsupportedBandwidthReception::CheckRx(uint32_t expectedCountRxSuccess,
+                                           uint32_t expectedCountRxFailure,
+                                           uint32_t expectedCountRxDropped,
+                                           std::optional<Time> expectedLastRxSucceeded,
+                                           std::optional<Time> expectedLastRxFailed,
+                                           std::optional<Time> expectedLastRxDropped)
+{
+    NS_TEST_ASSERT_MSG_EQ(m_countRxSuccess,
+                          expectedCountRxSuccess,
+                          "Didn't receive right number of successful packets");
+
+    NS_TEST_ASSERT_MSG_EQ(m_countRxFailure,
+                          expectedCountRxFailure,
+                          "Didn't receive right number of unsuccessful packets");
+
+    NS_TEST_ASSERT_MSG_EQ(m_countRxDropped,
+                          expectedCountRxDropped,
+                          "Didn't receive right number of dropped packets");
+
+    if (expectedCountRxSuccess > 0)
+    {
+        NS_ASSERT(m_lastRxSucceeded.has_value());
+        NS_ASSERT(expectedLastRxSucceeded.has_value());
+        NS_TEST_ASSERT_MSG_EQ(m_lastRxSucceeded.value(),
+                              expectedLastRxSucceeded.value(),
+                              "Didn't receive the last successful packet at the expected time");
+    }
+
+    if (expectedCountRxFailure > 0)
+    {
+        NS_ASSERT(m_lastRxFailed.has_value());
+        NS_ASSERT(expectedLastRxFailed.has_value());
+        NS_TEST_ASSERT_MSG_EQ(m_lastRxFailed.value(),
+                              expectedLastRxFailed.value(),
+                              "Didn't receive the last unsuccessful packet at the expected time");
+    }
+
+    if (expectedCountRxDropped > 0)
+    {
+        NS_ASSERT(m_lastRxDropped.has_value());
+        NS_ASSERT(expectedLastRxDropped.has_value());
+        NS_TEST_ASSERT_MSG_EQ(m_lastRxDropped.value(),
+                              expectedLastRxDropped.value(),
+                              "Didn't drop the last filtered packet at the expected time");
+    }
+}
+
+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(
+        MakeCallback(&TestUnsupportedBandwidthReception::RxFailure, this));
+    m_phy->TraceConnectWithoutContext(
+        "PhyRxDrop",
+        MakeCallback(&TestUnsupportedBandwidthReception::RxDropped, this));
+}
+
+void
+TestUnsupportedBandwidthReception::DoTeardown()
+{
+    m_phy->Dispose();
+    m_phy = nullptr;
+}
+
+void
+TestUnsupportedBandwidthReception::DoRun()
+{
+    RngSeedManager::SetSeed(1);
+    RngSeedManager::SetRun(1);
+
+    int64_t streamNumber = 0;
+    m_phy->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});
+
+    Simulator::Schedule(Seconds(1.0),
+                        &TestUnsupportedBandwidthReception::SendPpdu,
+                        this,
+                        WIFI_PHY_BAND_5GHZ,
+                        5190,
+                        40);
+
+    auto heSigAExpectedRxTime = Seconds(1.0) + MicroSeconds(32);
+    Simulator::Schedule(Seconds(1.5),
+                        &TestUnsupportedBandwidthReception::CheckRx,
+                        this,
+                        0,
+                        0,
+                        1,
+                        std::nullopt,
+                        std::nullopt,
+                        heSigAExpectedRxTime);
+
+    // TODO: this test can be extended with other scenarios
+
+    Simulator::Run();
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Primary 20 MHz Covered By Ppdu Test
+ * This test checks whether the functions WifiPpdu::DoesOverlapChannel and
+ * WifiPpdu::DoesCoverChannel are returning the expected results.
+ */
+class TestPrimary20CoveredByPpdu : public TestCase
+{
+  public:
+    TestPrimary20CoveredByPpdu();
+
+  private:
+    void DoSetup() override;
+    void DoRun() override;
+    void DoTeardown() override;
+
+    /**
+     * 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);
+
+    /**
+     * Run one function
+     *
+     * \param band the PHY band to use
+     * \param phyCenterFreqMhz the operating center frequency of the PHY (in MHz)
+     * \param p20Index the primary20 index
+     * \param ppduCenterFreqMhz the center frequency used for the transmission of the PPDU (in MHz)
+     * \param expectedP20Overlap flag whether the primary 20 MHz channel is expected to be fully
+     * covered by the bandwidth of the incoming PPDU
+     * \param expectedP20Covered flag whether the
+     * primary 20 MHz channel is expected to overlap with the bandwidth of the incoming PPDU
+     */
+    void RunOne(WifiPhyBand band,
+                uint16_t phyCenterFreqMhz,
+                uint8_t p20Index,
+                uint16_t ppduCenterFreqMhz,
+                bool expectedP20Overlap,
+                bool expectedP20Covered);
+
+    Ptr<SpectrumWifiPhy> m_phy; ///< PHY
+};
+
+TestPrimary20CoveredByPpdu::TestPrimary20CoveredByPpdu()
+    : TestCase("Check correct detection of whether P20 is fully covered (hence it can be received) "
+               "or overlaps with the bandwidth of an incoming PPDU")
+{
+}
+
+Ptr<HePpdu>
+TestPrimary20CoveredByPpdu::CreatePpdu(WifiPhyBand band, uint16_t ppduCenterFreqMhz)
+{
+    [[maybe_unused]] auto [channelNumber, centerFreq, channelWidth, type, phyBand] =
+        (*WifiPhyOperatingChannel::FindFirst(0, ppduCenterFreqMhz, 0, WIFI_STANDARD_80211ax, band));
+
+    auto txVector =
+        WifiTxVector(HePhy::GetHeMcs7(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, channelWidth, false);
+
+    auto pkt = Create<Packet>(1000);
+    WifiMacHeader hdr(WIFI_MAC_QOSDATA);
+
+    auto psdu = Create<WifiPsdu>(pkt, hdr);
+    auto txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, band);
+
+    return Create<HePpdu>(psdu, txVector, ppduCenterFreqMhz, txDuration, band, 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);
+}
+
+void
+TestPrimary20CoveredByPpdu::DoTeardown()
+{
+    m_phy->Dispose();
+    m_phy = nullptr;
+}
+
+void
+TestPrimary20CoveredByPpdu::RunOne(WifiPhyBand band,
+                                   uint16_t phyCenterFreqMhz,
+                                   uint8_t p20Index,
+                                   uint16_t ppduCenterFreqMhz,
+                                   bool expectedP20Overlap,
+                                   bool expectedP20Covered)
+{
+    [[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);
+    auto p20MinFreq = p20CenterFreq - 10;
+    auto p20MaxFreq = p20CenterFreq + 10;
+
+    auto ppdu = CreatePpdu(band, ppduCenterFreqMhz);
+
+    auto p20Overlap = ppdu->DoesOverlapChannel(p20MinFreq, p20MaxFreq);
+    NS_TEST_ASSERT_MSG_EQ(p20Overlap,
+                          expectedP20Overlap,
+                          "PPDU is " << (expectedP20Overlap ? "expected" : "not expected")
+                                     << " to overlap with the P20");
+
+    auto p20Covered =
+        m_phy->GetPhyEntity(WIFI_STANDARD_80211ax)
+            ->CanStartRx(
+                ppdu,
+                ppdu->GetTxVector()
+                    .GetChannelWidth()); // CanStartRx returns true is the P20 is fully covered
+    NS_TEST_ASSERT_MSG_EQ(p20Covered,
+                          expectedP20Covered,
+                          "PPDU is " << (expectedP20Covered ? "expected" : "not expected")
+                                     << " to cover the whole P20");
+}
+
+void
+TestPrimary20CoveredByPpdu::DoRun()
+{
+    /*
+     * Receiver PHY Operating Channel: 2.4 GHz Channel 4 (2417 MHz – 2437 MHz)
+     * Transmitted 20 MHz PPDU: 2.4 GHz Channel 4 (2417 MHz – 2437 MHz)
+     * Overlap with primary 20 MHz: yes
+     * Primary 20 MHz fully covered: yes
+     */
+    RunOne(WIFI_PHY_BAND_2_4GHZ, 2427, 0, 2427, true, true);
+
+    /*
+     * 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)
+     * Overlap with primary 20 MHz: yes
+     * Primary 20 MHz fully covered: no
+     */
+    RunOne(WIFI_PHY_BAND_2_4GHZ, 2427, 0, 2437, true, false);
+
+    /*
+     * Receiver PHY Operating Channel: 5 GHz Channel 36 (5170 MHz – 5190 MHz)
+     * Transmitted 40 MHz PPDU: 5 GHz Channel 38 (5170 MHz – 5210 MHz)
+     * Overlap with primary 20 MHz: yes
+     * Primary 20 MHz fully covered: yes
+     */
+    RunOne(WIFI_PHY_BAND_5GHZ, 5180, 0, 5190, true, true);
+
+    /*
+     * Receiver PHY Operating Channel: 5 GHz Channel 36 (5170 MHz–5190 MHz)
+     * Transmitted 20 MHz PPDU: 5 GHz Channel 40 (5190 MHz – 5210 MHz)
+     * Overlap with primary 20 MHz: no
+     * Primary 20 MHz fully covered: no
+     */
+    RunOne(WIFI_PHY_BAND_5GHZ, 5180, 0, 5200, false, false);
+
+    /*
+     * Receiver PHY Operating Channel: 5 GHz Channel 38 (5170 MHz – 5210 MHz) with P20 index 0
+     * Transmitted 20 MHz PPDU: 5 GHz Channel 36 (5170 MHz – 5190 MHz)
+     * Overlap with primary 20 MHz: yes
+     * Primary 20 MHz fully covered: yes
+     */
+    RunOne(WIFI_PHY_BAND_5GHZ, 5190, 0, 5180, true, true);
+
+    /*
+     * Receiver PHY Operating Channel: 5 GHz Channel 38 (5170 MHz – 5210 MHz) with P20 index 1
+     * Transmitted 20 MHz PPDU: 5 GHz Channel 36 (5170 MHz – 5190 MHz)
+     * Overlap with primary 20 MHz: no
+     * Primary 20 MHz fully covered: no
+     */
+    RunOne(WIFI_PHY_BAND_5GHZ, 5190, 1, 5180, false, false);
+
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
  * \brief wifi PHY reception Test Suite
  */
 class WifiPhyReceptionTestSuite : public TestSuite
@@ -4310,6 +4766,8 @@
     AddTestCase(new TestPhyHeadersReception, TestCase::QUICK);
     AddTestCase(new TestAmpduReception, TestCase::QUICK);
     AddTestCase(new TestUnsupportedModulationReception(), TestCase::QUICK);
+    AddTestCase(new TestUnsupportedBandwidthReception(), TestCase::QUICK);
+    AddTestCase(new TestPrimary20CoveredByPpdu(), TestCase::QUICK);
 }
 
 static WifiPhyReceptionTestSuite wifiPhyReceptionTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-phy-thresholds-test.cc ns-3.38/src/wifi/test/wifi-phy-thresholds-test.cc
--- ns-3.37/src/wifi/test/wifi-phy-thresholds-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-phy-thresholds-test.cc	2023-03-16 20:54:23.876869400 -0700
@@ -110,7 +110,7 @@
 
     Ptr<SpectrumWifiPhy> m_phy;   ///< PHY object
     uint32_t m_rxSuccess;         ///< count number of successfully received packets
-    uint32_t m_rxFailure;         ///< count number of unsuccessfully received packets
+    uint32_t m_rxFailure;         ///< count number of unsuccessfuly received packets
     uint32_t m_rxDropped;         ///< count number of dropped packets
     uint32_t m_stateChanged;      ///< count number of PHY state change
     uint32_t m_rxStateCount;      ///< count number of PHY state change to RX state
@@ -165,6 +165,7 @@
     txParams->txPhy = nullptr;
     txParams->duration = txDuration;
     txParams->ppdu = ppdu;
+    txParams->txWidth = CHANNEL_WIDTH;
     return txParams;
 }
 
@@ -406,7 +407,7 @@
 
     NS_TEST_ASSERT_MSG_EQ(m_rxDropped + m_rxFailure,
                           0,
-                          "Packet reception should have been successfull");
+                          "Packet reception should have been successful");
     NS_TEST_ASSERT_MSG_EQ(m_rxSuccess, 1, "Packet should have been successfully received");
     NS_TEST_ASSERT_MSG_EQ(m_ccabusyStateCount, 2, "State should have moved to CCA_BUSY once");
     NS_TEST_ASSERT_MSG_EQ(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-primary-channels-test.cc ns-3.38/src/wifi/test/wifi-primary-channels-test.cc
--- ns-3.37/src/wifi/test/wifi-primary-channels-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-primary-channels-test.cc	2023-03-16 20:54:23.876869400 -0700
@@ -834,7 +834,7 @@
 
         auto staDev = DynamicCast<WifiNetDevice>(m_staDevices[bss].Get(i - 1));
         uint16_t staId = DynamicCast<StaWifiMac>(staDev->GetMac())->GetAssociationId();
-        txVector.SetHeMuUserInfo(staId, {{ruType, index, primary80}, HePhy::GetHeMcs8(), 1});
+        txVector.SetHeMuUserInfo(staId, {{ruType, index, primary80}, 8, 1});
         hdr.SetAddr1(staDev->GetMac()->GetAddress());
         psduMap[staId] = Create<const WifiPsdu>(Create<Packet>(1000), hdr);
     }
@@ -929,8 +929,8 @@
                               false,
                               false,
                               bssColor);
-        txVector.SetHeMuUserInfo(staId, {{ruType, index, primary80}, HePhy::GetHeMcs8(), 1});
-        trigVector.SetHeMuUserInfo(staId, {{ruType, index, primary80}, HePhy::GetHeMcs8(), 1});
+        txVector.SetHeMuUserInfo(staId, {{ruType, index, primary80}, 8, 1});
+        trigVector.SetHeMuUserInfo(staId, {{ruType, index, primary80}, 8, 1});
 
         hdr.SetAddr2(staDev->GetMac()->GetAddress());
         Ptr<const WifiPsdu> psdu = Create<const WifiPsdu>(Create<Packet>(1000), hdr);
@@ -954,7 +954,7 @@
 
     // AP's PHY expects to receive a TRIGVECTOR (just once)
     trigVector.SetLength(length);
-    auto apHePhy = StaticCast<HePhy>(apDev->GetPhy()->GetPhyEntity(WIFI_MOD_CLASS_HE));
+    auto apHePhy = StaticCast<HePhy>(apDev->GetPhy()->GetLatestPhyEntity());
     apHePhy->SetTrigVector(trigVector, duration);
 }
 
@@ -965,7 +965,7 @@
     {
         auto dev = DynamicCast<WifiNetDevice>(m_apDevices.Get(bss));
         auto mac = DynamicCast<ApWifiMac>(dev->GetMac());
-        NS_TEST_EXPECT_MSG_EQ(mac->GetStaList().size(),
+        NS_TEST_EXPECT_MSG_EQ(mac->GetStaList(SINGLE_LINK_OP_ID).size(),
                               m_nStationsPerBss,
                               "Not all the stations completed association");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-ru-allocation-test.cc ns-3.38/src/wifi/test/wifi-ru-allocation-test.cc
--- ns-3.37/src/wifi/test/wifi-ru-allocation-test.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/src/wifi/test/wifi-ru-allocation-test.cc	2023-03-16 20:54:23.876869400 -0700
@@ -0,0 +1,696 @@
+/*
+ * 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/test.h"
+#include "ns3/wifi-phy-operating-channel.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("WifiRuAllocationTest");
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test the WifiPhyOperatingChannel::Get20MHzIndicesCoveringRu() method.
+ */
+class Wifi20MHzIndicesCoveringRuTest : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     */
+    Wifi20MHzIndicesCoveringRuTest();
+    ~Wifi20MHzIndicesCoveringRuTest() override = default;
+
+    /**
+     * Check that the indices of the 20 MHz channels covering the given RU as computed
+     * by WifiPhyOperatingChannel::Get20MHzIndicesCoveringRu() are correct.
+     *
+     * \param primary20 the index of the primary20 channel to configure
+     * \param ru the given RU
+     * \param width the width in MHz of the channel to which the given RU refers to; normally,
+     *              it is the width in MHz of the PPDU for which the RU is allocated
+     * \param indices the expected indices
+     */
+    void RunOne(uint8_t primary20,
+                HeRu::RuSpec ru,
+                uint16_t width,
+                const std::set<uint8_t>& indices);
+
+  private:
+    void DoRun() override;
+
+    WifiPhyOperatingChannel m_channel; //!< operating channel
+};
+
+Wifi20MHzIndicesCoveringRuTest::Wifi20MHzIndicesCoveringRuTest()
+    : TestCase("Check computation of the indices of the 20 MHz channels covering an RU")
+{
+}
+
+void
+Wifi20MHzIndicesCoveringRuTest::RunOne(uint8_t primary20,
+                                       HeRu::RuSpec ru,
+                                       uint16_t width,
+                                       const std::set<uint8_t>& indices)
+{
+    auto printToStr = [](const std::set<uint8_t>& s) {
+        std::stringstream ss;
+        ss << "{";
+        for (const auto& index : s)
+        {
+            ss << +index << " ";
+        }
+        ss << "}";
+        return ss.str();
+    };
+
+    m_channel.SetPrimary20Index(primary20);
+
+    auto actualIndices = m_channel.Get20MHzIndicesCoveringRu(ru, width);
+    NS_TEST_ASSERT_MSG_EQ((actualIndices == indices),
+                          true,
+                          "Channel width=" << m_channel.GetWidth() << " MHz, PPDU width=" << width
+                                           << " MHz, p20Index=" << +primary20 << " , RU=" << ru
+                                           << ". Expected indices " << printToStr(indices)
+                                           << " differs from actual " << printToStr(actualIndices));
+}
+
+void
+Wifi20MHzIndicesCoveringRuTest::DoRun()
+{
+    /******************
+     * 20 MHz channel *
+     ******************/
+    m_channel.SetDefault(20, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
+
+    /* 20 MHz PPDU */
+    {
+        const uint16_t width = 20;
+        const uint8_t p20Index = 0;
+
+        // All the 9 26-tone RUs are covered by the unique 20 MHz channel
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
+        }
+        // All the 4 52-tone RUs are covered by the unique 20 MHz channel
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
+        }
+        // Both 106-tone RUs are covered by the unique 20 MHz channel
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
+        }
+        // The 242-tone RU is covered by the unique 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
+    }
+
+    /******************
+     * 40 MHz channel *
+     ******************/
+    m_channel.SetDefault(40, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
+
+    /* 20 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 2; p20Index++)
+    {
+        const uint16_t width = 20;
+
+        // All the 9 26-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
+        }
+        // All the 4 52-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
+        }
+        // Both 106-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
+        }
+        // The 242-tone RU is covered by the primary 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
+    }
+
+    /* 40 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 2; p20Index++)
+    {
+        const uint16_t width = 40;
+
+        // The first 9 26-tone RUs are covered by the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {0});
+        }
+        // The second 9 26-tone RUs are covered by the second 20 MHz channel
+        for (std::size_t idx = 10; idx <= 18; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {1});
+        }
+        // The first 4 52-tone RUs are covered by the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {0});
+        }
+        // The second 4 52-tone RUs are covered by the second 20 MHz channel
+        for (std::size_t idx = 5; idx <= 8; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {1});
+        }
+        // The first 2 106-tone RUs are covered by the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {0});
+        }
+        // The second 2 106-tone RUs are covered by the second 20 MHz channel
+        for (std::size_t idx = 3; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {1});
+        }
+        // The first 242-tone RU is covered by the first 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {0});
+        // The second 242-tone RU is covered by the second 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {1});
+        // The 484-tone RU is covered by both 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {0, 1});
+    }
+
+    /******************
+     * 80 MHz channel *
+     ******************/
+    m_channel.SetDefault(80, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
+
+    /* 20 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 4; p20Index++)
+    {
+        const uint16_t width = 20;
+
+        // All the 9 26-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
+        }
+        // All the 4 52-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
+        }
+        // Both 106-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
+        }
+        // The 242-tone RU is covered by the primary 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
+    }
+
+    /* 40 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 4; p20Index++)
+    {
+        const uint16_t width = 40;
+        // PPDU is transmitted on P40, which may be in the lower or higher 40 MHz
+        const uint8_t p40Index = p20Index / 2;
+        // RUs can be allocated in one (or both) of the two 20 MHz channels in P40
+        const uint8_t ch20Index0 = p40Index * 2;
+        const uint8_t ch20Index1 = p40Index * 2 + 1;
+
+        // The first 9 26-tone RUs are in the lower 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 9 26-tone RUs are in the higher 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 10; idx <= 18; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index1});
+        }
+        // The first 4 52-tone RUs are in the lower 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 4 52-tone RUs are in the higher 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 5; idx <= 8; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index1});
+        }
+        // The first 2 106-tone RUs are in the lower 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 2 106-tone RUs are in the higher 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 3; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index1});
+        }
+        // The first 242-tone RU is in the lower 20 MHz of the PPDU bandwidth
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {ch20Index0});
+        // The second 242-tone RU is in the higher 20 MHz of the PPDU bandwidth
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {ch20Index1});
+        // The 484-tone RU is covered by both 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {ch20Index0, ch20Index1});
+    }
+
+    /* 80 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 4; p20Index++)
+    {
+        const uint16_t width = 80;
+
+        // The first 9 26-tone RUs are in the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {0});
+        }
+        // The second 9 26-tone RUs are in the second 20 MHz channel
+        for (std::size_t idx = 10; idx <= 18; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {1});
+        }
+        // The center 26-tone RU is covered by the central 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, 19, true), width, {1, 2});
+        // The following 9 26-tone RUs are in the third 20 MHz channel
+        for (std::size_t idx = 20; idx <= 28; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {2});
+        }
+        // The last 9 26-tone RUs are in the fourth 20 MHz channel
+        for (std::size_t idx = 29; idx <= 37; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {3});
+        }
+        // The first 4 52-tone RUs are in the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {0});
+        }
+        // The second 4 52-tone RUs are in the second 20 MHz channel
+        for (std::size_t idx = 5; idx <= 8; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {1});
+        }
+        // The third 4 52-tone RUs are in the third 20 MHz channel
+        for (std::size_t idx = 9; idx <= 12; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {2});
+        }
+        // The fourth 4 52-tone RUs are in the fourth 20 MHz channel
+        for (std::size_t idx = 13; idx <= 16; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {3});
+        }
+        // The first 2 106-tone RUs are in the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {0});
+        }
+        // The second 2 106-tone RUs are in the second 20 MHz channel
+        for (std::size_t idx = 3; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {1});
+        }
+        // The third 2 106-tone RUs are in the third 20 MHz channel
+        for (std::size_t idx = 5; idx <= 6; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {2});
+        }
+        // The fourth 2 106-tone RUs are in the fourth 20 MHz channel
+        for (std::size_t idx = 7; idx <= 8; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {3});
+        }
+        // The first 242-tone RU is in the first 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {0});
+        // The second 242-tone RU is in the second 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {1});
+        // The third 242-tone RU is in the third 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 3, true), width, {2});
+        // The fourth 242-tone RU is in the fourth 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 4, true), width, {3});
+        // The first 484-tone RU is covered by the first two 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {0, 1});
+        // The second 484-tone RU is covered by the last two 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 2, true), width, {2, 3});
+        // The 996-tone RU is covered by all the 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_996_TONE, 1, true), width, {0, 1, 2, 3});
+    }
+
+    /******************
+     * 160 MHz channel *
+     ******************/
+    m_channel.SetDefault(160, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ);
+
+    /* 20 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
+    {
+        const uint16_t width = 20;
+
+        // All the 9 26-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {p20Index});
+        }
+        // All the 4 52-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {p20Index});
+        }
+        // Both 106-tone RUs are covered by the primary 20 MHz channel
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {p20Index});
+        }
+        // The 242-tone RU is covered by the primary 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {p20Index});
+    }
+
+    /* 40 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
+    {
+        const uint16_t width = 40;
+        // PPDU is transmitted on P40, which is one of the four 40 MHz channels
+        const uint8_t p40Index = p20Index / 2;
+        // RUs can be allocated in one (or both) of the two 20 MHz channels in P40
+        const uint8_t ch20Index0 = p40Index * 2;
+        const uint8_t ch20Index1 = p40Index * 2 + 1;
+
+        // The first 9 26-tone RUs are in the lower 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 9 26-tone RUs are in the higher 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 10; idx <= 18; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index1});
+        }
+        // The first 4 52-tone RUs are in the lower 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 4 52-tone RUs are in the higher 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 5; idx <= 8; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index1});
+        }
+        // The first 2 106-tone RUs are in the lower 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 2 106-tone RUs are in the higher 20 MHz of the PPDU bandwidth
+        for (std::size_t idx = 3; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index1});
+        }
+        // The first 242-tone RU is in the lower 20 MHz of the PPDU bandwidth
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {ch20Index0});
+        // The second 242-tone RU is in the higher 20 MHz of the PPDU bandwidth
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {ch20Index1});
+        // The 484-tone RU is covered by both 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {ch20Index0, ch20Index1});
+    }
+
+    /* 80 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
+    {
+        const uint16_t width = 80;
+        // PPDU is transmitted on P80, which is one of the two 80 MHz channels
+        const uint8_t p80Index = p20Index / 4;
+        // RUs can be allocated in one (or more) of the four 20 MHz channels in P80
+        const uint8_t ch20Index0 = p80Index * 4;
+        const uint8_t ch20Index1 = p80Index * 4 + 1;
+        const uint8_t ch20Index2 = p80Index * 4 + 2;
+        const uint8_t ch20Index3 = p80Index * 4 + 3;
+
+        // The first 9 26-tone RUs are in the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 9; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 9 26-tone RUs are in the second 20 MHz channel
+        for (std::size_t idx = 10; idx <= 18; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index1});
+        }
+        // The center 26-tone RU is covered by the central 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, 19, true), width, {ch20Index1, ch20Index2});
+        // The following 9 26-tone RUs are in the third 20 MHz channel
+        for (std::size_t idx = 20; idx <= 28; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index2});
+        }
+        // The last 9 26-tone RUs are in the fourth 20 MHz channel
+        for (std::size_t idx = 29; idx <= 37; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_26_TONE, idx, true), width, {ch20Index3});
+        }
+        // The first 4 52-tone RUs are in the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 4 52-tone RUs are in the second 20 MHz channel
+        for (std::size_t idx = 5; idx <= 8; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index1});
+        }
+        // The third 4 52-tone RUs are in the third 20 MHz channel
+        for (std::size_t idx = 9; idx <= 12; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index2});
+        }
+        // The fourth 4 52-tone RUs are in the fourth 20 MHz channel
+        for (std::size_t idx = 13; idx <= 16; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_52_TONE, idx, true), width, {ch20Index3});
+        }
+        // The first 2 106-tone RUs are in the first 20 MHz channel
+        for (std::size_t idx = 1; idx <= 2; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index0});
+        }
+        // The second 2 106-tone RUs are in the second 20 MHz channel
+        for (std::size_t idx = 3; idx <= 4; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index1});
+        }
+        // The third 2 106-tone RUs are in the third 20 MHz channel
+        for (std::size_t idx = 5; idx <= 6; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index2});
+        }
+        // The fourth 2 106-tone RUs are in the fourth 20 MHz channel
+        for (std::size_t idx = 7; idx <= 8; idx++)
+        {
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_106_TONE, idx, true), width, {ch20Index3});
+        }
+        // The first 242-tone RU is in the first 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, true), width, {ch20Index0});
+        // The second 242-tone RU is in the second 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, true), width, {ch20Index1});
+        // The third 242-tone RU is in the third 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 3, true), width, {ch20Index2});
+        // The fourth 242-tone RU is in the fourth 20 MHz channel
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 4, true), width, {ch20Index3});
+        // The first 484-tone RU is covered by the first two 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 1, true), width, {ch20Index0, ch20Index1});
+        // The second 484-tone RU is covered by the last two 20 MHz channels
+        RunOne(p20Index, HeRu::RuSpec(HeRu::RU_484_TONE, 2, true), width, {ch20Index2, ch20Index3});
+        // The 996-tone RU is covered by all the 20 MHz channels
+        RunOne(p20Index,
+               HeRu::RuSpec(HeRu::RU_996_TONE, 1, true),
+               width,
+               {ch20Index0, ch20Index1, ch20Index2, ch20Index3});
+    }
+
+    /* 160 MHz PPDU */
+    for (uint8_t p20Index = 0; p20Index < 8; p20Index++)
+    {
+        const uint16_t width = 160;
+
+        for (auto primary80MHz : {true, false})
+        {
+            // RUs can be allocated in one (or more) of the four 20 MHz channels in P80/S80
+            // (depending on the primary80MHz flag)
+            const uint8_t p80Index = (primary80MHz == (p20Index < 4)) ? 0 : 1;
+            const uint8_t ch20Index0 = p80Index * 4;
+            const uint8_t ch20Index1 = p80Index * 4 + 1;
+            const uint8_t ch20Index2 = p80Index * 4 + 2;
+            const uint8_t ch20Index3 = p80Index * 4 + 3;
+
+            // The first 9 26-tone RUs are in the first 20 MHz channel
+            for (std::size_t idx = 1; idx <= 9; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index0});
+            }
+            // The second 9 26-tone RUs are in the second 20 MHz channel
+            for (std::size_t idx = 10; idx <= 18; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index1});
+            }
+            // The center 26-tone RU is covered by the central 20 MHz channels
+            RunOne(p20Index,
+                   HeRu::RuSpec(HeRu::RU_26_TONE, 19, primary80MHz),
+                   width,
+                   {ch20Index1, ch20Index2});
+            // The following 9 26-tone RUs are in the third 20 MHz channel
+            for (std::size_t idx = 20; idx <= 28; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index2});
+            }
+            // The last 9 26-tone RUs are in the fourth 20 MHz channel
+            for (std::size_t idx = 29; idx <= 37; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_26_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index3});
+            }
+            // The first 4 52-tone RUs are in the first 20 MHz channel
+            for (std::size_t idx = 1; idx <= 4; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index0});
+            }
+            // The second 4 52-tone RUs are in the second 20 MHz channel
+            for (std::size_t idx = 5; idx <= 8; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index1});
+            }
+            // The third 4 52-tone RUs are in the third 20 MHz channel
+            for (std::size_t idx = 9; idx <= 12; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index2});
+            }
+            // The fourth 4 52-tone RUs are in the fourth 20 MHz channel
+            for (std::size_t idx = 13; idx <= 16; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_52_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index3});
+            }
+            // The first 2 106-tone RUs are in the first 20 MHz channel
+            for (std::size_t idx = 1; idx <= 2; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index0});
+            }
+            // The second 2 106-tone RUs are in the second 20 MHz channel
+            for (std::size_t idx = 3; idx <= 4; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index1});
+            }
+            // The third 2 106-tone RUs are in the third 20 MHz channel
+            for (std::size_t idx = 5; idx <= 6; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index2});
+            }
+            // The fourth 2 106-tone RUs are in the fourth 20 MHz channel
+            for (std::size_t idx = 7; idx <= 8; idx++)
+            {
+                RunOne(p20Index,
+                       HeRu::RuSpec(HeRu::RU_106_TONE, idx, primary80MHz),
+                       width,
+                       {ch20Index3});
+            }
+            // The first 242-tone RU is in the first 20 MHz channel
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 1, primary80MHz), width, {ch20Index0});
+            // The second 242-tone RU is in the second 20 MHz channel
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 2, primary80MHz), width, {ch20Index1});
+            // The third 242-tone RU is in the third 20 MHz channel
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 3, primary80MHz), width, {ch20Index2});
+            // The fourth 242-tone RU is in the fourth 20 MHz channel
+            RunOne(p20Index, HeRu::RuSpec(HeRu::RU_242_TONE, 4, primary80MHz), width, {ch20Index3});
+            // The first 484-tone RU is covered by the first two 20 MHz channels
+            RunOne(p20Index,
+                   HeRu::RuSpec(HeRu::RU_484_TONE, 1, primary80MHz),
+                   width,
+                   {ch20Index0, ch20Index1});
+            // The second 484-tone RU is covered by the last two 20 MHz channels
+            RunOne(p20Index,
+                   HeRu::RuSpec(HeRu::RU_484_TONE, 2, primary80MHz),
+                   width,
+                   {ch20Index2, ch20Index3});
+            // The 996-tone RU is covered by all the 20 MHz channels
+            RunOne(p20Index,
+                   HeRu::RuSpec(HeRu::RU_996_TONE, 1, primary80MHz),
+                   width,
+                   {ch20Index0, ch20Index1, ch20Index2, ch20Index3});
+        }
+        // The 2x996-tone RU is covered by all the eight 20 MHz channels
+        RunOne(p20Index,
+               HeRu::RuSpec(HeRu::RU_2x996_TONE, 1, true),
+               width,
+               {0, 1, 2, 3, 4, 5, 6, 7});
+    }
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief wifi primary channels test suite
+ */
+class WifiRuAllocationTestSuite : public TestSuite
+{
+  public:
+    WifiRuAllocationTestSuite();
+};
+
+WifiRuAllocationTestSuite::WifiRuAllocationTestSuite()
+    : TestSuite("wifi-ru-allocation", UNIT)
+{
+    AddTestCase(new Wifi20MHzIndicesCoveringRuTest(), TestCase::QUICK);
+}
+
+static WifiRuAllocationTestSuite g_wifiRuAllocationTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-test.cc ns-3.38/src/wifi/test/wifi-test.cc
--- ns-3.37/src/wifi/test/wifi-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-test.cc	2023-03-16 20:54:23.876869400 -0700
@@ -1095,7 +1095,7 @@
         phySta = GetYansWifiPhyPtr(staDevice);
         NS_TEST_ASSERT_MSG_EQ(phySta->GetChannelNumber(), 7, "802.11ax-6GHz configuration");
         NS_TEST_ASSERT_MSG_EQ(phySta->GetChannelWidth(), 80, "802.11ax-6GHz configuration");
-        NS_TEST_ASSERT_MSG_EQ(phySta->GetFrequency(), 5975, "802.11ax-6GHz configuration");
+        NS_TEST_ASSERT_MSG_EQ(phySta->GetFrequency(), 5985, "802.11ax-6GHz configuration");
         phy.Set("ChannelSettings", StringValue("{0, 0, BAND_UNSPECIFIED, 0}")); // restore default
     }
     {
@@ -2161,6 +2161,20 @@
                                    uint8_t tid,
                                    OriginatorBlockAckAgreement::State state);
     /**
+     * Callback when a frame is transmitted.
+     * \param rxErrorModel the post reception error model on the receiver
+     * \param context the context
+     * \param psduMap the PSDU map
+     * \param txVector the TX vector
+     * \param txPowerW the tx power in Watts
+     */
+    void TxCallback(Ptr<ListErrorModel> rxErrorModel,
+                    std::string context,
+                    WifiConstPsduMap psduMap,
+                    WifiTxVector txVector,
+                    double txPowerW);
+
+    /**
      * Callback when packet is received
      * \param context node context
      * \param p the received packet
@@ -2195,10 +2209,9 @@
                          Address& destination) const;
     /**
      * Run subtest for this test suite
-     * \param apErrorModel ErrorModel used for AP
-     * \param staErrorModel ErrorModel used for STA
+     * \param rcvErrorType type of station (STA or AP) to install the post reception error model on
      */
-    void RunSubtest(PointerValue apErrorModel, PointerValue staErrorModel);
+    void RunSubtest(TypeOfStation rcvErrorType);
 
     uint16_t m_receivedNormalMpduCount; ///< Count received normal MPDU packets on STA
     uint16_t m_receivedAmpduCount;      ///< Count received A-MPDU packets on STA
@@ -2258,6 +2271,25 @@
 }
 
 void
+Bug2470TestCase::TxCallback(Ptr<ListErrorModel> rxErrorModel,
+                            std::string context,
+                            WifiConstPsduMap psduMap,
+                            WifiTxVector txVector,
+                            double txPowerW)
+{
+    auto psdu = psduMap.begin()->second;
+
+    // The sender is transmitting an ADDBA_REQUEST or ADDBA_RESPONSE frame. If this is
+    // the first attempt at establishing a BA agreement (i.e., before the second set of packets
+    // is generated), make the reception of the frame fail at the receiver.
+    if (psdu->GetHeader(0).GetType() == WIFI_MAC_MGT_ACTION && Simulator::Now() < Seconds(0.8))
+    {
+        auto uid = psdu->GetPayload(0)->GetUid();
+        rxErrorModel->SetList({uid});
+    }
+}
+
+void
 Bug2470TestCase::RxCallback(std::string context,
                             Ptr<const Packet> p,
                             uint16_t channelFreqMhz,
@@ -2307,7 +2339,7 @@
 }
 
 void
-Bug2470TestCase::RunSubtest(PointerValue apErrorModel, PointerValue staErrorModel)
+Bug2470TestCase::RunSubtest(TypeOfStation rcvErrorType)
 {
     RngSeedManager::SetSeed(1);
     RngSeedManager::SetRun(1);
@@ -2332,13 +2364,11 @@
 
     WifiMacHelper mac;
     NetDeviceContainer apDevice;
-    phy.Set("PostReceptionErrorModel", apErrorModel);
     phy.Set("ChannelSettings", StringValue("{36, 20, BAND_5GHZ, 0}"));
     mac.SetType("ns3::ApWifiMac", "EnableBeaconJitter", BooleanValue(false));
     apDevice = wifi.Install(phy, mac, wifiApNode);
 
     NetDeviceContainer staDevice;
-    phy.Set("PostReceptionErrorModel", staErrorModel);
     mac.SetType("ns3::StaWifiMac");
     staDevice = wifi.Install(phy, mac, wifiStaNode);
 
@@ -2356,6 +2386,21 @@
     mobility.Install(wifiApNode);
     mobility.Install(wifiStaNode);
 
+    auto rxErrorModel = CreateObject<ListErrorModel>();
+    Ptr<WifiMac> wifiMac;
+    switch (rcvErrorType)
+    {
+    case AP:
+        wifiMac = DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac();
+        break;
+    case STA:
+        wifiMac = DynamicCast<WifiNetDevice>(staDevice.Get(0))->GetMac();
+        break;
+    default:
+        NS_ABORT_MSG("Station type " << +rcvErrorType << " cannot be used here");
+    }
+    wifiMac->GetWifiPhy(0)->SetPostReceptionErrorModel(rxErrorModel);
+
     Config::Connect(
         "/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/$ns3::WifiPhy/MonitorSnifferRx",
         MakeCallback(&Bug2470TestCase::RxCallback, this));
@@ -2364,6 +2409,9 @@
     Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/$ns3::WifiMac/BE_Txop/"
                     "BlockAckManager/AgreementState",
                     MakeCallback(&Bug2470TestCase::AddbaStateChangedCallback, this));
+    Config::Connect("/NodeList/" + std::to_string(rcvErrorType == STA ? 0 /* AP */ : 1 /* STA */) +
+                        "/DeviceList/*/$ns3::WifiNetDevice/Phys/0/PhyTxPsduBegin",
+                    MakeCallback(&Bug2470TestCase::TxCallback, this).Bind(rxErrorModel));
 
     Simulator::Schedule(Seconds(0.5),
                         &Bug2470TestCase::SendPacketBurst,
@@ -2398,35 +2446,19 @@
 void
 Bug2470TestCase::DoRun()
 {
-    // Create ReceiveListErrorModel to corrupt ADDBA req packet. We use ReceiveListErrorModel
-    // instead of ListErrorModel since packet UID is incremented between simulations. But
-    // problem may occur because of random stream, therefore we suppress usage of RNG as
-    // much as possible (i.e., removing beacon jitter).
-    Ptr<ReceiveListErrorModel> staPem = CreateObject<ReceiveListErrorModel>();
-    std::list<uint32_t> blackList;
-    // Block ADDBA request 6 times (== maximum number of MAC frame transmissions in the ADDBA
-    // response timeout interval)
-    blackList.push_back(9);
-    blackList.push_back(10);
-    blackList.push_back(11);
-    blackList.push_back(12);
-    blackList.push_back(13);
-    blackList.push_back(14);
-    staPem->SetList(blackList);
-
     {
-        RunSubtest(PointerValue(), PointerValue(staPem));
-        NS_TEST_ASSERT_MSG_EQ(m_failedActionCount, 6, "ADDBA request packets are not failed");
+        RunSubtest(STA);
+        NS_TEST_ASSERT_MSG_EQ(m_failedActionCount, 7, "ADDBA request packets are not failed");
         // There are two sets of 5 packets to be transmitted. The first 5 packets should be sent by
         // normal MPDU because of failed ADDBA handshake. For the second set, the first packet
-        // should be sent by normal MPDU, and the rest with A-MPDU. In total we expect to receive 2
-        // normal MPDU packets and 8 A-MPDU packets.
+        // should be sent by normal MPDU, and the rest with A-MPDU. In total we expect to receive 6
+        // normal MPDU packets and 4 A-MPDU packet.
         NS_TEST_ASSERT_MSG_EQ(m_receivedNormalMpduCount,
-                              2,
+                              6,
                               "Receiving incorrect number of normal MPDU packet on subtest 1");
         NS_TEST_ASSERT_MSG_EQ(m_receivedAmpduCount,
-                              8,
-                              "Receiving incorrect number of A-MPDU packet on subtest 1");
+                              4,
+                              "Receiving incorrect number of A-MPDU packets on subtest 1");
 
         NS_TEST_ASSERT_MSG_EQ(m_addbaEstablishedCount,
                               1,
@@ -2434,7 +2466,7 @@
                               "established state on subtest 1");
         NS_TEST_ASSERT_MSG_EQ(
             m_addbaPendingCount,
-            1,
+            2,
             "Incorrect number of times the ADDBA state machine was in pending state on subtest 1");
         NS_TEST_ASSERT_MSG_EQ(
             m_addbaRejectedCount,
@@ -2442,11 +2474,11 @@
             "Incorrect number of times the ADDBA state machine was in rejected state on subtest 1");
         NS_TEST_ASSERT_MSG_EQ(
             m_addbaNoReplyCount,
-            0,
+            1,
             "Incorrect number of times the ADDBA state machine was in no_reply state on subtest 1");
         NS_TEST_ASSERT_MSG_EQ(
             m_addbaResetCount,
-            0,
+            1,
             "Incorrect number of times the ADDBA state machine was in reset state on subtest 1");
     }
 
@@ -2459,19 +2491,9 @@
     m_addbaNoReplyCount = 0;
     m_addbaResetCount = 0;
 
-    Ptr<ReceiveListErrorModel> apPem = CreateObject<ReceiveListErrorModel>();
-    blackList.clear();
-    // Block ADDBA request 4 times (== maximum number of MAC frame transmissions in the ADDBA
-    // response timeout interval)
-    blackList.push_back(5);
-    blackList.push_back(6);
-    blackList.push_back(7);
-    blackList.push_back(9);
-    apPem->SetList(blackList);
-
     {
-        RunSubtest(PointerValue(apPem), PointerValue());
-        NS_TEST_ASSERT_MSG_EQ(m_failedActionCount, 4, "ADDBA response packets are not failed");
+        RunSubtest(AP);
+        NS_TEST_ASSERT_MSG_EQ(m_failedActionCount, 7, "ADDBA response packets are not failed");
         // Similar to subtest 1, we also expect to receive 6 normal MPDU packets and 4 A-MPDU
         // packets.
         NS_TEST_ASSERT_MSG_EQ(m_receivedNormalMpduCount,
@@ -2487,7 +2509,7 @@
                               "established state on subtest 2");
         NS_TEST_ASSERT_MSG_EQ(
             m_addbaPendingCount,
-            1,
+            2,
             "Incorrect number of times the ADDBA state machine was in pending state on subtest 2");
         NS_TEST_ASSERT_MSG_EQ(
             m_addbaRejectedCount,
@@ -2499,7 +2521,7 @@
             "Incorrect number of times the ADDBA state machine was in no_reply state on subtest 2");
         NS_TEST_ASSERT_MSG_EQ(
             m_addbaResetCount,
-            0,
+            1,
             "Incorrect number of times the ADDBA state machine was in reset state on subtest 2");
     }
 
@@ -2560,7 +2582,7 @@
     uint16_t m_rxCount; ///< Count number of successfully received data packets
     uint16_t m_txCount; ///< Count number of transmitted data packets
     uint16_t
-        m_txMacFinalDataFailedCount; ///< Count number of unsuccessfully transmitted data packets
+        m_txMacFinalDataFailedCount; ///< Count number of unsuccessfuly transmitted data packets
 };
 
 Issue40TestCase::Issue40TestCase()
@@ -2677,7 +2699,7 @@
                         staDevice.Get(0)->GetAddress());
 
     // Transmit a second data packet once the station is away from the access point: it should be
-    // sent with the same high modulation and be unsuccessfully received
+    // sent with the same high modulation and be unsuccessfuly received
     Simulator::Schedule(Seconds(2.0),
                         &Issue40TestCase::SendPackets,
                         this,
@@ -3483,7 +3505,7 @@
                         staDevice.Get(0)->GetAddress());
     // Selected NSS should be 2 since both TX and RX support 2 antennas
     Simulator::Schedule(Seconds(9.2), &IdealRateManagerMimoTest::CheckLastSelectedNss, this, 2);
-    // Selecte rate should be the same as without diversity, as it uses 2 spatial streams so there
+    // Selected rate should be the same as without diversity, as it uses 2 spatial streams so there
     // is no more benefits from diversity in AWGN channels
     Simulator::Schedule(Seconds(9.2),
                         &IdealRateManagerMimoTest::CheckLastSelectedMode,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-transmit-mask-test.cc ns-3.38/src/wifi/test/wifi-transmit-mask-test.cc
--- ns-3.37/src/wifi/test/wifi-transmit-mask-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-transmit-mask-test.cc	2023-03-16 20:54:23.876869400 -0700
@@ -89,7 +89,7 @@
     */
     void InterpolateAndAppendValues(IndexPowerVect& vect,
                                     IndexPowerPair start,
-                                    IndexPowerPair stop);
+                                    IndexPowerPair stop) const;
 
     WifiStandard m_standard; ///< the wifi standard to use for the test
     WifiPhyBand m_band;      ///< the wifi PHY band to use for the test
@@ -253,7 +253,7 @@
 void
 WifiOfdmMaskSlopesTestCase::InterpolateAndAppendValues(IndexPowerVect& vect,
                                                        IndexPowerPair start,
-                                                       IndexPowerPair stop)
+                                                       IndexPowerPair stop) const
 {
     NS_LOG_FUNCTION(start.first << start.second << stop.first << stop.second);
     NS_ASSERT(start.first <= stop.first);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wifi/test/wifi-txop-test.cc ns-3.38/src/wifi/test/wifi-txop-test.cc
--- ns-3.37/src/wifi/test/wifi-txop-test.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wifi/test/wifi-txop-test.cc	2023-03-16 20:54:23.876869400 -0700
@@ -339,7 +339,7 @@
 WifiTxopTest::CheckResults()
 {
     Time tEnd;                        // TX end for a frame
-    Time tStart;                      // TX start fot the next frame
+    Time tStart;                      // TX start for the next frame
     Time txopStart;                   // TXOP start time
     Time tolerance = NanoSeconds(50); // due to propagation delay
     Time sifs = DynamicCast<WifiNetDevice>(m_apDevices.Get(0))->GetPhy()->GetSifs();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/doc/wimax.rst ns-3.38/src/wimax/doc/wimax.rst
--- ns-3.37/src/wimax/doc/wimax.rst	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wimax/doc/wimax.rst	2023-03-16 20:54:23.876869400 -0700
@@ -99,18 +99,18 @@
   NodeContainer ssNodes;
   NodeContainer bsNodes;
 
-  ssNodes.Create (2);
-  bsNodes.Create (1);
+  ssNodes.Create(2);
+  bsNodes.Create(1);
 
   WimaxHelper wimax;
 
   NetDeviceContainer ssDevs, bsDevs;
 
-  ssDevs = wimax.Install (ssNodes,
-                          WimaxHelper::DEVICE_TYPE_SUBSCRIBER_STATION,
-                          WimaxHelper::SIMPLE_PHY_TYPE_OFDM,
-                          scheduler);
-  bsDevs = wimax.Install (bsNodes, WimaxHelper::DEVICE_TYPE_BASE_STATION, WimaxHelper::SIMPLE_PHY_TYPE_OFDM, scheduler);
+  ssDevs = wimax.Install(ssNodes,
+                         WimaxHelper::DEVICE_TYPE_SUBSCRIBER_STATION,
+                         WimaxHelper::SIMPLE_PHY_TYPE_OFDM,
+                         scheduler);
+  bsDevs = wimax.Install(bsNodes, WimaxHelper::DEVICE_TYPE_BASE_STATION, WimaxHelper::SIMPLE_PHY_TYPE_OFDM, scheduler);
 
 This example shows that there are two subscriber stations and one base station
 created. The helper method ``Install`` allows the user to specify the scheduler
@@ -120,18 +120,18 @@
 ``src/wimax/examples/wimax-multicast.cc`` shows how to specify a non-default channel
 or propagation model::
 
-  channel = CreateObject<SimpleOfdmWimaxChannel> ();
-  channel->SetPropagationModel (SimpleOfdmWimaxChannel::COST231_PROPAGATION);
-  ssDevs = wimax.Install (ssNodes,
-                          WimaxHelper::DEVICE_TYPE_SUBSCRIBER_STATION,
-                          WimaxHelper::SIMPLE_PHY_TYPE_OFDM,
-                          channel,
-                          scheduler);
-  Ptr<WimaxNetDevice> dev = wimax.Install (bsNodes.Get (0),
-                                           WimaxHelper::DEVICE_TYPE_BASE_STATION,
-                                           WimaxHelper::SIMPLE_PHY_TYPE_OFDM,
-                                           channel,
-                                           scheduler);
+  channel = CreateObject<SimpleOfdmWimaxChannel>();
+  channel->SetPropagationModel(SimpleOfdmWimaxChannel::COST231_PROPAGATION);
+  ssDevs = wimax.Install(ssNodes,
+                         WimaxHelper::DEVICE_TYPE_SUBSCRIBER_STATION,
+                         WimaxHelper::SIMPLE_PHY_TYPE_OFDM,
+                         channel,
+                         scheduler);
+  Ptr<WimaxNetDevice> dev = wimax.Install(bsNodes.Get(0),
+                                          WimaxHelper::DEVICE_TYPE_BASE_STATION,
+                                          WimaxHelper::SIMPLE_PHY_TYPE_OFDM,
+                                          channel,
+                                          scheduler);
 
 Mobility is also supported in the same way as in Wifi models; see the
 ``src/wimax/examples/wimax-multicast.cc``.
@@ -143,11 +143,11 @@
 following code from ``src/wimax/examples/wimax-multicast.cc`` shows how this is
 configured from a helper level::
 
- ServiceFlow MulticastServiceFlow = wimax.CreateServiceFlow (ServiceFlow::SF_DIRECTION_DOWN,
-                                                             ServiceFlow::SF_TYPE_UGS,
-                                                             MulticastClassifier);
+ ServiceFlow MulticastServiceFlow = wimax.CreateServiceFlow(ServiceFlow::SF_DIRECTION_DOWN,
+                                                            ServiceFlow::SF_TYPE_UGS,
+                                                            MulticastClassifier);
 
-  bs->GetServiceFlowManager ()->AddMulticastServiceFlow (MulticastServiceFlow, WimaxPhy::MODULATION_TYPE_QPSK_12);
+  bs->GetServiceFlowManager()->AddMulticastServiceFlow(MulticastServiceFlow, WimaxPhy::MODULATION_TYPE_QPSK_12);
 
 
 Wimax Attributes
@@ -183,8 +183,8 @@
 
     AsciiTraceHelper ascii;
     WimaxHelper wimax;
-    wimax.EnablePcap ("wimax-program", false);
-    wimax.EnableAsciiAll (ascii.CreateFileStream ("wimax-program.tr");
+    wimax.EnablePcap("wimax-program", false);
+    wimax.EnableAsciiAll(ascii.CreateFileStream("wimax-program.tr");
 
 Unlike other helpers, there is also a special ``EnableAsciiForConnection()``
 method that limits the ascii tracing to a specific device and connection.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/examples/wimax-ipv4.cc ns-3.38/src/wimax/examples/wimax-ipv4.cc
--- ns-3.37/src/wimax/examples/wimax-ipv4.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wimax/examples/wimax-ipv4.cc	2023-03-16 20:54:23.876869400 -0700
@@ -28,7 +28,7 @@
 // scheduling type UGS, the second one rtPS and the third one BE.
 // Senders SSs send some stamped IP packets with variable bitrate to receiver SSs
 // through the BS station. receiver SSs receive the IP packets analyze them and
-// based on a sequnece number decide if all the packets are coorectly received
+// based on a sequence number decide if all the packets are coorectly received
 
 //      +-----+    +-----+    +-----+
 //      | SS0 |    | SS1 |    | SS2 |
@@ -144,7 +144,7 @@
     Ipv4InterfaceContainer BSinterface = address.Assign(bsDevs);
     if (verbose)
     {
-        wimax.EnableLogComponents(); // Turn on all wimax logging
+        WimaxHelper::EnableLogComponents(); // Turn on all wimax logging
     }
     /*------------------------------*/
     UdpServerHelper* udpServer = new UdpServerHelper[nbSS / 2];
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/examples/wimax-multicast.cc ns-3.38/src/wimax/examples/wimax-multicast.cc
--- ns-3.37/src/wimax/examples/wimax-multicast.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wimax/examples/wimax-multicast.cc	2023-03-16 20:54:23.876869400 -0700
@@ -157,7 +157,7 @@
     bsDevs.Add(dev);
     if (verbose)
     {
-        wimax.EnableLogComponents(); // Turn on all wimax logging
+        WimaxHelper::EnableLogComponents(); // Turn on all wimax logging
     }
 
     for (int i = 0; i < nbSS; i++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/examples/wimax-simple.cc ns-3.38/src/wimax/examples/wimax-simple.cc
--- ns-3.37/src/wimax/examples/wimax-simple.cc	2022-11-01 17:37:24.763395800 -0700
+++ ns-3.38/src/wimax/examples/wimax-simple.cc	2023-03-16 20:54:23.876869400 -0700
@@ -136,7 +136,7 @@
 
     if (verbose)
     {
-        wimax.EnableLogComponents(); // Turn on all wimax logging
+        WimaxHelper::EnableLogComponents(); // Turn on all wimax logging
     }
     /*------------------------------*/
     UdpServerHelper udpServer;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bs-net-device.cc ns-3.38/src/wimax/model/bs-net-device.cc
--- ns-3.37/src/wimax/model/bs-net-device.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bs-net-device.cc	2023-03-16 20:54:23.876869400 -0700
@@ -920,7 +920,7 @@
 
     /*either DCD and UCD must be created first because CCC is set during their
      creation, or CCC must be calculated first so that it could be set during
-     creation of DL-MAP and UL-MAP and then set duirng creation of DCD and UCD*/
+     creation of DL-MAP and UL-MAP and then set during creation of DCD and UCD*/
 
     if (sendDcd)
     {
@@ -990,7 +990,7 @@
     OfdmDlMapIe* dlMapIe;
     Cid cid;
 
-    while (downlinkBursts->size())
+    while (!downlinkBursts->empty())
     {
         pair = downlinkBursts->front();
         burst = pair.second;
@@ -1001,18 +1001,9 @@
         if (cid != GetInitialRangingConnection()->GetCid() &&
             cid != GetBroadcastConnection()->GetCid())
         {
-            if (m_serviceFlowManager->GetServiceFlow(cid) != nullptr)
-            {
-                modulationType =
-                    GetBurstProfileManager()->GetModulationType(diuc,
-                                                                WimaxNetDevice::DIRECTION_DOWNLINK);
-            }
-            else
-            {
-                modulationType =
-                    GetBurstProfileManager()->GetModulationType(diuc,
-                                                                WimaxNetDevice::DIRECTION_DOWNLINK);
-            }
+            modulationType =
+                GetBurstProfileManager()->GetModulationType(diuc,
+                                                            WimaxNetDevice::DIRECTION_DOWNLINK);
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bs-scheduler.cc ns-3.38/src/wimax/model/bs-scheduler.cc
--- ns-3.37/src/wimax/model/bs-scheduler.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bs-scheduler.cc	2023-03-16 20:54:23.876869400 -0700
@@ -68,7 +68,7 @@
 {
     std::list<std::pair<OfdmDlMapIe*, Ptr<PacketBurst>>>* downlinkBursts = m_downlinkBursts;
     std::pair<OfdmDlMapIe*, Ptr<PacketBurst>> pair;
-    while (downlinkBursts->size())
+    while (!downlinkBursts->empty())
     {
         pair = downlinkBursts->front();
         pair.second = nullptr;
@@ -99,7 +99,7 @@
     NS_LOG_INFO("BS Scheduler, CheckForFragmentation");
     if (connection->GetType() != Cid::TRANSPORT)
     {
-        NS_LOG_INFO("\t No Transport connction, Fragmentation IS NOT possible");
+        NS_LOG_INFO("\t No Transport connection, Fragmentation IS NOT possible");
         return false;
     }
     uint32_t availableByte = GetBs()->GetPhy()->GetNrBytes(availableSymbols, modulationType);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bs-scheduler-rtps.cc ns-3.38/src/wimax/model/bs-scheduler-rtps.cc
--- ns-3.37/src/wimax/model/bs-scheduler-rtps.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bs-scheduler-rtps.cc	2023-03-16 20:54:23.876869400 -0700
@@ -72,7 +72,7 @@
 {
     std::list<std::pair<OfdmDlMapIe*, Ptr<PacketBurst>>>* downlinkBursts = m_downlinkBursts;
     std::pair<OfdmDlMapIe*, Ptr<PacketBurst>> pair;
-    while (downlinkBursts->size())
+    while (!downlinkBursts->empty())
     {
         pair = downlinkBursts->front();
         pair.second = nullptr;
@@ -149,7 +149,7 @@
 
     BSSchedulerBEConnection(availableSymbols);
 
-    if (m_downlinkBursts->size())
+    if (!m_downlinkBursts->empty())
     {
         NS_LOG_DEBUG(
             "BS scheduler, number of bursts: "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bs-scheduler-simple.cc ns-3.38/src/wimax/model/bs-scheduler-simple.cc
--- ns-3.37/src/wimax/model/bs-scheduler-simple.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bs-scheduler-simple.cc	2023-03-16 20:54:23.876869400 -0700
@@ -71,7 +71,7 @@
 {
     std::list<std::pair<OfdmDlMapIe*, Ptr<PacketBurst>>>* downlinkBursts = m_downlinkBursts;
     std::pair<OfdmDlMapIe*, Ptr<PacketBurst>> pair;
-    while (downlinkBursts->size())
+    while (!downlinkBursts->empty())
     {
         pair = downlinkBursts->front();
         pair.second = nullptr;
@@ -220,7 +220,7 @@
         }
     }
 
-    if (m_downlinkBursts->size())
+    if (!m_downlinkBursts->empty())
     {
         NS_LOG_DEBUG(
             "BS scheduler, number of bursts: "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bs-uplink-scheduler-mbqos.cc ns-3.38/src/wimax/model/bs-uplink-scheduler-mbqos.cc
--- ns-3.37/src/wimax/model/bs-uplink-scheduler-mbqos.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bs-uplink-scheduler-mbqos.cc	2023-03-16 20:54:23.876869400 -0700
@@ -681,7 +681,7 @@
 UplinkSchedulerMBQoS::CheckDeadline(uint32_t& availableSymbols)
 {
     // for each request in the imermediate queue
-    if (m_uplinkJobs_inter.size() > 0)
+    if (!m_uplinkJobs_inter.empty())
     {
         std::list<Ptr<UlJob>>::iterator iter = m_uplinkJobs_inter.begin();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bs-uplink-scheduler-rtps.h ns-3.38/src/wimax/model/bs-uplink-scheduler-rtps.h
--- ns-3.37/src/wimax/model/bs-uplink-scheduler-rtps.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bs-uplink-scheduler-rtps.h	2023-03-16 20:54:23.876869400 -0700
@@ -188,7 +188,7 @@
     void SetupServiceFlow(SSRecord* ssRecord, ServiceFlow* serviceFlow) override;
 
     /**
-     * Process bandwidth requet function
+     * Process bandwidth request function
      * \param bwRequestHdr the bandwidth request header
      */
     void ProcessBandwidthRequest(const BandwidthRequestHeader& bwRequestHdr) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bs-uplink-scheduler-simple.h ns-3.38/src/wimax/model/bs-uplink-scheduler-simple.h
--- ns-3.37/src/wimax/model/bs-uplink-scheduler-simple.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bs-uplink-scheduler-simple.h	2023-03-16 20:54:23.876869400 -0700
@@ -143,7 +143,7 @@
     void SetupServiceFlow(SSRecord* ssRecord, ServiceFlow* serviceFlow) override;
 
     /**
-     * Process bandwidth requet function
+     * Process bandwidth request function
      * \param bwRequestHdr the bandwidth request header
      */
     void ProcessBandwidthRequest(const BandwidthRequestHeader& bwRequestHdr) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/burst-profile-manager.cc ns-3.38/src/wimax/model/burst-profile-manager.cc
--- ns-3.37/src/wimax/model/burst-profile-manager.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/burst-profile-manager.cc	2023-03-16 20:54:23.876869400 -0700
@@ -149,7 +149,7 @@
 uint8_t
 BurstProfileManager::GetBurstProfileForSS(const SSRecord* ssRecord,
                                           const RngReq* rngreq,
-                                          WimaxPhy::ModulationType& modulationType)
+                                          WimaxPhy::ModulationType& modulationType) const
 {
     /*during initial ranging or periodic ranging (or when RNG-REQ is used instead of
      DBPC) calculates the least robust burst profile for SS, e.g., based on distance,
@@ -160,7 +160,7 @@
 }
 
 WimaxPhy::ModulationType
-BurstProfileManager::GetModulationTypeForSS(const SSRecord* ssRecord, const RngReq* rngreq)
+BurstProfileManager::GetModulationTypeForSS(const SSRecord* ssRecord, const RngReq* rngreq) const
 {
     return GetModulationType(rngreq->GetReqDlBurstProfile(), WimaxNetDevice::DIRECTION_DOWNLINK);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/burst-profile-manager.h ns-3.38/src/wimax/model/burst-profile-manager.h
--- ns-3.37/src/wimax/model/burst-profile-manager.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/burst-profile-manager.h	2023-03-16 20:54:23.876869400 -0700
@@ -90,14 +90,15 @@
      */
     uint8_t GetBurstProfileForSS(const SSRecord* ssRecord,
                                  const RngReq* rngreq,
-                                 WimaxPhy::ModulationType& modulationType);
+                                 WimaxPhy::ModulationType& modulationType) const;
     /**
      * \brief Get module ation type for SS
      * \param ssRecord
      * \param rngreq
      * \returns the burst profile for SS
      */
-    WimaxPhy::ModulationType GetModulationTypeForSS(const SSRecord* ssRecord, const RngReq* rngreq);
+    WimaxPhy::ModulationType GetModulationTypeForSS(const SSRecord* ssRecord,
+                                                    const RngReq* rngreq) const;
     /**
      * \brief Get burst profile to request
      * \returns the burst profile for SS
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/bvec.h ns-3.38/src/wimax/model/bvec.h
--- ns-3.37/src/wimax/model/bvec.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/bvec.h	2023-03-16 20:54:23.876869400 -0700
@@ -26,7 +26,7 @@
 namespace ns3
 {
 /// boolean vector typedef
-typedef std::vector<bool> bvec;
+typedef std::vector<bool> Bvec;
 } // namespace ns3
 
 #endif /* BVEC_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/cid.cc ns-3.38/src/wimax/model/cid.cc
--- ns-3.37/src/wimax/model/cid.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/cid.cc	2023-03-16 20:54:23.876869400 -0700
@@ -114,7 +114,7 @@
 }
 
 /**
- * \brief output stream output opertor
+ * \brief output stream output operator
  * \param os output stream
  * \param cid CID
  * \returns output stream
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/crc8.cc ns-3.38/src/wimax/model/crc8.cc
--- ns-3.37/src/wimax/model/crc8.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/crc8.cc	2023-03-16 20:54:23.876869400 -0700
@@ -44,7 +44,8 @@
     0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
     0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
     0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
-    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3};
+    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3,
+};
 
 uint8_t
 CRC8Calculate(const uint8_t* data, int length)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/cs-parameters.h ns-3.38/src/wimax/model/cs-parameters.h
--- ns-3.37/src/wimax/model/cs-parameters.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/cs-parameters.h	2023-03-16 20:54:23.876869400 -0700
@@ -55,13 +55,13 @@
      * \param classifierDscAction the DCS action type
      * \param classifier the IPCS classifier record
      */
-    CsParameters(enum Action classifierDscAction, IpcsClassifierRecord classifier);
+    CsParameters(Action classifierDscAction, IpcsClassifierRecord classifier);
     /**
      * \brief sets the dynamic service classifier action to ADD, Change or delete. Only ADD is
      * supported
      * \param action the action enumeration
      */
-    void SetClassifierDscAction(enum Action action);
+    void SetClassifierDscAction(Action action);
     /**
      * \brief sets the packet classifier rules
      * \param packetClassifierRule the IPCS classifier record
@@ -70,7 +70,7 @@
     /**
      * \return the  dynamic service classifier action
      */
-    enum Action GetClassifierDscAction() const;
+    Action GetClassifierDscAction() const;
     /**
      * \return the  the packet classifier rules
      */
@@ -82,7 +82,7 @@
     Tlv ToTlv() const;
 
   private:
-    enum Action m_classifierDscAction;           ///< classifier DSC action
+    Action m_classifierDscAction;                ///< classifier DSC action
     IpcsClassifierRecord m_packetClassifierRule; ///< packet classifier rule
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/default-traces.h ns-3.38/src/wimax/model/default-traces.h
--- ns-3.37/src/wimax/model/default-traces.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/default-traces.h	2023-03-16 20:54:23.876869400 -0700
@@ -99,7 +99,8 @@
 
     {1.00000, 1.00000, 1.00000, 1.00000, 0.99941, 0.98950, 0.93732, 0.82094, 0.63678, 0.46376,
      0.29889, 0.18367, 0.10537, 0.06123, 0.03703, 0.01889, 0.01326, 0.00661, 0.00431, 0.00238,
-     0.00175, 0.00048, 0.00108, 0.00000, 0.00030, 0.00030, 0.00000, 0.00000, 0.00000}};
+     0.00175, 0.00048, 0.00108, 0.00000, 0.00030, 0.00030, 0.00000, 0.00000, 0.00000},
+};
 
 static const double modulation1[6][42] = {
 
@@ -135,7 +136,8 @@
      0.32219, 0.25960, 0.20713, 0.15527, 0.12453, 0.09355, 0.07386, 0.05968, 0.04018,
      0.03313, 0.02742, 0.02028, 0.01294, 0.01107, 0.00897, 0.00593, 0.00454, 0.00454,
      0.00238, 0.00238, 0.00135, 0.00135, 0.00188, 0.00162, 0.00030, 0.00064, 0.00048,
-     0.00000, 0.00108, 0.00030, 0.00000, 0.00030, 0.00000}};
+     0.00000, 0.00108, 0.00030, 0.00000, 0.00030, 0.00000},
+};
 
 static const double modulation2[6][96] = {
 
@@ -201,7 +203,8 @@
      0.00535, 0.00547, 0.00454, 0.00324, 0.00336, 0.00336, 0.00300, 0.00175, 0.00275, 0.00188,
      0.00238, 0.00162, 0.00175, 0.00226, 0.00162, 0.00188, 0.00122, 0.00048, 0.00135, 0.00094,
      0.00064, 0.00000, 0.00094, 0.00030, 0.00094, 0.00030, 0.00030, 0.00030, 0.00048, 0.00048,
-     0.00064, 0.00030, 0.00048, 0.00000, 0.00000, 0.00000}};
+     0.00064, 0.00030, 0.00048, 0.00000, 0.00000, 0.00000},
+};
 
 static const double modulation3[6][117] = {
 
@@ -278,7 +281,8 @@
      0.00201, 0.00188, 0.00213, 0.00122, 0.00122, 0.00175, 0.00135, 0.00048, 0.00094, 0.00162,
      0.00108, 0.00122, 0.00064, 0.00122, 0.00108, 0.00122, 0.00030, 0.00079, 0.00079, 0.00064,
      0.00048, 0.00048, 0.00048, 0.00048, 0.00048, 0.00079, 0.00000, 0.00030, 0.00000, 0.00030,
-     0.00048, 0.00079, 0.00048, 0.00030, 0.00030, 0.00079, 0.00000}};
+     0.00048, 0.00079, 0.00048, 0.00030, 0.00030, 0.00079, 0.00000},
+};
 
 static const double modulation4[6][331] = {
 
@@ -492,7 +496,8 @@
      0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
      0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
      0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
-     0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000}};
+     0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000},
+};
 
 static const double modulation5[6][547] = {
 
@@ -821,7 +826,8 @@
      0.01490, 0.01500, 0.01479, 0.01413, 0.01348, 0.01184, 0.01074, 0.01173, 0.01008, 0.01304,
      0.01041, 0.01206, 0.01151, 0.01304, 0.01272, 0.00952, 0.01250, 0.01052, 0.00852, 0.01063,
      0.01085, 0.01118, 0.00952, 0.01283, 0.01107, 0.00819, 0.01008, 0.00975, 0.01140, 0.00952,
-     0.00875, 0.00919}};
+     0.00875, 0.00919},
+};
 
 static const double modulation6[6][626] = {
 
@@ -1179,7 +1185,8 @@
      0.03313, 0.02806, 0.02870, 0.03028, 0.02944, 0.02901, 0.02774, 0.03018, 0.02901, 0.03134,
      0.02848, 0.03261, 0.03134, 0.02626, 0.02711, 0.02445, 0.02519, 0.02445, 0.03123, 0.03060,
      0.02488, 0.02296, 0.02135, 0.02028, 0.02891, 0.02221, 0.02328, 0.02317, 0.02509, 0.02221,
-     0.02753, 0.02604, 0.02103, 0.02328, 0.02200, 0.01953, 0.02210, 0.01964, 0.01943}};
+     0.02753, 0.02604, 0.02103, 0.02328, 0.02200, 0.01953, 0.02210, 0.01964, 0.01943},
+};
 
 /** @}*/
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/dl-mac-messages.h ns-3.38/src/wimax/model/dl-mac-messages.h
--- ns-3.37/src/wimax/model/dl-mac-messages.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/dl-mac-messages.h	2023-03-16 20:54:23.876869400 -0700
@@ -511,7 +511,7 @@
     uint16_t GetSize() const;
 
     /**
-     * Wrtie item
+     * Write item
      * \param start the iterator
      * \returns the updated iterator
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ipcs-classifier-record.cc ns-3.38/src/wimax/model/ipcs-classifier-record.cc
--- ns-3.37/src/wimax/model/ipcs-classifier-record.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ipcs-classifier-record.cc	2023-03-16 20:54:23.876869400 -0700
@@ -85,7 +85,7 @@
         }
         case ClassificationRuleVectorTlvValue::IP_src: {
             Ipv4AddressTlvValue* list = (Ipv4AddressTlvValue*)(*iter)->PeekValue();
-            for (std::vector<Ipv4AddressTlvValue::ipv4Addr>::const_iterator iter2 = list->Begin();
+            for (std::vector<Ipv4AddressTlvValue::Ipv4Addr>::const_iterator iter2 = list->Begin();
                  iter2 != list->End();
                  ++iter2)
             {
@@ -95,7 +95,7 @@
         }
         case ClassificationRuleVectorTlvValue::IP_dst: {
             Ipv4AddressTlvValue* list = (Ipv4AddressTlvValue*)(*iter)->PeekValue();
-            for (std::vector<Ipv4AddressTlvValue::ipv4Addr>::const_iterator iter2 = list->Begin();
+            for (std::vector<Ipv4AddressTlvValue::Ipv4Addr>::const_iterator iter2 = list->Begin();
                  iter2 != list->End();
                  ++iter2)
             {
@@ -158,7 +158,7 @@
 void
 IpcsClassifierRecord::AddSrcAddr(Ipv4Address srcAddress, Ipv4Mask srcMask)
 {
-    struct ipv4Addr tmp;
+    Ipv4Addr tmp;
     tmp.Address = srcAddress;
     tmp.Mask = srcMask;
     m_srcAddr.push_back(tmp);
@@ -167,7 +167,7 @@
 void
 IpcsClassifierRecord::AddDstAddr(Ipv4Address dstAddress, Ipv4Mask dstMask)
 {
-    struct ipv4Addr tmp;
+    Ipv4Addr tmp;
     tmp.Address = dstAddress;
     tmp.Mask = dstMask;
     m_dstAddr.push_back(tmp);
@@ -176,7 +176,7 @@
 void
 IpcsClassifierRecord::AddSrcPortRange(uint16_t srcPortLow, uint16_t srcPortHigh)
 {
-    struct PortRange tmp;
+    PortRange tmp;
     tmp.PortLow = srcPortLow;
     tmp.PortHigh = srcPortHigh;
     m_srcPortRange.push_back(tmp);
@@ -185,7 +185,7 @@
 void
 IpcsClassifierRecord::AddDstPortRange(uint16_t dstPortLow, uint16_t dstPortHigh)
 {
-    struct PortRange tmp;
+    PortRange tmp;
     tmp.PortLow = dstPortLow;
     tmp.PortHigh = dstPortHigh;
     m_dstPortRange.push_back(tmp);
@@ -236,8 +236,7 @@
 bool
 IpcsClassifierRecord::CheckMatchSrcAddr(Ipv4Address srcAddress) const
 {
-    for (std::vector<struct ipv4Addr>::const_iterator iter = m_srcAddr.begin();
-         iter != m_srcAddr.end();
+    for (std::vector<Ipv4Addr>::const_iterator iter = m_srcAddr.begin(); iter != m_srcAddr.end();
          ++iter)
     {
         NS_LOG_INFO("src addr check match: pkt=" << srcAddress << " cls=" << (*iter).Address << "/"
@@ -254,8 +253,7 @@
 bool
 IpcsClassifierRecord::CheckMatchDstAddr(Ipv4Address dstAddress) const
 {
-    for (std::vector<struct ipv4Addr>::const_iterator iter = m_dstAddr.begin();
-         iter != m_dstAddr.end();
+    for (std::vector<Ipv4Addr>::const_iterator iter = m_dstAddr.begin(); iter != m_dstAddr.end();
          ++iter)
     {
         NS_LOG_INFO("dst addr check match: pkt=" << dstAddress << " cls=" << (*iter).Address << "/"
@@ -272,7 +270,7 @@
 bool
 IpcsClassifierRecord::CheckMatchSrcPort(uint16_t port) const
 {
-    for (std::vector<struct PortRange>::const_iterator iter = m_srcPortRange.begin();
+    for (std::vector<PortRange>::const_iterator iter = m_srcPortRange.begin();
          iter != m_srcPortRange.end();
          ++iter)
     {
@@ -290,7 +288,7 @@
 bool
 IpcsClassifierRecord::CheckMatchDstPort(uint16_t port) const
 {
-    for (std::vector<struct PortRange>::const_iterator iter = m_dstPortRange.begin();
+    for (std::vector<PortRange>::const_iterator iter = m_dstPortRange.begin();
          iter != m_dstPortRange.end();
          ++iter)
     {
@@ -336,16 +334,14 @@
 IpcsClassifierRecord::ToTlv() const
 {
     Ipv4AddressTlvValue ipv4AddrValSrc;
-    for (std::vector<struct ipv4Addr>::const_iterator iter = m_srcAddr.begin();
-         iter != m_srcAddr.end();
+    for (std::vector<Ipv4Addr>::const_iterator iter = m_srcAddr.begin(); iter != m_srcAddr.end();
          ++iter)
     {
         ipv4AddrValSrc.Add((*iter).Address, (*iter).Mask);
     }
 
     Ipv4AddressTlvValue ipv4AddrValDst;
-    for (std::vector<struct ipv4Addr>::const_iterator iter = m_dstAddr.begin();
-         iter != m_dstAddr.end();
+    for (std::vector<Ipv4Addr>::const_iterator iter = m_dstAddr.begin(); iter != m_dstAddr.end();
          ++iter)
     {
         ipv4AddrValDst.Add((*iter).Address, (*iter).Mask);
@@ -359,7 +355,7 @@
     }
 
     PortRangeTlvValue portValueSrc;
-    for (std::vector<struct PortRange>::const_iterator iter = m_srcPortRange.begin();
+    for (std::vector<PortRange>::const_iterator iter = m_srcPortRange.begin();
          iter != m_srcPortRange.end();
          ++iter)
     {
@@ -367,7 +363,7 @@
     }
 
     PortRangeTlvValue portValueDst;
-    for (std::vector<struct PortRange>::const_iterator iter = m_dstPortRange.begin();
+    for (std::vector<PortRange>::const_iterator iter = m_dstPortRange.begin();
          iter != m_dstPortRange.end();
          ++iter)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ipcs-classifier-record.h ns-3.38/src/wimax/model/ipcs-classifier-record.h
--- ns-3.37/src/wimax/model/ipcs-classifier-record.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ipcs-classifier-record.h	2023-03-16 20:54:23.876869400 -0700
@@ -183,23 +183,23 @@
         uint16_t PortHigh; ///< port high
     };
 
-    /// ipv4Addr structure
-    struct ipv4Addr
+    /// Ipv4Addr structure
+    struct Ipv4Addr
     {
         Ipv4Address Address; ///< IP address
         Ipv4Mask Mask;       ///< net mask
     };
 
-    uint8_t m_priority;                           ///< priority
-    uint16_t m_index;                             ///< index
-    uint8_t m_tosLow;                             ///< TOS low
-    uint8_t m_tosHigh;                            ///< TOS high
-    uint8_t m_tosMask;                            ///< TOS mask
-    std::vector<uint8_t> m_protocol;              ///< protocol
-    std::vector<struct ipv4Addr> m_srcAddr;       ///< source address
-    std::vector<struct ipv4Addr> m_dstAddr;       ///< destination address
-    std::vector<struct PortRange> m_srcPortRange; ///< surce port range
-    std::vector<struct PortRange> m_dstPortRange; ///< destination port range
+    uint8_t m_priority;                    ///< priority
+    uint16_t m_index;                      ///< index
+    uint8_t m_tosLow;                      ///< TOS low
+    uint8_t m_tosHigh;                     ///< TOS high
+    uint8_t m_tosMask;                     ///< TOS mask
+    std::vector<uint8_t> m_protocol;       ///< protocol
+    std::vector<Ipv4Addr> m_srcAddr;       ///< source address
+    std::vector<Ipv4Addr> m_dstAddr;       ///< destination address
+    std::vector<PortRange> m_srcPortRange; ///< source port range
+    std::vector<PortRange> m_dstPortRange; ///< destination port range
 
     uint16_t m_cid; ///< the CID
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/mac-messages.h ns-3.38/src/wimax/model/mac-messages.h
--- ns-3.37/src/wimax/model/mac-messages.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/mac-messages.h	2023-03-16 20:54:23.880869400 -0700
@@ -376,8 +376,6 @@
 
 namespace ns3
 {
-class serviceFlow;
-
 /**
  * \ingroup wimax
  * This class implements the DSA-REQ message described by "IEEE Standard for
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ofdm-downlink-frame-prefix.h ns-3.38/src/wimax/model/ofdm-downlink-frame-prefix.h
--- ns-3.37/src/wimax/model/ofdm-downlink-frame-prefix.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ofdm-downlink-frame-prefix.h	2023-03-16 20:54:23.880869400 -0700
@@ -166,7 +166,7 @@
     void SetConfigurationChangeCount(uint8_t configurationChangeCount);
     /**
      * Add DL frame prefix element field
-     * \param dlFramePrefixElement the DL frane prefix element
+     * \param dlFramePrefixElement the DL frame prefix element
      */
     void AddDlFramePrefixElement(DlFramePrefixIe dlFramePrefixElement);
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/service-flow.h ns-3.38/src/wimax/model/service-flow.h
--- ns-3.37/src/wimax/model/service-flow.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/service-flow.h	2023-03-16 20:54:23.880869400 -0700
@@ -129,7 +129,7 @@
      *
      * \param direction the direction
      */
-    ServiceFlow(enum Direction direction);
+    ServiceFlow(Direction direction);
     /**
      * Constructor
      *
@@ -143,7 +143,7 @@
      * \param direction the direction
      * \param connection the connection object
      */
-    ServiceFlow(uint32_t sfid, enum Direction direction, Ptr<WimaxConnection> connection);
+    ServiceFlow(uint32_t sfid, Direction direction, Ptr<WimaxConnection> connection);
     /**
      * Destructor.
      */
@@ -163,12 +163,12 @@
      * Set direction
      * \param direction the direction value
      */
-    void SetDirection(enum Direction direction);
+    void SetDirection(Direction direction);
     /**
      * Get direction
      * \returns the direction
      */
-    enum Direction GetDirection() const;
+    Direction GetDirection() const;
     /**
      * Copy parameters from another service flow
      * \param sf the service flow
@@ -179,12 +179,12 @@
      * Set type of service flow
      * \param type the type value
      */
-    void SetType(enum Type type);
+    void SetType(Type type);
     /**
      * Get type of service flow
      * \returns the type
      */
-    enum Type GetType() const;
+    Type GetType() const;
     /**
      * Set connection
      * \param connection the connection
@@ -387,7 +387,7 @@
      * Get CS specification
      * \returns the CS specification
      */
-    enum CsSpecification GetCsSpecification() const;
+    CsSpecification GetCsSpecification() const;
     /**
      * Get convergence sublayer
      * \returns the convergence sublayer
@@ -538,7 +538,7 @@
      * Set CS specification
      * \param spec the CS specification
      */
-    void SetCsSpecification(enum CsSpecification spec);
+    void SetCsSpecification(CsSpecification spec);
     /**
      * Set convergence sublayer parameters
      * \param csparam the convergence sublayer parameters
@@ -591,7 +591,7 @@
     uint8_t m_arqDeliverInOrder;                       ///< ARQ deliver in order
     uint16_t m_arqPurgeTimeout;                        ///< ARQ purge timeout
     uint16_t m_arqBlockSize;                           ///< ARQ block size
-    enum CsSpecification m_csSpecification;            ///< CS specification
+    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
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/service-flow-record.cc ns-3.38/src/wimax/model/service-flow-record.cc
--- ns-3.37/src/wimax/model/service-flow-record.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/service-flow-record.cc	2023-03-16 20:54:23.880869400 -0700
@@ -165,7 +165,7 @@
 }
 
 uint32_t
-ServiceFlowRecord::GetRequestedBandwidth()
+ServiceFlowRecord::GetRequestedBandwidth() const
 {
     return m_requestedBandwidth;
 }
@@ -183,7 +183,7 @@
 }
 
 uint32_t
-ServiceFlowRecord::GetGrantedBandwidth()
+ServiceFlowRecord::GetGrantedBandwidth() const
 {
     return m_grantedBandwidth;
 }
@@ -201,7 +201,7 @@
 }
 
 uint32_t
-ServiceFlowRecord::GetGrantedBandwidthTemp()
+ServiceFlowRecord::GetGrantedBandwidthTemp() const
 {
     return m_grantedBandwidthTemp;
 }
@@ -267,7 +267,7 @@
 }
 
 uint32_t
-ServiceFlowRecord::GetBwSinceLastExpiry()
+ServiceFlowRecord::GetBwSinceLastExpiry() const
 {
     return m_bwSinceLastExpiry;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/service-flow-record.h ns-3.38/src/wimax/model/service-flow-record.h
--- ns-3.37/src/wimax/model/service-flow-record.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/service-flow-record.h	2023-03-16 20:54:23.880869400 -0700
@@ -141,7 +141,7 @@
     /**
      * \return The requested bandwidth
      */
-    uint32_t GetRequestedBandwidth();
+    uint32_t GetRequestedBandwidth() const;
 
     /**
      * \brief set the granted bandwidth
@@ -156,7 +156,7 @@
     /**
      * \return The granted bandwidth
      */
-    uint32_t GetGrantedBandwidth();
+    uint32_t GetGrantedBandwidth() const;
 
     /**
      * \brief set the temporary granted bandwidth
@@ -171,7 +171,7 @@
     /**
      * \return The temporary granted bandwidth
      */
-    uint32_t GetGrantedBandwidthTemp();
+    uint32_t GetGrantedBandwidthTemp() const;
 
     /**
      * \brief set BW since last expiry
@@ -186,7 +186,7 @@
     /**
      * \return The bandwidth since last expiry
      */
-    uint32_t GetBwSinceLastExpiry();
+    uint32_t GetBwSinceLastExpiry() const;
 
     /**
      * \brief set last grant time
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/simple-ofdm-send-param.cc ns-3.38/src/wimax/model/simple-ofdm-send-param.cc
--- ns-3.37/src/wimax/model/simple-ofdm-send-param.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/simple-ofdm-send-param.cc	2023-03-16 20:54:23.880869400 -0700
@@ -25,7 +25,7 @@
 
 namespace ns3
 {
-simpleOfdmSendParam::simpleOfdmSendParam()
+SimpleOfdmSendParam::SimpleOfdmSendParam()
 {
     // m_fecBlock = 0;
     m_burstSize = 0;
@@ -36,7 +36,7 @@
     m_rxPowerDbm = 0;
 }
 
-simpleOfdmSendParam::simpleOfdmSendParam(const bvec& fecBlock,
+SimpleOfdmSendParam::SimpleOfdmSendParam(const Bvec& fecBlock,
                                          uint32_t burstSize,
                                          bool isFirstBlock,
                                          uint64_t Frequency,
@@ -53,7 +53,7 @@
     m_rxPowerDbm = rxPowerDbm;
 }
 
-simpleOfdmSendParam::simpleOfdmSendParam(uint32_t burstSize,
+SimpleOfdmSendParam::SimpleOfdmSendParam(uint32_t burstSize,
                                          bool isFirstBlock,
                                          uint64_t Frequency,
                                          WimaxPhy::ModulationType modulationType,
@@ -70,96 +70,96 @@
     m_burst = burst;
 }
 
-simpleOfdmSendParam::~simpleOfdmSendParam()
+SimpleOfdmSendParam::~SimpleOfdmSendParam()
 {
 }
 
 void
-simpleOfdmSendParam::SetFecBlock(const bvec& fecBlock)
+SimpleOfdmSendParam::SetFecBlock(const Bvec& fecBlock)
 {
     m_fecBlock = fecBlock;
 }
 
 void
-simpleOfdmSendParam::SetBurstSize(uint32_t burstSize)
+SimpleOfdmSendParam::SetBurstSize(uint32_t burstSize)
 {
     m_burstSize = burstSize;
 }
 
 void
-simpleOfdmSendParam::SetIsFirstBlock(bool isFirstBlock)
+SimpleOfdmSendParam::SetIsFirstBlock(bool isFirstBlock)
 {
     m_isFirstBlock = isFirstBlock;
 }
 
 void
-simpleOfdmSendParam::SetFrequency(uint64_t Frequency)
+SimpleOfdmSendParam::SetFrequency(uint64_t Frequency)
 {
     m_frequency = Frequency;
 }
 
 void
-simpleOfdmSendParam::SetModulationType(WimaxPhy::ModulationType modulationType)
+SimpleOfdmSendParam::SetModulationType(WimaxPhy::ModulationType modulationType)
 {
     m_modulationType = modulationType;
 }
 
 void
-simpleOfdmSendParam::SetDirection(uint8_t direction)
+SimpleOfdmSendParam::SetDirection(uint8_t direction)
 {
     m_direction = direction;
 }
 
 void
-simpleOfdmSendParam::SetRxPowerDbm(double rxPowerDbm)
+SimpleOfdmSendParam::SetRxPowerDbm(double rxPowerDbm)
 {
     m_rxPowerDbm = rxPowerDbm;
 }
 
-bvec
-simpleOfdmSendParam::GetFecBlock()
+Bvec
+SimpleOfdmSendParam::GetFecBlock()
 {
     return m_fecBlock;
 }
 
 uint32_t
-simpleOfdmSendParam::GetBurstSize()
+SimpleOfdmSendParam::GetBurstSize() const
 {
     return m_burstSize;
 }
 
 bool
-simpleOfdmSendParam::GetIsFirstBlock()
+SimpleOfdmSendParam::GetIsFirstBlock() const
 {
     return m_isFirstBlock;
 }
 
 uint64_t
-simpleOfdmSendParam::GetFrequency()
+SimpleOfdmSendParam::GetFrequency() const
 {
     return m_frequency;
 }
 
 WimaxPhy::ModulationType
-simpleOfdmSendParam::GetModulationType()
+SimpleOfdmSendParam::GetModulationType()
 {
     return m_modulationType;
 }
 
 uint8_t
-simpleOfdmSendParam::GetDirection()
+SimpleOfdmSendParam::GetDirection() const
 {
     return m_direction;
 }
 
 double
-simpleOfdmSendParam::GetRxPowerDbm()
+SimpleOfdmSendParam::GetRxPowerDbm() const
 {
     return m_rxPowerDbm;
 }
 
 Ptr<PacketBurst>
-simpleOfdmSendParam::GetBurst()
+SimpleOfdmSendParam::GetBurst()
 {
     return m_burst;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/simple-ofdm-send-param.h ns-3.38/src/wimax/model/simple-ofdm-send-param.h
--- ns-3.37/src/wimax/model/simple-ofdm-send-param.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/simple-ofdm-send-param.h	2023-03-16 20:54:23.880869400 -0700
@@ -34,12 +34,12 @@
 
 /**
  * \ingroup wimax
- * simpleOfdmSendParam class
+ * SimpleOfdmSendParam class
  */
-class simpleOfdmSendParam
+class SimpleOfdmSendParam
 {
   public:
-    simpleOfdmSendParam();
+    SimpleOfdmSendParam();
     /**
      * Constructor
      *
@@ -51,7 +51,7 @@
      * \param direction the direction
      * \param rxPowerDbm receive power
      */
-    simpleOfdmSendParam(const bvec& fecBlock,
+    SimpleOfdmSendParam(const Bvec& fecBlock,
                         uint32_t burstSize,
                         bool isFirstBlock,
                         uint64_t Frequency,
@@ -69,19 +69,19 @@
      * \param rxPowerDbm receive power
      * \param burst packet burst object
      */
-    simpleOfdmSendParam(uint32_t burstSize,
+    SimpleOfdmSendParam(uint32_t burstSize,
                         bool isFirstBlock,
                         uint64_t Frequency,
                         WimaxPhy::ModulationType modulationType,
                         uint8_t direction,
                         double rxPowerDbm,
                         Ptr<PacketBurst> burst);
-    ~simpleOfdmSendParam();
+    ~SimpleOfdmSendParam();
     /**
      * \brief sent the fec block to send
      * \param fecBlock the fec block to send
      */
-    void SetFecBlock(const bvec& fecBlock);
+    void SetFecBlock(const Bvec& fecBlock);
     /**
      * \brief set the burst size
      * \param burstSize the burst size in bytes
@@ -111,19 +111,19 @@
     /**
      * \return the fec block
      */
-    bvec GetFecBlock();
+    Bvec GetFecBlock();
     /**
      * \return the burst size
      */
-    uint32_t GetBurstSize();
+    uint32_t GetBurstSize() const;
     /**
      * \return true if this fec block is the first one in the burst, false otherwise
      */
-    bool GetIsFirstBlock();
+    bool GetIsFirstBlock() const;
     /**
      * \return the frequency on which the fec block is sent/received
      */
-    uint64_t GetFrequency();
+    uint64_t GetFrequency() const;
     /**
      * \return the modulation type used to send this fec block
      */
@@ -131,18 +131,18 @@
     /**
      * \return the direction on which this fec block was sent. UP or DOWN
      */
-    uint8_t GetDirection();
+    uint8_t GetDirection() const;
     /**
      * \return the Received power
      */
-    double GetRxPowerDbm();
+    double GetRxPowerDbm() const;
     /**
      * \return the received burst
      */
     Ptr<PacketBurst> GetBurst();
 
   private:
-    bvec m_fecBlock;                           ///< FEC block
+    Bvec m_fecBlock;                           ///< FEC block
     uint32_t m_burstSize;                      ///< burst size
     bool m_isFirstBlock;                       ///< is first block
     uint64_t m_frequency;                      ///< frequency
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/simple-ofdm-wimax-channel.cc ns-3.38/src/wimax/model/simple-ofdm-wimax-channel.cc
--- ns-3.37/src/wimax/model/simple-ofdm-wimax-channel.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/simple-ofdm-wimax-channel.cc	2023-03-16 20:54:23.880869400 -0700
@@ -159,7 +159,7 @@
     Ptr<MobilityModel> senderMobility = nullptr;
     Ptr<MobilityModel> receiverMobility = nullptr;
     senderMobility = phy->GetDevice()->GetNode()->GetObject<MobilityModel>();
-    simpleOfdmSendParam* param;
+    SimpleOfdmSendParam* param;
     for (std::list<Ptr<SimpleOfdmWimaxPhy>>::iterator iter = m_phyList.begin();
          iter != m_phyList.end();
          ++iter)
@@ -176,7 +176,7 @@
                 rxPowerDbm = m_loss->CalcRxPower(txPowerDbm, senderMobility, receiverMobility);
             }
 
-            param = new simpleOfdmSendParam(burstSize,
+            param = new SimpleOfdmSendParam(burstSize,
                                             isFirstBlock,
                                             frequency,
                                             modulationType,
@@ -204,7 +204,7 @@
 }
 
 void
-SimpleOfdmWimaxChannel::EndSendDummyBlock(Ptr<SimpleOfdmWimaxPhy> rxphy, simpleOfdmSendParam* param)
+SimpleOfdmWimaxChannel::EndSendDummyBlock(Ptr<SimpleOfdmWimaxPhy> rxphy, SimpleOfdmSendParam* param)
 {
     rxphy->StartReceive(param->GetBurstSize(),
                         param->GetIsFirstBlock(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/simple-ofdm-wimax-channel.h ns-3.38/src/wimax/model/simple-ofdm-wimax-channel.h
--- ns-3.37/src/wimax/model/simple-ofdm-wimax-channel.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/simple-ofdm-wimax-channel.h	2023-03-16 20:54:23.880869400 -0700
@@ -109,7 +109,7 @@
 
   private:
     /**
-     * Attach functiion
+     * Attach function
      * \param phy the phy layer
      */
     void DoAttach(Ptr<WimaxPhy> phy) override;
@@ -122,9 +122,9 @@
     /**
      * End send dummy block function
      * \param rxphy the Ptr<SimpleOfdmWimaxPhy>
-     * \param param the simpleOfdmSendParam *
+     * \param param the SimpleOfdmSendParam *
      */
-    void EndSendDummyBlock(Ptr<SimpleOfdmWimaxPhy> rxphy, simpleOfdmSendParam* param);
+    void EndSendDummyBlock(Ptr<SimpleOfdmWimaxPhy> rxphy, SimpleOfdmSendParam* param);
     /**
      * Get device function
      * \param i the device index
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/simple-ofdm-wimax-phy.cc ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.cc
--- ns-3.37/src/wimax/model/simple-ofdm-wimax-phy.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.cc	2023-03-16 20:54:23.880869400 -0700
@@ -171,10 +171,10 @@
     m_rxGain = 0;
     m_txGain = 0;
     m_nfft = 256;
-    m_g = (double)1 / 4;
+    m_g = 1.0 / 4;
     SetNrCarriers(192);
-    m_fecBlocks = new std::list<bvec>;
-    m_receivedFecBlocks = new std::list<bvec>;
+    m_fecBlocks = new std::list<Bvec>;
+    m_receivedFecBlocks = new std::list<Bvec>;
     m_currentBurstSize = 0;
     m_noiseFigure = 5;      // dB
     m_txPower = 30;         // dBm
@@ -379,7 +379,7 @@
                                  double rxPower,
                                  Ptr<PacketBurst> burst)
 {
-    uint8_t drop = 0;
+    bool drop = false;
     double Nwb = -114 + m_noiseFigure + 10 * std::log(GetBandwidth() / 1000000000.0) / 2.303;
     double SNR = rxPower - Nwb;
 
@@ -394,26 +394,26 @@
 
     if (rand < blockErrorRate)
     {
-        drop = 1;
+        drop = true;
     }
     if (rand > blockErrorRate)
     {
-        drop = 0;
+        drop = false;
     }
 
     if (blockErrorRate == 1.0)
     {
-        drop = 1;
+        drop = true;
     }
     if (blockErrorRate == 0.0)
     {
-        drop = 0;
+        drop = false;
     }
     delete record;
 
     NS_LOG_INFO("PHY: Receive rxPower=" << rxPower << ", Nwb=" << Nwb << ", SNR=" << SNR
                                         << ", Modulation=" << modulationType << ", BlocErrorRate="
-                                        << blockErrorRate << ", drop=" << (int)drop);
+                                        << blockErrorRate << ", drop=" << std::boolalpha << drop);
 
     switch (GetState())
     {
@@ -465,7 +465,7 @@
 SimpleOfdmWimaxPhy::EndReceiveFecBlock(uint32_t burstSize,
                                        WimaxPhy::ModulationType modulationType,
                                        uint8_t direction,
-                                       uint8_t drop,
+                                       bool drop,
                                        Ptr<PacketBurst> burst)
 {
     SetState(PHY_STATE_IDLE);
@@ -500,10 +500,10 @@
     m_traceRx(burst);
 }
 
-bvec
+Bvec
 SimpleOfdmWimaxPhy::ConvertBurstToBits(Ptr<const PacketBurst> burst)
 {
-    bvec buffer(burst->GetSize() * 8, 0);
+    Bvec buffer(burst->GetSize() * 8, 0);
 
     std::list<Ptr<Packet>> packets = burst->GetPackets();
 
@@ -514,7 +514,7 @@
         uint8_t* pstart = (uint8_t*)std::malloc(packet->GetSize());
         std::memset(pstart, 0, packet->GetSize());
         packet->CopyData(pstart, packet->GetSize());
-        bvec temp(8);
+        Bvec temp(8);
         temp.resize(0, 0);
         temp.resize(8, 0);
         for (uint32_t i = 0; i < packet->GetSize(); i++)
@@ -533,21 +533,21 @@
 }
 
 /*
- Converts back the bit buffer (bvec) to the actual burst.
- Actually creates byte buffer from the bvec and resets the buffer
- of each packet in the copy of the orifinal burst stored before transmitting.
+ Converts back the bit buffer (Bvec) to the actual burst.
+ Actually creates byte buffer from the Bvec and resets the buffer
+ of each packet in the copy of the original burst stored before transmitting.
  By doing this it preserves the metadata and tags in the packet.
  Function could also be named DeserializeBurst because actually it
  copying to the burst's byte buffer.
  */
 Ptr<PacketBurst>
-SimpleOfdmWimaxPhy::ConvertBitsToBurst(bvec buffer)
+SimpleOfdmWimaxPhy::ConvertBitsToBurst(Bvec buffer)
 {
     uint8_t init[buffer.size() / 8];
     uint8_t* pstart = init;
     uint8_t temp;
     int32_t j = 0;
-    // recreating byte buffer from bit buffer (bvec)
+    // recreating byte buffer from bit buffer (Bvec)
     for (uint32_t i = 0; i < buffer.size(); i += 8)
     {
         temp = 0;
@@ -592,34 +592,34 @@
 }
 
 void
-SimpleOfdmWimaxPhy::CreateFecBlocks(const bvec& buffer, WimaxPhy::ModulationType modulationType)
+SimpleOfdmWimaxPhy::CreateFecBlocks(const Bvec& buffer, WimaxPhy::ModulationType modulationType)
 {
-    bvec fecBlock(m_blockSize);
+    Bvec fecBlock(m_blockSize);
     for (uint32_t i = 0, j = m_nrBlocks; j > 0; i += m_blockSize, j--)
     {
         if (j == 1 && m_paddingBits > 0) // last block can be smaller than block size
         {
-            fecBlock = bvec(buffer.begin() + i, buffer.end());
+            fecBlock = Bvec(buffer.begin() + i, buffer.end());
             fecBlock.resize(m_blockSize, 0);
         }
         else
         {
-            fecBlock = bvec(buffer.begin() + i, buffer.begin() + i + m_blockSize);
+            fecBlock = Bvec(buffer.begin() + i, buffer.begin() + i + m_blockSize);
         }
 
         m_fecBlocks->push_back(fecBlock);
     }
 }
 
-bvec
+Bvec
 SimpleOfdmWimaxPhy::RecreateBuffer()
 {
-    bvec buffer(m_blockSize * (unsigned long)m_nrBlocks);
-    bvec block(m_blockSize);
+    Bvec buffer(m_blockSize * (unsigned long)m_nrBlocks);
+    Bvec block(m_blockSize);
     uint32_t i = 0;
     for (uint32_t j = 0; j < m_nrBlocks; j++)
     {
-        bvec tmpRecFecBloc = m_receivedFecBlocks->front();
+        Bvec tmpRecFecBloc = m_receivedFecBlocks->front();
         buffer.insert(buffer.begin() + i, tmpRecFecBloc.begin(), tmpRecFecBloc.end());
         m_receivedFecBlocks->pop_front();
         i += m_blockSize;
@@ -648,27 +648,27 @@
     {
     case MODULATION_TYPE_BPSK_12:
         bitsPerSymbol = 1;
-        fecCode = (double)1 / 2;
+        fecCode = 1.0 / 2;
         break;
     case MODULATION_TYPE_QPSK_12:
         bitsPerSymbol = 2;
-        fecCode = (double)1 / 2;
+        fecCode = 1.0 / 2;
         break;
     case MODULATION_TYPE_QPSK_34:
         bitsPerSymbol = 2;
-        fecCode = (double)3 / 4;
+        fecCode = 3.0 / 4;
         break;
     case MODULATION_TYPE_QAM16_12:
         bitsPerSymbol = 4;
-        fecCode = (double)1 / 2;
+        fecCode = 1.0 / 2;
         break;
     case MODULATION_TYPE_QAM16_34:
         bitsPerSymbol = 4;
-        fecCode = (double)3 / 4;
+        fecCode = 3.0 / 4;
         break;
     case MODULATION_TYPE_QAM64_23:
         bitsPerSymbol = 6;
-        fecCode = (double)2 / 3;
+        fecCode = 2.0 / 3;
         break;
     case MODULATION_TYPE_QAM64_34:
         bitsPerSymbol = 6;
@@ -926,7 +926,7 @@
 }
 
 /*
- Retruns number of blocks (FEC blocks) the burst will be split in.
+ Returns number of blocks (FEC blocks) the burst will be split in.
  The size of the block is specific for each modulation type.
  */
 uint16_t
@@ -954,20 +954,20 @@
      frequency is 23040000, symbol (OFDM symbol) duration is 1.388888888888889e-05 seconds, PS
      duration is 1.7361111111111112e-07 seconds. Hence PSs per frame is 57600, symbols per frame is
      720 and PSs per symbol is 80. Note that defining these parameters (symbol and PS duration) as
-     Time may not result in exaclty these values therefore lrint has been used (otherwise should be
+     Time may not result in exactly these values therefore lrint has been used (otherwise should be
      defined as double). For licensed bands set channel bandwidth according to Table B.26, page
      810.*/
 
     double samplingFrequency = DoGetSamplingFrequency();
-    Time psDuration = Seconds((double)4 / samplingFrequency);
+    Time psDuration = Seconds(4.0 / samplingFrequency);
 
     SetPsDuration(psDuration);
     uint16_t psPerFrame = (uint16_t)(GetFrameDuration().GetSeconds() / psDuration.GetSeconds());
     SetPsPerFrame(psPerFrame);
     double subcarrierSpacing = samplingFrequency / DoGetNfft();
-    double tb = (double)1 / subcarrierSpacing; // Tb (useful symbol time)
-    double tg = DoGetGValue() * tb;            // Tg (cyclic prefix time)
-    Time symbolDuration = Seconds(tb + tg);    // OFDM Symbol Time
+    double tb = 1.0 / subcarrierSpacing;    // Tb (useful symbol time)
+    double tg = DoGetGValue() * tb;         // Tg (cyclic prefix time)
+    Time symbolDuration = Seconds(tb + tg); // OFDM Symbol Time
     SetSymbolDuration(symbolDuration);
     uint16_t psPerSymbol = lrint(symbolDuration.GetSeconds() / psDuration.GetSeconds());
     SetPsPerSymbol(psPerSymbol);
@@ -996,23 +996,23 @@
 
     if (channelBandwidth % 1750000 == 0)
     {
-        return (double)8 / 7;
+        return 8.0 / 7;
     }
     else if (channelBandwidth % 1500000 == 0)
     {
-        return (double)86 / 75;
+        return 86.0 / 75;
     }
     else if (channelBandwidth % 1250000 == 0)
     {
-        return (double)144 / 125;
+        return 144.0 / 125;
     }
     else if (channelBandwidth % 2750000 == 0)
     {
-        return (double)316 / 275;
+        return 316.0 / 275;
     }
     else if (channelBandwidth % 2000000 == 0)
     {
-        return (double)57 / 50;
+        return 57.0 / 50;
     }
     else
     {
@@ -1020,7 +1020,7 @@
         NS_FATAL_ERROR("wrong channel bandwidth for OFDM PHY");
     }
 
-    return (double)8 / 7;
+    return 8.0 / 7;
 }
 
 double
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/simple-ofdm-wimax-phy.h ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.h
--- ns-3.37/src/wimax/model/simple-ofdm-wimax-phy.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.h	2023-03-16 20:54:23.880869400 -0700
@@ -241,24 +241,24 @@
      * \param burst the packet burst
      * \returns the BVEC
      */
-    bvec ConvertBurstToBits(Ptr<const PacketBurst> burst);
+    Bvec ConvertBurstToBits(Ptr<const PacketBurst> burst);
     /**
      * Convert bits to burst
      * \param buffer the BVEC
      * \returns the packet burst
      */
-    Ptr<PacketBurst> ConvertBitsToBurst(bvec buffer);
+    Ptr<PacketBurst> ConvertBitsToBurst(Bvec buffer);
     /**
      * Create FEC blocks
      * \param buffer the BVEC
      * \param modulationType the modulation type
      */
-    void CreateFecBlocks(const bvec& buffer, WimaxPhy::ModulationType modulationType);
+    void CreateFecBlocks(const Bvec& buffer, WimaxPhy::ModulationType modulationType);
     /**
      * Recreate buffer
      * \returns BVEC
      */
-    bvec RecreateBuffer();
+    Bvec RecreateBuffer();
     /**
      * Get FEC block size
      * \param type the modulation type
@@ -303,13 +303,13 @@
      * \param burstSize the burst size
      * \param modulationType the modulation type
      * \param direction the direction
-     * \param drop the drop
+     * \param drop whether to drop
      * \param burst the burst
      */
     void EndReceiveFecBlock(uint32_t burstSize,
                             WimaxPhy::ModulationType modulationType,
                             uint8_t direction,
-                            uint8_t drop,
+                            bool drop,
                             Ptr<PacketBurst> burst);
     /**
      * Start end dummy FEC block
@@ -442,10 +442,10 @@
     uint16_t m_fecBlockSize;     ///< in bits, size of FEC block transmitted after PHY operations
     uint32_t m_currentBurstSize; ///< current burst size
 
-    std::list<bvec>* m_receivedFecBlocks; ///< a list of received FEC blocks until they are combined
+    std::list<Bvec>* m_receivedFecBlocks; ///< a list of received FEC blocks until they are combined
                                           ///< to recreate the full burst buffer
     uint32_t m_nrFecBlocksSent;   ///< counting the number of FEC blocks sent (within a burst)
-    std::list<bvec>* m_fecBlocks; ///< the FEC blocks
+    std::list<Bvec>* m_fecBlocks; ///< the FEC blocks
     Time m_blockTime;             ///< block time
 
     TracedCallback<Ptr<const PacketBurst>> m_traceRx; ///< trace receive callback
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/snr-to-block-error-rate-manager.h ns-3.38/src/wimax/model/snr-to-block-error-rate-manager.h
--- ns-3.37/src/wimax/model/snr-to-block-error-rate-manager.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/snr-to-block-error-rate-manager.h	2023-03-16 20:54:23.880869400 -0700
@@ -106,7 +106,7 @@
   private:
     /// Clear records function
     void ClearRecords();
-    uint8_t m_activateLoss;      ///< activate loss
+    bool m_activateLoss;         ///< activate loss
     std::string m_traceFilePath; ///< trace file path
 
     std::vector<SNRToBlockErrorRateRecord*>* m_recordModulation[7]; ///< record modulation
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/snr-to-block-error-rate-record.cc ns-3.38/src/wimax/model/snr-to-block-error-rate-record.cc
--- ns-3.37/src/wimax/model/snr-to-block-error-rate-record.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/snr-to-block-error-rate-record.cc	2023-03-16 20:54:23.880869400 -0700
@@ -42,7 +42,7 @@
 }
 
 SNRToBlockErrorRateRecord*
-SNRToBlockErrorRateRecord::Copy()
+SNRToBlockErrorRateRecord::Copy() const
 {
     return (new SNRToBlockErrorRateRecord(m_snrValue,
                                           m_bitErrorRate,
@@ -53,7 +53,7 @@
 }
 
 double
-SNRToBlockErrorRateRecord::GetSNRValue()
+SNRToBlockErrorRateRecord::GetSNRValue() const
 {
     return m_snrValue;
 }
@@ -69,31 +69,31 @@
 }
 
 double
-SNRToBlockErrorRateRecord::GetBitErrorRate()
+SNRToBlockErrorRateRecord::GetBitErrorRate() const
 {
     return m_bitErrorRate;
 }
 
 double
-SNRToBlockErrorRateRecord::GetBlockErrorRate()
+SNRToBlockErrorRateRecord::GetBlockErrorRate() const
 {
     return m_blockErrorRate;
 }
 
 double
-SNRToBlockErrorRateRecord::GetSigma2()
+SNRToBlockErrorRateRecord::GetSigma2() const
 {
     return m_sigma2;
 }
 
 double
-SNRToBlockErrorRateRecord::GetI1()
+SNRToBlockErrorRateRecord::GetI1() const
 {
     return m_i1;
 }
 
 double
-SNRToBlockErrorRateRecord::GetI2()
+SNRToBlockErrorRateRecord::GetI2() const
 {
     return m_i2;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/snr-to-block-error-rate-record.h ns-3.38/src/wimax/model/snr-to-block-error-rate-record.h
--- ns-3.37/src/wimax/model/snr-to-block-error-rate-record.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/snr-to-block-error-rate-record.h	2023-03-16 20:54:23.880869400 -0700
@@ -54,32 +54,32 @@
     /**
      * \return the SNR value
      */
-    double GetSNRValue();
+    double GetSNRValue() const;
     /**
      * \return the BER value
      */
-    double GetBitErrorRate();
+    double GetBitErrorRate() const;
     /**
      * \return the BlcER value
      */
-    double GetBlockErrorRate();
+    double GetBlockErrorRate() const;
     /**
      * \return the standard deviation
      */
-    double GetSigma2();
+    double GetSigma2() const;
     /**
      * \return the lower boundary of the confidence interval
      */
-    double GetI1();
+    double GetI1() const;
     /**
      * \return the upper boundary of the confidence interval
      */
-    double GetI2();
+    double GetI2() const;
     /**
      * \brief copy a record
      * \return a copy of this record
      */
-    SNRToBlockErrorRateRecord* Copy();
+    SNRToBlockErrorRateRecord* Copy() const;
 
     /**
      * \brief set the snr value
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ss-link-manager.cc ns-3.38/src/wimax/model/ss-link-manager.cc
--- ns-3.37/src/wimax/model/ss-link-manager.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ss-link-manager.cc	2023-03-16 20:54:23.880869400 -0700
@@ -337,7 +337,7 @@
 SSLinkManager::ResetRangingRequestCW()
 {
     m_rangingCW =
-        (uint8_t)std::pow((double)2, (double)m_ss->GetCurrentUcd().GetRangingBackoffStart()) - 1;
+        (uint8_t)std::pow(2.0, (double)m_ss->GetCurrentUcd().GetRangingBackoffStart()) - 1;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ss-link-manager.h ns-3.38/src/wimax/model/ss-link-manager.h
--- ns-3.37/src/wimax/model/ss-link-manager.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ss-link-manager.h	2023-03-16 20:54:23.880869400 -0700
@@ -78,7 +78,7 @@
      */
     bool GetRangingIntervalFound() const;
     /**
-     * Ser NR ranging trans opps
+     * Set NR ranging trans opps
      * \param nrRangingTransOpps the NR ranging trans opps
      */
     void SetNrRangingTransOpps(uint8_t nrRangingTransOpps);
@@ -90,7 +90,7 @@
     /// Increment NR invited polls received
     void IncrementNrInvitedPollsRecvd();
     /**
-     * Get DL map sync timeput event
+     * Get DL map sync timeout event
      * \returns the event ID
      */
     EventId GetDlMapSyncTimeoutEvent();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ss-net-device.cc ns-3.38/src/wimax/model/ss-net-device.cc
--- ns-3.37/src/wimax/model/ss-net-device.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ss-net-device.cc	2023-03-16 20:54:23.880869400 -0700
@@ -581,7 +581,7 @@
 bool
 SubscriberStationNetDevice::HasServiceFlows() const
 {
-    return GetServiceFlowManager()->GetServiceFlows(ServiceFlow::SF_TYPE_ALL).size() > 0;
+    return !GetServiceFlowManager()->GetServiceFlows(ServiceFlow::SF_TYPE_ALL).empty();
 }
 
 Ptr<IpcsClassifier>
@@ -640,13 +640,13 @@
 }
 
 void
-SubscriberStationNetDevice::AddServiceFlow(ServiceFlow sf)
+SubscriberStationNetDevice::AddServiceFlow(ServiceFlow sf) const
 {
     GetServiceFlowManager()->AddServiceFlow(sf);
 }
 
 void
-SubscriberStationNetDevice::AddServiceFlow(ServiceFlow* sf)
+SubscriberStationNetDevice::AddServiceFlow(ServiceFlow* sf) const
 {
     GetServiceFlowManager()->AddServiceFlow(sf);
 }
@@ -1313,7 +1313,7 @@
         return; // nothing new in UCD so don't read
     }
     SetCurrentUcd(ucd);
-    m_linkManager->SetRangingCW((uint8_t)std::pow((double)2, (double)ucd.GetRangingBackoffStart()) -
+    m_linkManager->SetRangingCW((uint8_t)std::pow(2.0, (double)ucd.GetRangingBackoffStart()) -
                                 1); // initializing ranging CW
     OfdmUcdChannelEncodings ucdChnlEncodings = ucd.GetChannelEncodings();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ss-net-device.h ns-3.38/src/wimax/model/ss-net-device.h
--- ns-3.37/src/wimax/model/ss-net-device.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ss-net-device.h	2023-03-16 20:54:23.880869400 -0700
@@ -297,12 +297,12 @@
      * \brief adds a new service flow
      * \param sf pointer to service flow to add
      */
-    void AddServiceFlow(ServiceFlow* sf);
+    void AddServiceFlow(ServiceFlow* sf) const;
     /**
      * \brief adds a new service flow
      * \param sf service flow to add
      */
-    void AddServiceFlow(ServiceFlow sf);
+    void AddServiceFlow(ServiceFlow sf) const;
     /**
      * \brief Set timer.  If in stopped state, the EventId passed in the first
      * argument will be cancelled.  If not, the second parameter will be set
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ss-record.cc ns-3.38/src/wimax/model/ss-record.cc
--- ns-3.37/src/wimax/model/ss-record.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ss-record.cc	2023-03-16 20:54:23.880869400 -0700
@@ -253,7 +253,7 @@
 }
 
 bool
-SSRecord::GetIsBroadcastSS()
+SSRecord::GetIsBroadcastSS() const
 {
     return m_broadcast;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ss-record.h ns-3.38/src/wimax/model/ss-record.h
--- ns-3.37/src/wimax/model/ss-record.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ss-record.h	2023-03-16 20:54:23.880869400 -0700
@@ -237,7 +237,7 @@
      * Get is broadcast SS
      * \returns the is broadcast SS flag
      */
-    bool GetIsBroadcastSS();
+    bool GetIsBroadcastSS() const;
 
     /**
      * Get IP address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ss-service-flow-manager.h ns-3.38/src/wimax/model/ss-service-flow-manager.h
--- ns-3.37/src/wimax/model/ss-service-flow-manager.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ss-service-flow-manager.h	2023-03-16 20:54:23.880869400 -0700
@@ -96,7 +96,7 @@
     EventId GetDsaRspTimeoutEvent() const;
     /**
      * Get DSA ack timeout event
-     * \returns the DSA ack timeput event
+     * \returns the DSA ack timeout event
      */
     EventId GetDsaAckTimeoutEvent() const;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ul-job.cc ns-3.38/src/wimax/model/ul-job.cc
--- ns-3.37/src/wimax/model/ul-job.cc	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ul-job.cc	2023-03-16 20:54:23.880869400 -0700
@@ -120,7 +120,7 @@
 }
 
 uint32_t
-UlJob::GetSize()
+UlJob::GetSize() const
 {
     return m_size;
 }
@@ -155,7 +155,7 @@
 }
 
 int
-PriorityUlJob::GetPriority()
+PriorityUlJob::GetPriority() const
 {
     return m_priority;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/ul-job.h ns-3.38/src/wimax/model/ul-job.h
--- ns-3.37/src/wimax/model/ul-job.h	2022-11-01 17:37:24.767396000 -0700
+++ ns-3.38/src/wimax/model/ul-job.h	2023-03-16 20:54:23.880869400 -0700
@@ -138,7 +138,7 @@
      * Get size
      * \returns the size
      */
-    uint32_t GetSize();
+    uint32_t GetSize() const;
     /**
      * Set size
      * \param size the size
@@ -176,7 +176,7 @@
      * Get priority
      * \returns the priority
      */
-    int GetPriority();
+    int GetPriority() const;
     /**
      * Set priority
      * \param priority the priority
@@ -184,7 +184,7 @@
     void SetPriority(int priority);
 
     /**
-     * Get UL job functiion
+     * Get UL job function
      * \returns the UL job
      */
     Ptr<UlJob> GetUlJob();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/wimax-tlv.cc ns-3.38/src/wimax/model/wimax-tlv.cc
--- ns-3.37/src/wimax/model/wimax-tlv.cc	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/src/wimax/model/wimax-tlv.cc	2023-03-16 20:54:23.880869400 -0700
@@ -914,7 +914,7 @@
 void
 PortRangeTlvValue::Add(uint16_t portLow, uint16_t portHigh)
 {
-    struct PortRange tmp;
+    PortRange tmp;
     tmp.PortLow = portLow;
     tmp.PortHigh = portHigh;
     m_portRange->push_back(tmp);
@@ -1024,7 +1024,7 @@
 
 Ipv4AddressTlvValue::Ipv4AddressTlvValue()
 {
-    m_ipv4Addr = new std::vector<struct ipv4Addr>;
+    m_ipv4Addr = new std::vector<Ipv4Addr>;
 }
 
 Ipv4AddressTlvValue::~Ipv4AddressTlvValue()
@@ -1046,7 +1046,7 @@
 void
 Ipv4AddressTlvValue::Serialize(Buffer::Iterator i) const
 {
-    for (std::vector<struct ipv4Addr>::const_iterator iter = m_ipv4Addr->begin();
+    for (std::vector<Ipv4Addr>::const_iterator iter = m_ipv4Addr->begin();
          iter != m_ipv4Addr->end();
          ++iter)
     {
@@ -1070,12 +1070,9 @@
 }
 
 void
-Ipv4AddressTlvValue::Add(Ipv4Address address, Ipv4Mask Mask)
+Ipv4AddressTlvValue::Add(Ipv4Address address, Ipv4Mask mask)
 {
-    struct ipv4Addr tmp;
-    tmp.Address = address;
-    tmp.Mask = Mask;
-    m_ipv4Addr->push_back(tmp);
+    m_ipv4Addr->push_back({address, mask});
 }
 
 Ipv4AddressTlvValue::Iterator
@@ -1094,7 +1091,7 @@
 Ipv4AddressTlvValue::Copy() const
 {
     Ipv4AddressTlvValue* tmp = new Ipv4AddressTlvValue();
-    for (std::vector<struct ipv4Addr>::const_iterator iter = m_ipv4Addr->begin();
+    for (std::vector<Ipv4Addr>::const_iterator iter = m_ipv4Addr->begin();
          iter != m_ipv4Addr->end();
          ++iter)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/src/wimax/model/wimax-tlv.h ns-3.38/src/wimax/model/wimax-tlv.h
--- ns-3.37/src/wimax/model/wimax-tlv.h	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/src/wimax/model/wimax-tlv.h	2023-03-16 20:54:23.880869400 -0700
@@ -571,14 +571,15 @@
 class Ipv4AddressTlvValue : public TlvValue
 {
   public:
-    /// ipv4Addr structure
-    struct ipv4Addr
+    /// Ipv4Addr structure
+    struct Ipv4Addr
     {
         Ipv4Address Address; ///< address
         Ipv4Mask Mask;       ///< mask
     };
+
     /// IPv4 address vector iterator typedef
-    typedef std::vector<struct ipv4Addr>::const_iterator Iterator;
+    typedef std::vector<Ipv4Addr>::const_iterator Iterator;
     Ipv4AddressTlvValue();
     ~Ipv4AddressTlvValue() override;
     uint32_t GetSerializedSize() const override;
@@ -587,9 +588,9 @@
     /**
      * Add IPv4 address and mask
      * \param address the IPv4 address
-     * \param Mask the IPv4 mask
+     * \param mask the IPv4 mask
      */
-    void Add(Ipv4Address address, Ipv4Mask Mask);
+    void Add(Ipv4Address address, Ipv4Mask mask);
     /**
      * Begin iterator
      * \returns the beginning element
@@ -603,7 +604,7 @@
     Ipv4AddressTlvValue* Copy() const override;
 
   private:
-    std::vector<struct ipv4Addr>* m_ipv4Addr; ///< ipv4 addr
+    std::vector<Ipv4Addr>* m_ipv4Addr; ///< ipv4 addr
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/test.py ns-3.38/test.py
--- ns-3.37/test.py	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/test.py	2023-03-16 20:54:23.880869400 -0700
@@ -754,7 +754,7 @@
 #     ...
 #   }
 #
-# You need to add a supression name which will only be printed out by valgrind in
+# You need to add a suppression name which will only be printed out by valgrind in
 # verbose mode (but it needs to be there in any case).  The entire stack frame is
 # shown to completely characterize the error, but in most cases you won't need
 # all of that info.  For example, if you want to turn off all errors that happen
@@ -810,18 +810,28 @@
     elapsed_time = time.time() - start_time
 
     retval = proc.returncode
-    try:
-        stdout_results = stdout_results.decode()
-    except UnicodeDecodeError:
-        print("Non-decodable character in stdout output of %s" % cmd)
-        print(stdout_results)
-        retval = 1
-    try:
-        stderr_results = stderr_results.decode()
-    except UnicodeDecodeError:
-        print("Non-decodable character in stderr output of %s" % cmd)
-        print(stderr_results)
-        retval = 1
+
+    def decode_stream_results(stream_results: bytes, stream_name: str) -> str:
+        try:
+            stream_results = stream_results.decode()
+        except UnicodeDecodeError:
+            def decode(byte_array: bytes):
+                try:
+                    byte_array.decode()
+                except UnicodeDecodeError:
+                    return byte_array
+
+            # Find lines where the decoding error happened
+            non_utf8_lines = list(map(lambda line: decode(line), stream_results.splitlines()))
+            non_utf8_lines = list(filter(lambda line: line is not None, non_utf8_lines))
+            print(f"Non-decodable characters found in {stream_name} output of {cmd}: {non_utf8_lines}")
+
+            # Continue decoding on errors
+            stream_results = stream_results.decode(errors="backslashreplace")
+        return stream_results
+
+    stdout_results = decode_stream_results(stdout_results, "stdout")
+    stderr_results = decode_stream_results(stderr_results, "stderr")
 
     if options.verbose:
         print("Return code = ", retval)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/bench-scheduler.cc ns-3.38/utils/bench-scheduler.cc
--- ns-3.37/utils/bench-scheduler.cc	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/bench-scheduler.cc	2023-03-16 20:54:23.880869400 -0700
@@ -371,12 +371,14 @@
 #undef ACCUMULATE
     }
 
-    auto stdev = Result{{std::sqrt(moment2.init.time / n),
-                         std::sqrt(moment2.init.rate / n),
-                         std::sqrt(moment2.init.period / n)},
-                        {std::sqrt(moment2.run.time / n),
-                         std::sqrt(moment2.run.rate / n),
-                         std::sqrt(moment2.run.period / n)}};
+    auto stdev = Result{
+        {std::sqrt(moment2.init.time / n),
+         std::sqrt(moment2.init.rate / n),
+         std::sqrt(moment2.init.period / n)},
+        {std::sqrt(moment2.run.time / n),
+         std::sqrt(moment2.run.rate / n),
+         std::sqrt(moment2.run.period / n)},
+    };
 
     average.Log("average");
     stdev.Log("stdev");
@@ -401,7 +403,7 @@
 {
     Ptr<RandomVariableStream> stream = nullptr;
 
-    if (filename == "")
+    if (filename.empty())
     {
         LOG("  Event time distribution:      default exponential");
         auto erv = CreateObject<ExponentialRandomVariable>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/ccache-miss-rate.py ns-3.38/utils/ccache-miss-rate.py
--- ns-3.37/utils/ccache-miss-rate.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/utils/ccache-miss-rate.py	2023-03-16 20:54:23.880869400 -0700
@@ -0,0 +1,8 @@
+#!/usr/bin/env python3
+
+import re
+import subprocess
+
+ccache_misses_line = subprocess.check_output(["ccache", "--print-stats"]).decode()
+ccache_misses = int(re.findall("cache_miss(.*)", ccache_misses_line)[0])
+print(ccache_misses)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/codespell-ignored-lines ns-3.38/utils/codespell-ignored-lines
--- ns-3.37/utils/codespell-ignored-lines	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/utils/codespell-ignored-lines	2023-03-16 20:54:23.880869400 -0700
@@ -0,0 +1,46 @@
+#lollipop-counter.h: 341, 343
+        LollipopCounter ans = val;
+        return ans;
+
+#arp-header.cc: 202
+    ReadFrom(i, m_macDest, hardwareAddressLen);   // Read THA (size HLN)
+
+#csma-net-device.cc: 351-352
+            Ptr<Packet> padd = Create<Packet>(buffer, 46 - p->GetSize());
+            p->AddAtEnd(padd);
+
+#epc-gtpu-header.h: 236
+     * This field is used to co-ordinate the data transmission for acknowledged mode
+
+#mobility-model.cc:100, 102
+    Vector oPosition = other->DoGetPosition();
+    return CalculateDistance(position, oPosition);
+
+#statistics.rst:299-300
+    read ANS
+    if [ "$ANS" = "yes" -o "$ANS" = "y" ]
+
+#wifi-example-db.sh:34-35
+  read ANS
+  if [ "$ANS" = "yes" -o "$ANS" = "y" ]
+
+#doxygen.warnings.report.sh:483, 485, 544, 549, 556, 561
+filterin=
+    filterin=$(              \
+#     if [ "$filterin" != "" ] ; then
+#         echo "$filterin"
+if [ "$filterin" != "" ] ; then
+    echo "$filterin"
+
+#./src/visualizer/visualizer/hud.py:93-94
+            tbe = math.log10(dx_over_ndiv)#;   /* looking for approx. 'ndiv' divisions in a length 'dx' */
+            div = pow(10, rint(tbe))#;         /* div: power of 10 closest to dx/ndiv */
+
+#./utils/tests/test-ns3.py:2944
+            'convert {image} -colorspace HSI -channel b -separate +channel -scale 1x1 -format "%[fx:100*u]" info:'
+
+#./doc/tutorial/source/introduction.rst:80
+Network Animator nam.  It is not possible to run a simulation
+
+#./src/internet-apps/model/v4ping.h:37
+ * \brief an application which sends one ICMP ECHO request, waits for a REPLYs
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/codespell-ignored-words ns-3.38/utils/codespell-ignored-words
--- ns-3.37/utils/codespell-ignored-words	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.38/utils/codespell-ignored-words	2023-03-16 20:54:23.880869400 -0700
@@ -0,0 +1,72 @@
+#Core
+anid
+anumber
+bu
+mone
+peripherial
+studi
+thre
+
+#Network
+ot
+nd
+
+#Internet
+devicec
+ect
+iif
+nce
+inflight
+
+#Internet Apps
+clen
+
+#Buildings
+nin
+
+#DSR
+cleanp
+
+#LTE
+commun
+couting
+ue
+ues
+
+#Netanim
+writen
+
+#Spectrum
+inh
+
+#Wave
+aci
+ois
+
+#Wifi
+fo
+
+#Traffic Control
+taht
+
+#Wimax
+opps
+
+#Propagation
+alos
+clos
+
+#Examples
+clienta
+
+#Brite
+brite
+
+#Mesh
+inport
+
+#Visualizer
+inout
+
+#Utils
+filetest
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/create-module.py ns-3.38/utils/create-module.py
--- ns-3.37/utils/create-module.py	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/create-module.py	2023-03-16 20:54:23.880869400 -0700
@@ -50,9 +50,18 @@
 #ifndef {INCLUDE_GUARD}
 #define {INCLUDE_GUARD}
 
+// Add a doxygen group for this module.
+// If you have more than one file, this should be in only one of them.
+/**
+ * \defgroup {MODULE} Description of the {MODULE}
+ */
+
 namespace ns3
 {{
 
+// Each class should be documented using Doxygen,
+// and have an \ingroup {MODULE} directive
+
 /* ... */
 
 }}
@@ -82,6 +91,9 @@
 namespace ns3
 {{
 
+// Each class should be documented using Doxygen,
+// and have an \ingroup {MODULE} directive
+
 /* ... */
 
 }}
@@ -103,6 +115,12 @@
 #include "ns3/core-module.h"
 #include "ns3/{MODULE}-helper.h"
 
+/**
+ * \\file
+ *
+ * Explain here what the example does.
+ */
+
 using namespace ns3;
 
 int
@@ -136,7 +154,19 @@
 // to use the using directive to access the ns3 namespace directly
 using namespace ns3;
 
+// Add a doxygen group for tests.
+// If you have more than one test, this should be in only one of them.
+/**
+ * \defgroup {MODULE}-tests Tests for {MODULE}
+ * \ingroup {MODULE}
+ * \ingroup tests
+ */
+
 // This is an example TestCase.
+/**
+ * \ingroup {MODULE}-tests
+ * Test case for feature 1
+ */
 class {CAPITALIZED}TestCase1 : public TestCase
 {{
   public:
@@ -175,7 +205,11 @@
 // The TestSuite class names the TestSuite, identifies what type of TestSuite,
 // and enables the TestCases to be run.  Typically, only the constructor for
 // this class must be defined
-//
+
+/**
+ * \ingroup {MODULE}-tests
+ * TestSuite for module {MODULE}
+ */
 class {CAPITALIZED}TestSuite : public TestSuite
 {{
   public:
@@ -190,6 +224,10 @@
 }}
 
 // Do not forget to allocate an instance of this TestSuite
+/**
+ * \ingroup {MODULE}-tests
+ * Static variable for test initialization
+ */
 static {CAPITALIZED}TestSuite s{COMPOUND}TestSuite;
 '''
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/print-introspected-doxygen.cc ns-3.38/utils/print-introspected-doxygen.cc
--- ns-3.37/utils/print-introspected-doxygen.cc	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/print-introspected-doxygen.cc	2023-03-16 20:54:23.884869300 -0700
@@ -40,6 +40,7 @@
 #include <iomanip>
 #include <iostream>
 #include <map>
+#include <utility> // as_const
 
 using namespace ns3;
 
@@ -96,14 +97,48 @@
 std::string templArgExplicit; ///< template argument required
 std::string templateArgument; ///< template argument
 std::string variable;         ///< variable or class member
-                              /** @} */
+
+/** @} */
+
+/**
+ * Alphabetize the AttributeInformation for a TypeId by the Attribute name
+ * \param tid The TypeId to process.
+ * \return The ordered list of Attributes.
+ */
+std::map<std::string, ns3::TypeId::AttributeInformation>
+SortedAttributeInfo(const TypeId tid)
+{
+    std::map<std::string, ns3::TypeId::AttributeInformation> index;
+    for (uint32_t j = 0; j < tid.GetAttributeN(); j++)
+    {
+        struct TypeId::AttributeInformation info = tid.GetAttribute(j);
+        index[info.name] = info;
+    }
+    return index;
+}
+
+/**
+ * Alphabetize the TraceSourceInformation for a TypeId by the
+ * TraceSource name.
+ * \param tid The TypeId to process.
+ * \return The ordered list of TraceSourceInformation
+ */
+std::map<std::string, ns3::TypeId::TraceSourceInformation>
+SortedTraceSourceInfo(const TypeId tid)
+{
+    std::map<std::string, ns3::TypeId::TraceSourceInformation> index;
+    for (uint32_t j = 0; j < tid.GetTraceSourceN(); j++)
+    {
+        struct TypeId::TraceSourceInformation info = tid.GetTraceSource(j);
+        index[info.name] = info;
+    }
+    return index;
+}
 
 } // unnamed namespace
 
 /**
  * Initialize the markup strings, for either doxygen or text.
- *
- * \param [in] outputText true for text output, false for doxygen output.
  */
 void
 SetMarkup()
@@ -664,11 +699,13 @@
 PrintAttributesTid(std::ostream& os, const TypeId tid)
 {
     NS_LOG_FUNCTION(tid);
+
+    auto index = SortedAttributeInfo(tid);
+
     os << listStart << std::endl;
-    for (uint32_t j = 0; j < tid.GetAttributeN(); j++)
+    for (const auto& [name, info] : index)
     {
-        struct TypeId::AttributeInformation info = tid.GetAttribute(j);
-        os << listLineStart << boldStart << info.name << boldStop << ": " << info.help << std::endl;
+        os << listLineStart << boldStart << name << boldStop << ": " << info.help << std::endl;
         os << indentHtmlOnly << listStart << std::endl;
         os << "    " << listLineStart << "Set with class: " << reference
            << info.checker->GetValueTypeName() << listLineStop << std::endl;
@@ -708,24 +745,10 @@
                 }
 
                 // Helper to match first part of string
-                class StringBeginMatcher
-                {
-                  public:
-                    StringBeginMatcher(const std::string s)
-                        : m_string(s){};
-
-                    bool operator()(const std::string t)
-                    {
-                        std::size_t pos = m_string.find(t);
-                        return pos == 0;
-                    };
-
-                  private:
-                    std::string m_string;
+                auto match = [&uType = std::as_const(underType)](const std::string& s) {
+                    return uType.rfind(s, 0) == 0; // only checks position 0
                 };
 
-                StringBeginMatcher match(underType);
-
                 if (match("bool") || match("double") || match("int8_t") || match("uint8_t") ||
                     match("int16_t") || match("uint16_t") || match("int32_t") ||
                     match("uint32_t") || match("int64_t") || match("uint64_t"))
@@ -743,7 +766,7 @@
         if (info.flags & TypeId::ATTR_CONSTRUCT && info.accessor->HasSetter())
         {
             std::string value = info.initialValue->SerializeToString(info.checker);
-            if (underType == "std::string" && value == "")
+            if (underType == "std::string" && value.empty())
             {
                 value = "\"\"";
             }
@@ -824,11 +847,13 @@
 PrintTraceSourcesTid(std::ostream& os, const TypeId tid)
 {
     NS_LOG_FUNCTION(tid);
+
+    auto index = SortedTraceSourceInfo(tid);
+
     os << listStart << std::endl;
-    for (uint32_t i = 0; i < tid.GetTraceSourceN(); ++i)
+    for (const auto& [name, info] : index)
     {
-        struct TypeId::TraceSourceInformation info = tid.GetTraceSource(i);
-        os << listLineStart << boldStart << info.name << boldStop << ": " << info.help << breakBoth;
+        os << listLineStart << boldStart << name << boldStop << ": " << info.help << breakBoth;
         if (!outputText)
         {
             //    '%' prevents doxygen from linking to the Callback class...
@@ -1016,13 +1041,14 @@
         {
             continue;
         }
-        os << boldStart << tid.GetName() << boldStop << breakHtmlOnly << std::endl;
 
+        auto index = SortedAttributeInfo(tid);
+
+        os << boldStart << tid.GetName() << boldStop << breakHtmlOnly << std::endl;
         os << listStart << std::endl;
-        for (uint32_t j = 0; j < tid.GetAttributeN(); ++j)
+        for (const auto& [name, info] : index)
         {
-            struct TypeId::AttributeInformation info = tid.GetAttribute(j);
-            os << listLineStart << boldStart << info.name << boldStop << ": " << info.help
+            os << listLineStart << boldStart << name << boldStop << ": " << info.help
                << listLineStop << std::endl;
         }
         os << listStop << std::endl;
@@ -1156,13 +1182,15 @@
         {
             continue;
         }
+
+        auto index = SortedTraceSourceInfo(tid);
+
         os << boldStart << tid.GetName() << boldStop << breakHtmlOnly << std::endl;
 
         os << listStart << std::endl;
-        for (uint32_t j = 0; j < tid.GetTraceSourceN(); ++j)
+        for (const auto& [name, info] : index)
         {
-            struct TypeId::TraceSourceInformation info = tid.GetTraceSource(j);
-            os << listLineStart << boldStart << info.name << boldStop << ": " << info.help
+            os << listLineStart << boldStart << name << boldStop << ": " << info.help
                << listLineStop << std::endl;
         }
         os << listStop << std::endl;
@@ -1359,13 +1387,13 @@
 } // PrintMakeChecker ()
 
 /**Descriptor for an AttributeValue. */
-typedef struct
+struct AttributeDescriptor
 {
     const std::string m_name;   //!< The base name of the resulting AttributeValue type.
     const std::string m_type;   //!< The name of the underlying type.
     const bool m_seeBase;       //!< Print a "see also" pointing to the base class.
     const std::string m_header; //!< The header file name.
-} AttributeDescriptor;
+};
 
 /**
  * Print documentation corresponding to use of the
@@ -1444,7 +1472,7 @@
     // clang-format on
 
     int i = 0;
-    while (attributes[i].m_name != "")
+    while (!attributes[i].m_name.empty())
     {
         PrintAttributeHelper(os, attributes[i]);
         ++i;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/python-unit-tests.py ns-3.38/utils/python-unit-tests.py
--- ns-3.37/utils/python-unit-tests.py	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/python-unit-tests.py	2023-03-16 20:54:23.884869300 -0700
@@ -39,16 +39,17 @@
     def testScheduleNow(self):
         """! Test schedule now
         @param self this object
-        @return none
+        @return None
         """
+
         def callback(args: ns.cppyy.gbl.std.vector) -> None:
             """! Callback function
             @param args arguments
-            return none
+            @return None
             """
-            import copy
             self._args_received = list(map(lambda x: x.decode("utf-8"), args))
             self._cb_time = ns.Simulator.Now()
+
         ns.Simulator.Destroy()
         self._args_received = None
         self._cb_time = None
@@ -67,15 +68,17 @@
     def testSchedule(self):
         """! Test schedule
         @param self this object
-        @return none
+        @return None
         """
+
         def callback(args: ns.cppyy.gbl.std.vector):
             """! Callback function
             @param args arguments
-            @return none
+            @return None
             """
             self._args_received = list(map(lambda x: x.decode("utf-8"), args))
             self._cb_time = ns.Simulator.Now()
+
         ns.Simulator.Destroy()
         self._args_received = None
         self._cb_time = None
@@ -94,15 +97,17 @@
     def testScheduleDestroy(self):
         """! Test schedule destroy
         @param self this object
-        @return none
+        @return None
         """
+
         def callback(args: ns.cppyy.gbl.std.vector):
             """! Callback function
             @param args
-            @return none
+            @return None
             """
             self._args_received = list(map(lambda x: x.decode("utf-8"), args))
             self._cb_time = ns.Simulator.Now()
+
         ns.Simulator.Destroy()
         self._args_received = None
         self._cb_time = None
@@ -124,17 +129,19 @@
     def testScheduleWithContext(self):
         """! Test schedule with context
         @param self this object
-        @return none
+        @return None
         """
+
         def callback(context, args: ns.cppyy.gbl.std.vector):
             """! Callback
-            @param context the cntet
+            @param context the context
             @param args the arguments
-            @return none
+            @return None
             """
             self._context_received = context
             self._args_received = list(map(lambda x: x.decode("utf-8"), args))
             self._cb_time = ns.Simulator.Now()
+
         ns.Simulator.Destroy()
         self._args_received = None
         self._cb_time = None
@@ -155,7 +162,7 @@
     def testTimeComparison(self):
         """! Test time comparison
         @param self this object
-        @return none
+        @return None
         """
         self.assertTrue(ns.Seconds(123) == ns.Seconds(123))
         self.assertTrue(ns.Seconds(123) >= ns.Seconds(123))
@@ -166,18 +173,18 @@
     def testTimeNumericOperations(self):
         """! Test numeric operations
         @param self this object
-        @return none
+        @return None
         """
         self.assertEqual(ns.Seconds(10) + ns.Seconds(5), ns.Seconds(15))
         self.assertEqual(ns.Seconds(10) - ns.Seconds(5), ns.Seconds(5))
 
-        v1 = ns.int64x64_t(5.0)*ns.int64x64_t(10)
+        v1 = ns.int64x64_t(5.0) * ns.int64x64_t(10)
         self.assertEqual(v1, ns.int64x64_t(50))
 
     def testConfig(self):
         """! Test configuration
         @param self this object
-        @return none
+        @return None
         """
         ns.Config.SetDefault("ns3::OnOffApplication::PacketSize", ns.core.UintegerValue(123))
         # hm.. no Config.Get?
@@ -185,7 +192,7 @@
     def testSocket(self):
         """! Test socket
         @param self
-        @return none
+        @return None
         """
         nc = ns.NodeContainer(1)
         node = nc.Get(0)
@@ -197,18 +204,19 @@
             self._received_packet = socket.Recv(maxSize=UINT32_MAX, flags=0)
 
         ns.cppyy.cppdef("""
-            Callback<void,ns3::Ptr<ns3::Socket> > make_rx_callback(void(*func)(Ptr<Socket>))
+            Callback<void,ns3::Ptr<ns3::Socket> > make_rx_callback_test_socket(void(*func)(Ptr<Socket>))
             {
                 return MakeCallback(func);
             }
         """)
 
         sink = ns.network.Socket.CreateSocket(node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory"))
-        sink.Bind(ns.addressFromInetSocketAddress(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80)))
-        sink.SetRecvCallback(ns.cppyy.gbl.make_rx_callback(python_rx_callback))
+        sink.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80).ConvertTo())
+        sink.SetRecvCallback(ns.cppyy.gbl.make_rx_callback_test_socket(python_rx_callback))
 
         source = ns.network.Socket.CreateSocket(node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory"))
-        source.SendTo(ns.network.Packet(19), 0, ns.addressFromInetSocketAddress(ns.network.InetSocketAddress(ns.network.Ipv4Address("127.0.0.1"), 80)))
+        source.SendTo(ns.network.Packet(19), 0,
+                      ns.network.InetSocketAddress(ns.network.Ipv4Address("127.0.0.1"), 80).ConvertTo())
 
         ns.Simulator.Run()
         self.assertTrue(self._received_packet is not None)
@@ -220,16 +228,16 @@
     def testAttributes(self):
         """! Test attributes function
         @param self this object
-        @return none
+        @return None
         """
         # Templated class DropTailQueue<Packet> in C++
         queue = ns.CreateObject("DropTailQueue<Packet>")
-        queueSizeValue = ns.network.QueueSizeValue (ns.network.QueueSize ("500p"))
+        queueSizeValue = ns.network.QueueSizeValue(ns.network.QueueSize("500p"))
         queue.SetAttribute("MaxSize", queueSizeValue)
 
         limit = ns.network.QueueSizeValue()
         queue.GetAttribute("MaxSize", limit)
-        self.assertEqual(limit.Get(), ns.network.QueueSize ("500p"))
+        self.assertEqual(limit.Get(), ns.network.QueueSize("500p"))
 
         ## -- object pointer values
         mobility = ns.CreateObject("RandomWaypointMobilityModel")
@@ -251,7 +259,7 @@
     def testIdentity(self):
         """! Test identify
         @param self this object
-        @return none
+        @return None
         """
         csma = ns.CreateObject("CsmaNetDevice")
         channel = ns.CreateObject("CsmaChannel")
@@ -268,11 +276,11 @@
     def testTypeId(self):
         """! Test type ID
         @param self this object
-        @return none
+        @return None
         """
         ok, typeId1 = ns.LookupByNameFailSafe("ns3::UdpSocketFactory")
         self.assertTrue(ok)
-        self.assertEqual(typeId1.GetName (), "ns3::UdpSocketFactory")
+        self.assertEqual(typeId1.GetName(), "ns3::UdpSocketFactory")
 
         ok, typeId1 = ns.LookupByNameFailSafe("ns3::__InvalidTypeName__")
         self.assertFalse(ok)
@@ -280,14 +288,14 @@
     def testCommandLine(self):
         """! Test command line
         @param self this object
-        @return none
+        @return None
         """
         from ctypes import c_bool, c_int, c_double, c_char_p, create_string_buffer
 
         test1 = c_bool(True)
         test2 = c_int(42)
         test3 = c_double(3.1415)
-        BUFFLEN = 40
+        BUFFLEN = 40  # noqa
         test4Buffer = create_string_buffer(b"this is a test option", BUFFLEN)
         test4 = c_char_p(test4Buffer.raw)
 
@@ -314,8 +322,9 @@
     def testSubclass(self):
         """! Test subclass
         @param self this object
-        @return none
+        @return None
         """
+
         ## MyNode class
         class MyNode(ns.network.Node):
             def GetLocalTime(self) -> ns.Time:
@@ -326,6 +335,156 @@
         self.assertEqual(forced_local_time, ns.Seconds(10))
         del node
 
+    def testEchoServerApplication(self):
+        """! Test python-based application
+        @param self this object
+        @return None
+        """
+        ns.Simulator.Destroy()
+
+        nodes = ns.network.NodeContainer()
+        nodes.Create(2)
+
+        pointToPoint = ns.point_to_point.PointToPointHelper()
+        pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
+        pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))
+
+        devices = pointToPoint.Install(nodes)
+
+        stack = ns.internet.InternetStackHelper()
+        stack.Install(nodes)
+
+        address = ns.internet.Ipv4AddressHelper()
+        address.SetBase(ns.network.Ipv4Address("10.1.1.0"),
+                        ns.network.Ipv4Mask("255.255.255.0"))
+
+        interfaces = address.Assign(devices)
+
+        ns.cppyy.cppdef("""
+            namespace ns3
+            {
+                Callback<void,Ptr<Socket> > make_rx_callback(void(*func)(Ptr<Socket>))
+                {
+                    return MakeCallback(func);
+                }
+                EventImpl* pythonMakeEventSend(void (*f)(Ptr<Socket>, Ptr<Packet>, Address&), Ptr<Socket> socket, Ptr<Packet> packet, Address address)
+                {
+                    return MakeEvent(f, socket, packet, address);
+                }
+            }
+        """)
+
+        ## EchoServer application class
+        class EchoServer(ns.applications.Application):
+            LOGGING = False
+            ECHO_PORT = 1234
+            socketToInstanceDict = {}
+
+            def __init__(self, node: ns.Node, port=ECHO_PORT):
+                """! Constructor needs to call first the constructor to Application (super class)
+                @param self this object
+                @param node node where this application will be executed
+                @param port port to listen
+                return None
+                """
+                super().__init__()
+                self.__python_owns__ = False  # Let C++ destroy this on Simulator::Destroy
+                ## Listen port for the server
+                self.port = port
+                ## Socket used by the server to listen to port
+                self.m_socket = ns.network.Socket.CreateSocket(node,
+                                                               ns.core.TypeId.LookupByName("ns3::UdpSocketFactory"))
+                self.m_socket.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), self.port).ConvertTo())
+                self.m_socket.SetRecvCallback(ns.make_rx_callback(EchoServer._Receive))
+                EchoServer.socketToInstanceDict[self.m_socket] = self
+
+            def __del__(self):
+                """! Destructor
+                @param self this object
+                return None
+                """
+                del EchoServer.socketToInstanceDict[self.m_socket]
+
+            def Send(self, packet: ns.Packet, address: ns.Address) -> None:
+                """! Function to send a packet to an address
+                @param self this object
+                @param packet packet to send
+                @param address destination address
+                return None
+                """
+                self.m_socket.SendTo(packet, 0, address)
+                if EchoServer.LOGGING:
+                    inetAddress = ns.InetSocketAddress.ConvertFrom(address)
+                    print("At time +{s}s server sent {b} bytes from {ip} port {port}"
+                          .format(s=ns.Simulator.Now().GetSeconds(),
+                                  b=packet.__deref__().GetSize(),
+                                  ip=inetAddress.GetIpv4(),
+                                  port=inetAddress.GetPort()),
+                          file=sys.stderr,
+                          flush=True)
+
+            def Receive(self):
+                """! Function to receive a packet from an address
+                @param self this object
+                @return None
+                """
+                address = ns.Address()
+                packet = self.m_socket.RecvFrom(address)
+                if EchoServer.LOGGING:
+                    inetAddress = ns.InetSocketAddress.ConvertFrom(address)
+                    print("At time +{s}s server received {b} bytes from {ip} port {port}"
+                          .format(s=ns.Simulator.Now().GetSeconds(),
+                                  b=packet.__deref__().GetSize(),
+                                  ip=inetAddress.GetIpv4(),
+                                  port=inetAddress.GetPort()),
+                          file=sys.stderr,
+                          flush=True)
+                event = ns.pythonMakeEventSend(EchoServer._Send, self.m_socket, packet, address)
+                ns.Simulator.Schedule(ns.Seconds(1), event)
+
+            @staticmethod
+            def _Send(socket: ns.Socket, packet: ns.Packet, address: ns.Address):
+                """! Static send function, which matches the output socket
+                to the EchoServer instance to call the instance Send function
+                @param socket socket from the instance that should send the packet
+                @param packet packet to send
+                @param address destination address
+                return None
+                """
+                instance = EchoServer.socketToInstanceDict[socket]
+                instance.Send(packet, address)
+
+            @staticmethod
+            def _Receive(socket: ns.Socket) -> None:
+                """! Static receive function, which matches the input socket
+                to the EchoServer instance to call the instance Receive function
+                @param socket socket from the instance that should receive the packet
+                return None
+                """
+                instance = EchoServer.socketToInstanceDict[socket]
+                instance.Receive()
+
+        echoServer = EchoServer(nodes.Get(1))
+        nodes.Get(1).AddApplication(echoServer)
+
+        serverApps = ns.ApplicationContainer()
+        serverApps.Add(echoServer)
+        serverApps.Start(ns.core.Seconds(1.0))
+        serverApps.Stop(ns.core.Seconds(10.0))
+
+        address = interfaces.GetAddress(1).ConvertTo()
+        echoClient = ns.applications.UdpEchoClientHelper(address, EchoServer.ECHO_PORT)
+        echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(10))
+        echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0)))
+        echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(101))
+
+        clientApps = echoClient.Install(nodes.Get(0))
+        clientApps.Start(ns.core.Seconds(2.0))
+        clientApps.Stop(ns.core.Seconds(10.0))
+
+        ns.Simulator.Run()
+        ns.Simulator.Destroy()
+
 
 if __name__ == '__main__':
     unittest.main(verbosity=1, failfast=True)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/gitlab-ci-clang.yml ns-3.38/utils/tests/gitlab-ci-clang.yml
--- ns-3.37/utils/tests/gitlab-ci-clang.yml	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/gitlab-ci-clang.yml	2023-03-16 20:54:23.884869300 -0700
@@ -21,6 +21,7 @@
     - apt install -y
       clang-$CLANG cmake ninja-build ccache build-essential
       libboost-all-dev
+      libeigen3-dev
       libgtk-3-0 libgtk-3-dev
       libgsl-dev $LIBGSL gsl-bin
       libsqlite3-dev sqlite3
@@ -43,6 +44,7 @@
     - apt install -y
       clang-$CLANG cmake ninja-build ccache build-essential
       libboost-all-dev
+      libeigen3-dev
       libgtk-3-0 libgtk-3-dev
       libgsl-dev $LIBGSL gsl-bin
       libsqlite3-dev sqlite3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/gitlab-ci-code-linting.yml ns-3.38/utils/tests/gitlab-ci-code-linting.yml
--- ns-3.37/utils/tests/gitlab-ci-code-linting.yml	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/gitlab-ci-code-linting.yml	2023-03-16 20:54:23.884869300 -0700
@@ -2,6 +2,7 @@
 #
 # 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
@@ -12,25 +13,101 @@
       clang-format-14
   script:
     - python3 utils/check-style-clang-format.py .
-  timeout: 3h
+  timeout: 1h
 
-clang-tidy:
+# Clang-tidy
+.base-clang-tidy:
   stage: code-linting
   image: ubuntu:latest
+  variables:
+    MPI_CI: 1
+    CLANG_TIDY_OUTPUT: clang-tidy-output.log
   before_script:
     - apt update
     - DEBIAN_FRONTEND=noninteractive apt install -y
       clang cmake
-      clang-tidy-14
-  script:
-    - ./ns3 configure --enable-clang-tidy -d debug --enable-examples --enable-tests
-    - run-clang-tidy-14 -p cmake-cache/ -quiet
-      1> clang-tidy-errors.log
-      2> /dev/null
-    - (! egrep -A 3 "error:|warning:" clang-tidy-errors.log)
-    - echo "No clang-tidy errors found"
+      clang-tidy clang-tidy-14
+      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
+    - ./ns3 configure -d debug
+      --enable-clang-tidy
+      --enable-examples --enable-tests --enable-asserts
+      --enable-mpi --enable-python-bindings
   artifacts:
     paths:
-      - clang-tidy-errors.log
+      - $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"
+
+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
+    # 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
+      -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
+      1> $CLANG_TIDY_OUTPUT
+      2> /dev/null ;
+      fi
+    - (! egrep -A 3 "error:|warning:|note:" $CLANG_TIDY_OUTPUT)
+    - echo "No clang-tidy errors found"
+
+# Emacs line
+emacs-line:
+  stage: code-linting
+  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"
+  timeout: 1h
+
+# Spell checking
+spell-check:
+  stage: code-linting
+  image: python:latest
+  before_script:
+    - pip install codespell
+  script:
+    - codespell ./
+  timeout: 1h
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/gitlab-ci-doc.yml ns-3.38/utils/tests/gitlab-ci-doc.yml
--- ns-3.37/utils/tests/gitlab-ci-doc.yml	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/gitlab-ci-doc.yml	2023-03-16 20:54:23.884869300 -0700
@@ -6,10 +6,11 @@
   before_script:
     - pacman-key --init
     - pacman -Syu --noconfirm
-      base-devel ninja cmake python ccache gsl gtk3 boost
-      doxygen graphviz imagemagick texlive-core texlive-bin texlive-latexextra python-sphinx
-    # dia is self-hosted
-    - pacman --noconfirm -U https://www.nsnam.org/wp-content/uploads/2022/dia-0.97.3-9-x86_64.pkg.tar.zst
+      base-devel ninja cmake python ccache gsl gtk3 boost openmpi openssh
+      graphviz imagemagick texlive-core texlive-bin texlive-latexextra 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
     # 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
@@ -27,7 +28,8 @@
     - export CCACHE_BASEDIR=${PWD}
     - export CCACHE_DIR=${PWD}/$CCACHE_BASEDIR_VALUE
     # debug build is the fastest, and we do not need to actually execute anything, just the examples for introspection.
-    - CXX=$COMPILER ./ns3 configure --enable-examples --enable-tests --enable-asserts -d debug
+    - export MPI_CI=1
+    - CXX=$COMPILER ./ns3 configure -d debug -GNinja --enable-examples --enable-tests --enable-asserts --enable-mpi
     - ./ns3 build
     - ./ns3 build assemble-introspected-command-line
     # - mkdir -p public/doxygen
@@ -45,18 +47,11 @@
     # - doxygen doc/doxygen.conf
     # - mv doc/html/ public/doxygen
   cache:
-    # Use separate key for each (debug/default/optimized) jobs because
-    # they run in parallel and will otherwise overwrite each other
-    # cache when they upload the cache archive at the end of the job,
-    # resulting in only the cache for the last finished configuration
-    # being stored.
-    #
-    # Do not distinguish between branches though to avoid
-    # recompilation of all the files when a new branch is created.
-    key: "ccache-$CI_JOB_NAME"
+    # Reuse the ccache from the per-commit-gcc-debug job
+    key: "ccache-per-commit-gcc-debug"
     paths:
       - $CCACHE_BASEDIR_VALUE/
-    when: "always"
+    policy: pull
   variables:
     CCACHE_BASEDIR_VALUE: ns-3-ccache-storage
     COMPILER: g++
@@ -77,8 +72,8 @@
     - make latexpdf
     # - mv build/html ../../public/manual
     # - mv build/latex/ns-3-manual.pdf ../../public/
-  only:
-    - master
+  # only:
+  #   - master
 
 tutorial:
   extends: .doc
@@ -89,8 +84,8 @@
     - make latexpdf
     # - mv build/html ../../public/manual
     # - mv build/latex/ns-3-tutorial.pdf ../../public/
-  only:
-    - master
+  # only:
+  #   - master
 
 models:
   extends: .doc
@@ -101,5 +96,29 @@
     - make latexpdf
     # - mv build/html ../../public/models
     # - mv build/latex/ns-3-model-library.pdf ../../public
-  only:
-    - master
+  # only:
+  #   - master
+
+contributing:
+  extends: .doc
+  script:
+    # - mkdir -p public/contributing
+    - cd doc/contributing
+    - make html
+    - make latexpdf
+    # - mv build/html ../../public/manual
+    # - mv build/latex/ns-3-manual.pdf ../../public/
+  # only:
+  #   - master
+
+installation:
+  extends: .doc
+  script:
+    # - mkdir -p public/installation
+    - cd doc/installation
+    - make html
+    - make latexpdf
+    # - mv build/html ../../public/manual
+    # - mv build/latex/ns-3-manual.pdf ../../public/
+  # only:
+  #   - master
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/gitlab-ci-fedora.yml ns-3.38/utils/tests/gitlab-ci-fedora.yml
--- ns-3.37/utils/tests/gitlab-ci-fedora.yml	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/gitlab-ci-fedora.yml	2023-03-16 20:54:23.884869300 -0700
@@ -18,6 +18,7 @@
     - dnf install --assumeyes
       gcc-c++ cmake ccache ninja-build
       boost-devel
+      eigen3-devel
       gtk3 gtk3-devel
       gsl gsl-devel
       sqlite sqlite-devel
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/gitlab-ci-gcc.yml ns-3.38/utils/tests/gitlab-ci-gcc.yml
--- ns-3.37/utils/tests/gitlab-ci-gcc.yml	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/gitlab-ci-gcc.yml	2023-03-16 20:54:23.884869300 -0700
@@ -7,122 +7,127 @@
 # We support from 8 to the latest version. Check if everything builds fine
 # under debug, default, and optimized, on Linux.
 
-.weekly-build-gcc:
+.weekly-build-gcc-base:
   extends: .base-build
   only:
     variables:
       - $RELEASE == "weekly"
-      - $GCC_BUILD_ENABLE == "True"
+      - $UBUNTU == "True"
   before_script:
     - apt update
-    - update-alternatives --install /usr/bin/gfortran gfortran /usr/local/bin/gfortran 999
+    - apt upgrade -y
     - DEBIAN_FRONTEND=noninteractive apt install -y
-      g++ cmake ninja-build ccache
+      $COMPILER cmake ninja-build ccache
       python3 python3-dev
-      libgsl0-dev gsl-bin
+      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
+      libeigen3-dev
       qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      bison flex ssh
+      ssh
   variables:
-    COMPILER: g++
     ENABLE_MPI: --enable-mpi
 
-# GCC 8
-weekly-build-gcc-8-debug:
-  extends: .weekly-build-gcc
-  image: gcc:8
-  variables:
-    MODE: debug
-
-weekly-build-gcc-8-default:
-  extends: .weekly-build-gcc
-  image: gcc:8
-  variables:
-    MODE: default
-
-weekly-build-gcc-8-optimized:
-  extends: .weekly-build-gcc
-  image: gcc:8
-  variables:
-    MODE: optimized
-
 # GCC 9
 weekly-build-gcc-9-debug:
-  extends: .weekly-build-gcc
-  image: gcc:9
+  extends: .weekly-build-gcc-base
+  image: ubuntu:20.04
   variables:
     MODE: debug
+    COMPILER: g++-9
+    LIBGSL: libgsl23
 
 weekly-build-gcc-9-default:
-  extends: .weekly-build-gcc
-  image: gcc:9
+  extends: .weekly-build-gcc-base
+  image: ubuntu:20.04
   variables:
     MODE: default
+    COMPILER: g++-9
+    LIBGSL: libgsl23
 
 weekly-build-gcc-9-optimized:
-  extends: .weekly-build-gcc
-  image: gcc:9
+  extends: .weekly-build-gcc-base
+  image: ubuntu:20.04
   variables:
     MODE: optimized
+    COMPILER: g++-9
+    LIBGSL: libgsl23
 
 # GCC 10
 weekly-build-gcc-10-debug:
-  extends: .weekly-build-gcc
-  image: gcc:10
+  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
-  image: gcc:10
+  extends: .weekly-build-gcc-base
+  image: ubuntu:20.04
   variables:
     MODE: default
+    COMPILER: g++-10
+    LIBGSL: libgsl23
 
 weekly-build-gcc-10-optimized:
-  extends: .weekly-build-gcc
-  image: gcc:10
+  extends: .weekly-build-gcc-base
+  image: ubuntu:20.04
   variables:
     MODE: optimized
+    COMPILER: g++-10
+    LIBGSL: libgsl23
 
 # GCC 11
 weekly-build-gcc-11-debug:
-  extends: .weekly-build-gcc
-  image: gcc:11
+  extends: .weekly-build-gcc-base
+  image: ubuntu:22.04
   variables:
     MODE: debug
+    COMPILER: g++-11
+    LIBGSL: libgsl27
 
 weekly-build-gcc-11-default:
-  extends: .weekly-build-gcc
-  image: gcc:11
+  extends: .weekly-build-gcc-base
+  image: ubuntu:22.04
   variables:
     MODE: default
+    COMPILER: g++-11
+    LIBGSL: libgsl27
 
 weekly-build-gcc-11-optimized:
-  extends: .weekly-build-gcc
-  image: gcc:11
+  extends: .weekly-build-gcc-base
+  image: ubuntu:22.04
   variables:
     MODE: optimized
+    COMPILER: g++-11
+    LIBGSL: libgsl27
 
 # GCC 12
 weekly-build-gcc-12-debug:
-  extends: .weekly-build-gcc
-  image: gcc:12
+  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
-  image: gcc:12
+  extends: .weekly-build-gcc-base
+  image: ubuntu:22.04
   variables:
     MODE: default
+    COMPILER: g++-12
+    LIBGSL: libgsl27
 
 weekly-build-gcc-12-optimized:
-  extends: .weekly-build-gcc
-  image: gcc:12
+  extends: .weekly-build-gcc-base
+  image: ubuntu:22.04
   variables:
     MODE: optimized
+    COMPILER: g++-12
+    LIBGSL: libgsl27
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/gitlab-ci-ubuntu.yml ns-3.38/utils/tests/gitlab-ci-ubuntu.yml
--- ns-3.37/utils/tests/gitlab-ci-ubuntu.yml	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/gitlab-ci-ubuntu.yml	2023-03-16 20:54:23.884869300 -0700
@@ -26,8 +26,9 @@
       libxml2 libxml2-dev
       libopenmpi-dev openmpi-bin openmpi-common openmpi-doc
       libsqlite3-dev sqlite3
+      libeigen3-dev
       qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      bison flex ssh
+      ssh
 
   variables:
     COMPILER: g++
@@ -53,7 +54,7 @@
       libopenmpi-dev openmpi-bin openmpi-common openmpi-doc
       libsqlite3-dev sqlite3
       qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      bison flex ssh
+      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
   variables:
@@ -79,7 +80,7 @@
       libopenmpi-dev openmpi-bin openmpi-common openmpi-doc
       libsqlite3-dev sqlite3
       qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      bison flex ssh
+      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
   variables:
@@ -105,7 +106,7 @@
       libopenmpi-dev openmpi-bin openmpi-common openmpi-doc
       libsqlite3-dev sqlite3
       qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      bison flex ssh
+      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
   variables:
@@ -177,6 +178,28 @@
   variables:
     MODE: optimized
     LIBGSL: libgsl27
+
+# Ubuntu Latest
+weekly-build-ubuntu-latest-debug:
+  extends: .weekly-build-ubuntu
+  image: ubuntu:latest
+  variables:
+    MODE: debug
+    LIBGSL: libgsl27
+
+weekly-build-ubuntu-latest-default:
+  extends: .weekly-build-ubuntu
+  image: ubuntu:latest
+  variables:
+    MODE: default
+    LIBGSL: libgsl27
+
+weekly-build-ubuntu-latest-optimized:
+  extends: .weekly-build-ubuntu
+  image: ubuntu:latest
+  variables:
+    MODE: optimized
+    LIBGSL: libgsl27
 
 # Ubuntu Rolling
 # weekly-build-ubuntu-rolling-debug:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/gitlab-ci.yml ns-3.38/utils/tests/gitlab-ci.yml
--- ns-3.37/utils/tests/gitlab-ci.yml	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/gitlab-ci.yml	2023-03-16 20:54:23.884869300 -0700
@@ -37,8 +37,10 @@
     - 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
+    - ccache -z
     - ./ns3 build
-    - if [ "$MODE" != "debug" ]; then ./test.py -n; fi
+    - if [ "$MODE" != "debug" ] && [ "`./utils/ccache-miss-rate.py`" != "0" ]; then ./test.py -n; fi
+    - ccache -s
     - ./ns3 clean
   cache:
     # Use separate key for each (debug/default/optimized) jobs because
@@ -52,7 +54,6 @@
     key: "ccache-$CI_JOB_NAME"
     paths:
       - $CCACHE_BASEDIR_VALUE/
-    when: "always"
   timeout: 12h
   variables:
     CCACHE_BASEDIR_VALUE: ns-3-ccache-storage
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/utils/tests/test-ns3.py ns-3.38/utils/tests/test-ns3.py
--- ns-3.37/utils/tests/test-ns3.py	2022-11-01 17:37:24.771396000 -0700
+++ ns-3.38/utils/tests/test-ns3.py	2023-03-16 20:54:23.884869300 -0700
@@ -68,6 +68,10 @@
                 shutil.rmtree(leftover, ignore_errors=True)
     if " -G " in args:
         args = args.format(generator=generator)
+    if env is None:
+        env = {}
+    # Disable colored output by default during tests
+    env["CLICOLOR"] = "0"
     return run_program(ns3_script, args, python=True, env=env)
 
 
@@ -140,7 +144,8 @@
     @param lib_outdir: path containing libraries
     @return list of built libraries.
     """
-    return glob.glob(lib_outdir + '/*', recursive=True)
+    libraries = glob.glob(lib_outdir + '/*', recursive=True)
+    return list(filter(lambda x: "scratch-nested-subdir-lib" not in x, libraries))
 
 
 def get_headers_list(outdir=usual_outdir):
@@ -180,6 +185,71 @@
     return read_lock_entry("NS3_ENABLED_MODULES")
 
 
+class DockerContainerManager:
+    """!
+    Python-on-whales wrapper for Docker-based ns-3 tests
+    """
+
+    def __init__(self, currentTestCase: unittest.TestCase, containerName: str = "ubuntu:latest"):
+        """!
+        Create and start container with containerName in the current ns-3 directory
+        @param self: the current DockerContainerManager instance
+        @param currentTestCase: the test case instance creating the DockerContainerManager
+        @param containerName: name of the container image to be used
+        """
+        global DockerException
+        try:
+            from python_on_whales import docker
+            from python_on_whales.exceptions import DockerException
+        except ModuleNotFoundError:
+            docker = None  # noqa
+            DockerException = None  # noqa
+            currentTestCase.skipTest("python-on-whales was not found")
+
+        # Import rootless docker settings from .bashrc
+        with open(os.path.expanduser("~/.bashrc"), "r") as f:
+            docker_settings = re.findall("(DOCKER_.*=.*)", f.read())
+            for setting in docker_settings:
+                key, value = setting.split("=")
+                os.environ[key] = value
+            del docker_settings, setting, key, value
+
+        # Create Docker client instance and start it
+        ## The Python-on-whales container instance
+        self.container = docker.run(containerName,
+                                    interactive=True, detach=True,
+                                    tty=False,
+                                    volumes=[(ns3_path, "/ns-3-dev")]
+                                    )
+
+        # Redefine the execute command of the container
+        def split_exec(docker_container, cmd):
+            return docker_container._execute(cmd.split(), workdir="/ns-3-dev")
+
+        self.container._execute = self.container.execute
+        self.container.execute = partial(split_exec, self.container)
+
+    def __enter__(self):
+        """!
+        Return the managed container when entiring the block "with DockerContainerManager() as container"
+        @param self: the current DockerContainerManager instance
+        @return container managed by DockerContainerManager.
+        """
+        return self.container
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        """!
+        Clean up the managed container at the end of the block "with DockerContainerManager() as container"
+        @param self: the current DockerContainerManager instance
+        @param exc_type: unused parameter
+        @param exc_val: unused parameter
+        @param exc_tb: unused parameter
+        @return None
+        """
+        self.container.stop()
+        self.container.remove()
+
+
 class NS3UnusedSourcesTestCase(unittest.TestCase):
     """!
     ns-3 tests related to checking if source files were left behind, not being used by CMake
@@ -596,15 +666,46 @@
         self.assertEqual(return_code, 2)
         self.assertIn("invalid choice: 'OPTIMIZED'", stderr)
 
+    def test_06_OverwriteDefaultSettings(self):
+        """!
+        Replace settings set by default (e.g. ASSERT/LOGs enabled in debug builds and disabled in default ones)
+        @return None
+        """
+        return_code, _, _ = run_ns3("clean")
+        self.assertEqual(return_code, 0)
+
+        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --dry-run -d debug")
+        self.assertEqual(return_code, 0)
+        self.assertIn(
+            "-DCMAKE_BUILD_TYPE=debug -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON -DNS3_NATIVE_OPTIMIZATIONS=OFF",
+            stdout)
+
+        return_code, stdout, stderr = run_ns3(
+            "configure -G \"{generator}\" --dry-run -d debug --disable-asserts --disable-logs --disable-werror")
+        self.assertEqual(return_code, 0)
+        self.assertIn(
+            "-DCMAKE_BUILD_TYPE=debug -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_ASSERT=OFF -DNS3_LOG=OFF -DNS3_WARNINGS_AS_ERRORS=OFF",
+            stdout)
+
+        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --dry-run")
+        self.assertEqual(return_code, 0)
+        self.assertIn(
+            "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=OFF -DNS3_NATIVE_OPTIMIZATIONS=OFF",
+            stdout)
+
+        return_code, stdout, stderr = run_ns3(
+            "configure -G \"{generator}\" --dry-run --enable-asserts --enable-logs --enable-werror")
+        self.assertEqual(return_code, 0)
+        self.assertIn(
+            "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON",
+            stdout)
+
 
 class NS3BaseTestCase(unittest.TestCase):
     """!
     Generic test case with basic function inherited by more complex tests.
     """
 
-    ## when cleaned_once is False, clean up build artifacts and reconfigure # noqa
-    cleaned_once = False
-
     def config_ok(self, return_code, stdout):
         """!
         Check if configuration for release mode worked normally
@@ -628,12 +729,10 @@
         if os.path.exists(ns3rc_script):
             os.remove(ns3rc_script)
 
-        # We only clear it once and then update the settings by changing flags or consuming ns3rc.
-        if not NS3BaseTestCase.cleaned_once:
-            NS3BaseTestCase.cleaned_once = True
-            run_ns3("clean")
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d release --enable-verbose")
-            self.config_ok(return_code, stdout)
+        # Reconfigure from scratch before each test
+        run_ns3("clean")
+        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d release --enable-verbose")
+        self.config_ok(return_code, stdout)
 
         # Check if .lock-ns3 exists, then read to get list of executables.
         self.assertTrue(os.path.exists(ns3_lock_filename))
@@ -651,17 +750,11 @@
     Test ns3 configuration options
     """
 
-    ## when cleaned_once is False, clean up build artifacts and reconfigure # noqa
-    cleaned_once = False
-
     def setUp(self):
         """!
         Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
         @return None
         """
-        if not NS3ConfigureTestCase.cleaned_once:
-            NS3ConfigureTestCase.cleaned_once = True
-            NS3BaseTestCase.cleaned_once = False
         super().setUp()
 
     def test_01_Examples(self):
@@ -1081,7 +1174,7 @@
         """
         return_code, stdout, stderr = run_ns3("show config")
         self.assertEqual(return_code, 0)
-        self.assertIn("Summary of optional ns-3 features", stdout)
+        self.assertIn("Summary of ns-3 settings", stdout)
 
     def test_11_CheckProfile(self):
         """!
@@ -1090,7 +1183,7 @@
         """
         return_code, stdout, stderr = run_ns3("show profile")
         self.assertEqual(return_code, 0)
-        self.assertIn("Build profile: default", stdout)
+        self.assertIn("Build profile: release", stdout)
 
     def test_12_CheckVersion(self):
         """!
@@ -1385,9 +1478,6 @@
             self.assertIn("--profiling-format=google-trace --profiling-output=../cmake_performance_trace.log", stdout)
         self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake_performance_trace.log")))
 
-        # Reconfigure to clean leftovers before the next test
-        NS3ConfigureTestCase.cleaned_once = False
-
     def test_18_CheckBuildVersionAndVersionCache(self):
         """!
         Check if ENABLE_BUILD_VERSION and version.cache are working
@@ -1395,35 +1485,8 @@
         @return None
         """
 
-        try:
-            from python_on_whales import docker
-            from python_on_whales.exceptions import DockerException
-        except ModuleNotFoundError:
-            docker = None  # noqa
-            DockerException = None  # noqa
-            self.skipTest("python-on-whales was not found")
-
-        # Import rootless docker settings from .bashrc
-        with open(os.path.expanduser("~/.bashrc"), "r") as f:
-            docker_settings = re.findall("(DOCKER_.*=.*)", f.read())
-            for setting in docker_settings:
-                key, value = setting.split("=")
-                os.environ[key] = value
-            del docker_settings, setting, key, value
-
         # Create Docker client instance and start it
-        with docker.run("ubuntu:22.04",
-                        interactive=True, detach=True,
-                        tty=False,
-                        volumes=[(ns3_path, "/ns-3-dev")]
-                        ) as container:
-            # Redefine the execute command of the container
-            def split_exec(docker_container, cmd):
-                return docker_container._execute(cmd.split(), workdir="/ns-3-dev")
-
-            container._execute = container.execute
-            container.execute = partial(split_exec, container)
-
+        with DockerContainerManager(self, "ubuntu:22.04") as container:
             # Install basic packages
             container.execute("apt-get update")
             container.execute("apt-get install -y python3 ninja-build cmake g++")
@@ -1496,9 +1559,6 @@
             if os.path.exists(version_cache_file):
                 os.remove(version_cache_file)
 
-        # Reconfigure to clean leftovers before the next test
-        NS3ConfigureTestCase.cleaned_once = False
-
     def test_19_FilterModuleExamplesAndTests(self):
         """!
         Test filtering in examples and tests from specific modules
@@ -1548,37 +1608,8 @@
         @return None
         """
 
-        try:
-            from python_on_whales import docker
-            from python_on_whales.exceptions import DockerException
-        except ModuleNotFoundError:
-            docker = None  # noqa
-            DockerException = None  # noqa
-            self.skipTest("python-on-whales was not found")
-
         run_ns3("clean")
-
-        # Import rootless docker settings from .bashrc
-        with open(os.path.expanduser("~/.bashrc"), "r") as f:
-            docker_settings = re.findall("(DOCKER_.*=.*)", f.read())
-            for setting in docker_settings:
-                key, value = setting.split("=")
-                os.environ[key] = value
-            del docker_settings, setting, key, value
-
-        # Create Docker client instance and start it
-        with docker.run("gcc:12.1",  # Debian with minimum supported version of GCC for Mold
-                        interactive=True, detach=True,
-                        tty=False,
-                        volumes=[(ns3_path, "/ns-3-dev")],
-                        ) as container:
-            # Redefine the execute command of the container
-            def split_exec(docker_container, cmd):
-                return docker_container._execute(cmd.split(), workdir="/ns-3-dev")
-
-            container._execute = container.execute
-            container.execute = partial(split_exec, container)
-
+        with DockerContainerManager(self, "gcc:12.1") as container:
             # Install basic packages
             container.execute("apt-get update")
             container.execute("apt-get install -y python3 ninja-build cmake g++ lld")
@@ -1621,8 +1652,192 @@
             # Delete mold leftovers
             os.remove("./mold-1.4.2-x86_64-linux.tar.gz")
 
-        # Clean leftovers before proceeding
+            # Disable use of fast linkers
+            container.execute("./ns3 configure -G Ninja -- -DNS3_FAST_LINKERS=OFF")
+
+            # Check if configuration properly disabled lld/mold usage
+            self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
+            with open(os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r") as f:
+                self.assertNotIn("-fuse-ld=mold", f.read())
+
+    def test_21_ClangTimeTrace(self):
+        """!
+        Check if NS3_CLANG_TIMETRACE feature is working
+        Clang's -ftime-trace plus ClangAnalyzer report
+        @return None
+        """
+
+        run_ns3("clean")
+        with DockerContainerManager(self, "ubuntu:20.04") as container:
+            container.execute("apt-get update")
+            container.execute("apt-get install -y python3 ninja-build cmake clang-10")
+
+            # Enable ClangTimeTrace without git (it should fail)
+            try:
+                container.execute(
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON")
+            except DockerException as e:
+                self.assertIn("could not find git for clone of ClangBuildAnalyzer", e.stderr)
+
+            container.execute("apt-get install -y git")
+
+            # Enable ClangTimeTrace without git (it should succeed)
+            try:
+                container.execute(
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON")
+            except DockerException as e:
+                self.assertIn("could not find git for clone of ClangBuildAnalyzer", e.stderr)
+
+            # Clean leftover time trace report
+            time_trace_report_path = os.path.join(ns3_path, "ClangBuildAnalyzerReport.txt")
+            if os.path.exists(time_trace_report_path):
+                os.remove(time_trace_report_path)
+
+            # Build new time trace report
+            try:
+                container.execute("./ns3 build timeTraceReport")
+            except DockerException as e:
+                self.assertTrue(False, "Failed to build the ClangAnalyzer's time trace report")
+
+            # Check if the report exists
+            self.assertTrue(os.path.exists(time_trace_report_path))
+
+            # Now try with GCC, which should fail during the configuration
+            run_ns3("clean")
+            container.execute("apt-get install -y g++")
+            container.execute("apt-get remove -y clang-10")
+
+            try:
+                container.execute(
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DNS3_CLANG_TIMETRACE=ON")
+                self.assertTrue(False, "ClangTimeTrace requires Clang, but GCC just passed the checks too")
+            except DockerException as e:
+                self.assertIn("TimeTrace is a Clang feature", e.stderr)
+
+    def test_22_NinjaTrace(self):
+        """!
+        Check if NS3_NINJA_TRACE feature is working
+        Ninja's .ninja_log conversion to about://tracing
+        json format conversion with Ninjatracing
+        @return None
+        """
+
         run_ns3("clean")
+        with DockerContainerManager(self, "ubuntu:20.04") as container:
+            container.execute("apt-get update")
+            container.execute("apt-get install -y python3 cmake clang-10")
+
+            # Enable Ninja tracing without using the Ninja generator
+            try:
+                container.execute(
+                    "./ns3 configure --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
+            except DockerException as e:
+                self.assertIn("Ninjatracing requires the Ninja generator", e.stderr)
+
+            # Clean build system leftovers
+            run_ns3("clean")
+
+            container.execute("apt-get install -y ninja-build")
+            # Enable Ninjatracing support without git (should fail)
+            try:
+                container.execute(
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
+            except DockerException as e:
+                self.assertIn("could not find git for clone of NinjaTracing", e.stderr)
+
+            container.execute("apt-get install -y git")
+            # Enable Ninjatracing support with git (it should succeed)
+            try:
+                container.execute(
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
+            except DockerException as e:
+                self.assertTrue(False, "Failed to configure with Ninjatracing")
+
+            # Clean leftover ninja trace
+            ninja_trace_path = os.path.join(ns3_path, "ninja_performance_trace.json")
+            if os.path.exists(ninja_trace_path):
+                os.remove(ninja_trace_path)
+
+            # Build the core module
+            container.execute("./ns3 build core")
+
+            # Build new ninja trace
+            try:
+                container.execute("./ns3 build ninjaTrace")
+            except DockerException as e:
+                self.assertTrue(False, "Failed to run Ninjatracing's tool to build the trace")
+
+            # Check if the report exists
+            self.assertTrue(os.path.exists(ninja_trace_path))
+            trace_size = os.stat(ninja_trace_path).st_size
+            os.remove(ninja_trace_path)
+
+            run_ns3("clean")
+
+            # Enable Clang TimeTrace feature for more detailed traces
+            try:
+                container.execute(
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON")
+            except DockerException as e:
+                self.assertTrue(False, "Failed to configure Ninjatracing with Clang's TimeTrace")
+
+            # Build the core module
+            container.execute("./ns3 build core")
+
+            # Build new ninja trace
+            try:
+                container.execute("./ns3 build ninjaTrace")
+            except DockerException as e:
+                self.assertTrue(False, "Failed to run Ninjatracing's tool to build the trace")
+
+            self.assertTrue(os.path.exists(ninja_trace_path))
+            timetrace_size = os.stat(ninja_trace_path).st_size
+            os.remove(ninja_trace_path)
+
+            # Check if timetrace's trace is bigger than the original trace (it should be)
+            self.assertGreater(timetrace_size, trace_size)
+
+    def test_23_PrecompiledHeaders(self):
+        """!
+        Check if precompiled headers are being enabled correctly.
+        @return None
+        """
+
+        run_ns3("clean")
+        # Ubuntu 18.04 ships with:
+        # - cmake 3.10: does not support PCH
+        # - ccache 3.4: incompatible with pch
+        with DockerContainerManager(self, "ubuntu:18.04") as container:
+            container.execute("apt-get update")
+            container.execute("apt-get install -y python3 cmake ccache clang-10")
+            try:
+                container.execute("./ns3 configure -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
+            except DockerException as e:
+                self.assertIn("does not support precompiled headers", e.stderr)
+        run_ns3("clean")
+
+        # Ubuntu 20.04 ships with:
+        # - cmake 3.16: does support PCH
+        # - ccache 3.7: incompatible with pch
+        with DockerContainerManager(self, "ubuntu:20.04") as container:
+            container.execute("apt-get update")
+            container.execute("apt-get install -y python3 cmake ccache g++")
+            try:
+                container.execute("./ns3 configure")
+            except DockerException as e:
+                self.assertIn("incompatible with ccache", e.stderr)
+        run_ns3("clean")
+
+        # Ubuntu 22.04 ships with:
+        # - cmake 3.22: does support PCH
+        # - ccache 4.5: compatible with pch
+        with DockerContainerManager(self, "ubuntu:22.04") as container:
+            container.execute("apt-get update")
+            container.execute("apt-get install -y python3 cmake ccache g++")
+            try:
+                container.execute("./ns3 configure")
+            except DockerException as e:
+                self.assertTrue(False, "Precompiled headers should have been enabled")
 
 
 class NS3BuildBaseTestCase(NS3BaseTestCase):
@@ -1630,17 +1845,11 @@
     Tests ns3 regarding building the project
     """
 
-    ## when cleaned_once is False, clean up build artifacts and reconfigure # noqa
-    cleaned_once = False
-
     def setUp(self):
         """!
         Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
         @return None
         """
-        if not NS3BuildBaseTestCase.cleaned_once:
-            NS3BuildBaseTestCase.cleaned_once = True
-            NS3BaseTestCase.cleaned_once = False
         super().setUp()
 
         self.ns3_libraries = get_libraries_list()
@@ -1706,9 +1915,6 @@
         return_code, stdout, stderr = run_ns3("build")
         self.assertEqual(return_code, 0)
 
-        # Reset flag to let it clean the build
-        NS3BuildBaseTestCase.cleaned_once = False
-
     def test_06_TestVersionFile(self):
         """!
         Test if changing the version file affects the library names
@@ -1753,9 +1959,6 @@
         with open(version_file, "w") as f:
             f.write("3-dev\n")
 
-        # Reset flag to let it clean the build.
-        NS3BuildBaseTestCase.cleaned_once = False
-
     def test_07_OutputDirectory(self):
         """!
         Try setting a different output directory and if everything is
@@ -1981,9 +2184,6 @@
         with open(version_file, "w") as f:
             f.write("3-dev\n")
 
-        # Reset flag to let it clean the build
-        NS3BuildBaseTestCase.cleaned_once = False
-
     def test_09_Scratches(self):
         """!
         Tries to build scratch-simulator and subdir/scratch-simulator-subdir
@@ -2011,8 +2211,6 @@
             stdout = stdout.replace("scratch_%s" % target_cmake, "")  # remove build lines
             self.assertIn(target_to_run.split("/")[-1].replace(".cc", ""), stdout)
 
-        NS3BuildBaseTestCase.cleaned_once = False
-
     def test_10_AmbiguityCheck(self):
         """!
         Test if ns3 can alert correctly in case a shortcut collision happens
@@ -2067,8 +2265,6 @@
         # Remove second
         os.remove("./scratch/second.cc")
 
-        NS3BuildBaseTestCase.cleaned_once = False
-
     def test_11_StaticBuilds(self):
         """!
         Test if we can build a static ns-3 library and link it to static programs
@@ -2094,7 +2290,6 @@
             self.assertIn("Built target", stdout)
 
         # Maybe check the built binary for shared library references? Using objdump, otool, etc
-        NS3BuildBaseTestCase.cleaned_once = False
 
     def test_12_CppyyBindings(self):
         """!
@@ -2131,24 +2326,19 @@
     Tests ns3 usage in more realistic scenarios
     """
 
-    ## when cleaned_once is False, clean up build artifacts and reconfigure # noqa
-    cleaned_once = False
-
     def setUp(self):
         """!
         Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
         Here examples, tests and documentation are also enabled.
         @return None
         """
-        if not NS3ExpectedUseTestCase.cleaned_once:
-            NS3ExpectedUseTestCase.cleaned_once = True
-            NS3BaseTestCase.cleaned_once = False
-            super().setUp()
-
-            # On top of the release build configured by NS3ConfigureTestCase, also enable examples, tests and docs.
-            return_code, stdout, stderr = run_ns3(
-                "configure -d release -G \"{generator}\" --enable-examples --enable-tests")
-            self.config_ok(return_code, stdout)
+
+        super().setUp()
+
+        # On top of the release build configured by NS3ConfigureTestCase, also enable examples, tests and docs.
+        return_code, stdout, stderr = run_ns3(
+            "configure -d release -G \"{generator}\" --enable-examples --enable-tests")
+        self.config_ok(return_code, stdout)
 
         # Check if .lock-ns3 exists, then read to get list of executables.
         self.assertTrue(os.path.exists(ns3_lock_filename))
@@ -2210,6 +2400,9 @@
         Try to run test-runner without building
         @return None
         """
+        return_code, stdout, stderr = run_ns3('build test-runner')
+        self.assertEqual(return_code, 0)
+
         return_code, stdout, stderr = run_ns3('run "test-runner --list" --no-build --verbose')
         self.assertEqual(return_code, 0)
         self.assertNotIn("Built target test-runner", stdout)
@@ -2241,6 +2434,9 @@
         if shutil.which("gdb") is None:
             self.skipTest("Missing gdb")
 
+        return_code, stdout, stderr = run_ns3("build scratch-simulator")
+        self.assertEqual(return_code, 0)
+
         return_code, stdout, stderr = run_ns3("run scratch-simulator --gdb --verbose --no-build", env={"gdb_eval": "1"})
         self.assertEqual(return_code, 0)
         self.assertIn("scratch-simulator", stdout)
@@ -2257,6 +2453,9 @@
         if shutil.which("valgrind") is None:
             self.skipTest("Missing valgrind")
 
+        return_code, stdout, stderr = run_ns3("build scratch-simulator")
+        self.assertEqual(return_code, 0)
+
         return_code, stdout, stderr = run_ns3("run scratch-simulator --valgrind --verbose --no-build")
         self.assertEqual(return_code, 0)
         self.assertIn("scratch-simulator", stderr)
@@ -2321,7 +2520,7 @@
         doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
 
         # For each sphinx doc target.
-        for target in ["contributing", "manual", "models", "tutorial"]:
+        for target in ["installation", "contributing", "manual", "models", "tutorial"]:
             # First we need to clean old docs, or it will not make any sense.
             doc_build_folder = os.sep.join([doc_folder, target, "build"])
             doc_temp_folder = os.sep.join([doc_folder, target, "source-temp"])
@@ -2468,9 +2667,8 @@
         return_code2, stdout2, stderr2 = run_ns3('run sample-simulator --command-template " "')
         return_code3, stdout3, stderr3 = run_ns3('run sample-simulator --command-template "echo "')
         self.assertEqual((return_code1, return_code2, return_code3), (1, 1, 1))
-        self.assertIn("not all arguments converted during string formatting", stderr1)
-        self.assertEqual(stderr1, stderr2)
-        self.assertEqual(stderr2, stderr3)
+        for stderr in [stderr1, stderr2, stderr3]:
+            self.assertIn("not all arguments converted during string formatting", stderr)
 
         # Command templates with %s should at least continue and try to run the target
         return_code4, stdout4, _ = run_ns3('run sample-simulator --command-template "%s --PrintVersion" --verbose')
@@ -2573,6 +2771,8 @@
         # Skip this test if requests library is not available
         try:
             import requests
+            import urllib3
+            urllib3.disable_warnings()
         except ImportError:
             requests = None  # noqa
             self.skipTest("Requests library is not available")
@@ -2639,7 +2839,8 @@
 
         # User agent string to make ACM and Elsevier let us check if links to papers are working
         headers = {
-            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36' # noqa
+            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
+            # noqa
         }
 
         def test_file_url(args):
@@ -2651,37 +2852,39 @@
                 validate_url(test_url)
             except ValidationError:
                 dead_link_msg = "%s: URL %s, invalid URL" % (test_filepath, test_url)
+            except Exception as e:
+                self.assertEqual(False, True, msg=e.__str__())
 
+            if dead_link_msg is not None:
+                return dead_link_msg
+            tries = 3
             # Check if valid URLs are alive
-            if dead_link_msg is None:
+            while tries > 0:
+                # Not verifying the certificate (verify=False) is potentially dangerous
+                # HEAD checks are not as reliable as GET ones,
+                # in some cases they may return bogus error codes and reasons
                 try:
-                    tries = 3
-                    while tries > 0:
-                        # Not verifying the certificate (verify=False) is potentially dangerous
-                        # HEAD checks are not as reliable as GET ones,
-                        # in some cases they may return bogus error codes and reasons
-                        response = requests.get(test_url, verify=False, headers=headers)
+                    response = requests.get(test_url, verify=False, headers=headers, timeout=50)
 
-                        # In case of success and redirection
-                        if response.status_code in [200, 301]:
+                    # In case of success and redirection
+                    if response.status_code in [200, 301]:
+                        dead_link_msg = None
+                        break
+
+                    # People use the wrong code, but the reason
+                    # can still be correct
+                    if response.status_code in [302, 308, 500, 503]:
+                        if response.reason.lower() in ['found',
+                                                       'moved temporarily',
+                                                       'permanent redirect',
+                                                       'ok',
+                                                       'service temporarily unavailable'
+                                                       ]:
                             dead_link_msg = None
                             break
-
-                        # People use the wrong code, but the reason
-                        # can still be correct
-                        if response.status_code in [302, 308, 500, 503]:
-                            if response.reason.lower() in ['found',
-                                                           'moved temporarily',
-                                                           'permanent redirect',
-                                                           'ok',
-                                                           'service temporarily unavailable'
-                                                           ]:
-                                dead_link_msg = None
-                                break
-                        # In case it didn't pass in any of the previous tests,
-                        # set dead_link_msg with the most recent error and try again
-                        dead_link_msg = "%s: URL %s: returned code %d" % (test_filepath, test_url, response.status_code)
-                        tries -= 1
+                    # In case it didn't pass in any of the previous tests,
+                    # set dead_link_msg with the most recent error and try again
+                    dead_link_msg = "%s: URL %s: returned code %d" % (test_filepath, test_url, response.status_code)
                 except requests.exceptions.InvalidURL:
                     dead_link_msg = "%s: URL %s: invalid URL" % (test_filepath, test_url)
                 except requests.exceptions.SSLError:
@@ -2694,11 +2897,12 @@
                     except AttributeError:
                         error_msg = e.args[0]
                     dead_link_msg = "%s: URL %s: failed with exception: %s" % (test_filepath, test_url, error_msg)
+                tries -= 1
             return dead_link_msg
 
         # Dispatch threads to test multiple URLs concurrently
         from concurrent.futures import ThreadPoolExecutor
-        with ThreadPoolExecutor(max_workers=20) as executor:
+        with ThreadPoolExecutor(max_workers=100) as executor:
             dead_links = list(executor.map(test_file_url, list(files_and_urls)))
 
         # Filter out None entries
@@ -2715,9 +2919,6 @@
         self.assertEqual(return_code, 0)
 
         test_return_code, stdout, stderr = run_program("test.py", "", python=True)
-
-        return_code, stdout, stderr = run_ns3("clean")
-        self.assertEqual(return_code, 0)
         self.assertEqual(test_return_code, 0)
 
     def test_03_CheckImageBrightness(self):
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/VERSION ns-3.38/VERSION
--- ns-3.37/VERSION	2022-11-01 17:39:10.947703100 -0700
+++ ns-3.38/VERSION	2023-03-16 20:54:51.628957000 -0700
@@ -1 +1 @@
-3.37
+3.38
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.37/.vscode/launch.json ns-3.38/.vscode/launch.json
--- ns-3.37/.vscode/launch.json	2022-11-01 17:37:24.359394800 -0700
+++ ns-3.38/.vscode/launch.json	2023-03-16 20:54:23.484868000 -0700
@@ -8,7 +8,7 @@
             "name": "(gdb) Launch from scratch",
             "type": "cppdbg",
             "request": "launch",
-            "program": "${workspaceFolder}/build/${relativeFileDirname}/ns3-dev-${fileBasenameNoExtension}-default",
+            "program": "${workspaceFolder}/build/${relativeFileDirname}/ns3-dev-${fileBasenameNoExtension}-${input:buildType}",
             "args": [],
             "stopAtEntry": false,
             "cwd": "${workspaceFolder}",
@@ -33,7 +33,7 @@
             "name": "(lldb) Launch from scratch",
             "type": "cppdbg",
             "request": "launch",
-            "program": "${workspaceFolder}/build/${relativeFileDirname}/ns3-dev-${fileBasenameNoExtension}-default",
+            "program": "${workspaceFolder}/build/${relativeFileDirname}/ns3-dev-${fileBasenameNoExtension}-${input:buildType}",
             "args": [],
             "stopAtEntry": false,
             "cwd": "${workspaceFolder}",
@@ -51,7 +51,7 @@
             "name": "(gdb) Launch testrunner",
             "type": "cppdbg",
             "request": "launch",
-            "program": "${workspaceFolder}/build/utils/ns3-dev-test-runner-debug",
+            "program": "${workspaceFolder}/build/utils/ns3-dev-test-runner-${input:buildType}",
             "args": [
                 "--suite=${selectedText}"
             ],
@@ -78,7 +78,7 @@
             "name": "(lldb) Launch testrunner",
             "type": "cppdbg",
             "request": "launch",
-            "program": "${workspaceFolder}/build/utils/ns3-dev-test-runner-debug",
+            "program": "${workspaceFolder}/build/utils/ns3-dev-test-runner-${input:buildType}",
             "args": [
                 "--suite=${selectedText}"
             ],
@@ -94,5 +94,20 @@
             "externalConsole": false,
             "MIMode": "lldb"
         }
+    ],
+    "inputs": [
+        {
+            "type": "pickString",
+            "id": "buildType",
+            "description": "What is the build option?",
+            "options": [
+                "debug",
+                "default",
+                "optimized",
+                "release",
+                "minsizerel"
+            ],
+            "default": "default"
+        }
     ]
 }
