Tracker interface changes

From IGSTK
Jump to: navigation, search

Four significant flaws have been identified with the tracker interface. In order of priority, these are:

  1. Identification of tools using (port,channel) IDs is aurora-specific. It really does not work well at all for passive optical tools, in particular reserving the first three POLARIS ports for wired tools and requiring passive SROMs to only be loaded into ports 3 and up has caused endless confusion.
  2. A few methods have to be converted to the "Request" convention.
  3. Setting the "reference" is not flexible enough, we need to be able to use a combination of tools as a reference for some applications.
  4. Accessing device-specific information about the tools is not supported.

Proposed Solutions:

Item 2 can be fixed by adding a "Request" prefix to each method that requires up-front data validation or that requires the tracker to be in a particular state.

Items 1, 3 and 4 are interrelated. We have discussed this in the T-con (David and Patrick) and decided to make the igstkTrackerTool objects visible to other components. In the current IGSTK code, the igstkTrackerTool objects are completely internal to the igstkTracker.

New Way of Accessing Tracker Tools

Currently, the Tracker class uses a "port number" and "tool number" together as the handle by which a particular tool is identified. This is AURORA specific and not particularly useful for other trackers. Even worse, "port number 0" corresponds to the port plug that is labelled "1" on the front of the AURORA. Trackers such as the MicronTracker and Vicra do not even have ports.

For all objects in IGSTK except for the TrackerTool, the object pointer itself is used as the handle for the object. We should use this same convention for TrackerTool objects, rather than using (port,tool) numbers as we have been doing. As discussed above, this means that the TrackerTool objects should be visible from outside of the Tracker class.

We do not want to have a "Get" method that returns the tools

Although it might seem appropriate to add a "Get" method to the Tracker class that returns a TrackerTool object, this would lead to problems:

  1. The Get method would have to be tracker-specific, since each tracker has its own way of identifying which tool is which.
  2. The Get method would have to be a "Request" method so that it can fail, and the caller would have to check for errors each time the "Get" method is called, resulting in a lot of error handling code in the application.

Right Way: The application creates TrackerTools, adds them to the Tracker

A much cleaner way of doing things is for the application to create the TrackerTool objects that it will be using, call methods of the TrackerTool objects to set information that the tracker can use to identify the tools (e.g. tool port number), and then connect them to the Tracker. This connection method is only done once, unlike a Get method that is called many times, and this will simplify the error handling.

In order for this to work, each TrackerTool object must contain information that the Tracker can use to uniquely identify it. The AuroraTrackerTool, for instance, will contain ivars that give the port and channel number, so that the AuroraTracker will know how to identify the tool. The ivars that contain this identifying information (and the methods that are used to set the information) will be defined in each device-specific subclass of TrackerTool.

What the IGSTK Tracker currently does is create a TrackerTool object for each tool that a particular tracker is capable of supporting. This is very wasteful, since some trackers can support a huge number of tools, although only 2 or 3 tools are typically used in any application! It makes more sense for the application to create the TrackerTools that it needs, and then add these to the Tracker so that positional information is reported for these tools only.

This will also simplify the use of SROMs. Right now the method to associate SROMs with a tool is a Tracker method, and whichever port we add the SROM to, we have to make sure that we later get the TrackerTool for that same port. After the redesign, we will create a TrackerTool object, and simply call a method of that TrackerTool object to associate the SROM with it.

Example: Aurora

typedef igstk::AuroraTracker TrackerType;
typedef igstk::AuroraTracker::AuroraTrackerToolType ToolType;

TrackerType::Pointer tracker = TrackerType::New();
ToolType::Pointer needleTool = ToolType::New();

needleTool->RequestSetPortNumber(1);
needleTool->RequestSetChannelNumber(0);
needleTool->RequestSetSROMFileName(srom);

tracker->RequestAddTrackerTool(needleTool);

needleTool->AttachSpatialObject(spatialObject);


The "RequestSet" methods will only work in the Initial state of the TrackerTool. After the tool is added to the tracker, calling these methods will generate an error.

Also note that the RequestAddTrackerTool method will fail in the following situations:

  1. necessary identification info was not set for the tool
  2. a tool with the same identification info has already been added

Tracker Tool State Machine

Currently the TrackerTool has a bogus state machine.

  • One input and seven states are declared
  • No transitions are defined
  • No Request methods are available
  • No Processing methods are available

Suggested State Machine definition

Here is a suggested State Machine definition

State Diagram

<graphviz> digraph G { Initial [style=bold]; Initial -> SpatialObjectAttached [label="RequestAttachSpatialObject"]; Initial -> Initial [label="RequestSetTransform",color=red]; Initial -> Initial [label="RequestAttachSpatialObject with bad object",color=red]; Initial -> Initial [label="RequestAttachToTracker",color=red]; SpatialObjectAttached -> ReadyToTrack [label="RequestAttachToTracker"]; SpatialObjectAttached -> SpatialObjectAttached [label="RequestSetTransform",color=red]; SpatialObjectAttached -> SpatialObjectAttached [label="RequestAttachSpatialObject",color=red]; ReadyToTrack -> ReadyToTrack [label="RequestSetTransform"]; ReadyToTrack -> ReadyToTrack [label="RequestAttachSpatialObject",color=red]; ReadyToTrack -> ReadyToTrack [label="RequestAttachToTracker",color=red]; } </graphviz>


Transitions in red indicate cases of Invalid requests.

