C++
The Cpp connector can be found on GitHub. It ships with the connector class OkapiConnector
and an associated test class, which shows how to use every method available.
Getting started
Prerequisite
- a C++ compiler: e.g. gcc or clang
- CMake: at least verison 3.2
- a text editor or IDE of your choice (we like to use VS Code or Code::Blocks)
- a terminal
- library dependencies:
- boost_system, boost_thread, boost_chrono, crypto, ssl and
- cpprest1
Compile the code
- Clone the OkapiJavaConnector into your development folder:
git clone https://github.com/OKAPIOrbits/OkapiCppConnector.git
- To be able to run the tests, insert your username and password in
OkapiConnectorTest.cpp
- When working via terminal create a
build
folder inOkapiCppConnector
and change into the directory.mkdir build cd build
- Execute
which prepares the make files
cmake ../
- Run
which compiles the code and creates the executable and library.
make install
- You can now find the
okapi-connector-test
in thebin
folder. Additionally, you can now find the dynamic librarylibokapi-connector.so
orlibokapi-connector.dylib
in thelib
folder. You can use this library and link it to be able to use the Okapi interface within your own application. To run the connector, useLD_LIBRARY_PATH=../lib ./okapi-connector-test
Using the Connector
Authentication
The authentication is achieved when calling the OkapiConnector
init
function:
OkapiConnector::OkapiResult initResult
= connector.init("https://api.okapiorbits.com/","username", "password");
401
http code will be returned. An new call to the init()
function will refresh the token.
Interacting with the API
Once the conncetor is initialized it can be used to interact with the platform. To send a pass prediction request first the Json object needs to created:
// Define the ground location
web::json::value groundLocationContent;
groundLocationContent[U("altitude")] = web::json::value::number( 0.048);
groundLocationContent[U("longitude")] = web::json::value::number(10.645);
groundLocationContent[U("latitude")] = web::json::value::number(52.328);
// Define the time window in which the passes should be generated
web::json::value timeWindowContent;
timeWindowContent[U("start")] = web::json::value::string("2018-08-06T18:19:44.256Z");
timeWindowContent[U("end")] = web::json::value::string("2018-08-07T00:00:00.000Z");
// Define how to propagate and the step size of the output
web::json::value predictPassesSettingsSimple;
predictPassesSettingsSimple[U("output_step_size")] = web::json::value::number(60);
predictPassesSettingsSimple[U("geopotential_degree_order")] = web::json::value::number(2);
// Defining the satellite properties and orbit
web::json::value simpleState;
simpleState[U("area")] = web::json::value::number(0.01);
simpleState[U("mass")] = web::json::value::number(1.3);
simpleState[U("x")] = web::json::value::number(-2915.65441951);
simpleState[U("y")] = web::json::value::number(-3078.17058851);
simpleState[U("z")] = web::json::value::number(5284.39698421);
simpleState[U("x_dot")] = web::json::value::number(4.94176934);
simpleState[U("y_dot")] = web::json::value::number(-5.83109248);
simpleState[U("z_dot")] = web::json::value::number(-0.66365683);
simpleState[U("epoch")] = web::json::value::string("2018-08-06T18:19:43.256Z");
// Bringing together the four parts for one request body
web::json::value passPredictionNumericalRequestBody;
web::json::value groundLocation;
groundLocation[U("type")] = web::json::value::string("ground_loc.json");
groundLocation[U("content")] = groundLocationContent;
passPredictionNumericalRequestBody[U("ground_location")] = groundLocation;
web::json::value timeWindow;
timeWindow[U("type")] = web::json::value::string("tw.json");
timeWindow[U("content")] = timeWindowContent;
passPredictionNumericalRequestBody[U("time_window")] = timeWindow;
web::json::value orbit;
orbit[U("type")] = web::json::value::string("state.json");
orbit[U("content")] = simpleState;
passPredictionNumericalRequestBody[U("orbit")] = orbit;
web::json::value settings;
settings[U("type")] = web::json::value::string("shared_prop_settings.json");
settings[U("content")] = predictPassesSettingsSimple;
passPredictionNumericalRequestBody[U("settings")] = settings;
The computation is handed of to the platform by invoking the sendRequest()
method:
// Send request for NEPTUNE pass prediction
OkapiConnector::OkapiResult response = connector.sendRequest("/predict-passes/neptune/requests", passPredictionNumericalRequestBody);
// Check response
if (response.error.code != 200 && response.error.code != 202)
{
cout << "Neptune request failed with status: " << response.error.status << endl;
cout << response.error.message << endl;
}
string requestId = connector.getRequestId(response);
cout << "Request ID: " << requestId << endl;
/predict-passes/neptune/requests
is used which utilizes the open source Neptune numerical propagator for the extrapolation of the state vector. Alternatives are the orekit propagator or the SGP4 analytic orbit theory for TLE, utilizing the endpoints /predict-passes/orekit/requests
and /predict-passes/sgp4/requests
, respectively. Please refer to the API for more information on all available endpoints.
Errors from the backend are passed in an OkapiConnector::OkapiError
struct. It contains the status code and a message.
The requestId
is retrieved using the line connector.getRequestId(responseNeptune);
. After the processing is finished it is used to retrieve the result via the waitForProcessingAndGetValues()
function:
OkapiConnector::OkapiResult result = connector.waitForProcessingAndGetValues("/predict-passes/neptune/results/" + requestId + "/summary");
if (result.error.code != 200 && result.error.code != 202)
{
cout << "Response failed with status: " << result.error.status << endl;
cout << result.error.message << endl;
}
else
{
cout << result.body.serialize() << endl;
}
waitForProcessingAndGetValues()
is a convenience method, which blocks as long as the full result is not available. For more control over the process of requesting a computation and retrieving results the methods sendRequest()
and getValues()
are available.
The Cpp connector offers special methods, which reduce the effort for the developer to interact with the platform. For example, the getSatellites()
acan be used to retrieve all satellites associated with a given account:
OkapiConnector::OkapiResult result = okapi.getSatellites();
// Retrieve the newly assigned satellite id
if (result.error.code == 200) {
cout << result.body.serialize() << endl;
} else {
cout << result.error.status << ": " << result.error.message << endl;
}
A satellite can be added just as easily:
string newSatelliteId = "";
web::json::value newSatellite;
newSatellite[U("name")] = web::json::value::string("Sputnik");
std::vector<web::json::value> noradIds;
noradIds.push_back(web::json::value::string("1"));
newSatellite[U("norad_ids")] = web::json::value::array(noradIds);
// This is a random ID, which will be changed by the backend but currently it is still required
newSatellite[U("satellite_id")] = web::json::value::string("550e8400-e29b-11d4-a716-446655440000");
newSatellite[U("space_track_status")] = web::json::value::string("sharing_agreement_signed");
OkapiConnector::OkapiResult result = okapi.addSatellite(newSatellite);
if (result.error.code == 200) {
newSatellite = result.body;
cout << newSatellite.serialize() << endl;
newSatelliteId = newSatellite.as_object().at(U("satellite_id")).as_string();
} else {
cout << result.error.status << ": " << result.error.message << endl;
}
return newSatelliteId;
Currently the satellite_id
must also be set by the developer. This id will however be overriden in the platform. The retrieved satellite newSatellite
will have an updated satellite_id
!
Other convenient methods are:
OkapiResult getConjunctions()
OkapiResult getCdms(string conjunctionId)
OkapiResult getManeuverEvals(string conjunctionId)
OkapiResult updateSatellite(web::json::value requestBody, string satelliteId)
OkapiResult deleteSatellite(string satelliteId)
-
https://github.com/microsoft/cpprestsdk ↩