
CybernetTM Systems Corporation CyberImpactTM SDK License Agreement
BEFORE ACCEPTING THIS SOFTWARE AND MATERIALS, CAREFULLY READ THE
TERMS
AND CONDITIONS OF THIS LICENSE AGREEMENT. BY ACCEPTING THIS SOFTWARE
AND
MATERIALS, YOU ARE CONSENTING TO BE BOUND BY AND ARE BECOMING
A PARTY TO
THIS LICENSE AGREEMENT. IF YOU DO NOT AGREE TO ALL OF THE TERMS
OF THIS
LICENSE AGREEMENT, DESTROY OR ERASE THE SOFTWARE AND MATERIALS.
1. CybernetTM Systems
Corporation ("Cybernet") grants to you a non-exclusive,
non-sublicensable, license to use this version of the Cybernet
CyberImpactTM Force Feedback Software Development
Kit (the "SDK Software" or "Software"), in
binary executable form for evaluation, software testing, PC game
development, simulation or engineering design software applications
only (the "License"). This License includes non-exclusive
rights to use the SDK Software to practice applicable method portions
of US Patents 5,389,865 and 5,459,382 subject to restrictions
described above and elsewhere in this License Agreement.
2. You may make copies of the SDK
Software as reasonably necessary for employees whose job duties
require them to use the Software, provided such use is limited
to the permitted uses in this License. In order to develop applications
software (subject to restrictions described in this License Agreement)
you may use, copy, modify, incorporate into, compile/link in combination
with your own programs, and distribute (in object code form only)
solely with your own programs, the SDK Software, provided that
you reproduce on each copy a Cybernet copyright notice and any
other proprietary legends that were on the original Software as
distributed, include on external material the acknowledgment in
quotation below in 8 point font or larger, and distribute such
SDK Software pursuant to a valid agreement that is at least as
protective of Cybernet's rights in the SDK Software as is this
Agreement. Except as expressly permitted in this License, you
may not make any use of the software which would violate Cybernet's
rights under copyright law including but not limited to incorporating
this Software into another software development kit, into an operating
system, or into firmware (firmware is defined as embedded software
incorporated into hardware device), and you may not decompile,
reverse engineer, disassemble, modify, rent, lease, loan, sublicense,
transmit over a network or from one computer to another, or distribute
or create derivative works based upon the SDK Software in whole
or in part. Your rights under this License will terminate automatically
without notice from Cybernet if you fail to comply with any terms(s)
of this License. "CyberImpactTM SDK is included.
Supports all CyberImpactTM Force Feedback Devices."
3. CYBERNET MAKES NO REPRESENTATIONS
ABOUT THE SUITABILITY OF THIS SOFTWARE OR ABOUT ANY CONTENT OR
INFORMATION MADE ACCESSIBLE BY THE SOFTWARE, FOR ANY PURPOSE.
THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT EXPRESS OR IMPLIED WARRANTIES,
INCLUDING WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE OR NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED GRATUITOUSLY
AND, ACCORDINGLY, CYBERNET SHALL NOT BE LIABLE UNDER ANY THEORY
OR ANY DAMAGES SUFFERED BY YOU OR ANY USER OF THE SOFTWARE. CYBERNET
DOES NOT GUARANTEE TO SUPPORT THIS SOFTWARE AND IS NOT BOUND TO
ISSUE UPDATES TO THIS SOFTWARE.
4. While Cybernet intends to distribute commercial releases of the Software, Cybernet reserves the right at any time not to release a new commercial release of the Software or, if released, to alter prices, features, specifications, capabilities, functions, licensing terms, release dates, general availability or other characteristics of the commercial release.
5. Title, ownership rights, and intellectual
property rights in and to the Software shall remain in Cybernet
and/or its suppliers. You agree to abide by the patent and copyright
law and all other applicable laws of the United States including,
but not limited to, export control laws. You acknowledge that
the Software in source code form remains a confidential trade
secret of Cybernet and/or its suppliers and therefore you agree
not to modify the Software or attempt to decipher, decompile,
disassemble or reverse engineer the Software, except to the extent
applicable laws specifically prohibit such restriction.
In no event shall Cybernet's total liability to you for all damages,
losses, and causes of action (whether in contract, tort, including
negligence, or otherwise) exceed $50. Cybernet Systems Corporation,
Cybernet Systems, Cybernet, CyberImpact, "Force-Feedback
Development Environment," "Object Representations for
Force Feedback Applications," "Force Feedback Software
Development Kit," "Feel the future", "Get
a grip on CyberImpact", "PER-Force", the Cybernet
logo, and the CyberImpact logo are trademarks of Cybernet Systems
Corporation. Copyright (c) 1992,3,4,5,6 Cybernet Systems Corporation.
All rights reserved.
All other product names and service names referenced herein are trademarks or service marks of their respective companies.
A Postscript format version of this document is available at Cybernet's home page along with other information about the SDK.
CyberImpactTM is a line of force-feedback hardware
devices and software tools produced by Cybernet Systems Corporation.
Since 1988, Cybernet Systems has been the leader in the development
of force-feedback technologies and devices, and currently holds
multiple patents in core areas of force-feedback technology. The
CyberImpactTM product line is the most extensive
line of quality, industrial-grade force-feedback devices and software
ever provided by one company. The product line currently includes:
A cost-effective 3 Degree-of-Freedom joystick which has been used for force-feedback research and utilized for remote vehicle operation
Capable of producing torques equivalent to a real steering wheel, it was developed as a tool to assist the auto industry in virtual prototyping applications
Developed for NASA for the use in low cost virtual reality flight simulators
Cybernet's flagship product developed originally for NASA to perform teleoperation of space vehicles and robotic equipment
A 21 Degree-of-Freedom device developed for Johnson Space Control Center that combines the HandController functionality with force-feedback for each finger
A custom design based on the HandController to enable disabled persons control of their environment through head motion. A similar custom design was developed for surgical simulation applications.
A full-body force-feedback device to be used in dismounted infantry soldier training.
The largest volume commercial force-feedback device available from Cybernet, the SpacePen was developed for Ford to do virtual prototyping of car body design, and also is being applied to ergonomics in aircraft maintenance.
A comprehensive, cross-platform API for development of force-feedback
in applications.
Cybernet Systems is also developing their own production of a
home entertainment force-feedback flight yoke, the Cybernet
RealFeelTM Yoke.
This document describes the CyberImpactTM Software
Developer's Kit (SDK), a set of force-feedback development
tools for integration of CyberImpactTM-compatible
hardware to an application's simulation software. The CyberImpactTM
SDK has an impressive list of features which will make it
the only SDK necessary for the integration of force-feedback
into simulation software. Features include:
Force-feedback is about providing a "sense of touch"
to your applications. By providing a device to output physical
information, force-feedback introduces users to a new sense of
immersion and interaction. A force-feedback joystick can kick
when controlling a gun being fired, become sluggish when controlling
a robot sloshing through mud, and vibrate when controlling a tank
with rough treads. A steering wheel can shake when a car is on
rocks, jolt when the car collides with other objects, and become
frictionless when the car is on ice. Then there is the whole
area of geometric and physical simulation. Surfaces of objects
and their textures can be felt and moved. Objects have mass and
inertia. All of these haptic effects (effects that stimulate the
sense of touch) provide a completely new way of involving users
with the application. The presentation of touch information will
become as important to immersive applications as audio and visual
are now.
Force-feedback devices are both input devices and display devices. Input to the application is generated upon interaction with the device, just as with normal joysticks. Force-feedback devices display information by applying forces to the user. The content of the force display is generated in the simulation software. The integrated input/output nature of these devices differentiates them significantly from simple input devices (such as traditional joystick, mice, and keyboards) and simple output devices (such as speakers and monitors). These devices are active! They are robotic devices that you physically interact with. The integration of these active devices into your software will create a new sensory modality for the user to experience.
The purpose of this document is to guide you through the installation
and testing of Cybernet's CyberImpactTM SDK.
This SDK is capable of supporting a wide variety of hardware
devices running under a range of operating systems. The information
contained in this document covers the Windows95, WindowsNT
and 32-bit Protected Mode DOS version of the SDK. Other platforms
supported include IRIX, BSD, Linux and SunOS.
The remainder of this first section provides a high level overview
of the CyberImpactTM SDK. By the end of this
first section, you should have a pretty good idea of how the elements
of the CyberImpactTM SDK fit together, and what
you need to do to get started with force-feedback development.
The Installation Section guides you through installation of CyberImpactTM
software. The Force Feedback Effects Library (FFELib) Section
describes easy-to-use API functions that provide easy-to-use haptic
effects. The Haptic Library (HAPLib) Section presents the main
API that provides the "building blocks" for all force-feedback
functionality. The Configuration Files section documents the content
of data files used by this API. The Haptic Development Environment
section describes the off-line development tool that can be used
to generate custom force-feedback effects.
Finally, the Design Considerations section offers some concepts to consider when designing your application. The Device Specific Information provides details of Cybernet's CyberImpactTM line of force-feedback devices and third-party devices. The current version of this manual documents the CyberImpactTM Joystick plus how to use your mouse as a pseudo force feedback device.
CyberImpactTM software facilitates the incorporation
of force-feedback into your application's software. The SDK is
the most advanced force-feedback software available. It has been
designed to provide all the tools necessary to create new and
interesting haptic effects while at the same time providing simple,
common effects. The tools can be used to create simple force-feedback
effects like jolts for gun kicks and vibrations for crashes (support
for common effects are included) or more complicated haptic effects
like the feeling of a flat tire, the balancing of a mass, or the
slipperiness of driving on ice. The CyberImpactTM
SDK provides the application developers with the tools necessary
to create new feels, thus differentiating your application by
"feel" as well as by sight and sound. Figure
1 presents an overview of the components that constitute
CyberImpactTM SDK software.

There are five primary components: the Force-Feedback Effects
Library (FFELib) with source code, the Haptic Library (HAPLib),
the Controls Library (CNTRLib), the Haptic Development Environment
(HDE) tool, and the Haptic Device Server. The function of these
five software components are as follows.

