VR-Vantage 3.2 API Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
exampleGimbalPlugin

Table of Contents

Overview

Create a simulated gimbaled camera and attached an observer to it.

Expected Result

exampleGimbalPlugin.png
Gimbal Plugin Result

Example details

DtGimbalDriver looks for the UAV element created by DtExampleDriver and implements a gimbal attached to that plane at a specified offset. It shows how to use the DtGeoReference class to position and orient the gimbal using the plane's position and orientation; and how to find azimuth and elevation angles to a target in the gimbal's local coordinate system. It attaches the observer named "Observer 1" to the gimbal, and uses the azimuth and elevation angles determined to lock the observer onto the target location.

The example also creates a sensor overlay based on the overlay used with the VR-Forces Sensor Field Of View overlay. It shows how to create and update the display. It also demonstrates the mechanism for changing one of the overlay section creators (in this case, the compass display).

Building the Example

VR-Vantage includes pre-built versions of the example plug-in. To build it yourself, follow the instructions at Building VR-Vantage Examples, Applications, and Plug-ins.

Running the Example

This example is a plug-in. You can run it by running ./bin64/exampleGimbalPlugin_stealth.bat or ./bin64/exampleGimbalPlugin_stealth.sh (on Linux). For more information about running examples, please see Running Applications and Examples.

Example Source Files

DtGimbalDriver.h

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#pragma once
#include <string>
namespace makVrv
{
class DtGeoReference;
class DtSceneObjectAgent;
class DtSceneObject;
}
class DtGeodeticCoord;
{
public:
DtGimbalDriver() = delete;
virtual ~DtGimbalDriver();
virtual const std::string& className() const;
protected:
virtual bool onStart();
virtual bool onStop();
virtual bool onTick();
virtual void attachObserverToEntity(const std::string& observerName, const std::string& entityName );
virtual void slot_displayEngineAdded( const makVrv::DtDeRecord& igRecord );
virtual bool initialize();
protected:
DtGeodeticCoord* myTargetLocation;
};

