Skip to content

Java Connector

The Java 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

  • Java Development Kit 11
  • Maven 3.6.0
  • org.json package (if you do not use maven)
  • com.fasterxml.jackson.core (if you do not use maven)

Building the package and running the test

At first, clone the OkapiJavaConnector into your development folder:

git clone https://github.com/OKAPIOrbits/OkapiJavaConnector.git

There are different options to build and run the test. Below, we introduce bare maven from the terminal, using Eclipse and Intellij Idea.

To build the connector, run Maven via command line in the directory you cloned:

mvn clean package

It uses pom.xml, which will automatically retrieve all dependencies (i.e. org.json and com.fasterxml.jackson.core). The OkapiJavaConnector will be created in the target folder.

You create a new project within Eclipse and select the directory where the OKAPI files are located. To get rid of the JSON errors, you need to include java-json.jar by opening the properties of your project. Then click on Java BuildPath and Add External JAR. Select the location of the file and apply all changes. Now you are set up to build the example.

Don't forget to add your username and password in the OkapiConnectorTest.java. Get an account at okapiorbits.space/signup!

Create a launch configuration using Eclipse to run the test

The OkapiConnectorTest.java contains a runnable part which can be used to create an Eclipse Application configuration. Under Run Configurations choose Java Application and choose New launch configuration. Type the name e.g. OkapiConnectorTest and select the OkapiJavaConnector in the Projectsection. For the Main class input click the search button. It should find the OkapiConnectorTest.java file. You can select this file. This is it! You can now proceed and run the program.

Open Intellij Idea and chose to import a new project. Navigate to the OkapiJavaConnector directory, select the pom.xml file, choose "OK" and then "Open as Project". Once the project is loaded, click on the "Build Project" icon (the little hammer symbol). It should retrieve the org.json dependency and build a jar file containing the connector. Depending on your default settings, you might have to chose Java 11 as Project SDK. This is can be done via File --> Project Structure --> Project Settings --> Project.

To test the connector, you can build an application using OkapiConnectorTest.java. At first, include your username and passwort in that file. Then, click on Add Configurations ... (close to the hammer icon), and then add a new configuration by chosing the little + icon in the top left corner. From the provided templates, choose Application. Select com.okapiorbits.platform.OkapiConnectorTest as main class. The JRE should be a valid JRE 11 runtime environment. Click OK, and you can execute the OkapiConnectorTest by clicking on the Run icon.

Using the connector

Authentication

The authentication is already achieved when calling the OkapiConnector constructor:

OkapiConnector okapiConnector = new OkapiConnector(
        "username",
        "password"
);
The access token will be returned and must be used when communicating with OKAPI:Aether. Please note that the token is valid for 24 hours and must be refreshed after expiration in order to keep communicating with the platform. It the authentication fails a 401 http code will be returned.

Interacting with the API

Once the conncetor is instantiated it can be used to interact with the platform. To send a pass prediction request first the Json object needs to created:

JSONObject simpleState = new JSONObject()
            .put("orbit", new JSONObject()
                    .put("type","state.json")
                    .put("content",new JSONObject()
                            .put("area", 0.01)
                            .put("mass", 1.3)
                            .put("x", -2915.65441951)
                            .put("y", -3078.17058851)
                            .put("z", 5284.39698421)
                            .put("x_dot", 4.94176934)
                            .put("y_dot", -5.83109248)
                            .put("z_dot", -0.66365683)
                            .put("epoch", "2018-08-06T18:19:43.256Z")
                    )
            )
            .put("ground_location", new JSONObject()
                    .put("type", "ground_loc.json")
                    .put("content", new JSONObject()
                            .put("altitude",  0.048)
                            .put("longitude", 10.645)
                            .put("latitude", 52.328)
                    )
            )
            .put("time_window", new JSONObject()
                    .put("type", "tw.json")
                    .put("content", new JSONObject()
                            .put("start",  "2018-08-06T18:19:44.256Z")
                            .put("end", "2018-08-07T00:00:00.000Z")
                    )
            )
            .put("settings", new JSONObject()
                    .put("type","shared_prop_settings.json")
                    .put("content",new JSONObject()
                            .put("output_step_size", 60)
                            .put("geopotential_degree_order", 2)
                    )
            );
It contains CCSDS OPM compliant orbit data, the ground station location, time window and settings for the propagation fidelity and output step size.

The computation is handed of to the platform by invoking the sendAndGetRequestId() method:

