Der NativeCalculator

Wie man in Java mit der Swiss Ephemeris in native Code arbeiten kann

Das Java Native Interface (JNI) bietet Java-Entwicklern die Möglichkeit, Maschinencode auszuführen. Man zerstört damit zwar die Plattformunabhängigkeit von Java, was ein gravierender Nachteil ist. Andererseits hat man die Möglichkeit, Bibliotheken in ihrer Originalform einzubinden, was zum Beispiel dann ein Vorteil ist, wenn die Portierung des Quellcodes der Bibliothek nach Java einen sehr hohen Aufwand darstellen würde.

Thomas Mack hat sich dankenswerterweise die Mühe gemacht, die Swiss Ephemeris nach Java zu portieren (der komplette Port ist auf seiner Download-Seite frei erhältlich). Von daher ist ein Native Interface an sich nicht mehr notwendig. Ich verwende das JNI in meinen produktiven Programmen auch nicht, sondern greife direkt auf Thomas Macks Java-Version zu.

Zu Test-, Forschungs- oder Vergleichszwecken habe ich jedoch die Klasse NativeCalculator erstellt, eine Implementierung des Interface ICalculator, die die Originalroutinen der Swiss Ephemeris DLL direkt aufruft.

Als "Schmiermittel" zwischen der Swiss Ephemeris DLL und der Java-Klasse bedarf es einer weiteren DLL NativeCalculator.dll, die einhüllende Funktionen zur Planetenberechnung mit JNI-konformen Schnittstellen enthält. Wenn Sie die Klasse NativeCalculator verwenden wollen, müssen Sie diese DLL herunterladen oder, wenn Sie nicht auf einer Windows-Plattform sind, den untenstehenden Quelltext zu einer C-Bibliothek kompilieren (die im Quelltext verwendete Headerdatei AstroOL_NativeCalculator.h können Sie von der Webseite herunterladen oder mit dem Kommando javah selbst aus der Klass NativeCalculator generieren). Diese Bibliothek oder DLL muss, ebenso wie die Swiss Ephemeris Bibliothek selbst, in einem für das Betriebssystem als Pfad von Bibliotheken oder ausführbaren Programmen gekennzeichneten Ordner abgelegt werden. Wenn das Betriebssystem die beiden genannten Bibliotheken nicht findet, kann die Klasse NativeCalculator nicht korrekt ausgeführt werden.

Unten folgt der Quellcode zur Erzeugung der Bibliothek. Ich habe mich auf das Minimum beschränkt, was zur Implementierung des Interfaces nötig war. Es sind weitere sinnvolle Funktionen denkbar, zum Beispiel eine Funktion zum Setzen des Pfades für die Swiss Ephemeris Dateien. Auch ist es nicht möglich, mit der Methode get_house() Längen für beliebige Mundanpositionen zu berechnen – es gehen nur die zwölf Häuserspitzen – obwohl die Schnittstelle das hergäbe, da für die Hausposition ein double verwendet wird. Auch hier ergäben sich sinnvolle Erweiterungsmöglichkeiten.

Noch eine Bemerkung zum Message Handling. Etwaige Fehlermeldungen werden, durch Zeilenvorschübe getrennt, direkt in dem privaten String _msg der Klasse NativeCalculator fortgeschrieben. Von dort kann man sie sich mit der nicht zum Interface ICalculator gehörigen Methode getMessage() abholen.

Hier also NativeCalculator.cpp (die Endung .cpp verwende ich nur, weil das JNI für Dateien, die als C++-Dateien erklärt sind, etwas komfortabler zu verwenden ist als für C. Abgesehen davon, hat dieser Code natürlich nichts mit C++ zu tun und würde ebensogut als C-Datei durchgehen):


//---------------------------------------------------------------------------
#include <stdio.h>
#include <math.h>
#define USE_DLL
#include "Swedll.h"
#include "AstroOL_NativeCalculator.h"

#pragma hdrstop

/*
 * Class:     AstroOL_NativeCalculator
 * Method:    getPlanetsAndHouses
 * Signature: (DDD[D[D)Z
 */
