-
Notifications
You must be signed in to change notification settings - Fork 0
Protocol
D.J. Figueroa edited this page Jan 14, 2021
·
2 revisions
Formal documentation will come soon (tm), in the mean time, here is some sample code to demonstrate how this early and subject to change protocol works.
#include <iostream>
#include <thread>
#include <string.h>
#define NOMINMAX
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <math.h>
int main() {
// Initialize WinSock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WinSock not initialized";
return -1;
}
char SERVER_ADDRESS[] = "127.0.0.1";
int SERVER_PORT = 8082;
SOCKET s = INVALID_SOCKET;
struct sockaddr_in serv_addr;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == INVALID_SOCKET) {
std::cerr << "Could not open socket" << std::endl;
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
if (inet_pton(AF_INET, SERVER_ADDRESS, &serv_addr.sin_addr) <= 0) {
std::cerr << "Invalid address" << std::endl;
}
// The "handshake"
// There actually isn't any real communication going on here (yet), it's just the device
// letting the driver know that it exists, which will create the OpenVR reference device
// and display a globe icon in the SteamVR devices panel.
char handshake[] = { 0x00 };
sendto(s, handshake, sizeof(handshake), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
// Advertising
// This is where the client intializes tracked devices. The format is as follows:
// 0x01 Advertise Byte
//
// for each tracker:
// char TRACKER_ID A number between 1-127 that the client will refer to the tracker by
// STRING TRACKER_SERIAL Serial string for the device
// 0x00 String terminator
struct Tracker {
char id;
std::string serial;
bool isVisible;
};
Tracker t;
t.id = 1;
t.serial = "Sample Device";
t.isVisible = true;
int advertise_length = t.serial.length() + 3;
char* advertise = new char[advertise_length];
advertise[0] = 0x01;
advertise[1] = t.id;
for (int i = 0; i < t.serial.length(); i++) {
advertise[2 + i] = t.serial[i];
}
advertise[advertise_length - 1] = 0;
sendto(s, advertise, advertise_length, 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
// Update
// This is where the client sends positional and rotational data for trackers. The format is as follows:
// 0x03 Update Byte
//
// for each tracker:
// char TRACKER_ID A number between 1-127 of a tracker already advertised.
// If the tracker is visible, send the id as a positive number.
// If it is not visible, send the id as a negative number.
// (ex: visible tracker id = 1, not visible tracker id = -1)
//
// NOTE: ONLY SEND TVEC AND RVEC IF THE TRACKER IS VISIBLE! If it is not visible, the driver will start processing the next byte as a new tracker to be updated.
// double[3] rvec A rotation vector represented as a Rodrigues rotation vector https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula.
// This was inspired by OpenCV's rotation vectors, which the application of this driver is using.
// double[3] tvec A position vector (x,y,z)
while (true) {
char update[1 + 1 + sizeof(double[6])] = { 0 };
update[0] = 0x03;
update[1] = t.isVisible ? t.id : -t.id;
double rvec[3] = { 0 };
double now = std::chrono::system_clock::now().time_since_epoch().count();
double y = sin(now / 2000000); // some arbitrary division to make a sine wave
double tvec[3] = { 0, y, 0 };
memcpy(&update[2], rvec, sizeof(rvec));
memcpy(&update[2 + sizeof(double[3])], tvec, sizeof(tvec));
sendto(s, update, sizeof(update), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
std::this_thread::sleep_for(std::chrono::milliseconds(17)); // ~ 60 Hz update rate
}
return 0;
}