Transition Table
SpatialObjectInput SpatialObjectInput Bad TransformInput TrackerInput
Initial SpatialObjectAttached Initial Initial Initial
SpatialObjectAttached SpatialObjectAttached SpatialObjectAttached SpatialObjectAttached ReadyToTrack
ReadyToTrack ReadyToTrack ReadyToTrack ReadyToTrack ReadyToTrack

Derived Classes

Derived classes such as

  • PolarisTrackerTool
  • AuroraTrackerTool
  • FlockOfBirdsTrackerTool

Will need to set up their port, channel, bird numbers before they can be attached to the Tracker. Since the State Machine of the base class is private, it is not available to the derived classes. We add therefore a protected method

virtual bool IsToolIdentificationCompleted() const

This method will be implemented in the derived classes and will return "false" if the {port,channel,bird} number of the tracker tool has not been set yet. The method will return "true" when all the identification parameters have been provided. This method will be called from the RequestAttachToTracker() method, and the returned bool will determine whether the TrackerTool transitions to the ReadyToTrack state.

The derived classes will have their own independent state machine for going from an "Initial" state, to a "IdentificationCompleted" state. Entering the "IdentificationCompleted" state will have the secondary effect of setting to "true" the boolean that is returned by the method "IsToolIdentificationCompleted". NOTE: this is not exposing directly an state of the state machine. We must always keep the State Machine private, in order to prevent the logic of the class to start leaking into the realm of the applications.

Comments: Polaris

For the Polaris, there should be two tool types: wired and wireless (or active and passive in NDI terminology). For wired tools it is necessary to set a port number, but for wireless tools there is no port number and it is necessary to set an SROM that will be used for the tool.

TrackerTool states

  • Initial: port number, SROM, and other identification info can be set
  • Connected: tool has been added to a tracker, info can no longer be set
  • Enabled: tracker is on and tool is functioning, but tracker is not attempting to track the tool
  • Visible: tracker is sucessfully tracking the tool
  • Invisible: tracker cannot track the tool (the tool is out of range)

Transitions between these states will generate events that other components can utilize.


This states have been replaced by the suggested state machine described above. The concept of Visible and Invisible is managed automatically by the expiration times of the Transforms. There is no need to make it a State of the Tracker tool. In any case, the Tracker does not report specific messages when the tools are out of range. In IGSTK that is implicitly deduced when objects have transforms that get to Expire. Ibanez 17:41, 15 August 2007 (EDT)

Methods That Will Be Changed

The most fundamental change to the Tracker component is that several methods will be moved from the Tracker class to the TrackerTool class. Very few other changes will be done.

Methods removed from Tracker

  • GetToolTransform(portNumber, toolNumber, transform): move to TrackerTool
  • AttachObjectToTool(portNumber, toolNumber, spatialObject): move to TrackerTool
  • SetToolCalibrationTransform(portNumber, toolNumber, transform): move to TrackerTool : actually moved to the SpatialObject attached to the tool
  • GetToolCalibrationTransform(portNumber, toolNumber): move to TrackerTool : is now the RequestGetTransformToParent() of the SpatialObject attached to the tool
  • SetPatientTransform(transform): move to SpatialObject, see Surgical coordinate changes. : Deprecated : the scene graph will allow using any spatial object as a reference.

The rationale for removing SetPatientTransform is discussed on Surgical coordinate changes.

Methods changed in Tracker

  • SetReferenceTool(applyReferenceTool, portNumber, toolNumber): change to RequestSetReferenceTool(toolObject)
  • GetReferenceTool(portNumber, toolNumber): return TrackerTool object instead of (portNumber, toolNumber), or perhaps this method can be removed completely

Only one of the tracker's own tools can be safely used as a reference.

Methods added to TrackerTool

  • AttachSpatialObject(spatialObject) : Actually : RequestAttachSpatialObject(spatialObject)
  • SetCalibrationTransform(transform) : Actually : SpatialObject::RequestSetCalibrationTransformToTrackerTool() in the spatial object attached to this tool
  • GetCalibrationTransform()  : Actually : SpatialObject::RequestGetTransformToParent()
  • RequestTransform(timestamp)  : Not needed anymore

The RequestTransform() method will send a transform to any observers that are listening, but only if there is a valid transform for the specified time. There will not be a GetTransform() method like the one that the tracker has, since such a method will return a transform even if no valid transform exists.

The device-specific TrackerTool derived classes will also have methods that are used for SROMS or for tool identification by port, channel, etc.

Methods added to TrackerTool derived classes

AuroraTrackerTool

  • RequestSetToolPort(port)  : RequestSetPort( int port ) : The "Tool" in the method name is redundant
  • RequestSetToolChannel(channel) : RequestSetChannel( int channel ) : The "Tool" in the method name is redundant
  • RequestSetToolSROM(SROM file) : RequestSetSROM( file ) : The "Tool" in the method name is redundant

PolarisWiredTrackerTool

  • RequestSetToolPort(port) : RequestSetPort( int port ) : The "Tool" in the method name is redundant
  • RequestSetToolSROM(SROM file)  : RequestSetSROM( file ) : The "Tool" in the method name is redundant

PolarisWirelessTrackerTool

  • RequestSetToolSROM(SROM file)  : RequestSetSROM( file ) : The "Tool" in the method name is redundant

FlockOfBirdsTrackerTool

  • RequestSetBirdNumber(bird)