Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • danc/MicroCART
  • snawerdt/MicroCART_17-18
  • bbartels/MicroCART_17-18
  • jonahu/MicroCART
4 results
Show changes
Showing
with 0 additions and 3608 deletions
#include <fcntl.h> // for open, O_RDONLY
#include <stdio.h> // for printf, fprintf, stderr
#include <stdlib.h> // for exit
#include <string.h> // for strcmp
#ifndef _WIN32
#include <netinet/in.h> // for ntohl
#include <unistd.h> // for close, read
#else
#include <io.h>
#endif
#include <vrpn_Connection.h> // for vrpn_HANDLERPARAM, etc
#include "vrpn_Shared.h" // for timeval, vrpn_TimevalMsecs
const int buflen = 8000;
void Usage (const char * name) {
fprintf(stderr, "Usage: %s [-n|-s] <filename>\n", name);
fprintf(stderr," -n: Print names instead of numbers.\n");
fprintf(stderr," -s: Summary only, start/end/duration\n");
}
int main (int argc, char ** argv) {
char * filename;
int name_mode = 0, summary_mode = 0;
char buffer [buflen];
vrpn_HANDLERPARAM header;
int file;
int retval;
if (argc < 2) {
Usage(argv[0]);
exit(0);
}
filename = argv[1];
if (!strcmp(argv[1], "-n")) {
filename = argv[2];
name_mode = 1;
fprintf(stderr, "FATAL ERROR: Name mode not implemented.\n");
exit(0);
} else if (!strcmp(argv[1], "-s")) {
filename = argv[2];
summary_mode = 1;
}
#ifdef _WIN32
// blech
const int oflag = O_RDONLY | O_BINARY;
#else
const int oflag = O_RDONLY;
#endif
file = open(filename, oflag);
if (file == -1) {
fprintf(stderr, "Couldn't open \"%s\".\n", filename);
exit(0);
}
struct timeval tvFirst, time;
int cEntries = 0;
while (1) {
int len;
long sender;
long type;
int len2;
retval = read(file, &header, sizeof(header));
if (retval < 0) { printf("ERROR\n"); close(file); exit(0); }
if (!retval) {
if (summary_mode) {
printf("Last timestamp in file: %ld:%ld\n", time.tv_sec, static_cast<long>(time.tv_usec));
timeval tvDuration = vrpn_TimevalDiff(time, tvFirst);
double dDuration = vrpn_TimevalMsecs(tvDuration) / 1000.0;
printf("Duration: %ld:%ld\n", tvDuration.tv_sec, static_cast<long>(tvDuration.tv_usec));
printf("%d enties over %gs = %.3fHz\n",
cEntries, dDuration, cEntries/dDuration);
} else {
printf("EOF\n");
}
close(file);
break;
}
cEntries++;
len = ntohl(header.payload_len);
time.tv_sec = ntohl(header.msg_time.tv_sec);
time.tv_usec = ntohl(header.msg_time.tv_usec);
sender = ntohl(header.sender);
type = ntohl(header.type);
if (summary_mode) {
static int first = 1;
if (first) {
printf("First timestamp in file: %ld:%ld\n", time.tv_sec, static_cast<long>(time.tv_usec));
tvFirst = time;
first = 0;
}
}
if (!summary_mode) {
if (name_mode)
printf("%ld from %ld, payload length %d\n",
type, sender, len);
else
printf("Message type %ld, sender %ld, payload length %d\n",
type, sender, len);
}
retval = read(file, buffer, len);
if (retval < 0) { printf("ERROR\n"); close(file); exit(0); }
if (!retval) { printf("EOF\n"); close(file); exit(0); }
if (summary_mode) {
continue;
}
printf(" <%d bytes> at %ld:%ld\n", retval, time.tv_sec, static_cast<long>(time.tv_usec));
switch (type) {
case vrpn_CONNECTION_SENDER_DESCRIPTION:
len2 = ntohl(* ((int *) buffer));
buffer[len2 + sizeof(int)] = 0;
printf(" The name of sender #%ld is \"%s\".\n", sender, buffer + sizeof(int));
break;
case vrpn_CONNECTION_TYPE_DESCRIPTION:
len2 = ntohl(* ((int *) buffer));
buffer[len2 + sizeof(int)] = 0;
printf(" The name of type #%ld is \"%s\".\n", sender, buffer + sizeof(int));
break;
case vrpn_CONNECTION_UDP_DESCRIPTION:
buffer[len] = 0;
printf(" UDP host is \"%s\", port %ld.\n", buffer, sender);
break;
case vrpn_CONNECTION_LOG_DESCRIPTION:
buffer[len] = 0;
printf(" Log to file \"%s\".\n", buffer);
break;
}
}
return 0;
}
/* clock_drift_estimator.C
This is a VRPN client program that will connect to a VRPN
server device use the built-in ping/poing messages from the
vrpn_BaseClass to estimate both the round-trip time for
VRPN messages and the clock drift between the client machine
and the server machine.
*/
#include <math.h> // for floor
#include <stdio.h> // for NULL, printf, fprintf, etc
#include <stdlib.h> // for exit
#include <vrpn_Shared.h> // for vrpn_gettimeofday, timeval, vrpn_TimevalSum, etc
#ifndef _WIN32_WCE
#include <signal.h> // for signal, SIGINT
#endif
#include <string.h> // for strcmp
#include <vrpn_BaseClass.h> // for vrpn_BaseClass
#include <vrpn_Connection.h> // for vrpn_Connection, etc
#include <vrpn_Tracker.h> // for vrpn_Tracker_NULL
#include "vrpn_Configure.h" // for VRPN_CALLBACK
int done = 0; // Signals that the program should exit
//-------------------------------------
// This object is used to connect to whatever server object is
// specified. The server object can be anything, a tracker, a button,
// an analog, etc. It just needs to be derived from vrpn_BaseClass
// so that it implements the ping/pong messages.
// XXX Later, make an interface to this so that it can be more generally
// useful as an estimator and move the printing up to the application.
class vrpn_Clock_Drift_Estimator : public vrpn_BaseClass {
public:
// The constructor adds another entry for the Pong message handler,
// so that we can use these responses to estimate.
vrpn_Clock_Drift_Estimator(const char * name, double min_repeat_wait_secs = 0, double estimation_interval_secs = 10, vrpn_Connection * c = NULL) :
vrpn_BaseClass(name, c),
d_doing_estimate(false),
d_doing_ping(false),
d_count(0)
{
vrpn_BaseClass::init();
if (d_connection != NULL) {
// Put in an additional handler for the pong message so that we can
// tell when we get a response.
register_autodeleted_handler(d_pong_message_id, handle_pong, this, d_sender_id);
// Initialize the member variables used in estimating the time.
// We want the estimation interval to start two seconds into the
// future to give things time to settle in and get connected.
vrpn_gettimeofday(&d_next_interval_time, NULL);
d_next_interval_time.tv_sec += 2;
d_next_ping_time = d_next_interval_time;
// Initialize the estimation interval and minimum waits used to
// tell when to do things.
if ( (min_repeat_wait_secs < 0) || (estimation_interval_secs <= 0) || (min_repeat_wait_secs > estimation_interval_secs) ) {
fprintf(stderr,"vrpn_Clock_Drift_Estimator::vrpn_Clock_Drift_Estimator(): Invalid time parameters (using 0, 10)\n");
min_repeat_wait_secs = 0;
estimation_interval_secs = 10;
}
d_min_repeat_wait.tv_sec = static_cast<long>(floor(min_repeat_wait_secs));
d_min_repeat_wait.tv_usec = static_cast<long>(floor( (min_repeat_wait_secs - d_min_repeat_wait.tv_sec) * 1e6));
d_estimation_interval.tv_sec = static_cast<long>(floor(estimation_interval_secs));
d_estimation_interval.tv_usec = static_cast<long>(floor( (min_repeat_wait_secs - d_estimation_interval.tv_sec) * 1e6));
d_last_ping_time.tv_sec = d_last_ping_time.tv_usec = 0;
}
};
~vrpn_Clock_Drift_Estimator() { return; };
/// Mainloop the connection to send the message.
void mainloop(void) {
client_mainloop();
if (d_connection) {
// Send any pings, collect any pongs.
d_connection->mainloop();
// See if it is time for the next interval to start. Start it if so.
struct timeval now;
vrpn_gettimeofday(&now, NULL);
if (vrpn_TimevalGreater(now, d_next_interval_time) || vrpn_TimevalEqual(now, d_next_interval_time)) {
// If we were doing an estimate, print the results.
if (d_doing_estimate) {
if (d_count == 0) {
fprintf(stderr,"vrpn_Clock_Drift_Estimator::mainloop(): Zero count in ping response!\n");
} else {
printf("vrpn_Clock_Drift_Estimator::mainloop(): Clock statistics for %d responses:\n", d_count);
printf(" Round-trip time: mean = %lg, min = %lg, max = %lg\n", d_sum_rtt/d_count, d_min_rtt, d_max_rtt);
printf(" Remote clock offset: mean = %lg, min = %lg, max = %lg\n", d_sum_skew/d_count, d_min_skew, d_max_skew);
}
}
// Set up for the next estimate interval.
d_doing_estimate = true;
d_next_interval_time = vrpn_TimevalSum(now, d_estimation_interval);
d_next_ping_time = now;
d_count = 0;
d_sum_rtt = 0;
d_sum_skew = 0;
}
// See if it is time to send the next ping. Send it if so.
if (vrpn_TimevalGreater(now, d_next_ping_time) | vrpn_TimevalEqual(now, d_next_ping_time)) {
vrpn_gettimeofday(&now, NULL);
//printf("XXX ping\n");
d_connection->pack_message(0, now, d_ping_message_id, d_sender_id,
NULL, vrpn_CONNECTION_RELIABLE);
d_last_ping_time = now;
// Tells the pong callback to listen to the response.
d_doing_ping = true;
// Don't do another until we hear a response.
d_next_ping_time = now;
d_next_ping_time.tv_sec += 10000;
}
// Send any pings, collect any pongs.
d_connection->mainloop();
}
};
protected:
// The interval over which to test before printing results,
// and the minimum time to wait between sending pings
struct timeval d_estimation_interval;
struct timeval d_min_repeat_wait;
// When we're going to next try a ping and when the next estimation interval starts
struct timeval d_next_ping_time;
struct timeval d_next_interval_time;
// When we last sent a ping.
struct timeval d_last_ping_time;
// Have we started doing an estimate or a ping?
bool d_doing_estimate;
bool d_doing_ping;
// Ping statistics
double d_min_rtt; // Round-trip time
double d_max_rtt;
double d_sum_rtt;
double d_min_skew; // Skew is from remote time to local time
double d_max_skew;
double d_sum_skew;
unsigned d_count;
/// No types to register beyond the ping/pong, which are done in BaseClass.
virtual int register_types(void) { return 0; };
// Report the elapsed time in seconds between the first and second time, where
// the first time is sooner. It will return a negative number if the first
// is later than the second.
static double elapsed_secs(const struct timeval &t1, const struct timeval &t2)
{
return 1e-3 * vrpn_TimevalMsecs( vrpn_TimevalDiff(t2,t1) );
};
static int VRPN_CALLBACK handle_pong(void *userdata, vrpn_HANDLERPARAM p)
{
//printf("XXX PONG\n");
vrpn_Clock_Drift_Estimator *me = static_cast<vrpn_Clock_Drift_Estimator *>(userdata);
// If we're currently estimating, then update the statistics based on
// the time of the response and the time we asked for a response.
if (me->d_doing_ping) {
struct timeval now;
vrpn_gettimeofday(&now, NULL);
// Find the round trip was by subtracting the time the last
// ping was sent from the current time.
double rtt = elapsed_secs(me->d_last_ping_time, now);
// Estimate the clock skew by assuming that the response was generated
// halfway between the sending time and now. We want to compute
// the number to add to the remote clock value to produce a time in
// the local clock value.
struct timeval interval = vrpn_TimevalDiff(now, me->d_last_ping_time);
struct timeval half_interval = vrpn_TimevalScale(interval, 0.5);
struct timeval expected = vrpn_TimevalSum(me->d_last_ping_time, half_interval);
double remote_to_local = elapsed_secs(p.msg_time, expected);
// If this is the first return, set the min and max values to the values
if (me->d_count == 0) {
me->d_min_rtt = me->d_max_rtt = rtt;
me->d_min_skew = me->d_max_skew = remote_to_local;
}
// Add these into the ongoing statistics and increment the count of
// samples.
me->d_sum_rtt += rtt;
if (rtt < me->d_min_rtt) { me->d_min_rtt = rtt; }
if (rtt > me->d_max_rtt) { me->d_max_rtt = rtt; }
me->d_sum_skew += remote_to_local;
if (remote_to_local < me->d_min_skew) { me->d_min_skew = remote_to_local; }
if (remote_to_local > me->d_max_skew) { me->d_max_skew = remote_to_local; }
me->d_count++;
// Set the time for the next ping. Mark us down as not doing an
// estimate, so we'll ignore misaligned pong responses.
me->d_next_ping_time = vrpn_TimevalSum(now, me->d_min_repeat_wait);
me->d_doing_ping = false;
}
return 0;
};
};
static vrpn_Clock_Drift_Estimator *g_clock = NULL;
static vrpn_Connection *g_connection = NULL;
static vrpn_Tracker_NULL *g_tracker = NULL;
// WARNING: On Windows systems, this handler is called in a separate
// thread from the main program (this differs from Unix). To avoid all
// sorts of chaos as the main program continues to handle packets, we
// set a done flag here and let the main program shut down in its own
// thread by calling shutdown() to do all of the stuff we used to do in
// this handler.
void handle_cntl_c(int) {
done = 1;
}
void Usage (const char * arg0) {
fprintf(stderr,
"Usage: %s server_to_use | LOCAL\n"
" server_to_use: VRPN name of device to connect to (eg: Tracker0@ioglab)\n"
" 'LOCAL' means to launch a local server on a dedicated connection\n",
arg0);
exit(0);
}
int main (int argc, char * argv [])
{
if (argc != 2) {
Usage(argv[0]);
} else {
if (strcmp(argv[1], "LOCAL") == 0) {
printf("Opening local server on dedicated connection\n");
g_connection = vrpn_create_server_connection();
g_tracker = new vrpn_Tracker_NULL("Tracker0", g_connection);
g_clock = new vrpn_Clock_Drift_Estimator("Tracker0@localhost", 0.01, 5);
} else {
printf("Connecting to server %s\n", argv[1]);
g_clock = new vrpn_Clock_Drift_Estimator(argv[1], 0.01, 5);
}
}
#ifndef _WIN32_WCE
// signal handler so logfiles get closed right
signal(SIGINT, handle_cntl_c);
#endif
/*
* main interactive loop
*/
printf("Press ^C to exit.\n");
while ( ! done ) {
g_clock->mainloop();
if (g_tracker) { g_tracker->mainloop(); }
if (g_connection) { g_connection->mainloop(); }
}
if (g_clock) { delete g_clock; g_clock = NULL; }
if (g_tracker) { delete g_tracker; g_tracker = NULL; }
if (g_connection) { delete g_connection; g_connection = NULL; }
return 0;
} /* main */
# Microsoft Developer Studio Project File - Name="clock_drift_estimator" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=clock_drift_estimator - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "clock_drift_estimator.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "clock_drift_estimator.mak" CFG="clock_drift_estimator - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "clock_drift_estimator - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "clock_drift_estimator - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "clock_drift_estimator - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "../pc_win32/client_src/clock_drift_estimatorRelease"
# PROP Intermediate_Dir "../pc_win32/client_src/clock_drift_estimatorRelease"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c /Tp
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /libpath:"../pc_win32/Release" /libpath:"../pc_win32/DLL/Release"
!ELSEIF "$(CFG)" == "clock_drift_estimator - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "../pc_win32/client_src/clock_drift_estimatorDebug"
# PROP Intermediate_Dir "../pc_win32/client_src/clock_drift_estimator/Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c /Tp
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"../pc_win32/Debug" /libpath:"../pc_win32/DLL/Debug"
!ENDIF
# Begin Target
# Name "clock_drift_estimator - Win32 Release"
# Name "clock_drift_estimator - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\clock_drift_estimator.C
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="clock_drift_estimator"
ProjectGUID="{9B18AC3E-2CA6-425A-9049-228821F3D081}"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\../pc_win32/client_src/clock_drift_estimatorDebug"
IntermediateDirectory=".\../pc_win32/client_src/clock_drift_estimator/Debug"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\../pc_win32/client_src/clock_drift_estimatorDebug/clock_drift_estimator.tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include&quot;;&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK\Include&quot;;.."
PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
PrecompiledHeaderFile=".\../pc_win32/client_src/clock_drift_estimator/Debug/clock_drift_estimator.pch"
AssemblerListingLocation=".\../pc_win32/client_src/clock_drift_estimator/Debug/"
ObjectFile=".\../pc_win32/client_src/clock_drift_estimator/Debug/"
ProgramDataBaseFileName=".\../pc_win32/client_src/clock_drift_estimator/Debug/"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile=".\../pc_win32/client_src/clock_drift_estimatorDebug/clock_drift_estimator.exe"
LinkIncremental="2"
SuppressStartupBanner="true"
AdditionalLibraryDirectories="$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib"
GenerateDebugInformation="true"
ProgramDatabaseFile=".\../pc_win32/client_src/clock_drift_estimatorDebug/clock_drift_estimator.pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\../pc_win32/client_src/clock_drift_estimatorDebug/clock_drift_estimator.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\../pc_win32/client_src/clock_drift_estimatorRelease"
IntermediateDirectory=".\../pc_win32/client_src/clock_drift_estimatorRelease"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\../pc_win32/client_src/clock_drift_estimatorRelease/clock_drift_estimator.tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include&quot;;&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK\Include&quot;;.."
PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE"
StringPooling="true"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
PrecompiledHeaderFile=".\../pc_win32/client_src/clock_drift_estimatorRelease/clock_drift_estimator.pch"
AssemblerListingLocation=".\../pc_win32/client_src/clock_drift_estimatorRelease/"
ObjectFile=".\../pc_win32/client_src/clock_drift_estimatorRelease/"
ProgramDataBaseFileName=".\../pc_win32/client_src/clock_drift_estimatorRelease/"
WarningLevel="3"
SuppressStartupBanner="true"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile=".\../pc_win32/client_src/clock_drift_estimatorRelease/clock_drift_estimator.exe"
LinkIncremental="1"
SuppressStartupBanner="true"
AdditionalLibraryDirectories="$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib"
ProgramDatabaseFile=".\../pc_win32/client_src/clock_drift_estimatorRelease/clock_drift_estimator.pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\../pc_win32/client_src/clock_drift_estimatorRelease/clock_drift_estimator.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
>
<File
RelativePath="clock_drift_estimator.C"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
CompileAs="2"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
CompileAs="2"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl"
>
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>
#include "vrpn_Sound.h"
#include <string.h>
#include <math.h>
#include <conio.h>
#include <cstdlib>
#define FASTSPEED 500
#define CIRCLERADIUS 5.0F
vrpn_Sound_Client *soundClient;
float X,Y,Z,adj;
int numconnections;
int numSounds = 0;
vrpn_Connection connection;
void MoveListener()
{
vrpn_float64 pos[3], ori[4];
char kbchar;
ori[0]=0;
ori[1]=0;
ori[2]=1;
ori[3]=1;
pos[0]=0;
pos[1]=0;
pos[2]=0;
printf(" Move Listener\n");
printf("*******************************\n");
printf(" I\n");
printf(" J K\n");
printf(" M\n");
printf("*******************************\n");
printf(" hit Q to return to main menu\n");
while(!(_kbhit() && ((_getch()=='q') || (_getch()=='Q')))){
kbchar = _getch();
if((kbchar=='i') || (kbchar=='I')){
pos[1] += .1;
(void)soundClient->setListenerPose(pos,ori);
soundClient->mainloop();
connection.mainloop();
}
if((kbchar=='j') || (kbchar=='J')){
pos[0] -= .1;
(void)soundClient->setListenerPose(pos,ori);
soundClient->mainloop();
connection.mainloop();
}
if((kbchar=='k') || (kbchar=='K')){
pos[0] += .1;
(void)soundClient->setListenerPose(pos,ori);
soundClient->mainloop();
connection.mainloop();
}
if((kbchar=='m') || (kbchar=='M')){
pos[1] -= .1;
(void)soundClient->setListenerPose(pos,ori);
soundClient->mainloop();
connection.mainloop();
}
if((kbchar=='q') || (kbchar=='Q')){
break;
}
kbchar = ' ';
}
}
static void init_sample_values()
{
Y=Z=0.0F;
X=5.0F;
adj=0.0F;
}
static void move_sample_values()
{
// handle the circle movement
adj+=(2.0F*3.14159265358979F/(((FASTSPEED+1-50.0F)*1.6F)+20.0F));
X=(float)(CIRCLERADIUS*cos(adj));
Y=(float)(CIRCLERADIUS*sin(adj));
}
void loopSound(vrpn_SoundID id)
{
float sA = 0;
vrpn_float64 position[3], orientation[4];
while(1)
{
//save old settings
position[0] = X; position[1] = Y; position[2] = Z;
orientation[0] = -X; orientation[1] = -Y; orientation[2] = Z; orientation[3] = 1;
(void)soundClient->setSoundPose(id,position, orientation);
printf(" %f %f %f \r",position[0], position[1], position[2]);
// move the sample values
move_sample_values();
/*
// calculate the delta vector
velocity[0]=X-sX;
velocity[1]=0;
velocity[2]=Z-sZ;
velocity[3]=((50.0F/300.0F)+1.0F)/1500.0F;
// restore the values to original
//X=sX;
//Z=sZ;
//adj=sA;
*/
// (void)soundClient->setSoundVelocity(id,velocity);
soundClient->mainloop();
if (X == 25.0F)
break;
vrpn_SleepMsecs(1);
}
}
void shutdown(){
printf("\nSound Client shutting down.\n");
soundClient = NULL;
}
int getOnlyDigits(){
char temp[80];
int id;
fgets(temp, sizeof(temp)-1, stdin);
for(int x=0; x<80; x++)
if(isalpha(temp[x])) return -1;
id=strtol(temp,0,10);
return id;
}
int main(int argc, char** argv)
{
char dummy[80];
vrpn_SoundID ids[100], id;
char files[100][80];
int curID = 0;
int command;
int loop = 1, i;
vrpn_int32 repeat, volume;
vrpn_float64 position[3], orientation[4], velocity[4];
vrpn_float64 pos[3], ori[4], Lvelocity[4];
numconnections = 0;
int flag = 0;
position[0] = 0; position[1] = 0; position[2] = 0;
orientation[0] = 0; orientation[1] = 0; orientation[2] = -1; orientation[3] = 1;
velocity[0] = 0; velocity[1] = 0; velocity[2] = 0; velocity[3] = 0;
pos[0] = 0;
pos[1] = 0;
pos[2] = 0;
ori[0] = 0;
ori[1] = 0;
ori[2] = 0;
ori[3] = 1;
/*
printf("Please enter the server you wish to connect to.\n");
scanf("%s", server);
printf("Please enter the sound device name you wish to connect to.\n");
scanf("%s", device);
*/
vrpn_Connection connection("localhost");
soundClient = new vrpn_Sound_Client("sound", &connection);
for(i = 0; i < 100; i++)
ids[i] = -1;
/*******************************************
*
* Client mainloop
*
*******************************************/
while(loop)
{
vrpn_SleepMsecs(1);
soundClient->mainloop();
connection.mainloop();
if (numconnections==0 && connection.connected())
numconnections++;
if (((numconnections!=0) && (!connection.connected())) || !connection.doing_okay()) {
shutdown();
numconnections=0;
break;
}
printf("\nCurrent sounds loaded***************\n");
for(i = 0; i < numSounds; i++)
printf("Sound# %d: %s\n", ids[i], files[i]);
printf("************************************\n");
printf("\nOptions\n");
printf("1) Load Sound\n");
printf("2) Unload Sound\n");
printf("3) Play Sound\n");
printf("4) Stop Sound\n");
printf("5) Change Sound Volume\n");
printf("6) Change Sound Position\n");
printf("7) Change Sound Orientation\n");
printf("8) Change Sound Velocity\n");
printf("9) Change Listener Position\n");
printf("10) Change Listener Orientation\n");
printf("11) Change Listener Velocity\n");
printf("12) Loop sound around head\n");
printf("13) Move Listener\n");
printf("14) Quit\n");
printf("Choose option ");
do{
command = getOnlyDigits();
}while(command == -1);
switch(command)
{
case 1:
printf("Enter path and file to load\n");
scanf("%s", dummy);
vrpn_SoundDef SoundDef;
// 1 meter in front of listener
SoundDef.pose.position[0] = 0;
SoundDef.pose.position[1] = 0;
SoundDef.pose.position[2] = 0;
// Looking back at listener
SoundDef.pose.orientation[0] = 0;
SoundDef.pose.orientation[1] = 0;
SoundDef.pose.orientation[2] = 0;
SoundDef.pose.orientation[3] = 1;
// DirectX default for both is 360
SoundDef.cone_inner_angle = 360;
SoundDef.cone_outer_angle = 360;
// No attenuation
SoundDef.cone_gain = -60;
SoundDef.min_front_dist = .1;
SoundDef.max_front_dist = 50;
SoundDef.velocity[0] = 0;
SoundDef.velocity[1] = 0;
SoundDef.velocity[2] = 0;
ids[curID] = soundClient->loadSound(dummy, curID, SoundDef);
strcpy(files[curID++], dummy);
numSounds++;
soundClient->mainloop();
break;
case 2:
do{
printf("Enter id of sound to unload:");
id = getOnlyDigits();
}while(id == -1);
(void)soundClient->unloadSound(id);
for(i = id; i < numSounds; i++){
strcpy(files[i], files[i+1]);
}
numSounds--;
curID--;
soundClient->mainloop();
break;
case 3:
do{
printf("Enter id of sound to play:");
id = getOnlyDigits();
}while(id == -1);
do{
printf("Enter number of times to repeat. (0 = continuous) ");
repeat = getOnlyDigits();
}while(repeat == -1);
(void)soundClient->playSound(id, repeat);
soundClient->mainloop();
break;
case 4:
do{
printf("Enter ID of sound to stop ");
id = getOnlyDigits();
}while(id == -1);
(void)soundClient->stopSound(id);
soundClient->mainloop();
break;
case 5:
do{
printf("Enter ID of sound to change ");
id = getOnlyDigits();
}while(id == -1);
do{
printf("Enter value to change volume to ");
volume = getOnlyDigits();
}while(volume == -1);
(void)soundClient->setSoundVolume(id, volume);
soundClient->mainloop();
break;
case 6:
do{
printf("Enter ID of sound to change ");
id = getOnlyDigits();
}while(id == -1);
printf("Enter the new X,Y, and Z position coordinates for the sound\n");
scanf("%lf %lf %lf", &position[0], &position[1], &position[2]);
(void)soundClient->setSoundPose(id, position, orientation);
soundClient->mainloop();
break;
case 7:
do{
printf("Enter ID of sound to change ");
id = getOnlyDigits();
}while(id == -1);
printf("Enter the new X,Y, Z, and W orientation coordinates for the sound\n");
scanf("%lf %lf %lf %lf", &orientation[0], &orientation[1], &orientation[2], &orientation[3]);
(void)soundClient->setSoundPose(id, position, orientation);
soundClient->mainloop();
break;
case 8:
do{
printf("Enter ID of sound to change ");
id = getOnlyDigits();
}while(id == -1);
printf("Enter the new X,Y, and Z velocity coordinates for the sound and magnitude\n");
scanf("%lf %lf %lf %lf", &velocity[0], &velocity[1], &velocity[2], &velocity[3]);
(void)soundClient->setSoundVelocity(id,velocity);
soundClient->mainloop();
break;
case 9:
printf("Enter the new X,Y, and Z position coordinates for the listener\n");
scanf("%lf %lf %lf", &pos[0], &pos[1], &pos[2]);
(void)soundClient->setListenerPose(pos,ori);
soundClient->mainloop();
break;
case 10:
printf("Enter the new orientation quaternion for the listener\n");
scanf("%lf %lf %lf %lf", &ori[0], &ori[1], &ori[2], &ori[3]);
(void)soundClient->setListenerPose(pos, ori);
soundClient->mainloop();
break;
case 11:
printf("Enter the new X,Y, and Z velocity coordinates for the listener and magnitude\n");
scanf("%lf %lf %lf %lf", &Lvelocity[0], &Lvelocity[1], &Lvelocity[2], &Lvelocity[3]);
(void)soundClient->setListenerVelocity(Lvelocity);
soundClient->mainloop();
break;
case 12:
do{
printf("Enter ID of sound to loop");
id = getOnlyDigits();
}while(id == -1);
init_sample_values();
loopSound(id);
break;
case 13:
MoveListener();
break;
case 14:
loop = 0;
shutdown();
break;
default:
break;
}
}
return 0;
}
# Microsoft Developer Studio Project File - Name="directx_sound_client" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=directx_sound_client - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "directx_sound_client.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "directx_sound_client.mak" CFG="directx_sound_client - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "directx_sound_client - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "directx_sound_client - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "directx_sound_client - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "directx_sound_client - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib vrpn.lib quat.lib winmm.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libc" /pdbtype:sept
!ENDIF
# Begin Target
# Name "directx_sound_client - Win32 Release"
# Name "directx_sound_client - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\directx_sound_client.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "directx_sound_client"=.\directx_sound_client.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################
//
// ff_client.C - generates an interesting sinusoidal force field
//
#include <math.h> // for M_PI, cos, sin
#include <stdio.h> // for printf, NULL
#include <vrpn_Button.h> // for vrpn_BUTTONCB, etc
#include <vrpn_ForceDevice.h> // for vrpn_ForceDevice_Remote, etc
#include <vrpn_Tracker.h> // for vrpn_TRACKERCB, etc
#include "vrpn_Configure.h" // for VRPN_CALLBACK
#include "vrpn_Types.h" // for vrpn_float64
#ifndef M_PI
#define M_PI (2*asin(0.0))
#endif
#define PHANTOM_SERVER "Tracker0@localhost"
/*****************************************************************************
*
Callback handler
*
*****************************************************************************/
void VRPN_CALLBACK handle_force_change(void *userdata, const vrpn_FORCECB f)
{
static vrpn_FORCECB lr; // last report
static int first_report_done = 0;
if ((!first_report_done) ||
((f.force[0] != lr.force[0]) || (f.force[1] != lr.force[1])
|| (f.force[2] != lr.force[2]))) {
//printf("force is (%f,%f,%f)\n", f.force[0], f.force[1], f.force[2]);
}
first_report_done = 1;
lr = f;
}
void VRPN_CALLBACK handle_tracker_change(void *userdata, const vrpn_TRACKERCB t)
{
static vrpn_TRACKERCB lr; // last report
static float dist_interval_sq = 0.004f;
float *pos = (float *)userdata;
if ((lr.pos[0] - t.pos[0])*(lr.pos[0] - t.pos[0]) +
(lr.pos[1] - t.pos[1])*(lr.pos[1] - t.pos[1]) +
(lr.pos[2] - t.pos[2])*(lr.pos[2] - t.pos[2]) > dist_interval_sq){
//printf("Sensor %d is now at (%g,%g,%g)\n", t.sensor,
// t.pos[0], t.pos[1], t.pos[2]);
lr = t;
}
pos[0] = static_cast<float>(t.pos[0]);
pos[1] = static_cast<float>(t.pos[1]);
pos[2] = static_cast<float>(t.pos[2]);
}
void VRPN_CALLBACK handle_button_change(void *userdata, const vrpn_BUTTONCB b)
{
static int buttonstate = 1;
if (b.state != buttonstate) {
printf("button #%d is in state %d\n", b.button, b.state);
buttonstate = b.state;
}
*(int *)userdata = buttonstate;
}
int main(int /* argc */, char * /* argv */ [])
{
printf("Trying to connect to ForceDevice, Tracker, and Button named:\n");
printf("%s\n", PHANTOM_SERVER);
int done = 0;
float pos[3];
int forceInEffect = 0;
int forceEnabled = 0;
vrpn_ForceDevice_Remote *forceDevice;
vrpn_Tracker_Remote *tracker;
vrpn_Button_Remote *button;
/* initialize the force device */
forceDevice = new vrpn_ForceDevice_Remote(PHANTOM_SERVER);
forceDevice->register_force_change_handler(NULL, handle_force_change);
/* initialize the tracker */
tracker = new vrpn_Tracker_Remote(PHANTOM_SERVER);
tracker->register_change_handler((void *)pos, handle_tracker_change);
/* initialize the button */
button = new vrpn_Button_Remote(PHANTOM_SERVER);
button->register_change_handler((void *)&forceEnabled, handle_button_change);
// main loop
while (! done )
{
// Let the forceDevice send its messages to remote force device
forceDevice->mainloop();
// Let tracker receive position information from remote tracker
tracker->mainloop();
// Let button receive button status from remote button
button->mainloop();
if (forceEnabled) {
forceDevice->setFF_Origin(pos[0],pos[1],pos[2]);
// units = dynes
forceDevice->setFF_Force((float)cos(pos[0]*20.0*M_PI),
(float)cos(pos[1]*20.0*M_PI),(float)cos(pos[2]*20.0*M_PI));
// set derivatives of force field:
// units = dynes/meter
forceDevice->setFF_Jacobian((float)(-20.0*M_PI*sin(pos[0]*20.0*M_PI)), 0, 0,
0, (float)(-20.0*M_PI*sin(pos[1]*20.0*M_PI)), 0,
0, 0, (float)(-20.0*M_PI*sin(pos[2]*20.0*M_PI)));
forceDevice->setFF_Radius(0.05f); // 5cm radius of validity
forceDevice->sendForceField();
forceInEffect = 1;
}
else if (forceInEffect){
forceDevice->stopForceField();
forceInEffect = 0;
}
}
} /* main */
//
// forcedevice_test_client.cpp - Program to test out the various functions
// of a vrpn_ForceDevice server, inspired by Randy Heiland. First,
// it generates a box within which the user can feel around; the walls
// have different characteristics. It later generates force fields
// and instant buzzing effects.
//
// It is meant to test devices that have a button and tracker associated
// with them, because it uses these devices to set coordinate systems and
// locations for effects.
//
#include <math.h> // for fabs, sqrt
#include <stdio.h> // for printf, NULL
#include <stdlib.h> // for exit
#include <vrpn_Shared.h> // for vrpn_gettimeofday
#include <vrpn_Button.h> // for vrpn_Button_Remote, etc
#include <vrpn_ForceDevice.h> // for vrpn_ForceDevice_Remote, etc
#include <vrpn_Tracker.h> // for vrpn_TRACKERCB, etc
#include "vrpn_Configure.h" // for VRPN_CALLBACK
#include "vrpn_Connection.h" // for vrpn_Connection
#include "vrpn_Shared.h" // for timeval, vrpn_TimevalDiff, etc
#include "vrpn_Types.h" // for vrpn_float32, vrpn_int32, etc
/*****************************************************************************
*
Classes and types and such
*
*****************************************************************************/
// What type of effect the application is presenting
typedef enum { box, pointconstraint, lineconstraint, planeconstraint, forcefield, buzzing, geometry, quit } APP_STATE;
// Which Object ID for the cube geometry object
vrpn_int32 CUBE_ID = 0;
// For the display of the box, this holds a description of the parameters
// for each side.
class BoxSide {
public:
BoxSide(double ox, double oy, double oz,
double nx, double ny, double nz,
double sMult, double fMult, double dMult)
{ d_oX = (float)ox; d_oY = (float)oy; d_oZ = (float)oz;
d_nX = (float)nx; d_nY = (float)ny; d_nZ = (float)nz;
d_sMult = (float)sMult;
d_fMult = (float)fMult;
d_dMult = (float)dMult;
}
float d_oX; //< Offset from center in X direction
float d_oY; //< Offset from center in Y direction
float d_oZ; //< Offset from center in Z direction
float d_nX; //< Normal in X;
float d_nY; //< Normal in Y;
float d_nZ; //< Normal in Z;
float d_sMult; //< (signed) How many multiples of change in spring constant to add
float d_fMult; //< (signed) How many multiples of change in friction to add
float d_dMult; //< (signed) How many multiples of change in damping to add
};
/*****************************************************************************
*
Global variables
*
*****************************************************************************/
// Force device client to use to control the server.
static vrpn_ForceDevice_Remote *g_forceDevice;
// Standard stiffness of a surface, and factor to multiply/divide by when it varies
static float g_kSpring = (float)0.5; // Unit is unknown, seems to be range is 0 < value <= 1.
static float g_kSpringMult = 2;
// Standard damping of a surface, and factor to multiply/divide by when it varies
static float g_kDamping = (float)0.001; // Unit is dynes*sec/cm. Range is 0 <= value <= 0.005
static float g_kDampingMult = 2;
// Standard values for static and dynamic friction
// Factors by which to multiple/divide static and dynamic friction when they vary
static float g_sFric = (float)0.2; // Unit is fraction of normal force, range is 0 <= value <= 1
//static float g_dFric = (float)0.2; // Unit is fraction of normal force, range is 0 <= value <= 1, dynamic <= static
static float g_sFricMult = (float)2;
//static float g_dFricMult = (float)2;
// State of the application (what should be generated now).
static APP_STATE g_state = box; //< What mode are we in?
static bool g_active = false; //< Is the current mode active?
// Current position of the device
static float g_xPos = (float)0.0;
static float g_yPos = (float)0.0;
static float g_zPos = (float)0.0;
// Center position for the current effect.
static float g_xCenter = g_xPos;
static float g_yCenter = g_yPos;
static float g_zCenter = g_zPos;
// Box side descriptions. They are in the following order: -X. -Y. -Z. +X. +Y, +Z.
// They are offset by 2cm from the center. Their characteristics match those
// found in the button-press description message.
static BoxSide g_box[6] = { BoxSide(-0.02, 0.00, 0.00, 1, 0, 0, -1, 0, 0),
BoxSide( 0.00,-0.02, 0.00, 0, 1, 0, 0,-1, 0),
BoxSide( 0.00, 0.00,-0.02, 0, 0, 1, 0, 0,-1),
BoxSide( 0.02, 0.00, 0.00, -1, 0, 0, 1, 0, 0),
BoxSide( 0.00, 0.02, 0.00, 0,-1, 0, 0, 1, 0),
BoxSide( 0.00, 0.00, 0.02, 0, 0,-1, 0, 0, 1) };
// Stiffness of the point, line and plane constraint
static float g_pointConstraintStiffness = (float)100.0;
static float g_lineConstraintStiffness = (float) 300.0;
static float g_planeConstraintStiffness = (float) 500.0;
// Strength and variability of the force field
static float g_forceFieldStrength = (float)0.0;
static float g_forceFieldVaries = (float)20.0;
static float g_forceFieldRadius = (float)0.3;
/*****************************************************************************
*
Callback handlers
*
*****************************************************************************/
void VRPN_CALLBACK handle_force_change(void *userdata, const vrpn_FORCECB f)
{
/*XXX
static vrpn_FORCECB lr; // last report
static int first_report_done = 0;
if ((!first_report_done) ||
((f.force[0] != lr.force[0]) || (f.force[1] != lr.force[1])
|| (f.force[2] != lr.force[2]))) {
printf("force is (%f,%f,%f)\n", f.force[0], f.force[1], f.force[2]);
}
first_report_done = 1;
lr = f;
XXX*/
}
void VRPN_CALLBACK handle_tracker_change(void *userdata, const vrpn_TRACKERCB t)
{
// Record the current position of the device in global variables
// so that the button routine can know where to store the center
// position.
g_xPos = (float)t.pos[0];
g_yPos = (float)t.pos[1];
g_zPos = (float)t.pos[2];
// This is the workhorse function for the program.
// If we are not active, then don't do anything.
if (!g_active) { return; }
// Depending on the
// current state of the application, it generates the appropriate
// effect.
switch (g_state) {
case box:
// We let the user feel around a box whose sides have different
// characteristics as described in the print statements in the
// button-press code.
{
// Figure out which side of the box to use. The index we are computing
// should have 0 = negative X from center, 1 = negative Y from center,
// 2 = negative Z, 3 = +X, 4 = +Y, 5 = +Z. This is determined based
// on which wall we are closest to touching.
double dx = g_xPos - g_xCenter;
double dy = g_yPos - g_yCenter;
double dz = g_zPos - g_zCenter;
double magx = fabs(dx);
double magy = fabs(dy);
double magz = fabs(dz);
double magmax = magx;
if (magy > magmax) { magmax = magy; }
if (magz > magmax) { magmax = magz; }
int index;
if (magx == magmax) {
if (dx < 0) {
index = 0; // X largest mag, cursor in -X from center
} else {
index = 3; // X largest mag, cursor in +X from center
}
}
if (magy == magmax) {
if (dy < 0) {
index = 1; // Y largest mag, cursor in -Y from center
} else {
index = 4; // Y largest mag, cursor in +Y from center
}
}
if (magz == magmax) {
if (dz < 0) {
index = 2; // Z largest mag, cursor in -Z from center
} else {
index = 5; // Z largest mag, cursor in +Z from center
}
}
// Find the A,B,C coefficients for the plane, which are just
// the normal pulled from the box side descriptor
float a = g_box[index].d_nX;
float b = g_box[index].d_nY;
float c = g_box[index].d_nZ;
// Find a point on the plane, one of which is located at the
// box wall's offset from the center location. We're going to
// need this to solve for D in the plane equation.
float ppx = g_xCenter + g_box[index].d_oX;
float ppy = g_yCenter + g_box[index].d_oY;
float ppz = g_zCenter + g_box[index].d_oZ;
// Find the value of D in the plane equation that makes it zero
// at the point on the plane we just found above: this requires
// D = - (Ax + By + Cz) for that point
float d = - ( a*ppx + b*ppy + c*ppz );
// Set plane parameters. First, the plane equation and then the
// surface parameters. We raise the multiplier to the power of
// the box-description multiplier and then apply this to the surface
// parameter to vary each for different sides. A box parameter of
// 0 will therefore cause no change (multiply by one), a parameter of
// -1 will reduce by the factor and a parameter of 1 will increase
// by the parameter. NOTE: Static and dynamic friction are set equal.
g_forceDevice->set_plane(a, b, c, d);
float spring = g_kSpring, springmult = g_box[index].d_sMult;
if (springmult == 1) { spring *= g_kSpringMult; }
if (springmult == -1) { spring /= g_kSpringMult; }
g_forceDevice->setSurfaceKspring(spring);
float damping = g_kDamping, dampingmult = g_box[index].d_dMult;
if (dampingmult == 1) { damping *= g_kDampingMult; }
if (dampingmult == -1) { damping /= g_kDampingMult; }
g_forceDevice->setSurfaceKdamping(damping);
float friction = g_sFric, frictionmult = g_box[index].d_fMult;
if (frictionmult == 1) { friction *= g_sFricMult; }
if (frictionmult == -1) { friction /= g_sFricMult; }
g_forceDevice->setSurfaceFstatic(friction);
g_forceDevice->setSurfaceFdynamic(friction);
// texture and buzzing stuff:
// this turns off buzzing and texture
g_forceDevice->setSurfaceBuzzAmplitude(0.0);
g_forceDevice->setSurfaceBuzzFrequency(60.0); // Hz
g_forceDevice->setSurfaceTextureAmplitude(0.00); // meters!!!
g_forceDevice->setSurfaceTextureWavelength((float)0.01); // meters!!!
g_forceDevice->setRecoveryTime(10); // recovery occurs over 10
// force update cycles
// enable force device and send surface.
g_forceDevice->startSurface();
}
break;
case pointconstraint:
// We produce a point constraint that pulls the user back towards the place where
// they pressed the button. The strength of this constraint is stored in a global.
{
vrpn_float32 center[3] = { g_xCenter, g_yCenter, g_zCenter };
g_forceDevice->setConstraintMode(vrpn_ForceDevice::POINT_CONSTRAINT);
g_forceDevice->setConstraintKSpring(g_pointConstraintStiffness);
g_forceDevice->setConstraintPoint(center);
g_forceDevice->enableConstraint(1);
}
break;
case lineconstraint:
// We produce a point constraint that pulls the user back towards the place where
// they pressed the button. The strength of this constraint is stored in a global.
{
vrpn_float32 center[3] = { g_xCenter, g_yCenter, g_zCenter };
vrpn_float32 direction[3] = { 0, 0, 1};
g_forceDevice->setConstraintMode(vrpn_ForceDevice::LINE_CONSTRAINT);
g_forceDevice->setConstraintKSpring(g_lineConstraintStiffness);
g_forceDevice->setConstraintLinePoint(center);
g_forceDevice->setConstraintLineDirection( direction );
g_forceDevice->enableConstraint(1);
}
break;
case planeconstraint:
// We produce a point constraint that pulls the user back towards the place where
// they pressed the button. The strength of this constraint is stored in a global.
{
vrpn_float32 center[3] = { g_xCenter, g_yCenter, g_zCenter };
vrpn_float32 direction[3] = { 0, 0, 1};
g_forceDevice->setConstraintMode(vrpn_ForceDevice::PLANE_CONSTRAINT);
g_forceDevice->setConstraintKSpring(g_planeConstraintStiffness);
g_forceDevice->setConstraintPlanePoint(center);
g_forceDevice->setConstraintPlaneNormal( direction );
g_forceDevice->enableConstraint(1);
}
break;
case forcefield:
// Produce a force field pointing in +Z, starting at the center position. As the
// user moves more in the +Z direction, the field will get smaller; larger as they
// move in -Z.
{
vrpn_float32 center[3] = { g_xCenter, g_yCenter, g_zCenter };
vrpn_float32 force[3] = { 0, 0, g_forceFieldStrength };
vrpn_float32 jacobian[3][3] = { { 0, 0, 0 }, {0, 0, 0}, {g_forceFieldVaries, 0, 0} };
g_forceDevice->sendForceField(center, force, jacobian, g_forceFieldRadius);
}
break;
case buzzing:
{
// Black magic dredged up from vrpn_Phantom.C
vrpn_float32 amplitude; //<
vrpn_float32 frequency; //< Frequency of the buzzing
vrpn_float32 duration; //< Duration of the effect
vrpn_float32 direction[3]; //< Normalized?
vrpn_float32 params[6]; //< In the order shown above
vrpn_int32 effectID = 0; //< 0 means "instant buzzing effect"
// We only send this every half second; the duration of the effect is
// 1/4 second.
{ static struct timeval last_sent = {0,0};
struct timeval now;
vrpn_gettimeofday(&now, NULL);
if (vrpn_TimevalMsecs(vrpn_TimevalDiff(now, last_sent)) < 500) { break; }
last_sent = now;
}
// See how far we are from the center in meters.
vrpn_float32 offset[3] = { g_xPos - g_xCenter, g_yPos - g_yCenter, g_zPos - g_zCenter };
// Adjust the amplitude based on motion in X
amplitude = (float)(0.5 + 0.5 * (offset[0] / 0.2));
if (amplitude < 0) { amplitude = 0; }
// Adjust the frequency based on motion in Y
frequency = (float)(200 + 200 * (offset[1] / 0.2));
if (frequency < 1) { frequency = 1; }
// Duration is 1/4 second, send twice per second!
duration = (float)0.25;
// Normalize the direction from the center and put it into direction.
// Set the direction to +Z if the probe is at the center.
vrpn_float32 length = (float)sqrt(offset[0]*offset[0] + offset[1]*offset[1] + offset[2]*offset[2]);
if (length == 0) {
direction[0] = direction[1] = 0; direction[2] = 1.0;
} else {
direction[0] = offset[0] / length;
direction[1] = offset[1] / length;
direction[2] = offset[2] / length;
}
params[0] = amplitude;
params[1] = frequency;
params[2] = duration;
params[3] = direction[0];
params[4] = direction[1];
params[5] = direction[2];
g_forceDevice->setCustomEffect(effectID, params, 6);
g_forceDevice->startEffect();
}
break;
case geometry:
{
// Only do this creation once!
static bool first_time = true;
if (first_time) {
first_time = false;
} else {
break;
}
const vrpn_float32 halfwidth = static_cast<vrpn_float32>(0.02);
vrpn_float32 left = g_xCenter - halfwidth;
vrpn_float32 right = g_xCenter + halfwidth;
vrpn_float32 top = g_yCenter - halfwidth;
vrpn_float32 bottom = g_yCenter + halfwidth;
vrpn_float32 front = g_zCenter - halfwidth;
vrpn_float32 back = g_zCenter + halfwidth;
// Create the cube object, rooted in the world.
g_forceDevice->useGhost();
g_forceDevice->addObject(CUBE_ID);
// Create a set of points at the cube corners.
const vrpn_int32 LBF = 0, RBF = 1, LTF = 2, RTF = 3,
LBB = 4, RBB = 5, LTB = 6, RTB = 7;
g_forceDevice->setObjectVertex(CUBE_ID, LBF, left, bottom, front);
g_forceDevice->setObjectVertex(CUBE_ID, RBF, right, bottom, front);
g_forceDevice->setObjectVertex(CUBE_ID, LTF, left, top, front);
g_forceDevice->setObjectVertex(CUBE_ID, RTF, right, top, front);
g_forceDevice->setObjectVertex(CUBE_ID, LBB, left, bottom, back);
g_forceDevice->setObjectVertex(CUBE_ID, RBB, right, bottom, back);
g_forceDevice->setObjectVertex(CUBE_ID, LTB, left, top, back);
g_forceDevice->setObjectVertex(CUBE_ID, RTB, right, top, back);
// Create a set of triangles that are the cube faces.
// REMEMBER to use the right-hand rule for creating of the
// triangles: the normal points out of that face.
// Front
g_forceDevice->setObjectTriangle(CUBE_ID, 0, LBF, LTF, RBF);
g_forceDevice->setObjectTriangle(CUBE_ID, 1, RBF, LTF, RTF);
// Back
g_forceDevice->setObjectTriangle(CUBE_ID, 2, LBB, RBB, LTB);
g_forceDevice->setObjectTriangle(CUBE_ID, 3, RBB, RTB, LTB);
// Left
g_forceDevice->setObjectTriangle(CUBE_ID, 4, LBF, LBB, LTF);
g_forceDevice->setObjectTriangle(CUBE_ID, 5, LTF, LBB, LTB);
// Right
g_forceDevice->setObjectTriangle(CUBE_ID, 6, RBF, RTF, RBB);
g_forceDevice->setObjectTriangle(CUBE_ID, 7, RTF, RTB, RBB);
// Top
g_forceDevice->setObjectTriangle(CUBE_ID, 8, LTF, LTB, RTF);
g_forceDevice->setObjectTriangle(CUBE_ID, 9, RTF, LTB, RTB);
// Bottom
g_forceDevice->setObjectTriangle(CUBE_ID, 10, LBF, RBF, LBB);
g_forceDevice->setObjectTriangle(CUBE_ID, 11, RBF, RBB, LBB);
// Push all of the changes and make them active.
g_forceDevice->updateObjectTrimeshChanges(CUBE_ID);
}
break;
default:
break;
};
}
void VRPN_CALLBACK handle_button_change(void *userdata, const vrpn_BUTTONCB b)
{
// When the button is pressed, start things going for the state
// we are in and tell that the forces are active.
// When the button is released, switch to a new state and set things
// as they need to be set for that state. Also, tell that the
// forces are inactive.
if (b.state) {
// Forces becoming active in the current state
g_active = true;
// Set the location at which the button was pressed based on
// the last tracker position so that effects which depend on
// it can know.
g_xCenter = g_xPos;
g_yCenter = g_yPos;
g_zCenter = g_zPos;
// Set things up to generate forces in the state we are in
switch (g_state) {
case box:
printf(" -X wall will have low spring constant\n");
printf(" +X wall will have high spring constant\n");
printf(" -Y wall will have low friction\n");
printf(" +Y wall will have high friction\n");
printf(" -Z wall will have low damping\n");
printf(" +Z wall will have high damping\n");
printf("\n");
printf("Release button to stop feeling inside box\n");
break;
case pointconstraint:
printf(" You will be pulled back towards where you pressed the button\n");
printf("\n");
printf("Release button to stop point constraint\n");
break;
case lineconstraint:
printf(" You will be able to pull along a line in the z direction\n");
printf("\n");
printf("Release button to stop line constraint\n");
break;
case planeconstraint:
printf(" You will be able to pull along the x-y plane\n");
printf("\n");
printf("Release button to stop plane constraint\n");
break;
case forcefield:
printf(" You will be pushed in Z while you are within the field\n");
printf(" The push is in +Z when you move in +X, in -Z when you move in -X\n");
printf("\n");
printf("Release button to stop force field\n");
break;
case buzzing:
printf(" Amplitude will increase as you move in +X, decrease as you move in -X\n");
printf(" Frequency will increase as you move in +Y, decrease as you move in -Y\n");
printf(" Direction will be towards where the button was pressed\n");
printf("\n");
printf("Release button to stop buzzing\n");
break;
case geometry:
printf(" A cube 4cm on a side will be created, centered at your start location.\n");
printf("\n");
printf("Release button to stop feeling the cube\n");
break;
default:
break;
};
} else {
// Not doing any forces until button pressed again.
g_active = false;
// Move from the state we are in to the next state.
switch (g_state) {
case box:
g_forceDevice->stopSurface();
printf("\n");
printf("Press button to start point constraint\n");
g_state = pointconstraint;
break;
case pointconstraint:
g_forceDevice->enableConstraint(0);
printf("\n");
printf("Press button to line constraint\n");
g_state = lineconstraint;
break;
case lineconstraint:
g_forceDevice->enableConstraint(0);
printf("\n");
printf("Press button to start plane constraint\n");
g_state = planeconstraint;
break;
case planeconstraint:
g_forceDevice->enableConstraint(0);
printf("\n");
printf("Press button to start force field\n");
g_state = forcefield;
break;
case forcefield:
g_forceDevice->stopForceField();
printf("\n");
printf("Press button to start buzzing\n");
g_state = buzzing;
break;
case buzzing:
g_forceDevice->stopEffect();
printf("\n");
printf("Press button to start geometry cube!\n");
g_state = geometry;
break;
case geometry:
g_forceDevice->removeObject(CUBE_ID);
printf("\n");
printf("Qutting program!\n");
g_state = quit;
break;
default:
g_state = quit;
break;
};
}
}
int main(int argc, char *argv[])
{
vrpn_Tracker_Remote *tracker;
vrpn_Button_Remote *button;
if (argc != 2) {
printf("Usage: %s device_name\n",argv[0]);
printf(" Example: %s Phantom@localhost\n",argv[0]);
exit(-1);
}
char *device_name = argv[1];
printf("Connecting to %s:\n",device_name);
/* initialize the force device */
g_forceDevice = new vrpn_ForceDevice_Remote(device_name);
g_forceDevice->register_force_change_handler(NULL, handle_force_change);
/* initialize the tracker */
tracker = new vrpn_Tracker_Remote(device_name);
tracker->register_change_handler(NULL, handle_tracker_change);
/* initialize the button */
button = new vrpn_Button_Remote(device_name);
button->register_change_handler(NULL, handle_button_change);
// Wait until we get connected to the server.
while (!g_forceDevice->connectionPtr()->connected()) {
g_forceDevice->mainloop();
}
printf("Press forceDevice's first button to begin feeling inside a box\n");
// main loop
while ( g_state != quit )
{
// Let tracker receive position information from remote tracker
tracker->mainloop();
// Let button receive button status from remote button
button->mainloop();
// Let the forceDevice send its planes to remote force device
g_forceDevice->mainloop();
}
delete tracker;
delete button;
delete g_forceDevice;
return 0;
} /* main */
# Microsoft Developer Studio Project File - Name="forcedevice_test_client" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=forcedevice_test_client - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "forcedevice_test_client.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "forcedevice_test_client.mak" CFG="forcedevice_test_client - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "forcedevice_test_client - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "forcedevice_test_client - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "forcedevice_test_client - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /libpath:"../pc_win32/Release" /libpath:"../pc_win32/DLL/Release"
!ELSEIF "$(CFG)" == "forcedevice_test_client - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"../pc_win32/Debug" /libpath:"../pc_win32/DLL/Debug"
!ENDIF
# Begin Target
# Name "forcedevice_test_client - Win32 Release"
# Name "forcedevice_test_client - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\forcedevice_test_client.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="forcedevice_test_client"
ProjectGUID="{91042ED0-E0EC-40E1-9AEB-FD5296EA11A7}"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Debug/forcedevice_test_client.tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include&quot;;&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK\Include&quot;;..;..\..\quat"
PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
PrecompiledHeaderFile=".\Debug/forcedevice_test_client.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
BrowseInformation="1"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile=".\Debug/forcedevice_test_client.exe"
LinkIncremental="2"
SuppressStartupBanner="true"
AdditionalLibraryDirectories="$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib"
GenerateDebugInformation="true"
ProgramDatabaseFile=".\Debug/forcedevice_test_client.pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\Debug/forcedevice_test_client.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Release/forcedevice_test_client.tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include&quot;;&quot;$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK\Include&quot;;..;..\..\quat"
PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE"
StringPooling="true"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
PrecompiledHeaderFile=".\Release/forcedevice_test_client.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="true"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile=".\Release/forcedevice_test_client.exe"
LinkIncremental="1"
SuppressStartupBanner="true"
AdditionalLibraryDirectories="$(SYSTEMDRIVE)\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib"
ProgramDatabaseFile=".\Release/forcedevice_test_client.pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\Release/forcedevice_test_client.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
>
<File
RelativePath="forcedevice_test_client.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl"
>
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>
#include <vrpn_Connection.h>
#include <vrpn_ForwarderController.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// This program connects to a vrpn (Tracker) server and can be used
// to tell it to open ports for other clients to listen to and to
// start forwarding Tracker messages to them.
// Tom Hudson, October 1998
vrpn_Connection * connection;
vrpn_Forwarder_Controller * controller;
void Usage (char * s) {
fprintf(stderr, "Usage: %s location\n", s);
exit(0);
}
int main (int argc, char ** argv) {
char ib [1000];
char sb [75], tb [75];
char * qp;
char * source_location;
int port;
int len;
if (argc != 2) Usage(argv[0]);
source_location = argv[1];
connection = vrpn_get_connection_by_name (source_location);
controller = new vrpn_Forwarder_Controller (connection);
printf("Commands:\n"
" quit\n"
" open <port>\n"
" forward <port> \"<service name>\" \"<message type>\"\n");
do {
if (fgets(ib, 1000, stdin) == NULL) {
perror("Could not read line");
return -1;
}
fprintf(stderr, "Got: >%s<\n", ib);
if (!strncmp(ib, "open", 4)) {
port = atoi(ib + 5);
controller->start_remote_forwarding(port);
fprintf(stderr, "Opening %d.\n", port);
}
if (!strncmp(ib, "forward", 7)) {
port = atoi(ib + 8);
qp = strchr(ib, '\"'); // start of service name
qp++;
len = static_cast<int>(strcspn(qp, "\"")); fprintf(stderr, "Prefix len %d.\n", len);
strncpy(sb, qp, len);
sb[len] = '\0';
qp = strchr(qp, '\"'); // end of service name
qp++;
qp = strchr(qp, '\"'); // start of message type name
qp++;
len = static_cast<int>(strcspn(qp, "\"")); fprintf(stderr, "Prefix len %d.\n", len);
strncpy(tb, qp, len);
tb[len] = '\0';
qp = strchr(qp, '\"'); // end of message type name
controller->forward_message_type(port, sb, tb);
fprintf(stderr, "Forwarding %s of %s on %d.\n", tb, sb, port);
}
connection->mainloop();
} while (strncmp(ib, "quit", 4));
}
CC = gcc
CXX = g++
CFLAGS = -O
CCFLAGS = -O
VRPNDIR = $(HOME)/vrpn
VRPNINC = -I$(VRPNDIR)/include
VRPNLIB = -L$(VRPNDIR)/lib -lvrpn -lquat -lpthread
INCS = $(VRPNINC)
LIBS = $(VRPNLIB) -lm
OBJS = testvrpn.o phantom-field.o phantom-plane.o phantom-wave.o \
glwin.o glvrpn.o smdvrpn.o
BINS = testvrpn phantom-field phantom-plane phantom-wave glvrpn smdvrpn
all: $(BINS) $(OBJS)
.c.o :
$(CC) $(CCFLAGS) $(INCS) -c $*.c
.C.o :
$(CXX) $(CCFLAGS) $(INCS) -c $*.C
glwin.o : glwin.c glwin.h
$(CC) $(CCFLAGS) -DUSEOPENGL $(INCS) -c $*.c
testvrpn : testvrpn.o
$(CXX) $(CCFLAGS) $(INCS) testvrpn.o $(LIBS) -o $@
phantom-field : phantom-field.o
$(CXX) $(CCFLAGS) $(INCS) phantom-field.o $(LIBS) -o $@
phantom-plane : phantom-plane.o
$(CXX) $(CCFLAGS) $(INCS) phantom-plane.o $(LIBS) -o $@
phantom-wave : phantom-wave.o
$(CXX) $(CCFLAGS) $(INCS) phantom-wave.o $(LIBS) -o $@
glvrpn : glvrpn.o glwin.o
$(CXX) $(CCFLAGS) $(INCS) glvrpn.o glwin.o $(LIBS) -lGL -lGLU -lX11 -o $@
smdvrpn: smdvrpn.o glwin.o
$(CXX) $(CCFLAGS) $(INCS) smdvrpn.o glwin.o $(LIBS) -lGL -lGLU -lX11 -o $@
clean :
rm -f $(OBJS) $(BINS)
This directory contains test and example files for using a Phantom
force-feedback device with VRPN. They were written by John Stone
at the NIH Resource for Macromolecular Modeling and Bioinformatics at the
Beckman Institute at UIUC.
These programs are made to compile and run on an SGI. The VRPN portions of
these programs should be portable across platforms, but the openGL portions
(if any) have not been ported to Windows.
This README is made by Russ Taylor, so hopefully it is accurate.
testvrpn.C:
This opens the Phantom as a tracker, button, and forcedevice.
It prints out all tracker messages, button messages, amd force messages
(should there be force messages?)
glwin.[hc]:
Helper functions to create openGL windows quickly and easily on Unix.
glvrpn.C:
This opens and openGL window and displays the tracker as it moves
around (along with a line from the origin to the tracker). It is a good
example of how to use a VRPN tracker with openGL.
phantom-field.C:
Sends a forcefield to the Phantom when the button is held down.
phantom-plane.C:
Sends a plane to the Phantom that the user can feel, quits when the
button is pressed.
phantom-wave.c:
Sends a spatially-varying force field to the Phantom while the button
is held down.
smdvrpn.C:
Simple molecular dynamics simulation using VRPN, graphics, and haptics.
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <vrpn_ForceDevice.h>
#include <vrpn_Tracker.h>
#include <vrpn_Button.h>
#define PHANTOM_SERVER "Tracker0@localhost"
/*****************************************************************************
*
Callback handler
*
*****************************************************************************/
void handle_force_change(void *userdata, const vrpn_FORCECB f)
{
static vrpn_FORCECB lr; // last report
static int first_report_done = 0;
if ((!first_report_done) ||
((f.force[0] != lr.force[0]) || (f.force[1] != lr.force[1])
|| (f.force[2] != lr.force[2]))) {
//printf("force is (%f,%f,%f)\n", f.force[0], f.force[1], f.force[2]);
}
first_report_done = 1;
lr = f;
}
void handle_tracker_change(void *userdata, const vrpn_TRACKERCB t)
{
static vrpn_TRACKERCB lr; // last report
static float dist_interval_sq = 0.004;
float *pos = (float *)userdata;
if ((lr.pos[0] - t.pos[0])*(lr.pos[0] - t.pos[0]) +
(lr.pos[1] - t.pos[1])*(lr.pos[1] - t.pos[1]) +
(lr.pos[2] - t.pos[2])*(lr.pos[2] - t.pos[2]) + dist_interval_sq){
//printf("Sensor %d is now at (%g,%g,%g)\n", t.sensor,
// t.pos[0], t.pos[1], t.pos[2]);
lr = t;
}
pos[0] = t.pos[0];
pos[1] = t.pos[1];
pos[2] = t.pos[2];
}
void handle_button_change(void *userdata, const vrpn_BUTTONCB b)
{
static int buttonstate = 1;
if (b.state != buttonstate) {
printf("button #%ld is in state %ld\n", b.button, b.state);
buttonstate = b.state;
}
*(int *)userdata = buttonstate;
}
int main(int argc, char *argv[])
{
int done = 0;
float pos[3];
int forceInEffect = 0;
int forceEnabled = 0;
vrpn_ForceDevice_Remote *forceDevice;
vrpn_Tracker_Remote *tracker;
vrpn_Button_Remote *button;
/* initialize the force device */
forceDevice = new vrpn_ForceDevice_Remote(PHANTOM_SERVER);
forceDevice->register_change_handler(NULL, handle_force_change);
/* initialize the tracker */
tracker = new vrpn_Tracker_Remote(PHANTOM_SERVER);
tracker->register_change_handler((void *)pos, handle_tracker_change);
/* initialize the button */
button = new vrpn_Button_Remote(PHANTOM_SERVER);
button->register_change_handler((void *)&forceEnabled, handle_button_change);
// main loop
while (! done )
{
// Let the forceDevice send its messages to remote force device
forceDevice->mainloop();
// Let tracker receive position information from remote tracker
tracker->mainloop();
// Let button receive button status from remote button
button->mainloop();
if (forceEnabled) {
forceDevice->setFF_Origin(0.1,0.1,0.0);
// units = dynes
forceDevice->setFF_Force(-10.0*pos[0],-10.0*pos[1],-10.0*pos[2]);
// set derivatives of force field:
// units = dynes/meter
forceDevice->setFF_Jacobian(-10,0, 0, 0,
-10, 0, 0, 0,
-10);
forceDevice->setFF_Radius(0.2); // 20cm radius of validity
forceDevice->sendForceField();
forceInEffect = 1;
}
else if (forceInEffect){
forceDevice->stopForceField();
forceInEffect = 0;
}
}
} /* main */
#include <stdio.h>
#include "vrpn_Tracker.h"
#include "vrpn_Button.h"
#include "vrpn_ForceDevice.h"
#include "quat.h"
#include "glwin.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
typedef struct {
float x; // position
float y;
float z;
double qx, qy, qz, qw; // quaternion orientation
} posquat;
typedef struct {
int b[100];
} buttons;
void handle_tracker(void *userdata, const vrpn_TRACKERCB t) {
posquat *pq = (posquat *) userdata;
pq->x = t.pos[0];
pq->y = t.pos[1];
pq->z = t.pos[2];
pq->qx=t.quat[Q_X];
pq->qy=t.quat[Q_Y];
pq->qz=t.quat[Q_Z];
pq->qw=t.quat[Q_W];
}
void handle_button(void *userdata, const vrpn_BUTTONCB b) {
buttons *bt = (buttons *) userdata;
if (b.button < 100)
bt->b[b.button] = b.state;
printf("\nButton %3d is in state: %d \n",
b.button, b.state);
fflush(stdout);
}
void handle_force(void *userdata, const vrpn_FORCECB f) {
#if 0
static vrpn_FORCECB lr; // last report
static int first_report_done = 0;
if ((!first_report_done) || ((f.force[0] != lr.force[0]) ||
(f.force[1] != lr.force[1]) || (f.force[2] != lr.force[2])))
printf("\nForce is (%11f, %11f, %11f) \n",
f.force[0], f.force[1], f.force[2]);
first_report_done = 1;
lr = f;
#endif
}
void init_graphics(void *myglwin) {
glwin_handle_events(myglwin); // let window to open and handle events once
glwin_handle_events(myglwin); // let window to open and handle events once
// setup OpenGL state...
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, 1.0, 0.01, 10.0);
gluLookAt( 0.0, 0.1, -5.0, // eye pos
0.0, 0.0, 0.0, // look at this point
0.0, 1.0, 0.0); // up direction
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_DEPTH_TEST);
//
// Setup cute lighting parms..
//
glEnable(GL_LIGHTING);
glEnable(GL_LEQUAL);
GLfloat lpos[] = { 0.0, 0.0, -100.0, 1.0};
GLfloat lamb[] = { 0.05, 0.05, 0.05, 1.0};
GLfloat ldif[] = { 0.5, 0.5, 0.5, 1.0};
GLfloat lspc[] = { 0.5, 0.5, 0.5, 1.0};
glLightfv(GL_LIGHT0, GL_POSITION, lpos);
glLightfv(GL_LIGHT0, GL_AMBIENT, lamb);
glLightfv(GL_LIGHT0, GL_DIFFUSE, ldif);
glLightfv(GL_LIGHT0, GL_SPECULAR, lspc);
glLightf(GL_LIGHT0, GL_SHININESS, 400.0);
glEnable(GL_LIGHT0);
GLfloat mdif[] = {0.9, 0.0, 0.2, 1.0};
GLfloat mspc[] = {0.2, 0.2, 0.2, 1.0};
glMaterialfv(GL_FRONT, GL_SPECULAR, mspc);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mdif);
glEnable(GL_COLOR_MATERIAL);
}
void draw_axes() {
//
// Draw Coordinate Axes
//
glDisable(GL_LIGHTING);
glBegin(GL_LINES);
float i;
glColor3f(0.0, 1.0, 0.0);
for (i=-10; i<10; i += 0.50) {
glVertex3f( -10.0, 0.0, i);
glVertex3f( 10.0, 0.0, i);
glVertex3f( i, 0.0, -10.0);
glVertex3f( i, 0.0, 10.0);
}
glEnd();
}
void draw_tracker(posquat *pq, GLUquadricObj *qobj1, GLUquadricObj *qobj2)
{
float x, y, z;
// Convert from haptic coordinates to our scene coordinates
x=-10.0*(pq->x);
y=10.0*(pq->y);
z=-10.0*(pq->z);
double q[4] = {-(pq->qx), (pq->qy), -(pq->qz), pq->qw};
// convert the orientation from quaternion to OpenGL matrix
double rotation[16];
q_to_ogl_matrix(rotation,q);
// Draw the tracker itself
//
// First draw line to object
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
glVertex3f(0.0, 0.0, 0.0 );
glVertex3f(x, y, z);
glEnd();
glEnable(GL_LIGHTING);
glColor3f(1.0, 0.0, 0.2);
glPushMatrix();
glTranslatef(x, y, z);
glMultMatrixd(rotation);
glTranslatef(-1.5, 0.0, 0.0);
gluSphere(qobj1, 0.2, 32, 32);
glTranslatef(3.0,0.0,0.0);
gluSphere(qobj2, 0.2, 32, 32);
glPopMatrix();
}
int main(int argc, char **argv) {
char * server;
posquat *tposquat = new posquat;
buttons bpos;
vrpn_Tracker_Remote *tkr;
vrpn_Button_Remote *btn;
vrpn_ForceDevice_Remote *fdv;
if (argc < 2) {
printf("%s: Invalid parms\n", argv[0]);
printf("usage: \n");
printf(" %s Tracker0@host\n", argv[0]);
return -1;
}
server = argv[1];
printf("Opening: %s ...\n", server);
tkr = new vrpn_Tracker_Remote(server);
tkr->register_change_handler(tposquat, handle_tracker);
btn = new vrpn_Button_Remote(server);
btn->register_change_handler(&bpos, handle_button);
fdv = new vrpn_ForceDevice_Remote(server);
fdv->register_force_change_handler(NULL, handle_force);
void * myglwin = glwin_create(700, 700);
if (myglwin == NULL) {
printf("Failed to open OpenGL window!!\n");
return -1;
}
init_graphics(myglwin);
GLUquadricObj *qobj1 = gluNewQuadric();
GLUquadricObj *qobj2 = gluNewQuadric();
/*
* main interactive loop
*/
while (1) {
// Let the tracker do its thing
tkr->mainloop();
btn->mainloop();
fdv->mainloop();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw_axes();
draw_tracker(tposquat, qobj1, qobj2);
glwin_swap_buffers(myglwin);
}
} /* main */
/*
* glwin.c -- simple code for opening an OpenGL display window, and doing
* BitBlt operations or whatever...
*
*/
#include <stdio.h>
#include <stdlib.h>
#ifdef USEOPENGL
#if !defined(WIN32) && !defined(_MSC_VER)
#include <X11/Xlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include "glwin.h"
typedef struct {
int scrnum;
Display *dpy;
Window root;
Window win;
GLXContext ctx;
int width;
int height;
} oglhandle;
void * glwin_create(int width, int height) {
oglhandle * handle;
XSetWindowAttributes attr;
unsigned long mask;
XVisualInfo *visinfo;
int attrib[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None };
handle = (oglhandle *) malloc(sizeof(oglhandle));
if (handle == NULL) {
printf("failed to allocate handle\n");
return NULL;
}
handle->width = width;
handle->height = height;
handle->dpy = XOpenDisplay(getenv("DISPLAY"));
if (handle->dpy == NULL) {
printf("failed to open Display!!\n");
free(handle);
return NULL;
}
handle->scrnum = DefaultScreen( handle->dpy );
handle->root = RootWindow( handle->dpy, handle->scrnum );
visinfo = glXChooseVisual( handle->dpy, handle->scrnum, attrib );
if (visinfo == NULL) {
printf("Error: couldn't get an RGB, Double-buffered visual\n");
free(handle);
return NULL;
}
/* window attributes */
attr.background_pixel = 0;
attr.border_pixel = 0;
attr.colormap = XCreateColormap(handle->dpy, handle->root,
visinfo->visual, AllocNone);
attr.event_mask = StructureNotifyMask | ExposureMask;
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
handle->win = XCreateWindow(handle->dpy, handle->root, 0, 0, width, height,
0, visinfo->depth, InputOutput,
visinfo->visual, mask, &attr );
handle->ctx = glXCreateContext( handle->dpy, visinfo, NULL, True );
glXMakeCurrent( handle->dpy, handle->win, handle->ctx );
XStoreName(handle->dpy, handle->win,
"OpenGL Window");
XMapWindow(handle->dpy, handle->win);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glwin_swap_buffers(handle);
glClear(GL_COLOR_BUFFER_BIT);
glwin_swap_buffers(handle);
glwin_handle_events(handle);
glwin_handle_events(handle);
XFlush(handle->dpy);
return handle;
}
void glwin_destroy(void * voidhandle) {
oglhandle * handle = (oglhandle *) voidhandle;
if (handle == NULL)
return;
XUnmapWindow(handle->dpy, handle->win);
glXMakeCurrent(handle->dpy, None, NULL);
XDestroyWindow(handle->dpy, handle->win);
XCloseDisplay(handle->dpy);
}
void glwin_swap_buffers(void * voidhandle) {
oglhandle * handle = (oglhandle *) voidhandle;
if (handle != NULL)
glXSwapBuffers(handle->dpy, handle->win);
}
int glwin_handle_events(void * voidhandle) {
oglhandle * handle = (oglhandle *) voidhandle;
XEvent event;
if (handle == NULL)
return 0;
XNextEvent(handle->dpy, &event);
switch(event.type) {
case ConfigureNotify:
handle->width = event.xconfigure.width;
handle->height = event.xconfigure.height;
glShadeModel(GL_FLAT);
glViewport(0, 0, handle->width, handle->height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
/* this is upside-down for what most code thinks, but it is */
/* right side up for the ray tracer.. */
gluOrtho2D(0.0, handle->width, 0.0, handle->height);
glMatrixMode(GL_MODELVIEW);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelZoom(1.0, 1.0); /* for upside-down images that ray uses */
return 1;
}
return 0;
}
void glwin_draw_image(void * voidhandle, int xsize, int ysize,
unsigned char * img) {
glRasterPos2i(0, 0);
glDrawPixels(xsize, ysize, GL_RGB, GL_UNSIGNED_BYTE, img);
glwin_swap_buffers(voidhandle);
}
#else
/*
* WIN32 Version
*/
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "glwin.h"
typedef struct {
HWND hWnd;
HDC hDC;
HGLRC hRC;
long scrwidth;
long scrheight;
int width;
int height;
} oglhandle;
static char szAppName[] = "OpenGL";
static char szAppTitle[]="OpenGL Window";
/* XXX GET RID OF THIS!!!!! */
static oglhandle * globhandle; /* XXX GET RID OF THIS!!!!! */
/* XXX GET RID OF THIS!!!!! */
/*
* declaration of myWindowProc()
*/
LONG WINAPI myWindowProc( HWND, UINT, WPARAM, LPARAM );
static int OpenWin32Connection(oglhandle * handle) {
WNDCLASS wc;
HINSTANCE hInstance = GetModuleHandle(NULL);
/* Clear (important!) and then fill in the window class structure. */
memset(&wc, 0, sizeof(WNDCLASS));
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC) myWindowProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
wc.hbrBackground = NULL; /* Default color */
wc.lpszMenuName = NULL;
wc.lpszClassName = szAppName;
if(!RegisterClass(&wc)) {
printf("Cannot register window class.\n");
return -1;
}
handle->scrwidth = GetSystemMetrics(SM_CXSCREEN);
handle->scrheight = GetSystemMetrics(SM_CYSCREEN);
return 0;
}
static HGLRC SetupOpenGL(HWND hWnd) {
int nMyPixelFormatID;
HDC hDC;
HGLRC hRC;
static PIXELFORMATDESCRIPTOR pfd = {
sizeof (PIXELFORMATDESCRIPTOR), /* struct size */
1, /* Version number */
PFD_DRAW_TO_WINDOW /* Flags, draw to a window, */
| PFD_SUPPORT_OPENGL, /* use OpenGL */
PFD_TYPE_RGBA, /* RGBA pixel values */
24, /* 24-bit color */
0, 0, 0, /* RGB bits & shift sizes. */
0, 0, 0, /* Don't care about them */
0, 0, /* No alpha buffer info */
0, 0, 0, 0, 0, /* No accumulation buffer */
32, /* 32-bit depth buffer */
0, /* No stencil buffer */
0, /* No auxiliary buffers */
PFD_MAIN_PLANE, /* Layer type */
0, /* Reserved (must be 0) */
0, /* No layer mask */
0, /* No visible mask */
0 /* No damage mask */
};
hDC = GetDC(hWnd);
nMyPixelFormatID = ChoosePixelFormat(hDC, &pfd);
/*
* catch errors here.
* If nMyPixelFormat is zero, then there's
* something wrong... most likely the window's
* style bits are incorrect (in CreateWindow() )
* or OpenGL isn't installed on this machine
*
*/
if (nMyPixelFormatID == 0) {
printf("Error selecting OpenGL Pixel Format!!\n");
return NULL;
}
SetPixelFormat( hDC, nMyPixelFormatID, &pfd );
hRC = wglCreateContext(hDC);
ReleaseDC(hWnd, hDC);
return hRC;
}
static int myCreateWindow(oglhandle * handle,
int xpos, int ypos, int xs, int ys) {
/* Create a main window for this application instance. */
handle->hWnd =
CreateWindow(
szAppName, /* app name */
szAppTitle, /* Text for window title bar */
WS_OVERLAPPEDWINDOW /* Window style */
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS, /* NEED THESE for OpenGL calls to work! */
xpos, ypos,
xs, ys,
NULL, /* no parent window */
NULL, /* Use the window class menu. */
GetModuleHandle(NULL), /* This instance owns this window */
handle /* We don't use any extra data */
);
if (!handle->hWnd) {
printf("Couldn't Open Window!!\n");
return -1;
}
handle->hDC = GetDC(handle->hWnd);
wglMakeCurrent(handle->hDC, handle->hRC);
/* Make the window visible & update its client area */
ShowWindow( handle->hWnd, SW_SHOW); /* Show the window */
UpdateWindow( handle->hWnd ); /* Sends WM_PAINT message */
return 0;
}
LONG WINAPI myWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps; /* Paint structure. */
switch(msg) {
case WM_CREATE:
globhandle->hRC = SetupOpenGL(hwnd);
return 0;
case WM_SIZE:
// hDC = GetDC(hwnd);
wglMakeCurrent(globhandle->hDC, globhandle->hRC);
glShadeModel(GL_FLAT);
glViewport(0, 0, (GLsizei) LOWORD(lParam), (GLsizei) HIWORD (lParam));
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
/* this is upside-down for what most code thinks, but it is */
/* right side up for the ray tracer.. */
gluOrtho2D(0.0, (GLsizei) LOWORD(lParam), 0.0, (GLsizei) HIWORD (lParam));
glMatrixMode(GL_MODELVIEW);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelZoom(1.0, 1.0); /* for upside-down images that ray uses */
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_PAINT:
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void * glwin_create(int width, int height) {
oglhandle * handle;
int rc;
handle = (oglhandle *) malloc(sizeof(oglhandle));
if (handle == NULL)
return NULL;
globhandle = handle;
rc = OpenWin32Connection(handle);
if (rc != 0) {
printf("OpenWin32Connection() returned an error!\n");
free(handle);
return NULL;
}
handle->width = width;
handle->height = height;
rc = myCreateWindow(handle, 0, 0, width, height);
if (rc != 0) {
printf("CreateWindow() returned an error!\n");
free(handle);
return NULL;
}
return handle;
}
void glwin_destroy(void * voidhandle) {
oglhandle * handle = (oglhandle *) voidhandle;
wglDeleteContext(handle->hRC);
PostQuitMessage( 0 );
/* glwin_handle_events(handle); */
}
void glwin_swap_buffers(void * voidhandle) {
oglhandle * handle = (oglhandle *) voidhandle;
glFlush();
}
int glwin_handle_events(void * voidhandle) {
oglhandle * handle = (oglhandle *) voidhandle;
MSG msg;
if (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
return 1;
}
return 0;
}
void glwin_draw_image(void * voidhandle, int xsize, int ysize, unsigned char * img) {
oglhandle * handle = (oglhandle *) voidhandle;
glRasterPos2i(0, 0);
glDrawPixels(xsize, ysize, GL_RGB, GL_UNSIGNED_BYTE, img);
glwin_swap_buffers(voidhandle);
return;
}
#endif
#else
void * glwin_create(int width, int height) {
return NULL;
}
void glwin_destroy(void * voidhandle) {
return;
}
void glwin_swap_buffers(void * voidhandle) {
return;
}
int glwin_handle_events(void * voidhandle) {
return 0;
}
void glwin_draw_image(void * voidhandle, int xsize, int ysize, unsigned char * img) {
return;
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
void * glwin_create(int width, int height);
void glwin_destroy(void * voidhandle);
void glwin_swap_buffers(void * voidhandle);
int glwin_handle_events(void * voidhandle);
void glwin_draw_image(void * voidhandle, int xsize, int ysize, unsigned char * img);
#ifdef __cplusplus
}
#endif
#include <stdlib.h>
#include <stdio.h>
#include <vrpn_ForceDevice.h>
#include <vrpn_Tracker.h>
#include <vrpn_Button.h>
/*****************************************************************************
*
Callback handler
*
*****************************************************************************/
void handle_force_change(void *userdata, const vrpn_FORCECB f)
{
static vrpn_FORCECB lr; // last report
static int first_report_done = 0;
if ((!first_report_done) ||
((f.force[0] != lr.force[0]) || (f.force[1] != lr.force[1])
|| (f.force[2] != lr.force[2]))) {
//printf("force is (%f,%f,%f)\n", f.force[0], f.force[1], f.force[2]);
}
first_report_done = 1;
lr = f;
}
void handle_tracker_change(void *userdata, const vrpn_TRACKERCB t)
{
static vrpn_TRACKERCB lr; // last report
static float dist_interval_sq = 0.004;
float *pos = (float *)userdata;
if ((lr.pos[0] - t.pos[0])*(lr.pos[0] - t.pos[0]) +
(lr.pos[1] - t.pos[1])*(lr.pos[1] - t.pos[1]) +
(lr.pos[2] - t.pos[2])*(lr.pos[2] - t.pos[2]) + dist_interval_sq){
//printf("Sensor %d is now at (%g,%g,%g)\n", t.sensor,
// t.pos[0], t.pos[1], t.pos[2]);
lr = t;
}
pos[0] = t.pos[0];
pos[1] = t.pos[1];
pos[2] = t.pos[2];
}
void handle_button_change(void *userdata, const vrpn_BUTTONCB b)
{
static int buttonstate = 1;
if (b.state != buttonstate) {
printf("button #%ld is in state %ld\n", b.button, b.state);
buttonstate = b.state;
}
*(int *)userdata = buttonstate;
}
int main(int argc, char **argv) {
int done = 0;
float pos[3];
int forceInEffect = 0;
int forceEnabled = 0;
vrpn_ForceDevice_Remote *forceDevice;
vrpn_Tracker_Remote *tracker;
vrpn_Button_Remote *button;
if (argc < 2) {
printf("Usage: %s Phantom@machine\n", argv[0]);
exit(1);
}
char * servername = argv[1];
/* initialize the force device */
forceDevice = new vrpn_ForceDevice_Remote(servername);
forceDevice->register_force_change_handler(NULL, handle_force_change);
/* initialize the tracker */
tracker = new vrpn_Tracker_Remote(servername);
tracker->register_change_handler((void *)pos, handle_tracker_change);
/* initialize the button */
button = new vrpn_Button_Remote(servername);
button->register_change_handler((void *)&forceEnabled, handle_button_change);
// main loop
while (! done ) {
// Let the forceDevice send its messages to remote force device
forceDevice->mainloop();
// Let tracker receive position information from remote tracker
tracker->mainloop();
// Let button receive button status from remote button
button->mainloop();
if (forceEnabled) {
forceDevice->setFF_Origin(pos[0],pos[1],pos[2]);
// units = dynes
forceDevice->setFF_Force(0.02*cos(pos[0]*200.0*M_PI),
0.02*cos(pos[1]*200.0*M_PI),
0.02*cos(pos[2]*200.0*M_PI));
// set derivatives of force field:
// units = dynes/meter
forceDevice->setFF_Jacobian(-4.0*M_PI*sin(pos[0]*200.0*M_PI), 0, 0, 0,
-4.0*M_PI*sin(pos[1]*200.0*M_PI), 0, 0, 0,
-4.0*M_PI*sin(pos[2]*200.0*M_PI));
forceDevice->setFF_Radius(0.15); // radius of validity
forceDevice->sendForceField();
forceInEffect = 1;
}
else if (forceInEffect){
forceDevice->stopForceField();
forceInEffect = 0;
}
}
} /* main */
#include <stdlib.h>
#include <stdio.h>
#include <vrpn_ForceDevice.h>
#include <vrpn_Tracker.h>
#include <vrpn_Button.h>
#define PHANTOM_SERVER "Phantom@budapest"
/******************************************************************************
* Callback handler
*****************************************************************************/
void handle_force_change(void *userdata, vrpn_FORCECB f)
{
static vrpn_FORCECB lr; // last report
static int first_report_done = 0;
if ((!first_report_done) ||
((f.force[0] != lr.force[0]) || (f.force[1] != lr.force[1])
|| (f.force[2] != lr.force[2])))
printf("force is (%f,%f,%f)\n", f.force[0], f.force[1], f.force[2]);
first_report_done = 1;
lr = f;
}
void handle_tracker_change(void *userdata, const vrpn_TRACKERCB t)
{
static vrpn_TRACKERCB lr; // last report
static float dist_interval_sq = 0.004;
if ((lr.pos[0] - t.pos[0])*(lr.pos[0] - t.pos[0]) +
(lr.pos[1] - t.pos[1])*(lr.pos[1] - t.pos[1]) +
(lr.pos[2] - t.pos[2])*(lr.pos[2] - t.pos[2]) > dist_interval_sq){
printf("Sensor %d is now at (%g,%g,%g)\n", t.sensor,
t.pos[0], t.pos[1], t.pos[2]);
lr = t;
}
}
void handle_button_change(void *userdata, vrpn_BUTTONCB b)
{
static int count=0;
static int buttonstate = 1;
int done = 0;
if (b.state != buttonstate) {
printf("button #%ld is in state %ld\n", b.button, b.state);
buttonstate = b.state;
count++;
}
if (count > 4)
done = 1;
*(int *)userdata = done;
}
int main(int argc, char *argv[]) {
int done = 0;
vrpn_ForceDevice_Remote *forceDevice;
vrpn_Tracker_Remote *tracker;
vrpn_Button_Remote *button;
/* initialize the force device */
forceDevice = new vrpn_ForceDevice_Remote(PHANTOM_SERVER);
forceDevice->register_force_change_handler(NULL, handle_force_change);
/* initialize the tracker */
tracker = new vrpn_Tracker_Remote(PHANTOM_SERVER);
tracker->register_change_handler(NULL, handle_tracker_change);
/* initialize the button */
button = new vrpn_Button_Remote(PHANTOM_SERVER);
button->register_change_handler(&done, handle_button_change);
// Set plane and surface parameters
forceDevice->set_plane(0.0, 1.0, 0.0, 1.0);
forceDevice->setSurfaceKspring(0.1); // spring constant - units of
// dynes/cm
forceDevice->setSurfaceKdamping(0.002); // damping constant -
// units of dynes*sec/cm
forceDevice->setSurfaceFstatic(0.01); // set static friction
forceDevice->setSurfaceFdynamic(0.005); // set dynamic friction
forceDevice->setRecoveryTime(10); // recovery occurs over 10
// force update cycles
// enable force device and send first surface
forceDevice->startSurface();
// main loop
while (! done ){
// Let the forceDevice send its planes to remote force device
forceDevice->mainloop();
// Let tracker receive position information from remote tracker
tracker->mainloop();
// Let button receive button status from remote button
button->mainloop();
// we may call forceDevice->set_plane(...) followed by
// forceDevice->sendSurface() here to change the plane
// for example: using position information from a tracker we can
// compute a plane to approximate a complex surface at that
// position and send that approximation 15-30 times per
// second to simulate the complex surface
}
// shut off force device
forceDevice->stopSurface();
} /* main */