Contenuto principale

Create and Simulate Wireless Network of Custom Nodes

Modeling wireless nodes accurately is a fundamental aspect of network simulation. With Wireless Network Toolbox™, you can design custom nodes to meet your simulation needs. This example shows how to create custom nodes from the base class wnet.Node and simulate a simple scenario with one transmitter and one receiver, without implementing specific network protocols.

Implement Custom Wireless Node

To create a custom node using the wnet.Node base class of Wireless Network Toolbox, follow these steps:

  • Inherit from the wnet.Node base class. The class definition must have this format, where hCustomNode is the name of your custom node class.

classdef hCustomNode < wnet.Node
   ...
 end
  • Override any of the base class public methods.

  • Save the class definition in an .m file.

  • In a simulation script, create an object of the customNode class.

This example implements a custom node using the wnet.Node base class. The model is attached to this example as a supporting file. For more information on the implementation of the model, see Supporting File. The hCustomNode class enables you model a wireless node that acts as either a transmitter or a receiver. As a transmitter, the node probabilistically generates and buffers wireless packets with configurable radio parameters and random in-phase and quadrature (IQ) data. As a receiver, the node accepts, processes, and clears incoming packets. The class provides event-driven callbacks for both transmitting and receiving. It also enables you to create multiple nodes at once.

Simulate Network

Set the random number generator.

rng("default");

Initialize the wireless network simulator

simulator = wirelessNetworkSimulator.init;

Create a transmitter node at position [10 30 0] using the hCustomNode helper object, attached to this example as a supporting file.

txNode = hCustomNode("Position",[10 30 0],IsTransmitter=true,Name="Transmitter");

Create a receiver nodes at position [300 0 0].

rxNode = hCustomNode(Position=[300 0 0],IsTransmitter=false,Name="Receiver1");

Add the transmitter and receiver nodes to the simulator.

addNodes(simulator,txNode)
addNodes(simulator,rxNode)

Initialize the wireless network viewer.

visualizer = wirelessNetworkViewer;

Add the nodes to the wireless network viewer.

addNodes(visualizer,txNode)
addNodes(visualizer,rxNode)

Register a callback function to display information when the transmitter starts transmission.

registerEventCallback(txNode,"TransmissionStarted",@displayInfo)
registerEventCallback(rxNode,"ReceptionEnded",@displayReceptionInfo)

Install a random-walk mobility model on the receiver node, confining its movement within a circular boundary and setting its speed range from 5 to 10 meters per second.

addMobility(rxNode,MobilityModel="random-walk", ...
    BoundaryShape="circle",SpeedRange=[5 10]);

Run the simulation for 10 seconds.

run(simulator,10);
            NodeID: 1
          NodeName: "Transmitter"
    TechnologyType: 101
         EventName: "TransmissionStarted"
         Timestamp: 0
         EventData: [1×1 struct]

[Receiver1 @ t=0.0000 s] Received packet #1 from Node 1 (Power: -66.3 dBm, Duration: 1.000 ms)
            NodeID: 1
          NodeName: "Transmitter"
    TechnologyType: 101
         EventName: "TransmissionStarted"
         Timestamp: 3
         EventData: [1×1 struct]

[Receiver1 @ t=3.0000 s] Received packet #2 from Node 1 (Power: -66.6 dBm, Duration: 1.000 ms)
            NodeID: 1
          NodeName: "Transmitter"
    TechnologyType: 101
         EventName: "TransmissionStarted"
         Timestamp: 5
         EventData: [1×1 struct]

[Receiver1 @ t=5.0000 s] Received packet #3 from Node 1 (Power: -66.5 dBm, Duration: 1.000 ms)
            NodeID: 1
          NodeName: "Transmitter"
    TechnologyType: 101
         EventName: "TransmissionStarted"
         Timestamp: 6
         EventData: [1×1 struct]

[Receiver1 @ t=6.0000 s] Received packet #4 from Node 1 (Power: -66.3 dBm, Duration: 1.000 ms)
            NodeID: 1
          NodeName: "Transmitter"
    TechnologyType: 101
         EventName: "TransmissionStarted"
         Timestamp: 7
         EventData: [1×1 struct]

[Receiver1 @ t=7.0000 s] Received packet #5 from Node 1 (Power: -66.1 dBm, Duration: 1.000 ms)
            NodeID: 1
          NodeName: "Transmitter"
    TechnologyType: 101
         EventName: "TransmissionStarted"
         Timestamp: 9
         EventData: [1×1 struct]

[Receiver1 @ t=9.0000 s] Received packet #6 from Node 1 (Power: -66.1 dBm, Duration: 1.000 ms)
            NodeID: 1
          NodeName: "Transmitter"
    TechnologyType: 101
         EventName: "TransmissionStarted"
         Timestamp: 10
         EventData: [1×1 struct]

