QualNet node placement and movement

Add some parts on the basis of the original manuscript: I only looked at the code roughly when I posted this blog, and did not go into details. After several days of development, I found that QualNet's own movement model, whether it is random waypoints, group movement, file setting pathpoints, has all the waypoints to be passed and the arrival time calculated during the initialization phase, and saved Enter the destination list, and then in the simulation, move the node to the corresponding position according to the arrival at the corresponding time. During the simulation process, new waypoints cannot be added, that is, the movement parameters of the node cannot be modified in the middle as desired. The solution is to initialize and set the node not to move, that is, not to call any mobile model, and then in the simulation process, if you need to move the node, set the destination and arrival time yourself, and copy the code of the move function. achieve. There is no need to insert the destination list, nor remove the destination from the list, set and move on the spot.

1 Initialize node position

struct NodePositions {
    NodeAddress       nodeId;
    int               partitionId;
    NodePlacementType nodePlacementType;
    MobilityData*     mobilityData;
};

The node location data structure includes node id, partition id of the node, node placement type, and node movement data. Call the MOBILITY_AllocateNodePositions() function in the main function to allocate the node position memory, read the user configuration file, set the placement type of each node, and then call the MOBILITY_PreInitialize() function to initialize the movement data in advance.

void MOBILITY_AllocateNodePositions(
        numNodes,
        nodeIdArray,
        &nodePositions,
        &nodePlacementTypeCounts,
        &nodeInput,
        seedVal);
{
    NodePositions* nodePositions =
        (NodePositions*)MEM_malloc(sizeof(NodePositions) * numNodes);
    int* nodePlacementTypeCounts =
        (int*)MEM_malloc(sizeof(int) * NUM_NODE_PLACEMENT_TYPES);

    for (i = 0; i < NUM_NODE_PLACEMENT_TYPES; i++) {
        nodePlacementTypeCounts[i] = 0;
    }

    for (i = 0; i < numNodes; i++) 
    {
        nodePositions[i].nodeId = nodeIdArray[i];
        nodePositions[i].partitionId = 0;

        //设置节点放置类型        
        IO_ReadString(
            nodeIdArray[i],
            ANY_ADDRESS,
            nodeInput,
            "NODE-PLACEMENT",
            &wasFound,
            buf);        

        if (strcmp(buf, "RANDOM") == 0) {
            nodePositions[i].nodePlacementType = RANDOM_PLACEMENT;
            nodePlacementTypeCounts[RANDOM_PLACEMENT]++;
        }
        else if (strcmp(buf, "UNIFORM") == 0) {
            nodePositions[i].nodePlacementType = UNIFORM_PLACEMENT;
            nodePlacementTypeCounts[UNIFORM_PLACEMENT]++;
        }
        else if (strcmp(buf, "GRID") == 0) {
            nodePositions[i].nodePlacementType = GRID_PLACEMENT;
            nodePlacementTypeCounts[GRID_PLACEMENT]++;
        }
        else if (strcmp(buf, "FILE") == 0) {
            nodePositions[i].nodePlacementType = FILE_BASED_PLACEMENT;
            nodePlacementTypeCounts[FILE_BASED_PLACEMENT]++;
        }
        else if (strcmp(buf, "GROUP") == 0) {
            nodePositions[i].nodePlacementType = GROUP_PLACEMENT;
            nodePlacementTypeCounts[GROUP_PLACEMENT]++;
        }
        else if (strcmp(buf, "EXTERNAL") == 0) {
            nodePositions[i].nodePlacementType = EXTERNAL_PLACEMENT;
            nodePlacementTypeCounts[EXTERNAL_PLACEMENT]++;
        }
        else {
            char errorMessage[MAX_STRING_LENGTH];

            sprintf(errorMessage, "Unknown NODE-PLACEMENT type: %s.\n", buf);
            ERROR_ReportError(errorMessage);
        }

        // 分配内存并初始化节点移动类型        
        nodePositions[i].mobilityData =
            (MobilityData*)MEM_malloc(sizeof(MobilityData));
        memset(nodePositions[i].mobilityData, 0, sizeof(MobilityData));

        MOBILITY_PreInitialize(
            nodeIdArray[i],
            nodePositions[i].mobilityData,
            nodeInput,
            seedVal);
    }		
}

 2 Pre-initialize node mobile data