JNIEXPORT jboolean JNICALL Java_AstroOL_NativeCalculator_getPlanetsAndHouses
  (JNIEnv *env, jobject o,
  jdouble jd_ut, jdouble lon, jdouble lat,
  jdoubleArray pl, jdoubleArray h) {

  int i;
  double l_jd_ut = jd_ut,
         l_lat = lat,
         l_lon = lon,
         x[6],
         l_ascmc[10],
         *l_pl, *l_h;
  char _msg[255];

  _msg[0] = '\0';

  /** Das Rückgabeflag verwenden wir in dieser Implementierung nicht */
  jboolean ret = true;

/* Planetenberechnung mit swe_calc */
  l_pl = env->GetDoubleArrayElements(pl,NULL);
  for (i = 0; i < 10; i++) {
    swe_calc( l_jd_ut + swe_deltat( l_jd_ut ), i, 0, x, _msg );
    l_pl[i] = x[0];
    }
  env->ReleaseDoubleArrayElements(pl,l_pl,0);

/* Hausberechnung mit swe_houses */
  l_h = env->GetDoubleArrayElements(h,NULL);
  swe_houses(l_jd_ut, l_lat, l_lon, 'P',
             l_h, l_ascmc);
  env->ReleaseDoubleArrayElements(h,l_h,0);

/* Etwaige Meldungen zurückgeben */
  if (_msg[0] != '\0') {
     jclass oClass = env->GetObjectClass(o);
     jfieldID msgID = env->GetFieldID(oClass,"_msg","Ljava/lang/String;");
     jstring jmsg = env->NewStringUTF(_msg);
     env->SetObjectField(o,msgID,jmsg);
     }

  return ret;

  }

/*
 * Class:     AstroOL_NativeCalculator
 * Method:    getPlanet
 * Signature: (DILjava/lang/StringBuffer;)[D
 */
JNIEXPORT jdoubleArray JNICALL Java_AstroOL_NativeCalculator_getPlanet
  (JNIEnv* env, jobject o, jdouble jd_ut, jint planet, jint iflag) {
     int l_pl = planet;
     int l_iflag = iflag;
     double l_jd_ut = jd_ut;
     double x[6];
     int i = 0;
     char _msg[255];
     _msg[0] = '\0';

     /* Einzelnen Planeten berechnen - Einfachstaufruf von swe_calc( ) */
     i = swe_calc( l_jd_ut + swe_deltat( l_jd_ut ), l_pl, l_iflag, x, _msg);

     /* Errechneten Array zurückgeben */
     jdoubleArray jx = env->NewDoubleArray(6);
     double *_jx = env->GetDoubleArrayElements(jx,NULL);
     for (i=0;i<6;i++) _jx[i] = x[i];
     env->ReleaseDoubleArrayElements(jx,_jx,0);

     /* Etwaige Meldungen zurückgeben */
     if (_msg[0] != '\0') {
       jclass oClass = env->GetObjectClass(o);
       jfieldID msgID = env->GetFieldID(oClass,"_msg","Ljava/lang/String;");
       jstring jmsg = env->NewStringUTF(_msg);
       env->SetObjectField(o,msgID,jmsg);
       }

     return jx;

  }

/*
 * Class:     AstroOL_NativeCalculator
 * Method:    getHouse
 * Signature: (DDDDLjava/lang/StringBuffer;)D
 */
JNIEXPORT jdouble JNICALL Java_AstroOL_NativeCalculator_getHouse
  (JNIEnv *env, jobject o, jdouble jd_ut, jdouble lon, jdouble lat,
   jdouble houseNumber, jobject serr){


  double l_jd_ut = jd_ut,
         l_lat = lat,
         l_lon = lon,
         l_ascmc[10],
         *l_h;

  /* Placidus-Häuser berechnen, einfachster Aufruf von swe_houses( ) */
  swe_houses(l_jd_ut, l_lat, l_lon, 'P',
                     l_h, l_ascmc);

  /* Zwischenwerte (z.B. Länge der Mitte von Haus II) werden nicht berechnet */
  int i = houseNumber;
  jdouble l = l_h[i];
  return l;

 }



Zur Doku-Startseite Ohne Hintergrund drucken Zurück zur Homepage