diff --git a/.clang-tidy b/.clang-tidy index a96824ca0..cf9f0822a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -29,17 +29,22 @@ Checks: > 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 --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000..c428309c9 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,4 @@ +[codespell] +skip = .git,.gitlab-ci-local,*.eps,*.pdf,*.css_t,AUTHORS +ignore-words = ./utils/codespell-ignored-words +exclude-file = ./utils/codespell-ignored-lines diff --git a/.github/workflows/per_commit.yml b/.github/workflows/per_commit.yml index deffd3c79..0dd09aafe 100644 --- a/.github/workflows/per_commit.yml +++ b/.github/workflows/per_commit.yml @@ -12,7 +12,7 @@ jobs: run: | sudo apt-get update sudo apt-get -y install apt-utils - sudo apt-get -y install git gcc g++ cmake python make ninja-build + sudo apt-get -y install git gcc g++ cmake python3 make ninja-build sudo apt-get -y install tcpdump libgsl-dev libxml2-dev sudo apt-get -y install curl unzip tar sudo apt-get -y install ccache @@ -65,7 +65,7 @@ jobs: run: | sudo apt-get update sudo apt-get -y install apt-utils - sudo apt-get -y install git gcc g++ cmake python make ninja-build + sudo apt-get -y install git gcc g++ cmake python3 make ninja-build sudo apt-get -y install tcpdump libgsl-dev libxml2-dev sudo apt-get -y install curl unzip tar sudo apt-get -y install ccache @@ -77,8 +77,8 @@ jobs: uses: actions/cache@v3 with: path: .ccache - key: ubuntu-ci-${{env.time}} - restore-keys: ubuntu-ci- + key: ubuntu-codeql-${{env.time}} + restore-keys: ubuntu-codeql- - name: Setup ccache run: | ccache --set-config=cache_dir="$GITHUB_WORKSPACE/.ccache" diff --git a/.gitignore b/.gitignore index ee2d5cb41..d7ad52f9f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ coverity TAGS .lock-ns3_* +.ns3rc build-dir/ build/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 9234f66b3..fc8dd7112 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -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" + } ] } diff --git a/AUTHORS b/AUTHORS index 7c29fbcb7..4b5b76c02 100644 --- a/AUTHORS +++ b/AUTHORS @@ -72,6 +72,7 @@ Zhiheng Dong (dzh2077@gmail.com) 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 @@ Michael Nowatkowski (nowatkom@gmail.com) 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) +Parth Pandya (parthpandyappp@gmail.com) Lluís Parcerisa (parcerisa@gmail.com) -Parth Pandya (parthpandyappp@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 @@ Rohit P. Tahiliani (tahiliar@tcd.ie) 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 --git a/CHANGES.md b/CHANGES.md index 044b398d0..dc4f605e1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,48 @@ Note that users who upgrade the simulator across versions, or who work directly 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 . 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 @@ Changes from ns-3.29 to ns-3.30 * 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 @@ Changes from ns-3.29 to ns-3.30 * 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 @@ Changes from ns-3.18.1 to ns-3.19 * 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 @@ Changes from ns-3.17 to ns-3.18 * 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 @@ Changes from ns-3.7 to ns-3.8 * **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 --git a/CMakeLists.txt b/CMakeLists.txt index cea53b950..866e844c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ option(NS3_NETANIM "Build netanim" OFF) # 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) @@ -75,12 +67,18 @@ option(NS3_MONOLIB option(NS3_MPI "Build with MPI support" OFF) option(NS3_MTP "Build with Multithreaded simulation 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 ) @@ -107,6 +105,21 @@ set(NS3_FILTER_MODULE_EXAMPLES_AND_TESTS "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 --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b43000a7..8e869dfd2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,7 +61,7 @@ ns-3 is intentionally very modular, and it's possible that a feature you need or ### 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 --git a/README.md b/README.md index fff84d8d3..f588524fd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # UNISON for ns-3 [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.10077300.svg)](https://doi.org/10.5281/zenodo.10077300) +[![CI](https://github.com/NASA-NJU/UNISON-for-ns-3/actions/workflows/per_commit.yml/badge.svg)](https://github.com/NASA-NJU/UNISON-for-ns-3/actions/workflows/per_commit.yml) + +## Table of Contents A fast and user-transparent parallel simulator implementation for ns-3. More information about UNISON can be found in our EuroSys '24 paper (coming soon). diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 46f2b1de4..18f5c7f74 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -13,6 +13,67 @@ a [GitLab.com issue tracker](https://gitlab.com/nsnam/ns-3-dev/-/issues) number, 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: + + +### 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 @@ This release has been tested on the following systems: - (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 @@ issue number (prefixed by '#'), or GitLab merge request number (prefixed by '!') - (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 @@ issue number (prefixed by '#'), or GitLab merge request number (prefixed by '!') - (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 @@ These platforms have been tested; others may work also: 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 @@ These platforms have been tested; others may work also: - 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 @@ wiki for more information: 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 --git a/VERSION b/VERSION index ef350da80..5ca3687ec 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.37 +3.38 diff --git a/bindings/python/ns__init__.py b/bindings/python/ns__init__.py index 473f05803..f43f639ce 100644 --- a/bindings/python/ns__init__.py +++ b/bindings/python/ns__init__.py @@ -74,6 +74,10 @@ def _search_libraries() -> dict: 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 @@ def _search_libraries() -> dict: # 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 @@ def load_modules(): 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 @@ def load_modules(): 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 LookupByNameFailSafe(std::string name) diff --git a/build-support/3rd-party/FindEigen3.cmake b/build-support/3rd-party/FindEigen3.cmake new file mode 100644 index 000000000..88ef27245 --- /dev/null +++ b/build-support/3rd-party/FindEigen3.cmake @@ -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, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# 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 --git a/build-support/3rd-party/colored-messages.cmake b/build-support/3rd-party/colored-messages.cmake index 2bb00d273..16a19e2f6 100644 --- a/build-support/3rd-party/colored-messages.cmake +++ b/build-support/3rd-party/colored-messages.cmake @@ -71,25 +71,25 @@ if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}") # 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 --git a/build-support/3rd-party/find-program-hints.cmake b/build-support/3rd-party/find-program-hints.cmake new file mode 100644 index 000000000..45547a1f1 --- /dev/null +++ b/build-support/3rd-party/find-program-hints.cmake @@ -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 --git a/build-support/compiler-workarounds/ostream-operator-nullptr.h b/build-support/compiler-workarounds/ostream-operator-nullptr.h index e13efe342..feb6b48b7 100644 --- a/build-support/compiler-workarounds/ostream-operator-nullptr.h +++ b/build-support/compiler-workarounds/ostream-operator-nullptr.h @@ -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 --git a/build-support/custom-modules/ns3-configtable.cmake b/build-support/custom-modules/ns3-configtable.cmake index a75fce41e..9f77f9b3e 100644 --- a/build-support/custom-modules/ns3-configtable.cmake +++ b/build-support/custom-modules/ns3-configtable.cmake @@ -113,12 +113,18 @@ function(print_formatted_table_with_modules table_name modules output) 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}") @@ -173,6 +179,9 @@ macro(write_configtable) 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}") @@ -214,4 +223,8 @@ macro(write_configtable) 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 --git a/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake b/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake new file mode 100644 index 000000000..90f774361 --- /dev/null +++ b/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake @@ -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 --git a/build-support/custom-modules/ns3-module-macros.cmake b/build-support/custom-modules/ns3-module-macros.cmake index 393041939..91398da5f 100644 --- a/build-support/custom-modules/ns3-module-macros.cmake +++ b/build-support/custom-modules/ns3-module-macros.cmake @@ -125,7 +125,8 @@ function(build_lib) ${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 @@ function(build_lib) # 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 @@ function(build_lib) 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 --git a/build-support/empty-main.cc b/build-support/empty-main.cc index df67b9156..1949917ac 100644 --- a/build-support/empty-main.cc +++ b/build-support/empty-main.cc @@ -1,4 +1,5 @@ int main() { + return 0; } diff --git a/build-support/macros-and-definitions.cmake b/build-support/macros-and-definitions.cmake index c1cc2226e..8622038c3 100644 --- a/build-support/macros-and-definitions.cmake +++ b/build-support/macros-and-definitions.cmake @@ -59,6 +59,12 @@ endif() if(APPLE) add_definitions(-D__APPLE__) + # cmake-format: off + # Configure find_program to search for AppBundles only if programs are not found in PATH. + # This prevents Doxywizard from being launched when the Doxygen.app is installed. + # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake + # cmake-format: on + set(CMAKE_FIND_APPBUNDLE "LAST") endif() if(WIN32) @@ -135,6 +141,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}) set(CMAKE_HEADER_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/include/ns3) set(THIRD_PARTY_DIRECTORY ${PROJECT_SOURCE_DIR}/3rd-party) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib) # Get installation folder default values for each platform and include package # configuration macro @@ -234,33 +241,35 @@ if(${CLANG} AND APPLE) 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) +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 + # 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() + # 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 @@ macro(clear_global_cached_variables) ) 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 @@ function(check_deps package_deps program_deps missing_deps) # 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 @@ macro(process_options) 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 @@ macro(process_options) 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 @@ macro(process_options) 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 @@ macro(process_options) ) 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 @@ macro(process_options) 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 @@ macro(process_options) 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'" @@ -991,63 +1074,13 @@ macro(process_options) 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 @@ -1055,6 +1088,69 @@ macro(process_options) 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 @@ -1087,6 +1183,7 @@ macro(process_options) 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) @@ -1134,6 +1231,7 @@ macro(process_options) sphinx_target(models) sphinx_target(tutorial) sphinx_target(contributing) + sphinx_target(installation) endif() # end of checking for documentation dependencies and creating targets @@ -1144,9 +1242,8 @@ macro(process_options) if(${NS3_INT64X64} MATCHES "INT128") check_cxx_source_compiles( "#include - 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; @@ -1155,9 +1252,8 @@ macro(process_options) ) check_cxx_source_compiles( "#include - 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; @@ -1298,8 +1394,35 @@ macro(process_options) "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 @@ -1340,10 +1463,15 @@ macro(process_options) ) - add_library(stdlib_pch OBJECT ${PROJECT_SOURCE_DIR}/build-support/empty.cc) - target_precompile_headers( - stdlib_pch PUBLIC "${precompiled_header_libraries}" + add_library( + stdlib_pch${build_profile_suffix} OBJECT + ${PROJECT_SOURCE_DIR}/build-support/empty.cc ) + target_precompile_headers( + 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 @@ -1380,6 +1508,12 @@ macro(process_options) ) 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) @@ -1440,6 +1574,17 @@ function(set_runtime_outputdirectory target_name output_directory target_prefix) 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) @@ -1451,6 +1596,13 @@ function(build_exec) "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 --git a/build-support/version-defines-template.h b/build-support/version-defines-template.h index ea19eccf8..5018cc68f 100644 --- a/build-support/version-defines-template.h +++ b/build-support/version-defines-template.h @@ -1,4 +1,3 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2018 Lawrence Livermore National Laboratory * diff --git a/doc/contributing/source/coding-style.rst b/doc/contributing/source/coding-style.rst index 4ef3ac654..5875ab101 100644 --- a/doc/contributing/source/coding-style.rst +++ b/doc/contributing/source/coding-style.rst @@ -97,8 +97,7 @@ Disable formatting in specific files or lines ============================================= To disable formatting in specific lines, surround them with the following -C++ comments [example adopted from -`official clang-format documentation `_]: +C++ comments: .. sourcecode:: cpp @@ -326,8 +325,8 @@ Indent constructor's initialization list with 4 spaces. .. sourcecode:: cpp MyClass::MyClass(int x, int y) - : m_x (x), - m_y (y) + : m_x(x), + m_y(y) { } @@ -798,6 +797,70 @@ that usage is consistent within a file. 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 @@ Casts ===== Where casts are necessary, use the Google C++ guidance: "Use C++-style casts -like ``static_cast (double_value)``, or brace initialization for +like ``static_cast(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 @@ to print the numeric value of any variable, such as: 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(x); + float y = 1 / static_cast(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 @@ can be rewritten as: if (n < 0) { - return n; + return false; } n += 3; @@ -1047,13 +1114,13 @@ the |ns3| smart pointer class ``Ptr`` should be used in boolean comparisons as f 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 @@ Miscellaneous items =================== - ``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 @@ Miscellaneous items 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 myVector{ + "string-1", + "string-2", + "string-3", + }; + + const std::map myMap{ + {1, "string-1"}, + {2, "string-2"}, + {3, "string-3"}, + }; + + - Const reference syntax: .. sourcecode:: cpp @@ -1132,6 +1216,24 @@ Miscellaneous items - 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 @@ of rules that should be observed while developing code. 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 @@ of rules that should be observed while developing code. - 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 --git a/doc/contributing/source/conf.py b/doc/contributing/source/conf.py index c11609c0c..36b709068 100644 --- a/doc/contributing/source/conf.py +++ b/doc/contributing/source/conf.py @@ -48,9 +48,9 @@ copyright = u'2015, ns-3 project' # 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 --git a/doc/contributing/source/external.rst b/doc/contributing/source/external.rst index 1c3fc2976..6eea997c7 100644 --- a/doc/contributing/source/external.rst +++ b/doc/contributing/source/external.rst @@ -19,12 +19,12 @@ with the |ns3| mainline. 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 `_. Rationale for the app store @@ -227,7 +227,7 @@ Links to related projects 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 `_. +`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 @@ not be listed in the app store, although popular extensions might be 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 @@ on our wiki: * Status (how it is maintained) Please also make clear in your code the applicable software license. -The contribution will be stored on our `wiki `_. 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 --git a/doc/contributing/source/index.rst b/doc/contributing/source/index.rst index 93e64f285..7604e4194 100644 --- a/doc/contributing/source/index.rst +++ b/doc/contributing/source/index.rst @@ -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 `_: Documentation of the public APIs of the simulator -* Tutorial for the `latest release `_ and `development tree `_ -* Contributing to ns-3 (this document) -* Manual, and Model Library for the `latest release `_ and `development tree `_ -* `ns-3 wiki `_ +* Several guides that are version controlled for each release (the + `latest release `_) and + `development tree `_: + + * Tutorial + * Installation Guide + * Manual + * Model Library + * Contributing Guide *(this document)* +* `ns-3 Doxygen `_: Documentation of the public APIs of + the simulator +* `ns-3 wiki `_ This document is written in `reStructuredText `_ for `Sphinx `_ 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 --git a/doc/contributing/source/models.rst b/doc/contributing/source/models.rst index 995c915be..d58b3d2d1 100644 --- a/doc/contributing/source/models.rst +++ b/doc/contributing/source/models.rst @@ -148,7 +148,7 @@ For example, if you have made changes to optimize one module, and in the 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 --git a/doc/doxygen.conf b/doc/doxygen.conf index 0fd6387d5..1ee923022 100644 --- a/doc/doxygen.conf +++ b/doc/doxygen.conf @@ -262,7 +262,7 @@ TAB_SIZE = 4 ## Inferred template parameters are clear from the context: ## template void Func (T x); ## Explicit template parameters must be given at the call site: -## template void Func (void); +## template void Func (); ## which is called as ## Func (); @@ -2691,3 +2691,7 @@ GENERATE_LEGEND = YES # 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 --git a/doc/doxygen.warnings.report.sh b/doc/doxygen.warnings.report.sh index 6eb6b22d0..ab2974b63 100755 --- a/doc/doxygen.warnings.report.sh +++ b/doc/doxygen.warnings.report.sh @@ -559,6 +559,12 @@ if [ "$filterin" != "" ] ; then 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 --git a/doc/installation/.gitignore b/doc/installation/.gitignore new file mode 100644 index 000000000..567609b12 --- /dev/null +++ b/doc/installation/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/doc/installation/Makefile b/doc/installation/Makefile new file mode 100644 index 000000000..ea2ecc46f --- /dev/null +++ b/doc/installation/Makefile @@ -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 ' where 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 --git a/doc/installation/source/_static/.hidden b/doc/installation/source/_static/.hidden new file mode 100644 index 000000000..e69de29bb diff --git a/doc/installation/source/_static/custom.css b/doc/installation/source/_static/custom.css new file mode 100644 index 000000000..8a5f414db --- /dev/null +++ b/doc/installation/source/_static/custom.css @@ -0,0 +1,4 @@ +table.align-left { + margin-left: auto; + margin-right: auto; +} diff --git a/doc/installation/source/bake.rst b/doc/installation/source/bake.rst new file mode 100644 index 000000000..db2120852 --- /dev/null +++ b/doc/installation/source/bake.rst @@ -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 `_ 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 `_ and in the +`Getting Started `_ chapter of the |ns3| tutorial. diff --git a/doc/installation/source/conf.py b/doc/installation/source/conf.py new file mode 100644 index 000000000..d4d4f43d7 --- /dev/null +++ b/doc/installation/source/conf.py @@ -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 +# " v 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 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 --git a/doc/installation/source/figures/README.txt b/doc/installation/source/figures/README.txt new file mode 100644 index 000000000..ceb61d4d4 --- /dev/null +++ b/doc/installation/source/figures/README.txt @@ -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 --git a/doc/installation/source/index.rst b/doc/installation/source/index.rst new file mode 100644 index 000000000..cbc5d8623 --- /dev/null +++ b/doc/installation/source/index.rst @@ -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 `_) and + `development tree `_: + + * Tutorial + * Installation Guide *(this document)* + * Manual + * Model Library + * Contributing Guide +* `ns-3 Doxygen `_: Documentation of the public APIs of + the simulator +* `ns-3 wiki `_ + +This document is written in `reStructuredText `_ +for `Sphinx `_ 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 --git a/doc/installation/source/linux.rst b/doc/installation/source/linux.rst new file mode 100644 index 000000000..0f1cf65a8 --- /dev/null +++ b/doc/installation/source/linux.rst @@ -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 `_ 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 `_. + +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 `_ 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 @@ + + - + + + + + + - + + + + + diff --git a/doc/installation/source/macos.rst b/doc/installation/source/macos.rst new file mode 100644 index 000000000..918671b13 --- /dev/null +++ b/doc/installation/source/macos.rst @@ -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 `_ or a more minimal install of +`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 `_ or `MacPorts `_. +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 `_ application; see ns-3 +`MR 1247 `_ for discussion about this. + +Finally, regarding Python, some |ns3| maintainers prefer to use a +`virtualenv `_ to guard against incompatibilities +that might arise from the native macOS Python and versions that may be installed by Homebrew +or `Anaconda `_. 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 `_. + +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 --git a/doc/installation/source/overview.rst b/doc/installation/source/overview.rst new file mode 100644 index 000000000..02c01f5ce --- /dev/null +++ b/doc/installation/source/overview.rst @@ -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 `_, in the GitLab.com +`Issue tracker `_, (or better yet, a patch to fix +in the `Merge Request tracker `_), 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 --git a/doc/installation/source/quick-start.rst b/doc/installation/source/quick-start.rst new file mode 100644 index 000000000..202092989 --- /dev/null +++ b/doc/installation/source/quick-start.rst @@ -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 `_, 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 `_ 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 `_, +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 --git a/doc/installation/source/replace.txt b/doc/installation/source/replace.txt new file mode 100644 index 000000000..d3c0a4273 --- /dev/null +++ b/doc/installation/source/replace.txt @@ -0,0 +1,5 @@ +.. |ns3| replace:: *ns-3* + +.. |ns2| replace:: *ns-2* + +.. |check| replace:: :math:`\checkmark` diff --git a/doc/installation/source/system.rst b/doc/installation/source/system.rst new file mode 100644 index 000000000..a4c8731ba --- /dev/null +++ b/doc/installation/source/system.rst @@ -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 `_ 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 `_ 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 `_ 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 `_ 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 `_ 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 `_ and +`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 `_ and `Valgrind `_ 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 `_ 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 `_. + +Database support +================ + +`SQLite `_ 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 `_. + +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 `_ 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 `_ 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 `_ is required. + +MPI-based distributed simulation +================================ + +`Open MPI `_ support is needed if you intend to run large, parallel +|ns3| simulations. + +Doxygen +======= + +`Doxygen `_ 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 `_ packages are needed. + +Eigen3 support +============== +`Eigen3 `_ is used to support more efficient calculations +when using the `3GPP propagation loss models `_ +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 `_ is needed for the XML-based Config Store feature. + +A GTK-based configuration system +================================ + +`GTK development libraries `_ 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 `_ and +`cxxfilt `_ are needed. + +You will also need to install +`castxml `_ and +`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 `_ and +`TUN/TAP device drivers `_ for emulation support. + +Support for openflow module +=========================== + +`OpenFlow switch support `_ requires +XML and `Boost `_ development libraries. diff --git a/doc/manual/source/windows.rst b/doc/installation/source/windows.rst similarity index 92% rename from doc/manual/source/windows.rst rename to doc/installation/source/windows.rst index aebdeffe2..0bb0db686 100644 --- a/doc/manual/source/windows.rst +++ b/doc/installation/source/windows.rst @@ -1,21 +1,46 @@ .. include:: replace.txt -.. highlight:: console +.. heading hierarchy: + ------------- Chapter + ************* Section (#.#) + ============= Subsection (#.#.#) + ############# Paragraph (no number) -.. Section Separators: - ---- - **** - ++++ - ==== - ~~~~ +.. _Windows: -.. _Windows 10: +Windows +------- -Windows 10 ----------- - -This chapter describes installation steps specific to Windows 10 and its +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 `_ +* `Developing in WSL with VS Code `_ +* `WSL tutorial `_ + .. _Windows 10 package prerequisites: Windows 10 package prerequisites @@ -31,7 +56,9 @@ Installation of the Msys2 environment 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. +|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. @@ -46,7 +73,7 @@ or via the following command (assuming it was installed to the default directory 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 +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 @@ -74,8 +101,13 @@ This is the minimal set of packages needed to run ns-3 C++ programs from a relea .. 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 + /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 @@ -121,14 +153,6 @@ 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 ++++++++++++++++++++++++++++++++++++++++ diff --git a/doc/main.h b/doc/main.h index c01855ccb..9a3ac3b55 100644 --- a/doc/main.h +++ b/doc/main.h @@ -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 --git a/doc/manual/Makefile b/doc/manual/Makefile index 4fff33698..3fb0b88e5 100644 --- a/doc/manual/Makefile +++ b/doc/manual/Makefile @@ -26,7 +26,7 @@ SOURCES = \ 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 @@ SOURCES = \ 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 --git a/doc/manual/source/attributes.rst b/doc/manual/source/attributes.rst index ebe086d0a..a62c9f798 100644 --- a/doc/manual/source/attributes.rst +++ b/doc/manual/source/attributes.rst @@ -32,7 +32,7 @@ specificity, these are: +=====================================+====================================+ | 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 @@ specificity, these are: | 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 @@ references to heap-allocated objects that may cause memory leaks. For most basic usage (syntax), treat a smart pointer like a regular pointer:: Ptr 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 @@ CreateObject 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 nd = CreateObject (); + Ptr nd = CreateObject(); 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 @@ Putting all of these concepts together, let's look at a specific 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 () - .SetGroupName ("Network") - .AddConstructor () - .AddAttribute ("DeviceList", - "The list of devices associated to this Node.", - ObjectVectorValue (), - MakeObjectVectorAccessor (&Node::m_devices), - MakeObjectVectorChecker ()) - .AddAttribute ("ApplicationList", - "The list of applications associated to this Node.", - ObjectVectorValue (), - MakeObjectVectorAccessor (&Node::m_applications), - MakeObjectVectorChecker ()) - .AddAttribute ("Id", - "The id (unique integer) of this Node.", - TypeId::ATTR_GET, // allow only getting it. - UintegerValue (0), - MakeUintegerAccessor (&Node::m_id), - MakeUintegerChecker ()) + static TypeId tid = TypeId("ns3::Node") + .SetParent() + .SetGroupName("Network") + .AddConstructor() + .AddAttribute("DeviceList", + "The list of devices associated to this Node.", + ObjectVectorValue(), + MakeObjectVectorAccessor(&Node::m_devices), + MakeObjectVectorChecker()) + .AddAttribute("ApplicationList", + "The list of applications associated to this Node.", + ObjectVectorValue(), + MakeObjectVectorAccessor(&Node::m_applications), + MakeObjectVectorChecker()) + .AddAttribute("Id", + "The id(unique integer) of this Node.", + TypeId::ATTR_GET, // allow only getting it. + UintegerValue(0), + MakeUintegerAccessor(&Node::m_id), + MakeUintegerChecker()) ; return tid; } @@ -192,38 +192,38 @@ as an extended form of run time type information (RTTI). The C++ language includes a simple kind of RTTI in order to support ``dynamic_cast`` and ``typeid`` operators. -The :cpp:func:`SetParent ()` call in the definition above is used in +The :cpp:func:`SetParent()` 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 ()` call is used in conjunction +The :cpp:func:`AddConstructor()` 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 n = CreateObject (); + Ptr n = CreateObject(); 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 node = factory.Create (); + factory.SetTypeId(typeId); + Ptr node = factory.Create (); Both of these methods result in fully initialized attributes being available in the resulting :cpp:class:`Object` instances. @@ -359,7 +359,7 @@ the following:: class QueueBase : public Object { public: - static TypeId GetTypeId (void); + static TypeId GetTypeId(); ... private: @@ -408,34 +408,34 @@ Let's consider things that a user may want to do with the value of 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 () - .SetGroupName ("Network") + static TypeId tid = TypeId("ns3::DropTailQueue") + .SetParent() + .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 @@ we will provide an example script that shows how users may manipulate 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 @@ function begins:: // 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). + // common is a pointer to a packet(Ptr). // // 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 @@ function begins:: // // 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 @@ the :cpp:class:`CommandLine` API documentation. // 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 n0 = CreateObject (); + Ptr n0 = CreateObject(); - Ptr net0 = CreateObject (); - n0->AddDevice (net0); + Ptr net0 = CreateObject(); + n0->AddDevice(net0); - Ptr > q = CreateObject > (); + Ptr> q = CreateObject> (); net0->AddQueue(q); At this point, we have created a single :cpp:class:`Node` (``n0``) @@ -569,23 +569,23 @@ the helper and low-level APIs; either from the constructors themselves:: Ptr p = CreateObjectWithAttributes - ("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 @@ one of the helper APIs for the class. 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 @@ First, we observe that we can get a pointer to the (base class) ``"TxQueue"``:: PointerValue ptr; - net0->GetAttribute ("TxQueue", ptr); - Ptr > txQueue = ptr.Get > (); + net0->GetAttribute("TxQueue", ptr); + Ptr> txQueue = ptr.Get>(); -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 > dtq = txQueue->GetObject > (); - NS_ASSERT (dtq != 0); + Ptr> dtq = txQueue->GetObject>(); + 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 @@ by either the `p` or `b` character). :: - 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 @@ would like to configure a specific attribute with a single statement. :: - 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 ``"...///...//"`` @@ -691,14 +691,14 @@ ends with a succession of member attributes, in this case the ``"MaxSize"`` 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 @@ namespace path. :: - 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 @@ or *via* strings. Direct implicit conversion of types to :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 @@ In general, the attribute code to assign values to the underlying class member 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 @@ variable using the metadata system. If it were not already provided by |ns3|, 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 ()) + .AddAttribute("Congestion window", + "Tcp congestion window(bytes)", + UintegerValue(1), + MakeUintegerAccessor(&TcpSocket::m_cWnd), + MakeUintegerChecker()) Now, the user with a pointer to a :cpp:class:`TcpSocket` instance can perform operations such as @@ -863,7 +863,7 @@ In the ``my-mobility.h`` header file:: 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 @@ This is a one-line public function declaration:: * 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 () - .SetGroupName ("Mobility") - .AddConstructor () - .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() + .SetGroupName("Mobility") + .AddConstructor() + .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 @@ in the ``my-mobility.cc`` implementation file:: 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 ()``. +``.SetParent()``. 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 @@ Header File 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 @@ In the class definition (``.cc`` file), the code looks like this:: 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 @@ to show how the system is extended:: class ConfigExample : public Object { public: - static TypeId GetTypeId (void) { - static TypeId tid = TypeId ("ns3::A") - .SetParent () - .AddAttribute ("TestInt16", "help text", - IntegerValue (-2), - MakeIntegerAccessor (&A::m_int16), - MakeIntegerChecker ()) + static TypeId GetTypeId() { + static TypeId tid = TypeId("ns3::A") + .SetParent() + .AddAttribute("TestInt16", "help text", + IntegerValue(-2), + MakeIntegerAccessor(&A::m_int16), + MakeIntegerChecker()) ; 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 a_obj = CreateObject (); - NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5, - "Cannot set ConfigExample's integer attribute via Config::SetDefault"); + Ptr a_obj = CreateObject(); + NS_ABORT_MSG_UNLESS(a_obj->m_int16 == -5, + "Cannot set ConfigExample's integer attribute via Config::SetDefault"); - Ptr a2_obj = CreateObject (); - a2_obj->SetAttribute ("TestInt16", IntegerValue (-3)); + Ptr a2_obj = CreateObject(); + 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 @@ or :cpp:class:`ns3::Channel` instance, 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 ConfigStore format is plain text or Xml (``"FileFormat=Xml"``) 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 @@ are registered before being used in object construction). :: - 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 @@ write out the resulting attributes to a separate file called #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 @@ As a matter of fact, some Objects might be created when the simulation starts. 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 @@ There is a GTK-based front end for the ConfigStore. This allows users to use a 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 @@ is rerun. 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 --git a/doc/manual/source/callbacks.rst b/doc/manual/source/callbacks.rst index c14a05b85..33a68f590 100644 --- a/doc/manual/source/callbacks.rst +++ b/doc/manual/source/callbacks.rst @@ -19,15 +19,15 @@ so that they can invoke methods on each other:: 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 @@ so that they can invoke methods on each other:: B::DoSomething() { // Tell a_instance that something happened - a_instance->ReceiveInput ( // parameters); + a_instance->ReceiveInput( /* parameters */ ); ... } @@ -58,7 +58,9 @@ This is not an abstract problem for network simulation research, but rather it 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 @@ What you get from this is a variable named simply ``pfi`` that is initialized to 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 @@ function like:: 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 pointer to function returning an int (PFI). 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 @@ particular class, one must declare that method in a class:: 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 @@ pointer. This, in turn, means there must be an object of MyClass to refer to. A 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 @@ It is basically just a packaged-up function call, possibly with some state. 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 @@ of the functor:: 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 @@ implement the specific ``operator()`` method:: m_pmi = _pmi; } - virtual int operator() (ARG arg) + virtual int operator()(ARG arg) { (*m_p.*m_pmi)(arg); } @@ -240,8 +242,8 @@ Here is an example of the usage:: 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 @@ with the object pointer using the C++ PMI syntax. 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 functor (&myclass, MyClass::MyMethod); + SpecificFunctor 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 @@ Using the Callback API with static functions 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 a function:: 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 @@ Now, we need to tie together this callback instance and the actual target functi 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 @@ arguments are the types of the arguments of the function signature. 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 @@ invoked. Consider this example, also from main-callback.cc:: 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 @@ invoked. Consider this example, also from main-callback.cc:: Callback 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 @@ Here, we pass an additional object pointer to the ``MakeCallback<>`` function. 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 @@ member syntax when it executes on an object:: 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 @@ check before using them. There is a special construct for a null callback, which is preferable to simply passing "0" as an argument; it is the ``MakeNullCallback<>`` construct:: - two = MakeNullCallback (); - NS_ASSERT (two.IsNull ()); + two = MakeNullCallback(); + 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 @@ function that needs to be called whenever a packet is received. This function 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 file, Ptr p); + static void DefaultSink(Ptr file, Ptr 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 file`` to the specific callback implementation when it is created and arrange for the @@ -501,7 +503,7 @@ We provide the ``MakeBoundCallback`` template function for that purpose. It 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 @@ with one or more bound arguments:: m_boundArg = boundArg; } - virtual int operator() (ARG arg) + virtual int operator()(ARG arg) { (*m_p.*m_pmi)(m_boundArg, arg); } @@ -532,7 +534,7 @@ You can see that when the specific functor is created, the bound argument is sav 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 @@ function call:: It's possible to bind two or three arguments as well. Say we have a function with signature:: - static void NotifyEvent (Ptr a, Ptr b, MyEventType e); + static void NotifyEvent(Ptr a, Ptr 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, Ptr b, MyEventType e); + static void NotifyEvent(Ptr a, Ptr 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 --git a/doc/manual/source/conf.py b/doc/manual/source/conf.py index dd6f7bd1d..1011e13a7 100644 --- a/doc/manual/source/conf.py +++ b/doc/manual/source/conf.py @@ -73,9 +73,9 @@ copyright = u'2006-2019' # 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 --git a/doc/manual/source/develop.rst b/doc/manual/source/develop.rst index 545175eed..b5ead5678 100644 --- a/doc/manual/source/develop.rst +++ b/doc/manual/source/develop.rst @@ -10,12 +10,10 @@ This chapter describes the development ecosystem generally used to create new mo working-with-git working-with-cmake - logging + logging-asserts tests new-models new-modules documentation profiling working-with-gitlab-ci-local - windows - diff --git a/doc/manual/source/documentation.rst b/doc/manual/source/documentation.rst index bb0912858..2395d0fb5 100644 --- a/doc/manual/source/documentation.rst +++ b/doc/manual/source/documentation.rst @@ -213,7 +213,7 @@ the basics here, instead focusing on preferred usage for |ns3|. | 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 @@ The preferred style for Doxygen comments is the JavaDoc style:: * 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 @@ For items needing only a brief description, either of these short forms is appropriate:: /** Destructor implementation. */ - void DoDispose (); + void DoDispose(); int m_count; //!< Count of ... @@ -355,11 +355,8 @@ Useful Features #. 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 @@ usage for |ns3|. * \tparam U \deduced The argument type. * \param [in] a The argument. */ - template T Function (U a); + template 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 @@ usage for |ns3|. * Use ``\tparam T \explicit`` because the type ``T`` can't be deduced; it must be given explicitly at the invocation site, as in - ``Create (...)`` + ``Create(...)`` * ``\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 @@ cases is: * 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 @@ cases is: * Register this type. * \return The object TypeId. */ - static TypeId GetTypeId (void); - - - + static TypeId GetTypeId(); diff --git a/doc/manual/source/events.rst b/doc/manual/source/events.rst index 0a7d6c373..b77c03db6 100644 --- a/doc/manual/source/events.rst +++ b/doc/manual/source/events.rst @@ -62,7 +62,7 @@ might write this: :: - 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 @@ What does this mean? :: - 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 @@ event execution. These are derived from the abstract base class `SimulatorImpl` 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 @@ For example one can use noisy local clocks with the real time adapter. 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 @@ The main job of the `Scheduler` classes is to maintain the priority queue of 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 @@ complexity of the other API calls. +=======================+=====================================+=============+==============+==========+==============+ | CalendarScheduler | ` []` | 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 @@ complexity of the other API calls. +-----------------------+-------------------------------------+-------------+--------------+----------+--------------+ | PriorityQueueSchduler | `std::priority_queue<,std::vector>` | Logarithimc | Logarithims | 24 bytes | 0 | +-----------------------+-------------------------------------+-------------+--------------+----------+--------------+ - - - diff --git a/doc/manual/source/gnuplot.rst b/doc/manual/source/gnuplot.rst index 7e885bb31..a8e84b6bb 100644 --- a/doc/manual/source/gnuplot.rst +++ b/doc/manual/source/gnuplot.rst @@ -9,7 +9,7 @@ There are 2 common methods to make a plot using |ns3| and gnuplot (http://www.gn #. 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 `_. +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 `_. Creating Plots Using the Gnuplot Class ************************************** @@ -88,24 +88,24 @@ was created using the following code from gnuplot-example.cc: :: 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 @@ was created using the following code from gnuplot-example.cc: :: 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 @@ was created using the following code from gnuplot-example.cc: :: 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 @@ was created using the following code from gnuplot-example.cc: :: // 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 @@ was created using the following code from gnuplot-example.cc: :: 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 @@ was created using the following code from gnuplot-example.cc: :: 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 --git a/doc/manual/source/hash-functions.rst b/doc/manual/source/hash-functions.rst index 4d52f9313..221ea3544 100644 --- a/doc/manual/source/hash-functions.rst +++ b/doc/manual/source/hash-functions.rst @@ -99,7 +99,7 @@ To add the hash function ``foo``, follow the ``hash-murmur3.h``/``.cc`` pattern: * ``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 ())`` + ``Hasher(Ptr())`` If your hash function is a single function, e.g. ``hashf``, you don't diff --git a/doc/manual/source/how-to-write-tests.rst b/doc/manual/source/how-to-write-tests.rst index 4282afef0..5a560cedf 100644 --- a/doc/manual/source/how-to-write-tests.rst +++ b/doc/manual/source/how-to-write-tests.rst @@ -60,8 +60,8 @@ is called "router" such as here: :: - RouterTestSuite::RouterTestSuite () - : TestSuite ("router", UNIT) + RouterTestSuite::RouterTestSuite() + : TestSuite("router", UNIT) Try this command: @@ -154,8 +154,8 @@ which looks like this: :: #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 @@ the wifi Information Elements. ... }; 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 @@ Storing and referencing non-trivial output data Presenting your output test data ******************************** - diff --git a/doc/manual/source/index.rst b/doc/manual/source/index.rst index 35c486d50..534153633 100644 --- a/doc/manual/source/index.rst +++ b/doc/manual/source/index.rst @@ -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 `_: Documentation of the public APIs of the simulator -* Tutorial, Manual *(this document)*, and Model Library for the `latest release `_ and `development tree `_ -* `ns-3 wiki `_ +* Several guides that are version controlled for each release (the + `latest release `_) and + `development tree `_: + + * Tutorial + * Installation Guide + * Manual *(this document)* + * Model Library + * Contributing Guide +* `ns-3 Doxygen `_: Documentation of the public APIs of + the simulator +* `ns-3 wiki `_ This document is written in `reStructuredText `_ for `Sphinx `_ 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 --git a/doc/manual/source/logging.rst b/doc/manual/source/logging-asserts.rst similarity index 74% rename from doc/manual/source/logging.rst rename to doc/manual/source/logging-asserts.rst index 6cd54ef45..3504b86c8 100644 --- a/doc/manual/source/logging.rst +++ b/doc/manual/source/logging-asserts.rst @@ -31,9 +31,9 @@ use of a particular function. For example, this code snippet is from ``Ipv4L3Protocol::IsDestinationAddress()``:: - if (address == iaddr.GetBroadcast ()) + if (address == iaddr.GetBroadcast()) { - NS_LOG_LOGIC ("For me (interface broadcast address)"); + NS_LOG_LOGIC("For me (interface broadcast address)"); return true; } @@ -41,8 +41,16 @@ 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.: @@ -66,17 +74,17 @@ 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[]) + main(int argc, char *argv[]) { - LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO); - LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO); + 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: @@ -107,7 +115,7 @@ in a module, spanning different compilation units, but logically grouped together, such as the |ns3| wifi code:: WifiHelper wifiHelper; - wifiHelper.EnableLogComponents (); + wifiHelper.EnableLogComponents(); The ``NS_LOG`` log component wildcard \`*' will enable all components. @@ -282,7 +290,7 @@ The name of the calling function can be included with the options ``NS_LOG`` Wildcards -#################### +==================== The log component wildcard \`*' will enable all components. To enable all components at a specific severity level @@ -319,7 +327,7 @@ How to add logging to your code Adding logging to your code is very simple: -1. Invoke the ``NS_LOG_COMPONENT_DEFINE (...);`` macro +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 @@ -330,7 +338,7 @@ Adding logging to your code is very simple: namespace ns3 { - NS_LOG_COMPONENT_DEFINE ("Ipv4L3Protocol"); + NS_LOG_COMPONENT_DEFINE("Ipv4L3Protocol"); ... This registers ``Ipv4L3Protocol`` as a log component. @@ -355,21 +363,21 @@ In case you want to add logging statements to the methods of your template class { ... private: - std::list > m_packets; //!< the items in the queue + std::list> 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 +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: + the ``NS_LOG_COMPONENT_DEFINE(...);`` macro in some module. For instance: :: template - Queue::Queue () - : NS_LOG_TEMPLATE_DEFINE ("Queue") + Queue::Queue() + : NS_LOG_TEMPLATE_DEFINE("Queue") { } @@ -380,22 +388,109 @@ In case you want to add logging statements to a static member template 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: + the ``NS_LOG_COMPONENT_DEFINE(...);`` macro in some module. For instance: :: template void - NetDeviceQueue::PacketEnqueued (Ptr > queue, - Ptr ndqi, - uint8_t txq, Ptr item) + NetDeviceQueue::PacketEnqueued(Ptr> queue, + Ptr ndqi, + uint8_t txq, Ptr item) { - NS_LOG_STATIC_TEMPLATE_DEFINE ("NetDeviceQueueInterface"); + 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(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 ******************************* @@ -418,99 +513,74 @@ or femtoseconds, the precision is expanded accordingly; e.g. for picosecond: 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`` +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 +Asserts +******* - ================ ========================== - 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 |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 macros function as output streamers, so anything you can send to - ``std::cout``, joined by ``<<`` operators, is allowed:: +The assert implementation is enabled in ``debug`` and ``default`` +builds, but disabled in all other build profiles to improve execution speed. - 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 << "!"); - } - ... - } +How to add asserts to your code +=============================== - 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. +There is only one macro one should use:: -Unconditional Logging -===================== + NS_ASSERT_MSG(condition, message); -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. +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. -Guidelines -========== +In either case if the condition evaluates to ``false`` the assert will print +an error message to ``std::cerr`` containing the following +information: -* Start every class method with ``NS_LOG_FUNCTION (this << args...);`` - This enables easy function call tracing. +* 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`` - * Except: don't log operators or explicit copy constructors, - since these will cause infinite recursion and stack overflow. +Here is an example which doesn't assert: - * For methods without arguments use the same form: - ``NS_LOG_FUNCTION (this);`` +.. sourcecode:: bash - * For static functions: + $ ./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. - * 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 (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'. +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 . +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 --git a/doc/manual/source/new-models.rst b/doc/manual/source/new-models.rst index bb6850c99..db813d2c1 100644 --- a/doc/manual/source/new-models.rst +++ b/doc/manual/source/new-models.rst @@ -62,7 +62,7 @@ So far, in our design, we have:: * \returns true if the Packet is to be considered as errored/corrupted * \param pkt Packet to apply error model to */ - bool IsCorrupt (Ptr pkt); + bool IsCorrupt(Ptr pkt); }; Note that we do not pass a const pointer, thereby allowing the function to @@ -109,16 +109,16 @@ a base class and first subclass that could be posted for initial review:: class ErrorModel { public: - ErrorModel (); - virtual ~ErrorModel (); - bool IsCorrupt (Ptr pkt); - void Reset (void); - void Enable (void); - void Disable (void); - bool IsEnabled (void) const; + ErrorModel(); + virtual ~ErrorModel(); + bool IsCorrupt(Ptr pkt); + void Reset(); + void Enable(); + void Disable(); + bool IsEnabled() const; private: - virtual bool DoCorrupt (Ptr pkt) = 0; - virtual void DoReset (void) = 0; + virtual bool DoCorrupt(Ptr pkt) = 0; + virtual void DoReset() = 0; }; enum ErrorUnit @@ -133,16 +133,16 @@ a base class and first subclass that could be posted for initial review:: 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 pkt); - virtual void DoReset (void); + virtual bool DoCorrupt(Ptr pkt); + virtual void DoReset(); }; @@ -294,19 +294,19 @@ from class Object.:: 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 @@ But we are in ``src/network/model``, so we must include it as "``#include "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 @@ file.:: 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 () - .SetGroupName ("Network") + static TypeId tid = TypeId("ns3::ErrorModel") + .SetParent() + .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 () - .SetGroupName ("Network") - .AddConstructor () + static TypeId tid = TypeId("ns3::RateErrorModel") + .SetParent() + .SetGroupName("Network") + .AddConstructor() ; 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 @@ Add Accessor :: void - PointToPointNetDevice::SetReceiveErrorModel (Ptr em) + PointToPointNetDevice::SetReceiveErrorModel(Ptr 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 ()) + PointerValue(), + MakePointerAccessor(&PointToPointNetDevice::m_receiveErrorModel), + MakePointerChecker()) Plumb Into the System +++++++++++++++++++++ :: - void PointToPointNetDevice::Receive (Ptr packet) + void PointToPointNetDevice::Receive(Ptr 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 @@ Plumb Into the System // 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 @@ Create Null Functional Script // We can obtain a handle to the NetDevice via the channel and node // pointers Ptr nd3 = PointToPointTopology::GetNetDevice - (n3, channel2); - Ptr em = Create (); - nd3->SetReceiveErrorModel (em); + (n3, channel2); + Ptr em = Create(); + nd3->SetReceiveErrorModel(em); bool - ErrorModel::DoCorrupt (Packet& p) + ErrorModel::DoCorrupt(Packet& p) { NS_LOG_FUNCTION; NS_LOG_UNCOND("Corrupt!"); @@ -514,7 +514,7 @@ Add a Subclass ************** 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 @@ We declare BasicErrorModel to be a subclass of ErrorModel as follows,:: class BasicErrorModel : public ErrorModel { public: - static TypeId GetTypeId (void); + static TypeId GetTypeId(); ... private: // Implement base class pure virtual functions - virtual bool DoCorrupt (Ptr p); - virtual bool DoReset (void); + virtual bool DoCorrupt(Ptr 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 () - .SetGroupName ("Network") - .AddConstructor () + static TypeId tid = TypeId("ns3::RateErrorModel") + .SetParent() + .SetGroupName("Network") + .AddConstructor() ... Build Core Functions and Unit Tests @@ -570,4 +570,3 @@ Assert Macros Writing Unit Tests ++++++++++++++++++ - diff --git a/doc/manual/source/new-modules.rst b/doc/manual/source/new-modules.rst index 21405f4a8..f5a24db67 100644 --- a/doc/manual/source/new-modules.rst +++ b/doc/manual/source/new-modules.rst @@ -161,10 +161,10 @@ The skeleton test suite will contain the below constructor, 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 --git a/doc/manual/source/object-model.rst b/doc/manual/source/object-model.rst index ae79e28a6..2cb661862 100644 --- a/doc/manual/source/object-model.rst +++ b/doc/manual/source/object-model.rst @@ -32,10 +32,10 @@ properties; for instance:: 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 @@ allocated using a templated Create or CreateObject method, as follows. For objects deriving from class :cpp:class:`Object`:: - Ptr device = CreateObject (); + Ptr device = CreateObject(); Please do not create such objects using ``operator new``; create them using :cpp:func:`CreateObject()` instead. @@ -141,7 +141,7 @@ For objects deriving from class :cpp:class:`SimpleRefCount`, or other objects that support usage of the smart pointer class, a templated helper function is available and recommended to be used:: - Ptr b = Create (); + Ptr b = Create(); This is simply a wrapper around operator new that correctly handles the reference counting system. @@ -190,12 +190,12 @@ node. Let's look at how some Ipv4 protocols are added to a node.:: static void AddIpv4Stack(Ptr node) { - Ptr ipv4 = CreateObject (); - ipv4->SetNode (node); - node->AggregateObject (ipv4); - Ptr ipv4Impl = CreateObject (); - ipv4Impl->SetIpv4 (ipv4); - node->AggregateObject (ipv4Impl); + Ptr ipv4 = CreateObject(); + ipv4->SetNode(node); + node->AggregateObject(ipv4); + Ptr ipv4Impl = CreateObject(); + ipv4Impl->SetIpv4(ipv4); + node->AggregateObject(ipv4Impl); } Note that the Ipv4 protocols are created using :cpp:func:`CreateObject()`. @@ -220,7 +220,7 @@ configure a default route. To do so, it must access an object within the node that has an interface to the IP forwarding configuration. It performs the following:: - Ptr ipv4 = m_node->GetObject (); + Ptr ipv4 = m_node->GetObject(); 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 @@ pattern in use in the |ns3| system. It is heavily used in the "helper" API. 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 Create (void) const; + void SetTypeId(TypeId tid); + void Set(std::string name, const AttributeValue &value); + Ptr 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 @@ For example: :: 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 = factory.Create (); - factory.Set ("SystemLoss", DoubleValue (3.0)); + Ptr object = factory.Create(); + factory.Set("SystemLoss", DoubleValue(3.0)); // Create another object with a different SystemLoss - Ptr object = factory.Create (); + Ptr object = factory.Create(); Downcasting *********** @@ -285,9 +285,9 @@ dynamic casting much more user friendly:: template Ptr - DynamicCast (Ptr const&p) + DynamicCast(Ptr const&p) { - return Ptr (dynamic_cast (PeekPointer (p))); + return Ptr(dynamic_cast(PeekPointer(p))); } DynamicCast works when the programmer has a base type pointer and is testing diff --git a/doc/manual/source/organization.rst b/doc/manual/source/organization.rst index f6aa5be6a..6e0535572 100644 --- a/doc/manual/source/organization.rst +++ b/doc/manual/source/organization.rst @@ -56,6 +56,6 @@ sockets API used by Internet applications. The next chapter covers 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 -`_). +The project maintains a manual section devoted to testing and validation +of |ns3| code (see the `tests section in the ns-3 manual +`_). diff --git a/doc/manual/source/profiling.rst b/doc/manual/source/profiling.rst index d27d3df32..69cd7e848 100644 --- a/doc/manual/source/profiling.rst +++ b/doc/manual/source/profiling.rst @@ -466,7 +466,7 @@ Performance Profilers .. _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 @@ output for a file is shown below. The line of ``---`` was inserted for clarity. .. 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 @@ and it is `not a priority`_ for GCC developers. It is recommended to use the Clang alternative. +.. _Clang : + Clang +++++ @@ -1164,6 +1166,37 @@ Precompiled headers (``-DNS3_PRECOMPILE_HEADERS=ON``) can `drastically speed up 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 @@ Or using the ns3 wrapper: ~/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 --git a/doc/manual/source/python.rst b/doc/manual/source/python.rst index 6afc2f4bd..ca7e14fee 100644 --- a/doc/manual/source/python.rst +++ b/doc/manual/source/python.rst @@ -83,7 +83,7 @@ Here is some example code that is written in Python and that runs |ns3|, which i 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 @@ module (`ns-3-dev/bindings/python/ns__init__.py`). 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 --git a/doc/manual/source/random-variables.rst b/doc/manual/source/random-variables.rst index 457974692..a42f9666a 100644 --- a/doc/manual/source/random-variables.rst +++ b/doc/manual/source/random-variables.rst @@ -117,20 +117,20 @@ The correct way to create these objects is to use the templated :: - Ptr x = CreateObject (); + Ptr x = CreateObject(); 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 @@ A class :cpp:class:`ns3::RngSeedManager` provides an API to control the seeding 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 x = CreateObject (); - Ptr y = CreateObject (); + Ptr x = CreateObject(); + Ptr y = CreateObject(); ... Which is better, setting a new seed or advancing the substream state? There is @@ -227,13 +227,13 @@ that access the next value in the substream. * \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 @@ that values can be set for them through the |ns3| attribute system. An example is in the propagation models for WifiNetDevice:: TypeId - RandomPropagationDelayModel::GetTypeId (void) + RandomPropagationDelayModel::GetTypeId() { - static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel") - .SetParent () - .SetGroupName ("Propagation") - .AddConstructor () - .AddAttribute ("Variable", - "The random variable which generates random delays (s).", - StringValue ("ns3::UniformRandomVariable"), - MakePointerAccessor (&RandomPropagationDelayModel::m_variable), - MakePointerChecker ()) + static TypeId tid = TypeId("ns3::RandomPropagationDelayModel") + .SetParent() + .SetGroupName("Propagation") + .AddConstructor() + .AddAttribute("Variable", + "The random variable which generates random delays (s).", + StringValue("ns3::UniformRandomVariable"), + MakePointerAccessor(&RandomPropagationDelayModel::m_variable), + MakePointerChecker()) ; return tid; } diff --git a/doc/manual/source/realtime.rst b/doc/manual/source/realtime.rst index d0c3db463..0f37f402d 100644 --- a/doc/manual/source/realtime.rst +++ b/doc/manual/source/realtime.rst @@ -58,8 +58,8 @@ The usage of the realtime simulator is straightforward, from a scripting 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 --git a/doc/manual/source/test-framework.rst b/doc/manual/source/test-framework.rst index 166d8ed63..79bac86c5 100644 --- a/doc/manual/source/test-framework.rst +++ b/doc/manual/source/test-framework.rst @@ -495,7 +495,7 @@ the directory where the test lives. For example, ``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 @@ So you could use the shell and do:: $ ./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 @@ arguments as needed, but basedir is the minimum needed):: (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 @@ as a ''unit'' test with the display name, ``my-test-suite-name``. 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 @@ override also the ``DoSetup`` method. 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 --git a/doc/manual/source/tracing.rst b/doc/manual/source/tracing.rst index c27069816..be791ecd7 100644 --- a/doc/manual/source/tracing.rst +++ b/doc/manual/source/tracing.rst @@ -24,7 +24,7 @@ output, as in, :: #include ... - int main () + int main() { ... std::cout << "The value of x is " << x << std::endl; @@ -139,19 +139,19 @@ made using those operators.:: class MyObject : public Object { public: - static TypeId GetTypeId (void) + static TypeId GetTypeId() { - static TypeId tid = TypeId ("MyObject") - .SetParent (Object::GetTypeId ()) - .AddConstructor () - .AddTraceSource ("MyInteger", - "An integer value to trace.", - MakeTraceSourceAccessor (&MyObject::m_myInt)) + static TypeId tid = TypeId("MyObject") + .SetParent(Object::GetTypeId()) + .AddConstructor() + .AddTraceSource("MyInteger", + "An integer value to trace.", + MakeTraceSourceAccessor(&MyObject::m_myInt)) ; return tid; } - MyObject () {} + MyObject() {} TracedValue m_myInt; }; @@ -166,7 +166,7 @@ infrastructure that overloads the operators mentioned above and drives the 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 @@ function. This function will be called whenever one of the operators of the ``TracedValue`` is executed.:: int - main (int argc, char *argv[]) + main(int argc, char *argv[]) { - Ptr myObject = CreateObject (); + Ptr myObject = CreateObject(); - myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace)); + myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace)); myObject->m_myInt = 1234; } @@ -228,13 +228,13 @@ called a *config path*. 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 @@ must be an ``Attribute`` of an ``Object``. In fact, if you had a pointer to the ``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 @@ This socket, the type of which turns out to be an ``ns3::TcpSocketImpl`` defines an attribute called "CongestionWindow" which is a ``TracedValue``. 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 @@ helper methods designed for use inside other (device) helpers. 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 @@ The class ``PcapHelperForDevice`` is a ``mixin`` provides the high level 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 nd, bool promiscuous) = 0; + virtual void EnablePcapInternal(std::string prefix, Ptr 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 nd, bool promiscuous = false, bool explicitFilename = false); + void EnablePcap(std::string prefix, Ptr 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 @@ Pcap Tracing Device Helper Methods :: - void EnablePcap (std::string prefix, Ptr 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 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 @@ mode) simply add a true parameter to any of the calls above. For example,:: Ptr 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 @@ since the net device must belong to exactly one ``Node``. For example,:: Ptr 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 @@ method. The ``Ptr`` is looked up from the name string. Again, the ```` 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 @@ example,:: 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 @@ enabled.:: 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 @@ your pcap file name will automatically pick this up and be called Finally, two of the methods shown above,:: - void EnablePcap (std::string prefix, Ptr 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 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 @@ given device, one could:: Ptr 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 @@ using ASCII tracing to a device helper class. As in the pcap case, every device must implement a single virtual method inherited from the ASCII trace ``mixin``.:: - virtual void EnableAsciiInternal (Ptr stream, std::string prefix, Ptr nd) = 0; + virtual void EnableAsciiInternal(Ptr stream, std::string prefix, Ptr 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 @@ class ``AsciiTraceHelperForDevice`` reduce to calling this single device- dependent implementation method. For example, the lowest level ASCII trace methods,:: - void EnableAscii (std::string prefix, Ptr nd); - void EnableAscii (Ptr stream, Ptr nd); + void EnableAscii(std::string prefix, Ptr nd); + void EnableAscii(Ptr stream, Ptr 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 @@ Ascii Tracing Device Helper Methods :: - void EnableAscii (std::string prefix, Ptr nd); - void EnableAscii (Ptr stream, Ptr nd); + void EnableAscii(std::string prefix, Ptr nd); + void EnableAscii(Ptr stream, Ptr nd); - void EnableAscii (std::string prefix, std::string ndName); - void EnableAscii (Ptr stream, std::string ndName); + void EnableAscii(std::string prefix, std::string ndName); + void EnableAscii(Ptr stream, std::string ndName); - void EnableAscii (std::string prefix, NetDeviceContainer d); - void EnableAscii (Ptr stream, NetDeviceContainer d); + void EnableAscii(std::string prefix, NetDeviceContainer d); + void EnableAscii(Ptr stream, NetDeviceContainer d); - void EnableAscii (std::string prefix, NodeContainer n); - void EnableAscii (Ptr stream, NodeContainer n); + void EnableAscii(std::string prefix, NodeContainer n); + void EnableAscii(Ptr stream, NodeContainer n); - void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid); - void EnableAscii (Ptr stream, uint32_t nodeid, uint32_t deviceid); + void EnableAscii(std::string prefix, uint32_t nodeid, uint32_t deviceid); + void EnableAscii(Ptr stream, uint32_t nodeid, uint32_t deviceid); - void EnableAsciiAll (std::string prefix); - void EnableAsciiAll (Ptr stream); + void EnableAsciiAll(std::string prefix); + void EnableAsciiAll(Ptr 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 @@ exactly one ``Node``. For example,:: Ptr 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 @@ refer to a single file:: Ptr nd1; Ptr nd2; ... - Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ You can enable ASCII tracing on a particular node/net-device pair by providing a string. Again, the ```` 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 stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ since the found net device must belong to exactly one ``Node``. For example,:: 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 --.tr convention. Combining all of the @@ -671,9 +671,9 @@ traces into a single file is accomplished similarly to the examples above:: NetDeviceContainer d = ...; ... - Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ enabled.:: 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 --.tr convention. Combining all of the @@ -694,14 +694,14 @@ 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.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 @@ difference will be in the method names and signatures. Different method names 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, uint32_t interface) = 0; + virtual void EnablePcapIpv4Internal(std::string prefix, Ptr 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, uint32_t interface); + void EnablePcapIpv4(std::string prefix, Ptr 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 @@ constraints. Note that just like in the device version, there are six methods:: - void EnablePcapIpv4 (std::string prefix, Ptr 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, 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 @@ the details of these methods; but to summarize ... You can enable pcap tracing on a particular protocol/interface pair by providing a ``Ptr`` and ``interface`` to an ``EnablePcap`` method. For example,:: - Ptr ipv4 = node->GetObject (); + Ptr ipv4 = node->GetObject(); ... - 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`` 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 @@ corresponding interface. For example,:: 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 @@ and tracing is enabled on the resulting pairs. For example,:: 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`` 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 @@ The class ``AsciiTraceHelperForIpv4`` adds the high level functionality for 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 stream, std::string prefix, - Ptr ipv4, uint32_t interface) = 0; + virtual void EnableAsciiIpv4Internal(Ptr stream, std::string prefix, + Ptr 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 @@ to a shared output stream. All of the public methods inherited from class dependent implementation method. For example, the lowest level ascii trace methods,:: - void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, uint32_t interface); - void EnableAsciiIpv4 (Ptr stream, Ptr ipv4, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, Ptr ipv4, uint32_t interface); + void EnableAsciiIpv4(Ptr stream, Ptr 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 @@ Ascii Tracing Device Helper Methods :: - void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, uint32_t interface); - void EnableAsciiIpv4 (Ptr stream, Ptr ipv4, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, Ptr ipv4, uint32_t interface); + void EnableAsciiIpv4(Ptr stream, Ptr ipv4, uint32_t interface); - void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface); - void EnableAsciiIpv4 (Ptr stream, std::string ipv4Name, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, std::string ipv4Name, uint32_t interface); + void EnableAsciiIpv4(Ptr stream, std::string ipv4Name, uint32_t interface); - void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c); - void EnableAsciiIpv4 (Ptr stream, Ipv4InterfaceContainer c); + void EnableAsciiIpv4(std::string prefix, Ipv4InterfaceContainer c); + void EnableAsciiIpv4(Ptr stream, Ipv4InterfaceContainer c); - void EnableAsciiIpv4 (std::string prefix, NodeContainer n); - void EnableAsciiIpv4 (Ptr stream, NodeContainer n); + void EnableAsciiIpv4(std::string prefix, NodeContainer n); + void EnableAsciiIpv4(Ptr stream, NodeContainer n); - void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid); - void EnableAsciiIpv4 (Ptr stream, uint32_t nodeid, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, uint32_t nodeid, uint32_t deviceid); + void EnableAsciiIpv4(Ptr stream, uint32_t nodeid, uint32_t interface); - void EnableAsciiIpv4All (std::string prefix); - void EnableAsciiIpv4All (Ptr stream); + void EnableAsciiIpv4All(std::string prefix); + void EnableAsciiIpv4All(Ptr 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 @@ protocol/interface pair by providing a ``Ptr`` and an ``interface`` to an Ptr 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 @@ traces sent to a single file, you can do that as well by using an object to refer to a single file. We have already something similar to this in the "cwnd" example above:: - Ptr protocol1 = node1->GetObject (); - Ptr protocol2 = node2->GetObject (); + Ptr protocol1 = node1->GetObject(); + Ptr protocol2 = node2->GetObject(); ... - Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ method. The ``Ptr`` is looked up from the name string. The ```` in 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 stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ one-to-one correspondence between each protocol and its node. For example,:: 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 -n-i.tr convention. Combining all of the @@ -1022,15 +1022,15 @@ traces into a single file is accomplished similarly to the examples above:: 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 stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ and tracing is enabled on the resulting pairs. For example,:: 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 --.tr convention. Combining all of the @@ -1050,14 +1050,14 @@ this case, the node-id is translated to a ``Ptr`` and the appropriate 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 --git a/doc/manual/source/troubleshoot.rst b/doc/manual/source/troubleshoot.rst index bb0cc5597..d3b88c836 100644 --- a/doc/manual/source/troubleshoot.rst +++ b/doc/manual/source/troubleshoot.rst @@ -28,7 +28,7 @@ Here is an example of what might occur:: 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 `_: @@ -55,7 +55,7 @@ closely, try running it under the `gdb debugger Program received signal SIGSEGV, Segmentation fault. 0x0804aa12 in main (argc=1, argv=0xbfdfefa4) at ../examples/tcp-point-to-point.cc:136 - 136 Ptr localSocket = socketFactory->CreateSocket (); + 136 Ptr localSocket = socketFactory->CreateSocket(); (gdb) p localSocket $1 = {m_ptr = 0x3c5d65} (gdb) p socketFactory @@ -73,9 +73,9 @@ Let's look around line 136 of tcp-point-to-point, as gdb suggests: .. sourcecode:: cpp - Ptr socketFactory = n2->GetObject (Tcp::iid); - Ptr localSocket = socketFactory->CreateSocket (); - localSocket->Bind (); + Ptr socketFactory = n2->GetObject(Tcp::iid); + Ptr localSocket = socketFactory->CreateSocket(); + localSocket->Bind(); The culprit here is that the return value of GetObject is not being checked and may be null. diff --git a/doc/manual/source/working-with-cmake.rst b/doc/manual/source/working-with-cmake.rst index 196336e45..99af4b5f9 100644 --- a/doc/manual/source/working-with-cmake.rst +++ b/doc/manual/source/working-with-cmake.rst @@ -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 @@ Waf-like interface for command-line users. .. _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 @@ project to work on it. 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 @@ print the configuration options: 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 @@ Now we run it for real: ... -- 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 @@ Below is a list of enabled modules and modules that cannot be built. 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 @@ build types and output executable and libraries names, which will receive a suff +==================+===================+ | DEBUG | -g | +------------------+-------------------+ -| RELEASE | -O3 -DNDEBUG | +| DEFAULT | -O2 -g -DNDEBUG | +------------------+-------------------+ | RELWITHDEBINFO | -O2 -g -DNDEBUG | +------------------+-------------------+ +| RELEASE | -O3 -DNDEBUG | ++------------------+-------------------+ | MINSIZEREL | -Os -DNDEBUG | +------------------+-------------------+ @@ -314,7 +341,7 @@ The refresh is done by running the CMake command from the CMake cache folder. 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 @@ calling the underlying build system (e.g. Ninja) directly. 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 @@ This translates to the following CMake lines: 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 @@ This translates into the following CMake: ${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 @@ scratch program produced by ``scratch/scratch-simulator.cc``, you need the follo 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 @@ The next few lines exporting variables guarantee the executable can find python ``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-`` 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 @@ automatically in the following cases: * 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 @@ the new name. Some IDEs can do this automatically through refactoring tools. ) -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 @@ associated header directories, forward compile flags and so on. 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 @@ As an example for the above, we use the Boost library 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 @@ use the following: 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 @@ The path is relative to the ``CMAKE_INSTALL_PREFIX`` (e.g. /usr). 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 @@ block by block. 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 @@ parsing phase of the compilation. 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 @@ to make sure CMake will be refreshed in case one of them changes. 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 @@ can be turned off by setting ``NS3_REEXPORT_THIRD_PARTY_LIBRARIES=OFF`` 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 @@ Next, we set the output name of the module library to n3version-modulename(optio # ... 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 @@ use them just by linking to one of the ns-3 modules. 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 @@ and append a list of object libraries that can be later used for the static ns-3 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 @@ CMakeLists.txt file, creating the examples. It also scans for python examples. # ... 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 @@ Module macros: build_lib_example 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 @@ or the src folder. # ... 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 @@ Note that both of these options are handled by the ``build_exec`` macro. 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 @@ Other project-wide compiler-dependent flags can be set during compiler checking. 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 @@ CCache and Precompiled Headers .. _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 @@ as shown below: .. 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 ()->GetId () << "] "; } diff --git a/doc/manual/source/working-with-git.rst b/doc/manual/source/working-with-git.rst index da6f4c98c..0bd276ecc 100644 --- a/doc/manual/source/working-with-git.rst +++ b/doc/manual/source/working-with-git.rst @@ -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 @@ This section of the manual provides common tips for both users and maintainers. 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 @@ Assume that you are the user *john* on GitLab.com and that you want to create a 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 @@ In this example we used the HTTPS address because in some place the git + ssh ad 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 @@ To see what ``origin`` is linking to:: $ 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 @@ Keep in sync your repository with latest ns-3-dev updates 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 @@ you should see something like:: * 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 @@ To switch between branches, remove the -b option. You should now see:: * 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 @@ You can see the history of the commits with git log. To show a particular commit 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 @@ After you have done some work on a branch, if you would like to share it with ot 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 @@ Submit work for review 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 @@ In these cases it is useful to skip the CI to save time, CI runners quota, and e $ git push -o ci.skip +GitLab CI optimization +++++++++++++++++++++++ -Porting patches from mercurial repositories to git +The GitLab Ci jobs are optimized to take advantage of caches (this is done automatically). + +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 @@ Pushing your own work 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 @@ As stated above, the project has adopted a workflow to aim for a mostly 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 @@ We change the VERSION field from '3-dev' to '3.34':: $ 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 @@ Make a commit of these files: $ 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 + + - $ git commit -m"Update availability in RELEASE_NOTES" RELEASE_NOTES +Commit this change: -Finally, add a git annotated tag: + $ git commit -m"Update availability in RELEASE_NOTES.md" RELEASE_NOTES.md + +Finally, add a Git annotated tag: :: @@ -427,7 +444,7 @@ Finally, commit the branch and delete our local release branch. $ 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 @@ We can next hand-edit these files to restore them to original state, so that:: $ 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 --git a/doc/models/Makefile b/doc/models/Makefile index 96fe4b028..f9a943ae6 100644 --- a/doc/models/Makefile +++ b/doc/models/Makefile @@ -343,6 +343,9 @@ SOURCEFIGS = \ $(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 --git a/doc/models/source/conf.py b/doc/models/source/conf.py index 743da8a87..e22f16312 100644 --- a/doc/models/source/conf.py +++ b/doc/models/source/conf.py @@ -73,9 +73,9 @@ copyright = u'2006-2019' # 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 --git a/doc/models/source/index.rst b/doc/models/source/index.rst index 0d2904078..29b6d3bba 100644 --- a/doc/models/source/index.rst +++ b/doc/models/source/index.rst @@ -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 `_: Documentation of the public APIs of the simulator -* Tutorial, Manual, and Model Library *(this document)* for the `latest release `_ and `development tree `_ -* `ns-3 wiki `_ +* Several guides that are version controlled for each release (the + `latest release `_) and + `development tree `_: + + * Tutorial + * Installation Guide + * Manual + * Model Library *(this document)* + * Contributing Guide +* `ns-3 Doxygen `_: Documentation of the public APIs of + the simulator +* `ns-3 wiki `_ This document is written in `reStructuredText `_ for `Sphinx `_ 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 @@ This document is written in `reStructuredText + href="https://www.nsnam.org/"> ns-3 Logo @@ -48,7 +48,7 @@ $extrastylesheet
  •   Home
  • + Installation
    Manual
    @@ -80,7 +83,7 @@ $extrastylesheet href="/docs/contributing/html/index.html" >Contributing
    Wiki
  • diff --git a/doc/ns3_html_theme/static/ns3_stylesheet.css b/doc/ns3_html_theme/static/ns3_stylesheet.css index f28f20f95..c20341dbc 100644 --- a/doc/ns3_html_theme/static/ns3_stylesheet.css +++ b/doc/ns3_html_theme/static/ns3_stylesheet.css @@ -4,12 +4,23 @@ body, 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 @@ p.caption { /* 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 @@ div.sphinxsidebar a { /* 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 @@ div.sphinxsidebar a { #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 @@ div.sphinxsidebar a { #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 @@ div.sphinxsidebar a { 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 @@ div.sphinxsidebar a { } #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 --git a/doc/release_steps.txt b/doc/release_steps.txt index 673066001..43ecc9d73 100644 --- a/doc/release_steps.txt +++ b/doc/release_steps.txt @@ -124,16 +124,16 @@ bake commit ba47854c (July 14, 2021). 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 --git a/doc/tutorial/source/building-topologies.rst b/doc/tutorial/source/building-topologies.rst index 2b39c08ed..d67fa726c 100644 --- a/doc/tutorial/source/building-topologies.rst +++ b/doc/tutorial/source/building-topologies.rst @@ -76,7 +76,7 @@ This is all just as it was in ``first.cc``, so there is nothing new yet. 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 @@ entirely comfortable with the following code at this point in the tutorial. 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 @@ done in ``first.cc``. :: 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 @@ object itself. :: 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 @@ the helper and a two millisecond delay on channels created by the helper. :: 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 @@ its native data type. :: 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 @@ stacks present. Just as in the ``first.cc`` script, we will use the :: 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 @@ two point-to-point devices. :: 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 @@ from network number 10.1.2.0 in this case, as seen below. :: - 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 @@ the constructor. :: - 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 @@ leftmost point-to-point node seen in the topology illustration. :: - 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 (p2pNodes.Get (0)); - clientApps.Start (Seconds (2.0)); - clientApps.Stop (Seconds (10.0)); + 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 @@ is a one-liner: :: - 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 @@ you haven't encountered yet. :: - 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 @@ the ``first.cc`` example. :: - Simulator::Run (); - Simulator::Destroy (); + Simulator::Run(); + Simulator::Destroy(); return 0; } @@ -564,9 +564,9 @@ number and device number as parameters. Go ahead and replace the :: - 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 @@ On line 110, notice the following command to enable tracing on one node .. 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 @@ argument of ``false`` indicates that you would like a non-promiscuous trace: .. 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 @@ component is defined. This should all be quite familiar by now. 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 @@ number of devices created. 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 @@ that we will connect via the point-to-point link. :: 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 @@ on the nodes and the channel between them. :: 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 @@ part of the bus (CSMA) network. :: 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 @@ selected nodes. :: 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 @@ point-to-point link as the node for the access point. :: 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 @@ helpers: :: - 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 @@ wireless medium and can communicate and interfere: :: - 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 @@ the MAC layer implementation. :: 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 @@ the WifiNetDevice objects that the helper create. :: 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 @@ create the Wi-Fi devices of these stations: :: 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 @@ requirements of the AP. :: - 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 @@ The next lines create the single AP which shares the same set of PHY-level :: 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 @@ First, we instantiate a ``MobilityHelper`` object and set some 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 @@ box. :: - 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 @@ We accomplish this by setting the mobility model for this node to be the :: - 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 @@ to install these stacks. :: 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 @@ both the STA devices and the AP on the wireless network. 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 @@ loop. :: - 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 @@ Finally, we actually run the simulation, clean up and then exit the program. :: - Simulator::Run (); - Simulator::Destroy (); + Simulator::Run(); + Simulator::Destroy(); return 0; } @@ -1357,11 +1357,11 @@ following function: :: void - CourseChange (std::string context, Ptr model) + CourseChange(std::string context, Ptr 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 @@ script just before the ``Simulator::Run`` call. :: 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 @@ At the device layer, there are device specific queues: 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 @@ Changing from the defaults * 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 --git a/doc/tutorial/source/conceptual-overview.rst b/doc/tutorial/source/conceptual-overview.rst index 6a8fbd992..a4d28ad62 100644 --- a/doc/tutorial/source/conceptual-overview.rst +++ b/doc/tutorial/source/conceptual-overview.rst @@ -125,7 +125,7 @@ versions of the ``NetDevice`` called ``CsmaNetDevice``, 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 @@ The next line of the script is the following, :: - 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 @@ The next lines of the script you will find are, :: 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 @@ to be the default value: :: - 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 @@ simulation. :: 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 @@ The next three lines in the script are, :: 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 @@ high-level perspective the next line, :: - 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 @@ final line, :: - 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 @@ following two lines of code, :: 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 @@ by the ``PointToPointHelper``, the ``Attributes`` previously set in the 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 @@ installed on our nodes. The next two lines of code will take care of that. :: 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 @@ The next two lines of code in our example script, ``first.cc``, :: 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 @@ The next line of code, :: - 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 @@ created. :: - 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 @@ Similar to many other helper objects, the ``UdpEchoServerHelper`` object 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``) 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 @@ converted for you. The two lines, :: - 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 @@ that is managed by an ``UdpEchoClientHelper``. :: - UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9); - echoClient.SetAttribute ("MaxPackets", UintegerValue (1)); - echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0))); - echoClient.SetAttribute ("PacketSize", UintegerValue (1024)); + 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)); + 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 @@ done using the global function ``Simulator::Run``. :: - 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 @@ took care of the hard part for you. The remaining lines of our first :: - Simulator::Destroy (); + Simulator::Destroy(); return 0; } @@ -764,7 +764,7 @@ not) be generated. 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 @@ in the first example program will schedule an explicit stop at 11 seconds: :: - + Simulator::Stop (Seconds (11.0)); - Simulator::Run (); - Simulator::Destroy (); + + Simulator::Stop(Seconds(11.0)); + Simulator::Run(); + Simulator::Destroy(); return 0; } diff --git a/doc/tutorial/source/conf.py b/doc/tutorial/source/conf.py index d83551bd3..0d41a74cf 100644 --- a/doc/tutorial/source/conf.py +++ b/doc/tutorial/source/conf.py @@ -71,9 +71,9 @@ copyright = u'2006-2019' # 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 --git a/doc/tutorial/source/data-collection.rst b/doc/tutorial/source/data-collection.rst index ba95465e8..4d5351efe 100644 --- a/doc/tutorial/source/data-collection.rst +++ b/doc/tutorial/source/data-collection.rst @@ -50,8 +50,8 @@ First, it has been enabled for IPv6 support with a command-line option: :: 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 @@ some of the new lines of this diff: + // 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"); + + fileHelper.WriteProbe(probeType, + + tracePath, + + "OutputBytes"); + - Simulator::Stop (Seconds (20)); - Simulator::Run (); - Simulator::Destroy (); + Simulator::Stop(Seconds(20)); + Simulator::Run(); + Simulator::Destroy(); The careful reader will have noticed, when testing the IPv6 command @@ -243,10 +243,10 @@ the GnuplotHelper object must be declared and configured: + // 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 @@ We use them here: + // 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 @@ observe: :: - .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 @@ the data out of the probed Packet object: :: TypeId - Ipv6PacketProbe::GetTypeId () + Ipv6PacketProbe::GetTypeId() { - static TypeId tid = TypeId ("ns3::Ipv6PacketProbe") - .SetParent () - .SetGroupName ("Stats") - .AddConstructor () - .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() + .SetGroupName("Stats") + .AddConstructor() + .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 @@ be seen in the filenames. Let's look at the code piece-by-piece: + 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 @@ FORMATTED is specified) with a format string such as follows: + + // 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 @@ trace source "OutputBytes" is hooked: + + // 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 @@ providing time series output has been added. The basic pattern described 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 --git a/doc/tutorial/source/getting-started.rst b/doc/tutorial/source/getting-started.rst index 054b26a91..45da02c8d 100644 --- a/doc/tutorial/source/getting-started.rst +++ b/doc/tutorial/source/getting-started.rst @@ -70,13 +70,13 @@ You may want to take this opportunity to explore the |ns3| wiki a bit, or the main web site at https://www.nsnam.org, since there is a wealth of information there. -As of the most recent |ns3| release (ns-3.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 @@ get a copy of a release by typing the following into your Linux shell $ 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 @@ release number: .. 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 @@ Downloading ns-3 Using Bake 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 @@ distribution of your choice. 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 @@ code either by inspection of the repository list or by going to the `"ns-3 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 @@ Step into the workspace directory and type the following into your shell: .. 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 @@ should yield something like: >> 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 @@ The above suggests that three sources have been downloaded. Check the $ 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 @@ native |ns3| build system, CMake, to be introduced later in this tutorial. 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 @@ and you should see something like: .. 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 @@ that a compiler warning will cause the build to fail. 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 @@ use the indicated Code Wrapper macro: .. 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 @@ current libraries from the ``build`` directory, but some users may find 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 @@ Note: the command above would fail if ``./ns3 build`` was not executed first, 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 @@ The resulting text file can then be saved with any corresponding then echo "$gitDiff" >> version.txt fi - diff --git a/doc/tutorial/source/index.rst b/doc/tutorial/source/index.rst index 1e7cc072e..042b1efd0 100644 --- a/doc/tutorial/source/index.rst +++ b/doc/tutorial/source/index.rst @@ -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 `_: Documentation of the public APIs of the simulator -* Tutorial *(this document)*, Manual, and Model Library for the `latest release `_ and `development tree `_ -* `ns-3 wiki `_ +* Several guides that are version controlled for each release (the + `latest release `_) and + `development tree `_: + + * Tutorial *(this document)* + * Installation Guide + * Manual + * Model Library + * Contributing Guide +* `ns-3 Doxygen `_: Documentation of the public APIs of + the simulator +* `ns-3 wiki `_ This document is written in `reStructuredText `_ for `Sphinx `_ 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 --git a/doc/tutorial/source/quick-start.rst b/doc/tutorial/source/quick-start.rst index 6c022d907..5ff222a45 100644 --- a/doc/tutorial/source/quick-start.rst +++ b/doc/tutorial/source/quick-start.rst @@ -64,13 +64,13 @@ Downloading the Latest Release :: - $ 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 @@ only to `cd` into ns-3-dev; the `master` branch is checked out by default. $ 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 --git a/doc/tutorial/source/tracing.rst b/doc/tutorial/source/tracing.rst index 4683bed12..12a20dcf1 100644 --- a/doc/tutorial/source/tracing.rst +++ b/doc/tutorial/source/tracing.rst @@ -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 @@ standard output, as in:: #include ... void - SomeFunction (void) + SomeFunction() { uint32_t x = SOME_INTERESTING_VALUE; ... @@ -107,16 +107,16 @@ other people as a patch to the existing core. 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, const TcpHeader& tcpHeader) + TcpSocketBase::ProcessEstablished(Ptr 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 @@ To log the SYN+ACK case, you can add a new ``NS_LOG_LOGIC`` in the /* 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, const TcpHeader& tcpHeader) + TcpSocketBase::ProcessEstablished(Ptr 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 @@ initialize this pointer to something meaningful, you need to have a 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 @@ your function:: 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 @@ simple Object we can work with. class MyObject : public Object { public: - static TypeId GetTypeId (void) + static TypeId GetTypeId() { - static TypeId tid = TypeId ("MyObject") - .SetParent (Object::GetTypeId ()) - .SetGroupName ("MyGroup") - .AddConstructor () - .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() + .AddTraceSource("MyInteger", + "An integer value to trace.", + MakeTraceSourceAccessor(&MyObject::m_myInt), + "ns3::TracedValueCallback::Int32") ; return tid; } - MyObject () {} + MyObject() {} TracedValue m_myInt; }; @@ -405,7 +405,7 @@ sink function ``traceSink`` for this TracedValue will need the signature :: - 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 @@ signature in other cases. 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 @@ We have now seen the trace source and the trace sink. What remains is 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 = CreateObject (); - myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace)); + Ptr myObject = CreateObject(); + myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace)); myObject->m_myInt = 1234; } @@ -519,10 +519,10 @@ from the mobility models of our simulation. It should now be a lot more clear to you what this function is doing:: void - CourseChange (std::string context, Ptr model) + CourseChange(std::string context, Ptr 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 @@ sink:: 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 @@ container to get a ``Ptr`` which we used to call ``GetId()``. We could have used this ``Ptr`` to call a Connect method directly:: - Ptr theObject = wifiStaNodes.Get (nWifi - 1); - theObject->TraceConnectWithoutContext ("CourseChange", MakeCallback (&CourseChange)); + Ptr 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 theObject = wifiStaNodes.Get (nWifi - 1); - theObject->TraceConnect ("CourseChange", MakeCallback (&CourseChange)); + Ptr 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 @@ to the eighth Node in the list of nodes created during the simulation 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 @@ mobility model --- which is of type ``ns3::MobilityModel``. If you are familiar with ``GetObject``, we have asked the system to do the following:: - Ptr mobilityModel = node->GetObject () + Ptr mobilityModel = node->GetObject() 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 @@ for "CourseChange" in your favorite editor. You should find :: - .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 @@ variable in ``mobility-model.h`` you will find :: - TracedCallback > m_courseChangeTrace; + TracedCallback> m_courseChangeTrace; The type declaration ``TracedCallback`` identifies ``m_courseChangeTrace`` as a special list of Callbacks that can be @@ -650,7 +650,7 @@ down to the end of the file, you will see a method defined called ``NotifyCourseChange()``:: void - MobilityModel::NotifyCourseChange (void) const + MobilityModel::NotifyCourseChange() const { m_courseChangeTrace(this); } @@ -843,8 +843,8 @@ and you may find your answer along with working code. For example, in 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 @@ an example. The example above, from same file:: static void - CourseChange (std::string context, Ptr model) + CourseChange(std::string context, Ptr model) { ... } @@ -915,7 +915,7 @@ callback will always be ``void``. The formal parameter list for a the declaration. Recall that for our current example, this is in ``mobility-model.h``, where we have previously found:: - TracedCallback > m_courseChangeTrace; + TracedCallback> 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 @@ that returns void and takes a ``Ptr``. For example:: void - CourseChange (Ptr model) + CourseChange(Ptr model) { ... } @@ -936,7 +936,7 @@ Callback function that takes a string context, then the template arguments:: void - CourseChange (std::string context, Ptr model) + CourseChange(std::string context, Ptr model) { ... } @@ -946,7 +946,7 @@ visible in your local file, you can add the keyword ``static`` and come up with:: static void - CourseChange (std::string path, Ptr model) + CourseChange(std::string path, Ptr model) { ... } @@ -971,7 +971,7 @@ The first thing we need to look at is the declaration of the trace source. Recall that this is in ``mobility-model.h``, where we have previously found:: - TracedCallback > m_courseChangeTrace; + TracedCallback> 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 @@ stuff. :: - TracedCallback::TracedCallback () - TracedCallback::ConnectWithoutContext (c ... - TracedCallback::Connect (const CallbackB ... + TracedCallback::TracedCallback() + TracedCallback::ConnectWithoutContext(c ... + TracedCallback::Connect(const CallbackB ... TracedCallback::DisconnectWithoutContext ... - TracedCallback::Disconnect (const Callba ... - TracedCallback::operator() (void) const ... - TracedCallback::operator() (T1 a1) const ... - TracedCallback::operator() (T1 a1, T2 a2 ... - TracedCallback::operator() (T1 a1, T2 a2 ... - TracedCallback::operator() (T1 a1, T2 a2 ... - TracedCallback::operator() (T1 a1, T2 a2 ... - TracedCallback::operator() (T1 a1, T2 a2 ... - TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::Disconnect(const Callba ... + TracedCallback::operator()() const ... + TracedCallback::operator()(T1 a1) const ... + TracedCallback::operator()(T1 a1, T2 a2 ... + TracedCallback::operator()(T1 a1, T2 a2 ... + TracedCallback::operator()(T1 a1, T2 a2 ... + TracedCallback::operator()(T1 a1, T2 a2 ... + TracedCallback::operator()(T1 a1, T2 a2 ... + TracedCallback::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 @@ This tells you that TracedCallback is a templated class. It has eight possible type parameters with default values. Go back and compare this with the declaration you are trying to understand:: - TracedCallback > m_courseChangeTrace; + TracedCallback> m_courseChangeTrace; The ``typename T1`` in the templated class declaration corresponds to the ``Ptr`` in the declaration above. All of the @@ -1071,8 +1071,8 @@ functions. If you scroll down, you will see a TracedCallback::ConnectWithoutContext ... { Callback 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 @@ instantiated for the declaration above, the compiler will replace void TracedCallback::ConnectWithoutContext ... cb { - Callback > cb; - cb.Assign (callback); - m_callbackList.push_back (cb); + Callback> 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 @@ We are trying to figure out what the :: - Callback > cb; + Callback> 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 @@ and takes a ``Ptr``. For example, :: void - CourseChangeCallback (Ptr model) + CourseChangeCallback(Ptr model) { ... } @@ -1141,7 +1141,7 @@ Callback function that takes a string context. This is because the ``Connect`` function will provide the context for you. You'll need:: void - CourseChangeCallback (std::string context, Ptr model) + CourseChangeCallback(std::string context, Ptr model) { ... } @@ -1151,7 +1151,7 @@ visible in your local file, you can add the keyword ``static`` and come up with:: static void - CourseChangeCallback (std::string path, Ptr model) + CourseChangeCallback(std::string path, Ptr model) { ... } @@ -1316,15 +1316,15 @@ and search for "CongestionWindow". You will find, :: - 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 @@ The two solutions to this conundrum are 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 @@ see some familiar looking code:: * Foundation, Include., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include - #include "ns3/core-module.h" - #include "ns3/network-module.h" - #include "ns3/internet-module.h" - #include "ns3/point-to-point-module.h" + #include "tutorial-app.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 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 @@ problem described above with ``Socket``. 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: + public: + TutorialApp(); + ~TutorialApp() override; - MyApp (); - virtual ~MyApp(); + /** + * Register this type. + * \return The TypeId. + */ + static TypeId GetTypeId(); - void Setup (Ptr socket, Address address, uint32_t packetSize, - uint32_t nPackets, DataRate dataRate); + /** + * 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, + Address address, + uint32_t packetSize, + uint32_t nPackets, + DataRate dataRate); - private: - virtual void StartApplication (void); - virtual void StopApplication (void); + private: + void StartApplication() override; + void StopApplication() override; - void ScheduleTx (void); - void SendPacket (void); + /// Schedule a new transmission. + void ScheduleTx(); + /// Send a packet. + void SendPacket(); - Ptr 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; + Ptr 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 @@ The most common way to start pumping events is to start an (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 @@ loops through its contained applications and calls, :: - 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 @@ node in a simulation, a pointer to that Node is added to the global 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 @@ what happens when ``Application::DoInitialize`` is called. Take a 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 @@ flow of data from the ``Application`` 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 @@ this ``Application`` in the first place. :: void - MyApp::Setup (Ptr socket, Address address, uint32_t packetSize, - uint32_t nPackets, DataRate dataRate) + TutorialApp::Setup(Ptr 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; + 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 @@ passing it to the ``Setup`` method. :: 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 @@ creating simulation events. :: 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 @@ chain of events that describes the ``Application`` behavior. :: void - MyApp::SendPacket (void) + TutorialApp::SendPacket() { - Ptr packet = Create (m_packetSize); - m_socket->Send (packet); + Ptr packet = Create(m_packetSize); + m_socket->Send(packet); - if (++m_packetsSent < m_nPackets) + if (++m_packetsSent < m_nPackets) { - ScheduleTx (); + ScheduleTx(); } } @@ -1788,12 +1818,12 @@ decides it has sent enough. :: void - MyApp::ScheduleTx (void) + TutorialApp::ScheduleTx() { - if (m_running) + if (m_running) { - Time tNext (Seconds (m_packetSize * 8 / static_cast (m_dataRate.GetBitRate ()))); - m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this); + Time tNext(Seconds(m_packetSize * 8 / static_cast(m_dataRate.GetBitRate()))); + m_sendEvent = Simulator::Schedule(tNext, &TutorialApp::SendPacket, this); } } @@ -1817,9 +1847,9 @@ indicating the congestion window has been updated. The next piece of 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 @@ demonstrate this working. :: static void - RxDrop (Ptr p) + RxDrop(Ptr 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 @@ see that this trace source refers to ``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 >``. This should tell you that the +``TracedCallback>``. This should tell you that the callback target should be a function that returns void and takes a single parameter which is a ``Ptr`` (assuming we use ``ConnectWithoutContext``) -- just what we have above. @@ -1858,20 +1888,45 @@ single parameter which is a ``Ptr`` (assuming we use 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); - PointToPointHelper pointToPoint; - pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); - pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); + // 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"))); - NetDeviceContainer devices; - devices = pointToPoint.Install (nodes); + +The following code should be very familiar to you by now:: + + NodeContainer nodes; + nodes.Create(2); + + 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 @@ into a ``Channel`` at a given *rate*. :: - Ptr em = CreateObject (); - em->SetAttribute ("ErrorRate", DoubleValue (0.00001)); - devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); + Ptr em = CreateObject(); + 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 @@ retransmissions and make our plot a little more interesting. :: - 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 @@ is commonly used in |ns3| for that purpose. :: - 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 @@ trace source. :: - Ptr ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), - TcpSocketFactory::GetTypeId ()); - ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", - MakeCallback (&CwndChange)); + Ptr 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 @@ didn't go to any trouble to create a helper to manage the ``Application`` so we are going to have to create and install it "manually". This is actually quite easy:: - Ptr app = CreateObject (); - app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps")); - nodes.Get (0)->AddApplication (app); - app->Start (Seconds (1.)); - app->Stop (Seconds (20.)); + Ptr app = CreateObject(); + 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 @@ We need to actually do the connect from the receiver point-to-point :: - 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 @@ just stop processing events at 20 seconds into the simulation. :: - Simulator::Stop (Seconds(20)); - Simulator::Run (); - Simulator::Destroy (); + Simulator::Stop(Seconds(20)); + Simulator::Run(); + Simulator::Destroy(); return 0; } @@ -2029,7 +2082,7 @@ Running ``fifth.cc`` ++++++++++++++++++++ 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 @@ of all of this work. Let's redirect that output to a file called 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 @@ information to a stream representing a file. :: static void - CwndChange (Ptr stream, uint32_t oldCwnd, uint32_t newCwnd) + CwndChange(Ptr 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 file, Ptr p) + RxDrop(Ptr file, Ptr 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 @@ the |ns3| callback system, which as you may recall, requires objects 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`` is really just carrying around a ``std::ofstream`` for you, and you can use it here @@ -2168,14 +2220,14 @@ sinks. If you look in the ``main`` function, you will find new code to do just that:: AsciiTraceHelper asciiTraceHelper; - Ptr stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd"); - ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream)); + Ptr stream = asciiTraceHelper.CreateFileStream("sixth.cwnd"); + ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow", MakeBoundCallback(&CwndChange, stream)); ... PcapHelper pcapHelper; - Ptr file = pcapHelper.CreateFile ("sixth.pcap", std::ios::out, PcapHelper::DLT_PPP); - devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file)); + Ptr 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 @@ did with the ``AsciiTraceHelper``. The line of code, :: - Ptr file = pcapHelper.CreateFile ("sixth.pcap", + Ptr 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 @@ window and a new congestion window suitable for directly importing 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 @@ tools. We did this without modifying any of the core code involved, and we did this in only 18 lines of code:: static void - CwndChange (Ptr stream, uint32_t oldCwnd, uint32_t newCwnd) + CwndChange(Ptr 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 stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd"); - ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream)); + Ptr stream = asciiTraceHelper.CreateFileStream("sixth.cwnd"); + ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow", MakeBoundCallback(&CwndChange, stream)); ... static void - RxDrop (Ptr file, Ptr p) + RxDrop(Ptr file, Ptr 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 file = pcapHelper.CreateFile ("sixth.pcap", "w", PcapHelper::DLT_PPP); - devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file)); + Ptr file = pcapHelper.CreateFile("sixth.pcap", "w", PcapHelper::DLT_PPP); + devices.Get(1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback(&RxDrop, file)); Trace Helpers ************* @@ -2365,10 +2417,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 @@ -2433,7 +2485,7 @@ class. :: - virtual void EnablePcapInternal (std::string prefix, Ptr nd, bool promiscuous, bool explicitFilename) = 0; + virtual void EnablePcapInternal(std::string prefix, Ptr 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 @@ PCAP method, :: - void EnablePcap (std::string prefix, Ptr nd, bool promiscuous = false, bool explicitFilename = false); + void EnablePcap(std::string prefix, Ptr 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 @@ Methods :: - void EnablePcap (std::string prefix, Ptr 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 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 @@ parameter to any of the calls above. For example, Ptr 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 @@ summarize ... Ptr 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 @@ summarize ... :: - 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 @@ summarize ... 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 @@ summarize ... 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 @@ summarize ... :: - 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 @@ Finally, two of the methods shown above, :: - void EnablePcap (std::string prefix, Ptr 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 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 @@ single promiscuous PCAP capture file of a specific name Ptr 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 @@ inherited from the ASCII trace ``mixin``. :: - virtual void EnableAsciiInternal (Ptr stream, - std::string prefix, - Ptr nd, - bool explicitFilename) = 0; + virtual void EnableAsciiInternal(Ptr stream, + std::string prefix, + Ptr nd, + bool explicitFilename) = 0; The signature of this method reflects the device-centric view of the @@ -2632,8 +2684,8 @@ trace methods, :: - void EnableAscii (std::string prefix, Ptr nd, bool explicitFilename = false); - void EnableAscii (Ptr stream, Ptr nd); + void EnableAscii(std::string prefix, Ptr nd, bool explicitFilename = false); + void EnableAscii(Ptr stream, Ptr nd); will call the device implementation of ``EnableAsciiInternal`` @@ -2650,23 +2702,23 @@ Methods :: - void EnableAscii (std::string prefix, Ptr nd, bool explicitFilename = false); - void EnableAscii (Ptr stream, Ptr nd); + void EnableAscii(std::string prefix, Ptr nd, bool explicitFilename = false); + void EnableAscii(Ptr stream, Ptr nd); - void EnableAscii (std::string prefix, std::string ndName, bool explicitFilename = false); - void EnableAscii (Ptr stream, std::string ndName); + void EnableAscii(std::string prefix, std::string ndName, bool explicitFilename = false); + void EnableAscii(Ptr stream, std::string ndName); - void EnableAscii (std::string prefix, NetDeviceContainer d); - void EnableAscii (Ptr stream, NetDeviceContainer d); + void EnableAscii(std::string prefix, NetDeviceContainer d); + void EnableAscii(Ptr stream, NetDeviceContainer d); - void EnableAscii (std::string prefix, NodeContainer n); - void EnableAscii (Ptr stream, NodeContainer n); + void EnableAscii(std::string prefix, NodeContainer n); + void EnableAscii(Ptr stream, NodeContainer n); - void EnableAsciiAll (std::string prefix); - void EnableAsciiAll (Ptr stream); + void EnableAsciiAll(std::string prefix); + void EnableAsciiAll(Ptr stream); - void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename); - void EnableAscii (Ptr stream, uint32_t nodeid, uint32_t deviceid); + void EnableAscii(std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename); + void EnableAscii(Ptr 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 @@ but to summarize ... Ptr 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 @@ but to summarize ... Ptr nd1; Ptr nd2; ... - Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ but to summarize ... :: - 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 @@ but to summarize ... 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 stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ but to summarize ... 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 ``--.tr`` @@ -2781,9 +2833,9 @@ but to summarize ... NetDeviceContainer d = ...; ... - Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ but to summarize ... 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 ``--.tr`` @@ -2810,7 +2862,7 @@ but to summarize ... :: - 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 @@ but to summarize ... :: - 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 @@ class ``Object``, and methods that share the same signature. :: - virtual void EnablePcapIpv4Internal (std::string prefix, - Ptr ipv4, - uint32_t interface, - bool explicitFilename) = 0; + virtual void EnablePcapIpv4Internal(std::string prefix, + Ptr 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 @@ example, the lowest level PCAP method, :: - void EnablePcapIpv4 (std::string prefix, Ptr ipv4, uint32_t interface, bool explicitFilename = false); + void EnablePcapIpv4(std::string prefix, Ptr ipv4, uint32_t interface, bool explicitFilename = false); will call the device implementation of ``EnablePcapIpv4Internal`` @@ -2920,12 +2972,12 @@ protocol and interface constraints. Note that just like in the device version, there are six methods:: - void EnablePcapIpv4 (std::string prefix, Ptr 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, 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 @@ summarize ... :: - Ptr ipv4 = node->GetObject (); + Ptr ipv4 = node->GetObject(); ... - 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 @@ summarize ... :: - 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 @@ summarize ... 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 @@ summarize ... 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`` and @@ -2990,7 +3042,7 @@ summarize ... :: - 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 @@ summarize ... :: - helper.EnablePcapIpv4All ("prefix"); + helper.EnablePcapIpv4All("prefix"); Filenames ######### @@ -3058,11 +3110,11 @@ method inherited from this class. :: - virtual void EnableAsciiIpv4Internal (Ptr stream, - std::string prefix, - Ptr ipv4, - uint32_t interface, - bool explicitFilename) = 0; + virtual void EnableAsciiIpv4Internal(Ptr stream, + std::string prefix, + Ptr 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 @@ level ASCII trace methods, :: - void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, uint32_t interface, bool explicitFilename = false); - void EnableAsciiIpv4 (Ptr stream, Ptr ipv4, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, Ptr ipv4, uint32_t interface, bool explicitFilename = false); + void EnableAsciiIpv4(Ptr stream, Ptr ipv4, uint32_t interface); will call the device implementation of ``EnableAsciiIpv4Internal`` @@ -3092,23 +3144,23 @@ Methods :: - void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, uint32_t interface, bool explicitFilename = false); - void EnableAsciiIpv4 (Ptr stream, Ptr ipv4, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, Ptr ipv4, uint32_t interface, bool explicitFilename = false); + void EnableAsciiIpv4(Ptr stream, Ptr ipv4, uint32_t interface); - void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false); - void EnableAsciiIpv4 (Ptr stream, std::string ipv4Name, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false); + void EnableAsciiIpv4(Ptr stream, std::string ipv4Name, uint32_t interface); - void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c); - void EnableAsciiIpv4 (Ptr stream, Ipv4InterfaceContainer c); + void EnableAsciiIpv4(std::string prefix, Ipv4InterfaceContainer c); + void EnableAsciiIpv4(Ptr stream, Ipv4InterfaceContainer c); - void EnableAsciiIpv4 (std::string prefix, NodeContainer n); - void EnableAsciiIpv4 (Ptr stream, NodeContainer n); + void EnableAsciiIpv4(std::string prefix, NodeContainer n); + void EnableAsciiIpv4(Ptr stream, NodeContainer n); - void EnableAsciiIpv4All (std::string prefix); - void EnableAsciiIpv4All (Ptr stream); + void EnableAsciiIpv4All(std::string prefix); + void EnableAsciiIpv4All(Ptr stream); - void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename); - void EnableAsciiIpv4 (Ptr stream, uint32_t nodeid, uint32_t interface); + void EnableAsciiIpv4(std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename); + void EnableAsciiIpv4(Ptr 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 @@ but to summarize ... Ptr 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 @@ but to summarize ... using an object to refer to a single file. We have already something similar to this in the "cwnd" example above:: - Ptr protocol1 = node1->GetObject (); - Ptr protocol2 = node2->GetObject (); + Ptr protocol1 = node1->GetObject(); + Ptr protocol2 = node2->GetObject(); ... - Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ but to summarize ... :: - 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 stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ but to summarize ... 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 -n-i.tr convention. @@ -3216,15 +3268,15 @@ but to summarize ... 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 stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); + Ptr 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 @@ but to summarize ... 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 --.tr @@ -3250,7 +3302,7 @@ but to summarize ... :: - 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 @@ but to summarize ... :: - 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 --git a/doc/tutorial/source/tweaking.rst b/doc/tutorial/source/tweaking.rst index 84a68390a..f3b0cb6c7 100644 --- a/doc/tutorial/source/tweaking.rst +++ b/doc/tutorial/source/tweaking.rst @@ -350,7 +350,7 @@ Recall that we have defined a logging component in that script: :: - 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 @@ Open ``scratch/myfirst.cc`` in your favorite editor and add the line, :: - 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 @@ in the following code, :: 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 @@ at the |ns3| ``Attribute`` system while walking through the :: 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 @@ any ``set`` operations as in the following example, ... NodeContainer nodes; - nodes.Create (2); + nodes.Create(2); PointToPointHelper pointToPoint; NetDeviceContainer devices; - devices = pointToPoint.Install (nodes); + devices = pointToPoint.Install(nodes); ... @@ -680,13 +680,13 @@ start with the following code, :: 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 @@ instead of the constant ``1`` as is shown below. :: - 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 @@ from C++ programs could be used: #include ... - int main () + int main() { ... std::cout << "The value of x is " << x << std::endl; @@ -838,12 +838,12 @@ generated by many scripts. 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 @@ The code used to enable pcap tracing is a one-liner. :: - 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 --git a/examples/energy/energy-model-example.cc b/examples/energy/energy-model-example.cc index 54eb780a6..ab42be174 100644 --- a/examples/energy/energy-model-example.cc +++ b/examples/energy/energy-model-example.cc @@ -39,7 +39,7 @@ NS_LOG_COMPONENT_DEFINE("EnergyExample"); * 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 @@ main(int argc, char* argv[]) WifiHelper wifi; if (verbose) { - wifi.EnableLogComponents(); + WifiHelper::EnableLogComponents(); } wifi.SetStandard(WIFI_STANDARD_80211b); diff --git a/examples/energy/energy-model-with-harvesting-example.cc b/examples/energy/energy-model-with-harvesting-example.cc index 67f1b2ec8..99fd3ba24 100644 --- a/examples/energy/energy-model-with-harvesting-example.cc +++ b/examples/energy/energy-model-with-harvesting-example.cc @@ -68,7 +68,7 @@ NS_LOG_COMPONENT_DEFINE("EnergyWithHarvestingExample"); * 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 @@ main(int argc, char* argv[]) WifiHelper wifi; if (verbose) { - wifi.EnableLogComponents(); + WifiHelper::EnableLogComponents(); } wifi.SetStandard(WIFI_STANDARD_80211b); diff --git a/examples/error-model/simple-error-model.cc b/examples/error-model/simple-error-model.cc index 1f260ab04..38c0c9402 100644 --- a/examples/error-model/simple-error-model.cc +++ b/examples/error-model/simple-error-model.cc @@ -158,7 +158,7 @@ main(int argc, char* argv[]) // 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 sampleList; + std::list sampleList; sampleList.push_back(11); sampleList.push_back(17); // This time, we'll explicitly create the error model we want diff --git a/examples/ipv6/CMakeLists.txt b/examples/ipv6/CMakeLists.txt index 094e36e23..ab81015ab 100644 --- a/examples/ipv6/CMakeLists.txt +++ b/examples/ipv6/CMakeLists.txt @@ -2,18 +2,18 @@ build_example( 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( ) 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( ) 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 @@ build_example( 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 --git a/examples/ipv6/fragmentation-ipv6-PMTU.cc b/examples/ipv6/fragmentation-ipv6-PMTU.cc index 4b941bfde..be64d391c 100644 --- a/examples/ipv6/fragmentation-ipv6-PMTU.cc +++ b/examples/ipv6/fragmentation-ipv6-PMTU.cc @@ -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 @@ main(int argc, char** argv) 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 n0 = CreateObject(); @@ -117,35 +120,33 @@ main(int argc, char** argv) i3.SetForwarding(0, true); i3.SetDefaultRouteInAllNodes(0); - Ipv6StaticRoutingHelper routingHelper; Ptr routingStream = Create(&std::cout); - routingHelper.PrintRoutingTableAt(Seconds(0), r1, routingStream); + Ipv6RoutingHelper::PrintRoutingTableAt(Seconds(0), r1, routingStream); + + // 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)); - /* 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; - ping6.SetLocal(i2.GetAddress(1, 1)); - ping6.SetRemote(i3.GetAddress(1, 1)); + // 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)); - 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 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 --git a/examples/ipv6/fragmentation-ipv6-two-MTU.cc b/examples/ipv6/fragmentation-ipv6-two-MTU.cc index df797b4e8..a3205c008 100644 --- a/examples/ipv6/fragmentation-ipv6-two-MTU.cc +++ b/examples/ipv6/fragmentation-ipv6-two-MTU.cc @@ -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 @@ main(int argc, char** argv) 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 n0 = CreateObject(); @@ -92,25 +95,24 @@ main(int argc, char** argv) i2.SetForwarding(0, true); i2.SetDefaultRouteInAllNodes(0); - Ipv6StaticRoutingHelper routingHelper; Ptr routingStream = Create(&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)); + 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)); - 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)); + 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 --git a/examples/ipv6/fragmentation-ipv6.cc b/examples/ipv6/fragmentation-ipv6.cc index b216a4be4..3cbd9f708 100644 --- a/examples/ipv6/fragmentation-ipv6.cc +++ b/examples/ipv6/fragmentation-ipv6.cc @@ -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 @@ main(int argc, char** argv) 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 n0 = CreateObject(); @@ -89,25 +92,24 @@ main(int argc, char** argv) i2.SetForwarding(0, true); i2.SetDefaultRouteInAllNodes(0); - Ipv6StaticRoutingHelper routingHelper; Ptr routingStream = Create(&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)); + 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)); - 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)); + 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 --git a/examples/ipv6/icmpv6-redirect.cc b/examples/ipv6/icmpv6-redirect.cc index 83d01989b..4497f5e63 100644 --- a/examples/ipv6/icmpv6-redirect.cc +++ b/examples/ipv6/icmpv6-redirect.cc @@ -114,21 +114,17 @@ main(int argc, char** argv) iic1.GetInterfaceIndex(1)); Ptr routingStream = Create(&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 --git a/examples/ipv6/loose-routing-ipv6.cc b/examples/ipv6/loose-routing-ipv6.cc index 1bd6048f3..f18876fd2 100644 --- a/examples/ipv6/loose-routing-ipv6.cc +++ b/examples/ipv6/loose-routing-ipv6.cc @@ -60,7 +60,7 @@ main(int argc, char** argv) 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 @@ main(int argc, char** argv) LogComponentEnable("NdiscCache", LOG_LEVEL_ALL); } + // LogComponentEnable("Ping", LOG_LEVEL_INFO); + NS_LOG_INFO("Create nodes."); Ptr h0 = CreateObject(); Ptr h1 = CreateObject(); @@ -164,16 +166,16 @@ main(int argc, char** argv) 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(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 --git a/examples/ipv6/ping6.cc b/examples/ipv6/ping6-example.cc similarity index 80% rename from examples/ipv6/ping6.cc rename to examples/ipv6/ping6-example.cc index 95d262e13..2a516bca3 100644 --- a/examples/ipv6/ping6.cc +++ b/examples/ipv6/ping6-example.cc @@ -43,9 +43,11 @@ 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) @@ -57,7 +59,7 @@ main(int argc, char** argv) 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); } @@ -82,25 +84,18 @@ main(int argc, char** argv) NS_LOG_INFO("Create Applications."); - /* Create a Ping6 application to send ICMPv6 echo request from node zero to - * all-nodes (ff02::1). - */ + // Create a Ping application to send ICMPv6 echo request from node zero 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()); + Ipv6Address destination = allNodes ? Ipv6Address::GetAllNodesMulticast() : i.GetAddress(1, 0); + PingHelper ping(destination); - ping6.SetAttribute("MaxPackets", UintegerValue(maxPacketCount)); - ping6.SetAttribute("Interval", TimeValue(interPacketInterval)); - ping6.SetAttribute("PacketSize", UintegerValue(packetSize)); - ApplicationContainer apps = ping6.Install(n.Get(0)); + 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)); diff --git a/examples/ipv6/radvd.cc b/examples/ipv6/radvd-one-prefix.cc similarity index 88% rename from examples/ipv6/radvd.cc rename to examples/ipv6/radvd-one-prefix.cc index 0ce2443ec..344536067 100644 --- a/examples/ipv6/radvd.cc +++ b/examples/ipv6/radvd-one-prefix.cc @@ -26,18 +26,17 @@ // // router // // - R sends RA to n0's subnet (2001:1::/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.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 "ns3/radvd.h" #include @@ -62,7 +61,7 @@ main(int argc, char** argv) 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."); @@ -129,21 +128,15 @@ main(int argc, char** argv) 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 */ + /* 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; - - /* 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)); - 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(7.0)); diff --git a/examples/ipv6/radvd-two-prefix.cc b/examples/ipv6/radvd-two-prefix.cc index 71c9a4e8a..d6087aebf 100644 --- a/examples/ipv6/radvd-two-prefix.cc +++ b/examples/ipv6/radvd-two-prefix.cc @@ -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 @@ main(int argc, char** argv) 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 @@ main(int argc, char** argv) 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 routingStream = Create(&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 --git a/examples/ipv6/wsn-ping6.cc b/examples/ipv6/wsn-ping6.cc index 23c88ebad..54d81a575 100644 --- a/examples/ipv6/wsn-ping6.cc +++ b/examples/ipv6/wsn-ping6.cc @@ -61,7 +61,7 @@ main(int argc, char** argv) 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 @@ main(int argc, char** argv) // 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 @@ main(int argc, char** argv) 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 --git a/examples/matrix-topology/matrix-topology.cc b/examples/matrix-topology/matrix-topology.cc index 6d050123c..0cfaa302b 100644 --- a/examples/matrix-topology/matrix-topology.cc +++ b/examples/matrix-topology/matrix-topology.cc @@ -117,7 +117,7 @@ main(int argc, char* argv[]) std::vector> 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 @@ readNxNMatrix(std::string adj_mat_file_name) { 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 @@ readCordinatesFile(std::string node_coordinates_file_name) std::string line; getline(node_coordinates_file, line); - if (line == "") + if (line.empty()) { NS_LOG_WARN("WARNING: Ignoring blank row: " << m); break; diff --git a/examples/realtime/realtime-udp-echo.py b/examples/realtime/realtime-udp-echo.py index dc094ee1b..a90bb6423 100644 --- a/examples/realtime/realtime-udp-echo.py +++ b/examples/realtime/realtime-udp-echo.py @@ -85,7 +85,7 @@ def main(argv): 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 --git a/examples/routing/dynamic-global-routing.cc b/examples/routing/dynamic-global-routing.cc index 9e2744726..a4484df3a 100644 --- a/examples/routing/dynamic-global-routing.cc +++ b/examples/routing/dynamic-global-routing.cc @@ -210,10 +210,9 @@ main(int argc, char* argv[]) Simulator::Schedule(Seconds(14), &Ipv4::SetUp, ipv41, ipv4ifIndex1); // Trace routing tables - Ipv4GlobalRoutingHelper g; Ptr routingStream = Create("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 --git a/examples/routing/global-routing-multi-switch-plus-router.cc b/examples/routing/global-routing-multi-switch-plus-router.cc index b76a19147..882f37dd5 100644 --- a/examples/routing/global-routing-multi-switch-plus-router.cc +++ b/examples/routing/global-routing-multi-switch-plus-router.cc @@ -332,7 +332,7 @@ main(int argc, char* argv[]) // Parse the pcapLocations string into pcapLocationVec // ---------------------------------------------------------------------- std::vector pcapLocationVec; - if (pcapLocations != "") + if (!pcapLocations.empty()) { std::stringstream sStream(pcapLocations); @@ -710,8 +710,7 @@ main(int argc, char* argv[]) Create("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 --git a/examples/routing/manet-routing-compare.cc b/examples/routing/manet-routing-compare.cc index bac711faf..a213ba278 100644 --- a/examples/routing/manet-routing-compare.cc +++ b/examples/routing/manet-routing-compare.cc @@ -102,7 +102,7 @@ class RoutingExperiment // 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 --git a/examples/routing/rip-simple-network.cc b/examples/routing/rip-simple-network.cc index 0f95ed6a8..5c4c52786 100644 --- a/examples/routing/rip-simple-network.cc +++ b/examples/routing/rip-simple-network.cc @@ -97,7 +97,7 @@ main(int argc, char** argv) 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 @@ main(int argc, char** argv) if (printRoutingTables) { - RipHelper routingHelper; - Ptr routingStream = Create(&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); + 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); - 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); + 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); - 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(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 --git a/examples/routing/ripng-simple-network.cc b/examples/routing/ripng-simple-network.cc index 64178e02f..b336af7af 100644 --- a/examples/routing/ripng-simple-network.cc +++ b/examples/routing/ripng-simple-network.cc @@ -96,12 +96,7 @@ main(int argc, char** argv) 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 @@ main(int argc, char** argv) if (printRoutingTables) { - RipNgHelper routingHelper; - Ptr routingStream = Create(&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); + 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); - 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); + 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); - 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(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 --git a/examples/routing/simple-routing-ping6.cc b/examples/routing/simple-routing-ping6.cc index 4ea404e45..0309ed3b5 100644 --- a/examples/routing/simple-routing-ping6.cc +++ b/examples/routing/simple-routing-ping6.cc @@ -98,7 +98,7 @@ main(int argc, char** argv) 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 @@ main(int argc, char** argv) 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 --git a/examples/routing/simple-routing-ping6.py b/examples/routing/simple-routing-ping6.py index aa5952e5b..f14d521d8 100644 --- a/examples/routing/simple-routing-ping6.py +++ b/examples/routing/simple-routing-ping6.py @@ -74,16 +74,17 @@ def main(argv): 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 --git a/examples/stats/wifi-example-apps.cc b/examples/stats/wifi-example-apps.cc index 9d07a8b34..ff5e1e2e7 100644 --- a/examples/stats/wifi-example-apps.cc +++ b/examples/stats/wifi-example-apps.cc @@ -37,6 +37,10 @@ using namespace ns3; NS_LOG_COMPONENT_DEFINE("WiFiDistanceApps"); +// ============================================== +// SENDER +// ============================================== + TypeId Sender::GetTypeId() { @@ -46,7 +50,7 @@ Sender::GetTypeId() .AddAttribute("PacketSize", "The size of packets transmitted.", UintegerValue(64), - MakeUintegerAccessor(&Sender::m_pktSize), + MakeUintegerAccessor(&Sender::m_packetSize), MakeUintegerChecker(1)) .AddAttribute("Destination", "Target host address.", @@ -61,7 +65,7 @@ Sender::GetTypeId() .AddAttribute("NumPackets", "Total number of packets to send.", UintegerValue(30), - MakeUintegerAccessor(&Sender::m_numPkts), + MakeUintegerAccessor(&Sender::m_nPackets), MakeUintegerChecker(1)) .AddAttribute("Interval", "Delay between transmissions.", @@ -79,7 +83,6 @@ Sender::Sender() { NS_LOG_FUNCTION_NOARGS(); m_interval = CreateObject(); - m_socket = nullptr; } Sender::~Sender() @@ -114,8 +117,6 @@ Sender::StartApplication() Simulator::Cancel(m_sendEvent); m_sendEvent = Simulator::ScheduleNow(&Sender::SendPacket, this); - - // end Sender::StartApplication } void @@ -123,16 +124,15 @@ Sender::StopApplication() { 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 = Create(m_pktSize); + Ptr packet = Create(m_packetSize); TimestampTag timestamp; timestamp.SetTimestamp(Simulator::Now()); @@ -145,18 +145,17 @@ Sender::SendPacket() // 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::GetTypeId() } Receiver::Receiver() - : m_calc(nullptr), - m_delay(nullptr) { NS_LOG_FUNCTION_NOARGS(); - m_socket = nullptr; } Receiver::~Receiver() @@ -209,8 +205,6 @@ Receiver::StartApplication() } m_socket->SetRecvCallback(MakeCallback(&Receiver::Receive, this)); - - // end Receiver::StartApplication } void @@ -222,22 +216,18 @@ Receiver::StopApplication() { m_socket->SetRecvCallback(MakeNullCallback>()); } - - // end Receiver::StopApplication } void Receiver::SetCounter(Ptr> calc) { m_calc = calc; - // end Receiver::SetCounter } void Receiver::SetDelayTracker(Ptr delay) { m_delay = delay; - // end Receiver::SetDelayTracker } void @@ -272,71 +262,5 @@ Receiver::Receive(Ptr socket) { m_calc->Update(); } - - // end receiving packets } - - // end Receiver::Receive -} - -//---------------------------------------------------------------------- -//-- TimestampTag -//------------------------------------------------------ -TypeId -TimestampTag::GetTypeId() -{ - static TypeId tid = TypeId("TimestampTag") - .SetParent() - .AddConstructor() - .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 --git a/examples/stats/wifi-example-apps.h b/examples/stats/wifi-example-apps.h index 2e8e1488e..e65a2c2c0 100644 --- a/examples/stats/wifi-example-apps.h +++ b/examples/stats/wifi-example-apps.h @@ -31,6 +31,10 @@ using namespace ns3; +// ============================================== +// SENDER +// ============================================== + /** * Sender application. */ @@ -42,6 +46,7 @@ class Sender : public Application * \return The object TypeId. */ static TypeId GetTypeId(); + Sender(); ~Sender() override; @@ -57,23 +62,24 @@ class Sender : public Application */ void SendPacket(); - uint32_t m_pktSize; //!< The packet size. - Ipv4Address m_destAddr; //!< Destination address. - uint32_t m_destPort; //!< Destination port. - Ptr 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 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 m_socket; //!< Sending socket. - EventId m_sendEvent; //!< Send packet event. + Ptr m_socket; //!< Sending socket + EventId m_sendEvent; //!< Send packet event - /// Tx TracedCallback. + /// Tx TracedCallback TracedCallback> m_txTrace; - - uint32_t m_count; //!< Number of packets sent. - - // end class Sender }; +// ============================================== +// RECEIVER +// ============================================== + /** * Receiver application. */ @@ -85,6 +91,7 @@ class Receiver : public Application * \return The object TypeId. */ static TypeId GetTypeId(); + Receiver(); ~Receiver() override; @@ -113,51 +120,9 @@ class Receiver : public Application */ void Receive(Ptr socket); - Ptr m_socket; //!< Receiving socket. - uint32_t m_port; //!< Listening port. + Ptr m_socket; //!< Receiving socket + uint32_t m_port{0}; //!< Listening port - Ptr> m_calc; //!< Counter of the number of received packets. - Ptr 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. - - // end class TimestampTag + Ptr> m_calc; //!< Counter of the number of received packets + Ptr m_delay; //!< Delay calculator }; diff --git a/examples/stats/wifi-example-sim.cc b/examples/stats/wifi-example-sim.cc index 6fbe90632..d2238254b 100644 --- a/examples/stats/wifi-example-sim.cc +++ b/examples/stats/wifi-example-sim.cc @@ -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 #include @@ -49,20 +49,16 @@ NS_LOG_COMPONENT_DEFINE("WiFiDistanceExperiment"); * 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> datac, std::string path, Ptr 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 @@ main(int argc, char* argv[]) input = sstr.str(); } - //------------------------------------------------------------ + //-------------------------------------------- //-- Create nodes and network stacks //-------------------------------------------- NS_LOG_INFO("Creating nodes."); @@ -131,7 +127,7 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) mobility.SetPositionAllocator(positionAlloc); mobility.Install(nodes); - //------------------------------------------------------------ + //-------------------------------------------- //-- Create a custom traffic source and sink //-------------------------------------------- NS_LOG_INFO("Create traffic source & sink."); @@ -159,7 +155,7 @@ main(int argc, char* argv[]) Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination", Ipv4AddressValue("192.168.0.2")); - //------------------------------------------------------------ + //-------------------------------------------- //-- Setup stats and data collection //-------------------------------------------- @@ -212,23 +208,24 @@ main(int argc, char* argv[]) receiver->SetCounter(appRx); data.AddDataCalculator(appRx); - /** - * Just to show this is here... - Ptr > test = - CreateObject >(); - test->SetKey("test-dc"); - data.AddDataCalculator(test); + // Just to show this is here... - test->Update(4); - test->Update(8); - test->Update(24); - test->Update(12); - **/ + /* + Ptr> test = + CreateObject>(); + 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 appTxPkts = CreateObject(); appTxPkts->SetKey("tx-pkt-size"); @@ -247,13 +244,13 @@ main(int argc, char* argv[]) receiver->SetDelayTracker(delayStat); data.AddDataCalculator(delayStat); - //------------------------------------------------------------ + //-------------------------------------------- //-- Run the simulation //-------------------------------------------- NS_LOG_INFO("Run Simulation."); Simulator::Run(); - //------------------------------------------------------------ + //-------------------------------------------- //-- Generate statistics output. //-------------------------------------------- @@ -286,6 +283,5 @@ main(int argc, char* argv[]) // Free any memory here at the end of this example. Simulator::Destroy(); - // end main return 0; } diff --git a/examples/tcp/tcp-linux-reno.cc b/examples/tcp/tcp-linux-reno.cc index e617b6aee..96978f00c 100644 --- a/examples/tcp/tcp-linux-reno.cc +++ b/examples/tcp/tcp-linux-reno.cc @@ -152,21 +152,11 @@ main(int argc, char* argv[]) // 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 --git a/examples/tcp/tcp-star-server.cc b/examples/tcp/tcp-star-server.cc index b406431be..8bd51e038 100644 --- a/examples/tcp/tcp-star-server.cc +++ b/examples/tcp/tcp-star-server.cc @@ -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 --git a/examples/tcp/tcp-validation.cc b/examples/tcp/tcp-validation.cc index 6c71e3751..7aed2a009 100644 --- a/examples/tcp/tcp-validation.cc +++ b/examples/tcp/tcp-validation.cc @@ -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 @@ TraceFirstCwnd(std::ofstream* ofStream, uint32_t oldCwnd, uint32_t newCwnd) { // 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(newCwnd) / 1448 << std::endl; @@ -225,7 +225,7 @@ TraceFirstCwnd(std::ofstream* ofStream, uint32_t oldCwnd, uint32_t newCwnd) 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 @@ TraceFirstDctcp(std::ofstream* ofStream, uint32_t bytesMarked, uint32_t bytesAck 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 @@ TraceSecondCwnd(std::ofstream* ofStream, uint32_t oldCwnd, uint32_t newCwnd) { // 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(newCwnd) / 1448 << std::endl; @@ -313,7 +313,7 @@ TraceSecondCwnd(std::ofstream* ofStream, uint32_t oldCwnd, uint32_t newCwnd) 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 @@ TraceSecondRtt(std::ofstream* ofStream, Time oldRtt, Time newRtt) 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 @@ TraceSecondDctcp(std::ofstream* ofStream, uint32_t bytesMarked, uint32_t bytesAc * \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 @@ TraceSecondRx(Ptr packet, const Address& address) void TraceQueueDrop(std::ofstream* ofStream, Ptr item) { - if (g_validate == "") + if (g_validate.empty()) { *ofStream << Simulator::Now().GetSeconds() << " " << std::hex << item->Hash() << std::endl; } @@ -402,7 +402,7 @@ TraceQueueDrop(std::ofstream* ofStream, Ptr item) void TraceQueueMark(std::ofstream* ofStream, Ptr 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 @@ void 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(newVal * 8) / (queueLinkRate.GetBitRate() / 1000) @@ -438,7 +438,7 @@ TraceQueueLength(std::ofstream* ofStream, DataRate queueLinkRate, uint32_t oldVa 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 @@ void 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 @@ TraceFirstThroughput(std::ofstream* ofStream, Time throughputInterval) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) //////////////////////////////////////////////////////////// // 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 = pingContainer.Get(0)->GetObject(); - v4Ping->TraceConnectWithoutContext("Rtt", MakeBoundCallback(&TracePingRtt, &pingOfStream)); + Ptr ping = pingContainer.Get(0)->GetObject(); + ping->TraceConnectWithoutContext("Rtt", MakeBoundCallback(&TracePingRtt, &pingOfStream)); pingContainer.Start(Seconds(1)); pingContainer.Stop(stopTime - Seconds(1)); @@ -1088,7 +1089,7 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); - if (g_validate == "") + if (g_validate.empty()) { pingOfStream.close(); firstTcpCwndOfStream.close(); diff --git a/examples/tcp/tcp-variants-comparison.cc b/examples/tcp/tcp-variants-comparison.cc index e987a7b08..31f0a9ef8 100644 --- a/examples/tcp/tcp-variants-comparison.cc +++ b/examples/tcp/tcp-variants-comparison.cc @@ -59,14 +59,14 @@ static std::map firstCwnd; //!< First conge static std::map firstSshThr; //!< First SlowStart threshold. static std::map firstRtt; //!< First RTT. static std::map firstRto; //!< First RTO. -static std::map> cWndStream; //!< Congstion window outut stream. +static std::map> cWndStream; //!< Congstion window output stream. static std::map> - ssThreshStream; //!< SlowStart threshold outut stream. -static std::map> rttStream; //!< RTT outut stream. -static std::map> rtoStream; //!< RTO outut stream. -static std::map> nextTxStream; //!< Next TX outut stream. -static std::map> nextRxStream; //!< Next RX outut stream. -static std::map> inFlightStream; //!< In flight outut stream. + ssThreshStream; //!< SlowStart threshold output stream. +static std::map> rttStream; //!< RTT output stream. +static std::map> rtoStream; //!< RTO output stream. +static std::map> nextTxStream; //!< Next TX output stream. +static std::map> nextRxStream; //!< Next RX output stream. +static std::map> inFlightStream; //!< In flight output stream. static std::map cWndValue; //!< congestion window value. static std::map ssThreshValue; //!< SlowStart threshold value. @@ -342,7 +342,7 @@ TraceNextRx(std::string& next_rx_seq_file_name, uint32_t nodeId) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 --git a/examples/traffic-control/queue-discs-benchmark.cc b/examples/traffic-control/queue-discs-benchmark.cc index dab856d7a..87eee9f3e 100644 --- a/examples/traffic-control/queue-discs-benchmark.cc +++ b/examples/traffic-control/queue-discs-benchmark.cc @@ -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 @@ using namespace ns3; 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 @@ LimitsTrace(Ptr stream, uint32_t oldVal, uint32_t newVal) /** * 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 @@ BytesInQueueTrace(Ptr stream, uint32_t oldVal, uint32_t new * 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 @@ GoodputSampling(ApplicationContainer app, Ptr stream, float * \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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) FlowMonitorHelper flowHelper; flowMonitor = flowHelper.InstallAll(); + accessLink.EnablePcapAll("queue"); + Simulator::Stop(Seconds(stopTime)); Simulator::Run(); diff --git a/examples/tutorial/first.py b/examples/tutorial/first.py index bfa194711..d3138bec8 100644 --- a/examples/tutorial/first.py +++ b/examples/tutorial/first.py @@ -49,7 +49,7 @@ serverApps = echoServer.Install(nodes.Get(1)) 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 --git a/examples/tutorial/second.py b/examples/tutorial/second.py index 0eb60e4e5..e3ccaec11 100644 --- a/examples/tutorial/second.py +++ b/examples/tutorial/second.py @@ -76,7 +76,7 @@ serverApps = echoServer.Install(csmaNodes.Get(nCsma.value)) 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 --git a/examples/tutorial/seventh.cc b/examples/tutorial/seventh.cc index 0f35e8271..f4b61be1e 100644 --- a/examples/tutorial/seventh.cc +++ b/examples/tutorial/seventh.cc @@ -69,7 +69,7 @@ NS_LOG_COMPONENT_DEFINE("SeventhScriptExample"); /** * 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 @@ CwndChange(Ptr stream, uint32_t oldCwnd, uint32_t newCwnd) /** * Rx drop callback * - * \param file The ouput PCAP file. + * \param file The output PCAP file. * \param p The dropped packet. */ static void diff --git a/examples/tutorial/sixth.cc b/examples/tutorial/sixth.cc index 4fa7629c9..85f49ee12 100644 --- a/examples/tutorial/sixth.cc +++ b/examples/tutorial/sixth.cc @@ -65,7 +65,7 @@ NS_LOG_COMPONENT_DEFINE("SixthScriptExample"); /** * 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 @@ CwndChange(Ptr stream, uint32_t oldCwnd, uint32_t newCwnd) /** * Rx drop callback * - * \param file The ouput PCAP file. + * \param file The output PCAP file. * \param p The dropped packet. */ static void diff --git a/examples/tutorial/third.py b/examples/tutorial/third.py index d24402371..6fad4e55e 100644 --- a/examples/tutorial/third.py +++ b/examples/tutorial/third.py @@ -126,7 +126,7 @@ serverApps = echoServer.Install(csmaNodes.Get(nCsma.value)) 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 --git a/examples/tutorial/tutorial-app.h b/examples/tutorial/tutorial-app.h index e6352d2cf..7ef0af004 100644 --- a/examples/tutorial/tutorial-app.h +++ b/examples/tutorial/tutorial-app.h @@ -46,7 +46,7 @@ class TutorialApp : public Application * \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, Address address, @@ -63,14 +63,14 @@ class TutorialApp : public Application /// Send a packet. void SendPacket(); - Ptr m_socket; //!< The tranmission socket. + Ptr 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 --git a/examples/wireless/CMakeLists.txt b/examples/wireless/CMakeLists.txt index 8d78a00fa..b6da631c6 100644 --- a/examples/wireless/CMakeLists.txt +++ b/examples/wireless/CMakeLists.txt @@ -291,3 +291,10 @@ build_example( 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 --git a/examples/wireless/examples-to-run.py b/examples/wireless/examples-to-run.py index 5e2a7ffe1..f7fdba6e8 100755 --- a/examples/wireless/examples-to-run.py +++ b/examples/wireless/examples-to-run.py @@ -39,11 +39,11 @@ cpp_examples = [ ("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 @@ cpp_examples = [ ("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 --git a/examples/wireless/mixed-wired-wireless.py b/examples/wireless/mixed-wired-wireless.py index c17f39d1a..daa72198b 100644 --- a/examples/wireless/mixed-wired-wireless.py +++ b/examples/wireless/mixed-wired-wireless.py @@ -320,15 +320,14 @@ def main(argv): # 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 --git a/examples/wireless/wifi-80211n-mimo.cc b/examples/wireless/wifi-80211n-mimo.cc index 473d73977..09170539e 100644 --- a/examples/wireless/wifi-80211n-mimo.cc +++ b/examples/wireless/wifi-80211n-mimo.cc @@ -62,42 +62,12 @@ main(int argc, char* argv[]) { std::ofstream file("80211n-mimo-throughput.plt"); - // clang-format off - std::vector 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 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 --git a/examples/wireless/wifi-adhoc.cc b/examples/wireless/wifi-adhoc.cc index ca38694c3..e65724382 100644 --- a/examples/wireless/wifi-adhoc.cc +++ b/examples/wireless/wifi-adhoc.cc @@ -67,7 +67,7 @@ class Experiment private: /** * Receive a packet. - * \param socket The recieving socket. + * \param socket The receiving socket. */ void ReceivePacket(Ptr socket); /** diff --git a/examples/wireless/wifi-ap.py b/examples/wireless/wifi-ap.py index 58b4fbb0f..9139e9a6c 100644 --- a/examples/wireless/wifi-ap.py +++ b/examples/wireless/wifi-ap.py @@ -128,8 +128,7 @@ def main(argv): 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 --git a/examples/wireless/wifi-clear-channel-cmu.cc b/examples/wireless/wifi-clear-channel-cmu.cc index c564f731e..4b245270f 100644 --- a/examples/wireless/wifi-clear-channel-cmu.cc +++ b/examples/wireless/wifi-clear-channel-cmu.cc @@ -64,7 +64,7 @@ class Experiment private: /** * Receive a packet. - * \param socket The recieving socket. + * \param socket The receiving socket. */ void ReceivePacket(Ptr socket); /** diff --git a/examples/wireless/wifi-eht-network.cc b/examples/wireless/wifi-eht-network.cc new file mode 100644 index 000000000..0841d2e20 --- /dev/null +++ b/examples/wireless/wifi-eht-network.cc @@ -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 + */ + +#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 +#include +#include + +// 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 +GetRxBytes(bool udp, const ApplicationContainer& serverApp, uint32_t payloadSize) +{ + std::vector rxBytes(serverApp.GetN(), 0); + if (udp) + { + for (uint32_t i = 0; i < serverApp.GetN(); i++) + { + rxBytes[i] = payloadSize * DynamicCast(serverApp.Get(i))->GetReceived(); + } + } + else + { + for (uint32_t i = 0; i < serverApp.GetN(); i++) + { + rxBytes[i] = DynamicCast(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& 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 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 spectrumChannel = + CreateObject(); + + Ptr lossModel = + CreateObject(); + 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 positionAlloc = CreateObject(); + + 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 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 --git a/examples/wireless/wifi-he-network.cc b/examples/wireless/wifi-he-network.cc index bc821d12b..06d89538b 100644 --- a/examples/wireless/wifi-he-network.cc +++ b/examples/wireless/wifi-he-network.cc @@ -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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) */ Ptr spectrumChannel = CreateObject(); + + Ptr lossModel = + CreateObject(); + spectrumChannel->AddPropagationLossModel(lossModel); + SpectrumWifiPhyHelper phy; phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO); phy.SetChannel(spectrumChannel); diff --git a/examples/wireless/wifi-hidden-terminal.cc b/examples/wireless/wifi-hidden-terminal.cc index fe4b80c24..6adfa9249 100644 --- a/examples/wireless/wifi-hidden-terminal.cc +++ b/examples/wireless/wifi-hidden-terminal.cc @@ -49,7 +49,7 @@ using namespace ns3; /** * 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 --git a/examples/wireless/wifi-ht-network.cc b/examples/wireless/wifi-ht-network.cc index f68d8043b..20eec4c70 100644 --- a/examples/wireless/wifi-ht-network.cc +++ b/examples/wireless/wifi-ht-network.cc @@ -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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 --git a/examples/wireless/wifi-multirate.cc b/examples/wireless/wifi-multirate.cc index b68a26035..0d8f0a65d 100644 --- a/examples/wireless/wifi-multirate.cc +++ b/examples/wireless/wifi-multirate.cc @@ -114,7 +114,7 @@ class Experiment * * \return true if routing is enabled. */ - bool IsRouting() + bool IsRouting() const { return (m_enableRouting == 1) ? 1 : 0; } @@ -124,7 +124,7 @@ class Experiment * * \return true if mobility is enabled. */ - bool IsMobility() + bool IsMobility() const { return (m_enableMobility == 1) ? 1 : 0; } @@ -134,7 +134,7 @@ class Experiment * * \return the scenario number. */ - uint32_t GetScenario() + uint32_t GetScenario() const { return m_scenario; } @@ -144,7 +144,7 @@ class Experiment * * \return the RTS Threshold. */ - std::string GetRtsThreshold() + std::string GetRtsThreshold() const { return m_rtsThreshold; } @@ -154,7 +154,7 @@ class Experiment * * \return the Output File Name. */ - std::string GetOutputFileName() + std::string GetOutputFileName() const { return m_outputFileName; } @@ -164,7 +164,7 @@ class Experiment * * \return the Rate Manager. */ - std::string GetRateManager() + std::string GetRateManager() const { return m_rateManager; } diff --git a/examples/wireless/wifi-ofdm-eht-validation.cc b/examples/wireless/wifi-ofdm-eht-validation.cc index 267ef7469..266ba1d11 100644 --- a/examples/wireless/wifi-ofdm-eht-validation.cc +++ b/examples/wireless/wifi-ofdm-eht-validation.cc @@ -124,25 +124,30 @@ main(int argc, char* argv[]) 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 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 --git a/examples/wireless/wifi-power-adaptation-interference.cc b/examples/wireless/wifi-power-adaptation-interference.cc index b7a8a5baf..8ea2446b3 100644 --- a/examples/wireless/wifi-power-adaptation-interference.cc +++ b/examples/wireless/wifi-power-adaptation-interference.cc @@ -189,7 +189,7 @@ class NodeStatistics * * \return the busy time. */ - double GetBusyTime(); + double GetBusyTime() const; private: /// Time, DataRate pair vector. @@ -416,7 +416,7 @@ NodeStatistics::GetTxDatafile() } double -NodeStatistics::GetBusyTime() +NodeStatistics::GetBusyTime() const { return m_totalBusyTime + m_totalRxTime; } diff --git a/examples/wireless/wifi-simple-adhoc-grid.cc b/examples/wireless/wifi-simple-adhoc-grid.cc index e63530dd4..d722ed193 100644 --- a/examples/wireless/wifi-simple-adhoc-grid.cc +++ b/examples/wireless/wifi-simple-adhoc-grid.cc @@ -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 @@ int 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 @@ main(int argc, char* argv[]) WifiHelper wifi; if (verbose) { - wifi.EnableLogComponents(); // Turn on all Wifi logging + WifiHelper::EnableLogComponents(); // Turn on all Wifi logging } YansWifiPhyHelper wifiPhy; @@ -246,10 +263,10 @@ main(int argc, char* argv[]) // Trace routing tables Ptr routingStream = Create("wifi-simple-adhoc-grid.routes", std::ios::out); - olsr.PrintRoutingTableAllEvery(Seconds(2), routingStream); + Ipv4RoutingHelper::PrintRoutingTableAllEvery(Seconds(2), routingStream); Ptr neighborStream = Create("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 --git a/examples/wireless/wifi-simple-adhoc.cc b/examples/wireless/wifi-simple-adhoc.cc index a7f880e47..b24b7d7c4 100644 --- a/examples/wireless/wifi-simple-adhoc.cc +++ b/examples/wireless/wifi-simple-adhoc.cc @@ -137,7 +137,7 @@ main(int argc, char* argv[]) WifiHelper wifi; if (verbose) { - wifi.EnableLogComponents(); // Turn on all Wifi logging + WifiHelper::EnableLogComponents(); // Turn on all Wifi logging } wifi.SetStandard(WIFI_STANDARD_80211b); diff --git a/examples/wireless/wifi-simple-infra.cc b/examples/wireless/wifi-simple-infra.cc index c66c4a915..208203bf9 100644 --- a/examples/wireless/wifi-simple-infra.cc +++ b/examples/wireless/wifi-simple-infra.cc @@ -168,7 +168,7 @@ main(int argc, char* argv[]) WifiHelper wifi; if (verbose) { - wifi.EnableLogComponents(); // Turn on all Wifi logging + WifiHelper::EnableLogComponents(); // Turn on all Wifi logging } wifi.SetStandard(WIFI_STANDARD_80211b); diff --git a/examples/wireless/wifi-simple-interference.cc b/examples/wireless/wifi-simple-interference.cc index 575efbd8f..cdc054298 100644 --- a/examples/wireless/wifi-simple-interference.cc +++ b/examples/wireless/wifi-simple-interference.cc @@ -198,7 +198,7 @@ main(int argc, char* argv[]) WifiHelper wifi; if (verbose) { - wifi.EnableLogComponents(); // Turn on all Wifi logging + WifiHelper::EnableLogComponents(); // Turn on all Wifi logging } wifi.SetStandard(WIFI_STANDARD_80211b); diff --git a/examples/wireless/wifi-sleep.cc b/examples/wireless/wifi-sleep.cc index cc3a5260e..857ea2039 100644 --- a/examples/wireless/wifi-sleep.cc +++ b/examples/wireless/wifi-sleep.cc @@ -147,7 +147,7 @@ main(int argc, char* argv[]) WifiHelper wifi; if (verbose) { - wifi.EnableLogComponents(); // Turn on all Wifi logging + WifiHelper::EnableLogComponents(); // Turn on all Wifi logging } wifi.SetStandard(WIFI_STANDARD_80211b); diff --git a/examples/wireless/wifi-spatial-reuse.cc b/examples/wireless/wifi-spatial-reuse.cc index 9bd4ad4a5..26aa40654 100644 --- a/examples/wireless/wifi-spatial-reuse.cc +++ b/examples/wireless/wifi-spatial-reuse.cc @@ -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 --git a/examples/wireless/wifi-spectrum-per-example.cc b/examples/wireless/wifi-spectrum-per-example.cc index aed4d655f..15f8d1a29 100644 --- a/examples/wireless/wifi-spectrum-per-example.cc +++ b/examples/wireless/wifi-spectrum-per-example.cc @@ -102,7 +102,7 @@ uint32_t g_samples; //!< Number of samples * 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 --git a/examples/wireless/wifi-spectrum-per-interference.cc b/examples/wireless/wifi-spectrum-per-interference.cc index 0964f3fec..61f21dc67 100644 --- a/examples/wireless/wifi-spectrum-per-interference.cc +++ b/examples/wireless/wifi-spectrum-per-interference.cc @@ -108,7 +108,7 @@ uint32_t g_samples; //!< Number of samples * 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 --git a/examples/wireless/wifi-tcp.cc b/examples/wireless/wifi-tcp.cc index 125e8f476..2cd2c0851 100644 --- a/examples/wireless/wifi-tcp.cc +++ b/examples/wireless/wifi-tcp.cc @@ -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 @@ Ptr sink; //!< Pointer to the packet sink application 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 @@ main(int argc, char* argv[]) 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 --git a/examples/wireless/wifi-vht-network.cc b/examples/wireless/wifi-vht-network.cc index 1167cebbf..0e48233f7 100644 --- a/examples/wireless/wifi-vht-network.cc +++ b/examples/wireless/wifi-vht-network.cc @@ -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 @@ main(int argc, char* argv[]) 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 --git a/ns3 b/ns3 index 8f399bd62..f5a8e2817 100755 --- a/ns3 +++ b/ns3 @@ -81,9 +81,16 @@ def add_argument_to_subparsers(parsers: list, 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 @@ def parse_args(argv): # 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 @@ def parse_args(argv): ("build-version", "embedding git changes as a build version during build"), ("clang-tidy", "clang-tidy static analysis"), ("dpdk", "the fd-net-device DPDK features"), + ("eigen", "Eigen3 library support"), ("examples", "the ns-3 examples"), ("gcov", "code coverage analysis"), ("gsl", "GNU Scientific Library (GSL) features"), @@ -143,6 +149,7 @@ def parse_args(argv): ("monolib", "a single shared library with all ns-3 modules"), ("mpi", "the MPI support for distributed simulation"), ("mtp", "Multithreading support for high speed parallel 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"), @@ -207,6 +214,9 @@ def parse_args(argv): 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) @@ -286,7 +296,7 @@ def parse_args(argv): 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") @@ -326,6 +336,15 @@ def parse_args(argv): 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("") @@ -347,15 +366,6 @@ def parse_args(argv): 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: @@ -364,7 +374,7 @@ def parse_args(argv): 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) @@ -418,28 +428,83 @@ def print_and_buffer(message): 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): @@ -576,6 +641,7 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener ("COVERAGE", "gcov"), ("DES_METRICS", "des_metrics"), ("DPDK", "dpdk"), + ("EIGEN", "eigen"), ("ENABLE_BUILD_VERSION", "build_version"), ("ENABLE_SUDO", "sudo"), ("EXAMPLES", "examples"), @@ -585,6 +651,7 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener ("MONOLIB", "monolib"), ("MPI", "mpi"), ("MTP", "mtp"), + ("NINJA_TRACING", "ninja_tracing"), ("PRECOMPILE_HEADERS", "precompiled_headers"), ("PYTHON_BINDINGS", "python_bindings"), ("SANITIZE", "sanitizers"), @@ -597,12 +664,17 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener 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) @@ -669,7 +741,8 @@ def update_scratches_list(current_cmake_cache_folder): 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) @@ -770,7 +843,8 @@ def parse_version(version_str): 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) @@ -783,12 +857,12 @@ def cmake_check_version(): 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, @@ -976,6 +1050,7 @@ def build_step(args, "sphinx", "manual", "models", + "ninjaTrace", "timeTraceReport", "tutorial", "contributing", @@ -1100,8 +1175,8 @@ def run_step(args, target_to_run, target_args): 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? @@ -1329,7 +1404,8 @@ def main(): 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 @@ -1358,6 +1434,13 @@ def main(): # 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'] @@ -1376,7 +1459,7 @@ def main(): 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 --git a/scratch/nested-subdir/CMakeLists.txt b/scratch/nested-subdir/CMakeLists.txt new file mode 100644 index 000000000..c3fe137b0 --- /dev/null +++ b/scratch/nested-subdir/CMakeLists.txt @@ -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 --git a/src/nix-vector-routing/model/ipv4-nix-vector-routing.h b/scratch/nested-subdir/lib/scratch-nested-subdir-library-header.h similarity index 60% rename from src/nix-vector-routing/model/ipv4-nix-vector-routing.h rename to scratch/nested-subdir/lib/scratch-nested-subdir-library-header.h index 9dd617fc5..55a765910 100644 --- a/src/nix-vector-routing/model/ipv4-nix-vector-routing.h +++ b/scratch/nested-subdir/lib/scratch-nested-subdir-library-header.h @@ -1,6 +1,4 @@ /* - * 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; @@ -13,12 +11,22 @@ * 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 */ -#pragma GCC warning \ - "NS_DEPRECATED_3_35 - Deprecated header, please use nix-vector-routing.h instead" -#include "nix-vector-routing.h" + +// 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 + +namespace ns3 +{ + +/** + * Get a message from the subdir. + * + * \return The message from the subdir + */ +std::string ScratchNestedSubdirGetMessage(); + +} // namespace ns3 diff --git a/src/nix-vector-routing/helper/ipv4-nix-vector-helper.h b/scratch/nested-subdir/lib/scratch-nested-subdir-library-source.cc similarity index 64% rename from src/nix-vector-routing/helper/ipv4-nix-vector-helper.h rename to scratch/nested-subdir/lib/scratch-nested-subdir-library-source.cc index 94e4afefe..186fb2cd2 100644 --- a/src/nix-vector-routing/helper/ipv4-nix-vector-helper.h +++ b/scratch/nested-subdir/lib/scratch-nested-subdir-library-source.cc @@ -1,6 +1,4 @@ /* - * 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; @@ -13,11 +11,20 @@ * 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 */ -#pragma GCC warning "NS_DEPRECATED_3_35 - Deprecated header, please use nix-vector-helper.h instead" -#include "nix-vector-helper.h" + +// 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 --git a/scratch/nested-subdir/scratch-nested-subdir-executable.cc b/scratch/nested-subdir/scratch-nested-subdir-executable.cc new file mode 100644 index 000000000..1e258edb3 --- /dev/null +++ b/scratch/nested-subdir/scratch-nested-subdir-executable.cc @@ -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 + +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 --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc571b937..f2d041ee4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -87,3 +87,7 @@ if(${NS3_MONOLIB}) add_dependencies(timeTraceReport ${lib-ns3-monolib}) endif() endif() + +if(${NS3_FETCH_OPTIONAL_COMPONENTS}) + add_dependency_to_optional_modules_dependencies() +endif() diff --git a/src/antenna/model/angles.cc b/src/antenna/model/angles.cc index 7b4bf5122..18e7e86f8 100644 --- a/src/antenna/model/angles.cc +++ b/src/antenna/model/angles.cc @@ -132,7 +132,7 @@ operator<<(std::ostream& os, const Angles& a) double incl; std::string unit; - if (a.m_printDeg) + if (Angles::m_printDeg) { azim = RadiansToDegrees(a.m_azimuth); incl = RadiansToDegrees(a.m_inclination); diff --git a/src/antenna/model/angles.h b/src/antenna/model/angles.h index 94dcfe283..e93e5a9d0 100644 --- a/src/antenna/model/angles.h +++ b/src/antenna/model/angles.h @@ -120,7 +120,7 @@ class Angles /** * 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 @@ class Angles * * 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 --git a/src/antenna/model/phased-array-model.cc b/src/antenna/model/phased-array-model.cc index 1bf4aeadd..f2af5b859 100644 --- a/src/antenna/model/phased-array-model.cc +++ b/src/antenna/model/phased-array-model.cc @@ -36,7 +36,7 @@ NS_OBJECT_ENSURE_REGISTERED(PhasedArrayModel); 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() PhasedArrayModel::~PhasedArrayModel() { - m_beamformingVector.clear(); } TypeId @@ -85,8 +84,8 @@ void 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 @@ PhasedArrayModel::GetBeamformingVector() const 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::GetBeamformingVector(Angles a) const 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 --git a/src/antenna/model/phased-array-model.h b/src/antenna/model/phased-array-model.h index 544608902..563bb8b3c 100644 --- a/src/antenna/model/phased-array-model.h +++ b/src/antenna/model/phased-array-model.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -51,8 +52,24 @@ class PhasedArrayModel : public Object */ static TypeId GetTypeId(); - typedef std::vector> - 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 @@ class PhasedArrayModel : public Object * 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 --git a/src/antenna/model/three-gpp-antenna-model.h b/src/antenna/model/three-gpp-antenna-model.h index aa2b89e44..78fd144be 100644 --- a/src/antenna/model/three-gpp-antenna-model.h +++ b/src/antenna/model/three-gpp-antenna-model.h @@ -25,11 +25,9 @@ namespace ns3 { /** - * * \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 @@ class ThreeGppAntennaModel : public AntennaModel 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 --git a/src/antenna/model/uniform-planar-array.cc b/src/antenna/model/uniform-planar-array.cc index abd187838..0f5eafa6d 100644 --- a/src/antenna/model/uniform-planar-array.cc +++ b/src/antenna/model/uniform-planar-array.cc @@ -249,7 +249,7 @@ UniformPlanarArray::GetElementLocation(uint64_t index) const return loc; } -uint64_t +size_t UniformPlanarArray::GetNumberOfElements() const { return m_numRows * m_numColumns; diff --git a/src/antenna/model/uniform-planar-array.h b/src/antenna/model/uniform-planar-array.h index 02e79551f..70ee3ee0d 100644 --- a/src/antenna/model/uniform-planar-array.h +++ b/src/antenna/model/uniform-planar-array.h @@ -81,7 +81,7 @@ class UniformPlanarArray : public PhasedArrayModel * Returns the number of antenna elements * \return the number of antenna elements */ - uint64_t GetNumberOfElements() const override; + size_t GetNumberOfElements() const override; private: /** diff --git a/src/antenna/test/test-cosine-antenna.cc b/src/antenna/test/test-cosine-antenna.cc index 7a14d330c..feabf24b4 100644 --- a/src/antenna/test/test-cosine-antenna.cc +++ b/src/antenna/test/test-cosine-antenna.cc @@ -66,7 +66,7 @@ class CosineAntennaModelTestCase : public TestCase * \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 --git a/src/antenna/test/test-degrees-radians.cc b/src/antenna/test/test-degrees-radians.cc index f693698cf..083d0c691 100644 --- a/src/antenna/test/test-degrees-radians.cc +++ b/src/antenna/test/test-degrees-radians.cc @@ -129,7 +129,7 @@ RadiansToDegreesTestCase::DoRun() /** * \ingroup tests * - * \brief TestSuite: degree to radians (and viceversa) conversions + * \brief TestSuite: degree to radians (and vice-versa) conversions */ class DegreesRadiansTestSuite : public TestSuite { diff --git a/src/antenna/test/test-isotropic-antenna.cc b/src/antenna/test/test-isotropic-antenna.cc index 28c04e5be..5dd45198a 100644 --- a/src/antenna/test/test-isotropic-antenna.cc +++ b/src/antenna/test/test-isotropic-antenna.cc @@ -45,7 +45,7 @@ class IsotropicAntennaModelTestCase : public TestCase /** * Constructor * \param a Antenna angle - * \param expectedGainDb Expeted antenna gain + * \param expectedGainDb Expected antenna gain */ IsotropicAntennaModelTestCase(Angles a, double expectedGainDb); diff --git a/src/antenna/test/test-parabolic-antenna.cc b/src/antenna/test/test-parabolic-antenna.cc index 431345c86..eddf0de61 100644 --- a/src/antenna/test/test-parabolic-antenna.cc +++ b/src/antenna/test/test-parabolic-antenna.cc @@ -66,7 +66,7 @@ class ParabolicAntennaModelTestCase : public TestCase * \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 --git a/src/antenna/test/test-uniform-planar-array.cc b/src/antenna/test/test-uniform-planar-array.cc index 83b24cf0e..22601057a 100644 --- a/src/antenna/test/test-uniform-planar-array.cc +++ b/src/antenna/test/test-uniform-planar-array.cc @@ -153,14 +153,16 @@ UniformPlanarArrayTestCase::ComputeGain(Ptr a) { // 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 fp = a->GetElementFieldPattern(m_direction); // scalar product dot (sv, bf) std::complex 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 --git a/src/aodv/examples/aodv.cc b/src/aodv/examples/aodv.cc index 361cba2e1..0d32222d4 100644 --- a/src/aodv/examples/aodv.cc +++ b/src/aodv/examples/aodv.cc @@ -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 @@ -46,8 +46,8 @@ using namespace ns3; * * 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 @@ AodvExample::InstallInternetStack() { Ptr routingStream = Create("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 --git a/src/aodv/model/aodv-id-cache.cc b/src/aodv/model/aodv-id-cache.cc index a1e632cc3..b8b9a075b 100644 --- a/src/aodv/model/aodv-id-cache.cc +++ b/src/aodv/model/aodv-id-cache.cc @@ -43,7 +43,7 @@ IdCache::IsDuplicate(Ipv4Address addr, uint32_t id) 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 --git a/src/aodv/model/aodv-id-cache.h b/src/aodv/model/aodv-id-cache.h index 8fd3ecb4b..f2699fab6 100644 --- a/src/aodv/model/aodv-id-cache.h +++ b/src/aodv/model/aodv-id-cache.h @@ -79,7 +79,7 @@ class IdCache /** * Return lifetime for existing entries in cache - * \returns thhe lifetime + * \returns the lifetime */ Time GetLifeTime() const { diff --git a/src/aodv/model/aodv-packet.h b/src/aodv/model/aodv-packet.h index 7c9ff69f1..47ad243cf 100644 --- a/src/aodv/model/aodv-packet.h +++ b/src/aodv/model/aodv-packet.h @@ -108,6 +108,7 @@ class TypeHeader : public Header /** * \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 --git a/src/aodv/model/aodv-routing-protocol.h b/src/aodv/model/aodv-routing-protocol.h index 0dc2efc1d..637b1b890 100644 --- a/src/aodv/model/aodv-routing-protocol.h +++ b/src/aodv/model/aodv-routing-protocol.h @@ -260,7 +260,6 @@ class RoutingProtocol : public Ipv4RoutingProtocol ///< 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 m_ipv4; @@ -377,8 +376,10 @@ class RoutingProtocol : public Ipv4RoutingProtocol */ Ptr LoopbackRoute(const Ipv4Header& header, Ptr oif) const; - ///\name Receive control packets - //\{ + /** + * \name Receive control packets + * @{ + */ /** * Receive and process control packet * \param socket input socket @@ -410,10 +411,12 @@ class RoutingProtocol : public Ipv4RoutingProtocol */ /// Receive from node with address src void RecvError(Ptr 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 @@ class RoutingProtocol : public Ipv4RoutingProtocol * \param origin originating node IP address */ void SendRerrWhenNoRouteToForward(Ipv4Address dst, uint32_t dstSeqNo, Ipv4Address origin); - /// @} + /** @} */ /** * Send packet to destination socket diff --git a/src/aodv/test/aodv-id-cache-test-suite.cc b/src/aodv/test/aodv-id-cache-test-suite.cc index 41a7e50af..780f23fe1 100644 --- a/src/aodv/test/aodv-id-cache-test-suite.cc +++ b/src/aodv/test/aodv-id-cache-test-suite.cc @@ -33,13 +33,13 @@ namespace aodv { /** - * \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 @@ IdCacheTest::CheckTimeout3() /** * \ingroup aodv-test - * \ingroup tests * * \brief Id Cache Test Suite */ diff --git a/src/aodv/test/aodv-regression.cc b/src/aodv/test/aodv-regression.cc index 053cb1e38..bad3baa27 100644 --- a/src/aodv/test/aodv-regression.cc +++ b/src/aodv/test/aodv-regression.cc @@ -45,7 +45,6 @@ using namespace ns3; /** * \ingroup aodv-test - * \ingroup tests * * \brief AODV regression test suite */ @@ -69,7 +68,6 @@ class AodvRegressionTestSuite : public TestSuite /** * \ingroup aodv-test - * \ingroup tests * * \brief Chain Regression Test */ diff --git a/src/aodv/test/aodv-test-suite.cc b/src/aodv/test/aodv-test-suite.cc index 8ecbf03cb..5f3f903de 100644 --- a/src/aodv/test/aodv-test-suite.cc +++ b/src/aodv/test/aodv-test-suite.cc @@ -30,7 +30,6 @@ namespace aodv /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for neighbors */ @@ -136,7 +135,6 @@ NeighborTest::DoRun() /** * \ingroup aodv-test - * \ingroup tests * * \brief Type header test case */ @@ -164,7 +162,6 @@ struct TypeHeaderTest : public TestCase /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for RREQ */ @@ -224,7 +221,6 @@ struct RreqHeaderTest : public TestCase /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for RREP */ @@ -282,7 +278,6 @@ struct RrepHeaderTest : public TestCase /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for RREP-ACK */ @@ -307,7 +302,6 @@ struct RrepAckHeaderTest : public TestCase /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for RERR */ @@ -342,7 +336,6 @@ struct RerrHeaderTest : public TestCase /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for AODV routing table entry */ @@ -559,7 +552,6 @@ AodvRqueueTest::CheckTimeout() /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for AODV routing table entry */ @@ -653,7 +645,6 @@ struct AodvRtableEntryTest : public TestCase /** * \ingroup aodv-test - * \ingroup tests * * \brief Unit test for AODV routing table */ @@ -741,7 +732,6 @@ struct AodvRtableTest : public TestCase /** * \ingroup aodv-test - * \ingroup tests * * \brief AODV test suite */ diff --git a/src/aodv/test/loopback.cc b/src/aodv/test/loopback.cc index 6250d52fa..57973ff0a 100644 --- a/src/aodv/test/loopback.cc +++ b/src/aodv/test/loopback.cc @@ -74,7 +74,7 @@ class LoopbackTestCase : public TestCase * Echo data function * \param socket The socket to echo data */ - void EchoData(Ptr socket); + void EchoData(Ptr socket) const; public: LoopbackTestCase(); @@ -98,7 +98,7 @@ LoopbackTestCase::ReceivePkt(Ptr socket) } void -LoopbackTestCase::EchoData(Ptr socket) +LoopbackTestCase::EchoData(Ptr socket) const { Address from; Ptr receivedPacket = socket->RecvFrom(std::numeric_limits::max(), 0, from); @@ -191,7 +191,6 @@ LoopbackTestCase::DoRun() /** * \ingroup aodv-test - * \ingroup tests * * \brief AODV Loopback test suite */ diff --git a/src/applications/model/udp-client.cc b/src/applications/model/udp-client.cc index a1e002e5a..f5b4c9379 100644 --- a/src/applications/model/udp-client.cc +++ b/src/applications/model/udp-client.cc @@ -50,11 +50,12 @@ UdpClient::GetTypeId() .SetParent() .SetGroupName("Applications") .AddConstructor() - .AddAttribute("MaxPackets", - "The maximum number of packets the application will send", - UintegerValue(100), - MakeUintegerAccessor(&UdpClient::m_count), - MakeUintegerChecker()) + .AddAttribute( + "MaxPackets", + "The maximum number of packets the application will send (zero means infinite)", + UintegerValue(100), + MakeUintegerAccessor(&UdpClient::m_count), + MakeUintegerChecker()) .AddAttribute("Interval", "The time to wait between packets", TimeValue(Seconds(1.0)), @@ -223,7 +224,7 @@ UdpClient::Send() } #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 --git a/src/applications/model/udp-echo-client.cc b/src/applications/model/udp-echo-client.cc index 70cd095cb..ef7531d57 100644 --- a/src/applications/model/udp-echo-client.cc +++ b/src/applications/model/udp-echo-client.cc @@ -44,11 +44,12 @@ UdpEchoClient::GetTypeId() .SetParent() .SetGroupName("Applications") .AddConstructor() - .AddAttribute("MaxPackets", - "The maximum number of packets the application will send", - UintegerValue(100), - MakeUintegerAccessor(&UdpEchoClient::m_count), - MakeUintegerChecker()) + .AddAttribute( + "MaxPackets", + "The maximum number of packets the application will send (zero means infinite)", + UintegerValue(100), + MakeUintegerAccessor(&UdpEchoClient::m_count), + MakeUintegerChecker()) .AddAttribute("Interval", "The time to wait between packets", TimeValue(Seconds(1.0)), @@ -391,7 +392,7 @@ UdpEchoClient::Send() << Inet6SocketAddress::ConvertFrom(m_peerAddress).GetPort()); } - if (m_sent < m_count) + if (m_sent < m_count || m_count == 0) { ScheduleTransmit(m_interval); } diff --git a/src/applications/model/udp-trace-client.cc b/src/applications/model/udp-trace-client.cc index b45180dd2..d279e2d10 100644 --- a/src/applications/model/udp-trace-client.cc +++ b/src/applications/model/udp-trace-client.cc @@ -48,16 +48,18 @@ NS_OBJECT_ENSURE_REGISTERED(UdpTraceClient); /** * \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 @@ void UdpTraceClient::SetTraceFile(std::string traceFile) { NS_LOG_FUNCTION(this << traceFile); - if (traceFile == "") + if (traceFile.empty()) { LoadDefaultTrace(); } @@ -229,9 +231,9 @@ UdpTraceClient::LoadDefaultTrace() { 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 --git a/src/applications/model/udp-trace-client.h b/src/applications/model/udp-trace-client.h index c34364b65..6b81b5a74 100644 --- a/src/applications/model/udp-trace-client.h +++ b/src/applications/model/udp-trace-client.h @@ -165,9 +165,9 @@ class UdpTraceClient : public Application uint16_t m_peerPort; //!< Remote peer port EventId m_sendEvent; //!< Event to send the next packet - std::vector 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 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 --git a/src/applications/test/bulk-send-application-test-suite.cc b/src/applications/test/bulk-send-application-test-suite.cc index bf344bb57..769742a73 100644 --- a/src/applications/test/bulk-send-application-test-suite.cc +++ b/src/applications/test/bulk-send-application-test-suite.cc @@ -173,8 +173,8 @@ class BulkSendSeqTsSizeTestCase : public TestCase 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 --git a/src/applications/test/udp-client-server-test.cc b/src/applications/test/udp-client-server-test.cc index b4b7a167a..33509284a 100644 --- a/src/applications/test/udp-client-server-test.cc +++ b/src/applications/test/udp-client-server-test.cc @@ -330,13 +330,13 @@ UdpEchoClientSetFillTestCase::DoRun() 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 --git a/src/bridge/examples/csma-bridge-one-hop.cc b/src/bridge/examples/csma-bridge-one-hop.cc index 1ea6317d7..50ffa8eb5 100644 --- a/src/bridge/examples/csma-bridge-one-hop.cc +++ b/src/bridge/examples/csma-bridge-one-hop.cc @@ -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 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); NS_LOG_INFO("Done."); + + return 0; } diff --git a/src/bridge/examples/csma-bridge.cc b/src/bridge/examples/csma-bridge.cc index b2d60e3e3..eea36b14e 100644 --- a/src/bridge/examples/csma-bridge.cc +++ b/src/bridge/examples/csma-bridge.cc @@ -167,4 +167,6 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); NS_LOG_INFO("Done."); + + return 0; } diff --git a/src/bridge/examples/csma-bridge.py b/src/bridge/examples/csma-bridge.py index f1b51b338..293a02aad 100644 --- a/src/bridge/examples/csma-bridge.py +++ b/src/bridge/examples/csma-bridge.py @@ -96,8 +96,7 @@ def main(argv): 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 @@ def main(argv): # 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 @@ def main(argv): # 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 --git a/src/brite/CMakeLists.txt b/src/brite/CMakeLists.txt index 8ca0d591b..99c7be210 100644 --- a/src/brite/CMakeLists.txt +++ b/src/brite/CMakeLists.txt @@ -18,8 +18,10 @@ find_external_library( 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 @@ if((NOT 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 --git a/src/brite/examples/brite-MPI-example.cc b/src/brite/examples/brite-MPI-example.cc index 8530b0249..428311600 100644 --- a/src/brite/examples/brite-MPI-example.cc +++ b/src/brite/examples/brite-MPI-example.cc @@ -14,6 +14,8 @@ * */ +#include "Brite.h" + #include "ns3/applications-module.h" #include "ns3/brite-module.h" #include "ns3/core-module.h" diff --git a/src/brite/examples/brite-generic-example.cc b/src/brite/examples/brite-generic-example.cc index c90d72d26..462addf13 100644 --- a/src/brite/examples/brite-generic-example.cc +++ b/src/brite/examples/brite-generic-example.cc @@ -14,6 +14,8 @@ * */ +#include "Brite.h" + #include "ns3/applications-module.h" #include "ns3/brite-module.h" #include "ns3/core-module.h" diff --git a/src/brite/examples/conf_files/ASBarabasi.conf b/src/brite/examples/conf_files/ASBarabasi.conf index 34aed1c23..b8530b97a 100644 --- a/src/brite/examples/conf_files/ASBarabasi.conf +++ b/src/brite/examples/conf_files/ASBarabasi.conf @@ -15,7 +15,7 @@ BeginModel 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 --git a/src/brite/examples/conf_files/ASWaxman.conf b/src/brite/examples/conf_files/ASWaxman.conf index e128c977a..35d56e6ff 100644 --- a/src/brite/examples/conf_files/ASWaxman.conf +++ b/src/brite/examples/conf_files/ASWaxman.conf @@ -17,7 +17,7 @@ BeginModel 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 --git a/src/brite/examples/conf_files/RTBarabasi.conf b/src/brite/examples/conf_files/RTBarabasi.conf index af374d851..d88730c20 100644 --- a/src/brite/examples/conf_files/RTBarabasi.conf +++ b/src/brite/examples/conf_files/RTBarabasi.conf @@ -16,7 +16,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 --git a/src/brite/examples/conf_files/RTBarabasi10.conf b/src/brite/examples/conf_files/RTBarabasi10.conf index 3c780ad69..c6fe1d49d 100644 --- a/src/brite/examples/conf_files/RTBarabasi10.conf +++ b/src/brite/examples/conf_files/RTBarabasi10.conf @@ -16,7 +16,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 --git a/src/brite/examples/conf_files/RTBarabasi20.conf b/src/brite/examples/conf_files/RTBarabasi20.conf index 4b8fe7d76..12ea5a8d3 100644 --- a/src/brite/examples/conf_files/RTBarabasi20.conf +++ b/src/brite/examples/conf_files/RTBarabasi20.conf @@ -16,7 +16,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 = 1 #0 = Do not visualize with Otter, 1 = Visualize EndOutput diff --git a/src/brite/examples/conf_files/RTBarabasi5.conf b/src/brite/examples/conf_files/RTBarabasi5.conf index 94c7b4de4..12e01a789 100644 --- a/src/brite/examples/conf_files/RTBarabasi5.conf +++ b/src/brite/examples/conf_files/RTBarabasi5.conf @@ -16,7 +16,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 --git a/src/brite/examples/conf_files/RTWaxman.conf b/src/brite/examples/conf_files/RTWaxman.conf index fa669b7f1..d96b3a9ba 100644 --- a/src/brite/examples/conf_files/RTWaxman.conf +++ b/src/brite/examples/conf_files/RTWaxman.conf @@ -19,7 +19,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 = 1 #0 = Do not visualize with Otter, 1 = Visualize EndOutput diff --git a/src/brite/examples/conf_files/RTWaxman10.conf b/src/brite/examples/conf_files/RTWaxman10.conf index dcbab8cbc..ea088fdc0 100644 --- a/src/brite/examples/conf_files/RTWaxman10.conf +++ b/src/brite/examples/conf_files/RTWaxman10.conf @@ -19,7 +19,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 --git a/src/brite/examples/conf_files/RTWaxman20.conf b/src/brite/examples/conf_files/RTWaxman20.conf index 2203edd2e..c79ac5604 100644 --- a/src/brite/examples/conf_files/RTWaxman20.conf +++ b/src/brite/examples/conf_files/RTWaxman20.conf @@ -19,7 +19,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 --git a/src/brite/examples/conf_files/RTWaxman5.conf b/src/brite/examples/conf_files/RTWaxman5.conf index 52bdc850c..e4b46c762 100644 --- a/src/brite/examples/conf_files/RTWaxman5.conf +++ b/src/brite/examples/conf_files/RTWaxman5.conf @@ -19,7 +19,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 --git a/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf b/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf index 40424104a..b143fc013 100644 --- a/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf +++ b/src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf @@ -43,7 +43,7 @@ BeginModel 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 --git a/src/brite/helper/brite-topology-helper.cc b/src/brite/helper/brite-topology-helper.cc index 3d38ffeeb..9392cbe00 100644 --- a/src/brite/helper/brite-topology-helper.cc +++ b/src/brite/helper/brite-topology-helper.cc @@ -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 --git a/src/brite/helper/brite-topology-helper.h b/src/brite/helper/brite-topology-helper.h index d864c0170..52aa1f7c4 100644 --- a/src/brite/helper/brite-topology-helper.h +++ b/src/brite/helper/brite-topology-helper.h @@ -28,8 +28,12 @@ #include #include -// located in BRITE source directory -#include "Brite.h" +// These are in #include , +// here we just need a forward declaration. +namespace brite +{ +struct Topology; +}; namespace ns3 { diff --git a/src/buildings/doc/source/buildings-design.rst b/src/buildings/doc/source/buildings-design.rst index 46e9b341b..acc410ed9 100644 --- a/src/buildings/doc/source/buildings-design.rst +++ b/src/buildings/doc/source/buildings-design.rst @@ -151,8 +151,8 @@ The shadowing is modeled according to a log-normal distribution with variable st 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 --git a/src/buildings/doc/source/buildings-user.rst b/src/buildings/doc/source/buildings-user.rst index 362a91d0b..5ae47dbda 100644 --- a/src/buildings/doc/source/buildings-user.rst +++ b/src/buildings/doc/source/buildings-user.rst @@ -30,13 +30,13 @@ As an example, let's create a residential 10 x 20 x 10 building:: double y_max = 20.0; double z_min = 0.0; double z_max = 10.0; - Ptr b = CreateObject (); - 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 b = CreateObject(); + 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 @@ create a set of buildings with identical characteristics placed on a rectangular grid. Here's an example of how to use it:: Ptr gridBuildingAllocator; - gridBuildingAllocator = CreateObject (); - 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->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 @@ use them with the buildings model you need an additional call to 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 @@ Any legacy ns-3 positioning method can be used to place node in the simulation. The important additional step is to For example, you can place nodes manually like this:: - Ptr mm0 = enbNodes.Get (0)->GetObject (); - Ptr mm1 = enbNodes.Get (1)->GetObject (); - mm0->SetPosition (Vector (5.0, 5.0, 1.5)); - mm1->SetPosition (Vector (30.0, 40.0, 1.5)); + Ptr mm0 = enbNodes.Get(0)->GetObject(); + Ptr mm1 = enbNodes.Get(1)->GetObject(); + 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 ``BuildingMobilityLossModel`` parameter configurable with the ns3 attribute 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 --git a/src/buildings/examples/buildings-pathloss-profiler.cc b/src/buildings/examples/buildings-pathloss-profiler.cc index 2633a31a5..f7b1b3e2a 100644 --- a/src/buildings/examples/buildings-pathloss-profiler.cc +++ b/src/buildings/examples/buildings-pathloss-profiler.cc @@ -95,4 +95,6 @@ main(int argc, char* argv[]) } Simulator::Destroy(); + + return 0; } diff --git a/src/buildings/examples/outdoor-group-mobility-example.cc b/src/buildings/examples/outdoor-group-mobility-example.cc index fa3b81b28..6f59bca76 100644 --- a/src/buildings/examples/outdoor-group-mobility-example.cc +++ b/src/buildings/examples/outdoor-group-mobility-example.cc @@ -98,7 +98,7 @@ PrintPosition(Ptr node) /** * 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 @@ main(int argc, char* argv[]) Simulator::Run(); g_timeSeries.close(); Simulator::Destroy(); + + return 0; } diff --git a/src/buildings/examples/outdoor-random-walk-example.cc b/src/buildings/examples/outdoor-random-walk-example.cc index 18cd8d666..7ba4a3ade 100644 --- a/src/buildings/examples/outdoor-random-walk-example.cc +++ b/src/buildings/examples/outdoor-random-walk-example.cc @@ -31,7 +31,7 @@ NS_LOG_COMPONENT_DEFINE("OutdoorRandomWalkExample"); /** * 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 @@ main(int argc, char* argv[]) Simulator::Stop(Seconds(1e4)); Simulator::Run(); Simulator::Destroy(); + + return 0; } diff --git a/src/buildings/helper/building-container.h b/src/buildings/helper/building-container.h index 7f48048f8..0f2f6fbd2 100644 --- a/src/buildings/helper/building-container.h +++ b/src/buildings/helper/building-container.h @@ -137,7 +137,7 @@ class BuildingContainer * 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. + * indexed Ptr. * * \code * uint32_t nBuildings = container.GetN (); diff --git a/src/buildings/helper/building-position-allocator.h b/src/buildings/helper/building-position-allocator.h index 404ff22cb..35d861f08 100644 --- a/src/buildings/helper/building-position-allocator.h +++ b/src/buildings/helper/building-position-allocator.h @@ -162,7 +162,7 @@ class RandomRoomPositionAllocator : public PositionAllocator private: /** - * Room informations + * Room information */ struct RoomInfo { diff --git a/src/buildings/model/building-list.cc b/src/buildings/model/building-list.cc index 9f533e83d..9bf0c3826 100644 --- a/src/buildings/model/building-list.cc +++ b/src/buildings/model/building-list.cc @@ -55,13 +55,13 @@ class BuildingListPriv : public Object */ uint32_t Add(Ptr 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 --git a/src/buildings/model/buildings-channel-condition-model.cc b/src/buildings/model/buildings-channel-condition-model.cc index bed68dc72..5e89c598b 100644 --- a/src/buildings/model/buildings-channel-condition-model.cc +++ b/src/buildings/model/buildings-channel-condition-model.cc @@ -105,7 +105,7 @@ BuildingsChannelConditionModel::GetChannelCondition(Ptr a, { 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 --git a/src/buildings/model/hybrid-buildings-propagation-loss-model.cc b/src/buildings/model/hybrid-buildings-propagation-loss-model.cc index 9509acd3e..9b07995fa 100644 --- a/src/buildings/model/hybrid-buildings-propagation-loss-model.cc +++ b/src/buildings/model/hybrid-buildings-propagation-loss-model.cc @@ -174,7 +174,7 @@ HybridBuildingsPropagationLossModel::GetLoss(Ptr a, Ptr Okumura Hata + // Over the rooftop transmission -> Okumura Hata loss = OkumuraHata(a, b); NS_LOG_INFO(this << " O-O (>1000): above rooftop -> OH : " << loss); } diff --git a/src/buildings/model/random-walk-2d-outdoor-mobility-model.h b/src/buildings/model/random-walk-2d-outdoor-mobility-model.h index 327de64e3..c5b29f17e 100644 --- a/src/buildings/model/random-walk-2d-outdoor-mobility-model.h +++ b/src/buildings/model/random-walk-2d-outdoor-mobility-model.h @@ -124,7 +124,7 @@ class RandomWalk2dOutdoorMobilityModel : public MobilityModel 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 m_speed; //!< rv for picking speed diff --git a/src/buildings/test/buildings-channel-condition-model-test.cc b/src/buildings/test/buildings-channel-condition-model-test.cc index 2924d4051..d5a4aea2d 100644 --- a/src/buildings/test/buildings-channel-condition-model-test.cc +++ b/src/buildings/test/buildings-channel-condition-model-test.cc @@ -59,14 +59,14 @@ class BuildingsChannelConditionModelTestCase : public TestCase /** * 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 m_testVectors; //!< array containg all the test vectors + TestVectors m_testVectors; //!< array containing all the test vectors }; BuildingsChannelConditionModelTestCase::BuildingsChannelConditionModelTestCase() diff --git a/src/buildings/test/buildings-pathloss-test.h b/src/buildings/test/buildings-pathloss-test.h index 7cbf3c3c2..7700d0c16 100644 --- a/src/buildings/test/buildings-pathloss-test.h +++ b/src/buildings/test/buildings-pathloss-test.h @@ -55,7 +55,7 @@ class BuildingsPathlossTestCase : public TestCase * \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 @@ class BuildingsPathlossTestCase : public TestCase 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 --git a/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc b/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc index ddb2f4eaf..bd3e041c5 100644 --- a/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc +++ b/src/buildings/test/three-gpp-v2v-channel-condition-model-test.cc @@ -68,15 +68,15 @@ class ThreeGppV2vBuildingsChCondModelTestCase : public TestCase /** * 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 m_testVectors; //!< array containg all the test vectors + TestVectors m_testVectors; //!< array containing all the test vectors }; ThreeGppV2vBuildingsChCondModelTestCase::ThreeGppV2vBuildingsChCondModelTestCase() @@ -221,13 +221,13 @@ class ThreeGppV2vUrbanLosNlosvChCondModelTestCase : public TestCase /** * 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 m_testVectors; //!< array containing all the test vectors Ptr m_condModel; //!< the channel condition model @@ -386,13 +386,13 @@ class ThreeGppV2vHighwayLosNlosvChCondModelTestCase : public TestCase /** * 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 m_testVectors; //!< array containing all the test vectors Ptr m_condModel; //!< the channel condition model diff --git a/src/click/CMakeLists.txt b/src/click/CMakeLists.txt index bc81f650c..72cd22495 100644 --- a/src/click/CMakeLists.txt +++ b/src/click/CMakeLists.txt @@ -23,6 +23,8 @@ if((NOT click_FOUND) AND (NOT ${click_FOUND}) + AND (NOT + ${NS3_FETCH_OPTIONAL_COMPONENTS}) ) message( ${HIGHLIGHTED_STATUS} @@ -31,6 +33,13 @@ if((NOT return() endif() +if(${NS3_FETCH_OPTIONAL_COMPONENTS}) + set(click_LIBRARIES + click + nsclick + ) +endif() + include_directories(${click_INCLUDE_DIRS}) set(NS3_CLICK "ON" diff --git a/src/click/doc/click.rst b/src/click/doc/click.rst index 3d248b18c..221377b43 100644 --- a/src/click/doc/click.rst +++ b/src/click/doc/click.rst @@ -142,9 +142,9 @@ class in your simulation script. For instance: .. 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 --git a/src/click/model/ipv4-click-routing.cc b/src/click/model/ipv4-click-routing.cc index c70b69628..8480c4b04 100644 --- a/src/click/model/ipv4-click-routing.cc +++ b/src/click/model/ipv4-click-routing.cc @@ -29,6 +29,7 @@ #include "ns3/random-variable-stream.h" #include "ns3/simulator.h" +#include #include #include #include @@ -817,7 +818,7 @@ simclick_sim_command(simclick_node_t* simnode, int cmd, ...) // 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 defines = clickInstance->GetDefines(); diff --git a/src/click/model/ipv4-click-routing.h b/src/click/model/ipv4-click-routing.h index 72217f4c7..c77528261 100644 --- a/src/click/model/ipv4-click-routing.h +++ b/src/click/model/ipv4-click-routing.h @@ -26,19 +26,18 @@ #include "ns3/packet.h" #include "ns3/test.h" -#include -#include - -#ifdef NS3_CLICK -#include -#endif - #include #include +#include +#include class ClickTrivialTest; class ClickIfidFromNameTest; class ClickIpMacAddressFromNameTest; +// These are in #include , +// here we just need a forward declaration. +struct simclick_node; +typedef struct simclick_node simclick_node_t; namespace ns3 { diff --git a/src/config-store/examples/config-store-save.cc b/src/config-store/examples/config-store-save.cc index 63c640e94..b72c615c9 100644 --- a/src/config-store/examples/config-store-save.cc +++ b/src/config-store/examples/config-store-save.cc @@ -113,4 +113,6 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); + + return 0; } diff --git a/src/config-store/model/attribute-iterator.cc b/src/config-store/model/attribute-iterator.cc index d6df2676f..ea6d947f5 100644 --- a/src/config-store/model/attribute-iterator.cc +++ b/src/config-store/model/attribute-iterator.cc @@ -73,7 +73,7 @@ AttributeIterator::GetCurrentPath(std::string attr) const { oss << "/" << m_currentPath[i]; } - if (attr != "") + if (!attr.empty()) { oss << "/" << attr; } diff --git a/src/config-store/model/config-store.cc b/src/config-store/model/config-store.cc index d611bb3bd..c81f06708 100644 --- a/src/config-store/model/config-store.cc +++ b/src/config-store/model/config-store.cc @@ -154,14 +154,14 @@ ConfigStore::~ConfigStore() } 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 --git a/src/config-store/model/config-store.h b/src/config-store/model/config-store.h index 468b3ed7b..fd85fe59c 100644 --- a/src/config-store/model/config-store.h +++ b/src/config-store/model/config-store.h @@ -96,12 +96,12 @@ class ConfigStore : public ObjectBase * 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 @@ class ConfigStore : public ObjectBase 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 --git a/src/config-store/model/model-node-creator.h b/src/config-store/model/model-node-creator.h index c8e5b04d0..3d7a46fb9 100644 --- a/src/config-store/model/model-node-creator.h +++ b/src/config-store/model/model-node-creator.h @@ -36,9 +36,7 @@ enum struct ModelNode { /** - * \enum node type * \brief node type structure - * */ enum { diff --git a/src/config-store/model/model-typeid-creator.h b/src/config-store/model/model-typeid-creator.h index 007680dd0..2763d2440 100644 --- a/src/config-store/model/model-typeid-creator.h +++ b/src/config-store/model/model-typeid-creator.h @@ -38,7 +38,6 @@ enum struct ModelTypeid { /** - * \enum node type * \brief Whether the node represents an attribute or TypeId */ enum diff --git a/src/config-store/model/xml-config.cc b/src/config-store/model/xml-config.cc index bbc7abf3f..72a0510e3 100644 --- a/src/config-store/model/xml-config.cc +++ b/src/config-store/model/xml-config.cc @@ -29,6 +29,7 @@ #include "ns3/string.h" #include +#include #include namespace ns3 @@ -46,7 +47,7 @@ void XmlConfigSave::SetFilename(std::string filename) { NS_LOG_FUNCTION(filename); - if (filename == "") + if (filename.empty()) { return; } diff --git a/src/config-store/model/xml-config.h b/src/config-store/model/xml-config.h index 8b9703042..7d499cc02 100644 --- a/src/config-store/model/xml-config.h +++ b/src/config-store/model/xml-config.h @@ -22,10 +22,13 @@ #include "file-config.h" -#include -#include #include +// These are in #include , +// here we just need a forward declaration. +typedef struct _xmlTextWriter xmlTextWriter; +typedef xmlTextWriter* xmlTextWriterPtr; + namespace ns3 { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8ca8c15de..3bc9fd689 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -163,12 +163,12 @@ set(source_files 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 @@ set(source_files model/trickle-timer.cc model/realtime-simulator-impl.cc model/wall-clock-synchronizer.cc + model/matrix-array.cc ) # Define core lib headers @@ -247,6 +248,7 @@ set(header_files 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 @@ -279,7 +281,6 @@ set(header_files 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 @@ -310,9 +311,12 @@ set(header_files 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 @@ -324,6 +328,7 @@ set(test_sources 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 @@ -337,6 +342,7 @@ set(test_sources 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 @@ -346,6 +352,8 @@ set(test_sources 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 --git a/src/core/doc/core.h b/src/core/doc/core.h index f4ed96e7f..bd95bebf9 100644 --- a/src/core/doc/core.h +++ b/src/core/doc/core.h @@ -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 --git a/src/core/doc/environment-variables.h b/src/core/doc/environment-variables.h new file mode 100644 index 000000000..b23fc53dd --- /dev/null +++ b/src/core/doc/environment-variables.h @@ -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. + */ + +// 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. + * + *
    + *
    %Parameters
    + *
    + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    nameThe name of the Attribute to set.
    valueThe value to set the Attribute to.
    ;Multiple \pname{name}=\pname{value} pairs should be delimited by ';'
    + *
    + *
    + * + * 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. + * + *
    + *
    %Parameters
    + *
    + * + * + * + * + * + *
    path The directory where ns3::CommandLine should write its Usage message.
    + *
    + *
    + * + * 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. + * + *
    + *
    %Parameters
    + *
    + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    nameThe name of the ns3::GlobalValue to set.
    valueThe value to set the ns3::GlobalValue to.
    ;Multiple \pname{name}=\pname{value} pairs should be delimited by ';'
    + *
    + *
    + * + * 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. + * + *
    + *
    %Parameters
    + *
    + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    componentThe logging component to enable.
    optionLog level and or prefix to enable. Multiple options should be delimited with '|'
    :Multiple logging components can be enabled simultaneously, delimited by ':'
    + *
    + *
    + * + * 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. + * + *
    + *
    %Parameters
    + *
    + * + * + * + * + * + *
    pathThe absolute path in which to create the temporary directory.
    + *
    + *
    + * + * Referenced by ns3::SystemPath::MakeTemporaryDirectoryName. + * + * @{ + */ +const char* TMP = "path"; +const char* TEMP = "path"; +/**@}*/ diff --git a/src/core/examples/CMakeLists.txt b/src/core/examples/CMakeLists.txt index 12a346cfb..9befa2b39 100644 --- a/src/core/examples/CMakeLists.txt +++ b/src/core/examples/CMakeLists.txt @@ -1,4 +1,5 @@ set(base_examples + assert-example command-line-example fatal-example hash-example @@ -55,3 +56,10 @@ build_lib_example( LIBRARIES_TO_LINK ${libcore} ${libflow-monitor} ) + +build_lib_example( + NAME log-example + SOURCE_FILES log-example.cc + LIBRARIES_TO_LINK ${libcore} + ${libnetwork} +) diff --git a/src/core/model/ref-count-base.cc b/src/core/examples/assert-example.cc similarity index 53% rename from src/core/model/ref-count-base.cc rename to src/core/examples/assert-example.cc index 055458f51..2248deec0 100644 --- a/src/core/model/ref-count-base.cc +++ b/src/core/examples/assert-example.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Georgia Tech Research Corporation + * 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 @@ -14,31 +14,34 @@ * 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 - * Adapted from original code in object.h by: - * Authors: Gustavo Carneiro , - * Mathieu Lacage + * Author: Peter D. Barnes, Jr. */ -#include "ref-count-base.h" - -#include "log.h" - /** * \file - * \ingroup ptr - * ns3::RefCountBase implementation. - * \deprecated See \ref ns3::SimpleRefCount + * \ingroup core-examples + * \ingroup assert + * Example program illustrating the use of NS_ASSERT_MSG() */ -namespace ns3 -{ +#include "ns3/core-module.h" -NS_LOG_COMPONENT_DEFINE("RefCountBase"); +#include -RefCountBase::~RefCountBase() +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("AssertExample"); + +int +main(int argc, char** argv) { - NS_LOG_FUNCTION(this); + 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; } - -} // namespace ns3 diff --git a/src/core/examples/command-line-example.cc b/src/core/examples/command-line-example.cc index 61e0f7c6a..7bb4e3fdc 100644 --- a/src/core/examples/command-line-example.cc +++ b/src/core/examples/command-line-example.cc @@ -49,7 +49,7 @@ std::string g_cbArg = "cbArg default"; * \returns \c true. */ bool -SetCbArg(std::string val) +SetCbArg(const std::string& val) { g_cbArg = val; return true; diff --git a/src/core/examples/empirical-random-variable-example.cc b/src/core/examples/empirical-random-variable-example.cc index 4de4f777f..ce78a348d 100644 --- a/src/core/examples/empirical-random-variable-example.cc +++ b/src/core/examples/empirical-random-variable-example.cc @@ -82,13 +82,16 @@ RunSingleSample(std::string mode, Ptr erv) * \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(n) * 100.0 << std::endl; + << count / static_cast(n) * 100.0 << std::setw(10) << std::right + << sum / static_cast(n) * 100.0 << std::endl; } /** @@ -136,21 +139,21 @@ RunBothModes(std::string mode, Ptr erv, long n) 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 @@ RunBothModes(std::string mode, Ptr erv, long n) 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 @@ RunBothModes(std::string mode, Ptr erv, long n) 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 @@ main(int argc, char* argv[]) // Create the ERV in sampling mode Ptr erv = CreateObject(); - 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 = 120/150 = 0.8 if (single) { diff --git a/src/core/examples/hash-example.cc b/src/core/examples/hash-example.cc index c3a3420e5..514e69197 100644 --- a/src/core/examples/hash-example.cc +++ b/src/core/examples/hash-example.cc @@ -137,7 +137,7 @@ class Collider * \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 @@ class Collider } /** Hash function. */ - enum Bits m_bits; + Bits m_bits; /** Hashed dictionary of first instance of each hash. */ typedef std::map hashdict_t; @@ -291,7 +291,7 @@ class Dictionary */ void Add(const std::string phrase) { - if (phrase.size() == 0) + if (phrase.empty()) { return; } @@ -437,7 +437,7 @@ class DictFiles * \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 @@ class DictFiles */ void ReadInto(Dictionary& dict) { - if (m_files.size() == 0) + if (m_files.empty()) { Add(GetDefault()); } @@ -545,4 +545,5 @@ main(int argc, char* argv[]) dict.Time(); } // if (timing) + return 0; } // main diff --git a/src/core/examples/length-example.cc b/src/core/examples/length-example.cc index c6a2b9282..e23e5a34d 100644 --- a/src/core/examples/length-example.cc +++ b/src/core/examples/length-example.cc @@ -23,17 +23,21 @@ #include /** - * \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 @@ Constructors() } /** - * \ingroup core-examples - * \ingroup length + * \ingroup length-examples * \brief Demonstrates the use of ns3::Length conversions. */ void @@ -75,8 +78,7 @@ Conversions() } /** - * \ingroup core-examples - * \ingroup length + * \ingroup length-examples * \brief Demonstrates the use of ns3::Length arithmetic operators. */ void @@ -103,8 +105,7 @@ ArithmeticOperators() } /** - * \ingroup core-examples - * \ingroup length + * \ingroup length-examples * \brief Demonstrates the use of ns3::Length equality operators. */ void @@ -141,8 +142,7 @@ EqualityOperators() } /** - * \ingroup core-examples - * \ingroup length + * \ingroup length-examples * \brief Demonstrates the use of ns3::Length multiplications and divisions. */ void diff --git a/src/core/examples/log-example.cc b/src/core/examples/log-example.cc new file mode 100644 index 000000000..f16c10ab9 --- /dev/null +++ b/src/core/examples/log-example.cc @@ -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. + */ + +/** + * \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().AddConstructor(); + 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(); + + NS_LOG_UNCOND("Creating MyEventObject"); + auto myObj = CreateObject(); + + 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 --git a/src/core/examples/main-random-variable-stream.cc b/src/core/examples/main-random-variable-stream.cc index b0455524d..43820d6b6 100644 --- a/src/core/examples/main-random-variable-stream.cc +++ b/src/core/examples/main-random-variable-stream.cc @@ -25,6 +25,8 @@ #include "ns3/string.h" #include +#include +#include #include /** @@ -116,15 +118,17 @@ main(int argc, char* argv[]) 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 x = CreateObject(); + auto x = CreateObject(); x->SetAttribute("Min", DoubleValue(0.0)); x->SetAttribute("Max", DoubleValue(1.0)); @@ -132,29 +136,31 @@ main(int argc, char* argv[]) 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 x1 = CreateObject(); + auto x1 = CreateObject(); 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 x2 = CreateObject(); + auto x2 = CreateObject(); 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 x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("Mean", DoubleValue(1.5)); plot.AddDataset(Histogram(x3, probes, precision, "ExponentialRandomVariable m=1.5")); @@ -162,28 +168,30 @@ main(int argc, char* argv[]) 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 x1 = CreateObject(); + auto x1 = CreateObject(); 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 x2 = CreateObject(); + auto x2 = CreateObject(); 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 x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("Scale", DoubleValue(1.0)); x3->SetAttribute("Shape", DoubleValue(2.5)); @@ -191,28 +199,30 @@ main(int argc, char* argv[]) 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 x1 = CreateObject(); + auto x1 = CreateObject(); 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 x2 = CreateObject(); + auto x2 = CreateObject(); 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 x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("Scale", DoubleValue(1.0)); x3->SetAttribute("Shape", DoubleValue(3.0)); @@ -220,16 +230,18 @@ main(int argc, char* argv[]) 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 x1 = CreateObject(); + auto x1 = CreateObject(); x1->SetAttribute("Mean", DoubleValue(0.0)); x1->SetAttribute("Variance", DoubleValue(1.0)); @@ -238,7 +250,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("NormalDist {/Symbol m}=0.0 {/Symbol s}=1.0", "NormalDist(x,0.0,1.0)")); - Ptr x2 = CreateObject(); + auto x2 = CreateObject(); x2->SetAttribute("Mean", DoubleValue(0.0)); x2->SetAttribute("Variance", DoubleValue(2.0)); @@ -247,7 +259,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("NormalDist {/Symbol m}=0.0 {/Symbol s}=sqrt(2.0)", "NormalDist(x,0.0,sqrt(2.0))")); - Ptr x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("Mean", DoubleValue(0.0)); x3->SetAttribute("Variance", DoubleValue(3.0)); @@ -257,55 +269,61 @@ main(int argc, char* argv[]) "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 [*:*]"); + std::cout << "EmpiricalVariable..........." << std::flush; + 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); + auto x = CreateObject(); + 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 (emp1, probes, precision, - "EmpiricalRandomVariable (Stairs)") ); + plot.AddDataset( + Histogram(x, probes, precision, "EmpiricalRandomVariable (Sampling)", true)); - gnuplots.AddPlot (plot); + 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); + 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(); + 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 x1 = CreateObject(); + auto x1 = CreateObject(); x1->SetAttribute("Mu", DoubleValue(0.0)); x1->SetAttribute("Sigma", DoubleValue(1.0)); @@ -314,13 +332,13 @@ main(int argc, char* argv[]) plot.AddDataset( Gnuplot2dFunction("LogNormalDist(x, 0.0, 1.0)", "LogNormalDist(x, 0.0, 1.0)")); - Ptr x2 = CreateObject(); + auto x2 = CreateObject(); 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 x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("Mu", DoubleValue(0.0)); x3->SetAttribute("Sigma", DoubleValue(0.25)); @@ -329,13 +347,13 @@ main(int argc, char* argv[]) plot.AddDataset( Gnuplot2dFunction("LogNormalDist(x, 0.0, 0.25)", "LogNormalDist(x, 0.0, 0.25)")); - Ptr x4 = CreateObject(); + auto x4 = CreateObject(); 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 x5 = CreateObject(); + auto x5 = CreateObject(); x5->SetAttribute("Mu", DoubleValue(0.0)); x5->SetAttribute("Sigma", DoubleValue(2.0)); @@ -344,21 +362,23 @@ main(int argc, char* argv[]) plot.AddDataset( Gnuplot2dFunction("LogNormalDist(x, 0.0, 2.0)", "LogNormalDist(x, 0.0, 2.0)")); - Ptr x6 = CreateObject(); + auto x6 = CreateObject(); 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 x1 = CreateObject(); + auto x1 = CreateObject(); x1->SetAttribute("Min", DoubleValue(0.0)); x1->SetAttribute("Max", DoubleValue(1.0)); x1->SetAttribute("Mean", DoubleValue(0.5)); @@ -366,7 +386,7 @@ main(int argc, char* argv[]) plot.AddDataset( Histogram(x1, probes, precision, "TriangularRandomVariable [0.0 .. 1.0) m=0.5")); - Ptr x2 = CreateObject(); + auto x2 = CreateObject(); x2->SetAttribute("Min", DoubleValue(0.0)); x2->SetAttribute("Max", DoubleValue(1.0)); x2->SetAttribute("Mean", DoubleValue(0.4)); @@ -374,7 +394,7 @@ main(int argc, char* argv[]) plot.AddDataset( Histogram(x2, probes, precision, "TriangularRandomVariable [0.0 .. 1.0) m=0.4")); - Ptr x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("Min", DoubleValue(0.0)); x3->SetAttribute("Max", DoubleValue(1.0)); x3->SetAttribute("Mean", DoubleValue(0.65)); @@ -383,9 +403,11 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) "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 x1 = CreateObject(); + auto x1 = CreateObject(); x1->SetAttribute("Alpha", DoubleValue(1.0)); x1->SetAttribute("Beta", DoubleValue(1.0)); @@ -404,7 +426,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 1.0, 1.0)", "GammaDist(x, 1.0, 1.0)")); - Ptr x2 = CreateObject(); + auto x2 = CreateObject(); x2->SetAttribute("Alpha", DoubleValue(1.5)); x2->SetAttribute("Beta", DoubleValue(1.0)); @@ -412,7 +434,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 1.5, 1.0)", "GammaDist(x, 1.5, 1.0)")); - Ptr x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("Alpha", DoubleValue(2.0)); x3->SetAttribute("Beta", DoubleValue(1.0)); @@ -420,7 +442,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 2.0, 1.0)", "GammaDist(x, 2.0, 1.0)")); - Ptr x4 = CreateObject(); + auto x4 = CreateObject(); x4->SetAttribute("Alpha", DoubleValue(4.0)); x4->SetAttribute("Beta", DoubleValue(1.0)); @@ -428,7 +450,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 4.0, 1.0)", "GammaDist(x, 4.0, 1.0)")); - Ptr x5 = CreateObject(); + auto x5 = CreateObject(); x5->SetAttribute("Alpha", DoubleValue(2.0)); x5->SetAttribute("Beta", DoubleValue(2.0)); @@ -436,7 +458,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 2.0, 2.0)", "GammaDist(x, 2.0, 2.0)")); - Ptr x6 = CreateObject(); + auto x6 = CreateObject(); x6->SetAttribute("Alpha", DoubleValue(2.5)); x6->SetAttribute("Beta", DoubleValue(3.0)); @@ -444,7 +466,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("{/Symbol g}(x, 2.5, 3.0)", "GammaDist(x, 2.5, 3.0)")); - Ptr x7 = CreateObject(); + auto x7 = CreateObject(); x7->SetAttribute("Alpha", DoubleValue(2.5)); x7->SetAttribute("Beta", DoubleValue(4.5)); @@ -453,9 +475,11 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) 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 x1 = CreateObject(); + auto x1 = CreateObject(); x1->SetAttribute("K", IntegerValue(1)); x1->SetAttribute("Lambda", DoubleValue(1.0)); @@ -473,7 +497,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("Erlang(x, 1, 1.0)", "ErlangDist(x, 1, 1.0)")); - Ptr x2 = CreateObject(); + auto x2 = CreateObject(); x2->SetAttribute("K", IntegerValue(2)); x2->SetAttribute("Lambda", DoubleValue(1.0)); @@ -482,7 +506,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("Erlang(x, 2, 1.0)", "ErlangDist(x, 2, 1.0)")); - Ptr x3 = CreateObject(); + auto x3 = CreateObject(); x3->SetAttribute("K", IntegerValue(3)); x3->SetAttribute("Lambda", DoubleValue(1.0)); @@ -491,7 +515,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("Erlang(x, 3, 1.0)", "ErlangDist(x, 3, 1.0)")); - Ptr x4 = CreateObject(); + auto x4 = CreateObject(); x4->SetAttribute("K", IntegerValue(5)); x4->SetAttribute("Lambda", DoubleValue(1.0)); @@ -500,7 +524,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("Erlang(x, 5, 1.0)", "ErlangDist(x, 5, 1.0)")); - Ptr x5 = CreateObject(); + auto x5 = CreateObject(); x5->SetAttribute("K", IntegerValue(2)); x5->SetAttribute("Lambda", DoubleValue(2.0)); @@ -509,7 +533,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("Erlang(x, 2, 2.0)", "ErlangDist(x, 2, 2.0)")); - Ptr x6 = CreateObject(); + auto x6 = CreateObject(); x6->SetAttribute("K", IntegerValue(2)); x6->SetAttribute("Lambda", DoubleValue(3.0)); @@ -518,7 +542,7 @@ main(int argc, char* argv[]) plot.AddDataset(Gnuplot2dFunction("Erlang(x, 2, 3.0)", "ErlangDist(x, 2, 3.0)")); - Ptr x7 = CreateObject(); + auto x7 = CreateObject(); x7->SetAttribute("K", IntegerValue(2)); x7->SetAttribute("Lambda", DoubleValue(5.0)); @@ -528,9 +552,30 @@ main(int argc, char* argv[]) 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 --git a/src/core/examples/main-test-sync.cc b/src/core/examples/main-test-sync.cc index b1fc37e6b..b8f1031f3 100644 --- a/src/core/examples/main-test-sync.cc +++ b/src/core/examples/main-test-sync.cc @@ -158,4 +158,6 @@ main(int argc, char* argv[]) { test(); } + + return 0; } diff --git a/src/core/examples/sample-log-time-format.cc b/src/core/examples/sample-log-time-format.cc index e63e7847a..cf9d5ede2 100644 --- a/src/core/examples/sample-log-time-format.cc +++ b/src/core/examples/sample-log-time-format.cc @@ -126,10 +126,12 @@ main(int argc, char* argv[]) LogComponentEnable("RandomVariableStream", LOG_LEVEL_ALL); LogComponentEnableAll(LOG_PREFIX_TIME); - std::map resolutionMap = {{"Time::US", Time::US}, - {"Time::NS", Time::NS}, - {"Time::PS", Time::PS}, - {"Time::FS", Time::FS}}; + std::map 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 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); + + return 0; } diff --git a/src/core/examples/sample-random-variable-stream.cc b/src/core/examples/sample-random-variable-stream.cc index 46f22ed98..66b694267 100644 --- a/src/core/examples/sample-random-variable-stream.cc +++ b/src/core/examples/sample-random-variable-stream.cc @@ -68,4 +68,6 @@ main(int argc, char* argv[]) Ptr uv = CreateObject(); std::cout << uv->GetValue() << std::endl; + + return 0; } diff --git a/src/core/examples/sample-random-variable.cc b/src/core/examples/sample-random-variable.cc index 6854fdbbc..4433d73e6 100644 --- a/src/core/examples/sample-random-variable.cc +++ b/src/core/examples/sample-random-variable.cc @@ -68,4 +68,6 @@ main(int argc, char* argv[]) Ptr uv = CreateObject(); std::cout << uv->GetValue() << std::endl; + + return 0; } diff --git a/src/core/examples/sample-simulator.cc b/src/core/examples/sample-simulator.cc index dc5ac0c67..66224bd29 100644 --- a/src/core/examples/sample-simulator.cc +++ b/src/core/examples/sample-simulator.cc @@ -124,4 +124,6 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); + + return 0; } diff --git a/src/core/examples/sample-simulator.py b/src/core/examples/sample-simulator.py index 58f2650d2..a8bb55ee7 100755 --- a/src/core/examples/sample-simulator.py +++ b/src/core/examples/sample-simulator.py @@ -47,7 +47,7 @@ ns.cppyy.cppdef(""" { public: /** Start model execution by scheduling a HandleEvent. */ - void Start (void); + void Start (); private: /** @@ -59,7 +59,7 @@ ns.cppyy.cppdef(""" }; void - MyModel::Start (void) + MyModel::Start () { Simulator::Schedule (Seconds (10.0), &MyModel::HandleEvent, @@ -92,7 +92,7 @@ ns.cppyy.cppdef(""" return MakeEvent(&RandomFunctionCpp, model); } - void CancelledFunctionCpp(void) { + void CancelledFunctionCpp() { CPyCppyy::Eval("CancelledEvent()"); } diff --git a/src/core/examples/test-string-value-formatting.cc b/src/core/examples/test-string-value-formatting.cc index 0379e4f06..526af8a13 100644 --- a/src/core/examples/test-string-value-formatting.cc +++ b/src/core/examples/test-string-value-formatting.cc @@ -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 @@ main(int argc, char* argv[]) 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 --git a/src/core/helper/csv-reader.cc b/src/core/helper/csv-reader.cc index 084ecfbdf..abd4c4d73 100644 --- a/src/core/helper/csv-reader.cc +++ b/src/core/helper/csv-reader.cc @@ -329,7 +329,7 @@ CsvReader::ParseLine(const std::string& line) 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 --git a/src/core/model/assert.h b/src/core/model/assert.h index 4902412e9..7a819c27f 100644 --- a/src/core/model/assert.h +++ b/src/core/model/assert.h @@ -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 --git a/src/core/model/attribute-construction-list.cc b/src/core/model/attribute-construction-list.cc index 87f0e51c7..92c759b6a 100644 --- a/src/core/model/attribute-construction-list.cc +++ b/src/core/model/attribute-construction-list.cc @@ -54,7 +54,7 @@ AttributeConstructionList::Add(std::string name, } } // store the new value. - struct Item attr; + Item attr; attr.checker = checker; attr.value = value; attr.name = name; diff --git a/src/core/model/attribute-container.h b/src/core/model/attribute-container.h index 08ffdccfc..88bc31ebc 100644 --- a/src/core/model/attribute-container.h +++ b/src/core/model/attribute-container.h @@ -228,7 +228,7 @@ template class C = std::list> Ptr MakeAttributeContainerChecker(Ptr 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 --git a/src/core/model/attribute-helper.h b/src/core/model/attribute-helper.h index a0f453bba..6f98305eb 100644 --- a/src/core/model/attribute-helper.h +++ b/src/core/model/attribute-helper.h @@ -111,7 +111,12 @@ MakeSimpleAttributeChecker(std::string name, std::string underlying) 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 --git a/src/core/model/calendar-scheduler.h b/src/core/model/calendar-scheduler.h index ceb997423..73c0822fd 100644 --- a/src/core/model/calendar-scheduler.h +++ b/src/core/model/calendar-scheduler.h @@ -55,7 +55,7 @@ class EventImpl; * `(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 --git a/src/core/model/callback.h b/src/core/model/callback.h index 662ca1f01..989ee19c3 100644 --- a/src/core/model/callback.h +++ b/src/core/model/callback.h @@ -441,6 +441,9 @@ class CallbackBase template class Callback : public CallbackBase { + template + friend class Callback; + public: Callback() { @@ -464,18 +467,17 @@ class Callback : public CallbackBase * \param [in] bargs The values of the bound arguments */ template - Callback(const CallbackBase& cb, BArgs... bargs) + Callback(const Callback& cb, BArgs... bargs) { - auto cbDerived = - static_cast*>(PeekPointer(cb.GetImpl())); + auto f = cb.DoPeekImpl()->GetFunction(); - std::function f(cbDerived->GetFunction()); - - CallbackComponentVector components(cbDerived->GetComponents()); + CallbackComponentVector components(cb.DoPeekImpl()->GetComponents()); components.insert(components.end(), {std::make_shared>(bargs)...}); m_impl = Create>( - [f, bargs...](UArgs... uargs) -> R { return f(bargs..., uargs...); }, + [f, bargs...](auto&&... uargs) -> R { + return f(bargs..., std::forward(uargs)...); + }, components); } @@ -508,7 +510,9 @@ class Callback : public CallbackBase std::make_shared>(bargs)...}); m_impl = Create>( - [f, bargs...](UArgs... uargs) -> R { return f(bargs..., uargs...); }, + [f, bargs...](auto&&... uargs) -> R { + return f(bargs..., std::forward(uargs)...); + }, components); } @@ -525,11 +529,23 @@ class Callback : public CallbackBase * The integer sequence is 0..N-1, where N is the number of arguments left unbound. */ template - auto BindImpl(std::index_sequence seq, BoundArgs... bargs) + auto BindImpl(std::index_sequence seq, BoundArgs&&... bargs) { - return Callback>...>( - *this, - bargs...); + Callback>...> cb; + + const auto f = DoPeekImpl()->GetFunction(); + + CallbackComponentVector components(DoPeekImpl()->GetComponents()); + components.insert(components.end(), + {std::make_shared>(bargs)...}); + + cb.m_impl = Create>( + [f, bargs...](auto&&... uargs) mutable { + return f(bargs..., std::forward(uargs)...); + }, + components); + + return cb; } public: @@ -541,7 +557,7 @@ class Callback : public CallbackBase * \return The bound callback */ template - auto Bind(BoundArgs... bargs) + auto Bind(BoundArgs&&... bargs) { static_assert(sizeof...(UArgs) > 0); return BindImpl(std::make_index_sequence{}, @@ -749,9 +765,9 @@ MakeNullCallback() */ template auto -MakeBoundCallback(R (*fnPtr)(Args...), BArgs... bargs) +MakeBoundCallback(R (*fnPtr)(Args...), BArgs&&... bargs) { - return Callback(fnPtr).Bind(bargs...); + return Callback(fnPtr).Bind(std::forward(bargs)...); } /** diff --git a/src/core/model/command-line.cc b/src/core/model/command-line.cc index b8e37f1b4..3423bb08e 100644 --- a/src/core/model/command-line.cc +++ b/src/core/model/command-line.cc @@ -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 // transform #include // tolower -#include // exit, getenv +#include // exit #include // strlen #include // setw, boolalpha #include @@ -198,7 +199,7 @@ CommandLine::Parse(std::vector args) m_nonOptionCount = 0; - if (args.size() > 0) + if (!args.empty()) { args.erase(args.begin()); // discard the program name @@ -385,8 +386,9 @@ CommandLine::PrintHelp(std::ostream& os) const // 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 @@ CommandLine::PrintDoxygenUsage() const { 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 @@ CommandLine::PrintDoxygenUsage() const // 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 @@ CommandLine::PrintDoxygenUsage() const os << "/**\n \\file " << m_shortName << ".cc\n" << "

    Usage

    \n" - << "$ ./ns3 run \"" << m_shortName << (m_options.size() ? " [Program Options]" : "") - << (nonOptions.size() ? " [Program Arguments]" : "") << "\"\n"; + << "$ ./ns3 run \"" << m_shortName << (!m_options.empty() ? " [Program Options]" : "") + << (!nonOptions.empty() ? " [Program Arguments]" : "") << "\"\n"; if (!m_usage.empty()) { @@ -691,7 +693,7 @@ CommandLine::HandleArgument(const std::string& name, const std::string& value) c auto errorExit = [this, name, value]() { std::cerr << "Invalid command-line argument: --" << name; - if (value != "") + if (!value.empty()) { std::cerr << "=" << value; } @@ -723,7 +725,7 @@ CommandLine::HandleArgument(const std::string& name, const std::string& value) c bool CommandLine::CallbackItem::HasDefault() const { - return m_default != ""; + return !m_default.empty(); } std::string @@ -759,7 +761,7 @@ CommandLine::AddValue(const std::string& name, void CommandLine::AddValue(const std::string& name, const std::string& help, - ns3::Callback callback, + ns3::Callback callback, const std::string& defaultValue /* = "" */) { diff --git a/src/core/model/command-line.h b/src/core/model/command-line.h index ad191935c..60d42f1cf 100644 --- a/src/core/model/command-line.h +++ b/src/core/model/command-line.h @@ -322,7 +322,7 @@ class CommandLine */ void AddValue(const std::string& name, const std::string& help, - ns3::Callback callback, + ns3::Callback callback, const std::string& defaultValue = ""); /** @@ -533,7 +533,7 @@ class CommandLine bool HasDefault() const override; std::string GetDefault() const override; - ns3::Callback m_callback; /**< The Callback */ + ns3::Callback m_callback; /**< The Callback */ std::string m_default; /**< The default value, as a string, if it exists. */ }; // class CallbackItem @@ -777,7 +777,7 @@ template bool CommandLine::UserItem::HasDefault() const { - return (m_default.size() > 0); + return !m_default.empty(); } template diff --git a/src/core/model/config.cc b/src/core/model/config.cc index d994aa6c9..566984ad5 100644 --- a/src/core/model/config.cc +++ b/src/core/model/config.cc @@ -129,7 +129,7 @@ MatchContainer::Connect(std::string name, const CallbackBase& cb) { 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 @@ void 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 --git a/src/core/model/deprecated.h b/src/core/model/deprecated.h index c67431341..701c59265 100644 --- a/src/core/model/deprecated.h +++ b/src/core/model/deprecated.h @@ -60,11 +60,27 @@ * 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. * */ #define NS_DEPRECATED(msg) [[deprecated(msg)]] +/** + * \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 @@ -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 --git a/src/core/model/des-metrics.cc b/src/core/model/des-metrics.cc index 2d54743d3..b79729b38 100644 --- a/src/core/model/des-metrics.cc +++ b/src/core/model/des-metrics.cc @@ -50,17 +50,17 @@ DesMetrics::Initialize(std::vector args, std::string outDir /* = "" 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 @@ DesMetrics::Initialize(std::vector args, std::string outDir /* = "" 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 --git a/src/core/model/environment-variable.cc b/src/core/model/environment-variable.cc new file mode 100644 index 000000000..fa71a3a6d --- /dev/null +++ b/src/core/model/environment-variable.cc @@ -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. + */ + +#include "environment-variable.h" + +#include "ns3/string.h" + +#include // std::getenv +#include // strlen +#include // clog +#include // Global functions setenv, unsetenv + +/** + * \file + * \ingroup core-environ + * Class EnvironmentVariable implementation. + */ + +#ifdef __WIN32__ +#include + +/** + * 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::GetDictionary(const std::string& envvar, const std::string& delim /* ";" */) +{ + NS_LOCAL_LOG(envvar << ", " << delim); + std::shared_ptr 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(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 --git a/src/core/model/environment-variable.h b/src/core/model/environment-variable.h new file mode 100644 index 000000000..607ee15c6 --- /dev/null +++ b/src/core/model/environment-variable.h @@ -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. + */ + +#ifndef ENVIRONMENT_VARIABLE_H +#define ENVIRONMENT_VARIABLE_H + +/** + * \file + * \ingroup core-environ + * Class Environment declaration. + */ + +#include // shared_ptr +#include +#include +#include // 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; + + /** + * 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 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; + + /** 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>; + + /** + * 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 --git a/src/core/model/example-as-test.cc b/src/core/model/example-as-test.cc index 617d86a2f..3ff6e203b 100644 --- a/src/core/model/example-as-test.cc +++ b/src/core/model/example-as-test.cc @@ -21,9 +21,11 @@ #include "ascii-test.h" #include "assert.h" +#include "environment-variable.h" +#include "fatal-error.h" #include "log.h" -#include // itoa(), system (), getenv () +#include // itoa(), system () #include #include #include @@ -45,11 +47,13 @@ NS_LOG_COMPONENT_DEFINE("ExampleAsTestCase"); 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 @@ ExampleAsTestCase::DoRun() 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 @@ ExampleAsTestSuite::ExampleAsTestSuite(const std::string name, 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 --git a/src/core/model/example-as-test.h b/src/core/model/example-as-test.h index 08724dcbe..4301817eb 100644 --- a/src/core/model/example-as-test.h +++ b/src/core/model/example-as-test.h @@ -39,8 +39,8 @@ namespace ns3 * 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 @@ class ExampleAsTestCase : public TestCase * `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 @@ class ExampleAsTestCase : public TestCase /** * 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 @@ class ExampleAsTestCase : public TestCase 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 @@ class ExampleAsTestCase : public TestCase * 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 @@ class ExampleAsTestSuite : public TestSuite 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 --git a/src/core/model/fatal-error.h b/src/core/model/fatal-error.h index 0539a26b0..67c199505 100644 --- a/src/core/model/fatal-error.h +++ b/src/core/model/fatal-error.h @@ -26,6 +26,7 @@ #include #include #include +#include /** * \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 --git a/src/core/model/global-value.cc b/src/core/model/global-value.cc index f0678f309..aad86274a 100644 --- a/src/core/model/global-value.cc +++ b/src/core/model/global-value.cc @@ -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 // getenv -#include // strlen - /** * \file * \ingroup core @@ -70,35 +68,15 @@ GlobalValue::InitializeFromEnv() { 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 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 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 @@ GlobalValue::Bind(std::string name, const AttributeValue& value) return; } } - NS_FATAL_ERROR("Non-existant global value: " << name); + NS_FATAL_ERROR("Non-existent global value: " << name); } bool diff --git a/src/core/model/hash-fnv.h b/src/core/model/hash-fnv.h index 72a945d39..21e9aa6bc 100644 --- a/src/core/model/hash-fnv.h +++ b/src/core/model/hash-fnv.h @@ -98,7 +98,7 @@ class Fnv1a : public Implementation /** * Seed value */ - enum seed + enum Seed { SEED = 0x8BADF00D /**< Ate bad food */ }; diff --git a/src/core/model/hash-murmur3.h b/src/core/model/hash-murmur3.h index 6e06ca8df..5ba8fee3e 100644 --- a/src/core/model/hash-murmur3.h +++ b/src/core/model/hash-murmur3.h @@ -101,7 +101,7 @@ class Murmur3 : public Implementation * 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 --git a/src/core/model/int64x64-cairo.h b/src/core/model/int64x64-cairo.h index 3e1eee324..e90d4d120 100644 --- a/src/core/model/int64x64-cairo.h +++ b/src/core/model/int64x64-cairo.h @@ -296,7 +296,7 @@ class int64x64_t /** * 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 --git a/src/core/model/int64x64-double.h b/src/core/model/int64x64-double.h index 8eec8e1e2..b965003c1 100644 --- a/src/core/model/int64x64-double.h +++ b/src/core/model/int64x64-double.h @@ -314,7 +314,7 @@ class int64x64_t * \name Arithmetic Operators * Arithmetic operators for int64x64_t. */ - /* + /** * @{ * Arithmetic operator. * \param [in] lhs Left hand argument @@ -367,7 +367,7 @@ class int64x64_t * \name Unary Operators * Unary operators for int64x64_t. */ - /* + /** * @{ * Unary operator. * \param [in] lhs Left hand argument diff --git a/src/core/model/length.cc b/src/core/model/length.cc index c17a78922..9a65fd354 100644 --- a/src/core/model/length.cc +++ b/src/core/model/length.cc @@ -181,7 +181,8 @@ Convert(double value, ns3::Length::Unit fromUnit, ns3::Length::Unit toUnit) {{Unit::Yard, Unit::Meter}, USToMeter>}, {{Unit::Meter, Unit::Yard}, MeterToUS>}, {{Unit::Mile, Unit::Meter}, USToMeter>}, - {{Unit::Meter, Unit::Mile}, MeterToUS>}}; + {{Unit::Meter, Unit::Mile}, MeterToUS>}, + }; auto iter = CONVERSIONS.find(Key{fromUnit, toUnit}); @@ -514,17 +515,19 @@ ToSymbol(Length::Unit unit) { using StringTable = std::unordered_map; - 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 @@ ToName(Length::Unit unit, bool plural /*=false*/) {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 @@ FromString(std::string unitString) { using UnitTable = std::unordered_map; - 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 @@ Miles(double value) return Length(value, Length::Unit::Mile); } -/**@}*/ - } // namespace ns3 diff --git a/src/core/model/length.h b/src/core/model/length.h index 89df5bea1..9f33774ec 100644 --- a/src/core/model/length.h +++ b/src/core/model/length.h @@ -134,7 +134,7 @@ namespace ns3 * 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 @@ namespace ns3 * from boost::unit quantities. * * \code - * //construct length from a boost::units quantitiy + * //construct length from a boost::units quantity * boost::units::quantity q * = 5 * boost::units::si::meter; * Length meters (q); @@ -445,7 +445,7 @@ class Length * 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 --git a/src/core/model/log.cc b/src/core/model/log.cc index 175781839..8a0ba2925 100644 --- a/src/core/model/log.cc +++ b/src/core/model/log.cc @@ -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 // getenv -#include // strlen +#include // transform +#include // strlen #include #include +#include // toupper +#include +#include // accumulate #include #include @@ -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 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 LOG_LEVEL_LABELS = {[]() { + std::map 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 @@ static NodePrinter g_logNodePrinter = nullptr; /** * \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 @@ LogComponent::GetComponentList() 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 @@ GetLogComponent(const std::string name) 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) + if (value.empty()) { - 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; - } - - pre_pipe = false; - } while (next_lev != std::string::npos); - - Enable((enum LogLevel)level); - } - } - cur = next + 1; + // Default is enable all levels, all prefixes + value = "**"; } + + // 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}; + + for (const auto& lev : flags) + { + if (lev == "**") + { + level |= LOG_LEVEL_ALL | LOG_PREFIX_ALL; + } + 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 @@ LogComponent::IsNoneEnabled() const } 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 @@ LogComponent::File() const /* 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 @@ LogComponentEnable(const char* name, enum LogLevel level) 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 @@ LogComponentEnableAll(enum LogLevel level) } 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 @@ LogComponentDisable(const char* name, enum LogLevel level) } 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 @@ LogComponentPrintList() 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 @@ ComponentExists(std::string componentName) 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) + for (auto& [component, value] : dict) { - 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) + if (component != "*" && component != "***" && !ComponentExists(component)) { - // 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"); - } + 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 --git a/src/core/model/log.h b/src/core/model/log.h index 135c9efad..b4512f235 100644 --- a/src/core/model/log.h +++ b/src/core/model/log.h @@ -134,7 +134,7 @@ enum LogLevel * \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 @@ void LogComponentEnable(const char* name, enum LogLevel level); * * \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 @@ void LogComponentEnableAll(enum LogLevel level); * \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 @@ class LogComponent * 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 @@ class LogComponent * * \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 @@ class LogComponent * \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 @@ class LogComponent typedef std::map ComponentList; /** - * Get the list of LogComponnents. + * Get the list of LogComponents. * * \internal * This should really be considered an internal API. diff --git a/src/core/model/map-scheduler.cc b/src/core/model/map-scheduler.cc index a3b1a5eab..fd18503c7 100644 --- a/src/core/model/map-scheduler.cc +++ b/src/core/model/map-scheduler.cc @@ -85,7 +85,7 @@ MapScheduler::PeekNext() const 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 @@ MapScheduler::RemoveNext() 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 --git a/src/core/model/matrix-array.cc b/src/core/model/matrix-array.cc new file mode 100644 index 000000000..affa42e6a --- /dev/null +++ b/src/core/model/matrix-array.cc @@ -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 + */ + +#include "matrix-array.h" + +#ifdef HAVE_EIGEN3 +#include +#endif + +namespace ns3 +{ + +#ifdef HAVE_EIGEN3 +template +using EigenMatrix = Eigen::Map>; +template +using ConstEigenMatrix = Eigen::Map>; +#endif + +template +MatrixArray::MatrixArray(uint16_t numRows, uint16_t numCols, uint16_t numPages) + : ValArray(numRows, numCols, numPages) +{ +} + +template +MatrixArray::MatrixArray(const std::valarray& values) + : ValArray(values) +{ +} + +template +MatrixArray::MatrixArray(std::valarray&& values) + : ValArray(std::move(values)) +{ +} + +template +MatrixArray::MatrixArray(const std::vector& values) + : ValArray(values) +{ +} + +template +MatrixArray::MatrixArray(uint16_t numRows, uint16_t numCols, const std::valarray& values) + : ValArray(numRows, numCols, values) +{ +} + +template +MatrixArray::MatrixArray(uint16_t numRows, uint16_t numCols, std::valarray&& values) + : ValArray(numRows, numCols, std::move(values)) +{ +} + +template +MatrixArray::MatrixArray(uint16_t numRows, + uint16_t numCols, + uint16_t numPages, + const std::valarray& values) + : ValArray(numRows, numCols, numPages, values) +{ +} + +template +MatrixArray::MatrixArray(uint16_t numRows, + uint16_t numCols, + uint16_t numPages, + std::valarray&& values) + : ValArray(numRows, numCols, numPages, std::move(values)) +{ +} + +template +MatrixArray +MatrixArray::operator*(const MatrixArray& 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 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 lhsEigenMatrix(GetPagePtr(page), m_numRows, m_numCols); + ConstEigenMatrix rhsEigenMatrix(rhs.GetPagePtr(page), rhs.m_numRows, rhs.m_numCols); + EigenMatrix 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 +MatrixArray +MatrixArray::Transpose() const +{ + // Create the matrix where m_numRows = this.m_numCols, m_numCols = this.m_numRows, + // m_numPages = this.m_numPages + MatrixArray 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 thisMatrix(GetPagePtr(page), m_numRows, m_numCols); + EigenMatrix 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 +MatrixArray +MatrixArray::MultiplyByLeftAndRightMatrix(const MatrixArray& lMatrix, + const MatrixArray& 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 res{lMatrix.m_numRows, rMatrix.m_numCols, m_numPages}; + +#ifdef HAVE_EIGEN3 + + ConstEigenMatrix lMatrixEigen(lMatrix.GetPagePtr(0), lMatrix.m_numRows, lMatrix.m_numCols); + ConstEigenMatrix 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 matrixEigen(GetPagePtr(page), m_numRows, m_numCols); + EigenMatrix 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 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 +template +MatrixArray +MatrixArray::HermitianTranspose() const +{ + MatrixArray> retMatrix = this->Transpose(); + + for (size_t index = 0; index < this->GetSize(); ++index) + { + retMatrix[index] = std::conj(m_values[index]); + } + return retMatrix; +} + +template MatrixArray> MatrixArray>::HermitianTranspose() + const; +template class MatrixArray>; +template class MatrixArray; +template class MatrixArray; + +} // namespace ns3 diff --git a/src/core/model/matrix-array.h b/src/core/model/matrix-array.h new file mode 100644 index 000000000..7bfab6b2c --- /dev/null +++ b/src/core/model/matrix-array.h @@ -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 + */ + +#ifndef MATRIX_ARRAY_H +#define MATRIX_ARRAY_H + +#include "val-array.h" + +#include + +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 MatrixArray : public ValArray +{ + public: + // instruct the compiler to generate the default constructor + MatrixArray() = 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(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 values to initialize the elements. + * \param values std::valarray that will be used to initialize elements of 1D array + */ + explicit MatrixArray(const std::valarray& values); + /** + * \brief Constructor creates a single array of values.size () elements and 1 column, + * and moves std::valarray values to initialize the elements. + * \param values std::valarray that will be moved to initialize elements of 1D array + */ + MatrixArray(std::valarray&& values); + /** + * \brief Constructor creates a single array of values.size () elements and 1 column, + * and uses std::vector values to initialize the elements. + * \param values std::vector that will be used to initialize elements of 1D array + */ + explicit MatrixArray(const std::vector& values); + /** + * \brief Constructor creates a single matrix of numRows and numCols, and uses + * std::valarray values to initialize the elements. + * \param numRows the number of rows + * \param numCols the number of columns + * \param values std::valarray that will be used to initialize elements of 3D array + */ + MatrixArray(uint16_t numRows, uint16_t numCols, const std::valarray& values); + /** + * \brief Constructor creates a single matrix of numRows and numCols, and moves + * std::valarray values to initialize the elements. + * \param numRows the number of rows + * \param numCols the number of columns + * \param values std::valarray that will be moved to initialize elements of 3D array + */ + MatrixArray(uint16_t numRows, uint16_t numCols, std::valarray&& values); + /** + * \brief Constructor creates the array of numPages matrices of numRows x numCols dimensions, + * and uses std::valarray 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 that will be used to initialize elements of 3D array + */ + MatrixArray(uint16_t numRows, + uint16_t numCols, + uint16_t numPages, + const std::valarray& values); + /** + * \brief Constructor creates the array of numPages matrices of numRows x numCols dimensions, + * and moves std::valarray 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 that will be used to initialize elements of 3D array + */ + MatrixArray(uint16_t numRows, + uint16_t numCols, + uint16_t numPages, + std::valarray&& values); + /** instruct the compiler to generate the implicitly declared destructor*/ + ~MatrixArray() override = default; + /** instruct the compiler to generate the implicitly declared copy constructor*/ + MatrixArray(const MatrixArray&) = 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& operator=(const MatrixArray&) = default; + /** instruct the compiler to generate the implicitly declared move constructor*/ + MatrixArray(MatrixArray&&) = 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& operator=(MatrixArray&&) = 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 operator*(const T& rhs) const; + /** + * \brief operator+ definition for MatrixArray. + * \param rhs The rhs MatrixArray object + * \returns The result of operator+ of this MatrixArray and rhs MatrixAray + */ + MatrixArray operator+(const MatrixArray& rhs) const; + /** + * \brief binary operator- definition for MatrixArray. + * \param rhs The rhs MatrixArray object + * \returns The result of operator- of this MatrixArray and rhs MatrixAray + */ + MatrixArray operator-(const MatrixArray& rhs) const; + /** + * \brief unary operator- definition for MatrixArray. + * \return the result of the operator- + */ + MatrixArray 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 operator*(const MatrixArray& 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. + * \return The resulting MatrixArray composed of the array of transposed matrices. + */ + MatrixArray Transpose() const; + /** + *\brief Multiply each matrix in the array by the left and the right matrix. + * For each page of this MatrixArray the operation performed is + * lMatrix * matrix(pageIndex) * rMatrix, and the resulting MatrixArray + * 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 MultiplyByLeftAndRightMatrix(const MatrixArray& lMatrix, + const MatrixArray& rMatrix) const; + + using ValArray::GetPagePtr; + using ValArray::EqualDims; + using ValArray::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 > specialization of MatrixArray. + * \return Returns a new matrix that is the result of the Hermitian transpose + */ + template >::value && + EnableBool)>::type> + MatrixArray HermitianTranspose() const; + + private: + // To simplify functions in MatrixArray that are using members from the template base class + using ValArray::m_numRows; + using ValArray::m_numCols; + using ValArray::m_numPages; + using ValArray::m_values; +}; + +/// Create an alias for MatrixArray using int type +using IntMatrixArray = MatrixArray; + +/// Create an alias for MatrixArray using double type +using DoubleMatrixArray = MatrixArray; + +/// Create an alias for MatrixArray using complex type +using ComplexMatrixArray = MatrixArray>; + +/************************************************* + ** Class MatrixArray inline implementations + ************************************************/ +template +inline MatrixArray +MatrixArray::operator*(const T& rhs) const +{ + return MatrixArray(m_numRows, + m_numCols, + m_numPages, + m_values * std::valarray(rhs, m_numRows * m_numCols * m_numPages)); +} + +template +inline MatrixArray +MatrixArray::operator+(const MatrixArray& rhs) const +{ + AssertEqualDims(rhs); + return MatrixArray(m_numRows, m_numCols, m_numPages, m_values + rhs.m_values); +} + +template +inline MatrixArray +MatrixArray::operator-(const MatrixArray& rhs) const +{ + AssertEqualDims(rhs); + return MatrixArray(m_numRows, m_numCols, m_numPages, m_values - rhs.m_values); +} + +template +inline MatrixArray +MatrixArray::operator-() const +{ + return MatrixArray(m_numRows, m_numCols, m_numPages, -m_values); +} + +} // namespace ns3 + +#endif // MATRIX_ARRAY_H diff --git a/src/core/model/nstime.h b/src/core/model/nstime.h index a762a6add..f0282d4d9 100644 --- a/src/core/model/nstime.h +++ b/src/core/model/nstime.h @@ -465,11 +465,11 @@ class Time * 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 @@ class Time * \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 @@ class Time 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 @@ class Time * \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 @@ class Time 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 @@ class Time * \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 @@ class Time * \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 @@ class Time /** 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 @@ class Time * \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 @@ class Time * \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 @@ class Time * 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 @@ operator*(T lhs, const Time& rhs) * 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 --git a/src/core/model/object-base.cc b/src/core/model/object-base.cc index ec8a1ca90..e1ca714a7 100644 --- a/src/core/model/object-base.cc +++ b/src/core/model/object-base.cc @@ -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 // getenv -#include // strlen -#include - /** * \file * \ingroup object @@ -42,64 +40,6 @@ NS_LOG_COMPONENT_DEFINE("ObjectBase"); 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 -EnvDictionary(std::string key) -{ - static std::unordered_map 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 @@ ObjectBase::NotifyConstructionCompleted() 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 @@ ObjectBase::ConstructSelf(const AttributeConstructionList& attributes) { 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 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 @@ ObjectBase::ConstructSelf(const AttributeConstructionList& attributes) 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(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 @@ ObjectBase::ConstructSelf(const AttributeConstructionList& attributes) 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 @@ ObjectBase::ConstructSelf(const AttributeConstructionList& attributes) NotifyConstructionCompleted(); } -#undef LOG_WHERE_VALUE - bool ObjectBase::DoSet(Ptr accessor, Ptr checker, diff --git a/src/core/model/object-base.h b/src/core/model/object-base.h index f8956cd6c..66aa4be1c 100644 --- a/src/core/model/object-base.h +++ b/src/core/model/object-base.h @@ -21,6 +21,7 @@ #include "callback.h" #include "type-id.h" +#include "warnings.h" #include #include @@ -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 @@ class ObjectBase */ 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 --git a/src/core/model/object-factory.cc b/src/core/model/object-factory.cc index abf73998f..6e086a2bf 100644 --- a/src/core/model/object-factory.cc +++ b/src/core/model/object-factory.cc @@ -66,7 +66,7 @@ void ObjectFactory::DoSet(const std::string& name, const AttributeValue& value) { NS_LOG_FUNCTION(this << name << &value); - if (name == "") + if (name.empty()) { return; } diff --git a/src/core/model/pointer.h b/src/core/model/pointer.h index 473665396..3708d1375 100644 --- a/src/core/model/pointer.h +++ b/src/core/model/pointer.h @@ -136,6 +136,7 @@ class PointerChecker : public ns3::PointerChecker } if (!value->GetObject()) { + // a null pointer is a valid value return true; } T* ptr = dynamic_cast(PeekPointer(value->GetObject())); diff --git a/src/core/model/random-variable-stream.cc b/src/core/model/random-variable-stream.cc index 832d3385a..5d1d30dce 100644 --- a/src/core/model/random-variable-stream.cc +++ b/src/core/model/random-variable-stream.cc @@ -102,6 +102,13 @@ RandomVariableStream::IsAntithetic() const return m_isAntithetic; } +uint32_t +RandomVariableStream::GetInteger() +{ + NS_LOG_FUNCTION(this); + return static_cast(GetValue()); +} + void RandomVariableStream::SetStream(int64_t stream) { @@ -216,7 +223,7 @@ uint32_t UniformRandomVariable::GetInteger() { NS_LOG_FUNCTION(this); - return (uint32_t)GetValue(m_min, m_max + 1); + return static_cast(GetValue(m_min, m_max + 1)); } NS_OBJECT_ENSURE_REGISTERED(ConstantRandomVariable); @@ -270,13 +277,6 @@ ConstantRandomVariable::GetValue() 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 @@ SequentialRandomVariable::GetValue() 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 @@ SequentialRandomVariable::GetValue() return r; } -uint32_t -SequentialRandomVariable::GetInteger() -{ - NS_LOG_FUNCTION(this); - return (uint32_t)GetValue(); -} - NS_OBJECT_ENSURE_REGISTERED(ExponentialRandomVariable); TypeId @@ -462,13 +455,6 @@ ExponentialRandomVariable::GetValue() 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 @@ ParetoRandomVariable::GetValue() 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 @@ WeibullRandomVariable::GetValue() 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 @@ NormalRandomVariable::GetValue() 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 @@ LogNormalRandomVariable::GetValue() 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 @@ GammaRandomVariable::GetValue(double alpha, double beta) return beta * d * v; } -uint32_t -GammaRandomVariable::GetInteger(uint32_t alpha, uint32_t beta) -{ - NS_LOG_FUNCTION(this << alpha << beta); - return static_cast(GetValue(alpha, beta)); -} - double GammaRandomVariable::GetValue() { @@ -1069,13 +1020,6 @@ GammaRandomVariable::GetValue() 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 @@ ErlangRandomVariable::GetValue() 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 @@ TriangularRandomVariable::GetValue() 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 @@ ZipfRandomVariable::GetValue() 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 @@ ZetaRandomVariable::GetValue() 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 @@ DeterministicRandomVariable::~DeterministicRandomVariable() } void -DeterministicRandomVariable::SetValueArray(double* values, std::size_t length) +DeterministicRandomVariable::SetValueArray(const std::vector& 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 @@ DeterministicRandomVariable::GetValue() 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 @@ EmpiricalRandomVariable::SetInterpolate(bool interpolate) return prev; } -uint32_t -EmpiricalRandomVariable::GetInteger() -{ - NS_LOG_FUNCTION(this); - return static_cast(GetValue()); -} - bool EmpiricalRandomVariable::PreSample(double& value) { diff --git a/src/core/model/random-variable-stream.h b/src/core/model/random-variable-stream.h index 67967880a..6feb07648 100644 --- a/src/core/model/random-variable-stream.h +++ b/src/core/model/random-variable-stream.h @@ -143,16 +143,14 @@ class RandomVariableStream : public Object 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 @@ class RandomVariableStream : public Object * 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 excluded), and \f$[min, max]\f$ (\c max included) + * 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 @@ class RandomVariableStream : public Object * 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 @@ class UniformRandomVariable : public RandomVariableStream 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 @@ class UniformRandomVariable : public RandomVariableStream * \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 @@ class ConstantRandomVariable : public RandomVariableStream 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 @@ class ConstantRandomVariable : public RandomVariableStream * 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 @@ class ConstantRandomVariable : public RandomVariableStream * 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 @@ class SequentialRandomVariable : public RandomVariableStream */ 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 @@ class SequentialRandomVariable : public RandomVariableStream * * 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) - * \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 * * \f[ - * x = - 1/\alpha \log(u) + * P(x; \alpha) dx = \alpha e^{-\alpha x} dx, \\ + * \quad x \in [0, +\infty) * \f] * - * where \f$u\f$ is a uniform random variable on \f$[0,1)\f$. + * 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 = - \frac{\log(u)}{\alpha} = - \text{Mean} \log(u) + * \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[ - * = 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 @@ class SequentialRandomVariable : public RandomVariableStream * 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 @@ class ExponentialRandomVariable : public RandomVariableStream 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 @@ class ExponentialRandomVariable : public RandomVariableStream * 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 probability density function of a Pareto variable is: * - * 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$. + * \f[ + * P(x; x_m, \alpha) dx = \alpha \frac{x_m^\alpha}{x^{\alpha + 1}} dx, \\ + * \quad x \in [x_m, +\infty) + * \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. + * 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 @@ class ExponentialRandomVariable : public RandomVariableStream * 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 @@ class ParetoRandomVariable : public RandomVariableStream 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 @@ class ParetoRandomVariable : public RandomVariableStream /** * \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 @@ class ParetoRandomVariable : public RandomVariableStream * 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 @@ class WeibullRandomVariable : public RandomVariableStream 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 @@ class WeibullRandomVariable : public RandomVariableStream * 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 @@ class WeibullRandomVariable : public RandomVariableStream * // 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 @@ class NormalRandomVariable : public RandomVariableStream 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 @@ class NormalRandomVariable : public RandomVariableStream * \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 probability density function is defined using two parameters + * \c Mu = \f$\mu\f$ and \c Sigma = \f$\sigma\f$ as: * - * 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$ + * \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 @@ class NormalRandomVariable : public RandomVariableStream * 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 @@ class LogNormalRandomVariable : public RandomVariableStream 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 @@ class LogNormalRandomVariable : public RandomVariableStream * 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 @@ class LogNormalRandomVariable : public RandomVariableStream * 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 @@ class GammaRandomVariable : public RandomVariableStream 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 @@ class GammaRandomVariable : public RandomVariableStream * \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 @@ class GammaRandomVariable : public RandomVariableStream * 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 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 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 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 @@ class GammaRandomVariable : public RandomVariableStream * 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 @@ class ErlangRandomVariable : public RandomVariableStream 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 @@ class ErlangRandomVariable : public RandomVariableStream * \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 @@ class ErlangRandomVariable : public RandomVariableStream * 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 @@ class ErlangRandomVariable : public RandomVariableStream * 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 @@ class TriangularRandomVariable : public RandomVariableStream 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 @@ class TriangularRandomVariable : public RandomVariableStream * 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 @@ class TriangularRandomVariable : public RandomVariableStream * 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 @@ class ZipfRandomVariable : public RandomVariableStream 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 @@ class ZipfRandomVariable : public RandomVariableStream * 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, 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$ + * 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 + * + * \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 x = CreateObject (); * 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 @@ class ZetaRandomVariable : public RandomVariableStream 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 @@ class ZetaRandomVariable : public RandomVariableStream * 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 s = CreateObject (); * - * // 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 @@ class DeterministicRandomVariable : public RandomVariableStream /** * \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& 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. */ + void SetValueArray(const double* values, std::size_t length); + + // Inherited double GetValue() override; - - /** - * \brief Returns the next value in the sequence. - * \return The integer next value in the sequence. - */ - uint32_t GetInteger() override; + using RandomVariableStream::GetInteger; private: /** Size of the array of values. */ @@ -2414,17 +1876,16 @@ class DeterministicRandomVariable : public RandomVariableStream * \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 @@ class DeterministicRandomVariable : public RandomVariableStream * 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. + * + * \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 x = CreateObject (); @@ -2465,8 +1929,9 @@ class DeterministicRandomVariable : public RandomVariableStream * 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 @@ class DeterministicRandomVariable : public RandomVariableStream * 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 @@ class EmpiricalRandomVariable : public RandomVariableStream * * \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 --git a/src/core/model/realtime-simulator-impl.cc b/src/core/model/realtime-simulator-impl.cc index cd25b196b..6ca7ed893 100644 --- a/src/core/model/realtime-simulator-impl.cc +++ b/src/core/model/realtime-simulator-impl.cc @@ -266,7 +266,7 @@ RealtimeSimulatorImpl::ProcessOneEvent() // // 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 @@ RealtimeSimulatorImpl::GetEventCount() const } void -RealtimeSimulatorImpl::SetSynchronizationMode(enum SynchronizationMode mode) +RealtimeSimulatorImpl::SetSynchronizationMode(SynchronizationMode mode) { NS_LOG_FUNCTION(this << mode); m_synchronizationMode = mode; diff --git a/src/core/model/ref-count-base.h b/src/core/model/ref-count-base.h deleted file mode 100644 index 463241424..000000000 --- a/src/core/model/ref-count-base.h +++ /dev/null @@ -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 - * Adapted from original code in object.h by: - * Authors: Gustavo Carneiro , - * Mathieu Lacage - */ -#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 -{ - public: - /** - * This only thing this class does it declare a virtual destructor - */ - virtual ~RefCountBase(); -}; - -} // namespace ns3 - -#endif /* REF_COUNT_BASE_H */ diff --git a/src/core/model/simple-ref-count.h b/src/core/model/simple-ref-count.h index 32c012156..144c348c6 100644 --- a/src/core/model/simple-ref-count.h +++ b/src/core/model/simple-ref-count.h @@ -41,7 +41,7 @@ namespace ns3 * \ingroup ptr * \brief Empty class, used as a default parent class for SimpleRefCount */ -class empty +class Empty { }; @@ -67,7 +67,7 @@ class empty * 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 @@ -77,7 +77,7 @@ class empty * * Interesting users of this class include ns3::Object as well as ns3::Packet. */ -template > +template > class SimpleRefCount : public PARENT { public: diff --git a/src/core/model/string.cc b/src/core/model/string.cc index 510efe3a2..8c3856fb9 100644 --- a/src/core/model/string.cc +++ b/src/core/model/string.cc @@ -30,4 +30,27 @@ namespace ns3 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 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 --git a/src/core/model/string.h b/src/core/model/string.h index f299d90da..b67150c8e 100644 --- a/src/core/model/string.h +++ b/src/core/model/string.h @@ -22,6 +22,7 @@ #include "attribute-helper.h" #include +#include /** * \file @@ -32,6 +33,19 @@ namespace ns3 { +/** Return type of SplitString. */ +using StringVector = std::vector; + +/** + * 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 --git a/src/core/model/system-path.cc b/src/core/model/system-path.cc index 493b7e023..9265ee501 100644 --- a/src/core/model/system-path.cc +++ b/src/core/model/system-path.cc @@ -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 -#include // getenv -#include // strlen #include #include #include @@ -61,6 +61,7 @@ namespace fs = std::experimental::filesystem; #endif #ifdef __linux__ +#include #include #endif @@ -257,19 +258,8 @@ std::list Split(std::string path) { NS_LOG_FUNCTION(path); - std::list 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 items = SplitString(path, SYSTEM_PATH_SEP); + std::list retval(items.begin(), items.end()); return retval; } @@ -280,7 +270,7 @@ Join(std::list::const_iterator begin, std::list::const std::string retval = ""; for (std::list::const_iterator i = begin; i != end; i++) { - if (*i == "") + if ((*i).empty()) { // skip empty strings in the path list continue; @@ -315,15 +305,13 @@ std::string 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("/tmp"); + path = "/tmp"; } } @@ -396,7 +384,7 @@ Exists(const std::string path) 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 --git a/src/core/model/system-path.h b/src/core/model/system-path.h index 745577f9c..f498f1260 100644 --- a/src/core/model/system-path.h +++ b/src/core/model/system-path.h @@ -120,12 +120,12 @@ std::list ReadFiles(std::string path); * * 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 --git a/src/core/model/system-wall-clock-ms.h b/src/core/model/system-wall-clock-ms.h index f95db1b28..c407aac9a 100644 --- a/src/core/model/system-wall-clock-ms.h +++ b/src/core/model/system-wall-clock-ms.h @@ -31,21 +31,6 @@ namespace ns3 { -/** - * \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 --git a/src/core/model/test.cc b/src/core/model/test.cc index b2392a904..688e2c8b6 100644 --- a/src/core/model/test.cc +++ b/src/core/model/test.cc @@ -54,7 +54,7 @@ TestDoubleIsEqual(const double x1, const double x2, const double epsilon) // { double max = (std::fabs(x1) > std::fabs(x2)) ? x1 : x2; - (void)std::frexp(max, &exponent); + std::frexp(max, &exponent); } // @@ -217,7 +217,7 @@ class TestRunnerImpl : public Singleton * * \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::const_iterator begin, std::list::const_iterator end, @@ -424,7 +424,7 @@ TestCase::CreateDataDirFilename(std::string filename) { 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 @@ TestRunnerImpl::FilterTests(std::string testName, // skip test continue; } - if (testName != "" && test->GetName() != testName) + if (!testName.empty() && test->GetName() != testName) { // skip test continue; @@ -1007,7 +1007,7 @@ TestRunnerImpl::Run(int argc, char* argv[]) argi++; } enum TestSuite::Type testType; - if (testTypeString == "") + if (testTypeString.empty()) { testType = TestSuite::ALL; } @@ -1040,7 +1040,7 @@ TestRunnerImpl::Run(int argc, char* argv[]) std::list tests = FilterTests(testName, testType, maximumTestDuration); - if (m_tempDir == "") + if (m_tempDir.empty()) { m_tempDir = SystemPath::MakeTemporaryDirectoryName(); } @@ -1060,7 +1060,7 @@ TestRunnerImpl::Run(int argc, char* argv[]) } std::ostream* os; - if (out != "") + if (!out.empty()) { std::ofstream* ofs; ofs = new std::ofstream(); @@ -1083,11 +1083,17 @@ TestRunnerImpl::Run(int argc, char* argv[]) // 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::const_iterator i = tests.begin(); i != tests.end(); ++i) { TestCase* test = *i; @@ -1128,7 +1134,7 @@ TestRunnerImpl::Run(int argc, char* argv[]) } } - if (out != "") + if (!out.empty()) { delete os; } diff --git a/src/core/model/test.h b/src/core/model/test.h index c4603d67c..ec492b5ac 100644 --- a/src/core/model/test.h +++ b/src/core/model/test.h @@ -225,7 +225,7 @@ namespace tests * \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 @@ class TestCase 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 @@ class TestCase 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 --git a/src/core/model/time.cc b/src/core/model/time.cc index a1b389ba8..fa5e81d1a 100644 --- a/src/core/model/time.cc +++ b/src/core/model/time.cc @@ -203,14 +203,14 @@ struct Time::Resolution& 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 @@ Time::SetResolution(enum Unit resolution) // 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 @@ Time::Clear(Time* const time) // static void -Time::ConvertTimes(const enum Unit unit) +Time::ConvertTimes(const Unit unit) { std::unique_lock lock{g_markingMutex}; @@ -414,7 +414,7 @@ Time::GetResolution() } TimeWithUnit -Time::As(const enum Unit unit /* = Time::AUTO */) const +Time::As(const Unit unit /* = Time::AUTO */) const { return TimeWithUnit(*this, unit); } diff --git a/src/core/model/timer.cc b/src/core/model/timer.cc index cd9bd163b..170091bd8 100644 --- a/src/core/model/timer.cc +++ b/src/core/model/timer.cc @@ -42,7 +42,7 @@ Timer::Timer() NS_LOG_FUNCTION(this); } -Timer::Timer(enum DestroyPolicy destroyPolicy) +Timer::Timer(DestroyPolicy destroyPolicy) : m_flags(destroyPolicy), m_delay(FemtoSeconds(0)), m_event(), diff --git a/src/core/model/timer.h b/src/core/model/timer.h index 563800f54..a3f49061f 100644 --- a/src/core/model/timer.h +++ b/src/core/model/timer.h @@ -121,7 +121,7 @@ class Timer * \param [in] destroyPolicy the event lifetime management policies * to use for destroy events */ - Timer(enum DestroyPolicy destroyPolicy); + Timer(DestroyPolicy destroyPolicy); ~Timer(); /** diff --git a/src/core/model/type-id.cc b/src/core/model/type-id.cc index a4890f41c..485f09c93 100644 --- a/src/core/model/type-id.cc +++ b/src/core/model/type-id.cc @@ -430,7 +430,7 @@ IidManager::AllocateUid(std::string name) } } - struct IidInformation information; + IidInformation information; information.name = name; information.hash = hash; information.parent = 0; @@ -1201,7 +1201,7 @@ TypeId::LookupTraceSourceByName(std::string name, struct TraceSourceInformation* Ptr TypeId::LookupTraceSourceByName(std::string name) const { - struct TraceSourceInformation info; + TraceSourceInformation info; return LookupTraceSourceByName(name, &info); } diff --git a/src/core/model/val-array.h b/src/core/model/val-array.h new file mode 100644 index 000000000..d09edac82 --- /dev/null +++ b/src/core/model/val-array.h @@ -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 + */ +#ifndef VAL_ARRAY_H +#define VAL_ARRAY_H + +#include +#include + +#include +#include +#include + +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 ValArray : public SimpleRefCount> +{ + public: + // instruct the compiler to generate the default constructor + ValArray() = default; + /** + * \brief Constructor that creates "numPages" number of 2D arrays that are of + * dimensions "numRows"x"numCols", and are initialized with all-zero elements. + * 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(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 values to initialize the elements. + * \param values std::valarray that will be used to initialize elements of 1D array + */ + explicit ValArray(const std::valarray& values); + /** + * \brief Constructor creates a single 1D array of values.size () elements and 1 column, + * and moves std::valarray values to initialize the elements. + * \param values std::valarray that will be moved to initialize elements of 1D array + */ + ValArray(std::valarray&& values); + /** + * \brief Constructor creates a single 1D array of values.size () elements and 1 column, + * and uses values std::vector to initialize the elements. + * \param values std::vector that will be used to initialize elements of 1D array + */ + explicit ValArray(const std::vector& values); + /** + * \brief Constructor creates a single 2D array of numRows and numCols, and uses + * std::valarray values to initialize the elements. + * \param numRows the number of rows + * \param numCols the number of columns + * \param values valarray that will be used to initialize elements of 3D array + */ + ValArray(uint16_t numRows, uint16_t numCols, const std::valarray& values); + /** + * \brief Constructor creates a single 2D array of numRows and numCols, and moves + * std::valarray values to initialize the elements. + * \param numRows the number of rows + * \param numCols the number of columns + * \param values valarray that will be used to initialize elements of 3D array + */ + ValArray(uint16_t numRows, uint16_t numCols, std::valarray&& values); + /** + * \brief Constructor creates the 3D array of numRows x numCols x numPages dimensions, + * and uses std::valarray 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 that will be used to initialize elements of 3D array + */ + ValArray(uint16_t numRows, + uint16_t numCols, + uint16_t numPages, + const std::valarray& values); + /** + * \brief Constructor creates the 3D array of numRows x numCols x numPages dimensions, + * and moves std::valarray 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 that will be used to initialize elements of 3D array + */ + ValArray(uint16_t numRows, uint16_t numCols, uint16_t numPages, std::valarray&& values); + /** instruct the compiler to generate the implicitly declared destructor*/ + virtual ~ValArray() = default; + /** instruct the compiler to generate the implicitly declared copy constructor*/ + ValArray(const ValArray&) = default; + /** + * \brief Copy assignment operator. + * Instruct the compiler to generate the implicitly declared copy assignment operator. + * \return a reference to the assigned object + */ + ValArray& operator=(const ValArray&) = default; + /** instruct the compiler to generate the implicitly declared move constructor*/ + ValArray(ValArray&&) = default; + /** + * \brief Move assignment operator. + * Instruct the compiler to generate the implicitly declared move assignment operator. + * \return a reference to the assigned object + */ + ValArray& operator=(ValArray&&) = 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 operator*(const T& rhs) const; + /** + * \brief operator+ definition for ValArray. + * \param rhs The rhs ValArray to be added to this ValArray. + * \return the ValArray instance that holds the results of the operator+ + */ + ValArray operator+(const ValArray& rhs) const; + /** + * \brief binary operator- definition for ValArray. + * \param rhs The rhs ValArray to be subtracted from this ValArray. + * \return the ValArray instance that holds the results of the operator- + */ + ValArray operator-(const ValArray& rhs) const; + /** + * \brief unary operator- definition for ValArray. + * \return the ValArray instance that holds the results of the operator- + */ + ValArray operator-() const; + /** + * \brief operator+= definition for ValArray. + * \param rhs The rhs ValArray to be added to this ValArray. + * \return a reference to this ValArray instance + */ + ValArray& operator+=(const ValArray& rhs); + /** + * \brief operator-= definition for ValArray. + * \param rhs The rhs ValArray to be subtracted from this ValArray. + ** \return a reference to this ValArray instance + */ + ValArray& operator-=(const ValArray& rhs); + /** + * \brief operator== definition for ValArray. + * \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& rhs) const; + /** + * \brief operator!= definition for ValArray. + * \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& 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& 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& 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& 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. + */ + const std::valarray& 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 m_values; //!< The data values +}; + +/************************************************* + ** Class ValArray inline implementations + ************************************************/ + +template +inline uint16_t +ValArray::GetNumRows() const +{ + return m_numRows; +}; + +template +inline uint16_t +ValArray::GetNumCols() const +{ + return m_numCols; +}; + +template +inline uint16_t +ValArray::GetNumPages() const +{ + return m_numPages; +}; + +template +inline size_t +ValArray::GetSize() const +{ + return m_values.size(); +} + +template +inline T& +ValArray::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 +inline const T& +ValArray::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 +inline T& +ValArray::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 +inline const T& +ValArray::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 +inline T& +ValArray::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 +inline const T& +ValArray::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 +inline ValArray +ValArray::operator*(const T& rhs) const +{ + return ValArray(m_numRows, + m_numCols, + m_numPages, + m_values * std::valarray(rhs, m_numRows * m_numCols * m_numPages)); +} + +template +inline ValArray +ValArray::operator+(const ValArray& rhs) const +{ + AssertEqualDims(rhs); + return ValArray(m_numRows, m_numCols, m_numPages, m_values + rhs.m_values); +} + +template +inline ValArray +ValArray::operator-(const ValArray& rhs) const +{ + AssertEqualDims(rhs); + return ValArray(m_numRows, m_numCols, m_numPages, m_values - rhs.m_values); +} + +template +inline ValArray +ValArray::operator-() const +{ + return ValArray(m_numRows, m_numCols, m_numPages, -m_values); +} + +template +inline ValArray& +ValArray::operator+=(const ValArray& rhs) +{ + AssertEqualDims(rhs); + m_values += rhs.m_values; + return *this; +} + +template +inline ValArray& +ValArray::operator-=(const ValArray& rhs) +{ + AssertEqualDims(rhs); + m_values -= rhs.m_values; + return *this; +} + +template +inline T* +ValArray::GetPagePtr(uint16_t pageIndex) +{ + NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index."); + return &(m_values[m_numRows * m_numCols * pageIndex]); +}; + +template +inline const T* +ValArray::GetPagePtr(uint16_t pageIndex) const +{ + NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index."); + return &(m_values[m_numRows * m_numCols * pageIndex]); +}; + +template +inline bool +ValArray::EqualDims(const ValArray& rhs) const +{ + return (m_numRows == rhs.m_numRows) && (m_numCols == rhs.m_numCols) && + (m_numPages == rhs.m_numPages); +} + +template +inline T& +ValArray::operator[](size_t index) +{ + return (*this)(index); +} + +template +inline const T& +ValArray::operator[](size_t index) const +{ + return (*this)(index); +} + +template +inline const std::valarray& +ValArray::GetValues() const +{ + return m_values; +} + +template +inline T& +ValArray::Elem(size_t row, size_t col, size_t page) +{ + return (*this)(row, col, page); +}; + +template +inline const T& +ValArray::Elem(size_t row, size_t col, size_t page) const +{ + return (*this)(row, col, page); +}; + +/************************************************* + ** Class ValArray non-inline implementations + ************************************************/ + +template +ValArray::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 +ValArray::ValArray(const std::valarray& values) + : m_numRows{(uint16_t)values.size()}, + m_numCols{1}, + m_numPages{1}, + m_values{values} +{ +} + +template +ValArray::ValArray(std::valarray&& values) + : m_numRows{(uint16_t)values.size()}, + m_numCols{1}, + m_numPages{1}, + m_values{std::move(values)} +{ +} + +template +ValArray::ValArray(const std::vector& 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 +ValArray::ValArray(uint16_t numRows, uint16_t numCols, const std::valarray& 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 +ValArray::ValArray(uint16_t numRows, uint16_t numCols, std::valarray&& 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 +ValArray::ValArray(uint16_t numRows, + uint16_t numCols, + uint16_t numPages, + const std::valarray& 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 +ValArray::ValArray(uint16_t numRows, + uint16_t numCols, + uint16_t numPages, + std::valarray&& 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 +bool +ValArray::operator==(const ValArray& rhs) const +{ + return EqualDims(rhs) && + std::equal(std::begin(m_values), std::end(m_values), std::begin(rhs.m_values)); +} + +template +bool +ValArray::operator!=(const ValArray& rhs) const +{ + return !((*this) == rhs); +} + +template +bool +ValArray::IsAlmostEqual(const ValArray& 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 +void +ValArray::AssertEqualDims(const ValArray& 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 +std::ostream& +operator<<(std::ostream& os, const ValArray& 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 --git a/src/core/model/valgrind.h b/src/core/model/valgrind.h index 466f61feb..65c66c899 100644 --- a/src/core/model/valgrind.h +++ b/src/core/model/valgrind.h @@ -974,7 +974,7 @@ typedef /* 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 --git a/src/core/model/warnings.h b/src/core/model/warnings.h new file mode 100644 index 000000000..5e38d0134 --- /dev/null +++ b/src/core/model/warnings.h @@ -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 + */ + +#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 --git a/src/core/test/attribute-test-suite.cc b/src/core/test/attribute-test-suite.cc index 4e6a706e1..de6e26b0e 100644 --- a/src/core/test/attribute-test-suite.cc +++ b/src/core/test/attribute-test-suite.cc @@ -265,7 +265,7 @@ class AttributeObjectTest : public Object MakeBooleanChecker()) .AddAttribute("EnumTraceSource", "help text", - EnumValue(false), + EnumValue(TEST_A), MakeEnumAccessor(&AttributeObjectTest::m_enumSrc), MakeEnumChecker(TEST_A, "TestA")) .AddAttribute("ValueClassSource", @@ -495,7 +495,7 @@ class AttributeObjectTest : public Object Ptr m_ptrInitialized; //!< Pointer to Derived class. Ptr m_ptrInitialized2; //!< Pointer to Derived class. TracedValue m_uintSrc; //!< uint8_t Traced value. - TracedValue m_enumSrc; //!< enum Traced value. + TracedValue m_enumSrc; //!< enum Traced value. TracedValue m_doubleSrc; //!< double Traced value. TracedValue m_boolSrc; //!< bool Traced value. Time m_timeWithBounds; //!< Time with bounds @@ -908,16 +908,16 @@ AttributeTestCase::DoRun() // 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 --git a/src/core/test/callback-test-suite.cc b/src/core/test/callback-test-suite.cc index ef76e4469..0e4ef6121 100644 --- a/src/core/test/callback-test-suite.cc +++ b/src/core/test/callback-test-suite.cc @@ -97,7 +97,7 @@ class BasicCallbackTestCase : public TestCase }; /** - * 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 @@ BasicCallbackTarget7(int a) } BasicCallbackTestCase::BasicCallbackTestCase() - : TestCase("Check basic Callback mechansim") + : TestCase("Check basic Callback mechanism") { } @@ -294,7 +294,7 @@ class MakeCallbackTestCase : public TestCase }; /** - * 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 @@ class MakeBoundCallbackTestCase : public TestCase }; /** - * 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 @@ CallbackEqualityTestCase::DoRun() // Make sure that a callback pointing to a lambda and a copy of it compare equal, // after binding the first argument. // - Callback target8b = target7b.Bind(1); - Callback target8c(target7c, 1); + Callback target8b = target7b.Bind(1); + Callback 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 target9b = target8b.Bind(2); - Callback target9c(target8c, 2); + Callback target9b = target8b.Bind(2.0); + Callback target9c(target8c, 2.0); NS_TEST_ASSERT_MSG_EQ(target9b.IsEqual(target9c), true, "Equality test failed"); // diff --git a/src/core/test/environment-variable-test-suite.cc b/src/core/test/environment-variable-test-suite.cc new file mode 100644 index 000000000..2efcce15c --- /dev/null +++ b/src/core/test/environment-variable-test-suite.cc @@ -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. + */ + +#include "ns3/environment-variable.h" +#include "ns3/test.h" + +#include // 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 --git a/src/core/test/int64x64-test-suite.cc b/src/core/test/int64x64-test-suite.cc index 0822f8d65..b3374563e 100644 --- a/src/core/test/int64x64-test-suite.cc +++ b/src/core/test/int64x64-test-suite.cc @@ -212,7 +212,7 @@ class Int64x64IntRoundTestCase : public TestCase * 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 @@ class Int64x64CompareTestCase : public TestCase * 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 @@ class Int64x64InvertTestCase : public TestCase * \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 --git a/src/core/test/length-test-suite.cc b/src/core/test/length-test-suite.cc index 5965c9f98..1d2003fa5 100644 --- a/src/core/test/length-test-suite.cc +++ b/src/core/test/length-test-suite.cc @@ -631,17 +631,19 @@ LengthTestCase::TestBuilderFreeFunctions() double inputValue = 10; - std::map 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 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 @@ LengthTestCase::TestTryParseReturnsTrue() { using TestInput = std::pair; using TestArgs = std::pair; - std::map tests{{{5, "m"}, {5, 0}}, - {{5, " m"}, {5, 0}}, - {{5, "kilometer"}, {5e3, 0}}, - {{5, " kilometer"}, {5e3, 0}}}; + std::map tests{ + {{5, "m"}, {5, 0}}, + {{5, " m"}, {5, 0}}, + {{5, "kilometer"}, {5e3, 0}}, + {{5, " kilometer"}, {5e3, 0}}, + }; for (auto& entry : tests) { diff --git a/src/core/test/matrix-array-test-suite.cc b/src/core/test/matrix-array-test-suite.cc new file mode 100644 index 000000000..77a016e89 --- /dev/null +++ b/src/core/test/matrix-array-test-suite.cc @@ -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 + */ + +#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 MatrixArrayTestCase : public TestCase +{ + public: + MatrixArrayTestCase() = default; + /** + * Constructor + * + * \param [in] name reference name + */ + MatrixArrayTestCase(const std::string name); + + /** Destructor. */ + ~MatrixArrayTestCase() override; + /** + * \brief Copy constructor. + * Instruct the compiler to generate the implicitly declared copy constructor + */ + MatrixArrayTestCase(const MatrixArrayTestCase&) = default; + /** + * \brief Copy assignment operator. + * Instruct the compiler to generate the implicitly declared copy assignment operator. + * \return A reference to this MatrixArrayTestCase + */ + MatrixArrayTestCase& operator=(const MatrixArrayTestCase&) = default; + /** + * \brief Move constructor. + * Instruct the compiler to generate the implicitly declared move constructor + */ + MatrixArrayTestCase(MatrixArrayTestCase&&) = default; + /** + * \brief Move assignment operator. + * Instruct the compiler to generate the implicitly declared copy constructor + * \return A reference to this MatrixArrayTestCase + */ + MatrixArrayTestCase& operator=(MatrixArrayTestCase&&) = default; + + protected: + private: + void DoRun() override; +}; + +template +MatrixArrayTestCase::MatrixArrayTestCase(const std::string name) + : TestCase(name) +{ +} + +template +MatrixArrayTestCase::~MatrixArrayTestCase() +{ +} + +template +void +MatrixArrayTestCase::DoRun() +{ + // test multiplication of matrices (MatrixArray containing only 1 matrix) + MatrixArray m1 = MatrixArray(2, 3); + MatrixArray m2 = MatrixArray(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 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 m4 = m3 * (static_cast(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(5.0)), + m4(i, j), + "The values are not equal"); + } + } + NS_LOG_INFO("m4 = m3 * 5:" << m4); + + // test multiplication of arrays of matrices + MatrixArray m5 = MatrixArray(2, 3, 2); + MatrixArray m6 = MatrixArray(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 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 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 m9 = MatrixArray(std::vector({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 m10 = + MatrixArray(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 a{0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5}; + std::valarray b{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}; + std::valarray c{2, 3, 4, 6, 2, 3, 4, 6}; + std::valarray aCasted(a.size()); + std::valarray bCasted(b.size()); + std::valarray cCasted(c.size()); + + for (size_t i = 0; i < a.size(); ++i) + { + aCasted[i] = static_cast(a[i]); + } + for (size_t i = 0; i < b.size(); ++i) + { + bCasted[i] = static_cast(b[i]); + } + for (size_t i = 0; i < c.size(); ++i) + { + cCasted[i] = static_cast(c[i]); + } + MatrixArray m11 = MatrixArray(2, 3, 2, aCasted); + MatrixArray m12 = MatrixArray(3, 2, 2, bCasted); + MatrixArray m13 = m11 * m12; + MatrixArray m14 = MatrixArray(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 d{1, 1, 1}; + std::valarray e{1, 1}; + std::valarray f{1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3}; + std::valarray g{12, 12}; + std::valarray dCasted(d.size()); + std::valarray eCasted(e.size()); + std::valarray fCasted(f.size()); + std::valarray gCasted(g.size()); + for (size_t i = 0; i < d.size(); ++i) + { + dCasted[i] = static_cast(d[i]); + } + for (size_t i = 0; i < e.size(); ++i) + { + eCasted[i] = static_cast(e[i]); + } + for (size_t i = 0; i < f.size(); ++i) + { + fCasted[i] = static_cast(f[i]); + } + for (size_t i = 0; i < g.size(); ++i) + { + gCasted[i] = static_cast(g[i]); + } + MatrixArray m15 = MatrixArray(1, 3, dCasted); + MatrixArray m16 = MatrixArray(2, 1, eCasted); + MatrixArray m17 = MatrixArray(3, 2, 2, fCasted); + MatrixArray m18 = MatrixArray(1, 1, 2, gCasted); + MatrixArray m19 = m17.MultiplyByLeftAndRightMatrix(m15, m16); + NS_TEST_ASSERT_MSG_EQ(m19, m18, "The matrices should be equal."); + + // test MultiplyByLeftAndRightMatrix + std::valarray h{1, 3, 2, 2, 4, 0}; + std::valarray j{2, 2, 3, 4, 1, 3, 0, 5}; + std::valarray 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 l{144, 132, 128, 104, 144, 132, 128, 104}; + std::valarray hCasted(h.size()); + std::valarray jCasted(j.size()); + std::valarray kCasted(k.size()); + std::valarray lCasted(l.size()); + for (size_t i = 0; i < h.size(); ++i) + { + hCasted[i] = static_cast(h[i]); + } + for (size_t i = 0; i < j.size(); ++i) + { + jCasted[i] = static_cast(j[i]); + } + for (size_t i = 0; i < k.size(); ++i) + { + kCasted[i] = static_cast(k[i]); + } + for (size_t i = 0; i < l.size(); ++i) + { + lCasted[i] = static_cast(l[i]); + } + MatrixArray m20 = MatrixArray(2, 3, hCasted); + MatrixArray m21 = MatrixArray(4, 2, jCasted); + MatrixArray m22 = MatrixArray(3, 4, 2, kCasted); + MatrixArray m23 = MatrixArray(2, 2, 2, lCasted); + MatrixArray 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 m25 = MatrixArray(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 m26 = MatrixArray(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 m27 = MatrixArray(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("Test MatrixArray")); + AddTestCase( + new MatrixArrayTestCase>("Test MatrixArray>")); + AddTestCase(new MatrixArrayTestCase("Test MatrixArray")); +} + +/** + * \ingroup matrixArray-tests + * MatrixArrayTestSuite instance variable. + */ +static MatrixArrayTestSuite g_matrixArrayTestSuite; + +} // namespace tests +} // namespace ns3 diff --git a/src/core/test/names-test-suite.cc b/src/core/test/names-test-suite.cc index f2930f184..30d2532fc 100644 --- a/src/core/test/names-test-suite.cc +++ b/src/core/test/names-test-suite.cc @@ -713,7 +713,7 @@ FindPathTestCase::DoRun() Ptr objectNotThere = CreateObject(); 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 --git a/src/core/test/random-variable-stream-test-suite.cc b/src/core/test/random-variable-stream-test-suite.cc index ad88bded5..610ea0d26 100644 --- a/src/core/test/random-variable-stream-test-suite.cc +++ b/src/core/test/random-variable-stream-test-suite.cc @@ -1835,7 +1835,7 @@ ErlangTestCase::ChiSquaredTest(Ptr rng) const 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 @@ ErlangAntitheticTestCase::ChiSquaredTest(Ptr rng) const 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 --git a/src/core/test/simulator-test-suite.cc b/src/core/test/simulator-test-suite.cc index 76515dde2..e0824ea6b 100644 --- a/src/core/test/simulator-test-suite.cc +++ b/src/core/test/simulator-test-suite.cc @@ -73,7 +73,7 @@ class SimulatorEventsTestCase : public TestCase */ uint64_t NowUs(); /** - * Checks that the events has been detroyed. + * Checks that the events has been destroyed. */ void Destroy(); /** diff --git a/src/core/test/splitstring-test-suite.cc b/src/core/test/splitstring-test-suite.cc new file mode 100644 index 000000000..48dadea2d --- /dev/null +++ b/src/core/test/splitstring-test-suite.cc @@ -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. + */ + +#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 --git a/src/core/test/threaded-test-suite.cc b/src/core/test/threaded-test-suite.cc index 4a5e45383..da75f31ce 100644 --- a/src/core/test/threaded-test-suite.cc +++ b/src/core/test/threaded-test-suite.cc @@ -289,11 +289,16 @@ class ThreadedSimulatorTestSuite : public TestSuite 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 --git a/src/core/test/time-test-suite.cc b/src/core/test/time-test-suite.cc index 211e5b7fa..1b522a3c2 100644 --- a/src/core/test/time-test-suite.cc +++ b/src/core/test/time-test-suite.cc @@ -94,7 +94,7 @@ class TimeSimpleTestCase : public TestCase * \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 @@ TimeSimpleTestCase::DoTimeOperations() 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 --git a/src/core/test/val-array-test-suite.cc b/src/core/test/val-array-test-suite.cc new file mode 100644 index 000000000..7da8eed07 --- /dev/null +++ b/src/core/test/val-array-test-suite.cc @@ -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 + */ + +#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 ValArrayTestCase : public TestCase +{ + public: + /** Default constructor*/ + ValArrayTestCase() = default; + /** + * Constructor + * + * \param [in] name reference name + */ + ValArrayTestCase(const std::string name); + + /** Destructor. */ + ~ValArrayTestCase() override; + /** + * \brief Copy constructor. + * Instruct the compiler to generate the implicitly declared copy constructor + */ + ValArrayTestCase(const ValArrayTestCase&) = default; + /** + * \brief Copy assignment operator. + * Instruct the compiler to generate the implicitly declared copy assignment operator. + * \return A reference to this ValArrayTestCase + */ + ValArrayTestCase& operator=(const ValArrayTestCase&) = default; + /** + * \brief Move constructor. + * Instruct the compiler to generate the implicitly declared move constructor + */ + ValArrayTestCase(ValArrayTestCase&&) = default; + /** + * \brief Move assignment operator. + * Instruct the compiler to generate the implicitly declared copy constructor + * \return A reference to this ValArrayTestCase + */ + ValArrayTestCase& operator=(ValArrayTestCase&&) = default; + + private: + void DoRun() override; +}; + +template +ValArrayTestCase::ValArrayTestCase(const std::string name) + : TestCase(name) +{ +} + +template +ValArrayTestCase::~ValArrayTestCase() +{ +} + +template +void +ValArrayTestCase::DoRun() +{ + ValArray v1 = ValArray(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 v2 = ValArray(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 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 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 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 initArray1{0, 1, 2, 3, 4, 5, 6, 7}; + std::valarray valArray1(initArray1.size()); // length is 8 elements + for (size_t i = 0; i < initArray1.size(); i++) + { + valArray1[i] = static_cast(initArray1[i]); + } + ValArray v6 = ValArray(2, 4, valArray1); + + // test constructor that moves valArray + NS_LOG_INFO("valarray1 size before move: " << valArray1.size()); + ValArray v11 = ValArray(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 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(initArray2[testIndex]), + "The values are not equal."); + testIndex++; + } + } + + // test constructor with initialization valArray for 3D array + std::valarray initArray3{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}; + std::valarray valArray2(initArray3.size()); // length is 8 elements + for (size_t i = 0; i < initArray3.size(); i++) + { + valArray2[i] = static_cast(initArray3[i]); + } + + ValArray v7 = ValArray(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 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(initArray4[testIndex]), + "The values are not equal."); + testIndex++; + } + } + } + + // multiplication with a scalar value with 3D array + ValArray v8 = v7 * (static_cast(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(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 v9(v8.GetNumRows(), v8.GetNumCols(), v8.GetNumPages()); + v9 += v8; + NS_LOG_INFO("v9 += v8" << v9); + ValArray 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(std::valarray({1, 2, 3})), + ValArray(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(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 v12 = ValArray(std::vector({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("Test ValArray")); + AddTestCase(new ValArrayTestCase>("Test ValArray>")); + AddTestCase(new ValArrayTestCase("Test ValArray")); +} + +/** + * \ingroup valArray-tests + * ValArrayTestSuite instance variable. + */ +static ValArrayTestSuite g_valArrayTestSuite; + +} // namespace tests +} // namespace ns3 diff --git a/src/csma-layout/examples/csma-star.cc b/src/csma-layout/examples/csma-star.cc index ec55b9484..859cd8ea2 100644 --- a/src/csma-layout/examples/csma-star.cc +++ b/src/csma-layout/examples/csma-star.cc @@ -116,7 +116,7 @@ main(int argc, char* argv[]) // 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 --git a/src/csma/doc/csma.rst b/src/csma/doc/csma.rst index 647a2c295..13cc3c876 100644 --- a/src/csma/doc/csma.rst +++ b/src/csma/doc/csma.rst @@ -210,24 +210,24 @@ helper. You just ask this helper to create as many computers (we call them ``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 @@ Although the ns-3 CsmaChannel and CsmaNetDevice does not model any kind of 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 --git a/src/csma/examples/csma-broadcast.cc b/src/csma/examples/csma-broadcast.cc index 5854824ec..10efb8ebd 100644 --- a/src/csma/examples/csma-broadcast.cc +++ b/src/csma/examples/csma-broadcast.cc @@ -121,4 +121,6 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); NS_LOG_INFO("Done."); + + return 0; } diff --git a/src/csma/examples/csma-multicast.cc b/src/csma/examples/csma-multicast.cc index ab166d3b1..61d51a712 100644 --- a/src/csma/examples/csma-multicast.cc +++ b/src/csma/examples/csma-multicast.cc @@ -178,4 +178,6 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); NS_LOG_INFO("Done."); + + return 0; } diff --git a/src/csma/examples/csma-one-subnet.cc b/src/csma/examples/csma-one-subnet.cc index 3a5c9e6a6..e05de1fc5 100644 --- a/src/csma/examples/csma-one-subnet.cc +++ b/src/csma/examples/csma-one-subnet.cc @@ -138,4 +138,6 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); NS_LOG_INFO("Done."); + + return 0; } diff --git a/src/csma/examples/csma-packet-socket.cc b/src/csma/examples/csma-packet-socket.cc index f94414f90..79cfd927d 100644 --- a/src/csma/examples/csma-packet-socket.cc +++ b/src/csma/examples/csma-packet-socket.cc @@ -40,7 +40,7 @@ using namespace ns3; NS_LOG_COMPONENT_DEFINE("CsmaPacketSocketExample"); -/// Ouput stream. +/// Output stream. std::ofstream g_os; /** diff --git a/src/csma/examples/csma-ping.cc b/src/csma/examples/csma-ping.cc index c6b208768..14389631a 100644 --- a/src/csma/examples/csma-ping.cc +++ b/src/csma/examples/csma-ping.cc @@ -47,25 +47,31 @@ NS_LOG_COMPONENT_DEFINE("CsmaPingExample"); static void SinkRx(Ptr 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 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) // 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 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); NS_LOG_INFO("Done."); + + return 0; } diff --git a/src/csma/examples/csma-raw-ip-socket.cc b/src/csma/examples/csma-raw-ip-socket.cc index 1e827f563..698baa4a7 100644 --- a/src/csma/examples/csma-raw-ip-socket.cc +++ b/src/csma/examples/csma-raw-ip-socket.cc @@ -121,4 +121,6 @@ main(int argc, char* argv[]) Simulator::Run(); Simulator::Destroy(); NS_LOG_INFO("Done."); + + return 0; } diff --git a/src/csma/model/backoff.cc b/src/csma/model/backoff.cc index 70bf5a34c..656156076 100644 --- a/src/csma/model/backoff.cc +++ b/src/csma/model/backoff.cc @@ -88,7 +88,7 @@ Backoff::ResetBackoffTime() } bool -Backoff::MaxRetriesReached() +Backoff::MaxRetriesReached() const { return (m_numBackoffRetries >= m_maxRetries); } diff --git a/src/csma/model/backoff.h b/src/csma/model/backoff.h index 4b5071e50..40ec8e15a 100644 --- a/src/csma/model/backoff.h +++ b/src/csma/model/backoff.h @@ -97,7 +97,7 @@ class Backoff /** * \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 @@ class Backoff 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 --git a/src/csma/model/csma-channel.cc b/src/csma/model/csma-channel.cc index 959fbc51e..9f1fad3ce 100644 --- a/src/csma/model/csma-channel.cc +++ b/src/csma/model/csma-channel.cc @@ -362,7 +362,7 @@ CsmaDeviceRec::CsmaDeviceRec(const CsmaDeviceRec& deviceRec) } bool -CsmaDeviceRec::IsActive() +CsmaDeviceRec::IsActive() const { return active; } diff --git a/src/csma/model/csma-channel.h b/src/csma/model/csma-channel.h index 8de1b5fd4..da4e948da 100644 --- a/src/csma/model/csma-channel.h +++ b/src/csma/model/csma-channel.h @@ -65,7 +65,7 @@ class CsmaDeviceRec * \return If the net device pointed to by the devicePtr is active * and ready to RX/TX. */ - bool IsActive(); + bool IsActive() const; }; /** diff --git a/src/csma/model/csma-net-device.cc b/src/csma/model/csma-net-device.cc index 9180e6c14..33d555e15 100644 --- a/src/csma/model/csma-net-device.cc +++ b/src/csma/model/csma-net-device.cc @@ -223,7 +223,7 @@ CsmaNetDevice::DoDispose() } void -CsmaNetDevice::SetEncapsulationMode(enum EncapsulationMode mode) +CsmaNetDevice::SetEncapsulationMode(EncapsulationMode mode) { NS_LOG_FUNCTION(mode); @@ -274,14 +274,14 @@ CsmaNetDevice::SetReceiveEnable(bool receiveEnable) } 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 @@ CsmaNetDevice::AddHeader(Ptr p, // // 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 --git a/src/csma/model/csma-net-device.h b/src/csma/model/csma-net-device.h index 4c7af46c8..237daf37e 100644 --- a/src/csma/model/csma-net-device.h +++ b/src/csma/model/csma-net-device.h @@ -178,7 +178,7 @@ class CsmaNetDevice : public NetDevice * * \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 @@ class CsmaNetDevice : public NetDevice * * \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 --git a/src/dsdv/examples/dsdv-manet.cc b/src/dsdv/examples/dsdv-manet.cc index cff66e7ac..63ca9ef19 100644 --- a/src/dsdv/examples/dsdv-manet.cc +++ b/src/dsdv/examples/dsdv-manet.cc @@ -377,7 +377,7 @@ DsdvManetExample::InstallInternetStack(std::string tr_name) { Ptr routingStream = Create((tr_name + ".routes"), std::ios::out); - dsdv.PrintRoutingTableAllAt(Seconds(m_periodicUpdateInterval), routingStream); + Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(m_periodicUpdateInterval), routingStream); } } diff --git a/src/dsdv/model/dsdv-packet-queue.cc b/src/dsdv/model/dsdv-packet-queue.cc index 9cab56e9c..40c447a14 100644 --- a/src/dsdv/model/dsdv-packet-queue.cc +++ b/src/dsdv/model/dsdv-packet-queue.cc @@ -53,7 +53,7 @@ PacketQueue::GetSize() 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::const_iterator i = m_queue.begin(); i != m_queue.end(); ++i) @@ -74,7 +74,7 @@ PacketQueue::Enqueue(QueueEntry& entry) } else { - // NS_LOG_DEBUG("Packet size while enqueing "<GetSize()); + // NS_LOG_DEBUG("Packet size while enqueuing "<GetSize()); entry.SetExpireTime(m_queueTimeout); m_queue.push_back(entry); return true; diff --git a/src/dsdv/model/dsdv-routing-protocol.cc b/src/dsdv/model/dsdv-routing-protocol.cc index cc4e7142f..0548066e6 100644 --- a/src/dsdv/model/dsdv-routing-protocol.cc +++ b/src/dsdv/model/dsdv-routing-protocol.cc @@ -826,7 +826,7 @@ RoutingProtocol::RecvDsdv(Ptr socket) } std::map 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 @@ RoutingProtocol::MergeTriggerPeriodicUpdates() "Merging advertised table changes with main table before sending out periodic update"); std::map allRoutes; m_advRoutingTable.GetListOfAllRoutes(allRoutes); - if (allRoutes.size() > 0) + if (!allRoutes.empty()) { for (std::map::const_iterator i = allRoutes.begin(); i != allRoutes.end(); diff --git a/src/dsdv/model/dsdv-routing-protocol.h b/src/dsdv/model/dsdv-routing-protocol.h index 2c7495041..e8b285210 100644 --- a/src/dsdv/model/dsdv-routing-protocol.h +++ b/src/dsdv/model/dsdv-routing-protocol.h @@ -179,7 +179,7 @@ class RoutingProtocol : public Ipv4RoutingProtocol 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 --git a/src/dsdv/model/dsdv-rtable.h b/src/dsdv/model/dsdv-rtable.h index 84ecb5f3f..7c2b29253 100644 --- a/src/dsdv/model/dsdv-rtable.h +++ b/src/dsdv/model/dsdv-rtable.h @@ -432,7 +432,7 @@ class RoutingTable */ 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 --git a/src/dsdv/test/dsdv-testcase.cc b/src/dsdv/test/dsdv-testcase.cc index cf57a1cf6..0248c4ec8 100644 --- a/src/dsdv/test/dsdv-testcase.cc +++ b/src/dsdv/test/dsdv-testcase.cc @@ -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 @@ DsdvHeaderTestCase::DoRun() /** * \ingroup dsdv-test - * \ingroup tests * * \brief DSDV routing table tests (adding and looking up routes) */ @@ -213,7 +212,6 @@ DsdvTableTestCase::DoRun() /** * \ingroup dsdv-test - * \ingroup tests * * \brief DSDV test suite */ diff --git a/src/dsr/doc/dsr.rst b/src/dsr/doc/dsr.rst index 6cd91411b..de1286748 100644 --- a/src/dsr/doc/dsr.rst +++ b/src/dsr/doc/dsr.rst @@ -162,7 +162,7 @@ and DsrMainHelpers in your simulation script. For instance: 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 --git a/src/dsr/examples/dsr.cc b/src/dsr/examples/dsr.cc index 3cb0babd7..39b6373c0 100644 --- a/src/dsr/examples/dsr.cc +++ b/src/dsr/examples/dsr.cc @@ -214,4 +214,6 @@ main(int argc, char* argv[]) Simulator::Stop(Seconds(TotalTime)); Simulator::Run(); Simulator::Destroy(); + + return 0; } diff --git a/src/dsr/model/dsr-fs-header.cc b/src/dsr/model/dsr-fs-header.cc index b70701146..f96ada4a5 100644 --- a/src/dsr/model/dsr-fs-header.cc +++ b/src/dsr/model/dsr-fs-header.cc @@ -273,7 +273,7 @@ DsrOptionField::CalculatePad(DsrOptionHeader::Alignment alignment) const } uint32_t -DsrOptionField::GetDsrOptionsOffset() +DsrOptionField::GetDsrOptionsOffset() const { return m_optionsOffset; } diff --git a/src/dsr/model/dsr-fs-header.h b/src/dsr/model/dsr-fs-header.h index 29c27ff9b..b166f7aac 100644 --- a/src/dsr/model/dsr-fs-header.h +++ b/src/dsr/model/dsr-fs-header.h @@ -247,7 +247,7 @@ class DsrOptionField * 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 --git a/src/dsr/model/dsr-option-header.cc b/src/dsr/model/dsr-option-header.cc index 3dd9905f7..f12d092ac 100644 --- a/src/dsr/model/dsr-option-header.cc +++ b/src/dsr/model/dsr-option-header.cc @@ -1085,15 +1085,15 @@ DsrOptionRerrUnsupportHeader::GetErrorDst() const } 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 @@ DsrOptionRerrUnsupportHeader::Print(std::ostream& os) const 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 @@ DsrOptionRerrUnsupportHeader::Serialize(Buffer::Iterator start) const 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 @@ DsrOptionRerrUnsupportHeader::Deserialize(Buffer::Iterator start) m_salvage = i.ReadU8(); ReadFrom(i, m_errorSrcAddress); ReadFrom(i, m_errorDstAddress); - m_unsupport = i.ReadU16(); + m_unsupported = i.ReadU16(); return GetSerializedSize(); } diff --git a/src/dsr/model/dsr-option-header.h b/src/dsr/model/dsr-option-header.h index 24e2adf37..86fe5ad0d 100644 --- a/src/dsr/model/dsr-option-header.h +++ b/src/dsr/model/dsr-option-header.h @@ -1136,7 +1136,7 @@ class DsrOptionRerrUnsupportHeader : public DsrOptionRerrHeader /** * \brief The unsupported option */ - uint16_t m_unsupport; + uint16_t m_unsupported; }; /** diff --git a/src/dsr/model/dsr-options.cc b/src/dsr/model/dsr-options.cc index d8c458284..08bfdb3e4 100644 --- a/src/dsr/model/dsr-options.cc +++ b/src/dsr/model/dsr-options.cc @@ -266,7 +266,7 @@ DsrOptions::PrintVector(std::vector& vec) /* * Check elements in a route vector */ - if (!vec.size()) + if (vec.empty()) { NS_LOG_DEBUG("The vector is empty"); } @@ -1112,7 +1112,7 @@ DsrOptionRrep::Process(Ptr packet, { 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 --git a/src/dsr/model/dsr-rcache.cc b/src/dsr/model/dsr-rcache.cc index 61fb5467a..d0cc2e1db 100644 --- a/src/dsr/model/dsr-rcache.cc +++ b/src/dsr/model/dsr-rcache.cc @@ -374,7 +374,7 @@ DsrRouteCache::RebuildBestRouteTable(Ipv4Address source) 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 @@ DsrRouteCache::DeleteAllRoutesIncludeLink(Ipv4Address errorSrc, // 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 @@ DsrRouteCache::DeleteAllRoutesIncludeLink(Ipv4Address errorSrc, std::map::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 @@ DsrRouteCache::DeleteAllRoutesIncludeLink(Ipv4Address errorSrc, 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 @@ DsrRouteCache::DeleteAllRoutesIncludeLink(Ipv4Address errorSrc, 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 @@ DsrRouteCache::DeleteAllRoutesIncludeLink(Ipv4Address errorSrc, { 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 @@ DsrRouteCache::PrintVector(std::vector& vec) /* * 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 @@ DsrRouteCache::Purge() Ipv4Address dst = i->first; std::list rtVector = i->second; NS_LOG_DEBUG("The route vector size of 1 " << dst << " " << rtVector.size()); - if (rtVector.size()) + if (!rtVector.empty()) { for (std::list::iterator j = rtVector.begin(); j != rtVector.end();) { @@ -1054,7 +1054,7 @@ DsrRouteCache::Purge() } } 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 --git a/src/dsr/model/dsr-routing.cc b/src/dsr/model/dsr-routing.cc index 4ec41cba5..e4a1c4825 100644 --- a/src/dsr/model/dsr-routing.cc +++ b/src/dsr/model/dsr-routing.cc @@ -551,7 +551,7 @@ DsrRouting::GetElementsFromContext(std::string context) std::vector 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 @@ DsrRouting::PrintVector(std::vector& vec) /* * Check elements in a route vector */ - if (!vec.size()) + if (vec.empty()) { NS_LOG_DEBUG("The vector is empty"); } diff --git a/src/dsr/model/dsr-rreq-table.h b/src/dsr/model/dsr-rreq-table.h index 7ac70d8ff..eade4d39f 100644 --- a/src/dsr/model/dsr-rreq-table.h +++ b/src/dsr/model/dsr-rreq-table.h @@ -199,7 +199,7 @@ class DsrReceivedRreqEntry // \} 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 --git a/src/energy/doc/energy.rst b/src/energy/doc/energy.rst index c894df943..6ee7308ba 100644 --- a/src/energy/doc/energy.rst +++ b/src/energy/doc/energy.rst @@ -162,34 +162,34 @@ of real measurements. 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. +[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. +[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. +[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. +[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. +[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 --git a/src/energy/examples/CMakeLists.txt b/src/energy/examples/CMakeLists.txt index b63b3152f..8fa2b88f5 100644 --- a/src/energy/examples/CMakeLists.txt +++ b/src/energy/examples/CMakeLists.txt @@ -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 --git a/src/energy/examples/li-ion-energy-source.cc b/src/energy/examples/li-ion-energy-source-example.cc similarity index 99% rename from src/energy/examples/li-ion-energy-source.cc rename to src/energy/examples/li-ion-energy-source-example.cc index 58ee0f329..f3c02f01e 100644 --- a/src/energy/examples/li-ion-energy-source.cc +++ b/src/energy/examples/li-ion-energy-source-example.cc @@ -17,10 +17,9 @@ * Author: Andrea Sacco */ -#include "ns3/li-ion-energy-source.h" - #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" @@ -100,4 +99,6 @@ main(int argc, char** argv) DoubleValue v; es->GetAttribute("ThresholdVoltage", v); NS_ASSERT(es->GetSupplyVoltage() <= v.Get()); + + return 0; } diff --git a/src/energy/examples/rv-battery-model-test.cc b/src/energy/examples/rv-battery-model-test.cc index 42a88469b..051bc0049 100644 --- a/src/energy/examples/rv-battery-model-test.cc +++ b/src/energy/examples/rv-battery-model-test.cc @@ -73,7 +73,7 @@ class BatteryLifetimeTest * 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 @@ class BatteryLifetimeTest */ bool VariableLoadTest(std::vector loads, std::vector