Next

CyberImpactTM

Software Development Kit

Version 2.0

18 April 97

BETA 2


Cybernet Systems Corporation




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.

  1. Cybernet may terminate this License at any time by delivering notice to you and you may terminate this License at any time by destroying or erasing all copies of the Software in your possession or control. Upon termination of this License by Cybernet, you agree to destroy or erase all copies of the Software. In the event of termination, the following sections of this License will survive: 3, 4, 5, 6, 7 and 8. This License is personal to you and you agree not to assign your rights herein. This License shall be governed by and construed in accordance with the laws of the State of Michigan and, as to matters affecting copyrights, trademarks and patents, by US federal law. This License sets forth the entire agreement between you and Cybernet.

  1. Use, duplication or disclosure by the Government is subject to restrictions set forth in subparagraphs (a) through (d) of the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when applicable, or in subparagraph (c) (1) (ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227- 7013, and in similar clauses in the NASA AR Supplement. Contractor/manufacturer is Cybernet Systems Corporation, 727 Airport Boulevard., Ann Arbor, Michigan 48108, USA.

  1. You may not download or otherwise export or re-export the Software or any underlying information or technology except in full compliance with all United States and other applicable laws and regulations. In particular, but without limitation, none of the Software or underlying information or technology may be downloaded or otherwise exported or re-exported to anyone on the US Treasury Department's list of Specially Designated Nationals or the US Commerce Department's Table of Deny Orders. By downloading the Software, you are agreeing to the foregoing and you are representing and warranting that you are not located in, under control of, or a national or resident of any such country or on any such list.

  1. CYBERNET OR ITS SUPPLIERS SHALL NOT BE LIABLE FOR (a) INCIDENTAL, CONSEQUENTIAL, SPECIAL OR INDIRECT DAMAGES OF ANY SORT, WHETHER ARISING IN TORT, CONTRACT OR OTHERWISE, EVEN IF CYBERNET HAS BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES, OR (b) FOR ANY CLAIM BY ANY OTHER PARTY. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO THE EXTENT THAT APPLICABLE LAW PROHIBITS SUCH LIMITATION. FURTHERMORE, SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION AND EXCLUSION MAY NOT APPLY TO YOU.

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.

Table of Contents
  • 1. INTRODUCTION 8
  • 1.1 About CyberImpact 8
  • 1.2 About Force-Feedback 9
  • 1.3 About This Manual 10
  • 1.4 CyberImpactTM Software 10
  • 1.5 Future Releases 13
  • 1.6 Revision History 13
  • 1.7 Questions and Comments 13
  • 2. Windows95 or DOS INSTALLATION 14
  • 2.1 System Requirements 14
  • 2.2 Software Installation 14
  • 2.2.1 config\ 14
  • 2.2.2 dll\ 15
  • 2.2.3 lib\ 15
  • 2.2.4 include\ 15
  • 2.2.5 hde\ 15
  • 2.2.6 test\ 15
  • 2.2.7 doc\ 16
  • 2.3 Selecting a COM Port 16
  • 2.4 Testing the Installation 16
  • 2.4.1 Setup 16
  • 2.4.2 Running test.exe 17
  • 2.4.3 Choosing an Effect from the Test Menu 18
  • 2.5 Making an Executable 20
  • 2.5.1 Compiling under Windows 95(for Microsoft Visual C++ 2.0 or greater) 20
  • 2.5.2 Compiling under Windows 95(for Watcom 10.5 or greater) 20
  • 2.5.3 Compiling under 32-Bit MS-DOS (Watcom 10.5 or greater) 21
  • 3. SGI-IRIX 5.3 or FreeBSD INSTALLATION 21
  • 3.1 System Requirements 21
  • 3.2 Software Installation 21
  • 3.2.1 config\ 21
  • 3.2.2 lib\ 22
  • 3.2.3 include\ 22
  • 3.2.4 test\ 22
  • 3.2.5 doc\ 22
  • 3.3 Selecting a COM Port 22
  • 3.4 Testing the Installation 23
  • 3.4.1 Setup 23
  • 3.4.2 Running Test 23
  • 3.4.3 Choosing an Effect from the Test Menu 25
  • 3.5 Making an Executable 26
  • 3.5.1 Compiling under SGI-IRIX 5.3 or FreeBSD 26
  • 4. Client Server Model 27
  • 5. FORCE-FEEDBACK EFFECTS LIBRARY (FFELib) 28
  • 5.1 Common Elements 28
  • 5.1.1 Return Error Structure 28
  • 5.1.2 Communication Structure 28
  • 5.1.3 Haptic Device Framework 29
  • 5.1.4 Units of Measure 29
  • 5.1.5 Effect Handles 29
  • 5.1.6 Notation and Conventions 30
  • 5.2 FFELib Functions 31
  • 5.2.1 FFE_Force 32
  • 5.2.2 FFE_Impulse 33
  • 5.2.3 FFE_Spring 35
  • 5.2.4 FFE_Damping 37
  • 5.2.5 FFE_Mass 38
  • 5.2.6 FFE_Vibration 40
  • 5.2.7 FFE_Buffet 42
  • 5.2.8 FFE_VibroKick 44
  • 5.2.9 FFE_Recoil 46
  • 5.2.10 FFE_MachineGun 48
  • 5.2.11 FFE_Wall 50
  • 5.2.12 FFE_VibrateAxes 52
  • 5.2.13 FFE_WaveAxis 54
  • 5.2.14 FFE_TimedBuffet 55
  • 6. HAPTIC LIBRARY (HAPLib) 57
  • 6.1 Common Elements 57
  • 6.1.1 Return Error Structure 57
  • 6.1.2 Communication Structure 57
  • 6.1.3 Haptic Device Framework 57
  • 6.1.4 Units of Measure 57
  • 6.1.5 Effect Handles 58
  • 6.1.6 Notation and Conventions 59
  • 6.2 HAPLib Functions 60
  • 6.2.1 HAP_Open 61
  • 6.2.2 HAP_Close 62
  • 6.2.3 HAP_CallOpen 63
  • 6.2.4 HAP_SendConfig 64
  • 6.2.5 HAP_SetupLine 65
  • 6.2.6 HAP_StartServer 66
  • 6.2.7 HAP_DriveServo 67
  • 6.2.8 HAP_GetDeviceInfo 68
  • 6.2.9 HAP_GetForce 69
  • 6.2.10 HAP_GetPos 70
  • 6.2.11 HAP_GetVel 71
  • 6.2.12 HAP_GetAcc 72
  • 6.2.13 HAP_GetToggles 73
  • 6.2.14 HAP_GetValuators 75
  • 6.2.15 HAP_PutForce 76
  • 6.2.16 HAP_PutKP 78
  • 6.2.17 HAP_PutPos 80
  • 6.2.18 HAP_PutKV 82
  • 6.2.19 HAP_PutVel 84
  • 6.2.20 HAP_PutKA 86
  • 6.2.21 HAP_PutAcc 88
  • 6.2.22 HAP_Vibration 90
  • 6.2.23 HAP_SampleEdit 93
  • 6.2.24 HAP_SampleRandom 95
  • 6.2.25 HAP_PlayCntl 97
  • 6.2.26 HAP_Play 100
  • 6.2.27 HAP_PlayMult 103
  • 6.2.28 HAP_PlayAdd 105
  • 6.2.29 HAP_SetHome 107
  • 6.2.30 HAP_RemoveEffect 108
  • 6.2.31 HAP_RemoveAllEffects 109
  • 6.2.32 HAP_RestartEffect 110
  • 6.2.33 HAP_RestartAllEffects 111
  • 6.2.34 HAP_StopEffect 112
  • 6.2.35 HAP_StopAllEffects 113
  • 6.2.36 HAP_PauseEffect 114
  • 6.2.37 HAP_PauseAllEffects 115
  • 6.2.38 HAP_Time 116
  • 6.2.39 HAP_LoadSample 117
  • 6.2.40 HAP_SendSetupLine 119
  • 6.2.41 HAP_SendSetupFile 120
  • 6.2.42 HAP_GetSetupData 121
  • 6.2.43 HAP_Home 122
  • 6.2.44 HAP_SetUnits 123
  • 6.2.45 HAP_GetUnits 125
  • 6.2.46 HAP_ SetTimeCallback 126
  • 7. CONTROLS LIBRARY (CNTRLib) 127
  • 7.1 Common Elements 127
  • 7.1.1 Return Error Structure 127
  • 7.1.2 Communication Structure 127
  • 7.1.3 Haptic Device Framework 127
  • 7.1.4 Units of Measure 127
  • 7.1.5 Effect Handles 128
  • 7.1.6 Notation and Conventions 128
  • 7.2 CNTRLib Functions 130
  • 7.2.1 CNTR_InstControl 131
  • 7.2.2 CNTR_DeInstControl 133
  • 7.2.3 CNTR_StartControl 134
  • 7.2.4 CNTR_StopControl 135
  • 7.2.5 CNTR_StartAllControls 136
  • 7.2.6 CNTR_StopAllControls 137
  • 7.2.7 CNTR_PeekAdd 138
  • 7.2.8 CNTR_PeekSend 139
  • 7.2.9 CNTR_PeekGet 140
  • 7.2.10 CNTR_Peek 141
  • 7.2.11 CNTR_PokeAdd 143
  • 7.2.12 CNTR_PokeSend 144
  • 7.2.13 CNTR_Poke 145
  • 7.2.14 CNTR_PokeAddDSV 147
  • 7.2.15 CNTR_PokeRemoveDSV 149
  • 7.3 Controls: 150
  • 7.3.1 TIME97 Control 150
  • 7.3.2 COND97 Control 151
  • 7.3.3 DEV97 Control 152
  • 7.3.4 PID97 Control 153
  • 7.3.5 WAVE97 Control 155
  • 7.3.6 SAM97 Control 156
  • 7.3.7 ENV97 Control 157
  • 7.3.8 SUM97 Control 158
  • 7.3.9 WALL97 Control 159
  • 7.3.10 FORA97 Control 160
  • 7.4 CNTRLib Example 161
  • 8. CONFIGURATION FILES 170
  • 8.1 Device Definition File 170
  • 8.2 Device Server Configuration File 170
  • 8.2.1 Version 171
  • 8.2.2 DeviceType 171
  • 8.2.3 Servo 172
  • 8.2.4 NumPAxes 172
  • 8.2.5 NumToggles 172
  • 8.2.6 NumNPAxes 173
  • 8.2.7 IMC# 173
  • 8.2.8 Timer 174
  • 8.2.9 EncWatchdog 174
  • 8.2.10 Units 174
  • 8.2.11 Trace 174
  • 8.2.12 Axis# 175
  • 8.2.13 Coeff# 175
  • 8.2.14 Home# 176
  • 8.2.15 Nub 176
  • 8.2.16 Mode# 177
  • 8.2.17 IMode# 177
  • 8.2.18 IFORCE 177
  • 8.2.19 Derivation Coefficients and Damping 178
  • 9. HAPTIC DEVELOPMENT ENVIRONMENT (HDE) 179
  • 9.1 About the HDE 179
  • 9.2 Client-Server Model 179
  • 9.3 Simultaneous Effects 179
  • 9.4 HDE User Interface 180
  • 9.4.1 System Menu 181
  • 9.4.2 Setup Menu 183
  • 9.4.3 HAPLib Menu 184
  • 9.4.4 Help Menu 199
  • 10. ERROR CODES 201
  • 11. DESIGN CONSIDERATIONS 202
  • 11.1 The Haptic Device Server 202
  • 11.2 Position Control vs. Rate Control Inputs 203
  • 11.3 Force, Position, Velocity, and Acceleration Control Outputs 203
  • 11.4 Servo Rates 204
  • 11.5 DOS Support 205
  • 11.6 Device Overheating 206
  • 12. DEVICE SPECIFIC INFORMATION 207
  • 12.1 Cybernet RealFeel Yoke 207
  • 12.2 CH Force FX Joystick 209
  • 12.3 CyberImpact Joystick 211
  • 12.4 CyberImpact FingerForcer 212
  • 12.5 Operating the Software without a Force-Feedback Device 215


  • INTRODUCTION


    A Postscript format version of this document is available at Cybernet's home page along with other information about the SDK.

    About CyberImpact

    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:

    About Force-Feedback

    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.

    About This Manual

    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

    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.


    Figure 1. The CyberImpactTM API Provides High-Level Effects Software, Powerful
    Lower-Level "Building Blocks," and a Custom Effects Development Tool While Shielding
    Developers From Hardware Device Communication and Control

    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.


    Figure 2: Haptic Device Server

    Future Releases

    The CyberImpactTM SDK contains the most advanced force feedback features available. Cybernet Systems Corporation will continue its leadership in force-feedback software by including the following functionality in future releases.

    Future releases will available on the Internet at http://www.cybernet.com. Please contact Cybernet for further details.

    Revision History

    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.
    VersionDate Comments
    1.1 (Beta)23 Jul 96First public release of CyberImpactTM SDK.
    1.1 20 Sep 96Release of 1.1 Final. Supports DOS and I-Force.[2]
    2.0 (Beta)21Mar 97Release 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.

    Questions and Comments

    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


    Windows95 or DOS INSTALLATION


    The section is a guide through the installation and testing of the Windows95 version of Cybernet Systems' CyberImpactTM SDK.

    System Requirements

    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.

    Software Installation

    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.

    config\

    This directory contains device configuration and initialization files utilized by CyberImpactTM software.

    dll\

    This directory contains Dynamic Link Libraries (DLL's) related to CyberImpactTM software.

    lib\

    This directory contains the static export libraries related CyberImpactTM software.

    include\

    This directory contains header files required to make the test executable provided with this distribution.

    hde\

    This directory contains files associated with the Haptic Development Environment (HDE) program.

    test\

    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.

    doc\

    This directory contains this document in multiple formats.

    Selecting a COM Port

    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 $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.

    Testing the Installation

    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.

    Setup

    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.

    Running test.exe

    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.


    Figure 3 Top-Level Menu for Sample Test Program, test.exe

    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

    Choosing an Effect from the Test Menu

    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.

    Making an Executable

    The CyberImpactTM SDK provides libraries and example source code to create and execute programs using either the Windows 95 operating system or MS-DOS. Due to the significant differences between these operating systems, the following OS-specific guidelines are provided to assist you in building an executable file.

    Compiling under Windows 95(for Microsoft Visual C++ 2.0 or greater)

    1.     Include the following files in the application:

    2.     Link against the following:

    1. Ensure that the provided CyberImpact Dynamic Linked Library (DLL) file is in the system path.
      NOTE: The CyberImpact.dll file (provided in $Cyb_Home\dll) is automatically copied to the location of the \Windows\System directory on your machine during Installation. It is automatically removed during the UnInstall process.
    2. Windows runtime libraries: Compile using the multithread runtime libraries
      ( /MTd - debug, /MT - release).

    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.

    Compiling under Windows 95(for Watcom 10.5 or greater)

    1.     Include the following files in the application:

    2.     Link against the following:

    1. Ensure that the provided CyberImpact Dynamic Linked Library (DLL) file is in the system path.
      NOTE: The CyberImpact.dll file (provided in $Cyb_Home\dll) is automatically copied to the location of the \Windows\System directory on your machine during Installation. It is automatically removed during the UnInstall process.

    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.

    Compiling under 32-Bit MS-DOS (Watcom 10.5 or greater)

    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.


    SGI-IRIX 5.3 or FreeBSD INSTALLATION


    The section is a guide through the installation and testing of the UNIX versions of Cybernet Systems' CyberImpactTM SDK.

    System Requirements

    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.

    Software Installation

    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.

    config\

    This directory contains device configuration and initialization files utilized by CyberImpactTM software.

    lib\

    This directory contains the static libraries related CyberImpactTM software.

    include\

    This directory contains header files required to make the test executable provided with this distribution.

    test\

    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.

    doc\

    This directory contains this document in multiple formats.

    Selecting a COM Port

    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.

    Testing the Installation

    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.

    Setup

    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.

    Running Test

    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.


    Figure 4 Top-Level Menu for Sample Test Program, test.exe

    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

    Choosing an Effect from the Test Menu

    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.

    Making an Executable

    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.

    Compiling under SGI-IRIX 5.3 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..


    Client Server Model


    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.


    FORCE-FEEDBACK EFFECTS LIBRARY (FFELib)


    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.

    Common Elements

    This section documents elements of the API that are shared by all Force-Feedback Effect (FFE) Library function calls.

    Return Error Structure

    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.

    Communication Structure

    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.

    Haptic Device Framework

    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.

    Units of Measure

    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:

    UnitAxis Dependent Description
    Second (S)NoUnit of time
    Meter (M)Translation Linear position (distance)
    M/STranslationMeters per second - linear velocity
    M/S2TranslationMeters per second per second -linear acceleration
    KGNoKilogram - mass
    NewtonTranslationKilogram-meters per second -linear force magnitude
    Radian (R)Rotational Angular position or distance
    R/SRotationalRadians per second - angular velocity
    R/S2RotationalRadians per second per second - angular acceleration
    NMRotationalNewton-Meters - angular torque

    Table 1. Units of Measure Used Within FFELib

    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.

    Effect Handles

    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.

    FunctionCan share handle with :
    HAP_PauseEffectall effects functions
    HAP_RemoveEffectall effects functions
    HAP_RestartEffectall effects functions
    HAP_StopEffectall effects functions

    Table 2. FFELib Functions That Share Effect Handles

    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.

    Notation and Conventions

    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.

    FFELib Functions

    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.

    FFE_Force

    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.

    FFE_Impulse

    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.

    FFE_Spring

    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.

    FFE_Damping

    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.

    FFE_Mass

    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.

    FFE_Vibration

    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.

    FFE_Buffet

    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).

    FFE_VibroKick

    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).

    FFE_Recoil

    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).

    FFE_MachineGun

    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).

    FFE_Wall

    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).

    FFE_VibrateAxes

    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).

    FFE_WaveAxis

    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).

    FFE_TimedBuffet

    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).


    HAPTIC LIBRARY (HAPLib)


    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.

    Common Elements

    This section documents elements of the API that are shared by all function calls.

    Return Error Structure

    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.

    Communication Structure

    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.

    Haptic Device Framework

    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.

    Units of Measure

    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:
    UnitAxis Dependent Description
    Second (S)NoUnit of time
    Meter (M)Translation Linear position (distance)
    M/STranslationMeters per second - linear velocity
    M/S2TranslationMeters per second per second -linear acceleration
    KGNoKilogram - mass
    NewtonTranslationKilogram-meters per second -linear force magnitude
    Radian (R)Rotational Angular position or distance
    R/SRotationalRadians per second - angular velocity
    R/S2RotationalRadians per second per second - angular acceleration
    NMRotationalNewton-Meters - angular torque

    Table 3. Units of Measure Used Within HAPLib

    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.

    Effect Handles

    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.

    FunctionCan share handle with :
    HAP_PutPosHAP_PutKP
    HAP_PutVelHAP_PutKV
    HAP_PutAccHAP_PutKA
    HAP_PlayHAP_PlayMult, HAP_PlayAdd, HAP_PlayCntl
    HAP_RemoveEffectall effects functions
    HAP_RestartEffectall effects functions
    HAP_StopEffectall effects functions

    Table 4. HAPLib Functions That Share Effect Handles

    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.

    Notation and Conventions

    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.

    HAPLib Functions

    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.

    HAP_Open

    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.

    HAP_Close

    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).

    HAP_CallOpen

    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).

    HAP_SendConfig

    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).

    HAP_SetupLine

    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).

    HAP_StartServer

    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).

    HAP_DriveServo

    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:

    1. Read the device positions.
    2. Compute the new forces to be output using all instantiated force effects.
    3. Output the forces to the device.

      For this call to function, the Name/Value pair Servo MUST be set to CLIENT.(See the
      documentation for Device Server configuration files for more details.) For issues in how and
      when to use this call most effectively, refer to the Design Considerations Section.

    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).

    HAP_GetDeviceInfo

    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.

    HAP_GetForce

    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).

    HAP_GetPos

    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).

    HAP_GetVel

    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).

    HAP_GetAcc

    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).

    HAP_GetToggles

    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).

    HAP_GetValuators

    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).

    HAP_PutForce

    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).

    HAP_PutKP

    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).

    HAP_PutPos

    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).

    HAP_PutKV

    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).

    HAP_PutVel

    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).

    HAP_PutKA

    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)..

    HAP_PutAcc

    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).

    HAP_Vibration

    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.


    Figure 5. Sample HAP_Vibration Call with Following Parameters:

    time: 10 sec frequency: 1 Hz duration: 0.5 fade: -0.9 magnitude[0]: 1.0 N

    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).

    HAP_SampleEdit

    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:
    Sample
    Sample Index
    0
    1
    2
    3
    4
    Magnitude
    1.3
    9.2
    0.0
    4.0
    7.0

    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.

    HAP_SampleRandom

    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.

    HAP_PlayCntl

    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.



    Figure 6. Example Ramp_Start And Ramp_End Values

    And Their Effect On A Sample Of Continuity Of 1

    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.

    HAP_Play

    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
    Sample Index
    0
    1
    2
    3
    4
    Magnitude
    0.5
    1.0
    0.0
    -0.5
    0.0

    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.



    Figure 7. Sample Being Played Back Over

    time = 2 secs and duration = 1 sec with continuity = 1

    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).

    HAP_PlayMult

    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).

    HAP_PlayAdd

    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.

    HAP_SetHome

    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).

    HAP_RemoveEffect

    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).

    HAP_RemoveAllEffects

    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).

    HAP_RestartEffect

    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).

    HAP_RestartAllEffects

    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).

    HAP_StopEffect

    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).

    HAP_StopAllEffects

    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).

    HAP_PauseEffect

    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).

    HAP_PauseAllEffects

    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).

    HAP_Time

    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.


    HAP_LoadSample

    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.

    HAP_SendSetupLine

    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.

    HAP_SendSetupFile

    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.

    HAP_GetSetupData

    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.

    HAP_Home

    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).

    HAP_SetUnits

    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).

    HAP_GetUnits

    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).

    HAP_ SetTimeCallback

    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.


    CONTROLS LIBRARY (CNTRLib)


    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.

    Common Elements

    This section documents elements of the API that are shared by all function calls.

    Return Error Structure

    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.

    Communication Structure

    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.

    Haptic Device Framework

    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.

    Units of Measure

    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:
    UnitAxis Dependent Description
    Second (S)NoUnit of time
    Meter (M)Translation Linear position (distance)
    M/STranslationMeters per second - linear velocity
    M/S2TranslationMeters per second per second -linear acceleration
    KGNoKilogram - mass
    NewtonTranslationKilogram-meters per second -linear force magnitude
    Radian (R)Rotational Angular position or distance
    R/SRotationalRadians per second - angular velocity
    R/S2RotationalRadians per second per second - angular acceleration
    NMRotationalNewton-Meters - angular torque

    Table 5. Units of Measure Used Within HAPLib

    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.

    Effect Handles

    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.


    Notation and Conventions

    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.

    CNTRLib Functions

    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.

    CNTR_InstControl

    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.

    CNTR_DeInstControl

    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.

    CNTR_StartControl

    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.

    CNTR_StopControl

    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.

    CNTR_StartAllControls

    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..

    CNTR_StopAllControls

    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..

    CNTR_PeekAdd

    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..

    CNTR_PeekSend

    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..

    CNTR_PeekGet

    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.

    CNTR_Peek

    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.

    CNTR_PokeAdd

    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.

    CNTR_PokeSend

    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..


    CNTR_Poke

    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.

    CNTR_PokeAddDSV

    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.

    CNTR_PokeRemoveDSV

    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.


    Controls:

    TIME97 Control

    Name: TIME97

    Control Parameters:

    Name(s)Quantity FormatDescription
    control_time1float The resultant "time"
    state1float 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.

    COND97 Control

    Name: COND97

    Control Parameters:

    Name(s)Quantity FormatDescription
    left1float Input to the control. The left comparison element.
    right1float Input to the control. The right comparison element.
    lgtr1float The value parameter will be set to this value if the left parameter is greater than the right parameter.
    rgtl1float The value parameter will be set to this value if the right parameter is greater than the left parameter.
    letr1float The value parameter will be set to this value if the left and right parameters are equal.
    result1float 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:

    if (left > right) result = lgtr
    if (left < right) result = rgtl
    if (left = right) result = letr

    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.


    DEV97 Control

    Name: DEV97

    Control Parameters:

    Name(s)Quantity FormatDescription
    sys_time1float Absolute time in seconds in the Device Server
    pos#axesfloat The axis positions.
    vel#axesfloat The axis velocities.
    acc#axesfloat The axis accelerations.
    togglebytes required for (bits * #toggles) charBitmask of the toggle states (0=released, 1=pressed)
    toggle_history#toggles charNumber of times each button has been pressed or released since the last query of this value.
    force#axesfloat 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.

    PID97 Control

    Name: PID97

    Control Parameters:

    Name(s)Quantity FormatDescription
    axis1float The zero-based axis number of the device.
    dfor1float Desired force.
    kf1float Force multiplier.
    dpos1float Desired Position.
    kp1float Position control scalar (-attraction, +repulsion)
    del1float Desired Velocity.
    kv1float Velocity control scalar (-attraction, +repulsion)
    dacc1float Desired Acceleration.
    ka1float Acceleration control scalar (-attraction, +repulsion)
    dint1float Desired position Integral.
    ki1float Position Integral control scalar (-attraction, +repulsion)
    result1float The result value of this control.
    possign1short Forces applied for positions with the same sign as this value, or all positions if possign is zero.
    velsign1short Forces applied for velocities with the same sign as this value, or all velocities if velsign is zero.
    accsign1short 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.

    WAVE97 Control

    Name: WAVE97

    Control Parameters:

    Name(s)Quantity FormatDescription
    type1char Specifies waveform being generated:
    0=SINE specifies a sine wave
    1=RANDOM specifies a random wave.
    loop1char 1=Repeat wave. 0=play wave only once.
    index1float Index into the wave form (e.g. time, axis position…)
    freq1float Frequency of the wave.
    result1float 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.

    SAM97 Control

    Name: SAM97

    Control Parameters:

    Name(s)Quantity FormatDescription
    name30char A character string that is the name of the sample.
    cont1char 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.
    loop1char 1=Repeat sample. 0=play sample only once.
    index1float Index into the sample form (e.g. time, axis position…)
    freq1float Frequency to play sample in sample elements per index unit (e.g. second).
    result1float 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.

    ENV97 Control

    Name: ENV97

    Control Parameters:

    Name(s)Quantity FormatDescription
    loop1char 1=Repeat envelope. 0=play envelope only once.
    index1float Index into the envelope (e.g. time, axis position…)
    attack1float Envelope attack length (ramp up to 1) Default 0.
    sustain1float Envelope sustain length (ramp up to 1) Default 0.
    decay1float Envelope decay length (ramp up to 1) Default 0.
    result1float 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.

    SUM97 Control

    Name: SUM97

    Control Parameters:

    Name(s)Quantity FormatDescription
    input10float Input variables. Default 0.0
    operation9char Operation:
    0: + addition
    1: * multiplication
    result1float 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:

    result = input0 operation0 input1 operation1 input2 ... operation8 input9
    float = float +or* float +or* float ... +or* float

    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.

    WALL97 Control

    Name: WALL97

    Control Parameters:

    Name(s)Quantity FormatDescription
    point#axesfloat coordinate of a point on the wall
    normal#axesfloat vector perpendicular to wall
    kp#axesfloat spring constant of wall
    kv#axesfloat damping constant of wall
    result#axesfloat 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.

    FORA97 Control

    Name: FORA97

    Control Parameters:

    Name(s)Quantity FormatDescription
    input#axesfloat Input variables. Default 0.0
    scale#axesfloat 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]..


    CNTRLib Example

    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:




    CONFIGURATION FILES


    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.

    Device Definition File

    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).

    Device Server Configuration File

    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.

    Version

    [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).

    DeviceType

    [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

    Servo

    [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

    NumPAxes

    [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

    NumToggles

    [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

    NumNPAxes

    [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

    IMC#

    [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

    Timer

    [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

    EncWatchdog

    [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

    Units

    [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.

    Trace

    [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

    Axis#

    [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
         

    Coeff#     

    [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

    Home#

    [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

    Nub

    [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

    Mode#

    [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

    IMode#

    [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

    IFORCE

    [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

    Derivation Coefficients and Damping

    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:

    Velocityx = VDCx (Positionx - LastPositionx / Time - LastTime) + (1.0 - VDCx) LastVelocityx
    0.0 < VDCx 1.0

    Similarly, for acceleration:

    Acceleration = ADCx (Velocityx - LastVelocityx / Time - LastTime) + (1.0 - ADCx) LastAccelerationx
    0.0 < ADCx 1.0

    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.


    HAPTIC DEVELOPMENT ENVIRONMENT (HDE)


    About the HDE

    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.

    Client-Server Model

    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.

    Simultaneous Effects

    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.

    HDE User Interface

    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).

    System Menu

    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):

    Connect

    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.

    Disconnect

    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.

    Reset Connection

    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.

    Center

    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.

    Home

    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.

    Log Messages …

    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.


    Figure 9. Log Message Dialog

    Clear Log

    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.

    Save Log

    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.

    Print Status

    Prints a page of information about the application and any configuration and status information regarding the connected device server.

    Print Setup …

    Sets up the default printer. All print operations are sent to the default printer.

    Exit

    Disconnects the application from the device server and exits. When the application disconnects from the device server, all installed controls are uninstalled/cleared.

    Setup Menu

    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):

    Device Configuration File …

    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.


    Figure 10. Device Configuration File Dialog

    Select Device …

    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.


    Figure 11. Device Selection Dialog


    HAPLib Menu

    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):

    HAP_GetButtons …

    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).


    Figure 12. HAP_GetButtons Window

    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);

    HAP_GetPos, Vel, Acc …

    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.


    Figure 13. HAP_GetPos, Vel, Acc Window

    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 );

    HAP_PutPos …

    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).


    Figure 14. HAP_PutPos Window

    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 );

    HAP_PutVel …

    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).


    Figure 15. HAP_PutVel Window

    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 );

    HAP_PutAcc …

    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).


    Figure 16. HAP_PutAcc Window

    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 );

    HAP_PutForce …

    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).


    Figure 17. HAP_PutForce Window

    HAPLib Functions Exercised

         status_type * HAP_PutForce(
              MPC_communication_struct_type *HapticID,
              HAP_handle_struct **handle,
              float *axes_force ,
              float *duration);

    HAP_Vibration …

    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).


    Figure 18. HAP_Vibration Window

    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);

    HAP_Sample …

    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.


    Figure 19. HAP_SampleEdit Window

    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);

    HAP_Play …

    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.


    Figure 20. HAP_SamplePlay Window

    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);

    Effects Manager …

    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.


    Figure 21. Effects Manager Window

    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);

    Remove All Effects

    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.

    Help Menu

    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):

    Index …

    Provides an index into on-line help available for the HDE tool.

    Using Help …

    Provides standard instructions for how to use on-line help.

    About HDE …

    Provides version information for the HDE tool.


    ERROR CODES


    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

    6041
    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.
    12006
    Haptic_ID pointer passed to HAP_Open( ) is not NULL. Initialize pointer to NULL prior to calling HAP_Open( ).
    12007
    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.
    12008
    Device name passed to HAP_Open( ) is not listed in HAPTIC.DEV file. Check parameter to HAP_Open( ).
    12010
    Device configuration file (*.nmr) is not at location specified in HAPTIC.DEV file. Verify location of device configuration file.
    12501
    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.
    12502
    Incorrect or misspelled name in name/value pair query. Check spelling of name.
    Ensure name exists as a name/value pair.
    12509
    Attempted to create a control with a non-NULL control handle Ensure control handles are set to NULL prior to instantiate call.
    12513
    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.
    12514
    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.
    12510
    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.
    12521
    Device Server Variable (DSV) name is NULL. Ensure variable name exists and is spelled correctly.
    12529
    Invalid axis passes to HAP_Home( ) Ensure axis number is valid for the device.
    12533
    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.
    5000
    CH Force FX stick not connected.
    Incorrect device specification.
    Check device connection.
    Check parameter to HAP_Open( ).


    DESIGN CONSIDERATIONS


    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

    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.

    Position Control vs. Rate Control Inputs

    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.

    Force, Position, Velocity, and Acceleration Control Outputs

    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.

    Servo Rates

    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.


    Figure 22. Effect of Servo Rate on Force Control, Vibrations, Samples, And Position Control.

    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.

    DOS Support

    There are a few design restrictions when using the CyberImpact SDK 2.0 release in a DOS environment. These restrictions are as follows:

    1. There must be a 16550AF(functional 16-b