VR-Link API Documentation for HLA Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DDM Example

Table of Contents

This DDM example is based on the simple talk/listen example and demonstrates VR-Link's support for using the HLA DDM services to reduce the amount of received data based on geographic filtering.

The HLA DDM services allow federates to specify regions used for both updating and subscribing data. When one federate's update region overlaps another federate's subscribe region, data will be delivered. When there are no overlaps, data is not delivered.

The DDM regions used by VR-Link are based on a geographic playbox specified in geodetic coordinates. Both the talk and listen federates must first enable DDM in the exercise connection and establish the playbox in order to define regions within it. Once DDM is enabled and the playbox is established, the talk federate only needs to create an entity and it is automatically associated with a DDM update region. The extents of the update region are based on the entity's current location and velocity. A larger velocity results in a larger region such that a new update of the entity will still find itself within its previous region. When the entity's publisher is ticked, VR-Link evaluates the entity's current location and velocity and adjusts the entity's update region if necessary (i.e., as the entity approaches the region's edge). In addition, the talk federate uses the entity's region to create an attached emitter using the same region. Like the simple talk example, the DDM talk example creates an aircraft entity with an emitter and flies the aircraft north.

The listen federate creates a lower lattitude region and upper lattitude region. It uses both of these subscription regions to initialze a reflected entity list but only the lower region with a reflected emitter list. When the talk federate is started, its update region overlaps the listen's lower subscription region. As a result, the listen federate initially receives updates from both the entity and emitter and prints emitter # and location values. After a short time ( ~10 seconds), the talk entity will move north such that its update region no longer overlaps the listen's lower subscription region and the entity and emitter objects will go out-of-scope and be removed. The value output will stop. After another short period, the entity's update region will overlap the listen's upper subscription region (only associated with the entity reflected list) and the entity object will come back in-scope and the location value output will resume. Eventually, the entity will go out-of-scope again and output will stop.

DDM Components of Interest

These two classes are used to specify DDM regions used by VR-Link:

How to Run the Example

  1. Launch the listen example from your VR-Link install's bin64 directory, choosing one of the HLA versions.
  2. Launch the talk example, choosing the same HLA versions.
  3. Observe the entity and emitter data in the listen console and note the entity being removed and re-discovered
  4. To exit, press the 'q' key.

Example Application Source

The DDM talk and listen examples are self contain in the following files:

Talk Application

/****************************************************************************
* Copyright (c) 2014 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include <vl/topoView.h>
int keybrdTick()
{
char* keyPtr = DtPollBlockingInputLine();
if ( keyPtr && (*keyPtr == 'q' || *keyPtr == 'Q') )
{
return -1;
}
return 0;
}
int main( int argc, char* argv[] )
{
// Used for error handling
DtINIT_MINIDUMPER( "DDM-Talk" );
DtVrlApplicationInitializer appInit(argc, argv, "VR-Link DDM Talk");
appInit.setExecName("VR-LinkDDM");
#if !DtHLA_1516
appInit.setFedFileName("example-ddm.fed");
#else
#if DtHLA_1516_EVOLVED
appInit.setFedFileName("example-ddm_evolved.xml");
#else
appInit.setFedFileName("example-ddm.xml");
#endif
#endif
appInit.parseCmdLine();
DtExerciseConn exConn(appInit);
// Set exercise connection to use geographic DDM within the given playbox area.
// Entities will automatically be published in geographic regions based on location and sized according to velocity
exConn.setUseGeographicDdm(true);
exConn.setDdmPlayBoxLowerBound(DtGeodeticCoord(DtDeg2Rad(30.0), DtDeg2Rad(-130.0), 0));
exConn.setDdmPlayBoxUpperBound(DtGeodeticCoord(DtDeg2Rad(40.0), DtDeg2Rad(-120.0), 0));
// Create an entity publisher for the entity we are simulating.
DtEntityPublisher entityPub(f18Type, &exConn, DtDrDrmRvw, DtForceFriendly,
// Get the publisher's state repository
DtEntityStateRepository* esr = entityPub.esr();
// Create an emitter system that uses the F18's region
try
{
emitterSys = new DtEmitterSystemPublisher(&exConn, 0, &(entityPub.publishingRegions()));
}
#ifdef DtHLA_1516_EVOLVED
catch( rti1516e::Exception& )
#elif defined DtHLA_1516
catch( rti1516::Exception& )
#elif defined DtHLA
catch( RTI::Exception& )
#endif
{
DtInfo << "Exception Caught: Make sure that DDM is enabled.\n";
emitterSys = NULL;
return 0;
}
DtEmitterSystemRepository* emitSR = emitterSys->esr();
emitSR->setEmitterNumber(23);
// Create a topographic view on the state repository, so we
// can set position information in topographic coordinates.
double refLatitude = DtDeg2Rad( 35.705);
double refLongitude = DtDeg2Rad(-121.327);
DtTopoView topoView(esr, refLatitude, refLongitude);
// We can use the ESR to set state.
esr->setMarkingText("VR-Link");
// Initialize VR-Link time.
DtClock* clock = exConn.clock();
DtVector position(0, 0, -100);
DtVector32 velocity(40, 0, 0);
// Main loop
DtTime dt = 1.0;
DtTime simTime = 0;
while (1)
{
// Check if user hit 'q' to quit.
if (keybrdTick() == -1)
break;
// Tell VR-Link the current value of simulation time.
clock->setSimTime(simTime);
// Process any incoming messages.
exConn.drainInput();
// Set the current position information.
topoView.setLocation(position);
topoView.setVelocity(velocity);
entLoc.setGeocentric(esr->location());
DtInfo << "Entity Location: " << DtRad2Deg(entLoc.lat()) << ", " << DtRad2Deg(entLoc.lon()) << std::endl;
// Call tick, which insures that any data that needs to be updated is sent.
// DDM Region will automatically be updated based on the entity's new location
entityPub.tick();
emitterSys->tick();
// Set up for next iteration.
position[0] += velocity[0] * dt;
simTime += dt;
// Wait till real time equals simulation time of next step.
DtSleep(simTime - clock->elapsedRealTime());
}
return 0;
}

Listen Application

/****************************************************************************
* Copyright (c) 2014 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include "vl/hlaObject.h"
// Print something when an actual reflect attribute update is received
// versus the periodic output of dead-reckoned location
void reflectCb(const DtStateMsg& msg, DtHlaObject* obj, void* usr)
{
DtInfo << "Reflect Update " << obj->name() << "(" << DtToString(obj->objectId()) << ") "
<< msg.handleMap().size() << " attributes" << std::endl;
// Print the list of reflected attributes
//msg.printAttrs(obj->exerciseConn(), obj->objectClassHandle(), 0);
// Print the list of reflected attributes and the state repository values
//msg.print(obj, true);
}
void removeObjectCb(DtHlaObject* obj, void* usr)
{
// Really not necessary to remove these callbacks since object will be removed
// At this point, any additional bookkeeping involving this reflected entity can be clean up
obj->removeRemoveObjectCb(removeObjectCb, 0);
obj->removePostReflectCb(reflectCb, 0);
}
void discoverObjectCb(DtHlaObject* obj, void* usr)
{
// Object has become in-scope and added to reflected list.
// Add callbacks for when it is updated and removed.
obj->addPostReflectCb(reflectCb, 0);
obj->addRemoveObjectCb(removeObjectCb, 0);
}
bool emitterDiscoveryCondition(DtReflectedObject* refObj, void* usr)
{
// Only recognize emitter's that have received reflection of static data values (i.e., from request-provide)
return emitter->emitterSysRep()->emitterNumber() != 0;
}
int main( int argc, char* argv[] )
{
// Used for error handling
DtINIT_MINIDUMPER( "DDM-Listen" );
DtVrlApplicationInitializer appInit(argc, argv, "VR-Link DDM Listen");
appInit.setExecName("VR-LinkDDM");
#if !DtHLA_1516
appInit.setFedFileName("example-ddm.fed");
#else
#if DtHLA_1516_EVOLVED
appInit.setFedFileName("example-ddm_evolved.xml");
#else
appInit.setFedFileName("example-ddm.xml");
#endif
#endif
appInit.parseCmdLine();
DtExerciseConn exConn(appInit);
try
{
// This switch needs be set in order to pull reflected objects off of the reflected lists when they go out of scope
exConn.rtiAmb()->enableAttributeScopeAdvisorySwitch();
}
catch( ... )
{
DtWarn << "Caught an exception attempting to enable the Attribute Scope Advisory Switch.";
DtWarn << " Please make sure Force Full Compliance is turned on." << std::endl;
return 0;
}
// Set playbox (area of interest... used for normalization)
exConn.setDdmPlayBoxLowerBound(DtGeodeticCoord(DtDeg2Rad(30.0), DtDeg2Rad(-130.0), 0));
exConn.setDdmPlayBoxUpperBound(DtGeodeticCoord(DtDeg2Rad(40.0), DtDeg2Rad(-120.0), 0));
exConn.addDiscoverObjectCallback(RTI::ObjectClassHandle(), discoverObjectCb, nullptr);
// By default reflected lists will perform a request class update when initialized
// When using DDM, however, objects go in and out of scope
// Switch to a request by object approach instead (request class updates must be disabled)
DtReflectedEntityList* reflEntList = nullptr;
DtReflectedEmitterSystemList* reflEmitList = nullptr;
DtGeodeticRegionSP lowerRegion;
DtGeodeticRegionSP upperRegion;
DtDDMRegionSet entityRegions;
DtDDMRegionSet emitterRegions;
try
{
// Smart pointers are used to create/pass around regions, since they can be created and passed around so much
lowerRegion = DtGeodeticRegionSP(new DtGeodeticRegion(&exConn));
upperRegion = DtGeodeticRegionSP(new DtGeodeticRegion(&exConn));
// Initialize region sets
entityRegions.insert(lowerRegion);
entityRegions.insert(upperRegion);
emitterRegions.insert(lowerRegion);
// Set region bounds for 2 vertically stacked regions with space in between
// DDM Talk entity traveling due north will start in lower region, leave that region and enter the in-between space,
// enter into the upper region and eventually leave that region also.
lowerRegion->setRegionBounds(DtGeodeticCoord(DtDeg2Rad(35.695), DtDeg2Rad(-121.337), 0),
DtGeodeticCoord(DtDeg2Rad(35.705), DtDeg2Rad(-121.317), 0));
upperRegion->setRegionBounds(DtGeodeticCoord(DtDeg2Rad(35.715), DtDeg2Rad(-121.337), 0),
DtGeodeticCoord(DtDeg2Rad(35.735), DtDeg2Rad(-121.317), 0));
// Reflected entity list subscribes with both lower and upper regions.
// It will see Talk entity initially in-scope, go out-of-scope, back in-scope, and eventually out-of-scope again.
reflEntList = new DtReflectedEntityList(&exConn, false, &entityRegions);
reflEntList->discoverOnlyWhenEntityTypeKnown(true);
// Reflected emitter system list only subscribes with lower region.
// It will see the emitter initially in-scope and then out-of-scope.
reflEmitList = new DtReflectedEmitterSystemList(&exConn, true, &emitterRegions);
reflEmitList->setDiscoveryCondition(emitterDiscoveryCondition, nullptr);
}
catch (DtException& except)
{
// If region creation fails, continue, but without regions
DtWarn << "Cannot create region: " << except << std::endl;
reflEntList = new DtReflectedEntityList(&exConn);
reflEmitList = new DtReflectedEmitterSystemList(&exConn);
}
// if at this point the smart pointers are null then something happened and we need to exit
if( lowerRegion == NULL || upperRegion == NULL )
{
DtWarn << "Exception occurred: regions were not created, exiting." << std::endl;
return 0;
}
DtClock* clock = exConn.clock();
// Main loop
int forever = 1;
while (forever)
{
// Check if user hit 'q' to quit.
if (input.keybrdTick() == -1)
break;
// Tell VR-Link the current value of simulation time.
clock->setSimTime(clock->elapsedRealTime());
// Process any incoming messages.
exConn.drainInput();
DtReflectedEntity* firstEntity = reflEntList->first();
DtReflectedEmitterSystem* firstEmitter = reflEmitList->first();
if (firstEntity)
{
entLoc.setGeocentric(firstEntity->esr()->location());
DtInfo << "Entity in scope. Location: " << DtRad2Deg(entLoc.lat()) << ", " << DtRad2Deg(entLoc.lon()) << std::endl;
}
if (firstEmitter)
{
DtInfo << "Emitter in scope. Emitter #: " << static_cast<int>(firstEmitter->esr()->emitterNumber()) << std::endl;
}
// Sleep till next iteration.
DtSleep(1.0);
}
delete reflEntList;
delete reflEmitList;
return 0;
}

Document ID: Generated on Thu Sep 19 02:12:35 EDT 2024 from SVN revision 269601
Copyright © 2005-2024 MAK Technologies. All Rights Reserved (www.mak.com)