OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
osvr_calibrate.cpp
Go to the documentation of this file.
1 
12 // Copyright 2014 Sensics, Inc.
13 //
14 // Licensed under the Apache License, Version 2.0 (the "License");
15 // you may not use this file except in compliance with the License.
16 // You may obtain a copy of the License at
17 //
18 // http://www.apache.org/licenses/LICENSE-2.0
19 //
20 // Unless required by applicable law or agreed to in writing, software
21 // distributed under the License is distributed on an "AS IS" BASIS,
22 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 // See the License for the specific language governing permissions and
24 // limitations under the License.
25 
26 // Internal Includes
27 #include "ClientMainloop.h"
28 #include "RecomposeTransform.h"
29 #include "WrapRoute.h"
32 #include <osvr/Common/JSONEigen.h>
35 
36 #include <osvr/Util/EigenInterop.h>
37 
38 // Library/third-party includes
39 #include <boost/program_options.hpp>
40 #include <boost/thread/thread.hpp>
41 #include <boost/date_time/posix_time/posix_time.hpp>
42 #include <json/value.h>
43 #include <json/reader.h>
44 
45 // Standard includes
46 #include <iostream>
47 #include <fstream>
48 #include <exception>
49 
50 using std::cout;
51 using std::cerr;
52 using std::endl;
53 
54 static osvr::server::ServerWeakPtr g_server;
55 
56 auto SETTLE_TIME = boost::posix_time::seconds(2);
57 
60  osvr::server::ServerPtr server(g_server.lock());
61  if (server) {
62  cout << "Received shutdown signal..." << endl;
63  server->signalStop();
64  } else {
65  cout << "Received shutdown signal but server already stopped..."
66  << endl;
67  }
68 }
69 
70 void waitForEnter() { std::cin.ignore(); }
71 
72 inline Json::Value removeCalibration(std::string const &input) {
73  Json::Reader reader;
74  Json::Value root;
75  if (!reader.parse(input, root)) {
76  throw std::runtime_error("Could not parse route");
77  }
78  return remove_levels_if(root, [](Json::Value const &current) {
79  return current.isMember("calibration") &&
80  current["calibration"].isBool() &&
81  current["calibration"].asBool();
82  });
83 }
84 
85 int main(int argc, char *argv[]) {
86  std::string configName;
87  std::string outputName;
88  namespace po = boost::program_options;
89  // clang-format off
90  po::options_description desc("Options");
91  desc.add_options()
92  ("help", "produce help message")
93  ("route", po::value<std::string>()->default_value("/me/head"), "route to calibrate")
94  ("output,O", po::value<std::string>(&outputName), "output file (defaults to same as config file)")
95  ;
96  po::options_description hidden("Hidden (positional-only) options");
97  hidden.add_options()
98  ("config", po::value<std::string>(&configName)->default_value(std::string(osvr::server::getDefaultConfigFilename())))
99  ;
100  // clang-format on
101 
102  po::positional_options_description p;
103  p.add("config", 1);
104 
105  po::variables_map vm;
106  po::store(po::command_line_parser(argc, argv)
107  .options(po::options_description().add(desc).add(hidden))
108  .positional(p)
109  .run(),
110  vm);
111  po::notify(vm);
112 
113  {
115  bool usage = false;
116 
117  if (vm.count("help")) {
118  usage = true;
119  } else if (vm.count("route") != 1) {
120  cout << "Error: --route is a required argument\n" << endl;
121  usage = true;
122  }
123 
124  if (usage) {
125  cout << "Usage: osvr_calibrate [config file name] [options]"
126  << endl;
127  cout << desc << "\n";
128  return 1;
129  }
130  }
131 
132  if (outputName.empty()) {
133  outputName = configName;
134  }
135 
138  if (!srv) {
139  return -1;
140  }
141  g_server = srv;
142 
143  cout << "Registering shutdown handler..." << endl;
144  osvr::server::registerShutdownHandler<&handleShutdown>();
145 
146  std::string dest = vm["route"].as<std::string>();
147  std::string route = srv->getSource(dest);
148  if (route.empty()) {
149  cerr << "Error: No route found for provided destination: " << dest
150  << endl;
151  return -1;
152  }
153  cout << dest << " -> " << route << endl;
154  Json::Value pruned = removeCalibration(route);
155  Json::Value prunedDirective(Json::objectValue);
156  {
157  cout << pruned.toStyledString() << endl;
158  cout << "Submitting cleaned route..." << endl;
159  prunedDirective["destination"] = dest;
160  prunedDirective["source"] = pruned;
161  srv->addRoute(prunedDirective.toStyledString());
162  }
163  cout << "Starting client..." << endl;
164  osvr::clientkit::ClientContext ctx("com.osvr.bundled.osvr_calibrate");
165  osvr::clientkit::Interface iface = ctx.getInterface(dest);
166 
167  ClientMainloop client(ctx);
168  srv->registerMainloopMethod([&client] { client.mainloop(); });
169  {
170  // Take ownership of the server inside this nested scope
171  // We want to ensure that the client parts outlive the server.
172  osvr::server::ServerPtr server(srv);
173  srv.reset();
174 
175  cout << "Starting server and client mainloop..." << endl;
176  server->start();
177  cout << "Waiting a few seconds for the server to settle..." << endl;
178  boost::this_thread::sleep(SETTLE_TIME);
179 
180  cout << "\n\nPlease place your device in its 'zero' orientation and "
181  "press enter." << endl;
182  waitForEnter();
183 
184  OSVR_OrientationState state;
185  OSVR_TimeValue timestamp;
186  OSVR_ReturnCode ret;
187  {
190  ClientMainloop::lock_type lock(client.getMutex());
191  ret = osvrGetOrientationState(iface.get(), &timestamp, &state);
192  }
193  if (ret != OSVR_RETURN_SUCCESS) {
194  cerr << "Sorry, no orientation state available for this route - "
195  "are you sure you have a device plugged in and your path "
196  "correct?" << endl;
197  return -1;
198  }
199  Eigen::AngleAxisd rotation(osvr::util::fromQuat(state).inverse());
200 
201  cout << "Angle: " << rotation.angle()
202  << " Axis: " << rotation.axis().transpose() << endl;
203  Json::Value newRoute;
204  {
205  Json::Value newLayer(Json::objectValue);
206  newLayer["calibration"] = true;
207  newLayer["rotate"]["radians"] = rotation.angle();
208  newLayer["rotate"]["axis"] = osvr::common::toJson(rotation.axis());
209  newRoute = wrapRoute(prunedDirective, newLayer);
210  bool isNew = server->addRoute(newRoute.toStyledString());
211  BOOST_ASSERT_MSG(
212  !isNew,
213  "Server claims this is a new, rather than a replacement, "
214  "route... should not happen!");
215  }
216 
219  boost::this_thread::sleep(SETTLE_TIME);
220 
221  cout << "\n\nNew calibration applied: please inspect it with the "
222  "Tracker Viewer." << endl;
223  cout << "(If rotations appear incorrect, you may first need to add "
224  "a basisChange transform layer to the route.)" << endl;
225  if (configName == outputName) {
226  cout << "If you are satisfied and want to OVERWRITE your existing "
227  "config file with this update, press y." << endl;
228  cout << "Otherwise, press enter or Ctrl-C to break out of this "
229  "program.\n" << endl;
230  cout << "Overwrite '" << configName << "'? [yN] ";
231  char confirm;
232  std::cin.get(confirm);
233  cout << endl;
234  if (confirm != 'y' && confirm != 'Y') {
235  cout << "Calibration save cancelled." << endl;
236  return 1;
237  }
238  }
239  Json::Value root;
240  {
241  std::ifstream config(configName);
242  if (!config.good()) {
243  cerr << "Could not read the original config file again!"
244  << endl;
245  return -1;
246  }
247 
248  Json::Reader reader;
249  if (!reader.parse(config, root)) {
250  cerr << "Could not parse the original config file again! "
251  "Should never happen!" << endl;
252  return -1;
253  }
254  }
255  auto &routes = root["routes"];
256  for (auto &fileRoute : routes) {
257  if (fileRoute["destination"] == dest) {
258  fileRoute = newRoute;
259  }
260  }
261  {
262  cout << "\n\nWriting updated config file to " << outputName << endl;
263  std::ofstream outfile(outputName);
264  outfile << root.toStyledString();
265  }
266 
267  cout << "Awaiting Ctrl-C to trigger server shutdown..." << endl;
268  server->awaitShutdown();
269  }
270  cout << "Server mainloop exited." << endl;
271 
272  return 0;
273 }
Header.
Interface handle object. Typically acquired from a ClientContext.
Client context object: Create and keep one in your application. Handles lifetime management and provi...
Definition: Context_decl.h:57
Header to register a handler for cross-platform shutdown/terminate signals. WARNING: includes windows...
A structure defining a quaternion, often a unit quaternion representing 3D rotation.
Definition: QuaternionC.h:49
Json::Value remove_levels_if(Json::Value input, Predicate p)
Goes through your transform, starting at the outermost layer, and keeping and re-composing only the l...
OSVR_SERVER_EXPORT ServerPtr configureServerFromFile(std::string const &configName)
This uses a file name to attempt to configure the server with that config file. Pass an empty string ...
Interface getInterface(const std::string &path)
Get the interface associated with the given path.
Definition: Context.h:61
OSVR_ClientInterface get()
Get the raw OSVR_ClientInterface from this wrapper.
Definition: Interface.h:55
Header.
Simple class to handle running a client mainloop in another thread, but easily pausable.
#define OSVR_RETURN_SUCCESS
The "success" value for an OSVR_ReturnCode.
Definition: ReturnCodesC.h:45
int main(int argc, char *argv[])
Header.
shared_ptr< Server > ServerPtr
How one should hold a Server.
Definition: ServerPtr.h:39
void handleShutdown()
Shutdown handler function - forcing the server pointer to be global.
Eigen::Quaterniond fromQuat(OSVR_Quaternion const &q)
Convert an OSVR_Quaternion to an Eigen::Quaterniond.
Definition: EigenInterop.h:63
Header for interoperation between the Eigen math library, the internal mini math library, and VRPN's quatlib.
OSVR_ReturnCode osvrGetOrientationState(OSVR_ClientInterface iface, struct OSVR_TimeValue *timestamp, OSVR_OrientationState *state)
Get Orientation state from an interface, returning failure if none \ * exists.
weak_ptr< Server > ServerWeakPtr
How one might observe a Server without owning it.
Definition: ServerPtr.h:43
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
Definition: TimeValueC.h:81
Json::Value toJson(Eigen::QuaternionBase< Derived > const &quat)
Converts quaternions to JSON objects.
Definition: JSONEigen.h:47