Future releases will available on the Internet at http://www.cybernet.com. Please contact Cybernet for further details.
This section highlights "what's new" with the latest
SDK release, and provides a history for all releases. This information
shall be geared towards helping developers stay on top of the
latest force-feedback developments, and thereby get the most out
of the CyberImpactTM SDK. The README.TXT
provide with the distribution will list in detail the changes
made with each revision, while this section will highlight the
changes made.
| Version | Date | Comments |
| 1.1 (Beta) | 23 Jul 96 | First public release of CyberImpactTM SDK. |
| 1.1 | 20 Sep 96 | Release of 1.1 Final. Supports DOS and I-Force.[2] |
| 2.0 (Beta) | 21Mar 97 | Release of Beta 2.0, with full DOS and I-Force support. Exposure of the Control's Library that HAPLib is based on. Inclusion of conditionals. Inclusion of normalized input/output parameters. Addition of new controls accessible only through the Controls Library. |
For more information on the CyberImpactTM software
or hardware please contact:
Donna Cash
313.668.2567 (Phone)
313.668.8780 (Fax)
dcash@cybernet.com
www.cybernet.com
Cybernet Systems Corporation
727 Airport Blvd.
Ann Arbor, MI 48108
The section is a guide through the installation and testing of the Windows95 version of Cybernet Systems' CyberImpactTM SDK.
This installation requires a 486 class PC or better with an available serial port. The software requires Windows95 or WindowsNT and 15 Mb of free disk space to install the Software Development Kit.
The CyberImpactTM SDK is distributed as a self-extracting
archive file named CyberImpactSDK2.0B.exe. In this file
resides the various static object libraries, dynamics link libraries
(DLL's), executables, include files, and example code needed to
start incorporating force-feedback into applications. All files
are contained in a single WINZIP self-extracting archive. To copy
the needed files to your hard disk, run the CyberImpactSDK2.0B.exe
executable. This program will prompt you for the location where
you wish to place the temporary files that are used by the standard
Windows95 InstallShield. WINZIP will automatically run the Windows95
setup program. These files can be deleted after the InstallShield
setup is complete. Alternatively, the distribution may be provided
on multiple 1.44 MB floppy disks. If installing from a floppy
distribution, insert the disk labeled 'Disk1' and run the Setup.exe
executable.
The directory selected for installation during the InstallShield
process will henceforth be referred to as $Cyb_Home, and
the InstallShield will place the software in this directory.
The CyberImpactTM directory structure will be
created under $Cyb_Home. In this directory you will find
README and LICENSE text files. The README file provides information
pertinent to the particular release received. The LICENSE file
outlines the terms associated with using CyberImpactTM
SDK software. You will also find one other type of file which
should not be deleted, DelsL*.isu. This file is used by
the UnInstallShield, in the event that you wish to remove the
CyberImpactTM SDK. To do this, consult Windows95
documentation on how to Add/Remove Programs.
As part of the Windows95 installation, a program group titled
CyberImpact SDK 2.0 Beta will be automatically added to
your Windows95 Start menu. This program group is represented
as a folder in the $Cyb_Home/CyberImpact SDK 2.0B and has
shortcuts to the Haptic Development Environment(HDE) software,
the CyberImpact Online Help, and the Sample Test Program
application. This program group will also be automatically removed
for you if UnInstallation is used.
The following sections provide brief descriptions for the files
located within each subdirectory.
This directory contains device configuration and initialization files utilized by CyberImpactTM software.
This directory contains Dynamic Link Libraries (DLL's) related to CyberImpactTM software.
This directory contains the static export libraries related CyberImpactTM software.
This directory contains header files required to make the test executable provided with this distribution.
This directory contains files associated with the Haptic Development Environment (HDE) program.
This directory contains a small test program that can be used
to test your set-up after installation. Source code for this
test program is also provided as an example of how to use the
CyberImpactTM API.
This directory contains this document in multiple formats.
IMC0: COM2, 115200, 3, 8,
1
Changing "COM2" to "COM1" directs the haptic device server to use serial port 1 to communicate with the force-feedback device. If you will be using a serial port other than 2, make the desired change in all *.nmr files located within the $Cyb_Home\config directory. See the Device Server Configuration File section for further details on the device configuration files. Note that the COM port selection can be set dynamically in the application code, as demonstrated in the test.c example source code provided with this release.
A test program is included with the CyberImpactTM SDK to provide sample source code, and to provide a testbed for compiling and linking CyberImpactTM software. The source code is located in the $Cyb_Home\test directory as the file test.c. A pre-made executable is also provided as test.exe. Versions demonstrating support for the Watcom compiler are available in the same directory as wtest.exe, a DOS4GW 32-bit DOS version, and wattest.exe, a Win95 console application.
For device-specific information explaining the process of connecting
your device to the computer, see the device information pages
in the Appendix.
The text.exe program is configured
to operate any of the following devices:
Cybernet RealFeelTM Yoke, CH Force FX
TM, CyberImpact TM Joystick, CyberImpact
TM Wheel, CyberImpact TM Yoke, CyberImpact
TM HandController.
If you do not have a force-feedback device, you can still
run this test code. You may emulate these devices using an ordinary
mouse. Simply change the
Name/Value pair DeviceType to Mouse in the device's
*.nmr file located in the
$Cyb_Home\config directory. Refer to the section on Operating
the Software without a Force-Feedback Device for more details
regarding device emulation using the mouse.
DOS USERS: Note that the test.c
code detects that the program is using the DOS 32-bit version,
and the Name/Value pair Servo is set to CLIENT automatically.
Timer and thread servo methods are not supported in DOS version.
See the Appendices concerning the Servo operation and DOS version
for a complete description.
When executed, the program will prompt the user to specify which
device is being used, and which serial port the device is connected
to. The test program then attempts to establish a connection
with the device. Once connected successfully (connecting should
take only a second or so), the test program will set up several
force feedback effects, and then display a menu of these effects
(as shown in the following figure).
NOTE: Before using any of the effects, it is recommended that
the user first position the device to the desired 'Home' or 'zero'
position, and then select the 'H' menu option. This calibrates
the device, assigning the current axes positions as zero.

The test program allows the user to selectively turn on and off
a variety force-feedback effects.
A brief description of these effects follows:
Q Shutdown Ends
the program.
H Home Allows
the user to set the zero position of the device.
I Device Information Reports
axis information, number toggles, number valuators
D Use HAP_DriveServo Toggles
use of HAP_DriveServo call
1 Sample & Play Plays
a pre-defined force profile sample on all three axes.
2 Vibration Vibrates
the roll axis.
3 Position Commands
the device to a positions, similar to a spring.
4 Velocity Engages
velocity dampening forces on the roll axis.
5 Acceleration Engages
acceleration dampening forces on the roll axis.
NOTE: For a device with more than one axis, the position,
velocity, and acceleration controls are set up to demonstrate
the difference between positive and negative control. For example,
a negative position control tries to keep axis 1 centered at zero,
resisting motion away from that position. Axis 2 has a positive
position control, repelling the axis away from the zero position.
6 Forces Commands
the device to output a constant force
7 Valuators Toggles
display of valuators values.
8 Toggles Toggles
display of toggles values.
9 Gets Toggles
display of position, velocity, acceleration, and force of a single
axis
v FFE_Vibration Renders
a vibration with specified magnitude and frequency.
i FFE_Impulse Renders
an impulse feel (a force vector for a specified duration).
f FFE_Force Renders
a directed force vector.
s FFE_Spring Renders
a spring effect with specified stiffness.
d FFE_Damping Renders
a damping effects with specified viscosity.
m FFE_Mass Renders
a variable mass effects with specified inertia.
b FFE_Buffet Renders
a random vibration in all axes.
k FFE_VibroKick Renders
a quickly fading vibration triggered by pressing toggle 1.
r FFE_Recoil Renders
a recoil force triggered by pressing toggle 1.
h FFE_MachineGun Renders
a repeating recoil force triggered by pressing toggle 1.
w FFE_Wall Renders
a wall at the zero position of the device (should home device
first).
+ Restart all effects Starts/Restarts
all effects.
- Stop all effects Stops
all effects.
/ Pause all effects Pause
all running effects.
x Remove all effects Removes
all effects.
C Center Simple
demo of a spring-like centering force, controlled by the client.
T Time HAP_* Time
a group of standard HAP calls for performance
To activate a certain effect, select the desired menu option by
pressing the number or letter preceding the effect's name. For
example, to activate the FFE_Vibration effect, press 'v'. The
test program will then ask if you wish to (S)top the effect, (P)ause
the effect, or (R)un the effect. To turn the effect off, choose
the effect again and select the 'stop effect' option. The status
of each effect is indicated by a single letter to the left of
the menu item. One of three possible letters will be present
to indicate the state of that particular effect.
'S' - effect is stopped
'P' - effect is paused
'R' - effect is running
Note: The difference between a (S)topped effect and a (P)aused
effect is that for a Paused effect, time continues to increment.
This means that when a Paused effect is restarted, it will take
into account the elapsed time and continue. For example, if an
effect is playing a predefined force profile (sample) over a time
of 10 seconds, and is paused for 2 seconds at the 5 second mark,
when restarted it continues outputting the sample forces from
the 7-second mark. Whereas a Stopped effect, when started, will
always start at the 0 second mark.
In addition to force feedback effects, several menu items are
used as toggle switches to turn on and off certain features.
These menu items gave either a (T)rue or (F)alse next to them,
indicating whether the option is on or not.
Three of the toggle menu items, '7', '8', and '9' are for displaying
data obtained by the HAP_Get* calls. Only one of the selected
items will be displayed, even if multiple items are selected,
with menu item '9' taking priority over '8', and '8' over '7'.
The data is displayed on a single line that is continuously updated.
If the user selects '9', an additional keypress is required,
specifying which axis to display. Only keys '0' - '9' are allowed
for selecting an axis.
1. Include the following files in the application:
2. Link against the following:
To ensure that your development environment is set up correctly
and that the DLL works correctly, compile and run the test.c
module within $Cyb_Home\test. In doing this, you will
effectively build another test.exe.
An example makefile for Microsoft Visual C/C++ 4.0, is
included in the $Cyb_Home\test directory as test.mak.
Compile and then run the resulting executable.
1. Include the following files in the application:
2. Link against the following:
To ensure that your development environment is set up correctly
and that the DLL works correctly, compile and run the wattest.c
module within $Cyb_Home\test. In doing this, you will
effectively build another wattest.exe.
An example makefile for Watcom C 10.6, is included in the
$Cyb_Home\test directory as wattest.mak. Compile
and then run the resulting executable. The typical command to
make is 'wmake -f wattest.mk', although you may need to edit the
files for your installation of Watcom.
1. Include the following files in the application:
2. Link against the following:
NOTE: There are 2 libraries, one using stack-based calling conventions,
the other using register based calling conventions. These libraries
correspond to the -s and -r compiler options, respectively.
Refer to the Watcom documentation for further details.
To ensure that your development environment is set up correctly
and that the DLL works correctly, compile and run the wtest.c
module within $Cyb_Home\test. In doing this, you will
effectively build another wtest.exe. To execute the program,
you must have the DOS4GW.exe in the system path.
An example makefile for Watcom C 10.6, is included in the
$Cyb_Home\test directory as wtest.mak. Compile
and then run the resulting executable. The typical command to
make is 'wmake -f wtest.mk', although you may need to edit the
files for your installation of Watcom.
The section is a guide through the installation and testing of the UNIX versions of Cybernet Systems' CyberImpactTM SDK.
This installation requires a 486 class PC or better with an available serial port. The software requires SGI-IRIX 5.3 or FreeBSD and 15 Mb of free disk space to install the Software Development Kit.
The CyberImpactTM SDK is distributed as a gzipped
tar archive file named CyberImpactSDK20B[sgi|bsd].tar.gz.
In this file resides the various static object libraries, executables,
include files, and example code needed to start incorporating
force-feedback into applications. Extraction of the archived files
will require the gzip compression application, which can be found
at various sites that distribute gnu applications, and tar, which
is the default archiver on most UNIX based systems. The compressed
archive must first be decompressed using gzip and then extracted
from using tar.
At the root of the CyberImpactTM directory structure
you will find README and LICENSE text files. The README file
provides information pertinent to the particular release received.
The LICENSE file outlines the terms associated with using CyberImpactTM
SDK software.
The following sections provide brief descriptions for the files
located within each subdirectory.
This directory contains device configuration and initialization files utilized by CyberImpactTM software.
This directory contains the static libraries related CyberImpactTM software.
This directory contains header files required to make the test executable provided with this distribution.
This directory contains a small test program that can be used
to test your set-up after installation. Source code for this
test program is also provided as an example of how to use the
CyberImpactTM API.
This directory contains this document in multiple formats.
The distributed files are set up to use serial port COM2 by default.
If your system requires you to use another serial port, you can
do this easily by looking in the config directory and editing
one line in the *.nmr file corresponding to your hardware
device. For example, if you have the Cybernet RealFeel YokeTM
and want to connect to COM2, you would need to edit the RFYoke.nmr
file located in the $Cyb_Home\config directory. Each *.nmr
file contains a line which looks like this:
IMC0: COM2, 115200, 3, 8,
1
Changing "COM2" to "COM1" directs the haptic device server to use serial port 1 to communicate with the force-feedback device. If you will be using a serial port other than 2, make the desired change in all *.nmr files located within the config directory. See the Device Server Configuration File section for further details on the device configuration files. Note that the COM port selection can be set dynamically in the application code, as demonstrated in the test.c example source code provided with this release.
A test program is included with the CyberImpactTM SDK to provide sample source code, and to provide a testbed for compiling and linking CyberImpactTM software. The source code is located in the test directory as the file test.c. A pre-made executable is also provided as Test.
For device-specific information explaining the process of connecting
your device to the computer, see the device information pages
in the Appendix.
The Test program is configured
to operate any of the following devices:
Cybernet RealFeelTM Yoke, CH Force FX
TM, CyberImpact TM Joystick, CyberImpact
TM Wheel, CyberImpact TM Yoke, CyberImpact
TM HandController.
If you do not have a force-feedback device, you can still
run this test code. You may emulate these devices using an ordinary
mouse. Simply change the
Name/Value pair DeviceType to Mouse in the device's
*.nmr file located in the
config directory. Refer to the section on Operating the
Software without a Force-Feedback Device for more details regarding
device emulation using the mouse.
SGI-IRIX USERS: Note that the beta release requires client/server
implementation when using a SGI-IRIX machine. A Windows95
server is available at Cybernets' web site (www.cybernet.com)
for this purpose. Also, the haptic.dev configuration file located
in the config directory must be modified for the client/server
configuration as described in Section 4.
When executed, the program will prompt the user to specify which
device is being used, and which serial port the device is connected
to. The test program then attempts to establish a connection
with the device. Once connected successfully (connecting should
take only a second or so), the test program will set up several
force feedback effects, and then display a menu of these effects
(as shown in the following figure).
NOTE: Before using any of the effects, it is recommended that
the user first position the device to the desired 'Home' or 'zero'
position, and then select the 'H' menu option. This calibrates
the device, assigning the current axes positions as zero.

The test program allows the user to selectively turn on and off
a variety force-feedback effects.
A brief description of these effects follows:
Q Shutdown Ends
the program.
H Home Allows
the user to set the zero position of the device.
I Device Information Reports
axis information, number toggles, number valuators
D Use HAP_DriveServo Toggles
use of HAP_DriveServo call
1 Sample & Play Plays
a pre-defined force profile sample on all three axes.
2 Vibration Vibrates
the roll axis.
3 Position Commands
the device to a positions, similar to a spring.
4 Velocity Engages
velocity dampening forces on the roll axis.
5 Acceleration Engages
acceleration dampening forces on the roll axis.
NOTE: For a device with more than one axis, the position,
velocity, and acceleration controls are set up to demonstrate
the difference between positive and negative control. For example,
a negative position control tries to keep axis 1 centered at zero,
resisting motion away from that position. Axis 2 has a positive
position control, repelling the axis away from the zero position.
6 Forces Commands
the device to output a constant force
7 Valuators Toggles
display of valuators values.
8 Toggles Toggles
display of toggles values.
9 Gets Toggles
display of position, velocity, acceleration, and force of a single
axis
v FFE_Vibration Renders
a vibration with specified magnitude and frequency.
i FFE_Impulse Renders
an impulse feel (a force vector for a specified duration).
f FFE_Force Renders
a directed force vector.
s FFE_Spring Renders
a spring effect with specified stiffness.
d FFE_Damping Renders
a damping effects with specified viscosity.
m FFE_Mass Renders
a variable mass effects with specified inertia.
b FFE_Buffet Renders
a random vibration in all axes.
k FFE_VibroKick Renders
a quickly fading vibration triggered by pressing toggle 1.
r FFE_Recoil Renders
a recoil force triggered by pressing toggle 1.
h FFE_MachineGun Renders
a repeating recoil force triggered by pressing toggle 1.
w FFE_Wall Renders
a wall at the zero position of the device (should home device
first).
+ Restart all effects Starts/Restarts
all effects.
- Stop all effects Stops
all effects.
/ Pause all effects Pause
all running effects.
x Remove all effects Removes
all effects.
C Center Simple
demo of a spring-like centering force, controlled by the client.
T Time HAP_* Time
a group of standard HAP calls for performance
To activate a certain effect, select the desired menu option by
pressing the number or letter preceding the effect's name. For
example, to activate the FFE_Vibration effect, press 'v'. The
test program will then ask if you wish to (S)top the effect, (P)ause
the effect, or (R)un the effect. To turn the effect off, choose
the effect again and select the 'stop effect' option. The status
of each effect is indicated by a single letter to the left of
the menu item. One of three possible letters will be present
to indicate the state of that particular effect.
'S' - effect is stopped
'P' - effect is paused
'R' - effect is running
Note: The difference between a (S)topped effect and a (P)aused
effect is that for a Paused effect, time continues to increment.
This means that when a Paused effect is restarted, it will take
into account the elapsed time and continue. For example, if an
effect is playing a predefined force profile (sample) over a time
of 10 seconds, and is paused for 2 seconds at the 5 second mark,
when restarted it continues outputting the sample forces from
the 7-second mark. Whereas a Stopped effect, when started, will
always start at the 0 second mark.
In addition to force feedback effects, several menu items are
used as toggle switches to turn on and off certain features.
These menu items gave either a (T)rue or (F)alse next to them,
indicating whether the option is on or not.
Three of the toggle menu items, '7', '8', and '9' are for displaying
data obtained by the HAP_Get* calls. Only one of the selected
items will be displayed, even if multiple items are selected,
with menu item '9' taking priority over '8', and '8' over '7'.
The data is displayed on a single line that is continuously updated.
If the user selects '9', an additional keypress is required,
specifying which axis to display. Only keys '0' - '9' are allowed
for selecting an axis.
The CyberImpactTM SDK provides libraries and
example source code to create and execute programs using either
the SGI-IRIX 5.3 operating system or FreeBSD.
1. Include the following files in the application:
2. Link against the following:
The actual Makefile used to build the Test executable is
included in the test directory as Makefile. This
makefile can be as a template when creating makefiles to build
your own executables..
The CyberImpactTM SDK provides the capability
to perform force-feedback processing as part of the client application
or to off load force feedback processing onto a separate machine
(or the same machine but different process) and communicate over
a TCP/IP socket to the Haptic Device Server (HDS).
By incorporating the HDS into the client application (the function
call communication version), communication between client application
and HDS is very fast (as opposed to socket communication). Running
the application is also a little simpler (you don't have to execute
a separate program). However, because the HDS is part of the
client application and therefor resides on the same machine, it
must share the processor with the application (The HDS consumes
about 5% of a Pentium 133MHz processor).
By running the HDS on a separate machine (the socket version),
the HDS then performs the low level high frequency servoing required
for realistic force-feedback without consuming processor cycles
from the client application. If you wish to off load force-feedback
in such a way, you must download a HDS binary (executable) from
Cybernet's website. There is a separate executable file for each
supported platform (Windows95/NT, FreeBSD, and IRIX). The executable
is stand-alone and can be run in its corresponding OS without
the need for any setup files. The Haptic Device Server must be
running before the client application (simulation, game, etc.)
can use force-feedback, so execute the Device Server application
first on the machine that is physically connected to the force-feedback
device Execution of the Windows95 device server requires the
specification of keyword "Socket" and the number of
the port to listen for connections on.
Usage(for the Windows95/NT HDS):
DS.exe Socket 2112
This listen port number (2112 in this case) must match that in
the haptic.dev file described below.
Back on the client application side, simply edit the haptic.dev
file and find the line matching the device you are connecting
to and make the following modifications:
To use the direct function call communication where the HDS is actually linked into the same executable as the client, use the setup line that looks like this:
CybernetRealFeelYoke = FunCall,
..\config\RFYoke.nmr
To use the TCP/IP socket call communication where the HDS is a separately executing process from the client, use the setup line that looks like this:
CybernetRealFeelYoke = Socket,
..\config\RFYoke.nmr, ###.###.##.#, 2112
Where ###.###.##.# is the IP address of the machine where
the HDS must be currently running and 2112 is the port number
that it is listening on. CybernetRealFeelYoke is the name
of the physical device connected to the HDS, and the RFYoke.nmr
file contains configuration data (See the section on Configuration
Files for more details about haptic.dev and RFYoke.nmr).
When the HDS is run, it will come up in a "Waiting for Connection" state. You may now run the client/application program.
When the client application is executed, it will simply initiates a connection to a currently
executing Haptic Device Server(described above) which will then
provide force-feedback. This allows the haptic servoing to occur
on a separate machine, thus off loading the processor cycles required
for force feedback away from the application.
Because the communication method has been abstracted away from
application programmers, they will not have to use the CyberImpactTM
SDK any differently than when using the direct function call
method.
This section is designed to introduce application developers to force-feedback by describing higher-level force-feedback "effects" that may be useful to their development efforts. The described effects provide a tool kit for the creation of new effects, combination of commonly used effects, and augmentation of described effects. For each effect, example applications will be described, API calls used for implementation will be listed, and facilities by which to customize the effect will be detailed.
This section documents elements of the API that are shared by all Force-Feedback Effect (FFE) Library function calls.
All functions return a status structure that contains an
error code and a description of any error that might have occurred.
The structure will be referred to as RetError in the
examples of the functions and is defined as:
typedef
struct status_type {
unsigned char description[1000];
long number;
short iserror;
} status_type;
Where iserror is set to 1 if an error has occurred, 0 if not. If an error has occurred (iserror is 1), then number and description fields will have been filled. The description field contains a verbal description of the problem including a description of the call stack, from deep to shallow, left to right. Upon error, the number field will contain an integer value corresponding to an enumeration of the API function error.
In Version 2.0, error numbers with the last three digits less than 500 are fatal errors that cause the server to shut down. Error numbers with the last three digits greater than or equal to 500 are non-fatal and allow the server to continue functioning. Both fatal and non-fatal errors are reported with the above error structure. TIP: A convenient way to determine whether an error is fatal or not is to use the modulus operator (%) on the error number. (Error_number)%1000 will give you a number from 0-999 that you can use to determine the severity of the error (<500 is fatal, >=500 non-fatal).
Refer to the Error Codes Section to see a list of possible error
codes. Error codes that are returned and that are not on the
list are internal errors and should be referred to Cybernet Systems,
along with the description string.
The first parameter to every function listed in this section is a single or double pointer to MPC_communication_struct_type structure. HAP_Open allocates space for and fills this structure which is then used for all subsequent calls to the same device server. This structure contains information about the communication to the device server.
Many of the haptic functions described in the following sections
parameterize physical device behaviors. Essentially, a haptic
device is a robot that is made up of a number of links
joined together at joints (joints are also called
axes). Each axis which joins two links in the haptic device
may be either translational or rotational. A translation
(or prismatic) axis is an axis whose Degree of Freedom (DoF) allows
one of its two connecting links to move in a straight line with
respect to the other (like the two parts of an extending ladder).
A rotational (or revolute) axis is one whose DoF allows
the axis to act as a hinge between its two connecting links.
To facilitate the description of haptic functions, as well as
to standardize their use, a set of generic units of measure will
be adhered to. The CyberImpactTM FFELib API
defaults to using metric SI units. The units of measure which
will be implied during the remainder of the haptic library description
are listed below:
| Unit | Axis Dependent | Description |
| Second (S) | No | Unit of time |
| Meter (M) | Translation | Linear position (distance) |
| M/S | Translation | Meters per second - linear velocity |
| M/S2 | Translation | Meters per second per second -linear acceleration |
| KG | No | Kilogram - mass |
| Newton | Translation | Kilogram-meters per second -linear force magnitude |
| Radian (R) | Rotational | Angular position or distance |
| R/S | Rotational | Radians per second - angular velocity |
| R/S2 | Rotational | Radians per second per second - angular acceleration |
| NM | Rotational | Newton-Meters - angular torque |
Units of measure other than those listed are used within the scope
of the haptic library, but they are simply comprised of those
that listed in Table 1 (e.g. rotational geometric
attraction scaling factor would be given as NM/M2 (Newton-Meters
per Meters squared)).
In addition to the metric SI Units, two other modes are available, depending on the application's needs.
These two modes are Normalized and Native. In Normalized
mode, all units except for time are scaled to cover the range
from -1.0 to 1.0. This provides a more device-independent method
of supplying parameters to haptic effects. The Native mode
provides the units as they come from the device. For example,
if a rotational stage has 12 bits of information for its position,
then values 0 to 4095 will be returned.
In order to give the application complete control over the execution
of haptic effects without forcing the application programmer to
keep track of ongoing effects explicitly, it was necessary to
create an effects handle. Those FFE functions that cause effects
to occur and/or modify the behavior of effects, have as a second
argument a pointer to a pointer to the effect handle structure
(e.g. HAP_handle_struct **handle).
The existence of this input/output value gives the application
a method to refer to a previously instantiated effect for the
purposes of either terminating the effect or altering its behavior.
Supplying one of the effect functions (e.g. HAP_PutForce)
with a handle pointer that has not been allocated (i.e. is NULL)
will cause the effect function to allocate space for handle
and instantiate a new effect. Supplying an allocated handle to
this function will have the result of altering the effect that
was previously instantiated and the handle itself is left unchanged.
A given handle pointer cannot be passed around to just any effects
function. The effects function HAP_Play will not operate
correctly with a handle recently returned from HAP_PutPos
since they are unrelated effects. A handle can usually be used
in only one or two functions. Table 2 presents
the sets of FFELib functions that can reference a common handle.
| Function | Can share handle with : |
| HAP_PauseEffect | all effects functions |
| HAP_RemoveEffect | all effects functions |
| HAP_RestartEffect | all effects functions |
| HAP_StopEffect | all effects functions |
Supplying a handle returned from an unrelated HAP function will
cause the function to return an error. Additionally, a single
device server is only capable of instantiating a limited number
of effects of any one type. Should the application attempt to
exceed this number, the effects function will return an error.
Wording conventions used within the following API function descriptions
are described here:
Vector: The word vector
is often used when discussing the transmission and retrieval of
axis information. Since typical haptic devices have a number
of axes that we control simultaneously, it is useful to refer
to the data sent to or received from the axes as vectors. For
example, we can send a force vector of length 6 to a 6
DOF haptic device. Each element in the vector would then correspond
to one axis of the device.
Error: This
word is used to describe the difference between a desired value
and the actual value. For example, the HAP_PutPos function can
command the haptic device to pull towards or push away from, a
supplied reference position. In this case, the position error
is the difference between this reference position and the actual
physical position of the device axis.
Memory: Throughout the following
functions, the responsibility of memory allocation is assumed
to be as follows; The MPC_communication_struct_type and the HAP_handle_struct
are allocated and maintained by the Haptic library. The application
need only provide the memory for the pointer to the structures.
For functions that are using arrays to receive or send data,
it is assumed that the application has allocated enough space.
The space required is determined by the function prototypes and
by the device specific characteristics, such as number of axes
and number of buttons. If the memory is incorrectly handled by
the application, unpredictable results may occur and will not
be returned as errors by the haptic API functions.
Axes Order: Axes are reported in order (1, 2, 3, etc.).
See device specific documentation in the Device Specific Information
Section for axis order information.
The following functions are provided as part of the Force-Feedback
Effects Library, FFELib:
FFE_Force : Applies
a force vector using each of the device's axes.
FFE_Impulse : Applies
a force vector for a specified length of time.
FFE_Vibration : Vibrates
device at specified frequency and magnitude as specified for each
axis.
FFE_Spring : Causes
device to pull/push with a specified strength for each axis.
FFE_Damping : Generates
forces that resist motion by specified viscosity for each axis.
FFE_Mass : Generates
forces that resist acceleration by specified inertia for each
axis.
FFE_Buffet : Applies
random forces across each axis with specified frequency/magnitude.
FFE_VibroKick : Causes
a quicky fading vibration when a specified toggle is pressed.
FFE_Recoil : Applies
a force for a specified time when a specified toggle is pressed.
FFE_MachineGun : Causes a repeating timed force
while a specified toggle is pressed.
To implement effects using the FFE functions, they must
be used in conjunction with the following HAPLib utility
functions:
HAP_Open : Opens
a communications channel to a haptic device server
HAP_Close : Closes
a communications channel to a haptic device server
HAP_RestartEffect : Command the haptic server
to start/restart an effect that was previously stopped.
HAP_StopEffect :
Command the haptic server to stop a currently executing effect.
HAP_RemoveEffect : Command the haptic server to
remove a previously instantiated effect.
Purpose:
This function instantiates an effect that applies a force vector
using each axis of the device. A magnitude vector is passed to
it which specifies how much force (and in what direction, positive
or negative) to apply to each axis. If no force is desired in
a particular axis, use a value of 0.0 for the corresponding element
of the axes_force array.
Prototype:
status_type *FFE_Force(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_force );
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
axes_force is a pointer to a floating point array vector
filled with values representing the forces which will be applied
to the haptic device axes.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *ForceHandle;
status_type *RetError;
float axes_force[3]; /* For a 3-DOF device */
axes_force[0] = 5.3; /* Newtons */
axes_force[1] = 2.5; /* Newtons */
axes_force[2] = -7.9; /* Newtons */
RetError = FFE_Force(HapticID, &ForceHandle, axes_force);
if(RetError->iserror){
PrintError("Error
with FFE_Force(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). The number of elements in the axes_force vector must be greater than or equal to the number of device axes.
Purpose:
This function instantiates an effect that applies a force vector
using all axes of the device for a specified duration. A magnitude
vector is passed to it which specifies how much force (and in
what direction, positive or negative) to apply to each axis.
If no impulse is desired in a particular axis, use a value of
0.0 for the corresponding element of the axes_force array.
Prototype:
status_type *FFE_Impulse(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_force ,
float
time);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
axes_force is a pointer to a floating point array vector
filled with values representing the forces which will be applied
to the haptic device axes.
time is a single floating point value representing the
duration of time (measured in seconds) to apply the forces to
the haptic device axes. The same duration value is used for all
axes.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *ForceHandle;
status_type *RetError;
float axes_force[3]; /* For a 3-DOF device */
float duration; /* Used for all axes */
axes_force[0] = 5.3; /* Newtons */
axes_force[1] = 2.5; /* Newtons */
axes_force[2] = -7.9; /* Newtons */
duration = 2.3; /* Seconds */
RetError = FFE_Impulse(HapticID, &ImpulseHandle,
axes_force, duration);
if(RetError->iserror){
PrintError("Error
with HAP_PutForce(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). The number of elements in the axes_force vector must be greater than or equal to the number of device axes.
Purpose:
This function instantiates an effect that applies a spring effect
on all axes of the device. The user defines a position and a
spring stiffness factor for each axis. The device axis will then
pull towards or push away from the define position depending on
the sign of the stiffness factor. Positive stiffness is used
to pull toward the position, and negative stiffness is used to
push away from the position. The strength of the spring is specified
in the magnitude of the stiffness. If no spring effect is desired
in a particular axis, use a value of 0.0 for the corresponding
element of the stiffness array.
Prototype:
status_type *FFE_Spring(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*dpos ,
float
*stiffness);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
dpos is a pointer to a floating point array vector. Each
value of this array represents the position for one axis toward
or away from which the device will be attracted or repelled respectively.
stiffness is a pointer to a floating point array vector.
Each element in this array represents the spring strength (magnitude
of the stiffness) and whether it is attractive (positive) or repulsive
(negative).
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *SpringHandle;
status_type *RetError;
float dpos[3]; /* For a 3-DOF device */
float stiffness[3]; /* For a 3 DOF device */
dpos[0] = 0.4; /* Meters or Radians */
dpos[1] = 0.6; /* Meters or Radians */
dpos[2] = -0.8; /* Meters or Radians */
/* Newtons per Meter or Newton-meters per radian */
stiffness[0] = 2.3;
stiffness[1] = 1.0;
stiffness[2] = 4.0;
RetError = FFE_Spring(HapticID, &SpringHandle, dpos, stiffness);
if(RetError->iserror){
PrintError("Error
with HAP_Spring(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). The number of elements in the dpos vector and the stiffness vector must be greater than or equal to the number of device axes.
Purpose:
This function instantiates an effect that applies a viscous effect
on each axes of the device. The user defines a damping factor
for each axis. The device axis will then resist or amplify any
motion of the axis depending on the sign of the viscosity. Positive
viscous values resist motion, and negative values amplify motion.
The strength of the damping/amplification is specified by the
magnitude of the viscosity term. If no damping effect
is desired in a particular axis, use a value of 0.0 for the corresponding
element of the viscosity array.
Prototype:
status_type *FFE_Damping(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*viscosity);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
viscosity is a pointer to a floating point array vector.
Each element in this array represents the damping/amplification
strength (magnitude of the viscosity) and whether it is damping
(positive) or amplifying (negative).
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *DampingHandle;
status_type *RetError;
float viscosity[3]; /* For a 3 DOF device */
/* Newtons per Meter per second or
Newton-meter per radian per second */
viscosity[0] = 2.3;
viscosity[1] = 1.0;
viscosity[2] = 4.0;
RetError = FFE_Damping(HapticID, &DampingHandle, stiffness);
if(RetError->iserror){
PrintError("Error
with FFE_Damping(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). The number of elements in the viscosity vector must be greater than or equal to the number of device axes.
Purpose:
This function instantiates an effect that applies an mass/inertia
effect on each axes of the device. The user defines an inertia
factor for each axis. The effect will then increase or decrease
the apparent mass of the device along each axis depending on the
sign of the inertia value. Positive inertia increases apparent
mass, negative decreases it. The strength of the effect is specified
by the magnitude of the inertia term. A large positive
value would make the axis feel much heavier, whereas a large negative
value would make the axis feel unnaturally light. If no mass
effect is desired in a particular axis, use a value of 0.0 for
the corresponding element of the inertia array.
Prototype:
status_type *FFE_Mass(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*inertia);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
inertia is a pointer to a floating point array vector.
Each element in this array represents the inertia strength (mass)
and whether it is making the device lighter or heavier (positive
and negative inertia sign respectively).
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *MassHandle;
status_type *RetError;
float inertia[3]; /* For a 3 DOF device */
/* Newtons per Meters per second per second or
Newton-meter per radians per second per seconds */
inertia[0] = 2.3;
inertia[1] = 1.0;
inertia[2] = 4.0;
RetError = FFE_Mass(HapticID, &MassHandle, inertia);
if(RetError->iserror){
PrintError("Error
with FFE_Mass(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). The number of elements in the inertia vector must be greater than or equal to the number of device axes.
Purpose:
This function instantiates an effect that applies a vibration
effect on each axis of the device. The user defines vibration
magnitude for each axis, and a single frequency that is used for
all axes. If no vibration is desired in a particular axis, use
a value of 0.0 for the corresponding element of the magnitude
array.
Prototype:
status_type *FFE_Vibration(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*magnitude,
float
frequency);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
magnitude is a pointer to a floating point array vector.
Each element in this array represents the magnitude at which
the axis should vibrate. The force on the axis will range between
positive and negative magnitude.
frequency is the number of vibration cycles per second.
E.g. a frequency of 6 would cause the axis to oscillate 6 times
every second.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *VibHandle;
status_type *RetError;
float magnitude[3]; /* For a 3 DOF device */
float frequency;
/* Newtons */
magnitude[0] = 0.3;
magnitude[1] = 0.3;
magnitude[2] = 0.1;
frequency = 7.3; /* oscillations per second */
RetError = FFE_Vibration(HapticID, &VibHandle,
magnitude, frequency);
if(RetError->iserror){
PrintError("Error
with FFE_Vibration(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). The number of elements in the magnitude vector must be greater than or equal to the number of device axes.
Purpose:
This function instantiates an effect that applies a random vibration
effect using each axis of the device. The user defines a magnitude
and a frequency for this "shaking" effect. If no buffet
effect is desired in a particular axis, use a value of 0.0 for
the corresponding element of the magnitude array.
The developer indicates whether a new buffet effect should be
started or an existing one should be used by either passing in
NULL pointers for the play_handle and sample_handles,
or by passing in already created structures from a previous call
to FFE_Buffet.
Prototype:
status_type *FFE_Buffet(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**play_handle,
HAP_handle_struct
***sample_handles,
float
*magnitude,
float
frequency);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
play_handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect. This is the effect that the developer will use calls
such as HAP_RestartEffect to control.
sample_handles is a pointer to an array of pointers to
a HAP_handle_struct structure and the method by which the application
can reference the effect.
magnitude is a pointer to a floating point array vector.
Each value specifies the maximum force used when defining the
random force to be applied.
frequency is the number of random magnitude value per second
used to generate the buffet feel. The same value is used for
each axis.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
sample_handles is an array of HAP_handle_structs, 1 for
each axis, that are random samples used to provide the buffet
effect. Each HAP_handle_struct should be removed with HAP_RemoveEffect
after the buffet effect is used.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *BufHandle;
status_type *RetError;
float magnitude[3]; /* For a 3-DOF device */
float frequency;
/* Newtons */
magnitude[0] = 0.5;
magnitude[1] = 0.9;
magnitude[2] = 0.0;
/* Samples generated per second */
frequency = 9.0;
RetError = FFE_Buffet(HapticID, &BufHandle, magnitude, frequency);
if(RetError->iserror){
PrintError("Error
with FFE_Buffet(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function instantiates an effect that applies a quickly fading
vibration effect to axes of the device when a specified toggle
is pressed. The user defines the vibration magnitude for each
axis and duration for this vibration effect. If no effect is
desired in a particular axis, use a value of 0.0 for the corresponding
element of the magnitude array.
Prototype:
status_type *FFE_VibroKick(
MPC_communication_struct_type
*Haptic_ID,
META_handle_struct
**meta_handle,
float
*magnitude,
unsigned
short toggle_num,
float
seconds);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
meta_handle is a pointer to a pointer to a META_handle_struct
structure and the method by which the application can reference
the effect. The developer will use calls such as FFE_MetaStart
to control this effect.
magnitude is a pointer to a floating point array vector.
Each value specifies the maximum force used when defining the
vibration to be applied.
toggle_num is the index of the toggle that will cause this
effect to start.
seconds is the duration of the vibration feel. The same
value is used for each axis.
Output:
meta_handle is a pointer to a pointer to a HAP_handle_struct structure and the method by which the application can reference the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
META_handle_struct *VibKickHandle;
status_type *RetError;
float magnitude[3]; /* For a 3-DOF device */
float seconds;
unsigned short toggle;
magnitude[0] = 0.5; /* Newtons */
magnitude[1] = 0.9;
magnitude[2] = 0.0;
seconds = 1.0; /* Duration of the effect */
toggle = 1.0; /* Toggle that will start this effect */
RetError = FFE_VibroKick(HapticID, &VibKickHandle, magnitude,
toggle, seconds);
if(RetError->iserror){
PrintError("Error
with FFE_VibroKick(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function instantiates an effect that applies a recoil force
to specified axes of the device when a specified toggle is pressed.
The user defines the force magnitude for each axis and the duration
of the effect. If no effect is desired in a particular axis,
use a value of 0.0 for the corresponding element of the magnitude
array.
Prototype:
status_type *FFE_Recoil(
MPC_communication_struct_type
*Haptic_ID,
META_handle_struct
**meta_handle,
float
*magnitude,
unsigned
short toggle_num,
float
seconds);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
meta_handle is a pointer to a pointer to a META_handle_struct
structure and the method by which the application can reference
the effect. The developer will use calls such as FFE_MetaStart
to control this effect.
magnitude is a pointer to a floating point array vector.
Each value specifies the maximum force used when defining the
recoil to be applied.
toggle_num is the index of the toggle that will cause this
effect to start.
seconds is the duration of the recoil feel. The same value
is used for each axis.
Output:
meta_handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
META_handle_struct *RecoilHandle;
status_type *RetError;
float magnitude[3]; /* For a 3-DOF device */
float seconds;
unsigned short toggle;
/* Newtons */
magnitude[0] = 0.5;
magnitude[1] = 0.9;
magnitude[2] = 0.0;
/* Duration of the effect */
seconds = 1.0;
/* Toggle that will start this effect */
toggle = 1.0;
RetError = FFE_Recoil(HapticID, &RecoilHandle, magnitude,
toggle, seconds);
if(RetError->iserror){
PrintError("Error
with FFE_Recoil(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function instantiates an effect that applies a repeating
recoil force to specified axes of the device while a specified
toggle is pressed. The user defines the force magnitude for each
axis, the duration of the effect, and whether the effect should
loop or not . If no effect is desired in a particular axis, use
a value of 0.0 for the corresponding element of the magnitude
array.
Prototype:
status_type *FFE_MachineGun(
MPC_communication_struct_type
*Haptic_ID,
META_handle_struct
**meta_handle,
float
*magnitude,
unsigned
short toggle_num,
float
seconds,
unsigned
char loop);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
meta_handle is a pointer to a pointer to a META_handle_struct
structure and the method by which the application can reference
the effect. The developer will use calls such as FFE_MetaStart
to control this effect.
magnitude is a pointer to a floating point array vector.
Each value specifies the maximum force used when defining the
recoil to be applied.
toggle_num is the index of the toggle that will cause this
effect to play.
seconds is the duration of each recoil feel. The same
value is used for each axis.
loop specifies whether the effect should repeat when one
recoil is finished. In this way, the developer can specify fully
automatic or single-shot.
Output:
meta_handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
META_handle_struct *MachineGunHandle;
status_type *RetError;
float magnitude[3]; /* For a 3-DOF device */
float seconds;
unsigned short toggle;
unsigned char loop;
/* Newtons */
magnitude[0] = 0.5;
magnitude[1] = 0.9;
magnitude[2] = 0.0;
/* Duration of each effect */
seconds = 0.1;
/* Toggle that will start this effect */
toggle = 1.0;
/* Specify fully automatic (rapid fire) */
loop = 1.0;
RetError = FFE_MachineGun(HapticID, &MachineGunHandle, magnitude,
toggle, seconds, loop);
if(RetError->iserror){
PrintError("Error
with FFE_MachineGun(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function creates a virtual wall defined by the point
and normal parameters. If the position of a point defined
by the device axes info passes the wall plane, a force generated
on the device. The wall and point coordinates are defined in
up to three dimensions, depending on the number of axes of the
device. For example, this function will work in two dimensions
(x,y) for a two axis joystick, and in one dimension (x) for a
steering wheel.
The force generated is based on two factors: the distance that
the point has penetrated the surface, and how fast the point
is moving. The constants kp and kv are used a scaling
factors for these two quantities as follows:
force =
(kp * (penetration distance)) + (kv * velocity)
Prototype:
status_type *FFE_Wall(
MPC_communication_struct_type
*HapticID,
META_Handle_Struct
**meta_handle,
float
magnitude,
float
point,
float
normal,
float
kp,
float
kv);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
meta_handle is a structure containing a list of handle
pointers to a pointer to a HAP_Handle_Struct structure and the
method by which the application can reference the effect.
magnitude is a pointer to a floating point array vector.
Each value specifies the scalings used when applying the forces
to the axes.
point is a pointer to an array defining a point on the
wall.
normal is a pointer to an array vector that is perpendicular
to the wall.
kp is a scaling factor to compute the position component
of penetration force. F = kp * penetration distance.
kv is a scaling factor to compute the velocity component
of penetration force. F = kv * point velocity.
Output:
meta_handle is a pointer to a pointer to a META_Handle_Struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type.
Example Call:
MPC_communication_struct_type *HapticID;
META_handle_struct *WallHandle;
status_type *RetError;
float magnitude[3]; /* For a 3-DOF device */
float point[3];
float normal[3];
float kp;
float kv;
/* Newtons */
magnitude[0] = 0.5;
magnitude[1] = 0.9;
magnitude[2] = 0.0;
/* point on the wall (wall passes through origin) */
point[0] = 0.0;
point[1] = 0.0;
point[2] = 0.0;
/* vector perpendicular to wall (wall is perpendicular to x-axis)
*/
normal[0] = 1.0;
normal[0] = 0.0;
normal[0] = 0.0;
/* Specify material properties of wall */
kp = -1.0;
kv = -0.02;
RetError=FFE_Wall(HapticID, &WallHandle, magnitude, point,
normal, kp, kv);
if(RetError->iserror){
PrintError("Error
with FFE_Wall(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function creates a vibration on each of the device axes lasting
for a limited time. The magnitudes of the vibration are defined
by the magnitude array (a magnitude of zero for an axis
means no effect for that axis). The duration of the effect is
specified by the time parameter, and the frequency of the
vibration is defined by the frequency parameter (same for
all axes).
Prototype:
status_type *FFE_VibrateAxes(
MPC_communication_struct_type *Haptic_ID,
META_handle_struct **meta_handle,
float *magnitudes,
float time,
float frequency);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
meta_handle is a structure containing a list of handle
pointers to a pointer to a HAP_Handle_Struct structure and the
method by which the application can reference the effect.
magnitude is a pointer to a floating point array vector.
Each value specifies the maximum force used when defining the
vibration to be applied. If no effect is desired for a certain
axis, assign the axis a magnitude of zero.
time is a float specifying the duration of this effect.
frequency is a a float defining the frequency of the vibration.
Output:
meta_handle is a pointer to a pointer to a META_Handle_Struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type.
Example Call:
MPC_communication_struct_type *HapticID;
META_handle_struct *VibAxesHandle;
status_type *RetError;
float magnitude[3]; /* For a 3-DOF device */
float time;
float frequency;
/* Scalings */
magnitude[0] = 0.5;
magnitude[1] = 0.9;
magnitude[2] = 0.0;
/* duration of the effect */
time = 3.0;
/* frequency of the vibration */
frequency = 10.0;
RetError=FFE_VibrateAxes(HapticID, &VibAxesHandle, magnitude,
time, frequency);
if(RetError->iserror){
PrintError("Error
with FFE_VibrateAxes(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function creates a wave effect on a certain axis. The axis
affected by this effect is specified by the axis parameter,
the magnitude of the vibration is defined by the magnitude
parameter, and the frequency of the vibration is defined by the
frequency parameter.
Prototype:
status_type *FFE_WaveAxis(
MPC_communication_struct_type *Haptic_ID,
META_handle_struct **meta_handle,
unsigned short axis,
float magnitude,
float frequency);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
meta_handle is a structure containing a list of handle
pointers to a pointer to a HAP_Handle_Struct structure and the
method by which the application can reference the effect.
axis is an unsinged short specifying which axis is to be
affected.
magnitude is a float specifying the maximum force used
when defining the vibration to be applied.
frequency is a a float defining the frequency of the vibration.
Output:
meta_handle is a pointer to a pointer to a META_Handle_Struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type.
Example Call:
MPC_communication_struct_type *HapticID;
META_handle_struct *WaveAxisHandle;
status_type *RetError;
float axis;
float magnitude;
float frequency;
/* vibrate the first axis */
axis = 0;
/* magnitude of the vibration */
magnitude = 1.0;
/* frequency of the vibration */
frequency = 10.0;
RetError=FFE_WaveAxis(HapticID, &WaveAxisHandle, axis, magnitude,
frequency);
if(RetError->iserror){
PrintError("Error
with FFE_WaveAxis(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function creates a random buffeting force on the device for
a specified duration. The maximum random force of the effect is
defined by the magnitude parameter, the duration of the
effect is specified by the time parameter, and the frequency
of the variation is defined by the frequency parameter.
Prototype:
status_type *FFE_TimedBuffet(
MPC_communication_struct_type *Haptic_ID,
META_handle_struct **meta_handle,
float magnitude,
float time,
float frequency);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
meta_handle is a structure containing a list of handle
pointers to a pointer to a HAP_Handle_Struct structure and the
method by which the application can reference the effect.
magnitude is a float specifying the maximum random force
of the effect.
time is a float specifying the duration of the effect.
frequency is a a float defining the frequency of the variation.
Output:
meta_handle is a pointer to a pointer to a META_Handle_Struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type.
Example Call:
MPC_communication_struct_type *HapticID;
META_handle_struct *TimedBuffetHandle;
status_type *RetError;
float magnitude;
float time;
float frequency;
/* max force in the effect */
magnitude = 1.0;
/* duration of the effect */
magnitude = 1.0;
/* frequency of the variation */
frequency = 10.0;
RetError=FFE_TimedBuffet(HapticID, &TimedBuffetHandle, magnitude,
time, frequency);
if(RetError->iserror){
PrintError("Error
with FFE_TimedBuffet(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
The Haptic Library Application Programmer Interface (also referred to as the "HAPlib API") provides extensive functionality for the integration of force-feedback into applications and the customization of haptic sensations. This document describes key concepts associated with force-feedback devices and details the API function calls.
This section documents elements of the API that are shared by
all function calls.
All functions return a status structure that contains an
error code and a description of any error that might have occurred.
The structure will be referred to as RetError in the
examples of the functions and is defined as:
typedef
struct status_type {
unsigned char description[1000];
long number;
short iserror;
} status_type;
Where iserror is set to 1 if an error has occurred,
0 if not. If an error has occurred (iserror is 1), then
number and description fields will have been filled. The description
field contains a textual description of the problem including
a description of the call stack, from deep to shallow, left to
right. Upon error, the number field will contain an integer
value corresponding to an enumeration of the API function error.
The first parameter to every function listed in this section is a single or double pointer to MPC_communication_struct_type structure. HAP_Open allocates space for and fills this structure which is then used for all subsequent calls to the same device server. This structure contains information about the communication to the device server.
Many of the haptic functions described in the following sections
parameterize physical device behaviors. Essentially, a haptic
device is a robot that is made up of a number of links
joined together at joints (joints are also called
axes). Each axis which joins two links in the haptic device
may be either translational or rotational. A translation
(or prismatic) axis is an axis whose Degree of Freedom (DOF) allows
one of its two connecting links to move in a straight line with
respect to the other (like the two parts of an extending ladder).
A rotational (or revolute) axis is one whose DOF allows
the axis to act as a hinge between its two connecting links.
To facilitate the description of haptic functions, as well as
to standardize their use, a set of generic units of measure will
be adhered to. The CyberImpact HAPLib API defaults to
using metric SI units. The units of measure which will be implied
during the remainder of the haptic library description are listed
below:
| Unit | Axis Dependent | Description |
| Second (S) | No | Unit of time |
| Meter (M) | Translation | Linear position (distance) |
| M/S | Translation | Meters per second - linear velocity |
| M/S2 | Translation | Meters per second per second -linear acceleration |
| KG | No | Kilogram - mass |
| Newton | Translation | Kilogram-meters per second -linear force magnitude |
| Radian (R) | Rotational | Angular position or distance |
| R/S | Rotational | Radians per second - angular velocity |
| R/S2 | Rotational | Radians per second per second - angular acceleration |
| NM | Rotational | Newton-Meters - angular torque |
Units of measure other than those listed are used within the scope
of the haptic library, but they are simply comprised of those
that listed in Table 3 (e.g. rotational geometric
attraction scaling factor would be given as NM/M2 (Newton-Meters
per Meters squared)).
In addition to the metric SI Units, two other modes are available, depending on the application's needs.
These two modes are Normalized and Native. In Normalized
mode, all units except for time are scaled to cover the range
from -1.0 to 1.0. This provides a more device-independent method
of supplying parameters to haptic effects. The Native mode
provides the units as they come from the device. For example,
if a rotational stage has 12 bits of information for its position,
then values 0 to 4095 will be returned.
In order to give the application complete control over the execution
of haptic effects without forcing the application programmer to
keep track of ongoing effects explicitly, it was necessary to
create an effects handle. Those HAP functions that cause effects
to occur and/or modify the behavior of effects, have as a second
argument a pointer to a pointer to the effect handle structure
(e.g. HAP_handle_struct **handle).
The existence of this input/output value gives the application
a method to refer to a previously instantiated effect for the
purposes of either terminating the effect or altering its behavior.
Supplying one of the effect functions (e.g. HAP_PutForce)
with a handle pointer that has not been allocated (i.e. is NULL)
will cause the effect function to allocate space for handle
and instantiate a new effect. Supplying an allocated handle to
this function will have the result of altering the effect that
was previously instantiated and the handle itself is left unchanged.
A given handle pointer cannot be passed around to just any effects function. The effects function HAP_Play will not operate correctly with a handle recently returned from HAP_PutPos since they are unrelated effects. A handle can usually be used in only one or two functions. Table 4 presents sets of HAPLib functions that can reference a common handle.
| Function | Can share handle with : |
| HAP_PutPos | HAP_PutKP |
| HAP_PutVel | HAP_PutKV |
| HAP_PutAcc | HAP_PutKA |
| HAP_Play | HAP_PlayMult, HAP_PlayAdd, HAP_PlayCntl |
| HAP_RemoveEffect | all effects functions |
| HAP_RestartEffect | all effects functions |
| HAP_StopEffect | all effects functions |
Supplying a handle returned from an unrelated HAP function will
cause the function to return an error. Additionally, a single
device server is only capable of instantiating a limited number
of effects of any one type. Should the application attempt to
exceed this number, the effects function will return an error.
Wording conventions used within the following API function descriptions
are described here:
Vector: The word vector
is often used when discussing the transmission and retrieval of
axis information. Since typical haptic devices have a number
of axes that we control simultaneously, it is useful to refer
to the data sent to or received from the axes as vectors. For
example, we can send a force vector of length 6 to a 6
DOF haptic device. Each element in the vector would then correspond
to one axis of the device.
Error: This
word is used to describe the difference between a desired value
and the actual value. For example, the HAP_PutPos function can
command the haptic device to pull towards or push away from, a
supplied reference position. In this case, the position error
is the difference between this reference position and the actual
physical position of the device axis.
Memory: Throughout the following
functions, the responsibility of memory allocation is assumed
to be as follows; The MPC_communication_struct_type and the HAP_handle_struct
are allocated and maintained by the Haptic library. The application
need only provide the memory for the pointer to the structures.
For functions that are using arrays to receive or send data,
it is assumed that the application has allocated enough space.
The space required is determined by the function prototypes and
by the device specific characteristics, such as number of axes
and number of buttons. If the memory is incorrectly handled by
the application, unpredictable results may occur and will not
be returned as errors by the haptic API functions.
Axes Order: Axes are reported in order (1, 2, 3, etc.).
See device specific documentation in the Device Specific Information
Section for axis order information.
The haptic server is capable of performing multiple simultaneous
tasks at a given time. Here is a list of the functions provided:
HAP_Open : Opens
a communications channel to a haptic server and sets it up.
HAP_Close : Shuts
down server and closes a communications channel to haptic server
HAP_CallOpen : Opens
a FunctionCall communication channel but does no setup.
HAP_SendConfig : Sends a config file to the haptic
device server
HAP_SetupLine :
Adds a Name/Value pair to the configuration.
HAP_StartServer : Starts
a haptic server running.
HAP_DriveServo : Commands
the haptic server to execute one servo loop
HAP_GetDeviceInfo :
Gets device specific information from a haptic server
HAP_GetForce :
Get the axes forces from the haptic server
HAP_GetPos :
Get the axes positions from the haptic server
HAP_GetVel :
Get the axes velocities from the haptic server
HAP_GetAcc :
Get the axes accelerations from the haptic server
HAP_GetToggles : Get
the status of toggle switches from the haptic server
HAP_GetValuators :
Get the status of analog valuators from the haptic server
HAP_PutForce :
Send axes Forces to the haptic server
HAP_PutPos :
Send axes Positions to the haptic server
HAP_PutKP :
Send axes Position error scale factors (spring value) to the haptic
server
HAP_PutVel :
Send axes Velocities to the haptic server
HAP_PutKV :
Send axes Velocity error scale factors (damper value) to the haptic
server
HAP_PutAcc :
Send axes Accelerations to the haptic server
HAP_PutKA :
Send axes Acceleration error scale factors to the haptic server
HAP_Vibration :
Command the haptic server to output periodic forces
HAP_SampleEdit : Create
and edit a Haptic sample
HAP_SampleRandom :
Create a random Haptic Sample
HAP_PlayCntl :
Specifies Control parameters for playing a sample
HAP_Play :
Specifies period of time over which Haptic sample is played
HAP_PlayAdd :
Specifies Additive factor for playing a sample
HAP_PlayMult :
Specifies Multiplicative factor for playing a sample
HAP_SetHome :
Sets the current physical device position as the 'home' point
of the device
HAP_PauseEffect : Command
the haptic server to pause a previously instantiated effect
HAP_PauseAllEffects :
Command the haptic server to pause ALL previously instantiated
effects
HAP_RemoveEffect :
Command the haptic server to remove a previously instantiated
effect
HAP_RemoveAllEffects :
Command the haptic server to remove ALL previously instantiated
effects
HAP_RestartEffect :
Command the haptic server to restart an effect that was previously
stopped or paused
HAP_RestartAllEffects :
Command the haptic server to restart ALL effects that were previously
stopped or paused
HAP_StopEffect :
Command the haptic server to stop a currently executing effects
HAP_StopAllEffects :
Command the haptic server to stop ALL currently executing effects
HAP_Time :
Gets the haptic server performance statistics
HAP_LoadSample : Load
a sample into HDS memory
HAP_SendSetupLine :
Send a setup name/value pair to the HDS.
HAP_SendSetupFile :
Send a whole file full of setup name/value pairs to the HDS
HAP_GetSetupData :
Get the HDS setup value for the specified name.
HAP_Home :
Set the position of the specified axis to zero in its current
position.
HAP_SetUnits :
Set the current units (metric, raw, or normalized)
HAP_SetTimeCallback :
Allows the application to provide the HDS with a function for
getting high resolution time.
Purpose:
This function opens up a communication channel to the specific
haptic server by device name. The device name is a label found
in the device definition file with the specification of what communication
method and protocol to utilize.
This function will open the communication channel to the device
server and setup the device server using the device server configuration
file(*.nmr) specified in the device definition file(HAPTIC.DEV).
However, this call will NOT start the device server. Rather,
the HAP_StartServer call should be used. This allows the
developer to dynamically modify any of the Name/Value pairs read
in from the device server configuration file before starting the
server.
Prototype:
status_type *HAP_Open(
MPC_communication_struct_type
**HapticID,
char
*deviceName,
char
*deviceFilename);
Input:
deviceName is the logical
name of the haptic server device. Devices that are supported
with device calibration data files as part of this distribution
include the following:
CyberImpactFingerForcer
= 21-DOF CyberImpact
Hand/Fingers Device
CyberImpactHandcontroller = 6-DOF CyberImpact HandController
CyberImpactJoystick3 = 3-DOF CyberImpact Joystick
CyberImpactYoke = 2-DOF CyberImpact Yoke
CyberImpactWheel = 1-DOF CyberImpact Steering
Wheel
CHForce FX
= CH Products Force FX Joystick
deviceFilename is the file
in which to find the deviceName parameters.
Output:
HapticID is a double pointer to a MPC_communication_struct_type structure.
This is used for all subsequent
calls to the same device server.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
char device[3] = "CyberImpactJoystick3";
char deviceCfgFile[] = "haptic.dev";
RetError = HAP_Open(&HapticID, device, deviceCfgFile);
if(RetError->iserror){
PrintError("Error
with HAP_Open(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an incorrect device name/label; one that is not defined in the device file. If all parameters are correct, then a communication error can occur (e.g. a time-out on the device server) incorrect communication parameters from the device file, or connecting to a server that does not control the device that was specified.
Purpose:
This function closes the previously opened connection to a device
server.
Prototype:
status_type *HAP_Close(
MPC_communication_struct_type
**HapticID );
Input:
HapticID is a double pointer
to a MPC_communication_struct_type structure.
Output:
None
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
RetError = HAP_Close(&HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_Close(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
The primary purpose of HAP_CallOpen is to provide two capabilities
to the developer if required. The first is that no external files
are required such as a device definition file or device server
configuration file. The second is that the user may dynamically
set different Name/Value pairs.
As mentioned above, when using HAP_CallOpen there is no
device definition file, rather, the 'Call' in HAP_CallOpen
indicates the method of communication that will be used to interact
with the device server. (Future releases will include a HAP_SocketOpen).
The 'Call' method is equivalent to the 'FunCall' setting in the
device file(HAPTIC.DEV), in which the client and application run
on the same computer and communicate via direct function calls.
The communication channel to the Haptic Device server is opened,
but no actual setup or starting of the Haptic device server happens
at this time. Instead, the developer uses HAP_SendConfig,
and HAP_SetupLine to create and send a setup file to the
device server.
Prototype:
status_type *HAP_CallOpen(
MPC_communication_struct_type
**HapticID );
Input:
HapticID is a double pointer
to a MPC_communication_struct_type structure.
Output:
HapticID is a double pointer to a MPC_communication_struct_type structure.
This is used for all subsequent
calls to the same device server.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
RetError = HAP_CallOpen(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_CallOpen(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function causes a setup file to be sent to the Haptic device
server. If the setup file is complete, the Haptic device server
attempts to connect to the device and begin communicating.
Prototype:
status_type *HAP_SendConfig(
MPC_communication_struct_type
*HapticID );
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
Output:
None
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
RetError = HAP_SendConfig(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_SendConfig(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function is used to add one Name/Value pair to a configuration
for a device server. Depending on what Flag is set to,
the setup line maybe sent immediately or not. If it is not sent
immediately, all setup lines maybe later sent with the HAP_SendConfig
call.
Note that the most recent call to create a setup line, either from HAP_SetupLine or HAP_ReadConfig, will overwrite the Values of previous lines with the same Names. Also,
HAP_SetupLine may be used at anytime after a Open
call and before a Close call, allowing for dynamic updates
to the configuration if required.
Prototype:
status_type *HAP_SetupLine(
MPC_communication_struct_type
*HapticID,
char
*Name,
char
*Value,
unsigned
char Flag;
Input:
HapticID is a double pointer to a MPC_communication_struct_type structure.
Name is a text string containing a legal Name.
Value is a text string containing a legal Value for the above Name.
Flag is an unsigned char
where 1 indicates send the Name/Value pair immediately, and 0
indicates to not send the Name/Value pair immediately.
Output:
None
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
char name[256];
char value[256];
unsigned char flag;
/* Setup serial comm to a 1-Axis
CyberImpact device on COM2 */
strcpy(name, "IMC1");
strcpy(value, "COM2, 57600, 1, 2, 0");
flag = 0; /* dont send setup line
immediately */
RetError = HAP_SetupLine(HapticID,
name, value, flag);
if(RetError->iserror){
PrintError("Error
with HAP_SetupLine(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function is used to start a haptic device server after a
communication channel has been opened and the device server has
been initialized. If the Servo Name/Value pair is set
to TIMER, then the device server will begin driving the
servo loop immediately. However, if CLIENT is being used,
the user must still drive each servo loop with the HAP_DriveServo
call.
It should also be noted that during this call the device server
first attempt to connect to the device.
Prototype:
status_type *HAP_StartServer(
MPC_communication_struct_type
*HapticID)
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
Output:
None
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
RetError = HAP_StartServer(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_StartServer(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function causes one iteration of the device server servo
loop to happen. In one servo loop of the device server, the following
conceptual steps happen:
Prototype:
status_type *HAP_DriveServo(
MPC_communication_struct_type
*HapticID );
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
Output:
None
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
RetError = HAP_DriveServo(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_DriveServo(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function obtains the device specific information. Information
returned is the minimum and maximum position ranges for each axis
of the device, the maximum force allowed to be output to each
axis, the number of axes, the number of toggles, and the number
of valuators.
Note that the minimum and maximum ranges are the values for if
the device is exactly homed at the physical center of the device(See
HAP_SetHome) . Values outside of this range may be reported
depending on where the device has been homed at, although the
full range(maximum - minimum) will always remain the same.
Prototype:
status_type *HAP_GetDeviceInfo(
MPC_communication_struct_type
*HapticID ,
HAP_device_info_struct
**HapticDeviceInfo);
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
Output:
HapticDeviceInfo is a double
pointer to a HAP_device_info_struct structure and contains the
following fields.
num_toggles is an unsigned short indicating the number
of toggles on the device.
num_valuators is an unsigned short indicating the number
of valuators on the device.
num_axes is an unsigned short indicating the number of
axis on the device.
axis_min_range is an array of floats containing the minimum
position units of the device.
axis_max_range is an array of floats containing the maximum
position units.
axis_max_force is an array of floats containing the maximum
force accepted by the Haptic device server. Any values larger
than this will be clipped.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
HAP_device_info_struct *HapticDeviceInfo
= NULL;
RetError = HAP_GetDeviceInfo(HapticID,
&HapticDeviceInfo);
if(RetError->iserror){
PrintError("Error
with HAP_GetDeviceInfo(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or a HAP_device_info_struct pointer is passed in that
is not set to NULL.
Purpose:
The purpose of this function is to retrieve the current axes'
forces of the physical haptic device. The axes forces will be
returned as floating point (4 byte) values, and will be given
in Newtons for translation axes and Newton-Meters
for rotational axes.
Prototype:
status_type * HAP_GetForce(
MPC_communication_struct_type
*HapticID,
float
*axes_forces );
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
axes_forces is a pointer
to an empty but pre-allocated floating point array.
Output:
axes_forces is a pointer to a floating point array into
which this function will place the current force for every axis
of the haptic device. Axes are reported in order (1, 2, 3, etc.).
See device specific documentation for axis order information.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float axes_force[3]; /* For a 3-DOF device */
RetError = HAP_GetForce(HapticID, axes_force);
if(RetError->iserror){
PrintError("Error
with HAP_GetForce(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to retrieve the current axes positions
of the physical haptic device. The axes positions will be returned
as floating point (4 byte) values, and will be given in Meters
for translation axes and Radians for rotational axes.
Prototype:
status_type * HAP_GetPos(
MPC_communication_struct_type
*HapticID,
float
*axes_positions );
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
axes_positions is a pointer
to an empty, but pre-allocated, floating point array.
Output:
axes_positions is a pointer to a floating point array
in which this function will place the current position for every
axis of the haptic device. Axes are reported in order (1, 2,
3, etc.). See device specific documentation for axis order information.
Return:
Returns a pointer to an error structure of type status_type.
This structure is described above.
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float axes_pos[3]; /* For a 3-DOF device */
RetError = HAP_GetPos(HapticID, axes_pos);
if(RetError->iserror){
PrintError("Error
with HAP_GetPos(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to retrieve the current axes'
velocities of the physical haptic device. The axes velocities
will be returned as floating point (4 byte) values, and will be
given in Meter/Second for translation axes and Radian/Second
for rotational axes.
Prototype:
status_type * HAP_GetVel(
MPC_communication_struct_type
*HapticID,
float
*axes_velocities );
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
axes_velocities is a pointer
to an empty, but pre-allocated, floating point array.
Output:
axes_velocities is a pointer to a floating point array
into which this function will place the current velocities for
every axis of the haptic device.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float axes_vel[3]; /* For a 3-DOF device */
RetError = HAP_GetVel(HapticID, axes_vel);
if(RetError->iserror){
PrintError("Error
with HAP_GetVel(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to retrieve the current axes'
accelerations of the physical haptic device. The axes accelerations
will be returned as floating point (4 byte) values, and will be
given in Meter/Second2 for translation axes and Radian/Second2
for rotational axes.
Prototype:
status_type * HAP_GetAcc(
MPC_communication_struct_type
*HapticID,
float
*axes_accelerations );
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
axes_accelerations is a pointer to an empty but pre-allocated
floating point array.
Output:
axes_accelerations is a pointer to a floating point array
into which this function will place the current accelerations
for every axis of the haptic device.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float axes_accel[3]; /* For a 3-DOF device */
RetError = HAP_GetAccel(HapticID, axes_accel);
if(RetError->iserror){
PrintError("Error
with HAP_GetAccel(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to retrieve the current haptic
device toggle states as well as some information about toggle
history. The current toggle state will be given as a bit mask
with each toggle being represented as a one (toggle on) or a zero
(toggle off). A list of values containing the number of transitions
(pressed to released or released to pressed) for each of the toggles
is also returned.
Prototype:
status_type * HAP_GetToggles(
MPC_communication_struct_type
*HapticID,
unsigned
char *toggle_mask,
unsigned
char *toggle_history );
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
toggle_mask is a pointer
to an empty, but pre-allocated, unsigned character array.
toggle_history is a pointer
to an empty, but pre-allocated, unsigned character array.
Output:
toggle_mask is a pointer to an unsigned character array into which this function will place the current status for every button of the haptic device. Buttons are reported in order (1, 2, 3, 4, etc.) and bits placed in the character array low-to-high order bit (1, 2, 4, 8, etc.), and increasing character array index. For example, if buttons 1, 2, 6, 12, and 14 were the only buttons currently depressed, then the toggle_mask would look like this:
toggle_mask[0] 00100011 toggles
1,2, and 6
toggle_mask[1] 00101000 toggles
12 and 14
See device specific documentation for toggle assignment
information.
toggle_history is a pointer to an unsigned character array
into which this function will place a value representing the number
of transitions that each toggle has experienced since the last
time toggle information was requested from the device server.
Obviously, no more than 255 transitions per toggle can be represented
in this array. A transition is defined as a toggle switch to
on or off, so a toggle switched on and off is 2 transitions.
These values are placed into the unsigned character array in sequence
(0, 1, 2, etc.).
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
unsigned char t_mask[1]; /* bits for a 4 button + 4 direction
hat
device */
unsigned char t_hist[8]; /* bytes for a history of 4 button
+ 4
direction
hat device */
RetError = HAP_GetToggles(HapticID, t_mask, t_hist);
if(RetError->iserror){
PrintError("Error
with HAP_GetToggles(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to retrieve the current haptic
device valuator states of read-only analog interfaces on the Haptic
device. The current value of the analog device is returned as
a value from 0.0 to 1.0, representing the minimum and maximum
extents of the range of the analog interface. Practical examples
include triggers, gas pedals, thrusters, etc.
Prototype:
status_type * HAP_GetValuators(
MPC_communication_struct_type
*HapticID,
float
*valuators);
Input:
HapticID is a pointer to
a MPC_communication_struct_type structure.
valuators is a pointer to
an empty but pre-allocated array of floats.
Output:
valuators is a pointer to an float array into which this
function will place the current status for every analog read-only
interface of the haptic device. Valuators are reported in order
(1, 2, 3, 4, etc.). See device specific documentation for valuator
assignment information.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float trigger[1]; /* 1 float for a trigger */
RetError = HAP_GetValuators(HapticID, trigger);
if(RetError->iserror){
PrintError("Error
with HAP_GetValuators(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to command the axes of the physical
haptic device to apply forces and or torques. The axes forces/torques
must be supplied as a vector of floating point (4 byte) values,
and are given in Newtons (N) for translation axes (forces)
and Newton-Meters (NM) for rotational axes (torques). Each
force can be applied for a specified period of time.
Prototype:
status_type * HAP_PutForce(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_force ,
float
*duration);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
axes_force is a pointer to a floating point array vector
filled with values representing the forces which will be applied
to the haptic device axes.
duration is a pointers to a floating point array vector
filled with values representing the duration of time (measured
in seconds) to apply the force to the haptic device axes.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *ForceHandle;
status_type *RetError;
float axes_force[3]; /* For a 3-DOF device */
float duration[3]; /* For a 3 DOF device */
axes_force[0] = 5.3; /* Newtons */
axes_force[1] = 2.5; /* Newtons */
axes_force[2] = -7.9; /* Newtons */
duration[0] = 2.3; /* Seconds */
duration[1] = 1;
duration[2] = 4.0;
RetError = HAP_PutForce(HapticID, &ForceHandle, axes_force,
duration);
if(RetError->iserror){
PrintError("Error
with HAP_PutForce(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to parameterize position control.
This function operates in conjunction with the HAP_PutPos
function by allowing the application to specify attributes of
the position control. The forces to be experienced by the user
are defined by the following function:
Force/Torquex
=
scaling_factorx
* |desired_positionx - actual_positionx| exponentx
*
sign(desired_positionx
- actual_positionx)
"x" is the axis number and is used to denote the index
into each of the vectors in the above formula. Each force/torque
element will vary as its corresponding physical axis (actual_position)
is moved about, thereby changing the position error term. The
desired_position is the one control parameter vector set
in the HAP_PutPos function.
The two control parameter vectors scaling_factor and exponent
are set in this function. Positive scaling_factors result
in repulsive forces, while negative scaling_factors result
in attractive forces. The position error is raised to the value
contained in exponent. An exponent of 1 suggests
a simple spring. Negative exponent values would result
in gravitational type behaviors.
Prototype:
status_type * HAP_PutKP(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*scaling_factors,
float
*exponents );
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
scaling_factors is a pointer to a floating point array
filled with values representing the strength magnitudes (one for
each axis) with which the haptic device will apply the positioning
forces.
exponent is a pointer to a floating point array filled
with the values to be used as position error exponents.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *PosHandle;
status_type *RetError;
float scaling[3]; /* For a 3-DOF device */
float expon[3]; /* For a 3-DOF device */
scaling[0] = 4.6;
scaling[1] = 5.1;
scaling[2] = 1.8;
expon[0] = 1.0;
expon[1] = 1.0;
expon[2] = 1.0;
RetError = HAP_PutKP(HapticID, &PosHandle, scaling, expon);
if(RetError->iserror){
PrintError("Error
with HAP_PutKP(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to command the axes of the physical
haptic device to move toward or away from a given position (i.e.
position control). The axes positions must be supplied as floating
point (4 byte) values, and are given in Meters for translation
axes and Radians for rotational axes. This function supplies
the axes positions to the haptic device server as well as commanding
it to execute the effect. However, position control parameterization
is distributed over two functions: this function, HAP_PutPos,
and HAP_PutKP. Refer to the documentation to determine
how HAP_PutKP is used (with the same handle) to set the
attraction/ repulsion parameters.
Prototype:
status_type * HAP_PutPos(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_pos,
float
*duration);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
axes_pos is a pointer to a floating point array filled
with values representing the positions to which the haptic device
axes will be drawn or repelled (See HAP_PutKP).
duration is a pointer to a floating point array vector
filled with values representing the duration of time (measured
in seconds) to apply the position control to the haptic device
axes.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure. Supplying a handle that has not been allocated (returned
from this or another HAP function) will cause this function to
allocate space for handle and fill it with the appropriate
information. If handle has already been allocated and filled
in by a previous call to this HAP function, then it is left unchanged.
Supplying a handle returned from an unrelated HAP function will
cause the function to return an error.
Return:
Returns a pointer to an error structure of type status_type.
This structure is described above.
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *PositionHandle;
status_type *RetError;
float scaling[3]; /* For a 3-DOF device */
float duration[3];
float expon[3];
float axes_pos[3];
scaling[0] = 4.6;
scaling[1] = 5.1;
scaling[2] = 1.8;
expon[0] = 1.0;
expon[1] = 1.0;
expon[2] = 1.0;
RetError = HAP_PutKP(HapticID, &PosHandle, scaling, expon);
if(RetError->iserror){
PrintError("Error
with HAP_PutKP(): Err=%d, %s",
RetError->number , RetError->description);
}
axes_pos[0] = 1.0;
axes_pos[1] = -2.9;
axes_pos[2] = 0.9;
duration[0] = 2.3;
duration[1] = 2.4;
duration[2] = 1.0;
RetError = HAP_PutPos(HapticID, &PosHandle, axes_pos, duration);
if(RetError->iserror){
PrintError("Error
with HAP_PutPos(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to parameterize velocity control.
This function operates in conjunction with HAP_PutVel by
allowing the application to specify attributes of the velocity
control. The forces to be experienced by the user are defined
by the following function:
Force/Torquex =
scaling_factorx * |desired_velocityx - actual_velocityx|
exponentx *
sign(desired_velocityx - actual_velocityx)
"x" is the axis number and is used to denote the index
into each of the vectors in the above formula. Each force/torque
element will vary as its corresponding physical axis (actual_velocity)
is moved about, thereby changing the velocity error term.
The desired_velocity is the one control parameter vector
set in HAP_PutVel. The two control parameter vectors scaling_factor
and exponent are set here in this function. Positive scaling_factors
result in velocity magnification, while negative scaling_factors
result in damping forces. The velocity error is raised to the
value contained in exponent. An exponent of 1 suggests
a simple damper.
Prototype:
status_type * HAP_PutKV(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*scaling_factors,
float
*exponents );
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
scaling_factors is a pointer to a floating point array
filled with values representing the strength magnitudes (one for
each axis) with which the haptic device will apply the velocity
forces.
exponent is a pointer to a floating point array vector
filled with the values to be used as velocity error exponents.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *VelHandle;
status_type *RetError;
float scaling[3]; /* For a 3-DOF device */
float expon[3]; /* For a 3-DOF device */
scaling[0] = -1.9;
scaling[1] = -1.1;
scaling[2] = -3.8;
expon[0] = 1.0;
expon[1] = 1.0;
expon[2] = 1.0;
RetError = HAP_PutKV(HapticID, &VelHandle, scaling, expon);
if(RetError->iserror){
PrintError("Error
with HAP_PutKV(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
The purpose of this function is to command the axes of the physical
haptic device to bring itself toward or away from a given velocity
(i.e. velocity control). The axes velocities must be supplied
as floating point (4 byte) values, and are given in Meter/Second
for translation axes and Radians for rotational axes.
This function supplies the axis velocities to the haptic device
server, as well as commanding it to execute the effect. However,
velocity control parameterization is distributed over two functions:
this one and HAP_PutKV. Refer to the documentation to
see how HAP_PutKV is used (with the same handle) to set
the velocity control parameters.
Prototype:
status_type * HAP_PutVel(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_vel,
float
*duration);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
axes_vel is a pointer to a floating point array filled
with values representing the velocities to which the haptic device
axes will be drawn or repelled (See HAP_PutKV). Velocities
are taken from this list and applied to the device axes in order
(1, 2, 3, etc.), thereby instantiating the velocity magnification
or damping effect.
duration is a pointer to a floating point array vector
filled with values representing the duration of time (measured
in seconds) to apply the velocity control to the haptic device
axes.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *VelHandle;
status_type *RetError;
float scaling[3]; /* For a 3-DOF device */
float duration[3]
float expon[3];
float axes_vel[3];
scaling[0] = -1.9;
scaling[1] = -1.1;
scaling[2] = -3.8;
expon[0] = 1.0;
expon[1] = 1.0;
expon[2] = 1.0;
RetError = HAP_PutKV(HapticID, &VelHandle, scaling, expon);
if(RetError->iserror){
PrintError("Error
with HAP_PutKV(): Err=%d, %s",
RetError->number , RetError->description);
}
axes_vel[0] = 0.0;
axes_vel[1] = 0.0;
axes_vel[2] = 0.0;
duration[0] = 1.0;
duration[1] = 2.0;
duration[2] = 3.0;
RetError = HAP_PutVel(HapticID, &VelHandle, axes_vel, duration);
if(RetError->iserror){
PrintError("Error with HAP_PutVel(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
The purpose of this function is to parameterize acceleration control.
This function operates in conjunction with HAP_PutAcc by
allowing the application to specify attributes of the acceleration
control. The forces to be experienced by the user are defined
by the following function:
Force/Torquex
=
scaling_factorx
* sign(desired_accelerationx - actual_accelerationx) *
|desired_accelerationx
- actual_accelerationx| exponentx
"x" is the axis number and is used to denote the index
into each of the vectors in the above formula. Each force/torque
element will vary as its corresponding physical axis (actual_acceleration)
is moved about, thereby changing the acceleration error term.
The desired_acceleration is the one control parameter
vector set in HAP_PutAcc. The two control parameter vectors
scaling_factor and exponent are set here in this
function. Positive scaling_factors result in a lighter
inertia-less feel, while negative scaling_factors result
in increased inertia. The acceleration error is raised to the
value contained in exponent.
Prototype:
status_type * HAP_PutKA(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*scaling_factors,
float
*exponents );
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
scaling_factors is a pointer to a floating point array
filled with values representing the strength magnitudes (one for
each axis) with which the haptic device will apply the acceleration
forces.
exponent is a pointer to a floating point array vector
filled with the values to be used as acceleration error exponents.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type.
This structure is described above.
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *AccHandle;
status_type *RetError;
float scaling[3]; /* For a 3-DOF device */
float expon[3];
scaling[0] = -2.0;
scaling[1] = -1.0;
scaling[2] = -1.5;
expon[0] = 1.0;
expon[1] = 1.0;
expon[2] = 1.0;
RetError = HAP_PutAcc(HapticID, &AccHandle, scaling, expon);
if(RetError->iserror){
PrintError("Error
with HAP_PutAcc(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID)..
Purpose:
The purpose of this function is to command the axes of the physical
haptic device to bring itself toward or away from a given acceleration
(i.e. acceleration control). The axes accelerations must be supplied
as floating point (4 byte) values, and are given in Meter/Second2
for translation axes and Radians for rotational axes.
This function supplies the axis positions to the haptic device
server as well as commanding it to execute the effect. However,
position control parameterization is distributed over two functions:
this one and HAP_PutKA. Read the documentation to learn
how HAP_PutKA is used (with the same handle) to set the
acceleration control parameters.
Prototype:
status_type * HAP_PutAcc(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_acc
float
*duration);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
axes_acc is a pointer to a floating point array filled
with values representing the accelerations to which the haptic
device axes will be drawn or repelled (See HAP_PutKA).
Accelerations are taken from this list and applied to the device
axes in order (1, 2, 3, etc.) thereby instantiating the acceleration
magnification or damping effect.
duration is a pointer to a floating point array vector
filled with values representing the duration of time (measured
in seconds) to apply the acceleration control to the haptic device
axes.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *AccHandle;
status_type *RetError;
float scaling[3]; /* For a 3-DOF device */
float duration[3];
float expon[3];
float axes_accel[3];
scaling[0] = -1.9;
scaling[1] = -1.1;
scaling[2] = -3.8;
expon[0] = 1.0;
expon[1] = 1.0;
expon[2] = 1.0;
RetError = HAP_PutKA(HapticID, &AccHandle, scaling, expon);
if(RetError->iserror){
PrintError("Error
with HAP_PutKA(): Err=%d, %s",
RetError->number , RetError->description);
}
axes_accel[0] = 0.0;
axes_accel[1] = 0.0;
axes_accel[2] = 0.0;
duration[0] = 0.5;
duration[1] = 1.2;
duration[2] = 1.3;
RetError = HAP_PutAcc(HapticID, &AccHandle, axes_accel, duration);
if(RetError->iserror){
PrintError("Error with HAP_PutAcc(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
The purpose of this function is to command the axes of the physical
haptic device to apply forces and or torques in an oscillating,
or vibratory, pattern. The parameters supplied are primarily
unitless, since they define the characteristics of the oscillatory
effect. The only exceptions are the values in the magnitude
vector, which are specified in Newtons, and the time parameter,
which is seconds. Figure 5 presents a graphical
example of the output generated by the HAP_Vibration call
for the indicated set of input parameters.

Prototype:
status_type * HAP_Vibration(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
time,
float
*magnitude,
float
duration,
float
fade,
float
frequency);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
time is a floating point value indicating the number of
seconds that the haptic device server will run the vibration effect.
If time is set to -1.0, then the effect runs until stopped or
modified. Num_cycles, cycle_length, frequency, and time are related
by the following equation:
num_cycles
= time/frequency
cycle_length
= 1/frequency
magnitude is vector which contains the starting magnitude
for the vibration on each axis of the device. If a value is set
to 0, no vibration will occur on the corresponding axis. Note
that direction is also indicated, according to the sign of the
magnitude.
duration is a floating point value ranging from 0.0 to
1.0 and is the portion of 1 cycle_length for which the force is
sustained.
frequency is a floating point value that is the rate of
the effect. The range of values is from 0 Hz to the maximum servo
rate of the haptic device server, which varies based on a number
of computing platform parameters.
fade is the multiplicative constant that the magnitude
is multiplied by in each cycle. The multiplication is cumulative
in effect. It is a floating point value, and depending on the
value various force output behavior is exhibited:
fade
is negative(<0.0) direction of
force alternates every other cycle.
fade
is positive(>0.0) direction of
force is in direction of magnitude.
fade
> 1.0 or < -1.0 magnitude of
force increases every cycle, up to the
MAX_FORCE
specified by the haptic device server.
fade
< 1.0 or > -1.0 magnitude
of output force decreases every cycle,
down
to the MIN_FORCE specified by the haptic
device
server.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *VibHandle;
status_type *RetError;
float time = 10.0; /* Secs */
float magnitude[3]; /* 3DOF device */ /*Newtons*/
float duration = 0.5; /* Apply force for half of 1 cycle
*/
float fade = -0.9; /* Alternate, decreasing */
float frequency = 1.0; /* Hz */
magnitude[0] = 1.0; /* starting magnitude of 1 Newton */
magnitude[1] = 0.0; /* no vibration on the second axis */
magnitude[2] = 2.0;
RetError = HAP_Vibration(HapticID,
&VibHandle,
time,
magnitude,
duration,
fade,
frequency);
if(RetError->iserror){
PrintError("Error
with HAP_Vibration(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to create and edit user-defined haptic samples.
A haptic sample is specified as an array of floating point values
that the haptic device server "plays" over a specified
period of time. Using the correct "play" function,
these values can be rendered as forces, positions, velocities,
or accelerations . Consider the following sample:
This sample is very short (only 5 values long), but it may be
played over an arbitrary period of time using the HAP_Play
function. See the Appendix C for limits on total sample
length possible in memory at once.
Four functions are available to define precisely how the sampled data is played back:
If a randomized sample is desired, use HAP_SampleRandom
instead of HAP_SampleEdit to create the effect. Finally,
use HAP_RestartEffect to begin playing the sample.
Prototype:
status_type * HAP_SampleEdit(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**sample_handle,
unsigned
short sample_length,
unsigned
short edit_start,
unsigned
short edit_length,
float
*sub_sample);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
sample_handle is a pointer to a pointer to a structure
for the sample being edited. Previously existing samples may
be edited, or, if the sample_handle is null, a new sample is created,
and sample_handle is set.
sample_length is an unsigned short specifying the number
of sample elements in the sample. If edit_start and edit_length
access sample elements beyond this length, those sample element
values are ignored.
edit_start is an unsigned short which is an index into
the sample. Using this parameter along with edit_length and the
sample itself, this function is capable of editing arbitrary subsections
of the overall sample. This index indicates the beginning of
the sub-sample that you wish to edit.
edit_length is an unsigned short that indicates the length
of the sub-sample that you wish to edit.
sub_sample is an array of floating point values representing
a force profile to be played over a period of time specified in
the HAP_Play function.
Output:
None
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
unsigned short edit_start, edit_length;
HAP_handle_struct *sample_handle;
unsigned short sample_length;
float sub_sample[5];
edit_start = 0;
edit_length = 5;
sample_length = 5;
sub_sample[0] = 1.0;
sub_sample[1] = 0.9;
sub_sample[2] = 0.0;
sub_sample[3] = 0.5;
sub_sample[4] = 0.9;
RetError = HAP_SampleEdit(HapticID, &sample_handle, sample_length,
edit_start, edit_length, sub_sample);
if(RetError->iserror){
PrintError("Error
with HAP_SampleEdit(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID). Also, attempting to create too large a sample, or
trying to edit parts of the sample that do not exist will cause
an error. Limits on maximum sample number and maximum sample size
are given in Appendix C of this document.
Purpose:
This function is used to create a randomized haptic sample. Using the correct "play" function, these values can be rendered as forces, positions, velocities, or accelerations. A call to HAP_SampleRandom specifies the parameters that describe the nature of the random sample. The 3 parameters are; min_magnitude, max_magnitude, and frequency.
Four functions are available to define precisely how the random sampled data is played back:
If a (non-random) user-specified sample is desired, use HAP_SampleEdit
instead of HAP_SampleRandom to create the effect. Finally,
use HAP_RestartEffect to begin playing the sample.
Prototype:
status_type * HAP_SampleRandom(
MPC_communication_struct_type
*HapticID,
unsigned
short sample_num,
float
min_magnitude,
float
max_magnitude,
float
frequency);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
sample_num is an unsigned short specifying a unique number
for the sample being edited. Previously existing samples may
be edited, or, if the sample_num has never been used before, a
new sample is created.
min_magnitude is a floating point value which represents
the minimum value which may be possible generated in the random
sample.
max_magnitude is a floating point value which represents
the maximum value which may be possible generated in the random
sample.
frequency is a floating point value which represents the
rate at which the random values are generated for the sample.
Output:
None
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
unsigned short sample_num;
float min_magnitude;
float max_magnitude;
float frequency;
sample_num = 1;
min_magnitude = -0.5;
max_magnitude = 1.5;
frequency = 10.0; /* Hz */
RetError = HAP_SampleRandom(HapticID, sample_num, min_magnitude,
max_magnitude, frequency);
if(RetError->iserror){
PrintError("Error
with HAP_SampleRandom(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID). Also, attempting to create too large a sample, or
trying to edit parts of the sample that do not exist will cause
an error. Limits on maximum sample number and maximum sample size
are given in Appendix C of this document.
Purpose:
This function is used to specify information about how the haptic
device server is to "play" haptic samples. The sample
format is described in the HAP_SampleEdit function, which
is where the samples themselves are created and/or altered. This
function allows the developer to specify play parameters. Samples
can be played as force control, position control, velocity control,
or acceleration control. This function facilitates the type of
control desired.
Three parameter vectors are used to convey the control information.
The first of these is a vector that specifies the type
of control, i.e. force, position, velocity, or acceleration.
The second and third vectors specify the error scale_factor
and error exponent that determine the magnitude and direction
of the forces. These two vectors have a meaning that depends
on the first vector. If the specification vector gives "position
control" as the type of control then scale_factor
is a position error scale factor and exponent is a position
error exponent. However, the scale_factor and exponent
have no meaning when the specification vector gives "force
control" as the type of control since force control requires
no scaling or exponent factors. The scale_vector and exponent
values are used identically as described in the HAP_Put
function and it should be referenced for a complete descriptions
of these values. Note also that in the Hap_Put function
is the desired_value parameter, which is equivalent to the individual
sample elements of the sample as they are played over time.
Three additional vectors are used to specify how the samples are
played back over time for each axis: ramp_start, ramp_end
and continuity. Ramp_start and ramp_end
combined specify a linear scalar that increases or decreases over
time. This scalar is multiplied with the sample elements to
cause the sample to increase or decrease in magnitude over time.

Continuity is used to specify how the haptic device server
will interpolate force values during the playback of a sample.
Interpolation is required when the device server servo-rate is
of higher resolution than the frequency of playback determined
by the duration and sample_length in the HAP_Play
function. The term continuity comes from graphics, and represents
what order of polynomial is used to interpolate between sample
elements. The order of the polynomial, in turn, determines how
'smooth' the transitions are at the sample elements. Continuity
of 0 is the simplest form of interpolation, in which the previous
sample element's value is sustained until the next sample element
is to be played. The next level, 1, provides a linear interpolation
of values between 2 sample elements. Continuity levels 2 and
3 are currently unavailable, but would provide even 'smoother'
transitions, at a cost in computational load to the haptic device
server. See Figure 6 for an illustration
of the various levels of continuity.
Prototype:
status_type * HAP_PlayCntl(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
unsigned
char *specification,
float
*scale_factors,
float
*exponent,
float
*ramp_start,
float
*ramp_end,
unsigned
char *continuity);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
specification is a pointer to an array of unsigned characters.
This vector specifies the type of control through which the sample
will be interpreted. The lower case letter "p" indicates
position control, "v" indicates velocity control, "a"
indicates acceleration control, and "f" indicates direct
force control. Other values will cause error.
scaling_factors is a pointer to a floating point array
filled with values representing the strength magnitudes (one for
each axis) with which the haptic device will apply position, velocity,
or acceleration forces. It is meaningless for force control.
exponent is a pointer to a floating point array vector
filled with the values to be used as position, velocity, or acceleration
error exponents. It is meaningless for force control.
ramp_start is a pointer to a floating point array vector.
The values represent the initial start values for the linear,
time-varying scalar.
ramp_end is a pointer to a floating point array vector.
The values represent the final desired values for the linear,
time-varying scalar.
continuity is a pointer to an unsigned char vector. The
values specify the degree of continuities to be used for interpolation
for each axes.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *PlayHandle;
status_type *RetError;
float scale_factors[3]; /* For a 3-DOF device */
float exponents[3];
unsigned char spec[3];
float ramp_start[3];
float ramp_end[3]
unsigned char continuity[3];
spec[0] = 'p';
spec[1] = 'p';
spec[2] = 'p';
scale_factors[0] = -4.1;
scale_factors[1] = 0.9;
scale_factors[2] = 0.0;
exponents[0] = 2.0;
exponents[1] = 2.0;
exponents[2] = 2.0;
ramp_start[0] = 0.2;
ramp_end[0] = 1.1; /* Increase scale from 0.2 to 1.1 */
ramp_start[1] = -1.0;
ramp_end[1] = 0.0; /* Decrease scale from 1 to 0.0,
opposite magnitudes */
ramp_start[2] = 1.0;
ramp_end[2] = 1.0; /* No change */
continuity[0] = 1; /* 1st order continuity for all
axes */
continuity[1] = 1;
continuity[2] = 1;
RetError = HAP_PlayCntl(HapticID, &PlayHandle, spec, scale_factors,
exponents, ramp_start, ramp_end, continuity);
if(RetError->iserror){
PrintError("Error
with HAP_PlayAdd(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). The valid number of sample effects that can be implemented at one time are given in Appendix C of this document.
Purpose:
This function is used to specify the duration over which the haptic
device server will "play" a haptic sample. Sample clips
are specified as arrays of floating point values that the haptic
device server plays over specified periods of time. The format
of a sample is described in the HAP_SampleEdit function,
which is where the samples themselves are created and/or altered.
The time parameter allows the user to specify the length
of time over which the sample will be played (set to -1 if continuous
play is desired). Consider the haptic sample from the HAP_SampleEdit
section:
| Sample Index | |||||
| Magnitude | |||||
Although this sample is very short (only 5 values long), it may
be played over an arbitrary period of time. Figure 7
illustrates the sample being played with time = 2 seconds
and the duration = 1 sec. This example is for one sample
being played on one axis, but in general n samples are
capable of being played back simultaneously (n being the
number of device axes). Therefore, each axes has its own individual
set of playback parameters that may be specified.

As noted below by the equations, specifying duration indirectly specifies the frequency, or rate, at which the samples are to be played. If the frequency is higher than the maximum servo rate of the Haptic device server, then the sample will be linearly subsampled so as to still play the whole sample.
Num_loops, sample_length, frequency, duration, and time are related
by the following equations:
num_loops
= time/duration
frequency
= sample_length/duration
Prototype:
status_type * HAP_Play(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
HAP_handle_struct
**sample_handle,
float
*time,
float
*duration);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
sample_handle is the array of handle pointers which are
the identification for the samples to be played for each axis.
These handles are unique and are assigned by the HAP_SampleEdit
function.
time is a vector of floating point values that represent
the period of time (in seconds) over which the samples will be
played. If time is set to -1, then the effect runs until stopped
or modified. (HAP_StopEffect will stop it, of course, as
will calling HAP_Play again with the same handle).
duration is a vector of floating point values that are
the duration of time for 1 complete sample to be played.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *PlayHandle;
status_type *RetError;
/* values for HAP_SampleEdit call*/
unsigned short edit_start, edit_length;
float sub_sample[MAX_SAMPLE_LEN];
HAP_handle_struct *sample_handle[3];
/* values for HAP_Play call*/
unsigned short sample[3]; /* 3 samples for 3 axes */
float time[3];
float duration[3];
edit_start = 0;
edit_length = 5;
sample_num = 1; /* Assign this sample as #1 */
sub_sample[0] = 1.3;
sub_sample[1] = 9.2;
sub_sample[2] = 0.0;
sub_sample[3] = 4.0;
sub_sample[4] = 7.0;
RetError = HAP_SampleEdit(HapticID, &sample_handle[0], edit_start,
edit_length, dir_vector, sub_sample);
if(RetError->iserror){
PrintError("Error
with HAP_SampleEdit(): Err=%d, %s",
RetError->number , RetError->description);
}
time[0] = 16.0; /* secs */
time[1] = 16.0;
time[2] = 16.0;
duration[0] = 5; /* 5 samples/5 Hz = 1 Hz */
duration[1] = 5;
duration[2] = 5;
sample_handle[1] = sample_handle[2] = sample_handle[0];
RetError = *HAP_Play(HapticID, &PlayHandle,
sample_handle,
time, duration);
if(RetError->iserror){
PrintError("Error
with HAP_Play(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to specify a multiplicative factor
used by the haptic device server while "playing" haptic
samples. This function, along with the HAP_PlayAdd function,
allow the user to specify a multiplicative and additive vector
which will be applied to the samples as they are played. This
will have the effect of changing the nature of the sample without
editing the sample directly.
The multiplicative and additive vectors are of length n,
where n is the number of device axes. As seen in the HAP_Play
section, the haptic device server will play a separate sample
for each of the device's axes (for maximum flexibility). These
samples may all be the same or all different, depending on need.
The sample vector is modified by the multiplicative and additive
vectors as follows:
Mod_Samplex(y)
= mult_vectx * Samplex(i) + add_vectx
"x" is the axis number. "i"
is an index into the sample. Sample is the original sample vector
(one for each axis). The mult_vect is the multiplication
vector (specified in this function) and add_vect is the
additive vector (specified in the HAP_PlayAdd function).
Finally, Mod_Sample is the modified sample, that will
be played by the haptic device server.
Prototype:
status_type * HAP_PlayMult(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*mult_vect);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
mult_vect is the multiplicative vector that will be applied
to the sample vector. This is a floating point vector.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *PlayHandle;
status_type *RetError;
float mult_vect[3]; /* For a 3-DOF device */
mult_vect[0] = -2.0
mult_vect[1] = 1.0;
mult_vect[2] = 8.6;
RetError = HAP_PlayMult(HapticID, &PlayHandle, mult_vect);
if(RetError->iserror)
PrintError("Error
with HAP_PlayMult(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to specify a additive factor used
by the haptic device server while "playing" haptic samples.
This function, along with the HAP_PlayMult function, allow
the user to specify a additive and multiplicative vector which
will be applied to the samples as they are played. This will
have the effect of changing the nature of the sample without editing
the sample directly.
The additive and multiplicative vectors are of length n,
where n is the number of device axes. As seen in the HAP_Play
section, the haptic device server will play a separate sample
for each of the device's axes (for maximum flexibility). These
samples may all be the same or all different, depending on need.
The sample vector is modified by the additive and multiplicative
vectors as follows:
Mod_Samplex(y)
= mult_vectx * Samplex(i) + add_vectx
"x" is the axis number. "i"
is an index into the sample. Sample is the original sample vector
(one for each axis). The add_vect is the additive vector
(specified in this function) and mult_vect is the multiplicative
vector (specified in the HAP_PlayMult function). Finally,
Mod_Sample is the modified sample, that will be played
by the haptic device server.
Prototype:
status_type * HAP_PlayAdd(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*add_vect);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
add_vect is the additive vector that will be applied to
the sample vector. This is a floating point vector.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type.
This structure is described above.
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *PlayHandle;
status_type *RetError;
float add_vect[3]; /* For a 3-DOF device */
add_vect[0] = 2.1;
add_vect[1] = 0.0;
add_vect[2] = -5.3;
RetError = HAP_PlayAdd(HapticID, &PlayHandle, add_vect);
if(RetError->iserror){
PrintError("Error
with HAP_PlayAdd(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID). Exceeding the valid number of effects of this type will also cause the function to return an error. The valid number of sample effects that can be implemented at one time are given in Appendix C of this document.
Purpose:
This function is used to command the device server to set the
current position of the device as the 'Home' position. Position
coordinates returned from the HAP_Position call will now
return 0.0 for all axes for the 'Home' position.
Note that a device server only returns relative position values
and that, depending on the 'Home' position, the range of the values
may shift. For example, a device with a range of motion of 90
degrees on one axis will report values from -45 to 45 degrees
when precisely centered at the physical center. However, if the
device is at the full minimum extent when homed, then the values
returned will be from 0 to 90 degrees.
Prototype:
status_type * HAP_SetHome(
MPC_communication_struct_type
*HapticID,
unsigned
char *axes);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
axes is an pointer to an array of unsigned char representing
the axes that the Haptic DS should output the force to during
each cycle. The low bit indicates axis 1 up to the high bit representing
axis 8. For example;
axes[0] 00100011 axis
1,2, and 6
Output:
None.
Return:
Returns a pointer to an error structure of type status_type.
This structure is described above.
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
unsigned char axes;
axes = 0x07; /* Axes 1,2,3 */
RetError = HAP_SetHome(HapticID, &axes);
if(RetError->iserror){
PrintError("Error
with HAP_SetHome(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to remove the instantiation of an effect
immediately from the Haptic DS. All memory used by the effect
is freed and the pointer's value is set to NULL. Additionally,
the effect becomes available for use again.
Prototype:
status_type * HAP_RemoveEffect(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *Handle;
status_type *RetError;
RetError = HAP_RemoveEffect(HapticID, &Handle);
if(RetError->iserror){
PrintError("Error
with HAP_RemoveEffect(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to remove the instantiation of all effects
immediately from the Haptic DS. All memory used by the effects
are freed and all current effects handles will be invalid pointers
and should be set to NULL before reuse.
Prototype:
status_type * HAP_RemoveAllEffects(
MPC_communication_struct_type
*HapticID);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
RetError = HAP_RemoveAllEffects(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_RemoveAllEffects(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to restart the execution of a effect. The
parameters used are the ones most recently assigned to the effect.
For time-based effects, the effect is reset to time zero if the
effect was Stopped. If the effect was Paused, the
effect continues running. Note that the time while paused is added
to the total time ran of the effect.
Prototype:
status_type * HAP_RestartEffect(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *Handle;
status_type *RetError;
RetError = HAP_RestartEffect(HapticID, &Handle);
if(RetError->iserror){
PrintError("Error
with HAP_RestartEffect(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to restart the execution of all effects.
The parameters used are the ones most recently assigned to the
effects. For time-based effects, the effects are reset to time
zero if the effects were Stopped. If the effects were Paused,
the effects continues running. Note that the time while paused
is added to the total time ran of the effects.
Prototype:
status_type * HAP_RestartAllEffects(
MPC_communication_struct_type
*HapticID);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
Output:
none.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
RetError = HAP_RestartAllEffects(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_RestartAllEffects(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function is used to halt the execution of an effect. The
effect must be restarted through the use of HAP_RestartEffect
or HAP_RestartAllEffects. For time-based effects, the
time will be reset to zero.
Prototype:
status_type * HAP_StopEffect(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *Handle;
status_type *RetError;
RetError = HAP_StopEffect(HapticID, &Handle);
if(RetError->iserror){
PrintError("Error
with HAP_StopEffect(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type (HapticID).
Purpose:
This function is used to halt the execution of all effects. The
effects must be restarted through the use of HAP_RestartEffect
or HAP_RestartAllEffects. For time-based effects, the
time will be reset to zero.
Prototype:
status_type * HAP_StopAllEffects(
MPC_communication_struct_type
*HapticID);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
Output:
none
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
RetError = HAP_StopAllEffects(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_StopAllEffects(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function is used to pause the execution of an effect. The
effect must be restarted through the use of HAP_RestartEffect
or HAP_RestartAllEffects. For time-based effects,
time is counted for the paused duration.
Prototype:
status_type * HAP_PauseEffect(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Output:
handle is a pointer to a pointer to a HAP_handle_struct
structure and the method by which the application can reference
the effect.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *Handle;
status_type *RetError;
RetError = HAP_PauseEffect(HapticID, &Handle);
if(RetError->iserror){
PrintError("Error
with HAP_PauseEffect(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function is used to halt or pause the execution of all effects.
The effects must be restarted through the use of HAP_RestartEffect
or HAP_RestartAllEffects. For time-based effects,
time is counted for the paused duration.
Prototype:
status_type * HAP_PauseAllEffects(
MPC_communication_struct_type
*HapticID);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
HAP_handle_struct *Handle;
status_type *RetError;
RetError = HAP_PauseAllEffects(HapticID);
if(RetError->iserror){
PrintError("Error
with HAP_PauseAllEffects(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function is used to obtain the device server time performance
characteristics. It is intended as diagnostics aid gauge performance
of the Haptic system.
Prototype:
status_type * HAP_Time(
MPC_communication_struct_type
*HapticID,
HAP_time_info_struct
**HapticTimeInfo);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
Output:
HapticTimeInfo is a double pointer to a HAP_time_info_struct
structure and contains the following fields.
servo_rate is a float giving the average servo rate since
the server was started
servo_average_period is a float giving the average execution
time of 1 servo loop since the device server was started and is
equal to 1/servo_rate.
servo_smallest_period is a float measuring the smallest
execution time of 1 servo loop since the serer was started.
servo_largest_period is a float measuring the largest execution
time of 1 servo loop since the serer was started.
servo_rate_recent is a float giving the average servo rate
over the last second.
servo_average_period_recent is a float giving the average
execution time of 1 servo loop over the last second and is equal
to 1/servo_rate_recent.
times_hit is a float indicating the number of times the
servo loop has been executed since the device server was started.
profile is the distribution of the servo periods from 0
to 50 ms in 10% increments. (ie 0-4.99 ms is profile[0], 5.0-9.99
ms is profile[1], etc.)
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
HAP_device_info_struct *HapticTimeInfo
= NULL;
RetError = HAP_Time(HapticID, &HapticTimeInfo);
if(RetError->iserror){
PrintError("Error
with HAP_Time(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or a HAP_device_info_struct pointer is passed in that
is not set to NULL.
Purpose:
This function is used to load a 1 or 2 dimensional array of bytes
to the HDS. The
Prototype:
status_type *HAP_LoadSample(
MPC_communication_struct_type
*Haptic_ID,
char
*name, char *data,
unsigned
short rows,
unsigned
short columns);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
name is the logical name of the sample.
data is a pointer to a character array that must contain
the sample data.
rows is the number of rows of sample data
columns is the number of columns of sample data, so the
overall length of the sample in bytes is the product of rows
and columns. To send a single dimensional sample, simply
set rows or columns to zero.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
char sample_array[10];
unsigned short length;
/* the next control plays a continuous
sample over time */
/* first create a sample and send it to the DS */
sample_array[0] = 0;
sample_array[1] = 33;
sample_array[2] = 55;
sample_array[3] = 24;
sample_array[4] = -10;
sample_array[5] = -70;
sample_array[6] = -29;
sample_array[7] = 3;
sample_array[8] = 126;
sample_array[9] = -4;
/* now send it to the DS */
length = 10;
RetError = HAP_LoadSample(HID,
"sample1", sample_array, length,
(unsigned
short) 1);
if(RetError->iserror){
PrintError("Error
with HAP_LoadSample(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or passing in a sample pointer that has insufficient
space allocated for the rows and columns specified.
Purpose:
This function is used to send one setup line (a name value pair)
to the HDS. This has the effect of changing setup values in
the HDS.
Prototype:
status_type *HAP_SendSetupLine(
MPC_communication_struct_type
*Haptic_ID,
unsigned
char *name,
unsigned
char *value);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
name is a null-terminated string specifying the HDS variable
name.
value is a null-terminated string containing value(s)
to which the HDS variables will be set.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
/* set the smoothing coefficients
for axis 1 (2nd axis) to 0.3 and
0.1 (velocity and acceleration smoothing)
*/
RetError = HAP_SendSetupLine(HID,
"Coeff1", "0.3 0.1");
if(RetError->iserror){
PrintError("Error
with HAP_SendSetupLine(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or passing in an invalid name value pair into this
function.
Purpose:
This function is used to send one setup line (a name value pair)
to the HDS. This has the effect of changing setup values in
the HDS.
Prototype:
status_type *HAP_SendSetupFile(
MPC_communication_struct_type
*Haptic_ID,
unsigned
char *config_file);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
config_file is a null-terminated string specifying the
path and name of the setup file that must reside on the application
host machine.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
RetError = HAP_SendSetupFile(HID,
"CITWheel.nmr");
if(RetError->iserror){
PrintError("Error
with HAP_SendSetupFile(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or passing in an invalid name value pair into this
function.
Purpose:
This function is used to retrieve one setup line (a name value
pair) from the HDS.
Prototype:
status_type *HAP_GetSetupData(
MPC_communication_struct_type
*Haptic_ID,
unsigned
char *name,
unsigned
char *value);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
name is a null-terminated string specifying the HDS variable
name.
Output:
value is a character array where this function will place
the setup value(s) string. This string contains the setup value
information that resides on the HDS.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
char values[40]; /* more than enough
space for the two values */
/* set the smoothing coefficient
for axis 1 (2nd axis) to 0.3 */
RetError = HAP_GetSetupData(HID,
"Coeff1", values);
if(RetError->iserror){
PrintError("Error
with HAP_SendSetupLine(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or passing in an invalid name into this function.
Purpose:
This function is used to command the device server to set the
current position of one axis the device as the 'Home' position.
This differs from the HAP_SetHome function in that HAP_SetHome
can zero multiple axes at once. This function zeros only the
one axis specified. Position coordinates returned from the HAP_Position
call will now return 0.0 for all axes for the new axis 'Home'
position.
Note that a device server only returns relative position values
and that, depending on the 'Home' position, the range of the values
may shift. For example, a device with a range of motion of 90
degrees on one axis will report values from -45 to 45 degrees
when precisely centered at the physical center. However, if the
device is at the full minimum extent when homed, then the values
returned will be from 0 to 90 degrees.
Prototype:
status_type *HAP_Home(
MPC_communication_struct_type
*Haptic_ID,
unsigned
short axis);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
axis is a value specifying the axis to be homed (zero based,
so 0 is the first axis).
Output:
None.
Return:
Returns a pointer to an error structure of type status_type.
This structure is described above.
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
unsigned short axis;
axis = 1; /* second axis */
RetError = HAP_Home(HapticID, axis);
if(RetError->iserror){
PrintError("Error
with HAP_Home(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
This function is used to set the current units to be used by controls
that are instantiated subsequent to this call. For example, if
the units is set to "native" and then a PID97 control
is instantiated (see CNTR_InstControl) then that instantiated
control will input the device axes information as raw unscaled
values and output forces in the raw device units. HAP_SetUnits
function sets the unit state and when a control is instantiated,
it will automatically always use the unit state that was current
during its instantiation.
There are three valid unit types:
0=metric (Meters, Radians, Newtons,
Newton-Meters)
1=normalized (-1 to 1 for everything)
2=native (Unscaled values from device)
Note: the default unit type is Metric.
Prototype:
status_type *HAP_SetUnits(
MPC_communication_struct_type
*Haptic_ID,
unsigned
char flag);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
flag is a character value specifying the units to be used:
0=metric 1=normalized 2=native
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
unsigned char unit_type = 1; /*
normalized */
. . .
RetError = HAP_SetUnits(HID, unit_type);
if(RetError->iserror){
PrintError("Error
with HAP_SetUnits(): Err=%d, %s",
RetError->number , RetError->description);
}
/* now the newly instantiated PID97
control will automatically use
normalized units */
RetError = CNTR_InstControl(HID,
&dev_control, "PID97");
if(RetError->iserror){
PrintError("Error
with CNTR_InstControl(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or passing in an invalid unit type (i.e. not 0, 1,
or 2).
Purpose:
This function is used to query the current type of units being
used by controls.
There are three valid unit types:
0=metric (Meters, Radians, Newtons,
Newton-Meters)
1=normalized (-1 to 1 for everything)
2=native (Unscaled values from device)
Prototype:
status_type *HAP_GetUnits(
MPC_communication_struct_type
*Haptic_ID,
unsigned
char *flag);
Input:
HapticID is a pointer to a MPC_communication_struct_type
structure.
flag is a pointer to a character that will be filled in
with a value specifying the units being used: 0=metric 1=normalized
2=native
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type
*HapticID;
status_type *RetError;
unsigned char unit_type;
. . .
RetError = HAP_GetUnits(HID, &unit_type);
if(RetError->iserror){
PrintError("Error
with HAP_GetUnits(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID).
Purpose:
Especially useful for the DOS version of the SDK, this function
allows the application programmer to provide a hi-res clock to
the HDS. This function was implemented because the application
programmer may have access to the very low level system timer
functionality (this found to be a problem in DOS). Such system-level
access gives the application programmer the ability to corrupt
just about any timing facility that could be implemented within
the HDS. For this reason, we provide the application programmer
the capability to provide a timer callback that fits within his/her
timing/interrupt paradigm.
Prototype:
status_type *HAP_SetTimeCallback(
double (*user_time_callback) (void));
Input:
user_time_callback is a function pointer to the application
programmer defined function. This function must return a double
and must have no arguments.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
/*
define the timer callback function - just a simple counter in
this
case */
double
my_timer() {
static
double the_time = 0.0;
/*
advance time one-onehundredth of a second */
the_time
+= 0.01;
return(the_time);
}
. . .
MPC_communication_struct_type *HapticID;
status_type *RetError;
. . .
/* set the callback to my_timer
*/
RetError = HAP_SetTimeCallback((double
(*) (void)) TimeCallback);
if(RetError->iserror){
PrintError("Error
with HAP_SetTimeCallback(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid pointer to a MPC_communication_struct_type
(HapticID) or passing in an invalid function pointer.
The Controls Library Application Programmer Interface (also referred to as the "CNTRLib API") provides a simple means of reading and writing values to the parameters of controls. A 'control' is used in this context to mean a piece of code that is executed once every servo loop in the Haptic Device Server. It may output forces, it may be a conditional control, or generate a sine wave, etc. The Controls Library also provides the capability to route the output from one control as input to another control. This feature allows very complex and powerful effects to be built from a few simple yet powerful controls. These new controls are included in this section also, as they are accessible only through the CNTRlib.
This section documents elements of the API that are shared by
all function calls.
All functions return a status structure that contains an
error code and a description of any error that might have occurred.
The structure will be referred to as RetError in the
examples of the functions and is defined as:
typedef
struct status_type {
unsigned char description[1000];
long number;
short iserror;
} status_type;
Where iserror is set to 1 if an error has occurred, 0 if
not. If an error has occurred (iserror is 1), then number
and description fields will have been filled. The description
field contains a textual description of the problem including
a description of the call stack, from deep to shallow, left to
right. Upon error, the number field will contain an integer
value corresponding to an enumeration of the API function error.
The first parameter to every function listed in this section is a single or double pointer to MPC_communication_struct_type structure. HAP_Open allocates space for and fills this structure which is then used for all subsequent calls to the same device server. This structure contains information about the communication to the device server.
Many of the haptic functions described in the following sections
parameterize physical device behaviors. Essentially, a haptic
device is a robot that is made up of a number of links
joined together at joints (joints are also called
axes). Each axis which joins two links in the haptic device
may be either translational or rotational. A translation
(or prismatic) axis is an axis whose Degree of Freedom (DOF) allows
one of its two connecting links to move in a straight line with
respect to the other (like the two parts of an extending ladder).
A rotational (or revolute) axis is one whose DOF allows
the axis to act as a hinge between its two connecting links.
To facilitate the description of haptic functions, as well as
to standardize their use, a set of generic units of measure will
be adhered to. The CyberImpactTM CNTRLib API
defaults to using metric SI units. The units of measure which
will be implied during the remainder of the haptic library description
are listed below:
| Unit | Axis Dependent | Description |
| Second (S) | No | Unit of time |
| Meter (M) | Translation | Linear position (distance) |
| M/S | Translation | Meters per second - linear velocity |
| M/S2 | Translation | Meters per second per second -linear acceleration |
| KG | No | Kilogram - mass |
| Newton | Translation | Kilogram-meters per second -linear force magnitude |
| Radian (R) | Rotational | Angular position or distance |
| R/S | Rotational | Radians per second - angular velocity |
| R/S2 | Rotational | Radians per second per second - angular acceleration |
| NM | Rotational | Newton-Meters - angular torque |
Units of measure other than those listed are used within the scope
of the haptic library, but they are simply comprised of those
that listed in Table 3 (e.g. rotational geometric
attraction scaling factor would be given as NM/M2 (Newton-Meters
per Meters squared)).
In addition to the metric SI Units, two other modes are available, depending on the application's needs.
These two modes are Normalized and Native. In Normalized
mode, all units except for time are scaled to cover the range
from -1.0 to 1.0. This provides a more device-independent method
of supplying parameters to haptic effects. The Native mode
provides the units as they come from the device. For example,
if a rotational stage has 12 bits of information for its position,
then values 0 to 4095 will be returned.
In order to give the application complete control over the execution
of controls without forcing the application programmer to keep
track of ongoing effects explicitly, it was necessary to create
an effects handle. Those HAP functions that cause effects to
occur and/or modify the behavior of effects, have as a second
argument a pointer to a pointer to the effect handle structure
(e.g. HAP_handle_struct **handle). The HAPLib and CNTRLib
libraries share use the same effects handle structure! The terms
'effect handle' and 'control handles' should be considered synonymous
when mentioned in this manual. The use of one term over another
will usually be dictated by the context of the subject.
Unlike the HAPLib library, the CNTRLib functions requires a valid
control handle for all of its functions except for the CNTR_InstControl,
whose purpose is to explicitly create a control handle.
Wording conventions used within the following API function descriptions
are described here:
Vector: The word vector
is often used when discussing the transmission and retrieval of
axis information. Since typical haptic devices have a number
of axes that we control simultaneously, it is useful to refer
to the data sent to or received from the axes as vectors. For
example, we can send a force vector of length 6 to a 6
DOF haptic device. Each element in the vector would then correspond
to one axis of the device.
Error: This
word is used to describe the difference between a desired value
and the actual value. For example, the HAP_PutPos function can
command the haptic device to pull towards or push away from, a
supplied reference position. In this case, the position error
is the difference between this reference position and the actual
physical position of the device axis.
Memory: Throughout the following
functions, the responsibility of memory allocation is assumed
to be as follows; The MPC_communication_struct_type and the HAP_handle_struct
are allocated and maintained by the Haptic library. The application
need only provide the memory for the pointer to the structures.
For functions that are using arrays to receive or send data,
it is assumed that the application has allocated enough space.
The space required is determined by the function prototypes and
by the device specific characteristics, such as number of axes
and number of buttons. If the memory is incorrectly handled by
the application, unpredictable results may occur and will not
be returned as errors by the haptic API functions.
Axes Order: Axes are reported in order (1, 2, 3, etc.).
See device specific documentation in the Device Specific Information
Section for axis order information.
The haptic server is capable of performing multiple simultaneous
tasks at a given time. Here is a list of the functions provided:
CNTR_InstControl :
Instantiates a control by name
CNTR_DeInstControl :
De-instantiates a control created in CNTR_InstControl.
CNTR_StartControl :
Activate a control.
CNTR_StopControl :
De-activate a control.
CNTR_StartAllControls :
Activate all instantiated controls.
CNTR_StopAllControls :
De-activate all instantiated controls
CNTR_PeekAdd :
Add an element to the peek request list.
CNTR_PeekSend : Send
the peek request list to the HDS.
CNTR_PeekGet :
Get one element from the peek reply list.
CNTR_Peek :
Send peek request and get reply in one function.
CNTR_PokeAdd :
Add an element to the poke list.
CNTR_PokeSend : Send
the poke request list to the HDS.
CNTR_Poke :
Create one-element poke list and send it to HDS in one function.
CNTR_PokeAddDSV : Create
a data pipe from one control parameter to another.
CNTR_PokeRemoveDSV :
Removes a data pipe created in CNTR_PokeAddDSV.
Purpose:
This function creates a control. The user specifies the control
type by name, the HDS creates an instantiation of the control,
and this function allocates a control handle by which the application
will refer to the control.
IMPORTANT: Many of the controls that exist in the HDS get information
(position, velocity, acceleration) about the device axes and/or
send forces out to these axes. The control inputs the axis information
and outputs the forces in one of three Unit types.
The three valid unit types are:
0=metric (Meters, Radians, Newtons,
Newton-Meters)
1=normalized (-1 to 1 for everything)
2=native (Unscaled values from device)
(Note: the default unit type is Metric.)
At any given time, there is a current Unit state specified in
the HDS. Upon a call to CNTR_InstControl, the control being
created automatically inherits this unit type and will keep this
unit type for its entire life.
The function HAP_SetUnits is used to set the current unit
state in the HDS. For example, if the units is set to 2 (native)
and then a PID97 control is instantiated using this function then
the newly instantiated PID97 control will input the device axes
information as native unscaled values and output forces in the
native device units.
Prototype:
status_type *CNTR_InstControl(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
**handle_in,
char
*name);
Input:
Haptic_ID is the device communication
structure pointer.
controlName is the logical
name of the control.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
unsigned char unit_type = 2; /* native */
. . .
/* first set the unit state to "native"
units */
RetError = HAP_SetUnits(HapticID,
unit_type);
if(RetError->iserror){
PrintError("Error
with HAP_SetUnits(): Err=%d, %s",
RetError->number , RetError->description);
}
/* now the newly instantiated DEV97 control will automatically
use
native device units */
RetError = CNTR_InstControl(HapticID, &dev_control, "DEV97");
if(RetError->iserror){
PrintError("Error
with CNTR_InstControl(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an incorrect control name.
Purpose:
This function removes an instantiated control.
Prototype:
status_type *CNTR_DeInstControl(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
**handle_in);
Input:
Haptic_ID is the device communication
structure pointer.
handle_in is a double pointer
to the handle structure allocated in CNTR_InstControl.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_DeInstControl(HapticID, &dev_control);
if(RetError->iserror){
PrintError("Error
with CNTR_DeInstControl(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid handle pointer.
Purpose:
This function activates (causes it to be executed in HDS) an
instantiated control.
Prototype:
status_type *CNTR_StartControl(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_in);
Input:
Haptic_ID is the device communication
structure pointer.
handle_in is a pointer to
the handle structure allocated in CNTR_InstControl.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_StartControl(HapticID, &dev_control);
if(RetError->iserror){
PrintError("Error
with CNTR_StartControl(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid handle pointer.
Purpose:
This function deactivates an instantiated control.
Prototype:
status_type *CNTR_StopControl(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_in);
Input:
Haptic_ID is the device communication
structure pointer.
handle_in is a pointer to
the handle structure allocated in CNTR_InstControl.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_StopControl(HapticID, &dev_control);
if(RetError->iserror){
PrintError("Error
with CNTR_StopControl(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid handle pointer.
Purpose:
This function activates all instantiated controls.
Prototype:
status_type *CNTR_StartAllControls(
MPC_communication_struct_type
*Haptic_ID);
Input:
Haptic_ID is the device communication
structure pointer.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_StartAllControls(HapticID);
if(RetError->iserror){
PrintError("Error
with CNTR_StartAllControls(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer..
Purpose:
This function deactivates all instantiated controls.
Prototype:
status_type *CNTR_StopAllControls(
MPC_communication_struct_type
*Haptic_ID);
Input:
Haptic_ID is the device communication
structure pointer.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_StopAllControls(HapticID);
if(RetError->iserror){
PrintError("Error
with CNTR_StopAllControls(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer..
Purpose:
This function adds an element into the peek request list for the
specified device. Use this function to request the value of a
control parameter, or an array of control parameters. Consider
the DEV97 control. For a 3-axis device this control includes
(among many others) the following parameters: pos0, pos1,
and pos2. These parameters are consecutive in the order
listed here. If the user specifies "pos1" as
the input name for this function, then the CNTRLib assumes
that the user is requesting the single position value for axis
1 (the second axis - zero based). If the user specifies "pos"
(not ending with an integer value), then the CNTRLib assumes
that the user is requesting all of the pos values (that
is, the array of values that look like "posN"
where N is an integer). The actual request will not be
sent to the HDS until the CNTR_PeekSend function is called,
at which point all of the values are sent at once.
Prototype:
status_type *CNTR_PeekAdd(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_in,
char
*name);
Input:
Haptic_ID is the device communication
structure pointer.
handle_in is a pointer to
a handle structure allocated in CNTR_InstControl.
name is a character string
specifying the logical name of the value/array.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_PeekAdd(HapticID, dev_control, "pos1");
if(RetError->iserror){
PrintError("Error
with CNTR_PeekAdd(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer..
Purpose:
This function sends out all of the peek requests generated by
CNTR_PeekAdd. The HDS will respond to this packet by sending
back the values of all the requested parameters. These can be
then grabbed by the application by using the CNTR_PeekGet
function.
Prototype:
status_type *CNTR_PeekSend(
MPC_communication_struct_type
*Haptic_ID);
Input:
Haptic_ID is the device communication
structure pointer.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_PeekSend(HapticID);
if(RetError->iserror){
PrintError("Error
with CNTR_PeekSend(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer..
Purpose:
This function gets the next peek response (corresponding to a
request generated by CNTR_PeekAdd) from the HDS. The
data corresponding to the request peek is included along with
the size of the data. The application programmer is given the
option of using a blocking read that will wait until the HDS response
has arrived, or a non-blocking read that just checks to see if
the data has arrived without waiting for it.
Prototype:
status_type *CNTR_PeekGet(
MPC_communication_struct_type
*Haptic_ID,
char
*data,
unsigned
short *length,
unsigned
char Block,
unsigned
char *flag);
Input:
Haptic_ID is the device communication
structure pointer.
name is a character string
specifying the logical name of the value/array.
block is an unsigned character
value that the application uses to specify a blocking read or
a
non-blocking
read (0=nonblocking 1=blocking).
Output:
data is a character array where the incoming peeked data
will be placed.
length is a short value that returns the length (in bytes)
of the data in this peek element.
flag is a pointer to an unsigned character value that the
function returns to the application that tells whether or not
the data was ready (for nonblocking peek-get only).
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float f_data;
unsigned short length;
unsigned char flag, block;
. . .
block = 1;
RetError = CNTR_PeekGet(HapticID, (unsigned char *) &f_data,
length, block, &flag);
if(RetError->iserror){
PrintError("Error
with CNTR_PeekGet(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer or insufficient room in the data array for the control
parameter data.
Purpose:
This function requests a parameter or parameter array, waits for
a reply from the HDS and then returns the parameter data to the
application. (Effectively doing the jobs of CNTR_PeekAdd,
CNTR_PeekSend, and CNTR_PeekGet all in one function).
Use this function to get the value of a control parameter, or
an array of control parameters. As in CNTR_PeekAdd described
above, assume that we have previously instantiated a DEV97 control.
For a 3-axis device this control includes (among many others)
the following parameters: pos0, pos1, and pos2.
These parameters are consecutive in the order listed here. If
the user specifies "pos1" as the input name for
this function, then the CNTRLib assumes that the user is
requesting the single position value for axis 1 (the second axis
- zero based). If the user specifies "pos" (not
ending with an integer value), then the CNTRLib assumes
that the user is requesting all of the pos values (that
is, the array of values that look like "posN"
where N is an integer). The actual request is sent to
the HDS and the data that returns is placed in the data array
argument of this function
Prototype:
status_type *CNTR_Peek(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_in,
char
*name,
unsigned
short *length,
char
*data);
Input:
Haptic_ID is the device communication
structure pointer.
handle_in is a pointer to
a handle structure allocated in CNTR_InstControl.
name is a character string
specifying the logical name of the value/array.
Output:
length is a short value that returns the length (in bytes)
of the data in this peek.
data is a character array where the incoming peeked data
will be placed.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float f_data[3];
unsigned short length;
unsigned char flag, block;
HAP_handle_struct *dev_control;
. . .
block = 1;
RetError = CNTR_Peek(HapticID, dev_control, (unsigned char *)
"pos", &length, f_data); /* get the whole pos array
*/
if(RetError->iserror){
PrintError("Error
with CNTR_Peek(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer or insufficient room in the data array for the control
parameter data.
Purpose:
This function adds an element into the poke command list for the
specified device. Use this function to command the value of a
control parameter, or an array of control parameters. Consider
the FORA97 control. For a 3-axis device this control includes
(among others) the following parameters: input0, input1,
and input2. These FORA97 control parameters are consecutive
in the order listed here. If the user specifies "input1"
as the input name for this function, then the CNTRLib assumes
that the user is commanding the single input value for axis 1
(the second axis - zero based). If the user specifies "input"
(not ending with an integer value), then the CNTRLib assumes
that the user is commanding all of the input values (that
is, the array of values that look like "inputN"
where N is an integer). The actual command will not be
sent to the HDS until the CNTR_PokeSend function is called,
at which point all of the values are sent at once.
Prototype:
status_type *CNTR_PokeAdd(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_in,
char
*name);
Input:
Haptic_ID is the device communication
structure pointer.
handle_in is a pointer to
a handle structure allocated in CNTR_InstControl.
name is a character string
specifying the logical name of the value/array.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
float f_data = 2.3;
. . .
RetError = CNTR_PokeAdd(HapticID, dev_control, "input1",
&f_data);
if(RetError->iserror){
PrintError("Error
with CNTR_PokeAdd(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer or an invalid control handle, or a data pointer that insufficient
space allocated.
Purpose:
This function sends out all of the poke commands generated by
CNTR_PokeAdd. The HDS will respond to this packet by changing
the actual control parameters referred to in this function call..
Prototype:
status_type *CNTR_PokeSend(
MPC_communication_struct_type
*Haptic_ID);
Input:
Haptic_ID is the device communication
structure pointer.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *dev_control;
. . .
RetError = CNTR_PokeSend(HapticID);
if(RetError->iserror){
PrintError("Error
with CNTR_PokeSend(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer..
Purpose:
This function sets the value of a parameter or parameter array.
(Effectively doing the jobs of CNTR_PokeAdd and CNTR_PokeSend
in one function. Use this function to set the value of a control
parameter, or an array of control parameters. As in CNTR_PokeAdd
example described above, assume that we have previously instantiated
a FORA97 control. For a 3-axis device this control includes (among
many others) the following parameters: input0, input1,
and input2. These parameters are consecutive in the order
listed here. If the user specifies "input1"
as the input name for this function, then the CNTRLib assumes
that the user is requesting the single position value for axis
1 (the second axis - zero based). If the user specifies "input"
(not ending with an integer value), then the CNTRLib assumes
that the user is requesting all of the input values (that
is, the array of values that look like "inputN"
where N is an integer). The actual request is sent to
the HDS and the data that returns is placed in the data array
argument of this function
Prototype:
status_type *CNTR_Poke(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_in,
char
*name,
char
*data);
Input:
Haptic_ID is the device communication
structure pointer.
handle_in is a pointer to
a handle structure allocated in CNTR_InstControl.
name is a character string
specifying the logical name of the value/array.
data is a character array
where the incoming peeked data will be placed.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
float f_data;
unsigned char flag, block;
HAP_handle_struct *dev_control;
. . .
f_data = 1.23;
RetError = CNTR_Poke(HapticID, dev_control, (unsigned char *)
"input2", &f_data); /*
set the 3rd (zero based) input value */
if(RetError->iserror){
PrintError("Error
with CNTR_Poke(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer, and invalid control pointer, or insufficient room in
the data array for the control parameter data.
Purpose:
This function creates a pipe from one control parameter to another
or from a HDS system variable (like current time, axis, or button
information) to a control parameter. For example, the result
parameter of a PID97 control can be piped into the input0
parameter of the FORA97 control. Using this function it is possible
to built whole chains of simple controls to create a meta-control
that may do something much more complex. Note that this is different
from simply peeking the value of one control parameter and poking
that value into another control parameter because the pipe created
here continuously updates the receiving parameter every
HDS servo iteration.
Prototype:
status_type *CNTR_PokeAddDSV(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_dest,
unsigned
char *name_dest,
HAP_handle_struct
*handle_source,
unsigned
char *name_source);
Input:
Haptic_ID is the device communication
structure pointer.
handle_dest is a pointer
to a pipe destination handle structure allocated in a call to
CNTR_InstControl.
name_dest is a character
string specifying the logical name of the destination parameter
that
must
exist in the control specified by handle_dest.
handle_source is a pointer
to a pipe source handle structure allocated in a call to
CNTR_InstControl
or NULL if the source is a HDS system variable.
name_source is a character
string specifying the logical name of the source parameter that
must
exist in the control specified by handle_source, or if
the handle_source is
NULL,
then this argument must be one of the following strings specifying
an
HDS
system variable:
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *pid_control, *fora_control;
. . .
/* pipe a PID97 contol's result parameter into a FORA97
control's
input0 parameter */
RetError = CNTR_PokeAddDSV(HapticID, fora _control, "input0",
pid_control, "result");
if(RetError->iserror){
PrintError("Error
with CNTR_ PokeAddDSV(): Err=%d, %s",
RetError->number , RetError->description);
}
/* pipe the HDS system variable TIME into a WAVE97 control's index
parameter */
RetError = CNTR_PokeAddDSV(HapticID, wave_control, "index",
NULL,
"TIME");
if(RetError->iserror){
PrintError("Error
with CNTR_PokeAddDSV(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer or an invalid source or destination control handle, an
invalid parameter name, or an invalid system variable name.
Purpose:
This function removes a pipe created in CNTR_PokeAddDSV
by specifying the destintation control and parameter name. The
pipe that terminated at this control parameter will be removed.
Prototype:
status_type *CNTR_PokeRemoveDSV(
MPC_communication_struct_type
*Haptic_ID,
HAP_handle_struct
*handle_dest,
unsigned
char *name_dest);
Input:
Haptic_ID is the device communication
structure pointer.
handle_dest is a pointer
to a pipe destination handle structure allocated in a call to
CNTR_InstControl.
name_dest is a character
string specifying the logical name of the destination parameter
that
must
exist in the control specified by handle_dest.
Output:
None.
Return:
Returns a pointer to an error structure of type status_type (described
above).
Example Call:
MPC_communication_struct_type *HapticID;
status_type *RetError;
HAP_handle_struct *fora_control;
. . .
/* remove the pipe (if one exists) that terminates at FORA97
control's input0 parameter */
RetError = CNTR_PokeRemoveDSV(HapticID, fora _control, "input0");
if(RetError->iserror){
PrintError("Error
with CNTR_PokeRemoveDSV(): Err=%d, %s",
RetError->number , RetError->description);
}
Potential Errors:
Possible errors include passing in an invalid device communication
pointer or an invalid control handle or an invalid parameter name.
Name: TIME97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| control_time | 1 | float | The resultant "time" |
| state | 1 | float | commands the manner in which control_time changes
=0.0 - hold control_time at current value. >0.0 - control_time moves ahead at system clock speed multiplied by this variable. <0.0 - control_time is set to 0.0 This variable defaults to 1.0. |
Description: This control is simply a seconds clock,
the parameter control_time counts off seconds at some
speed that may or may not correspond to "real" seconds.
This clock has a control "knob" on it called state.
If state is set to zero then the clock stops right where
it is. If the state knob is turned up, then the clock
starts running faster and faster. When state reaches 1.0,
control_time will be counting seconds as they occur in
real-time. If state is turned up to, say, 2.0, then control_time
will be increasing in value +2.0 per second (e.g. it will be
like a clock running at double speed). Similarly, if the state
is set to, say, 0.5 then the control_time will be counting
time off at half speed.
Forces to Device: None.
Name: COND97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| left | 1 | float | Input to the control. The left comparison element. |
| right | 1 | float | Input to the control. The right comparison element. |
| lgtr | 1 | float | The value parameter will be set to this value if the left parameter is greater than the right parameter. |
| rgtl | 1 | float | The value parameter will be set to this value if the right parameter is greater than the left parameter. |
| letr | 1 | float | The value parameter will be set to this value if the left and right parameters are equal. |
| result | 1 | float | The resultant value set to lgtr, rgtl, or letr |
Description: This is the conditional control. Its
job is to place one of three values into the result parameter.
Which of the three values gets put into result is based on the
values of left and right. Here's the rules:
Using this control, you can cause action to be applied when some
value is over a threshold (such as an axis value greater than
some predetermined value). You can also cause other controls
to "start up" by piping the result of this control
into the TIME97 state parameter.
Forces to Device: None.
Name: DEV97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| sys_time | 1 | float | Absolute time in seconds in the Device Server |
| pos | #axes | float | The axis positions. |
| vel | #axes | float | The axis velocities. |
| acc | #axes | float | The axis accelerations. |
| toggle | bytes required for (bits * #toggles) | char | Bitmask of the toggle states (0=released, 1=pressed) |
| toggle_history | #toggles | char | Number of times each button has been pressed or released since the last query of this value. |
| force | #axes | float | Current forces put to the axes. |
Description: This control monitors the state of
the device. Primarily created for use as an input-only control,
it records the position, velocity, and accelerations for each
of the device axes. Analog valuator data is reported as additional
axes. Also included is a n-character bitmask ( where
n = INT((#toggles-1)/8 + 1) ) called toggle
that makes the toggle (button) states available, and an array
of characters that records the number of times that the toggle
has changed states from pressed-to-released or released-to-pressed.
A toggle_history array element is reset to zero when it is peeked
(looked at) by the application.
Forces to Device: None.
Name: PID97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| axis | 1 | float | The zero-based axis number of the device. |
| dfor | 1 | float | Desired force. |
| kf | 1 | float | Force multiplier. |
| dpos | 1 | float | Desired Position. |
| kp | 1 | float | Position control scalar (-attraction, +repulsion) |
| del | 1 | float | Desired Velocity. |
| kv | 1 | float | Velocity control scalar (-attraction, +repulsion) |
| dacc | 1 | float | Desired Acceleration. |
| ka | 1 | float | Acceleration control scalar (-attraction, +repulsion) |
| dint | 1 | float | Desired position Integral. |
| ki | 1 | float | Position Integral control scalar (-attraction, +repulsion) |
| result | 1 | float | The result value of this control. |
| possign | 1 | short | Forces applied for positions with the same sign as this value, or all positions if possign is zero. |
| velsign | 1 | short | Forces applied for velocities with the same sign as this value, or all velocities if velsign is zero. |
| accsign | 1 | short | Forces applied for accelerations with the same sign as this value, or all accelerations if accsign is zero. |
Description: This control calculates a PID (Position/Integral/Derivative)
- like force for a device axis given by axis. The result
is generated using the following formulas:
Pos_Error = dpos - POSITION[axis] (position
error term - difference between desired and actual position)
Vel_Error = dvel - VELOCITY[axis]
(velocity error term)
Acc_Error = dacc - ACCELERATION[axis]
(acceleration error term)
Int_Error = dint - Pos_Error (integral
error term - difference between desired integral and actual sum
of all previous position errors)
if ((possign * POSITION[axis]) < 0.0 OR
velsign * VELOCITY[axis]) < 0.0 OR
accsign * ACCELERATION[axis]) < 0.0) then
result = 0.0
else
result = Pos_Error*Kp + Vel_Error*Kv
+ Acc_Error*Ka + Int_Error*Ki + dfor*Kf
Using the PID97 control, you can create a number of different
effects. PID controls are often used to control robots, and that's
what haptic devices really are. Here are some examples of the
things that can be done with the PID97 control:
Forces to Device: None.
Name: WAVE97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| type | 1 | char | Specifies waveform being generated:
0=SINE specifies a sine wave 1=RANDOM specifies a random wave. |
| loop | 1 | char | 1=Repeat wave. 0=play wave only once. |
| index | 1 | float | Index into the wave form (e.g. time, axis position ) |
| freq | 1 | float | Frequency of the wave. |
| result | 1 | float | Wave amplitude at index point. |
Description: This control generates a wave. This
wave can either be sinusoidal or random in nature. The type
parameter specifies the type. Sine waves simply oscillate between
-1 and 1 at the frequency specified by freq. Random waves
simply adjust their forces linearly towards an end goal. This
end goal is a value between -1 and 1 and is updated once every
period (i.e. 1/freq). The wave will only be active for
one period (1/freq) unless loop is set to TRUE (1) in which
case the wave repeats forever. The amplitude of the wave at the
point specified by index is placed in result. The wave
can easily be played as a function of time (by piping the time
output from the TIME97 control, or the system time variable into
index) for a vibration type effect, or as a function of position
(by piping system axis information into index) for a texture type
effect.
Forces to Device: None.
Name: SAM97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| name | 30 | char | A character string that is the name of the sample. |
| cont | 1 | char | Order of continuity:
0: no continuity enforced, forces simply "step up/down" when the next sample value is reached. 1: linear continuity enforced, forces always "ramp up/down" to the next sample value. |
| loop | 1 | char | 1=Repeat sample. 0=play sample only once. |
| index | 1 | float | Index into the sample form (e.g. time, axis position ) |
| freq | 1 | float | Frequency to play sample in sample elements per index unit (e.g. second). |
| result | 1 | float | Sample amplitude at index point. |
Description: This control plays a sample (loaded
to the Device Server using the HAP_LoadSample function. The wave
can easily be played as a function of time (by piping the time
output from the TIME97 control, or the system time variable into
index) for a vibration type effect, or as a function of position
(by piping system axis information into index) for a texture type
effect. See HAP_PokeSendDSV for information about piping
data. The sample amplitude at the point specified by index is
stored in the result parameter.
Forces to Device: None.
Name: ENV97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| loop | 1 | char | 1=Repeat envelope. 0=play envelope only once. |
| index | 1 | float | Index into the envelope (e.g. time, axis position ) |
| attack | 1 | float | Envelope attack length (ramp up to 1) Default 0. |
| sustain | 1 | float | Envelope sustain length (ramp up to 1) Default 0. |
| decay | 1 | float | Envelope decay length (ramp up to 1) Default 0. |
| result | 1 | float | Envelope amplitude at index point. |
Description: This control plays a sort of simplified
sample. The envelope is simply made up of an attack, a sustain,
and a decay. For the purposes of this description let us assume
that the index into the envelope (the index parameter) is moving
forward like a clock. As the index parameter increases from time
zero, the first thing to occur is the attack. The attack last
for a time specified by the attack parameter, and during
that time, the amplitude linearly increases from zero to 1.0.
The amplitude is continuously stored in the result parameter.
At this point, the sustain holds an amplitude of 1.0 for a length
of time specified by the sustain parameter. Finally, the
amplitude trails off to zero over a period of time specified by
the decay parameter. If the loop parameter is set
to 1 then the process repeats (i.e. attack, sustain, decay, attack,
sustain, decay, attack, sustain, etc.
.) otherwise, once
the index becomes larger than the sum of the attack, sustain,
and decay parameters, the result will always be zero.
Remember that this description assumes an index controlled by
a time source. This does not have to be the case as the index
value can be piped in from any other control or from any system
variable.
Forces to Device: None.
Name: SUM97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| input | 10 | float | Input variables. Default 0.0 |
| operation | 9 | char | Operation:
0: + addition 1: * multiplication |
| result | 1 | float | Envelope amplitude at index point. |
Description: This control's purpose is to sum, multiply,
offset, and scale the results of other controls. By piping in
the results of other instantiated controls and system variables,
a result is generated by summing and multiplying the input parameters
together. The formula used to calculate the result looks like
this:
The inputs (input0, input1, etc.) are simply the exact values
inside of the input parameter array. Each operation is either
addition or multiplication depending on the value of the operation
parameter array element. 0 is addition and 1 is multiplication.
The control follows the typical left-to-right and multiplication-first
precedent rules. The calculated value is put into the result
parameter.
Forces to Device: None.
Name: WALL97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| point | #axes | float | coordinate of a point on the wall |
| normal | #axes | float | vector perpendicular to wall |
| kp | #axes | float | spring constant of wall |
| kv | #axes | float | damping constant of wall |
| result | #axes | float | force resulting from contact |
Description: This control creates a virtual wall
defined by the point and normal parameters. If
the position of a point defined by the device axes info passes
the wall plane, a force value is computed and output as result.
The wall and point coordinates are defined in up to three dimensions,
depending on the number of axes of the device. For example,
this control will work in two dimensions (x,y) for a two axis
joystick, and in one dimension (x) for a steering wheel.
The force result is computed based on two factors: the distance
that the point has penetrated the surface, and how fast the point
is moving. The constants kp and kv are used a scaling
factors for these two quantities as follows:
result
= (kp * (penetration distance)) + (kv * velocity)
Forces to Device: None.
Name: FORA97
Control Parameters:
| Name(s) | Quantity | Format | Description |
| input | #axes | float | Input variables. Default 0.0 |
| scale | #axes | float | Scale variables. Default 0.0 |
Description: This control's purpose is to apply
forces to the device itself. The idea is to pipe the result
of another control into the input parameter corresponding to the
axis that you want the force to be applied to. The input value
is then multiplied by its corresponding scale parameter and that
number is the force that is output to the axis device. In other
words:
Force on axis 1 = input[1] * scale[1]
Force on axis 2 = input[2] * scale[2]
Force on axis 3 = input[3] * scale[3] . . .
Forces to Device: Axis N force = input[N] multiplied
by scale[N]..
The following example instantiates a set of controls that cause
a sample and a viscosity to be applied continuously to axis 0
and causes a vibration effect to be applied to axis 1 whenever
button 1 is pressed.
Here is a list of the controls involved and a brief explanation of how they are used in this example.
First we create a macro for handling errors, this macro is called
IFerror and looks like this:
#define IFerror {
\
if (eflag->iserror) {
\
fprintf(stderr, "ERROR: iserror:%d number:%d description:%s
\n", \
eflag->iserror, eflag->number, eflag->description);
\
if ((eflag->number%1000) < 500){
\
goto CLOSE_END;
\
}
\
}
\
}
The first thing the example program does is to create
(instantiate) a DEV97 control using CNTR_InstControl (CNTR_InstControl
is the function used to instantiate any control) . This will
allow us to get the state of the device at any time.
/* allows us to get the state of the
device at any time */
eflag = CNTR_InstControl(HID, &dev_handle, "DEV97");
IFerror;
Next, a PID97 control is instantiated. The parameters are assigned
such that this control performs velocity damping. We poke (using
CNTR_Poke) in values of 0 for the axis parameter so that
the control will use the first (zero-based) axis velocity value
for its calculation, and -0.5 for the kv parameter. This
will cause the PID97 control to output a result directly proportional
(in fact equal) to the velocity of the 0th axis multiplied
by kv value. The desired velocity defaults to zero which
is what we want anyway, so we don't have to set it.
/* sets up a continuous velocity damping
on the first axis */
/* (axis number 0 - zero based) */
eflag = CNTR_InstControl(HID, &pid0_handle, "PID97");
IFerror;
unsignedshorttemp = 0;
eflag = CNTR_Poke(HID, pid0_handle, "axis", (char
*)
&unsignedshorttemp); IFerror;
floattemp = -0.5f;
eflag = CNTR_Poke(HID, pid0_handle, "kv", (char *)&floattemp);
IFerror;

Next we create a sample. We fill a 10 character array with values
that describe the desired sample.
/* first create a sample */
sample_array[0] = 0;
sample_array[1] = 33;
sample_array[2] = 55;
sample_array[3] = 24;
sample_array[4] = -10;
sample_array[5] = -70;
sample_array[6] = -29;
sample_array[7] = 3;
sample_array[8] = 126;
sample_array[9] = -4;
Once we have defined the sample, we name it "sample 1"
and send it to the DS using HAP_LoadSample.
/* now send it to the HDS */
length = 10;
eflag = HAP_LoadSample(HID, "sample1", sample_array,
length,
(unsigned short) 1); IFerror;
In order to do something with this sample we now create the sample
control by calling CNTR_InstControl with the name SAM97.
/* Create the sample control */
eflag = CNTR_InstControl(HID, &sam_handle, "SAM97");
IFerror;
We now tell the sample control which sample to play. Since we
just sent over a sample and named it "sample1" this
is the sample name we send. Because there are 30 nameX
(where X is an integer 0 through 29) parameters in the
SAM97 control, it is essential to have declared temp_param_name
as a character array of at least length 30 (see the Control descriptions,
specifically SAM97). CNTR_Poke will indeed copy 30 bytes
from temp_param_name and send them to the HDS.
/* set the sample being used to the one
we just sent to the DS */
strcpy(temp_param_name, "sample1");
eflag = CNTR_Poke(HID, sam_handle, "name", temp_param_name);
IFerror;
We want SAM97 to play our sample as system time increments. Using
CNTR_PokeAddDSV (CNTR_PokeAddDSV is the function
used to pipe values to one control parameter from another control
parameter or from a HDS system variable), we can pipe the system
clock to SAM97's index parameter. The fourth and fifth
function arguments provide the source of the pipe by specifying
NULL as the destination handle (a NULL fourth argument means "use
a system variable instead of a control parameter") and TIME
as the specific system variable name. The second and third function
arguments tell where this data should be piped by specifying sam_handle
as the destination handle and index as the destination parameter.
The pipe created here pipes the HDS time value into the
index parameter of the sample control, causing our sample
array to be played as time increments.
/* set the index to the system time */
eflag = CNTR_PokeAddDSV(HID, sam_handle, "index", NULL,
"TIME");
IFerror;
Finally some other parameters of the sample have to be set. We
now simply poke in (using CNTR_Poke) the freq (frequency
= 5 in this case), the cont parameter (continuity = 1
for 1st order or linear continuity), and the loop
parameter (1 for continuously loop).
/* set frequency to 5 sample elements
per second */
floattemp = 5.0f;
eflag = CNTR_Poke(HID, sam_handle, "freq", (char *)&floattemp);
IFerror;
/* set to first order continuity between sample elements */
unsignedchartemp[0] = 1;
eflag = CNTR_Poke(HID, sam_handle, "cont", (char *)
unsignedchartemp); IFerror;
/* make it continuously loop */
unsignedchartemp[0] = 1;
eflag = CNTR_Poke(HID, sam_handle, "loop", (char *)
unsignedchartemp); IFerror;

COND97 is the next control that is instantiated.
/* the next control is a conditional
that returns -1 whenever button one is pressed, */
/* +1 otherwise */
eflag = CNTR_InstControl(HID, &cond_handle, "COND97");
IFerror;
The condition control's whole purpose is to continuously compare
two of its parameters: left and right. Based on
the result of this comparison it will copy one of three parameters
(lgtr, rgrl, or letr) into the its result
parameter. The rules are simple. If left is greater than
right, then the control copies lgtr ("left
greater than right") into result . If right
is greater than left, then the control copies rgtl
("right greater than left") into result. If
left and right are equal, then the control copies
letr ("left equal to right") into result. Now,
what we want to accomplish is to have this control output a negative
1 whenever the first button is pressed (consider that the result
parameter is this control's output). We don't want the control
to continuously output a negative one when the button is held
down, but only upon the transition into being pressed. The HDS
system variable TOGGLE_PRESS0 does exactly what we need. Its
value is zero almost all the time, and only becomes 1 during the
instant in time when toggle0 is pressed. TOGGLE_PRESS0 then reverts
back to zero until toggle0 is pressed again.
eflag = CNTR_PokeAddDSV(HID, cond_handle,
"left", NULL, "TOGGLE_PRESS0"); IFerror;
Using CNTR_PokeAddDSV we pipe the TOGGLE_PRESS0 into the
left parameter of COND97. The value 1 is poked into the
right parameter so the control compares the TOGGLE_PRESS0
to 1. The values for the three possible outputs letr (set
to -1), lgtr (set to 1), and rgtl (set to 1) parameters
are poked as well. In this manner, when the first toggle is pressed,
TOGGLE_PRESS0 becomes 1 which causes left to be equal to
right. This left-equal-to-right condition (letr) causes
the control to put -1 into result. So result becomes
-1 whenever the toggle is pressed, and is 1 otherwise (left is
less than right).
floattemp = 1.0f;
eflag = CNTR_Poke(HID, cond_handle, "right", (char *)&floattemp);
IFerror;
floattemp = -1.0f;
eflag = CNTR_Poke(HID, cond_handle, "letr", (char *)&floattemp);
IFerror;
floattemp = 1.0f;
eflag = CNTR_Poke(HID, cond_handle, "lgtr", (char *)&floattemp);
IFerror;
floattemp = 1.0f;
eflag = CNTR_Poke(HID, cond_handle, "rgtl", (char *)&floattemp);
IFerror;
The next control involved is TIME97.
/* Setup a time control that resets back
to time zero whenever button 1 is pressed */
eflag = CNTR_InstControl(HID, &time_handle, "TIME97");
IFerror;
We pipe the result from the COND97 control into this control's
state parameter. Whenever this state parameter
is becomes -1, (which will now occur whenever the toggle is pushed),
TIME97 will reset the control_time parameter to zero.
Otherwise, state will be 1, causing the control_time
to advance normally. The behavior of this TIME97 control is to
advance control_time normally until toggle0 is pressed,
at which point control_time resets to zero.
eflag = CNTR_PokeAddDSV(HID, time_handle,
"state", cond_handle, "result"); IFerror;

The next control to instantiate is WAVE97.
/* Setup a wave control that does a sine
wave based on the time output of the */
/* previous control */
eflag = CNTR_InstControl(HID, &wave_handle, "WAVE97");
IFerror;
We now make a pipe from the TIME97 control's control_time
parameter into this WAVE97 control's index parameter (CNTR_PokeAddDSV).
This causes the wave to play as the time generated by the TIME97
control increments. This way, whenever the toggle0 is pressed,
the time will restart at zero and the wave will restart.
eflag = CNTR_PokeAddDSV(HID, wave_handle,
"index", time_handle, "control_time"); IFerror;
Values are also poked (CNTR_Poke) into type (which
specifies type of wave, in this case 0 for sine wave), freq
(frequency = 14 Hz), and loop (1 = on). These values will
determine what kind of wave is created, which is in this case
a continuously looping 14Hz sine wave.
unsignedchartemp[0] = 0;
eflag = CNTR_Poke(HID, wave_handle, "type", (char *)unsignedchartemp);
IFerror;
floattemp = 14.0f;
eflag = CNTR_Poke(HID, wave_handle, "freq", (char *)&floattemp);
IFerror;
unsignedchartemp[0] = 1;
eflag = CNTR_Poke(HID, wave_handle, "loop", (char *)unsignedchartemp);
IFerror;

Now we create (using CNTR_InstControl) an ENV97 control.
This will give us an envelope (which is basically a simple attack-sustain-decay
profile) with which we will scale WAVE97 output.
/* Set up an envelope control that is
also based on the control_time parameter of the TIME97 control
*/
eflag = CNTR_InstControl(HID, &env_handle, "ENV97");
IFerror;
We also create a data pipe (using CNTR_PokeAddDSV) that
causes the ENV97 control to get its index parameter from
the TIME97 control_time parameter (exactly the same way
it was piped into the WAVE97 control index). This will
sync both controls to restart when toggle0 is pressed.
eflag = CNTR_PokeAddDSV(HID, env_handle,
"index", time_handle, "control_time"); IFerror;
floattemp = 0.001f;
We also poke in values for attack (0.001 seconds), sustain
(0.1 seconds), decay (0.5 seconds), and loop (0
= do not loop). These values determine the "shape"
of the envelope that will eventually scale the wave generated
in the WAVE97 control.
eflag = CNTR_Poke(HID, env_handle, "attack",
(char *)&floattemp); IFerror;
floattemp = 0.1f;
eflag = CNTR_Poke(HID, env_handle, "sustain", (char
*)&floattemp); IFerror;
floattemp = 0.5f;
eflag = CNTR_Poke(HID, env_handle, "decay", (char *)&floattemp);
IFerror;
unsignedchartemp[0] = 0;
eflag = CNTR_Poke(HID, env_handle, "loop", (char *)
unsignedchartemp); IFerror;

The control_time from TIME97 that is piped to both the
WAVE97 and ENV97 controls determines when these controls will
restart their indices. Effectively, when the button is pressed,
the TIME97 control's control_time parameter is set back
to zero, to begins moving forward from zero once again. Notice
that since the loop parameter of the ENV97 control is zero
the envelope only "plays" once, so the output of the
ENV97 will be zero when after the index finishes the decay.
The effect of this is that when the button is pressed, the TIME97
control's control_time parameter resets to zero, causing
the this ENV97 control to index through the envelope's attack,
sustain, and decay one time through, after which it just outputs
zero.
The next control to instantiate is SUM97. We create this one
to add (the default operation) the results of the SAM97 and PID97
controls results.
/* Sum up the forces generated by PID97
and SAM97 */
eflag = CNTR_InstControl(HID, &sum0_handle, "SUM97");
IFerror;
eflag = CNTR_PokeAddDSV(HID, sum0_handle, "input0",
pid0_handle, "result"); IFerror;
eflag = CNTR_PokeAddDSV(HID, sum0_handle, "input1",
sam_handle, "result"); IFerror;

Since all of the operation values in the SUM97 control default
to zero (addition), it is unnecessary to set the operation0
parameter (which is the operation between input0 and input1).
We also need to instantiate another SUM97 control. This one will
multiply together its input0 and input1 parameters.
We pipe the results from WAVE97 and ENV97 into the input0
and input1 parameters of the SUM97 control respectively
(using CNTR_AddPokeDSV).
/* Multiply the forces generated by WAVE97
and ENV97 */
eflag = CNTR_InstControl(HID, &sum1_handle, "SUM97");
IFerror;
eflag = CNTR_PokeAddDSV(HID, sum1_handle, "input0",
wave_handle, "result"); IFerror;
eflag = CNTR_PokeAddDSV(HID, sum1_handle, "input1",
env_handle, "result"); IFerror;
Because we want to multiply (not add) the first two input parameters
we must also poke in a value of 1 (for multiplication) for the
operation0 parameter.
unsignedchartemp[0] = 1;
eflag = CNTR_Poke(HID, sum1_handle, "operation", (char
*) unsignedchartemp); IFerror;
The effect of this multiplication will be to scale the continuously
looping wave with the magnitude of the envelope. The result will
be a vibration that will quickly rise, sustain itself briefly,
and then fade out.
WAVE97
* ENV97
= After SUM97 multiply
The final control is FORA97. This control receives input from
the two SUM97 controls and applies those inputs as forces to two
of the device axes.
/* Instantiate the output as forces on
the first(0) and second(1) axes */
eflag = CNTR_InstControl(HID, &forc_handle, "FORA97");
IFerror;
The result of the first SUM97 (the sum of the SAM97 and PID97
control results) is piped to the input0 parameter and a
value of 0.5 is poked into the scale0 parameter. The product
of input0 and scale0 will be applied as a force
to axis 0.
eflag = CNTR_PokeAddDSV(HID, forc_handle,
"input0", sum0_handle, "result"); IFerror;
floattemp = 0.5f;
eflag = CNTR_Poke(HID, forc_handle, "scale0", (char
*)&floattemp); IFerror;

The result of the second SUM97 (the product of the WAVE97 and
ENV97 control results) is piped to the input2 parameter
and a value of 1.0 is poked into the scale2 parameter.
The product of input2 and scale2 will be applied
as a force to axis 2 (the third axis).
eflag = CNTR_PokeAddDSV(HID, forc_handle,
"input1", sum1_handle, "result"); IFerror;
floattemp = 1.0f;
eflag = CNTR_Poke(HID, forc_handle, "scale1", (char
*)&floattemp); IFerror;
The resulting effect on axis 0 is a velocity damping (viscous
feeling) along with a repeating sample. The resulting effect
on axis 2 is a momentary vibration that occurs when the first
toggle is pressed.
This following diagram shows the entire process from beginning
to end:

Software setup and configuration consists of a Device Definition
file and a Device Server Configuration file. The HAPTIC.DEV
file is the Device Definition file and files with the .nmr
extension are Device Server Configuration file. The Device Definition
file defines each device by name and what communication media
and parameters the application will need to connect with it. The
Device Server Configuration file contains values specific to the
physical device as well as determining the behavior of the device
server.
Each device is identified by a mnemonic name (label) that is used
in the application code. A device server can have a variety of
devices that it can control. Therefore, specifying a specific
device to open at the application level is necessary. The Device
Definition file contains various device labels, the communication
media and parameters on which to use to connect to each device,
and the configuration files name to send them once connected.
The Device Server Configuration file allows the user to adjust various parameters effecting the behavior of the device server. Fine tuning some of these values will allow for a better application/device server fit, especially when both reside on the same processor. Other values in the Device Server Configuration file are hardware specific and should not be adjusted by the end user. A complete set of Device Server configuration files for the CyberImpact line of force-feedback devices is provided with the SDK.
The application opens a device server by specifying the device
label in the HAP_Open call (see HAPLib documentation for
further details). This device label is defined in the Device Definition
file. The device file is divided into sections by the type of
device servers available. Currently, only device servers of the
type HAPTIC are supported.
Each line in the HAPTIC
section of the Device Definition file contains information about
a particular device including the communication type, and the
configuration file to send it. Two examples of a Device Definition
is provided below.
[HAPTIC]
CyberImpactJoystick3 = Funcall, ..\config\CIJStick3.nmr
CybernetRealFeelYoke = Socket, ..\config\RFYoke.nmr,
123.456.78.9 2112
The sections are identified by the square brackets, e.g. [HAPTIC]. The maximum length of the line allowed is determined by MAX_CONFIG_LINE_SIZE setting in the HAPlib.h include file provided with the SDK, and is currently set to 120 characters. The first parameter is the label assigned to the device by the device/server developer. These are the labels that the application uses to open specific devices. Note that if pathnames are given as relative, they are relative from the location of the executable making the HAP_Open call. The second parameter is the communication type used to connect to the device server that is controlling the device (Funcall to use the function call interface and Socket to offload the force feedback. See the Client Server section for more details). The remainder of the parameters are used by the specific type of communication protocol, e.g. Socket communications take an IP address (123.456.78.9 in this case)and a port number (2112 in this case).
Each device server (DS) requires the existence of an associated
configuration file. This file contains device specific information
about the device being controlled. The Device Configuration files
are organized using Name/Value pairs. Every line in the file
must adhere to the following format. The line may be a comment,
indicated by a '!', and all text following the '!' is ignored.
Otherwise the line is a legal Name, followed by any number of
legal Values, which are determined by the Name. Here is an example
of a typical device server configuration file (this one is named
CIJStick3.nmr):
!------------------------------------!
! CyberImpact SDK Configuration File !
!------------------------------------!
Version:2.0
DeviceType:CIJStick3
NumPAxes:3
NumNPAxes:1
NumToggles:8
Servo:TIMER
!parameters for an IMC connection
!com port, baud, timer/sleep delay
IMC0: COM2, 115200, 3, 8, 1
Timer: 15
![Physical Data]
!this section describes the physical characteristics of the device
!ticksPerUnit,minUnits,maxUnits,forcePerVolt,forceOffset,maxForce
Axis0: 3055.77749 -0.6981316 0.6981316 -0.00030619 0.000000 0.9492
Axis1: 3055.77749 -0.6981316 0.6981316 -0.00030619 0.000000 0.9492
Axis2: 3395.30832 -0.7853982 0.6981316 -0.00034022 0.000000 1.0546
Axis3: 1.0 -10.0 10.0
0.0 0.0 0.0
![Home Positions]
!'Home' position for each axis
!(optional, defaults are 0.0)
Home0: 0.000000
Home1: 0.000000
Home2: 0.000000
Home3: 0.000000
![Derivation Coefficients]
!velocity, acceleration
Coeff0: 0.2 0.2
Coeff1: 0.2 0.2
Coeff2: 0.2 0.2
Coeff3: 0.2 0.2
Certain segments of this configuration file specify
scaling, homing, and calibration type data that should never change.
Other settings refer to communications and other software execution
parameters and can be adjusted as desired. The following provides
a legal Name, a brief description, the legal values for that name(indicated
by bold) and their descriptions.
[OPTIONAL - 1.0]
Indicates the version of this configuration file. Version 2.0
format is slightly different from the previous versions in that
all per-axis information is now zero based. For example, in version
2.0, the first axis is referred to as Axis0. In previous versions,
this axis would have been referred to as Axis 1. For the version
2 setup file example above, the three axes are 0,1,and 2 (they
were previously 1,2, and 3 in old versions).
Other fields that have changed slightly in Version 2.0 are as follows:
The NumAxes field has been replaced by NumPAxes (# of powered
axes).
The NumValuators field has been replaced by NumNPAxes (# of non-powered
axes).
[REQUIRED]
Indicates the type of device being connected to. Legal values
with descriptions are:
CyberImpact
Generic CyberImpact Device.
CIJStick3
CyberImpact 3DOF Joystick
CIHandC
CyberImpact 6DOF Handcontroller
CIYoke
CyberImpact 2DOF Yoke
CIWheel
CyberImpact 1DOF Steering Wheel
Mouse
2 Button Mouse(Win95
only currently)
IForce
IForce-compatible device
Dummy
No device
Joy
Joystick (Win95 only currently)
!Connect to a Generic CyberImpact Device
DeviceType: CyberImpact
[REQUIRED]
Indicates how the Device Server servo loop is to be controlled.
CLIENT
The application controls the servo-loop through the use
of HAP_DriveServo
TIMER
The operating systems multitasking is used to start an independent
task that will drive the servo loop.
See the Timer Name/Value for controlling the length
of the servo loop period. Currently supported in
Win95 only.
THREAD
The operating systems multitasking is used to start an independent
thread that will drive the
servo loop. Currently supported in Win95 only.
!Use timer for servo loop driving
Servo: TIMER
[REQUIRED]
Indicates the expected number of powered axis on the device. If
the Nub Name/Value is set to TRUE, then there is no actual
device since a serial port loopback is being used, and then the
value of NumPAxes + NumNPAxes is considered to be the number of
axes present.
In previous versions, this name was called NumAxes. See Version
field above for more info.
#
Integer number >=
0
!A 3 DOF device
NumPAxes: 3
[REQUIRED]
Indicates the expected number of toggle buttons on the device.
For the actual assignments of the toggles, refer to the device
specific documentation for that device.
#
Integer number>= 0
!Device supports 8 toggles, 4 buttons and 4-way hat.
NumToggles: 8
[REQUIRED]
Indicates the expected number of valuators (non-powered axes)
on the device. Valuators are interfaces with more than 2 states,
such as a gas pedal. For the actual assignments of the valuators,
refer to the device specific documentation for that device.
In previous versions, this name was called NumValuators. See
Version field above for more info.
#
Integer number>= 0
!Device supports 1 valuator, an analog trigger
NumNPAxes:1
[REQUIRED with CyberImpact-compatible devices]
Must be used with CyberImpact-compatible devices, indicates the
serial port and baud rate to use to communicate with the device.
For certain CyberImpact devices(such as the CyberImpact FingerForcerTM
device) multiple IMC Name/Value pairs may be used, to indicate
that multiple serial ports are being used to connect to the device.
In this case, the last 3 value pairs listed below are used to
indicate the information being sent from the device over each
serial port. The sum total of the num_axis, num_toggles, and num_valuators
values for each IMC line MUST equal the NumPAxes,
NumNPAxes, and NumToggles values.
COM1 or COM2
Which serial port to
use.
#
Integer number > 0, a legal baud rate supported by the
serial port and the device.
#
Integer Number >= 0, the number of axis from
the device that communicate on this serial port.
#
Integer Number >= 0, the number of toggles from
the device that communicate on this serial port.
#
Integer Number >= 0, the number of valuators
from the device that communicate on this serial port.
!Connect to CyberImpact device on COM1 at 115200
IMC0: COM1, 115200, 3, 8, 1
!A multi-serial port example for FingerForcer, a 21 degree-of-freedom
device.
!COM2 gets the first 6 axis of the device, 0 - 5.
!COM1 gets the next 15 axis of the device, 6-20.
!COM port, baud rate, num axes, num toggles, num valuators
IMC0: COM2, 115200, 6, 0 , 0
IMC1: COM1, 57600, 15, 0 , 0
[REQUIRED when SERVO = TIMER]
Indicates the period of time to sleep in between device server
servo loops. Time is in milliseconds. Depending on the operating
system, values below a certain threshold should not be used, as
significant CPU resources will be spent in thread switching. Currently
only supported in Win95.
#
Integer number > 0
!Use the timer, and sleep for 10 ms ~= 100 Hz servo
rate
Timer: 10
[OPTIONAL - FALSE]
Indicates that a Encoder Watchdog is to be employed for extra
safety, available for CyberImpact compatible devices only. If
the communication between the device and any of its individual
axis is broken, the device shuts down.
ON or TRUE
Encoder Watchdog is on.
OFF or FALSE
Encoder Watchdog is off.
!Use EncWatchdog for extra safety
EncWatchdog: ON
[OPTIONAL - METRIC]
Indicates the type of units that will be used in calculations.
The three options are as follows:
METRIC - Forces are given in Newtons, positions are specified
in meters, and angular displacements are measured in radians.
NORMALIZED - Range of device positons and forces is scaled
to go from 0 (min) to 1 (max). Using normalized units, every
device will have the same position and force ranges. Device-independent
force effects can be created.
NATIVE - No scaling is applied to the information received
from or passed to the device. This type of unit measurement is
extremely device-specific and is determined by the manufacturer
of the device. METRIC and NORMALIZED are the recommended types
of units for applications that will support multiple device types.
[OPTIONAL - FALSE]
Indicates whether or not tracing of HAP calls should be recorded.
Intended for debugging internal errors.
ON or TRUE
Trace is on.
OFF or FALSE
Trace is off.
!Turn on tracing for debugging, record all HAP calls
Trace: ON
[REQUIRED]
Configures each individual axis of the device in terms of min
and max range, max force, etc. These values convert the device
native units to the desired units, such as newtons, meters, etc.
All values listed below are required, in order. The #
in the Axis name is a value which represents which axis
on the device this is (zero based). For the order of physical
axes on the device, refer to the device specific documentation.
In Version 2.0, data for valuators are reported as extra axes.
For this reason, extra Axis lines are required to specify
information about the valuators. All force fields in the values
should be zero for valuators.
#.#
Real number > 0.0.
Encoder ticks per unit of measure.
#.#
Real number. Minimum
value returned for that axis.
#.#
Real number. Maximum
value returned for that axis.
#.#
Real number > 0.0.
Force per unit. Depends on motors used on device.
#.#
Real number. Force offset
to shift range of forces.
#.#
Real number> 0.0. Maximum force
output.
!ticksPerUnit,minUnits,maxUnits,forcePerVolt,forceOffset,maxForce
Axis0: 3055.77749 -0.6981316 0.6981316 0.00030619 0.000000
0.9492
[OPTIONAL - 1.0 1.0]
Configures coefficients for velocity, acceleration, and force
derivations for each axis. See the next section for a further
description of these values and their effect. All values listed
below are required, in order. The # in the Coeff
name is a value which represents which axis on the device this
is. For the order of physical axes on the device, refer to the
device specific documentation.
#.#
Real number between 0.0 and
1.0. Velocity derivation coefficient.
#.#
Real number between 0.0 and
1.0. Acceleration derivation coefficient.
!velocity, acceleration
Coeff1: 0.2 0.2
[OPTIONAL - 0.0]
Sets the home position for each axis. All values listed below
are required, in order. The # in the Home name is
a value which represents which axis on the device this is (zero
based). For the order of physical axes on the device, refer to
the device specific documentation.
#.#
Real number. Value is in units
of measure determined by the Axis# Name/Value pair.
!'Home' position for each axis
!(optional, defaults are 0.0)
Home0: 0.000000
[OPTIONAL - FALSE]
Indicates whether a 'nub' is being used. A 'nub' refers to a loopback
device that attaches to the serial port. When used, it allows
testing of the device server for communication to a CyberImpact
device without actually having the device, but in all other aspects
the device server runs as if connected to a real device. Useful
for debugging serial port problems. When used, the NumAxes
Name/Value pair configures how many axis the device server controls.
ON or TRUE
A nub is present.
OFF or FALSE
No nub is present.
!Use the Nub for serial port loopback debugging.
Nub: ON
[OPTIONAL - 0]
Provided to support previous custom-made CyberImpact devices.
Replaced with IMode for new devices. Indicates which communication
mode is being used. For certain applications requiring high-performance(servo
rates 500 Hz and above), modes are available that utilize special
high-performance communication. Contact Cybernet Systems for more
details. The # in Mode# is used to indicate for
each IMC# line in the config file.
The following are the available IMC modes for Mode :
#
0 Normal performance mode:
1 Special high-performance
mode:
!Use normal mode for communication on IMC connection
0. Axis, button, and valuator information.
Mode0: 0
[OPTIONAL - 0]
Available on current devices supporting CyberImpact communication
protocol. Indicates which communication mode is being used. For
certain applications requiring high-performance(servo rates 500
Hz and above), modes are available that utilize special high-performance
communication. Contact Cybernet Systems for more details. The
# in IMode# is used to indicate for each IMC#
line in the config file.
The following are the available IMC modes for IMode:
#
Special high-performance modes:
0 Axis
information only. No buttons or valuators.
1 Axis
and button information.
2 Axis
and valuator information.
3 Axis,
button, and valuator information.
Normal performance modes:
4 Axis
information only. No buttons or valuators.
5 Axis
and button information.
6 Axis
and valuator information.
7 Axis,
button, and valuator information.
!Use normal mode for communication on IMC connection
0. Axis, button, and valuator information.
IMode0: 7
[REQUIRED when DeviceType = IForce]
When DeviceType is IForce, this Name/Value is used
to set the serial port and baud rate, much like IMC for CyberImpact
devices.
COM1 or COM2
Which serial port to
use.
#
Integer number > 0, a legal baud
rate supported by the serial port and the device.
!Connect to IForce device on COM1 at 9600
IFORCE: COM1, 9600
The Coeff Name/Value pair contains information used to
generate velocity and acceleration values from the position values
taken from the joint axis encoders. Velocity is calculated by
dividing the position difference from iteration to iteration,
by the time per iteration. Acceleration is similarly calculated
by, in turn, differentiating the calculated velocities. Without
the Coeff values, velocities and accelerations would be
calculated exactly this way. These values, however, adds something
into the calculation.
!velocity, acceleration
Coeff0: 0.2 0.2
These values are used to smooth the velocity over time. Instead
of the velocity being set to dx/dt (distance over time) it uses
the following formula:
Similarly, for acceleration:
The subscript "x" is the joint axis number, so
most of the variables in the above equations are vector elements.
VDC and ADC are the velocity and acceleration derivation
coefficient vectors respectively. These values are the ones provided
in the Coeff Name/Value pairs and are all 0.2. Velocity
and Acceleration are calculated for use within any control
that uses them. LastVelocity and LastAcceleration
are the calculated velocity and acceleration from the last iteration.
Time is the current time in seconds and LastTime
is the time for the previous iteration.
The effect of all this is that the current velocity is a weighted
sum of the current and previous velocities. The effect of past
velocities (or degree of smoothing) increases as the VDC
value decreases. Similarly, current acceleration is a weighted
sum of the current and previous accelerations, and the effect
of past accelerations (or degree of smoothing) increases as the
ADC value decreases.
These parameters may be adjust if controls involving velocity or acceleration control produce unwanted oscillations. Remember, lower values produce smoother values. VDC and ADC values must be greater than zero and less than or equal to one.
Integration of force-feedback into existing and new applications
involves the development of "feels" that are coordinated
with other events occurring in the application. This mode of
physical interaction is new to most developers; the technology
has been, until now, almost exclusively in the hands of a few
hi-tech research labs.
To aid developers new to force-feedback devices, the Haptic Development Environment (HDE) tool was created. The graphical user interface allows the developer to interactively experiment with and experience force-feedback effects. Furthermore, the HDE software was designed to closely match the HAPLib API function calls described in Section 4. Therefore, the HDE software can be used for development by aiding in the selection of specific parameters that directly map into the HAPLib API calls.
The HDE software application is a "client" application.
It operates by connecting and communicating to a haptic device
"server." The device server is not running until a
connection is attempted by the HDE, which starts the device server
and then connects to it. The device server communicates by serial
port to the force-feedback device. Once begun, the device server
is continuously reading and writing to the device and communicating
to the client application.
You may need to edit the .nmr device/calibration file corresponding
to the hardware device of interest to connect with the desired
serial port. By default, devices expect to communicate via COM2.
For example, the CIJStick.nmr
file located in the $Cyb_Home\config directory contains
a segment which looks like this:
!parameters for an CyberImpact
connection (com port, baud,...)
IMC0: COM2, 115200, 3, 8, 1
Changing "COM1" to "COM2" directs the haptic device server to use comm port 2 to communicate with the force-feedback device. If a comm port other than 1 is desired, make the desired change in all .nmr files located within the $Cyb_Home\config directory.
The CyberImpactTM API supports the creation
of very complex and interesting force-feedback effects by allowing
more than one individual effect to be active at any given time.
The haptic device server is capable of handling multiple classes
of controls, and multiple instances of individual effects simultaneously.
A brief discussion of this capability is provided to aid understanding
of elements of the HDE tool that might otherwise be confusing.
The device server understands six classes of control methodologies;
direct force, position, velocity, acceleration, vibration, and
sampled control. There can be more than one control method active
at any time, and multiple individual effects for each class of
controls. For example, the developer can mix multiple instances
of force, position, velocity, acceleration, and sampled control
to create very complex force-feedback effects. The device server
will instantiate, de-instantiate, parameterize, and re-parameterize
these controls when commanded by the user application through
the API described in this document.
In the Haptic Development Environment(HDE) interface, there is
a limit on the number of effects for each type of control of 10.
When developing with the HAPLib API, much larger number of effects
may be used for each type of control. For the Sample control,
the maximum length in the HDE is 100 float values.
1. Force Control (Renders
direct force control)
2. Position Control (Renders
position dependent forces)
3. Velocity Control (Renders
velocity dependent forces)
4. Acceleration Control (Renders
acceleration dependent forces)
5. Vibration Control (Renders
oscillatory forces)
6. Sample Control (Renders
user-defined or randomly generated samples)
The terminology used above is also used throughout the HDE tool. For example, you will be using different windows within the user interface to generate/edit force controls, position controls, velocity controls, etc. A summary of the status of all effects currently defined is provided using the Effects Manager feature under the HDE's HAPLib menu (see Section 6.4). The concept of an Effect Handle is used throughout the HDE tool to define which individual effect is being considered at any moment. The value of the Effect Handle takes on values from 0 to 9 which covers the current limit of 10 instantiations per control type.
Figure 6 presents a screen shot of the HDE's main window, including
the top-level menus and button bar.
Figure 8. HDE Main Window
HDE uses four top-level menus. From left to right these menus are System, Setup, HAPLib, and Help.
The following four sections present additional information for each top-level menu. HDE's main window includes a button bar to provide shortcuts to some commonly used functions. Place the cursor over each button and you will be presented with a very brief description of the button's function. From left to right you have buttons for Connect, Disconnect, Reset, Center, Home, Print Setup, Print, and Help. The center of the main window is used to present a text to the user. Text messages are presented to inform the user of functions performed by the HDE, and to alert the user to error conditions encountered. The text may automatically be captured to a file using the logging features of the HDE (see below).
This section presents HDE functionality available from the System Menu. From top to bottom, the following menu entries are provided:
Connect
Disconnect
Reset Connection
Center
Home
Log Message
Clear Log
Save Log
Print Status
Print Setup
Exit
Each menu item performs the following function(s):
Connects the selected force-feedback device to a haptic device
server. You must connect to a device server prior to performing
some of the functions provided within the HDE. The device server
provides the low-level communication and control of the force-feedback
hardware device selected. Hence, there is not much the HDE can
due prior to establishing a connection with the server. To change
device selection, use Select Device under the Setup
Menu.
Disconnects the HDE application from the currently connected haptic device server. The device server will uninstall/clear all active effect controls before disconnecting. Hence, if you have a complicated set of control effects programmed, you will want to be careful to not disconnect unintentionally since doing so clears out all active effects.
Resets the connection between HDE and the current haptic device server by disconnecting and then re-connecting. As noted above, the device server will uninstall/clear all active effect controls before disconnecting. Hence, if you have a complicated set of control effects programmed, you will want to be careful to not reset unintentionally since doing so clears out all active effects.
Performs a common force-feedback operation called centering. The HDE software repeatedly queries the device for its current position, and uses this information to output a force to center the device to the Home position. Centering is normally not done in this manner; a position control is typically used. It is included here as a method for benchmarking the rate at which the HDE software is communicating to the haptic device server. This information is reported back to the user once the centering operation is complete.
Resets the zero, or home, position of the using the current positions on all axes. All positions reported after this command is executed are relative to the newly defined home position.
The Log Messages dialog, Figure 9, allows
the user to specify the log file name to be used to output log
messages. Also allows logging to the screen and file to be toggled
on/off. If the user chooses the Save Log option from the
System Menu, this is the file that the log messages will
be written to.

Clears the on-screen log of any previous logged messages. Clear Log does not delete any disk files, nor does it alter the content of the log file currently being written to.
Saves the current on-screen log to the file specified using Log Messages dialog. Box. If no file name has been defined using the Log Messages dialog, then the LogFileName defined in the HDE.INI file is used as the default log file name.
Prints a page of information about the application and any configuration and status information regarding the connected device server.
Sets up the default printer. All print operations are sent to the default printer.
Disconnects the application from the device server and exits. When the application disconnects from the device server, all installed controls are uninstalled/cleared.
This section presents HDE functionality available from the Setup Menu. From top to bottom, the following menu entries are provided:
Device Configuration File
Select Device
Each menu item performs the following function(s):
The Device Configuration File dialog, Figure 10,
allows the user to select the device configuration file to use.
The device configuration file is sent to the HAP_Open
function call. The application opens specific types of devices
by also specifying the device label in the HAP_Open call.
The device configuration file contains a list of device labels,
plus some additional information for each device listed.

The Device Selection dialog, Figure 11,
allows the user to select which force-feedback hardware device
is to be used. The current dialog lists four CyberImpactTM
hardware devices. Future versions of the HDE software shall be
expanded to match the growing list of devices supported under
the CyberImpactTM API. If you do not have force-feedback
hardware, select "Mouse" under the list of simulation
methods. Doing so instructs the software to treat the mouse as
a pseudo force-feedback device. This feature allows developers
to exercise this tool, and the entire API, prior to obtaining
actual force-feedback hardware.

This section presents HDE functionality available from the HAPLib Menu. From top to bottom, the following menu entries are provided:
HAP_GetButtons
HAP_GetPos, Vel, Acc
HAP_PutPos
HAP_PutVel
HAP_PutAcc
HAP_PutForce
HAP_Vibration
HAP_Sample
HAP_Play
Effects Manager
Remove All Effects
Each menu item performs the following function(s):
The HAP_GetButtons window, Figure 12, displays
information about the device's button state as retrieved from
the device server. This window is currently geared specifically
towards the CyberImpactTM Joystick device.
The CyberImpactTM Joystick has a 4-switch
(8-direction) hat button, 2 auxiliary toggles, 1 deadman toggle
as a safety to control enabling/disabling of force-feedback, and
1 analog valuator (the trigger).

HAPLib Functions Exercised
status_type * HAP_GetToggles(
MPC_communication_struct_type
*HapticID,
unsigned
char *toggle_mask,
unsigned
char *toggle_history );
status_type * HAP_GetValuators(
MPC_communication_struct_type
*HapticID,
float
*valuators);
The HAP_GetPos, Vel, Acc, Force window, Figure 13,
shows read-only values of Position, Velocity, Acceleration, and
Force obtained from the device server. The dialog shows an Update
button and a Continuous check-box. When the Continuous box is
checked, the values in the window are updated as defined by the
interval defined in the device configuration file. If the box
is not checked, then pressing the Update button will retrieve
the information one time. When the window is popped down or the
Exit button is pressed, these haptic function calls are suppressed.
This widow is especially useful when using the mouse to simulate
a force-feedback device. The forces that would be going to the
device are displayed visually, allowing the developer to evaluate
the combination of controls created.

HAPLib Functions Exercised
status_type * HAP_GetPos(
MPC_communication_struct_type
*HapticID,
float
*axes_positions );
status_type * HAP_GetVel(
MPC_communication_struct_type
*HapticID,
float
*axes_velocities );
status_type * HAP_GetAcc(
MPC_communication_struct_type
*HapticID,
float
*axes_accelerations );
status_type * HAP_GetForce(
MPC_communication_struct_type
*HapticID,
float
*axes_forces );
The HAP_PutPos window, Figure 14, allows
the user to generate force effects as a function of the position
of the axes of the force-feedback device. Numerical values that
affect the generated force effect can be entered using the scroll
bars or they can be entered directly into the text boxes at the
right. The numbers shown atop the scroll bars are the limits
of the device as defined in the device calibration file.
The difference in the devices actual position and the desired
position is calculated as the error. The error is then raised
to the exponent and multiplied by the scale to determine
the appropriate force for each axis. Each position control is
assigned its own Effect Handle integer, shown at the bottom.
As with other controls, data should be Updated before the control
is made active to ensure proper operation. This window currently
supports up to 6 degrees-of-freedom. The RPY tab allows interaction
with 3 rotational degrees-of-freedom; roll, pitch, and yaw. The
XYZ tab allows interaction with the remaining 3 translational
degrees-of-freedom. The current notation of this window maps
into the capabilities of the CyberImpactTM Joystick
(3-DOF) and the CyberImpactTM HandController
(6-DOF).

HAPLib Functions Exercised
status_type * HAP_PutPos(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_pos,
float
*duration);
status_type * HAP_PutKP(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*scaling_factors,
float
*exponents );
The HAP_PutVel window, Figure 15, allows
the user to generate force effects as a function of velocity of
the axes of the force-feedback device. Numerical values that affect
the generated force effect can be entered using the scroll bars
or they can be entered directly into the text boxes at the right.
The numbers shown atop the scroll bars are reasonable limits of
the device as defined within the HDE tool.
The difference in the devices actual velocity and the desired
velocity is calculated as the error. The error is then raised
to the exponent and multiplied by the scale to determine
the appropriate force for each axis. Each velocity control is
assigned its own Effect Handle integer, shown at the bottom.
As with other controls, data should be Updated before the control
is made active to ensure proper operation.
This window currently supports up to 6 degrees-of-freedom. The
RPY tab allows interaction with 3 rotational degrees-of-freedom;
roll, pitch, and yaw. The XYZ tab allows interaction with the
remaining 3 translational degrees-of-freedom. The current notation
of this window maps into the capabilities of the CyberImpactTM
Joystick (3-DOF) and the CyberImpactTM
HandController (6-DOF).

HAPLib Functions Exercised
status_type * HAP_PutVel(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_vel,
float
*duration);
status_type * HAP_PutKV(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*scaling_factors,
float
*exponents );
The HAP_PutAcc window, Figure 16, allows
the user to generate force effects as a function of acceleration
of the axes of the force-feedback device. Numerical values that
affect the generated force effect can be entered using the scroll
bars or they can be entered directly into the text boxes at the
right. The numbers shown atop the scroll bars are reasonable limits
of the device as defined within the HDE tool.
The difference in the devices actual acceleration and the desired
acceleration is calculated as the error. The error is then raised
to the exponent and multiplied by the scale to determine
the appropriate force for each axis. Each acceleration control
is assigned its own Effect Handle integer, shown at the bottom.
As with other controls, data should be Updated before the control
is made active to ensure proper operation.
This window currently supports up to 6 degrees-of-freedom. The
RPY tab allows interaction with 3 rotational degrees-of-freedom;
roll, pitch, and yaw. The XYZ tab allows interaction with the
remaining 3 translational degrees-of-freedom. The current notation
of this window maps into the capabilities of the CyberImpactTM
Joystick (3-DOF) and the CyberImpactTM
HandController (6-DOF).

HAPLib Functions Exercised
status_type * HAP_PutAcc(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_acc
float
*duration);
status_type * HAP_PutKA(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*scaling_factors,
float
*exponents );
The HAP_PutForce window, Figure 17, allows
the user directly control the forces sent to the individual axes
of the force-feedback device. The numerical force values can
be entered using the scroll bars or they can be entered directly
into the text boxes at the right. Each force control is assigned
its own Effect Handle integer, shown at the bottom. As with other
controls, data should be Updated before the control is made active
to ensure proper operation.
This window currently supports up to 6 degrees-of-freedom. The
RPY tab allows interaction with 3 rotational degrees-of-freedom;
roll, pitch, and yaw. The XYZ tab allows interaction with the
remaining 3 translational degrees-of-freedom. The current notation
of this window maps into the capabilities of the CyberImpactTM
Joystick (3-DOF) and the CyberImpactTM
HandController (6-DOF).

HAPLib Functions Exercised
status_type * HAP_PutForce(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*axes_force ,
float
*duration);
The HAP_Vibration window, Figure 18, allows
the user generate oscillatory force effects to each axes of the
force-feedback device. The user can input desired time,
magnitude, duration, frequency, and fade
values. Numerical values can be entered by using the horizontal
slider bars or they can be entered directly into the text boxes
on the right. Vibrations can be assigned individually to any
of the device axes by checking the appropriate check box corresponding
to the desired axis.
As with other controls, each vibration control is identified by
an Effect Handle integer, seen in the box at the bottom. Data
should be updated before the control is made active for proper
operation. Once a control has been updated and activated, the
Continuous box may be checked to continually update the control
with any data changes made in the dialog.
Check-boxes are used to identify which axes are affected. More
than one axes may be active at any given time. These checkboxes
are converted to an array of 0's and 1's which is multiplied by
the entered magnitude value to obtain the final vector, magnitude,
used in the HAPLib function calls.
This window currently supports up to 6 degrees-of-freedom. The
axes are labeled roll, pitch, yaw, X, Y, and Z. The current notation
of this window maps into the capabilities of the CyberImpactTM
Joystick (3-DOF) and the CyberImpactTM
HandController (6-DOF).

HAPLib Functions Exercised
status_type * HAP_Vibration(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
time,
float
*magnitude,
float
duration,
float
fade,
float
frequency);
The HAP_SampleEdit window, Figure 19, allows
the user to create custom haptic samples. A haptic sample is
a sequence of values that can be played over a period of time
to generate force-feedback effects based on position, velocity,
acceleration, or force type controls. The HAP_Play window
is used to actual render the custom samples.
There are 2 modes of sample creation available; Sample and Random.
If the Random box is checked, a random sample will be created
each time this sample is played. The min-magnitude, max-magnitude,
and frequency can be specified for random samples by
entering the desired values in the text boxes at the lower right
of the window. As with other controls, the Effect Handle integer
identifies the sample, enabling the user to access and modify
it later. The slider bars are not used in a Random sample and
it is normal for them to remain in a flat profile.
If Sample is chosen, the user defines the start time and
the length of the sample. These values are entered in
the text boxes at the top right of the window. Slider controls
are used to enter values for each point in time. A edit window
of 10 points are shown at a time, but the user can select which
10 are shown by adjusting the horizontal slider below the vertical
sliders. For each Sample ID (top right), the user can control
each point's value (from -1.0 to 1.0) and the length of the sample.
Pressing the Update button will completely update the sample who's
ID number is displayed in the Sample box, whether Sample or Random
is selected. The Sub-Update button will only update the 10 points
shown in the edit window, and is only functional when Sample is
checked.

HAPLib Functions Exercised
status_type * HAP_SampleEdit(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**sample_handle,
unsigned
short sample_length,
unsigned
short edit_start,
unsigned
short edit_length,
float
*sub_sample);
status_type * HAP_SampleRandom(
MPC_communication_struct_type
*HapticID,
unsigned
short sample_num,
float
min_magnitude,
float
max_magnitude,
float
frequency);
The HAP_SamplePlay window, Figure 20, allows
the user to render user-defined force-feedback effects based on
position, velocity, acceleration, or force type controls, much
as an audio file plays back a digital recording.
Using the scroll buttons about halfway down on the left, the user
can set which sample to play for each axes. The function of the
sample is also adjustable by the check boxes. A sample can be
associated with position, velocity, acceleration, or force type
controls.
Radio buttons along the top of the window allow the user to alter
the haptic sample with multiplication, addition, scaling, exponent,
ramp start, ramp end, and continuity effect values. The horizontal
scroll bars allow these adjustments to be made for each axis individually.
Time and duration values can also be specified for
each axes by entering the desired value in the text boxes on the
right.

As with other controls, each sample control is identified by an
Effect Handle integer, seen in the box at the bottom. The Default
button resets ALL values to their default values (not just the
ones currently controlled by the scroll bars). Pressing the
Update button will completely update values set by the scroll-bar
controls at the top half of the dialog.
Press the Play button to send the information in the lower half
of the dialog (sample #, time and duration BUT not control type)
to the device server. Special attention should be given to the
time parameter. As soon as the Play button is pressed,
the samples are played for the length of the time parameter.
If its value is -1, then the effect plays forever until paused
by the Active button being turned off, the effect being removed,
or a new non-negative time value being entered and sent
with the Play button.
Time should not be confused with the duration parameter,
which indicates the length of time to play 1 sample. See the HAPLib
HAP_Play call for further details.
HAPLib Functions Exercised
status_type * HAP_PlayCntl(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
unsigned
char *specification,
float
*scale_factors,
float
*exponent,
float
*ramp_start,
float
*ramp_end,
unsigned
char *continuity);
status_type * HAP_Play(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
HAP_handle_struct
**sample_handle,
float
*time,
float
*duration);
status_type * HAP_PlayMult(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*mult_vect);
status_type * HAP_PlayAdd(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle,
float
*add_vect);
The Effects Manager window, Figure 21, allows
the user to control the status of individual force-feedback effects
currently defined. The user can select any of the control types
by pressing the radio buttons at the top of the window. To view
the status of an individual force effect of the selected type,
select the corresponding Effects Handle integer in the bottom
left of the window. The status of the specified force effect
is shown by the check boxes on the bottom right. You can activate
the effect, stop the effect, or remove the effect using the Effects
Manager.

HAPLib Functions Exercised
status_type * HAP_RestartEffect(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle);
status_type * HAP_StopEffect(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle);
status_type * HAP_RemoveEffect(
MPC_communication_struct_type
*HapticID,
HAP_handle_struct
**handle);
This menu option removes ALL active effects for Position, Velocity,
Acceleration, Force, Vibration, and Sample controls. If an effect
is Active, the effect is Stopped and Removed.
This section presents HDE functionality available from the Help Menu. From top to bottom, the following menu entries are provided:
Index
Using Help
About HDE
Each menu item performs the following function(s):
Provides an index into on-line help available for the HDE tool.
Provides standard instructions for how to use on-line help.
Provides version information for the HDE tool.
NOTE: Error codes that are returned and that are not on the list are internal errors and should be referred to Cybernet Systems, along with the description string.
| Error # | Probable Causes | Possible Solution |
| Device not connected to serial port. Wrong COM port specified. Bad serial cable. Device not receiving power. | Check serial port connection. Check COM port specification in .nmr file. Check cable. Ensure power cord is plugged in. | |
| Haptic_ID pointer passed to HAP_Open( ) is not NULL. | Initialize pointer to NULL prior to calling HAP_Open( ). | |
| Device file path passed to HAP_Open( ) is incorrect or file does not exist. | Check parameter to HAP_Open( ). Verify location of HAPTIC.DEV file. | |
| Device name passed to HAP_Open( ) is not listed in HAPTIC.DEV file. | Check parameter to HAP_Open( ). | |
| Device configuration file (*.nmr) is not at location specified in HAPTIC.DEV file. | Verify location of device configuration file. | |
| Value is NULL in function call to HAP_SendSetupLine( ). Name/value pair in device config file (*.nmr) is missing the value part. | Check to see that value is not NULL. Check config file (*.nmr) to see if any values are missing from name/value pairs. | |
| Incorrect or misspelled name in name/value pair query. | Check spelling of name. Ensure name exists as a name/value pair. | |
| Attempted to create a control with a non-NULL control handle | Ensure control handles are set to NULL prior to instantiate call. | |
12515 12518 12520 12524 12526 | Trying to peek or poke a value from a control that was not created. | Ensure that the control was successfully created prior to peeking or poking data. |
12517 12519 12522 12523 12525 12528 | Parameter specified in peek or poke call is not a member of the specified control. Parameter name was misspelled in peek or poke call. | Refer to documentation to find correct parameter name for the desired control field. |
12511 12512 12530 12531 12532 | Attempted to remove, start, stop, or pause a control that was not successfully created. | Ensure that the control was successfully instantiated prior to removal, start, stop, or pause call. |
| Device Server Variable (DSV) name is NULL. | Ensure variable name exists and is spelled correctly. | |
| Invalid axis passes to HAP_Home( ) | Ensure axis number is valid for the device. | |
| Device communication was not successfully opened with HAP_Open( ) before using a HAP function call. | Ensure that HAP_Open( ) is called successfully prior to using Haptic_ID in a HAP function call. | |
| CH Force FX stick not connected.
Incorrect device specification. | Check device connection. Check parameter to HAP_Open( ). |
To effectively integrate force-feedback into applications and
to customize the forces generated by force-feedback devices, key
concepts associated with force-feedback and the devices which
display the force information must be understood. This section
contains these key concepts.
The Haptic Device Server contains the controller software for
the force-feedback hardware device. It receives instruction from
a client application (a game, simulation, etc.) . The client
application tells the device server how the device should "move".
The device server then calculates voltages for motors on the
physical device to facilitate rendering forces and/or movement.
The device server is its own process and apart from the data
flow occurring between it and the client application, executes
semi-independently from the application. You might wonder why
it is necessary for this intervening process to exist at all if
the application could just command the forces directly. To implement
force-feedback effectively, it is important to decouple the force
update rate from the rate the application is exhibiting. Effective
force-feedback requires high-rate updates that may or may not
be possible within a given application. This is why the device
server exists - to ensure that high-rate updates to force output
can be attained independent of the client application. The downside
to this twofold: 1) The underlying OS is required to have some
form of multitasking capability and 2) the OS may be limited in
how fast it can multitask between the device server and the client
application. For situations in which the previous 2 reasons become
significant, the following solution is provided.
To offer the most flexibility to the developer, a call has been
provided that allows the client to control when the device server
control loop is executed. The client drives the device server
with the HAP_DriveServo call. The client must make sure
to call this function at reasonable and regular rates if the desired
performance is to be obtained from the device. Furthermore, the
designer of the application should be aware of issues such as
when to call other HAP_* calls. For instance, calling HAP_GetPos
multiple times in between HAP_DriveServo calls is ineffective
because the device server will be reporting back the same position
until HAP_DriveServo is called. Any number of effects and
their parameters may be modified in between HAP_DriveServo
calls. However, if the user were to output to force vectors to
the same force effect(meaning the same HAP_handle_struct
* is used), only the last set of force values applied would be
output to the device when the next HAP_DriveServo is called.
If the developer requires both forces to be felt, simply using
2 separate force effects would circumvent this. Note that this
problem could arise in the TIMER version also, if force updates
are sent faster than the servo rate at which the device server
is operating.
At the core of the Cybernet device server is a control loop that
executes independently from the application. This is implemented
as a separate process thread executing a loop that, in order of
execution:
1. Receives device joint position and button/trigger updates from
the IMCs,
2. Calculates new forces using the instantiated controls,
3. Sends the forces as updates to the IMCs.
The rate at which this loop iterates partly depends on the baud
rate that will be used during the communication to and from the
IMCs. This is the next parameter of the configuration file, 115200
in this case (allowed baud rates include 4800, 9600, 19200, 38400,
57600, 115200). The rate of execution is also limited by the
time it actually takes to calculate the forces. Unless there
are an unusual number of controls instantiated, then force calculation
will not be significant.
Understanding how force-feedback devices are integrated into application
software requires a thorough understanding of how to interpret
the input from the device; position control or rate control.
Although position and rate control refer to the interpretation
of input data (Haptic devices are input as well as output devices),
the conceptual differences between these two approaches require
correspondingly different approaches for generating haptic output.
In position control mode, the physical movement of the device
is directly mapped to the movement of a virtual object. The most
common example of position control is the mapping between a mouse
and the cursor it controls. When the mouse is moved, the cursor
moves in response. For some devices (like a steering wheel) position
control is obvious and makes the application of force-feedback
very intuitive. Simply stated, the movement of the virtual object
directly corresponds to the movement of the device. Therefore,
when something like a collision occurs in the virtual world, the
corresponding forces can be easily mapped to the physical device.
Controlling a virtual object by rate control is accomplished by
mapping the displacement of the device to a rate at which the
object should move. This is how most joysticks are integrated
into games. The displacement of the device is mapped to the velocity
of the object being manipulated. This is significantly different
than position control because the device does not have to be in
motion for virtual movement to occur. In addition, rate control
is often used 1st person. That is, device motions
do not effect the position/orientation of some object on the screen,
but instead the user himself/herself (e.g. pulling the joystick
back while running a flight simulator causes the user's perceptual
viewpoint to pitch). As a result, using a force-feedback device's
input in rate control increases the abstraction between the forces
calculated in the game and the forces displayed by the device.
For example, displaying the forces of a collision in a rate control
device is abstract because the device may not be moving, but the
virtual object is. As a consequence, forces must be abstracted
to generate the collision sensation. Even with the abstract nature
of some forces on a rate control device, many interesting and
compelling haptic (touch) special effects can be created. In
fact, the abstraction provides the freedom to the game developer
to create a wide range of physical sensations.
There are four ways for a client application to command forces;
Force Control, Position Control, Velocity Control, and Acceleration
Control.
Force control is a direct commanding of force on the device (e.g.
apply 6 newtons of force on axes X). Force control is the most
rudimentary method for controlling a haptic device. Use of direct
force control demands that the application figure out the exact
forces that are to be applied to the haptic device axes. Put
simply, the application uses input data (positions, velocities,
etc.) to calculate output forces. These forces will be continuously
applied until the application updates them. Using force control
requires that any "work" required to generate interesting
forces must occur within the application. This is analogous to
forcing your application to color every pixel in every display
frame. Additionally, the forces will only be updated as fast
as the application updates them. More complex (and therefore
more interesting) control methods provide a solution for generating
useful effects that can update forces at speeds that are independent
of the application loop rate. One common example is a vibration
effect that applies forces at a periodic rate based on the parameters
given, such as frequency and magnitude.
Position, velocity, and acceleration control allow the application
to specify higher order information from which the device server
will generate forces. If the application instructs the device
server to execute a position control, the device server will generate
forces in an attempt to gravitate toward or repulse away from
a desired position. The forces are calculated using a defined
formula and are updated as fast as the device server can generate
them. Once the desired position (position control) is instantiated,
the speed of the application loop does not effect the haptic calculations.
Velocity and acceleration control are similar to position control, except that the forces are related to a desired velocity or acceleration by a specified formula.
Just as a graphics engine refreshes the screen at a specified
rate, a force-feedback device uses incoming information to determine
what forces and torques need to be applied at a defined rate (often
called the servo rate and the loop executed at the servo rate
is the servo loop). Also like graphics, if the refresh rate is
too slow, the perception of the sensor information is adversely
affected. While there are many similarities between graphics
and haptic rendering, they differ significantly in the updates
required for realistic human perception. For graphics, an update
rate of 20-30 Hz is acceptable. For force-feedback, 30 Hz can
work, but perceptual differences in performance increase all the
way up to 1,000 Hz. Unfortunately, like graphics, increasing
the update rate increases the necessary processor time. A single
486 class processor can achieve a 1,000 Hz update rate, but the
process consumes a significant portion of the CPU. As a result,
if the servo loop is to run on the same processor as a processor
intensive application, the servo rate must be lowered.
While lowering the servo rate is a reasonable thing to do to increase
the performance of an application, the programmer must understand
the detrimental effect on force-feedback device control. Figure
22 shows the servo rate's effect on force control,
vibrations, samples, and position control. For force control,
the faster the servo rate, the more accurate the approximation
of a force profile. For vibrations and samples, if the servo
rate is not as fast as the highest frequency feature, the output
will not be equivalent to the commanded output. For position
control (the commanding of the device to a position), the faster
the servo rate, the smoother the force profile to command a position.
For velocity control (the commanding of the device based on velocity)
or acceleration control (the commanding of the device based on
acceleration) the performance degradation is similar to that of
position, but worse. Because velocity and acceleration are derivatives
of position, their performance degrades exponentially with a decreased
servo rate.
The top row of Figure 22 depicts the device
server attempting to apply a steadily increasing force to one
of its axes over a period of time. Because the device server
cannot update the forces at an infinite speed, a stair-step force
profile is inevitable. As shown, the faster the servo rate, the
more accurate the force profile.

The second row of Figure 22 shows the effect
of servo rate on a vibration control. Obviously the servo rate
must be iterating at least as fast as the desired vibration.
In order to avoid harsh beat frequencies between the servo and
vibration rates, the servo rate should be at least twice the desired
vibration rate.
The last two rows of Figure 22 show how the servo rate effects position control. The position control pulls the device to a desired position. The problem is that the time lag occurring between force updates causes the device to overshoot the desired position. The larger the time lag, the farther that the device axis will get before the control can reverse the force direction to compensate. This causes unwanted vibrations to occur.
There are a few design restrictions when using the CyberImpact
SDK 2.0 release in a DOS environment. These restrictions are
as follows: