Monday, March 25, 2019

Calcolo di posizione con JPL toolkit - funzione reclat_c per le coordinate eclittiche

Prima di passare alla stesura di alcune semplici applicazioni, devo completare l'elenco di funzioni del jpl toolkit con qualcosa di adatto a calcolare le posizioni eclittiche (longitudine e latitudine). Come già abbiamo fatto in precedenza con il linguaggio Python, utilizzeremo il kernel trueepoch.tf già creato qualche post addietro, con frame di riferimento eclittico e dinamico.

Nel JPL toolkit esiste una funzione adatta per il calcolo delle coordinate eclittiche, reclat_c, utile per passare dalle coordinate eclittiche rettangolari alle eclittiche sferiche.

L'immagine sopra, tratta da Wikipedia, mostra efficacemente l'inclinazione del piano equatoriale rispetto a quello eclittico. Il punto di intersezione dei due circoli equatoriale ed eclittico, dal quale parte il semicircolo eclittico al di sopra dell'equatore (declinazioni positive nel riferimento equatoriale) fino al punto opposto a 0° Bilancia , è chiamato punto vernale e coincide con la posizione solare apparente all'equinozio di primavera e con lo 0° di Ariete. Per completezza, le costellazioni zodiacali che costituiscono il folclore dell'Astrologia, non sono più usate da secoli (per effetto della precessione degli equinozi l'allineamento tra segni e costellazioni è andato completamente perso), lo zodiaco è determinato dalla scelta del punto 0° di Ariete e da lì il cerchio eclittico viene diviso in dodici parti di ampiezza 30°, che si succedono in senso antiorario, come del resto il movimento apparente del Sole nel corso dell'anno solare.

Vediamo la funzione che ci interessa:

reclat_c

Perchè il suo utilizzo abbia senso, il sistema o frame di riferimento deve essere eclittico. Nel toolkit, le funzioni recrad_c, reclat_c e recsph_c, usate con lo stesso frame, non fanno altro che riproporre le stesse distanze e angoli, ruotati opportunamente.

Per questo motivo, una volta scelto il riferimento eclittico da indicare nella funzione spkezr_c (ECLIPJ2000 se statico, o il nostro MYTRUEEPOCH se dinamico), possiamo utilizzare efficacemente la funzione. Vediamo la signature in C, molto simile a quella già incontrata per recrad_c):

   void reclat_c ( ConstSpiceDouble    rectan[3],
                        SpiceDouble       * radius,
                        SpiceDouble       * longitude,
                        SpiceDouble       * latitude  )
Brief_I/O
 
   VARIABLE  I/O  DESCRIPTION
   --------  ---  --------------------------------------------------
   rectan     I   Rectangular coordinates of a point.
   radius     O   Distance of the point from the origin.
   longitude  O   Longitude of the point in radians.
   latitude   O   Latitude of the point in radians.

Nulla di nuovo, come vediamo. Nella classe CSpice già vista nei precedenti post aggiungeremo le parti di interesse:

    public interface CLibrary extends Library {
        double pi_c();
        double spd_c();
        double dpr_c();
        void furnsh_c(String s);
        void unload_c(String s);
        double str2et_c(String s, DoubleByReference p);
        double spkezr_c(String targ, double et, String ref, String abcorr, String obs, double[] starg, DoubleByReference lt);
        double recrad_c(double[] rectan, DoubleByReference range, DoubleByReference ra, DoubleByReference dec);
        double reclat_c(double[] rectan, DoubleByReference radius, DoubleByReference longitude, DoubleByReference latitude);
    }

nella parte declarativa (interfaccia) e, nella parte riguardante la definizione della funzione, il codice seguente:

    double[] reclat_c(double[] rectan, DoubleByReference radius, DoubleByReference longitude, DoubleByReference latitude){
        radius.setValue(0.0); longitude.setValue(0.0); latitude.setValue(0.0);
        INSTANCE.reclat_c(rectan, radius, longitude, latitude);
        double[] ret = new double[6];
        ret[0] = rectan[0]; ret[1] = rectan[1]; ret[2] = rectan[2]; 
        ret[3] = radius.getValue(); ret[4] = longitude.getValue(); ret[5] = latitude.getValue();
        return ret;
        
    }

Per testare la nuova funzione utilizzero' il sito Miriade dell'Observatoire de Paris, che attraverso l'IMCCE Virtual Observatory Solar System portal fornisce un comodo ambiente web per il calcolo di posizione secondo vari sistemi e frame di riferimento.

Usando il mio programma di test, usando ECLIPJ2000 come ref statico, ottengo i seguenti: (longitudine e latitudine in gradi, minuti e secondi con decimali per una più facile comparazione con i risultati di Miriade)

Vettore posizione e velocità di  SUN 2019-03-25T17:15:00

 x:  0.9941741023222863
 y:  0.07837450273804566
 z:  -6.386035799020023E-6


Coordinate rettangolari e sferiche eclittiche
radius:  0.9972585965779094
longitudine:  4°30'27".07
latitudine:  -0°00'01".32


Vettore posizione e velocità di  MOON 2019-03-25T17:15:00

 x:  -0.001033206460039779
 y:  -0.002309224486574583
 z:  1.6574816415858715E-4


Coordinate rettangolari e sferiche eclittiche
radius:  0.0025352526052418367
longitudine:  245°53'42".11
latitudine:  3°44'54".68


Vettore posizione e velocità di  MERCURY 2019-03-25T17:15:00

 x:  0.6283821386649743
 y:  -0.15394903328787
 z:  0.01456646607325417


Coordinate rettangolari e sferiche eclittiche
radius:  0.6471295071137615
longitudine:  346°14'02".74
latitudine:  1°17'23".28


Vettore posizione e velocità di  VENUS 2019-03-25T17:15:00

 x:  1.0515933099261605
 y:  -0.6464412430519756
 z:  -0.01326528066622894


Coordinate rettangolari e sferiche eclittiche
radius:  1.2344677953965406
longitudine:  328°25'11".63
latitudine:  -0°36'56".51


Vettore posizione e velocità di  MARS 2019-03-25T17:15:00

 x:  1.0996446391999108
 y:  1.6346349816138936
 z:  0.03001478808475523


Coordinate rettangolari e sferiche eclittiche
radius:  1.9703174219248858
longitudine:  56°04'14".37
latitudine:  0°52'22".25


Vettore posizione e velocità di  JUPITER_BARYCENTER 2019-03-25T17:15:00

 x:  -0.5546811837096326
 y:  -5.015523791671758
 z:  0.05580703045516249


Coordinate rettangolari e sferiche eclittiche
radius:  5.046411055892613
longitudine:  263°41'20".91
latitudine:  0°38'01".08


Vettore posizione e velocità di  SATURN_BARYCENTER 2019-03-25T17:15:00

 x:  3.3846391497900328
 y:  -9.689305891517016
 z:  0.07463820459939993


Coordinate rettangolari e sferiche eclittiche
radius:  10.263722604165778
longitudine:  289°15'18".63
latitudine:  0°24'59".98


Vettore posizione e velocità di  URANUS_BARYCENTER 2019-03-25T17:15:00

 x:  17.836331344752253
 y:  10.586677831321417
 z:  -0.17908834868983295


Coordinate rettangolari e sferiche eclittiche
radius:  20.74233680134956
longitudine:  30°41'27".84
latitudine:  -0°29'40".90


Vettore posizione e velocità di  NEPTUNE_BARYCENTER 2019-03-25T17:15:00

 x:  30.040066592923566
 y:  -7.154084379289532
 z:  -0.5205399170871235


Coordinate rettangolari e sferiche eclittiche
radius:  30.884583306539174
longitudine:  346°36'16".04
latitudine:  -0°57'56"63


Vettore posizione e velocità di  PLUTO_BARYCENTER 2019-03-25T17:15:00

 x:  13.128721019597675
 y:  -31.432013468122367
 z:  -0.13859786812604474


Coordinate rettangolari e sferiche eclittiche
radius:  34.06396917036681
longitudine:  292°40'10".65
latitudine:  -0°13'59".24

Per lo stesso istante Miriade mi dà i seguenti (copio uno alla volta dal sito, Plutone è escluso);


Planetary theory: DE406 Reference Plane:ecliptic 
Type of Coordinates: Spherical Type of Ephemeris: MeanJ2000
-----------------------------------------------------------------------------
Target            Date              Longitude       Latitude       Distance
----------------------------------------------------------------------------
Sun     2019-03-25T17:15:00.00   4 30 21.4251 -00  0  0.9002  0.997243696
Moon    2019-03-25T17:15:00.00 245 53 42.0999 +03 44 54.6775  0.002566436
Mercury 2019-03-25T17:15:00.00 346 13 52.9789 +01 17 23.9700  0.647123973
Venus   2019-03-25T17:15:00.00 328 25  6.4727 -00 36 56.1647  1.234471820
Mars    2019-03-25T17:15:00.00  56  4 13.8153 +00 52 22.5126  1.970286798
Jupiter 2019-03-25T17:15:00.00 263 41 20.5542 +00 38  1.1265  5.046440794
Saturn  2019-03-25T17:15:00.00 289 15 18.2025 +00 24 59.9873 10.263743803
Uranus  2019-03-25T17:15:00.00  30 41 27.7752 -00 29 40.8371 20.742311016
Neptune 2019-03-25T17:15:00.00 346 36 15.7283 -00 57 56.5707 30.884601149
-----------------------------------------------------------------------------

Considerate le diversità già accennate tra i due sistemi di calcolo, i risultati sono incoraggianti. Dal prossimo post iniziamo a costruire piccoli software grafici in JavaFX (o in Swing, devo capire quale libreria grafica di Java dà i migliori risultati). Come IDE penso che usero' NetBeans, è un po' che ci traffico e mi sembra abbastanza comodo e facile da usare. Al prossimo post.

Saturday, March 16, 2019

Calcolo di posizione con JPL toolkit - funzione recrad_c per le coordinate equatoriali

Per la parte strettamente legata all'implementazione delle funzioni essenziali del toolkit, in questo post aggiungero' una funzione utile per avere la rappresentazione in coordinate sferiche equatoriali.

Con la funzione spekzr_c abbiamo ottenuto una rappresentazione vettoriale, di posizione e di velocità secondo le componenti x, y e z riferite ad uno specifico istante temporale, rappresentato come UTC e corretto automaticamente in TDB.

il passo ulteriore è convertire le coordinate rettangolari in sferiche. Ci viene in aiuto una funzione C dello spice toolkit, recrad_c, che fornisce distanza, ascensione retta e declinazione.

Brevemente descrivo il codice, vi sarà già chiaro come modificare opportunamente la classe CSpice.java per la sua implementazione.

Cominciamo con la signature della funzione, presente nella documentazione del jpl toolkit:

   void recrad_c ( ConstSpiceDouble    rectan[3],
                   SpiceDouble       * range,
                   SpiceDouble       * ra,
                   SpiceDouble       * dec      ) 

Brief_I/O
 
   VARIABLE  I/O  DESCRIPTION 
   --------  ---  -------------------------------------------------- 
   rectan     I   Rectangular coordinates of a point. 
   range      O   Distance of the point from the origin. 
   ra         O   Right ascension in radians. 
   dec        O   Declination in radians. 

Il rendering in codice Java è quello che segue:

    public interface CLibrary extends Library {
        ......
        double recrad_c(double[] rectan, DoubleByReference range, DoubleByReference ra, DoubleByReference dec);
        
    }
    double[] reclat_c(double[] rectan, DoubleByReference radius, DoubleByReference longitude, DoubleByReference latitude){
        radius.setValue(0.0); longitude.setValue(0.0); latitude.setValue(0.0);        
        INSTANCE.reclat_c(rectan, radius, longitude, latitude);
        double[] ret = new double[6];
        ret[0] = rectan[0]; ret[1] = rectan[1]; ret[2] = rectan[2]; 
        ret[3] = radius.getValue(); ret[4] = longitude.getValue(); ret[5] = latitude.getValue();
        return ret;
    }

La definizione di dettaglio della funzione va invece semplicemente accodata alle precedenti. Come già in precedenza, in C la funzione non ritorna valore, in quanto le variabili vengono modificate per riferimento ed estratte al di fuori della funzione stessa. In Java preferisco creare artificialmente un array di double come valore di ritorno. Come già discusso in precedenza, nella sezione declarativa dell'interfaccia le funzioni che restituiscono un array vanno dichiarate per tipo, non per array del tipo, che invece è il valore di ritorno delle stesse e, come tale, rappresentato nella sezione di definizione. Per il resto si segue la procedura già vista in precedenza. Vediamo le cose essenziali:

La funzione accetta un vettore di tre elementi (le coordinate rettangolari di posizione, già viste come valore di ritorno della funzione spekzr_c, e modifica tre puntatori a variabile double, fornite come parametri di input. Nulla di nuovo, la funzione java sarà modificata per restituire tre double, ottenuti estraendo il relativo valore dalle variabili dichiarate DoubleByReference con il metodo getValue(). La restituzione di un array di 6 double contenente anche le coordinate rettangolari fornite in input non è necessaria, l'ho implementata solo per controllo.

Con questo post termino la parte di pura stesura del codice. Dal prossimo post iniziamo a creare un applicativo per usare quento abbiamo realizzato finora. Al prossimo post.

Thursday, March 7, 2019

Calcolo di posizione con JPL toolkit - nuove funzioni

Per completare il nucleo essenziale delle funzioni usate per pilotare l'accesso al jpl toolkit scriviamo tre nuove, e per il momento, ultime, funzioni. Il toolkit è molto ampio, c'è ancora parecchio da sviluppare, ma per l'astrologia serve molto poco.

Spekzr_c

Questa funzione serve per il calcolo di posizione, espressa in coordinate rettangolari e vettore velocità, di un corpo celeste dati il corpo obiettivo, il punto di osservazione, l'istante temporale e alcune altre variabili che non utilizzeremo. Vediamo per prima cosa la signature, ripetendo quello che abbiamo già visto con le ctypes di Python:

   void spkezr_c ( ConstSpiceChar     *targ,
                   SpiceDouble         et,
                   ConstSpiceChar     *ref,
                   ConstSpiceChar     *abcorr,
                   ConstSpiceChar     *obs,
                   SpiceDouble         starg[6],
                   SpiceDouble        *lt        )

   Variable  I/O  Description 
   --------  ---  -------------------------------------------------- 
   targ       I   Target body name. 
   et         I   Observer epoch. 
   ref        I   Reference frame of output state vector. 
   abcorr     I   Aberration correction flag. 
   obs        I   Observing body name. 
   starg      O   State of target. 
   lt         O   One way light time between observer and target. 
 
   starg       is a Cartesian state vector representing the position 
               and velocity of the target body relative to the 
               specified observer. `starg' is corrected for the 
               specified aberrations, and is expressed with respect 
               to the reference frame specified by `ref'. The first 
               three components of `starg' represent the x-, y- and 
               z-components of the target's position; the last three 
               components form the corresponding velocity vector. 
 
               The position component of `starg' points from the 
               observer's location at `et' to the aberration-corrected 
               location of the target. Note that the sense of the 
               position vector is independent of the direction of 
               radiation travel implied by the aberration 
               correction. 
 
               The velocity component of `starg' is the derivative
               with respect to time of the position component of
               `starg.'
 
               Units are always km and km/sec. 

La documentazione ci dice che, dati il target body name (stringa), l'observer epoch (double), il reference frame (il nostro opzionale kernel), il flag di correzione dell'aberrazione (che non usiamo), il nome del corpo di osservazione (la Terra nel nostro caso - Stringa) e dun vettore double di 6 elementi e un eventuale puntatore a double (light time, non utilizzato), possiamo accedere ad un vettore posizione (coordinate x,y,z) e un vettore velocità (vx, vy, vz) deferenziando il vettore double che abbiamo fornito in input).

Siccome il codice sta diventando troppo lungo per incorporarlo per intero, scrivo solo i segmenti, avrete capito che i suguenti frammenti vanno accodati al codice già scritto nei post precedenti, uno all'interno della public interface CLibrary e uno in coda. Attenti però a come definiamo il valore di ritorno, nel caso vogliamo dereferenziare un vettore: nella dichiarazione dentro l'interface indicheremo solo il tipo di dati, nella definizione il double sarà dichiarato come array:

public class CSpice {
    private static final CLibrary INSTANCE;

    static {
        INSTANCE = Native.load("cspice", CLibrary.class);
    }

    public interface CLibrary extends Library {
     ...........

        double spkezr_c(String targ, double et, String ref, 
                     String abcorr, String obs, 
                     double[] starg, DoubleByReference lt);
        }

     // funzione spekzr_c
    }
 
    double[] spkezr_c(String targ, double et, String ref, 
                      String abcorr, String obs, double[] starg, DoubleByReference lt){
        
             for (int i = 0; i < 6; i++){        
                 starg[0] = 0.0;
             }
             INSTANCE.spkezr_c(targ, et, ref, abcorr, obs, starg, lt);
             return starg;
    }

L'array starg, come valore di ritorno, andrà scandito con un ciclo for per recuperare i singoli valori dei due vettori.

Estendiamo il file Main.java:

        ....
        DoubleByReference p = new DoubleByReference();
        System.out.println("Secondi dal J2000: "+spice.str2et_c("2000-01-01T12:00:00", p));
        System.out.println("\n");
        
        // spkezr_c        
        String timestring = "2017-08-10T18:53:22";
        double et = spice.str2et_c(timestring, p);
        String ref = "J2000";
        String target = "SATURN_BARYCENTER";
        String observer = "EARTH";
        String abcorr = "NONE";
        double[] starg = new double[6];
        DoubleByReference lt = new DoubleByReference();
        double[] d = new double[6]; 
        d = spice.spkezr_c(target, et, ref, abcorr, observer, starg, lt);
        String[] elem = {" x: "," y: "," z: ","vx: ","vy: ","vz: "};
        System.out.println("Vettore posizione e velocità di Saturno baricentro per il " + timestring + "\n");
        for (int i = 0; i < 6; i++){
            System.out.println(elem[i]+ " " + d[i]);            
        }
        System.out.println("\n");

In breve, inizializziamo le variabili di input, comprese quelle che serviranno per estrarre i valori di ritorno, secondo il loro tipo, chiamiamo la funzione e scompattiamo l'array.

Facciamo una prova:

ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ javac -classpath .:jna-5.2.0.jar -g Main.java
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ java -classpath .:jna-5.2.0.jar Main
Valore di Pi greco: 3.141592653589793
Secondi in un giorno: 86400.0
Numero di gradi per radiante: 57.29577951308232
OK Kernel caricati
Secondi dal J2000: 64.18392728473108


Vettore posizione e velocità di Saturno baricentro per il 2017-08-10T18:53:22

 x:  -2.1891291720093992E8
 y:  -1.2962750669325852E9
 z:  -5.289702916654781E8
vx:  -10.299865176779855
vy:  -20.74633831944049
vz:  -9.37634837706102




OK Kernel dismessi
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ 

Come potete verificare, ho ottenuto gli stessi valori, ma in notazione decimale anzichè esponenziale, che avevo ottenuto con python ctypes; per le verifiche di accuratezza potere rifarvi a questo post precedente. Gli input erano identici, quindi non occorre ripetere le verifiche di accuratezza.

Nei prossimi post aggiungeremo le funzioni di conversione da coordinate rettangoli a sferiche equatoriali ed eclittiche utilizzando due diversi frame di riferimento.

Wednesday, March 6, 2019

Caricare i kernel - CSPICE data types - Ulteriori funzioni

Proseguiamo l'estensione delle funzioni del toolkit accessibili tramite JNA. Come già visto nei post dedicati al linguaggio Python, il toolkit fornisce due metodi che possiamo utilizzare per il caricamento e per la dismissione dei kernel. I kernel sono i file dati utilizzati dal toolkit, ce ne sono di vario tipo, per una disamina completa potete leggere questa pagina della documentazione. Per ora ci intereressa caricare il kernel delle effemeridi (de421.bsp o simili) e il naif0012.tls per i leapsecond. Quindi aggiungeremo in seguito il kernel per specificare un frame di riferimento customizzato (trueepoch.tf)

Nel piccolo file interfaccia creato nel post precedente era già integrata la dichiarazione delle due funzioni di libreria furnsh_c e unload_c, ora ne scriveremo la definizione.

Partiamo dalle signature:


        void furnsh_c ( ConstSpiceChar  * file ) 
        void unload_c ( ConstSpiceChar  * file )

In Java le tradurremo come:


        void furnsh_c(String s);
        void unload_c(String s);

e la corrispondente definizione sarà:


    public void furnsh_c(String s){    
        INSTANCE.furnsh_c(s);        
        return;
    }

    public void unload_c(String s){
        INSTANCE.unload_c(s);
        return;
    }

Rivediamo il file CSpice.java e Main.java modificati:

import com.sun.jna.*;
import com.sun.jna.ptr.*;

public class CSpice {
    private static CLibrary INSTANCE;

    static {
        INSTANCE = Native.load("cspice", CLibrary.class);
    }

    public interface CLibrary extends Library {
        double pi_c();
        double spd_c();
        double dpr_c();
        void furnsh_c(String s);
        void unload_c(String s);

    }
    public double pi_c() {
        return INSTANCE.pi_c();
    }

    public double spd_c() {
        return INSTANCE.spd_c();
    }

    public double dpr_c() {
        return INSTANCE.dpr_c();
    }

    public void furnsh_c(String s){    
        INSTANCE.furnsh_c(s);        
        return;
    }

    public void unload_c(String s){
        INSTANCE.unload_c(s);
        return;
    }

}

e
public class Main {

     public static void main(String[] args) {
        CSpice spice = new CSpice();
        System.out.println("Valore di Pi greco: " + spice.pi_c());
        System.out.println("Secondi in un giorno: " + spice.spd_c());
        System.out.println("Numero di gradi per radiante: " + spice.dpr_c());
        spice.furnsh_c("de421.bsp");
        spice.furnsh_c("naif0012.tls");
        System.out.println("OK Kernel caricati");
        spice.unload_c("de421.bsp");
        spice.unload_c("naif0012.tls");
        System.out.println("OK Kernel dismessi");        
     }
}

Ricompiliamo il tutto ed eseguiamo:

ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ javac -classpath .:jna-5.2.0.jar -g Main.java
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ java -classpath .:jna-5.2.0.jar Main
Valore di Pi greco: 3.141592653589793
Secondi in un giorno: 86400.0
Numero di gradi per radiante: 57.29577951308232
OK Kernel caricati
OK Kernel dismessi
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ 

Siamo pra pronti ad inserire la chiamata alla funzione che, data una stringa di formattazione di data e ora in formato ISO 8601, restituisce il numero di secondi TDB (Temps Dynamique Barycentrique) a partire dalla epoch J2000.

La signature della funzione è la seguente:


void str2et_c ( ConstSpiceChar * str,
                SpiceDouble    * et   )

dove str è la data in formato ISO, et è il puntatore al valore double fornito come parametro e che va recuperato come valore.

per i puntatori abbiamo già considerato l'import delle classi del blocco com.sun.jna.ptr (seconda riga del file CSpice). La traduzione in Java sarà la seguente:


       double str2et_c(String s, DoubleByReference p);

per la dichiarazione e


    double str2et_c(String s, DoubleByReference p){
        INSTANCE.str2et_c(s,p);
        return p.getValue();
    }

per la definizione.

In buona sostanza: la funzione str2et_c, che nel toolkit restituisce un void, viene trasformata da noi in modo da restituire un double, che corrisponde al nuovo contenuto della variabile p, modificato dalla funzione ed estratto (dereferenziato, con la terminologia usata dal linguaggio C) con il metodo getValue(). Molto simile, nel procedimento, a quanto già fatto con python ctypes.

Rivediamo i file CSpice.java e Main.java con le ultime modifiche:

import com.sun.jna.*;
import com.sun.jna.ptr.*;

public class CSpice {
    private static CLibrary INSTANCE;

    static {
        INSTANCE = Native.load("cspice", CLibrary.class);
    }

    public interface CLibrary extends Library {
        double pi_c();
        double spd_c();
        double dpr_c();
        void furnsh_c(String s);
        void unload_c(String s);
        double str2et_c(String s, DoubleByReference p);

    }
    public double pi_c() {
        return INSTANCE.pi_c();
    }

    public double spd_c() {
        return INSTANCE.spd_c();
    }

    public double dpr_c() {
        return INSTANCE.dpr_c();
    }

    public void furnsh_c(String s){    
        INSTANCE.furnsh_c(s);        
        return;
    }

    public void unload_c(String s){
        INSTANCE.unload_c(s);
        return;
    }

    double str2et_c(String s, DoubleByReference p){
        INSTANCE.str2et_c(s,p);
        return p.getValue();
    }

}
import com.sun.jna.ptr.DoubleByReference;

public class Main {

     public static void main(String[] args) {
        CSpice spice = new CSpice();
        System.out.println("Valore di Pi greco: " + spice.pi_c());
        System.out.println("Secondi in un giorno: " + spice.spd_c());
        System.out.println("Numero di gradi per radiante: " + spice.dpr_c());
        spice.furnsh_c("de421.bsp");
        spice.furnsh_c("naif0012.tls");
        System.out.println("OK Kernel caricati");

        DoubleByReference et = new DoubleByReference();
        System.out.println("Secondi dal J2000: "+spice.str2et_c("2000-01-01T12:00:00", et));
        
        spice.unload_c("de421.bsp");
        spice.unload_c("naif0012.tls");
        System.out.println("OK Kernel dismessi");        
     }
}

E ricompiliamo

ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ javac -classpath .:jna-5.2.0.jar -g Main.java
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ java -classpath .:jna-5.2.0.jar Main
Valore di Pi greco: 3.141592653589793
Secondi in un giorno: 86400.0
Numero di gradi per radiante: 57.29577951308232
OK Kernel caricati
Secondi dal J2000: 64.18392728473108
OK Kernel dismessi
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ 

Tuesday, March 5, 2019

Marshalling di alcune funzioni riferite a costanti fondamentali

Come ho fatto per il linguaggio Python, scrivero' per prime alcune funzioni in Java finalizzate al recupero di valori di costanti matematiche o astronomiche di rilievo (pi greco, numero di secondi in un giorno, gradi per radiante.

Le funzioni del toolkit che consentono queste operazioni sono le seguenti, con la loro signature completa:

SpiceDouble pi_c  ( void )
SpiceDouble spd_c ( void )
SpiceDouble dpr_c ( void )

Nel file CSpice.java introdurro' le seguenti modifiche:

import com.sun.jna.*;
import com.sun.jna.ptr.*;

public class CSpice {
    private static CLibrary INSTANCE;

    static {
        INSTANCE = Native.load("cspice", CLibrary.class);
    }

    public interface CLibrary extends Library {
        double pi_c();
        double spd_c();
        double dpr_c();
        void furnsh_c(String s);
        void unload_c(String s);

    }
    public double pi_c() {
        return INSTANCE.pi_c();
    }

    public double spd_c() {
        return INSTANCE.spd_c();
    }

    public double dpr_c() {
        return INSTANCE.dpr_c();
    }

}

Nell'interface ho definito le funzioni traducendo i tipi di variabili SpaceDouble del toolkit nel tipo double di Java e il tipo void del parametro delle funzioni in uno spazio vuoto. In questo caso il marshalling è particolarmente semplice. La stesura effettiva delle funzioni relative è appena poco più estesa: le funzioni prima dichiarate diventano metodi della INSTANCE della libreria nativa cspice.

Per finire modifichiamo opportunamente la classe Main.java per estrarre i valori delle costanti dichiarate tramite i metodi associati:

public class Main {

     public static void main(String[] args) {
        CSpice spice = new CSpice();
        System.out.println("Valore di Pi greco: " + spice.pi_c());
        System.out.println("Secondi in un giorno: " + spice.spd_c());
        System.out.println("Numero di gradi per radiante: " + spice.dpr_c());

     }
}

A questo punto ricompiliamo il tutto:

ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ javac -classpath .:jna-5.2.0.jar -g Main.java
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ java -classpath .:jna-5.2.0.jar Main
Valore di Pi greco: 3.141592653589793
Secondi in un giorno: 86400.0
Numero di gradi per radiante: 57.29577951308232
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ 

Come è facile vedere, il risultato è perfettamente corrispondente a quello atteso. Il più è fatto. Da qui in poi estenderemo le funzioni usate fino a riprodurre quanto abbiamo già realizzato con il linguaggio python. Nel post successivo iniziamo a definire funzioni un po' più complesse

JNA e jpl toolkit - creazione dell'interfaccia

Per chiamare da Java le funzioni della shared library, dobbiamo imparare le fondamentali tecniche di interfacciamento e di marshalling (cioè di definizione di formati dati di interscambio tra codice gestito (Java) e non gestito (libreria C).

Prima di tutto scarichiamo la libreria in formato .jar dell'ultima versione disponibile di JNA, che troviamo QUI.


Cominciando dall'interfaccia, utilizzeremo un template, cioè un po' di codice boilerplate, come lo chiamano gli Americani, per renderci la vita più semplice.

import com.sun.jna.*;
import com.sun.jna.ptr.*;

public class CSpice {
    private static CLibrary INSTANCE;

    static {
        INSTANCE = Native.load("cspice", CLibrary.class);
    }

    public interface CLibrary extends Library {
//<-----------------definizione dei prototipi delle funzioni------------>
    }
//<-----------------chiamata alle funzioni, una alla volta-------------->
}

Spiegazione: l'import riguarda il core della libreria JNA e il complesso delle classi per la gestione dei puntatori, lo vediamo nel corso dello sviluppo. La classe pubblica CSpice conterrà tutto il codice per la creazione di un oggetto che sarà l'interfaccia da usare. Per il momento dò per scontato il resto del codice, fatta eccezione per la definizione statica dell'INSTANCE come
Native.Load("cspice", CLibrary.class).
Qui la libreria libcspice.so è richiamata esplicitamente, togliendo dal nome il prefisso lib e l'estensione .so. Questo vale per qualsiasi altra situazione in cui una generica libreria .so è utilizzata: libHelloWorld.so -> INSTANCE = Native.load("HelloWorld", CLibrary.class);.

Buttiamo giù un veloce file di classe Main.java, giusto per ospitare un metodo main:

public class Main {

     public static void main(String[] args) {
        CSpice spice = new CSpice();
     }
}

Facciamo una compilazione di prova: ricordiamoci di richiamare esplicitamente la libreria jna con l'opzione classpath:

javac -classpath .:jna-5.2.0.jar -g Main.java

Come potete apprezzare, la classe CSpice che contiene l'interfaccia viene richiamata implicitamente durante la compilazione di Main.java e il file di classe corrispondente viene creato come CSpice.class nella directory di progetto. Se facciamo il listing della directory vediamo i file seguenti:

ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ ls
 backup.java           CSpice.java     libcspice.so   naif0012.tls
'CSpice$CLibrary.class'   de421.bsp       Main.class     trueepoch.tf
 CSpice.class          jna-5.2.0.jar   Main.java
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$

Proviamo a lanciare il programma con il comando seguente:

java -classpath .:jna-5.2.0.jar Main

Se il comando non produce output vuol dire che va tutto bene

Mi fermo qui, continuiamo nel prossimo post.

JAVA, JNA, codice unmanaged e marshalling

Lavorare su codice nativo non gestito da un ambiente operativo diverso da quello di produzione non è sempre facile, ma spesso non avete alternative. Pensate al nostro JPL toolkit, scritto in C e Fortran. Avreste voglia di riscrivere tutto in Python o in Java? sono centinaia di funzioni, non è il caso. Questo vale per molte situazioni diverse, pensate alle librerie scientifiche compilate in Fortran negli anni dei pionieri, o a tutto il codice generato dalle vecchie versioni Visual Studio (non so se interoperabilità COM vi ricorda qualcosa). Anche la nostra piccola shared library compilata qualche post fa, libcspice.so o la sua equivalente in Windows libcspice.dll rischia di restare un bel giocattolo se non troviamo il modo di riutilizzarla con i compilatori o interpreti scritti in altri linguaggi. Abbiamo esplorato un po' ctypes di Python, ora voglio iniziare, come accennato nel post precedente, a JNA di Java.

Per questo piccolo progetto non usero', almeno prima di un certo numero di post, un IDE grafico come Eclipse o Netbeans, preferisco, per semplicità e beneficio didattico, fare tutte le compilazioni da riga di comando. Userò, come compilatore C per JPL SPICE il GNU gcc (ma trovate tutto già spiegato nei post precedenti) e come compilatore java openjdk (una qualsiasi versione successiva alla 7 va bene, attualmente nella mia Ubuntu 18.10 uso la versione 11.0.2). Se operate in ambiente windows, vista l'elevata portabilità di Java, potremo facilmente usare la linea di comando in una finestra terminale, se volete un compilatore C per questa piattaforma avete ampia libertà di scelta, c'è Visual Studio, oppure Mingw che riproduce un ambiente POSIX sotto Windows, vedete voi cosa preferite, alla fine di tutto faro' io un post apposito).

La prima cosa che faremo sarà creare una directory dedicata al progettino Java sul desktop o in qualsiasi posizione comoda per lavorare. All'interno collocheremo alcuni file creati precedentemente, come vi mostra lo schemino seguente.

ubuntu@ubuntu-desktop:~/Scrivania/CSpice$ ls -l
totale 20596
-rw-rw-r-- 1 ubuntu ubuntu 16788480 mar  2 10:56 de421.bsp
-rwxrwxr-x 1 ubuntu ubuntu  4281824 mar  2 10:45 libcspice.so
-rw-rw-r-- 1 ubuntu ubuntu     5257 mar  2 11:00 naif0012.tls
-rwxrwxrwx 1 ubuntu ubuntu      598 ago 12  2017 trueepoch.tf
ubuntu@ubuntu-desktop:~/Scrivania/CSpice$

Riepilogando:

libcspice.so è la libreria condivisa che contiene le funzioni del JPL toolkit (per un accesso veloce ai metodi di compilazione partite da Questo post in avanti)

de421.bsp è il file delle effemeridi fornito dal sito ftp della NASA. Se volete un file più recente c'è ampia scelta, il 421 ha il pregio di essere abbastanza leggero e portabile anche in ambiente Android.

naif0012.tls è il kernel generico che contiene l'elenco aggiornato dei leapsecond (secondo intervalare in italiano), ricordate che si tratta della ".. correzione del tempo applicata al tempo coordinato universale (UTC) per mantenerlo allineato al giorno solare medio, l'ente responsabile della regolazione è l'International Earth Rotation and Reference Systems Service (IERS) (sorgente Wikipedia). Trovate l'elenco dei kernel disponibili in questa pagina.

Infine trueepoch.tf è il kernel creato da me seguendo le istruzioni fornite dal NAIF in questa pagina , che servirà più avanti per svincolarsi dal frame di riferimento inerziale e spostarsi su un frame dinamico come quello che usano gli astrologi. Trovate i dettagli per la sua costruzione nei miei post precedenti già citati.

A questo punto possiamo iniziare a scrivere un po' di codice Java. Per semplicità metto tutto nella stessa directory, rinunciando a creare un package, nulla vieta di strutturare meglio il progetto, lo faremo in seguito.
Dopo questa lunga premessa, passo al post successivo per la scrittura delle prime classi Java che ci servono.

Monday, March 4, 2019

JPL Spice Toolkit, Java, JNA - come utilizzare la libreria nativa libcspice.so in ambiente Java

Sono due anni che prometto di occuparmi di domificazione e ogni volta trovo una scusa per rinviare. Sarà che non sento molto il problema, sarà che ce n'è fin troppi di sistemi di domificazione, sarà che il più usato (Placidus) ha un comportamento degenere alle latitudini estreme, per un motivo o per l'altro trovo sempre qualcos'altro di cui occuparmi.

Da qualche giorno sto pasticciando con JAVA (vi ricordo che non sono un professionista né dell'informatica né dell'astrologia), l'ho usato un po' in passato per ambienti desktop e un po' per creare qualcosa per Android. Mi chiedevo se era possibile usare una libreria nativa come la libreria dinamica libcspice.so che avevo compilato qualche post addietro, in piattaforma JAVA, un po' come abbiamo fatto con la libreria ctypes di Python.

Il metodo effettivamente esiste, anzi ne esiste più di uno, c'è SWIG, c'è JNI, ma poi ho cominciato a usare JNA e ha funzionato abbastanza presto. Ho, pertanto, deciso di scrivere un piccolo tutorial su JNA, visto che quello che si trova in giro, anche in ambienti seri come Stack Overflow è poco più di Hello World. (per inciso, se un giorno scoppierà una rivoluzione, sarà per l'esasperazione dei programmatori che si trovano sempre un HelloWorld inutile ogni volta che cercano qualche informazione sul web).

Se andate indietro di qualche post e rileggete quello che ho scritto su ctypes, vi renderete conto che è una libreria non di immediata comprensione, ma che, una volta imparate le basi, diventa estremamente soddisfacente. La stessa cosa vale per JNA, interfaccia in puro linguaggio JAVA per il binding di funzioni nascoste in una libreria di sistema, sia essa una DLL di Windows o una SO di Linux. Se avrete la pazienza di seguirmi, ho qualche piccolo trucco da esporre in chiaro.

Per rendere le cose più semplici, partirò, come ho fatto per Python ctypes, con il call di costanti presenti nel JPL toolkit e aggiungerò progressivamente delle chiamate a funzioni, facendovi partecipi di questo accostamento iniziale a JNA. Mi perdonerete se l'approccio potrà sembrare poco professionale, ma, ripeto, sono un programmatore amatoriale.

Al prossimo post, penso di scriverlo a giorni, le mie routine sono sufficientemente mature per una breve esposizione. A presto.

How to create a virtual linux machine with qemu under Debian or Ubuntu with near native graphics performance

It's been a long time since my latest post. I know, I'm lazy. But every now and then I like to publish something that other people c...