String requestId;
// Send a predict-passes request
try {
    requestId = okapi.sendAndGetRequestId(
            "/predict-passes/neptune/requests",
            simpleState.toString(),
            accessToken);
} catch (OkapiConnector.OkapiPlatformException okapiPlatformException) {
    okapiPlatformException.printStackTrace();
    return;
}
The end point /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 as OkapiPlatformException. When ever a http code >= 300 is returned an exception is raised.

The requestId is stored and is used to retrieve the predicted passes after the processing is finished via the waitForProcessingAndGetValues() method:

String predictPassesResult;
// Retrieve results for predicted passes in summary format
try {
    predictPassesResult = okapi.waitForProcessingAndGetValues("/predict-passes/neptune/results/" + requestId + "/summary", accessToken);
} catch (OkapiConnector.OkapiPlatformException okapiPlatformException) {
    okapiPlatformException.printStackTrace();
    return;
}
if (okapi.responseCode == 200) {
    System.out.println(new JSONObject(predictPassesResult).toString(4));
}
The 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 send() and getValues() are available. An example of the more compex approach is given below in the exprt section.

The Java connector offers special methods, which reduce the effort for the developer to interact with the platform. For example, the getSatellites() automatically creates a Java class from the returned json response. It enables seemless interaction:

Satellites satellites;

// Retrieve all satellite definitions connected to the account
try {
    satellites = okapi.getSatellites(accessToken);
} catch (OkapiConnector.OkapiPlatformException | IOException okapiPlatformException) {
    okapiPlatformException.printStackTrace();
    return;
}

if (okapi.responseCode == 200) {
    System.out.println(satellites.toString());
}

The method call returns all satellite associated to the account of the user as Satellites, which in turn contains the Satellite classes.

A satellite can be added just as simple by creating a new Satellite class, naming the satellite and defining the mandatory fields:

Satellite newSatellite = new Satellite();
newSatellite.setName("Sputnik");
// This is a random ID, which will be changed by the backend but currently it is still required
newSatellite.setSatelliteId("550e8400-e29b-11d4-a716-446655440000");
newSatellite.setSpaceTrackStatus(Satellite.SpaceTrackStatus.SHARING_AGREEMENT_SIGNED);

// Send new satellite definition to the backend to add to the collection and retrieve the new instance from the
// backend.
try {
    newSatellite = okapi.addSatellite(newSatellite,accessToken);

} catch (OkapiConnector.OkapiPlatformException | IOException okapiPlatformException) {
    okapiPlatformException.printStackTrace();
    return;
}

// Retrieve the newly assigned satellite id
if (okapi.responseCode == 200) {
    System.out.println(newSatellite.toString());
    satelliteId = newSatellite.getSatelliteId();
}

Currently the satelliteId must also be set by the developer. This id will however be overriden in the platform. The retrieved satellite newSatellite will have an updated satelliteId!

Other convenience methods are:

  Conjunctions getConjunctions(String accessToken)
  SpaceTrackCdms getCdms(String conjunctionId, String accessToken)
  ManeuverEvals getManeuverEvals(Satellite existingSatellite, String accessToken)
  Satellite updateSatellite(Satellite existingSatellite, String accessToken)
  deleteSatellite deleteSatellite(String satelliteId, String accessToken)

For Experts

For parallelization purposes multiple requests can be send to the platform at once using the sendAndGetRequestId() method in a loop. The developer must then handle the different requests via the requestIds and poll the backend for the results, like so:

int responseCode;
int requestCounter = 0;
String result;

// Retrieve results using the provided requestId
try {
    do {
        result = okapi.getValues("/estimate-risk/all-methods/results/" + requestId + "/simple", accessToken);
        responseCode = okapi.responseCode;
        requestCounter++;
        if (responseCode != 202 || requestCounter == 15) break;
        System.out.println("The request was successful. The backend is processing it.");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } while(true);
} catch (OkapiConnector.OkapiPlatformException okapiPlatformException) {
    okapiPlatformException.printStackTrace();
    return;
}
if (okapi.responseCode == 200) {
    System.out.println(new JSONObject(result).toString(4));
}
The example shows the retrieval of a risk estimation. The backend is polled for the result using a requestId in the line result = okapi.getValues("/estimate-risk/all-methods/results/" + requestId + "/simple", accessToken); Within the do loop another loop over multiple requestIds can be envisaged to scale for increased performance. Please contact OKAPI if you are looking for an increased workload, e.g. for simulation purposes. Due to our flexible backend we are able to scale dynamically for an increased workload.