[Receiver1 @ t=10.0000 s] Received packet #7 from Node 1 (Power: -66.1 dBm, Duration: 1.000 ms)

Figure Wireless Network Viewer contains an axes object. The axes object with xlabel X-axis (m), ylabel Y-axis (m) contains 6 objects of type line, text. One or more of the lines displays its values using only markers This object represents hCustomNode.

This figure shows the nodes in the network and the trajectory of the mobility pattern for the node with mobility enabled.

Use this callback function to display event information.

function displayInfo(event)
% Display transmitted packet information
disp(event)
end

function displayReceptionInfo(event)
% Display received packet information
eventData = event.EventData;
fprintf('[%s @ t=%.4f s] Received packet #%d from Node %d (Power: %.1f dBm, Duration: %.3f ms)\n', ...
    event.NodeName, event.Timestamp, eventData.PacketNumber, ...
    eventData.TransmitterID,eventData.ReceivedPower,eventData.Duration*1000);
end

Obtain the statistics for the transmitter and receiver nodes.

txStats = statistics(txNode)
txStats = struct with fields:
                       ID: 1
                     Name: "Transmitter"
            IsTransmitter: 1
    NumPacketsTransmitted: 7
       NumPacketsReceived: 0

rxStats = statistics(rxNode)
rxStats = struct with fields:
                       ID: 2
                     Name: "Receiver1"
            IsTransmitter: 0
    NumPacketsTransmitted: 0
       NumPacketsReceived: 7

Supporting File

  • hCustomNode.m — Implements a custom wireless node.