DtGimbalDriver.cxx

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#include "DtGimbalDriver.h"
#include <vrvCore/DtDe.h>
#include <vlutil/vlUtil.h>
// Use the VR-Vantage namespace.
// All classes in VR-Vantage are in this namespace.
using namespace makVrv;
// Tower Roof Location on GroundDB-II
const double DtTargetLatitude = DtDeg2Rad( -0.00542 );
const double DtTargetLongitude = DtDeg2Rad( -0.00168 );
const double DtTargetAltitude = 25.00;
// Gimbal position on attached-to platform
const double DtGimbalForwardOffset = 2.0;
const double DtGimbalDownOffset = 2.5;
const double DtGimbalRightOffset = 0.;
// Window/channel to control (and draw the sensor overlay on)
const std::string DtWindowName = "Window 1";
const std::string DtChannelName = "Channel 1";
// Platform Entity Name; this is the correct name when using exampleDriverPlugin (when
// 'SIM_IS_SHIP' is not defined). If using exampleDriver instead, change to "Entity From Facade"
const std::string DtPlatformName = "UAV 1";
// Gimbal Entity Name
const std::string DtGimbalName = "Gimbal";
// Observer attached to the Gimbal entity. Note that this is the observer currently attached
// to the window/channel named above (it could be looked up from the window/channel names,
// but keeping the example simple)
const std::string DtObserverName = "Observer 1";
// String placed in the 'tracking mode' slot of the sensor overlay
const std::string DtSensorOverlayInfoString = "Example Gimbal";
: makVrv::DtDriver( am, "DtGimbalDriver" )
, myPlaneGeoRef(nullptr)
, myGimbalGeoRef(nullptr)
, myGimbalSceneObjectAgent(nullptr)
, myTargetLocation(nullptr)
, myPlaneSceneObject(nullptr)
, myGimbalId( 0 )
, myGimbalSensorOverlay( nullptr )
{
myTargetLocation = new DtGeodeticCoord( DtTargetLatitude, DtTargetLongitude, DtTargetAltitude,
myAgentManager.de().sharedState().coordinateSystem().referenceEllipsoid() );
mySignalConnections += DtDeSharedStateSignaler::instance( myAgentManager.de() )
.signal_coordinateSystemChanged.connect( boost::bind(
}
{
{
}
DtDELETE( myPlaneGeoRef );
DtDELETE( myGimbalGeoRef );
DtDELETE( myTargetLocation );
}
{
DtDELETE( myTargetLocation );
myTargetLocation = new DtGeodeticCoord( DtTargetLatitude, DtTargetLongitude, DtTargetAltitude,
}
const std::string& DtGimbalDriver::className() const
{
static std::string name = "DtGimbalDriver";
return name;
}
{
// look up the plane from exampleDriver (or exampleDriverPlugin)
// It has display name <DtPlatformName>
bool found = false;
// Iterate through all the elements
DtElementData& elemData = de.dataBank().elementData();
DtElementData::ElementEntryMap::const_iterator curIter = elemData.elements().begin();
DtElementData::ElementEntryMap::const_iterator endIter = elemData.elements().end();
for( ; curIter != endIter; ++curIter )
{
std::string entityName;
if (elemData.attributeManager().getElementDisplayName(curIter->first, entityName) && entityName == DtPlatformName)
{
// Found it. Look up the scene object id used to visualize this element
// in the 3D model set. We can use that scene object to get the position
// and orientation of the plane
myPlaneElementId = curIter->first;
DtElementData::SceneObjectIdList sceneObjectIds;
elemData.getSceneObjectIds(myPlaneElementId, sceneObjectIds, DtModelSetId::Models3D );
// getSceneObjectIds() can return multiple scene object ids if multiple model sets
// are specified. Since we only asked for the 3D model set scene object,
// sceneObjectIds.size() should be 1.
// It *should* be one, but because ExampleDriverPlugin DtExampleSimEngine::init
// adds a hatlinefacade to the element there are two scene objects on the element
if ( sceneObjectIds.size() == 2 )
{
DtAgentUpdateResolverInterface* updater = agentManager().findUpdater( sceneObjectIds.front() );
if(updater)
{
found = true;
}
}
}
}
// If we didn't find it, assume it hasn't been created yet. Return false, and we'll
// try again next tick
if (! found )
{
return false;
}
// We have the plane's scene object. Now we can create GeoReferences for the
// plane and the Gimbal
// We create a scene object (and associate it with an element) for the Gimbal so that
// our observer can be attached to it. When an observer is attached to an element in
// Mimic mode, it effectively uses that element's coordinate system; so attaching to
// this Gimbal gives us an observer in Gimbal coordinates. Note that no models are
// added to the scene object, so this element will be invisible (but if you had a 3D
// gimbal model you could create and add it here
myGimbalSceneObjectAgent = DtSceneObjectAgent::create( myAgentManager );
myGimbalSceneObjectAgent->setModelSet( DtModelSetId::Models3D );
// Set up the element
myGimbalSceneObjectAgent->setElementID( myGimbalId );
reportElementCreated( myGimbalId, 0, DtElementEntry::Entity );
// Give the element a name that we can use to attach to it
DtElementAttributeDriverType* driverType = new DtElementAttributeDriverType( "Gimbal Driver");
// Now set up the sensor overlay
// Sensor View Data - only needs to be set once
// Used only for getting data, e.g. heading/location of the sensor platform, for most of the sensor view overlay
// Only needs to be set once
// Used only for rendering informational text and does not have an impact on the channel or observer
// Only needs to be set once
myGimbalSensorOverlay->fovData().setTrackingMode(DtSensorOverlayInfoString);
// Used for determining how to size certain visuals in the overlay
// Only needs to be set once
// These are for determining the range to use for the elevation tape on the left side of the sensor view overlay
// Only needs to be set once
// Used for determining how to size certain visuals in the overlay and for simulating the sensor's maximum zoom level.
// Only needs to be set once
return true;
}
{
return true;
}
{
return true;
}
{
if ( myGimbalSceneObjectAgent == nullptr)
{
if ( ! initialize() )
{
// Everything needed to initialize hasn't been discovered yet. This is not a failure, it just
// means we need to wait.
return true;
}
// The plane was found. Attach to it.
attachObserverToEntity( DtObserverName, DtGimbalName );
if (myGimbalSensorOverlay != nullptr)
{
myGimbalSensorOverlay->initialize(DtObserverName, DtWindowName, DtChannelName);
}
}
// Set up a GeoRef for the plane. We use this to find the Gimbal position
// and orientation for the Gimbal scene object (and Gimbal GeoRef)
DtVector planePosition;
DtTaitBryan planeOrientation;
// Drivers are ticked early in the frame. Entities are dead-reckoned later.
// So at this point, we only have the position and orientation of where the entity
// was last frame, not necessarily where it will be this frame. However, we can
// directly request that it update NOW rather than when all the other entities
// update.
if (updater)
{
{
}
}
// Now we can get the entity's position and orientation for this frame.
myPlaneSceneObject->getPosition( 0, planePosition );
myAgentManager.de().sharedState().coordinateSystem() ), planeOrientation );
// Find the position and orientation of the gimbal in geodetic coordinates, using
// the GeoRef placed above;
DtGeodeticCoord gimbalGeod;
DtTaitBryan gimbalOri;
DtVector( DtGimbalForwardOffset, DtGimbalRightOffset, DtGimbalDownOffset),
DtTaitBryan(0., 0., 0.),
gimbalGeod, gimbalOri );
// The gimbal's georef is now positioned and oriented with the UAV. But we want to point the gimbal
// at the target (without any roll component) so starting with a 'level' gimbal will make that easier.
gimbalOri = DtTaitBryan(0.0, 0.0, 0.0);
// Set up the GeoRef for our Gimbal
myGimbalGeoRef->setLocationAndOrientation( gimbalGeod, gimbalOri );
// Position our gimbal scene object (this is what the observer is attached to)
myGimbalSceneObjectAgent->setPosition( 0, DtGeodeticToLocal( gimbalGeod,
// Get tower location in gimbal's topocentric system. (We don't need the tower's orientation)
DtVector targetTopo;
DtTaitBryan unused( 0., 0., 0. );
myGimbalGeoRef->geodeticToTopo( *myTargetLocation, unused, targetTopo, unused );
// find azimuth and elevation angles to our target
double azimuthAngle = atan2( targetTopo[1], targetTopo[0] );
double xyPlaneDistance = sqrt( targetTopo[0] * targetTopo[0] + targetTopo[1] * targetTopo[1] );
double elevationAngle = DtDeg2Rad( 90. ) - atan2( xyPlaneDistance, -targetTopo[2] );
// Set the gimbal scene object to point at the target
myGimbalSceneObjectAgent->setTopographicOrientation(0, DtTaitBryan(azimuthAngle, elevationAngle, 0.));
// Finally, update the sensor overlay
if (myGimbalSensorOverlay != nullptr)
{
}
return true;
}
void DtGimbalDriver::attachObserverToEntity(const std::string& observerName, const std::string& entityName )
{
if ( observer )
{
// Start by getting the existing observer state record for this observer.
// We can modify it and then set it back. We use the observer record
// mechanism because it will allow the observer to attach even if the
// plane hasn't been created yet (the observer will retry attaching when
// an observer record is used until the attach succeeds
observer->getRecord( observerState );
// Attach to the plane
std::set<std::string> displayNames;
displayNames.insert( entityName );
observerState.setAttachedToDisplayNames( displayNames );
// We want no offset
observerState.setObserverOffsetVector( 0., 0., 0. );
observerState.setObserverRotationOffset(0., 0., 0., 1.);
// Get the observer settings record associated with this observer state record
// so we can force the attach type to mimic
// Add the modified settings record back to the observer state record
observerState.setObserverSettingsRecord( settingsRec );
// Set a view magnification
observerState.setViewMagnification( 100. );
// ... and then add the observer state to the observer
observer->setFromRecord( observerState );
}
}
{
stop();
start();
}

DtGimbalSensorOverlay.h

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#pragma once
namespace makVrv
{
class DtOsgSensorViewCompassOverlaySection;
class DtObserver;
}
{
public:
virtual void initialize(const std::string& observerName, const std::string& windowName, const std::string& channelName);
virtual void update();
virtual void setPlatformTopoOrientation(double ori);
virtual void setGimbalOrientation(double ori);
protected:
std::string myObserverName;
};

DtGimbalSensorOverlay.cxx

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#include <vrvCore/DtDe.h>
#include <vector>
: myGimbalSensorViewData()
, mySensorOverlayDrawer(nullptr)
, mySensorViewCompassOverlaySection(nullptr)
, myObserverName()
, myObserver(nullptr)
, myDe(de)
{
// Register section creators on our own so we can specify our compass overlay creator instead of the default
sectionCreators.push_back(new makVrv::DtOsgSensorViewPitchOverlaySectionCreator);
sectionCreators.push_back(new DtGimbalCompassOverlaySectionCreator);
}
{
}
{
}
void DtGimbalSensorOverlay::initialize(const std::string& observerName, const std::string& windowName, const std::string& channelName)
{
myObserverName = observerName;
myObserver = myDe.driverManager().inputDriver().findObserverByName(myObserverName);
{
mySensorOverlayDrawer->initialize(windowName, channelName);
// Once the drawer is created and initialized we can look up / cache a pointer to the DtOsgSensorViewCompassOverlaySection
// which we will use to update the azimuth scale
{
DtWarn << "DtGimbalSensorOverlay::initialize: failed to find DtGimbalCompassOverlaySection" << std::endl;
}
}
}
{
}
{
}
{
}

DtGimbalCompassOverlaySection.h

DtGimbalCompassOverlaySection.cxx

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#include <vrvCore/DtDe.h>
#include <matrix/vlMath.h>
: DtOsgSensorViewCompassOverlaySection(de)
, myPlatformOrientation(0.0)
, myGimbalOrientation(0.0)
{
}
{
//Intentional nop
}
{
double azimuthScale = DtRad2Deg(myGimbalOrientation) - DtRad2Deg(myPlatformOrientation);
if (azimuthScale < -180.0)
{
azimuthScale += 360.0;
}
updateAzimuthScale(azimuthScale);
}
{
}
{
}
{
}
{
}

[<< Examples] [Home] [Top of Page]



Copyright © 2005-2024 MAK Technologies. All Rights Reserved (www.mak.com)