struct MobilityData 
{
    MobilityType mobilityType;
    D_Float32 distanceGranularity;
    D_BOOL groundNode;
    RandomSeed        seed;
    //组移动模型相关
    RandomSeed        groupSeed;
    int               groupIndex;
    Coordinates groupTerrainOrigin;
    Coordinates groupTerrainDimensions;
    double groupMaxSpeed;
    double groupMinSpeed;
    double internalMaxSpeed;
    double internalMinSpeed;
    clocktype internalMobilityPause;
    //序列号,未来、当前、过去位置
    int               sequenceNum;
    MobilityElement*  next;
    MobilityElement*  current;
    MobilityElement*  past[NUM_PAST_MOBILITY_EVENTS];
    //目的地数量、目的地列表
    int               numDests;
    MobilityElement*  destArray;
    MobilityRemainder remainder;
    void *mobilityVar;
};

The node movement data structure mainly includes movement type, current past and future location, destination list, etc. There are also parameters such as the maximum and minimum movement speed of the components in the group when the group moves. The MOBILITY_PreInitialize() function reads the user configuration file, sets the node movement type and other parameters, and initializes the current past and future positions of the node.

void MOBILITY_PreInitialize(
    NodeAddress nodeId,
    MobilityData* mobilityData,
    NodeInput* nodeInput,
    int seedVal)
{    
    // 设置节点移动类型.
    IO_ReadString(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY",
        &wasFound,
        buf);
    if (wasFound) 
    {
        if (strcmp(buf, "NONE") == 0) 
            mobilityData->mobilityType = NO_MOBILITY;

        else if (strcmp(buf, "RANDOM-WAYPOINT") == 0) 
            mobilityData->mobilityType = RANDOM_WAYPOINT_MOBILITY;

        else if (strcmp(buf, "GROUP-MOBILITY") == 0)
            mobilityData->mobilityType = GROUP_MOBILITY;

        else if (strcmp(buf, "TRACE") == 0) 
        {
            ERROR_ReportWarning(
                "\"MOBILITY TRACE\" is obsolete; "
                "use \"MOBILITY FILE\" instead.\n");
            mobilityData->mobilityType = FILE_BASED_MOBILITY;
        }
        else if (strcmp(buf, "FILE") == 0) 
            mobilityData->mobilityType = FILE_BASED_MOBILITY;

        else {
            char errorMessage[MAX_STRING_LENGTH];
            sprintf(errorMessage, "Unknown MOBILITY type: %s.\n", buf);
            ERROR_ReportError(errorMessage);
        }
    }
    else 
        mobilityData->mobilityType = NO_MOBILITY;

    IO_ReadFloat(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-POSITION-GRANULARITY",
        &wasFound,
        &distGran);
    IO_ReadBool(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUND-NODE",
        &wasFound,
        &returnVal);
    IO_ReadBool(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-STATISTICS",
        &wasFound,
        &mobilityStats);
 
    // 为节点下一个位置、当前位置和过去两个点的位置分配内存
    // 实际节点位置在函数MOBILITY_SetNodePositions()中设置
    mobilityData->next =
        (MobilityElement*)MEM_malloc(sizeof(MobilityElement));
    memset(mobilityData->next, 0, sizeof(MobilityElement));

    mobilityData->current =
        (MobilityElement*)MEM_malloc(sizeof(MobilityElement));
    memset(mobilityData->current, 0, sizeof(MobilityElement));
    
    // NUM_PAST_MOBILITY_EVENTS = 2
    for (i = 0; i < NUM_PAST_MOBILITY_EVENTS; i++) {
        mobilityData->past[i] =
            (MobilityElement*)MEM_malloc(sizeof(MobilityElement));
        memset(mobilityData->past[i], 0, sizeof(MobilityElement));
    }
    mobilityData->mobilityVar = NULL;
}