classdef hCustomNode < wnet.Node
    % hCustomNode Implements a custom wireless node for simulation
    %
    % This class extends the base wnet.Node class to add custom behavior for
    % transmitter and receiver nodes, including packet generation, reception
    % processing, and event callbacks.

    properties(SetAccess=private)
        % IsTransmitter - Specifies if the node operates as a transmitter (true) or receiver (false)
        IsTransmitter

        % CenterFrequency - Carrier frequency in Hz (default: 2.4 GHz)
        CenterFrequency

        % Bandwidth - Channel bandwidth in Hz (default: 20 MHz)
        Bandwidth

        % SampleRate - Sampling rate in Hz (default: 20 MHz)
        SampleRate

        % TransmitPower - Transmit power in dBm (default: 23 dBm)
        TransmitPower

        % NumTransmitAntennas - Number of transmit antennas (default: 1)
        NumTransmitAntennas

        % NumReceiveAntennas - Number of receive antennas (default: 1)
        NumReceiveAntennas

        % PacketDuration - Duration of each transmitted packet in seconds (default: 1 ms)
        PacketDuration

        % PDULength - Packet Data Unit length in bytes (default: 1000)
        PDULength

        % TransmissionProbability - Probability of transmitting in each run() invocation (default: 0.5)
        TransmissionProbability

        % TransmitterBuffer - Stores the most recently generated packet awaiting transmission
        TransmitterBuffer = [];

        % ReceptionBuffer - Array of received packets awaiting processing
        ReceptionBuffer = [];

        % NumPacketsTransmitted - Tracks the number of packets transmitted by this node
        NumPacketsTransmitted = 0;

        % NumPacketsReceived - Tracks the number of packets successfully received by this node
        NumPacketsReceived = 0;
    end

    properties(Access=private)
        % TransmissionStartedCallbacks - Cell array of function handles for TransmissionStarted event
        TransmissionStartedCallbacks = cell(1,0);

        % ReceptionCompleteCallbacks - Cell array of function handles for ReceptionComplete event
        ReceptionCompleteCallbacks = cell(1,0);
    end

    methods
        function obj = hCustomNode(args)
            % hCustomNode Constructor for hCustomNode
            %
            % Name-Value Arguments:
            %   Position (Nx3 double) - Node position(s) in 3D space [x, y, z] in meters
            %   Name (string) - Node name; empty = auto-generated by base class
            %   IsTransmitter (logical) - true for transmitter, false for receiver
            %   CenterFrequency (double) - Carrier frequency in Hz
            %   Bandwidth (double) - Channel bandwidth in Hz
            %   SampleRate (double) - Sampling rate in Hz
            %   TransmitPower (double) - Transmit power in dBm
            %   NumTransmitAntennas (integer) - Number of transmit antennas
            %   NumReceiveAntennas (integer) - Number of receive antennas
            %   PacketDuration (double) - Packet duration in seconds
            %   PDULength (integer) - Packet Data Unit length in bytes
            %   TransmissionProbability (double) - Probability of transmission [0,1]

            arguments
                args.Position (:,3) double = [0 0 0]
                args.Name string = ""
                args.IsTransmitter logical = false
                args.CenterFrequency (1,1) double {mustBePositive} = 2.4e9
                args.Bandwidth (1,1) double {mustBePositive} = 20e6
                args.SampleRate (1,1) double {mustBePositive} = 20e6
                args.TransmitPower (1,1) double = 23
                args.NumTransmitAntennas (1,1) double {mustBeInteger, mustBePositive} = 1
                args.NumReceiveAntennas (1,1) double {mustBeInteger, mustBePositive} = 1
                args.PacketDuration (1,1) double {mustBePositive} = 1e-3
                args.PDULength (1,1) double {mustBeInteger, mustBePositive} = 1000
                args.TransmissionProbability (1,1) double {mustBeGreaterThanOrEqual(args.TransmissionProbability,0), mustBeLessThanOrEqual(args.TransmissionProbability,1)} = 0.5
            end

            % Prepare base class constructor arguments
            baseClassArgs = {};
            baseClassArgs{end+1} = 'Position';
            baseClassArgs{end+1} = args.Position;
            if strlength(args.Name) > 0
                baseClassArgs{end+1} = 'Name';
                baseClassArgs{end+1} = args.Name;
            end

            % Call base class constructor unconditionally (MUST be first)
            obj@wnet.Node(baseClassArgs{:});

            % Initialize custom properties for all nodes
            obj.IsTransmitter = args.IsTransmitter;
            obj.CenterFrequency = args.CenterFrequency;
            obj.Bandwidth = args.Bandwidth;
            obj.SampleRate = args.SampleRate;
            obj.TransmitPower = args.TransmitPower;
            obj.NumTransmitAntennas = args.NumTransmitAntennas;
            obj.NumReceiveAntennas = args.NumReceiveAntennas;
            obj.PacketDuration = args.PacketDuration;
            obj.PDULength = args.PDULength;
            obj.TransmissionProbability = args.TransmissionProbability;
        end

        function nextInvokeTime = run(obj, currentTime)
            % run - Executes node logic at the current simulation time. This method is
            % called by wirelessNetworkSimulator.
            %
            % Inputs:
            %   currentTime - Current simulation time in seconds
            %
            % Outputs:
            %   nextInvokeTime - Time in seconds when run() should be invoked again

            nextInvokeTime = Inf;
            if obj.IsTransmitter
                % TRANSMITTER LOGIC
                if rand < obj.TransmissionProbability
                    % Create and configure wireless packet
                    packet = wirelessPacket();
                    packet.TransmitterID = obj.ID;
                    packet.TransmitterPosition = obj.Position;
                    packet.TechnologyType = wnet.TechnologyType.Custom1;
                    packet.Power = obj.TransmitPower;
                    packet.CenterFrequency = obj.CenterFrequency;
                    packet.Bandwidth = obj.Bandwidth;
                    packet.SampleRate = obj.SampleRate;
                    packet.Duration = obj.PacketDuration;
                    packet.NumTransmitAntennas = obj.NumTransmitAntennas;
                    packet.StartTime = currentTime;

                    % Generate random complex IQ data
                    numSamples = round(packet.Duration* packet.SampleRate);
                    packet.Data = complex( ...
                        rand(numSamples,obj.NumTransmitAntennas,"single"), ...
                        rand(numSamples,obj.NumTransmitAntennas,"single"));

                    % Store packet in transmitter buffer
                    obj.TransmitterBuffer = packet;

                    % Increment transmission counter
                    obj.NumPacketsTransmitted = obj.NumPacketsTransmitted + 1;

                    % Notify registered callbacks
                    if ~isempty(obj.TransmissionStartedCallbacks)
                        notificationData = obj.createNotificationData( ...
                            "TransmissionStarted",currentTime);

                        eventData = struct( ...
                            "CenterFrequency",packet.CenterFrequency, ...
                            "Bandwidth",packet.Bandwidth, ...
                            "Duration",packet.Duration, ...
                            "PDU",randi(255, [obj.PDULength 1],'uint8'), ...
                            "Length",obj.PDULength, ...
                            "TransmitPower",packet.Power, ...
                            "NumTransmitAntennas",packet.NumTransmitAntennas);
                        notificationData.EventData = eventData;

                        for idx = 1:numel(obj.TransmissionStartedCallbacks)
                            obj.TransmissionStartedCallbacks{idx}(notificationData);
                        end
                    end
                end
                % Schedule next invocation 1 second later. The node can be invoked earlier
                % than this nextInvokeTime by the simulator, if any reception is scheduled
                % before this nextInvokeTime.
                nextInvokeTime = currentTime + 1;
            else
                % RECEIVER LOGIC
                if ~isempty(obj.ReceptionBuffer)
                    numPackets = numel(obj.ReceptionBuffer);
                    for pktIdx = 1:numPackets
                        packet = obj.ReceptionBuffer(pktIdx);

                        % Increment reception counter
                        obj.NumPacketsReceived = obj.NumPacketsReceived + 1;

                        % Example processing: Display received packet information
                        fprintf('[%s @ t=%.4f s] Received packet #%d from Node %d (Power: %.1f dBm, Duration: %.3f ms)\n', ...
                            obj.Name, currentTime,obj.NumPacketsReceived, ...
                            packet.TransmitterID,packet.Power,packet.Duration*1000);

                        % Notify registered callbacks
                        if ~isempty(obj.ReceptionCompleteCallbacks)
                            notificationData = obj.createNotificationData( ...
                                "ReceptionEnded",currentTime);

                            eventData = struct( ...
                                "TransmitterID",packet.TransmitterID, ...
                                "TransmitterPosition",packet.TransmitterPosition, ...
                                "CenterFrequency",packet.CenterFrequency, ...
                                "Bandwidth",packet.Bandwidth, ...
                                "Duration",packet.Duration, ...
                                "ReceivedPower",packet.Power, ...
                                "NumReceiveAntennas",obj.NumReceiveAntennas, ...
                                "PacketNumber",obj.NumPacketsReceived);
                            notificationData.EventData = eventData;

                            for idx = 1:numel(obj.ReceptionCompleteCallbacks)
                                obj.ReceptionCompleteCallbacks{idx}(notificationData);
                            end
                        end
                    end
                    % Clear reception buffer after processing
                    obj.ReceptionBuffer = [];
                end
            end
        end

        function packet = pullTransmittedPacket(obj)
            % pullTransmittedPacket - Retrieves and clears the transmitter buffer. This
            % method is called by wirelessNetworkSimulator.
            %
            % Outputs:
            %   packet - wirelessPacket object (or empty if no packet buffered)

            packet = obj.TransmitterBuffer;
            obj.TransmitterBuffer = [];
        end

        function pushReceivedPacket(obj,packet)
            % pushReceivedPacket - Adds a received packet to the reception buffer. It
            % will queue the received packets for further processing. This method is
            % called by wirelessNetworkSimulator.
            %
            % Inputs:
            %   packet - wirelessPacket object containing received data

            obj.ReceptionBuffer = [obj.ReceptionBuffer packet];
        end

        function registerEventCallback(obj,eventName,callback)
            % registerEventCallback - Registers a callback function for a named event
            %
            % Inputs:
            %   eventName - "TransmissionStarted" or "ReceptionEnded"
            %   callback  - Function handle: function(notificationData)
            %
            % Examples:
            %   txNode.registerEventCallback("TransmissionStarted", ...
            %       @(data) fprintf('TX at t=%.3f s\n', data.Timestamp));
            %   rxNode.registerEventCallback("ReceptionEnded", ...
            %       @(data) fprintf('RX from Node %d\n', data.EventData.TransmitterID));

            arguments
                obj (1,:)
                eventName (1,:) string
                callback (1,1) function_handle
            end

            % Validate event name
            validEvents = ["TransmissionStarted","ReceptionEnded"];
            eventName = validatestring(eventName,validEvents, ...
                'registerEventCallback','eventName',2);

            % Add callback to appropriate list
            switch eventName
                case "TransmissionStarted"
                    obj.TransmissionStartedCallbacks{end+1} = callback;
                case "ReceptionEnded"
                    obj.ReceptionCompleteCallbacks{end+1} = callback;
            end
        end

        function stats = statistics(obj)
            % statistics - Returns a structure containing node statistics
            %
            % Outputs:
            %   stats - Struct with node statistics and configuration

            stats = struct();
            stats.ID = obj.ID;
            stats.Name = obj.Name;
            stats.IsTransmitter = obj.IsTransmitter;
            stats.NumPacketsTransmitted = obj.NumPacketsTransmitted;
            stats.NumPacketsReceived = obj.NumPacketsReceived;
        end
    end

    methods (Access=private)
        function notificationData = createNotificationData(obj,eventName,currentTime)
            % createNotificationData - Creates notification data structure
            %
            % Inputs:
            %   eventName - Name of the event
            %   currentTime - Current simulation time
            %
            % Outputs:
            %   notificationData - Structure with event information

            notificationData.NodeID = obj.ID;
            notificationData.NodeName = obj.Name;
            notificationData.TechnologyType = wnet.TechnologyType.Custom1;
            notificationData.EventName = eventName;
            notificationData.Timestamp = currentTime;
        end
    end
end

See Also

Objects

Classes

Topics