3 Place node (set node position)

See ~/libraries/developer/src/mobility_placement.cpp. According to the node placement method in the scene configuration, call the function MOBILITY_AddANewDestination() in the placement method functions such as SetNodesPositionsRandomly() to place the node (that is, set the first position of the node).

(1) Except for group placement, all other placement methods call SetRandomMobility() function to move after placing the first node. If the movement model is None, then no movement; if the movement method is a random path point, then random movement .

(2) The group placement method SetNodesPositionsInGroup() is an exception, and the MOBILITY_GroupMobilityInit() function is called to initialize the group mobility model. It should be noted that there is no group placement method for initial placement of nodes. After you choose any placement method to place the nodes, combine any number of nodes into a GROUP, then the placement method of these nodes will be changed to the group placement method. To use the group movement model, it must be the group placement method.

void MOBILITY_SetNodePositions(
    int numNodes,
    NodePositions* nodePositions,
    int* nodePlacementTypeCounts,
    TerrainData* terrainData,
    NodeInput* nodeInput,
    RandomSeed seed,
    clocktype maxSimTime,
    clocktype startSimTime)
{    
    // 随机放置节点
    if (nodePlacementTypeCounts[RANDOM_PLACEMENT] != 0) {
        SetNodePositionsRandomly(
            numNodes,
            nodePlacementTypeCounts[RANDOM_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
    // 统一放置节点
    if (nodePlacementTypeCounts[UNIFORM_PLACEMENT] != 0) {
        SetNodePositionsUniformly(
            numNodes,
            nodePlacementTypeCounts[UNIFORM_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
    // 网格式放置节点
    if (nodePlacementTypeCounts[GRID_PLACEMENT] != 0) {
        SetNodePositionsInGrid(
            numNodes,
            nodePlacementTypeCounts[GRID_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            maxSimTime);
    }
    // 基于文件放置节点
    if (nodePlacementTypeCounts[FILE_BASED_PLACEMENT] != 0) {
        SetNodePositionsWithFileInputs(
            numNodes,
            nodePlacementTypeCounts[FILE_BASED_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            maxSimTime,
            startSimTime);
    }
    // 外部节点
    if (nodePlacementTypeCounts[EXTERNAL_PLACEMENT] != 0) {
        SetNodePositionsExternal(
            numNodes,
            nodePlacementTypeCounts[EXTERNAL_PLACEMENT],
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
    // 按组放置节点
    if (nodePlacementTypeCounts[GROUP_PLACEMENT] != 0) {
        SetNodePositionsInGroup(
            numNodes,
            nodePlacementTypeCounts,
            nodePositions,
            terrainData,
            nodeInput,
            seed,
            maxSimTime);
    }
}

 4 mobile models

Take group movement as an example. After the first point is placed in SetNodesPositionsInGroup(), the MOBILITY_GroupMobilityInit() function is called to initialize the group movement model. The MOBILITY_GroupMobilityInit() function then calls MOBILITY_GroupMobility() to start the movement, that is, continuously calculating the next position and adding the next position to the destination list destArray. When calculating the position, the arrival sequence of group movement and inter-group movement is taken into consideration, and pay attention to the distinction.

Note: In the random waypoint and group movement model, all destination points are calculated and added to the list during the initialization phase. In the subsequent simulation process, new destination points cannot be added before the last destination time point.

static
void MOBILITY_GroupMobility(NodeAddress nodeId,
                            MobilityData* mobilityData,
                            TerrainData* terrainData,
                            const NodeInput* nodeInput,
                            clocktype maxSimClock,
                            int groupIndex,
                            Coordinates groupTerrainOrigin,
                            Coordinates groupTerrainDimensions)
{
    clocktype simClock;
    Orientation orientation;
    //参考点,组域范围X\Y轴距
    Coordinates refPoint;
    double groupDimensionX;
    double groupDimensionY;
    //组移动间歇时间、最大最小速度
    clocktype groupMobilityPause;
    double groupMinSpeed;
    double groupMaxSpeed;
    //组内移动间歇时间、最大最小速度
    clocktype internalMobilityPause;
    double internalMinSpeed;
    double internalMaxSpeed;
    //当前位置、目标位置
    Coordinates current;
    Coordinates dest;
    //组参考点移动目标(即组移动的目标),到达时间、速度、距离
    Coordinates refDest = {
   
   {
   
   {0,0,0}}};
    clocktype refDeltaTime;
    double refSpeed;
    double refDistance;
    //组内目标点(即组不移动时组内某节点的目标点),到达时间、速度、距离
    Coordinates internalDest = {
   
   {
   
   {0,0,0}}};
    clocktype internalDeltaTime;
    double internalSpeed;
    double internalDistance;
    
    char buf[MAX_STRING_LENGTH];
    BOOL wasFound;
    BOOL beRefPause = FALSE;
    BOOL beInternalPause = FALSE;
    RandomSeed groupSeed;
    int seedVal;    

    //运动模型分别由X和Y方向两个随机速度进行,不考虑方向(方位角、俯仰角)
    orientation.azimuth = 0;
    orientation.elevation = 0;
    //读取组移动参数
    IO_ReadStringInstance(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUP-PAUSE",
        groupIndex,
        TRUE,
        &wasFound,
        buf);
    //没找到组停留时间参数,移动模型返回
    //设置为0也是设置了,不算没找到
    if (wasFound == FALSE) return;
    groupMobilityPause = TIME_ConvertToClock(buf);
    IO_ReadDoubleInstance(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUP-MIN-SPEED",
        groupIndex,
        TRUE,
        &wasFound,
        &groupMinSpeed);
    IO_ReadDoubleInstance(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUP-MAX-SPEED",
        groupIndex,
        TRUE,
        &wasFound,
        &groupMaxSpeed);
    //读取组内移动参数
    IO_ReadStringInstance(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUP-INTERNAL-PAUSE",
        groupIndex,
        TRUE,
        &wasFound,
        buf);
    internalMobilityPause = TIME_ConvertToClock(buf);
    IO_ReadDoubleInstance(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUP-INTERNAL-MIN-SPEED",
        groupIndex,
        TRUE,
        &wasFound,
        &internalMinSpeed);
    IO_ReadDoubleInstance(
        nodeId,
        ANY_ADDRESS,
        nodeInput,
        "MOBILITY-GROUP-INTERNAL-MAX-SPEED",
        groupIndex,
        TRUE,
        &wasFound,
        &internalMaxSpeed);
    //产生随机种子
    seedVal = 1;
    IO_ReadInt(
        ANY_NODEID,
        ANY_ADDRESS,
        nodeInput,
        "SEED",
        &wasFound,
        &seedVal);
    RANDOM_SetSeed(groupSeed,
                   seedVal,
                   GROUP_MOBILITY,
                   groupIndex);
    //组域范围
    groupDimensionX = groupTerrainDimensions.common.c1;
    groupDimensionY = groupTerrainDimensions.common.c2;

    //以组域范围中心点作为参考点
    refPoint.common.c1 = groupTerrainOrigin.common.c1 + groupDimensionX / 2;
    refPoint.common.c2 = groupTerrainOrigin.common.c2 + groupDimensionY / 2;
    refPoint.common.c3 = mobilityData->current->position.common.c3;
    //下一个参考点位置到达时间
    refDeltaTime = 0;

    //依据移动模型产生未来位置点
    simClock = 0;
    internalDeltaTime = 0;
    current = mobilityData->current->position;
    while (simClock < maxSimClock)
    {
        //组移动时刻到达
        if (refDeltaTime <= 0)
        {
            //最大组移动速度小于0,参考点不变
            if (groupMaxSpeed < MOBILITY_GROUP_DELTA_ZERO)
            {
                refDest = refPoint;
                refDeltaTime = maxSimClock;
            }
            else
            {
                //组移动暂停时
                if ((beRefPause == TRUE) && (groupMobilityPause > 0))
                {
                    beRefPause = FALSE;
                    refDest = refPoint;
                    refDeltaTime = groupMobilityPause;
                }
                //组移动进行时
                else
                {
                    beRefPause = TRUE;
                    //参考点目标位置
                    refDest.common.c1 =
                        terrainData->getOrigin().common.c1 +
                        groupDimensionX / 2 +
                        ((terrainData->getDimensions().common.c1 -
                        groupDimensionX ) *
                        RANDOM_erand(groupSeed));
                    refDest.common.c2 =
                        terrainData->getOrigin().common.c2 +
                        groupDimensionY / 2 +
                        ((terrainData->getDimensions().common.c2 -
                        groupDimensionY) *
                        RANDOM_erand(groupSeed));
                    refDest.common.c3 = refPoint.common.c3;
                    //组移动速度,为最小和最大速度之间随机值
                    refSpeed = groupMinSpeed +
                               (RANDOM_erand(groupSeed) *
                               (groupMaxSpeed - groupMinSpeed));
                    //计算参考点移动距离,计算移动所需时间
                    COORD_CalcDistance(
                        terrainData->getCoordinateSystem(),
                        &refPoint, &refDest, &refDistance);                    
                    refDeltaTime = (clocktype)(refDistance / refSpeed * SECOND);
                }
            }
        }
        //组内移动 
        if (internalDeltaTime <= 0)
        {
            //组内移动速度小于0,不移动
            if (internalMaxSpeed < MOBILITY_GROUP_DELTA_ZERO)
            {
                internalDest = current;
                internalDeltaTime = maxSimClock;
            }
            else
            {
                if ((beInternalPause == TRUE) &&
                    (internalMobilityPause > 0))
                {
                    beInternalPause = FALSE;
                    internalDeltaTime = internalMobilityPause;
                    internalDest = current;
                }
                else
                {
                    beInternalPause = TRUE;
                    //组内移动目标位置
                    internalDest.common.c1 =
                        (refPoint.common.c1 - groupDimensionX / 2) +
                        (groupDimensionX * RANDOM_erand(mobilityData->seed));
                    internalDest.common.c2 =
                        (refPoint.common.c2 - groupDimensionY / 2) +
                        (groupDimensionY * RANDOM_erand(mobilityData->seed));
                    internalDest.common.c3 = current.common.c3;
                    //组内移动速度、距离、时间
                    internalSpeed = internalMinSpeed +
                                    (RANDOM_erand(mobilityData->seed) *
                                    (internalMaxSpeed - internalMinSpeed));
                    COORD_CalcDistance(
                        terrainData->getCoordinateSystem(),
                        &current, &internalDest, &internalDistance);
                    internalDeltaTime = (clocktype) (internalDistance / internalSpeed * SECOND);
                }
            }
        }
        
        dest = current;    
        if (refDeltaTime >= internalDeltaTime)
        {
            //组内移动先完成
            dest = internalDest;
            dest.common.c1 += ((refDest.common.c1 - refPoint.common.c1) *
                              ((double)internalDeltaTime /
                              (double)refDeltaTime));
            dest.common.c2 += ((refDest.common.c2 - refPoint.common.c2) *
                              ((double)internalDeltaTime /
                              (double)refDeltaTime));
            dest.common.c3 += refDest.common.c3;
            simClock += internalDeltaTime;

            //更新参考点位置
            refPoint.common.c1 += (refDest.common.c1 - refPoint.common.c1) *
                                  ((double)internalDeltaTime /
                                  (double)refDeltaTime);
            refPoint.common.c2 += (refDest.common.c2 - refPoint.common.c2) *
                                  ((double)internalDeltaTime /
                                  (double)refDeltaTime);
            refDeltaTime -= internalDeltaTime;
            internalDeltaTime = 0;
        }       
        else
        {
            //组移动先完成
            CoordinateType internalDeltaX;
            CoordinateType internalDeltaY;
            internalDeltaX = internalDest.common.c1 - current.common.c1;
            internalDeltaY = internalDest.common.c2 - current.common.c2;
            if (refDeltaTime == 0)
            {
                refDeltaTime = internalDeltaTime;
            }
            //当前位置加上组内移动量
            dest.common.c1 += internalDeltaX *
                              ((double)refDeltaTime /
                              (double)internalDeltaTime);
            dest.common.c2 += internalDeltaY *
                              ((double)refDeltaTime /
                              (double)internalDeltaTime);
            dest.common.c3 = current.common.c3;

            //再加上组移动量
            dest.common.c1 += (refDest.common.c1 - refPoint.common.c1);
            dest.common.c2 += (refDest.common.c2 - refPoint.common.c2);
            simClock += refDeltaTime;

            //更新组内移动目标点位置
            internalDest.common.c1 +=
                (refDest.common.c1 - refPoint.common.c1);
            internalDest.common.c2 +=
                (refDest.common.c2 - refPoint.common.c2);
            refPoint = refDest;
            internalDeltaTime -= refDeltaTime;
            refDeltaTime = 0;
        }
        //将计算好的目标点、时间等添加入目的地列表
        MOBILITY_AddANewDestination(mobilityData,
                                    simClock,
                                    dest,
                                    orientation);
        //跳转至目标位置,完成一次运动
        current = dest;
    }
    return;
}

5 Mobile implementation

The movement mentioned above is actually just adding the next location to the destination list through the MOBILITY_AddANewDestination() function, and it does not actually move. When initializing a node, the PARTITION_InitializeNodes() function not only reads the configuration initialization node protocol stack, but also calls the MOBILITY_PostInitialize() function to initialize the node movement related parameters. If it is the file configuration mode, call the MOBILITY_NextPosition() function to jump to the next position. If there is no next position, it will not move. For other motion methods (random motion, group motion), call the MOBILITY_InsertEvent() function to insert the node movement event, and realize the node movement through the event processing function.

void MOBILITY_PostInitialize(Node *node, NodeInput *nodeInput) 
{
    MobilityData *mobilityData = node->mobilityData;
    MobilityRemainder *remainder = &(mobilityData->remainder);

    //初始化节点剩余目的地
    remainder->numMovesToNextDest = 0;
    remainder->destCounter = 0;
    remainder->moveInterval = 0;
    remainder->nextMoveTime = 0;
    remainder->nextPosition = mobilityData->current->position;
    remainder->nextOrientation = mobilityData->current->orientation;

    if (node->mobilityData->numDests > 1) 
    {
        //当节点采用文件配置位置方式时使用,跳至下一节点
        MOBILITY_NextPosition(node, mobilityData->next);
    }
    else 
    {
        //下一节点位置时间为无穷大时刻,即不运动
        mobilityData->next->time = CLOCKTYPE_MAX;
    }    
    //插入节点移动事件,此函数为核心函数,看不到代码
    MOBILITY_InsertEvent(&(node->partitionData->mobilityHeap), node);
}

 

void MOBILITY_ProcessEvent(Node* node) 
{
    MobilityHeap *heapPtr;
    MobilityData *mobilityData = node->mobilityData;
    clocktype currentTime = getSimTime(node);
    MobilityElement* tmp;
    int index = mobilityData->current->sequenceNum % NUM_PAST_MOBILITY_EVENTS;

    node->partitionData->numberOfMobilityEvents++;

    //设置下一位置
    //临时变量为一个过去位置,过去位置等于现在位置,现在位置等于下一个位置
    tmp = mobilityData->past[index];
    mobilityData->past[index] = mobilityData->current;
    mobilityData->current = mobilityData->next;
    //从目的位置列表读取下一个位置信息,放入tmp中
    MOBILITY_NextPosition(node, tmp);
    mobilityData->next = tmp;

    heapPtr = &(node->partitionData->mobilityHeap);
    assert(heapPtr->heapNodePtr[1] == node);
    //插入一个事件,并按时间顺序排序
    MOBILITY_HeapFixDownEvent(heapPtr, 1);
}

 

Guess you like

Origin blog.csdn.net/zhang1806618/article/details/108033074