Thursday, November 16, 2023

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 can find useful.


Today I'm going to give you a simple and effective way of creating a virtual machine with QEMU that works fine and have a fast rendering of nice graphical programs that require some speed, such as simple videogames. I'm actually working with debian bookworm as a host system, and I want to create an Arch vm from scratch. For this purpose I will use a desktop iso for Arch.

Download and build QEMU from source

Most Linux distributions have QEMU in their repository, the problem is that the packages are often old versions. I need a recent version, possibly the latest, with pipewire enabled. So I will show you how to download the source package, uncompress it, configure it, and compile from source with make. At the time of writing the latest version is 8.2.0-rc0. Download it from this address.

Uncompress the file you have just downloaded in a directory of your choice using xz or your preinstalled graphic compression manager in your desktop or wherever you like. All the uncompressed files are contained in a directory named qemu-8.2.0-rc

Before compiling we need to download some debian packages, if you have not already installed them for other purposes. We'll use a script, in order to make our job simple and easy.

First of all, you need a compiler. I usually use gcc, to get it let's download and install the build-essential package using the first instruction in our script.

Open a terminal and write:

cd qemu-8.2.0-rc0

Now that you are within your qemu directory you will create a bash script to add some packages and procede to initial configuration:

nano pre-qemu.sh

Let's fill in the new created script with the following lines, one after another:

sudo apt install build-essential libusb-1.0-0-dev libgtk-3-dev \
       libpulse-dev libgbm-dev libepoxy-dev libspice-server-dev libglib2.0-dev \
       libfdt-dev libpixman-1-dev zlib1g-dev ninja-build libasound2-dev \
       libsdl2-* libslirp-dev libvirglrenderer-dev libpipewire-0.3-dev


./configure --target-list=x86_64-softmmu --enable-opengl --enable-sdl --enable-gtk \
       --enable-kvm --enable-guest-agent --enable-spice --enable-pipewire --audio-drv-list="alsa, default, \
       oss, pa, sdl" --enable-libusb --enable-slirp --enable-virglrenderer

save and exit your script with ctrl-o and ctrl-x

The first group of lines add some system packages, the second one provides an initial configuration that the compiler will use to enrich the availability of options in QEMU

make the script executable:

chmod +x pre-qemu.sh

Now, from within your qemu directory, you can procede to the compilation:

./pre-qemu.sh
make -j8

The number after j depends on the actual number of cores and threads in your processor, adjust it according with your needs. You can verify the number of available cores from the command line with:

nproc

or

lscpu

or

cat /proc/cpuinfo

After some time the compilation will end. Move the compiled libraries into your system dirs:

sudo make install

Now you can check if qemu works using the following command:

qemu-system-x86_64 --version

It shoud show the following message:

QEMU emulator version 8.1.2
Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers

I have compiled only qemu for x86_64 as you can see from the pre-qemu script, but qemu can be compiled against a number of different architectures, namely i386, arm, aarch64, sparc, mips and so on. I'm not interested in these alternatives, as they are given for other systems emulation, so I don't bother with them.

Ok, now we are ready to create a working virtual machine.


You can download the Arch distro iso from the official website. I personally prefer to use a graphical installation with Calamares. The iso I use is this one. The references to the iso, in case you prefer one of your choice, must be updated in the installation script you're going to create.

Now it's to time create a working directory, in your desktop if you like, or elsewhere. Name it, let's say, Arch Let's copy your freshly downloaded iso inside.

Let's create a suitable container for our virtual machine

Open a terminal in the newly created Arch directory and write:

qemu-img create -f qcow2 arch.qcow2 256G

This command creates a qcow2 container for our virtual machine, it's a growing system up to 256G. Modify the capacity as you like.

Let's install ovmf, it's Open Virtual Machine Firmware, it's used to enable UEFI support for virtual machines:

sudo apt install ovmf

and the new pipewire audio server for debian bookworm, testing and unstable (Ubuntu users shoud follow the specific instructions given for that platform, see the related wiki).

sudo su 
goes into administrator mode, it requires your root password
apt install wireplumber pipewire-media-session- pipewire-audio pipewire-jack pipewire-alsa exit
back to user mode
systemctl --user --now enable wireplumber.service sudo apt install pipewire-pulse
to convert calls to pulseaudio to pipewire

Reboot your system

Check the effectiveness of last operation:

LANG=C aplay -L | grep -A 1 default

It shoud read:

default
    Playback/recording through the PulseAudio sound server

or something like that. It's a major improvement for your host system and I'm sure you will find extremely useful if you need to work with audio.

Now we can go ahead with a new script for Arch installation.



Arch installation

Let's create, in our Arch directory, an installation script.

cd into your Arch directory, open here a terminal and write:

nano install.sh

And that's it. Now we can write an installation script.

copy and paste all the following lines:

qemu-system-x86_64 \
-bios /usr/share/OVMF/OVMF_CODE.fd \
-m 8192 \
-M q35 -enable-kvm \
-cpu host \
-smp 2 \
-cdrom Calam-Arch-Installer-2023-11.iso \
-hda arch.qcow2 \

ctrl-o ctrl-x and make the script executable.

Two words of explanation: the call to qemu is followed by a number of options:

  • use UEFI for your virtual machine
  • give the emulated ram memory a capacity of 8Gb
  • use an advanced emulated machine and enable near native acceleration
  • align emulated cpu to the host one
  • give the processor two cores
  • load the iso in a simulated cdrom
  • make the arch.qcow2 the first hard disk of your vm

The subsequent events are more or less the same as a physical installation, so it's up to you to check the installation. The script is low profile, some better options are given in the next script. In case you see crashes during Arch installation using Calamares, it's probably related to a weak partitioning scheme. Maybe you want to manually provide a basic partitioning before installing Arch, it's how I managed to successfully complete the installation.



Launch the virtual Arch machine

As before, create a new script:

nano launch.sh

Inside of it write the following lines:

qemu-system-x86_64 \
-bios /usr/share/OVMF/OVMF_CODE.fd \
-device virtio-vga-gl \
-display sdl,gl=on \
-m 8192 \
-M q35,accel=kvm,kernel-irqchip=split \
-cpu Skylake-Server-v4 \
-smp 4 \
-device intel-iommu,intremap=on,caching-mode=on \
-boot c \
-drive file=arch.qcow2,media=disk \
-nic user \
-usb -device usb-mouse -device usb-kbd  \
-parallel none -nodefaults \
-audiodev pipewire,id=audio0 \
-device intel-hda -device ac97,audiodev=audio0 \

You'll note that I have added a few options

  • changed emulated cpu to Intel (not necessary)
  • given the cpu 4 cores
  • configured video output to virtio driver and sdl with an opengl option
  • enabled iommu for virtualization
  • enabled pipewire for audio

for some options I simple made copy and paste, I'm not sure about the possible alternatives. It simply works, feel free to change each option (even by trials and errors, refer to the qemu manual for better comprehension)

As before, ctrl-o ctrl-x and make the script executable the usual way

When you launch the script, the Arch vm will appear in all its shining beauty, you can update packages, modify your desktop, do all what you would normally do with a real machine. The call to UEFI firmware, in the boot menu, allows you to change, among other options, the virtual machine resolution to a more defined one.

Try to install a graphic game, I usually use supertuxkart to check the availability of a fast graphic server. You'll be surprised by the results.

If all goes well, let me know in the comments. See you next time.

Sunday, January 2, 2022

How to write a basic C program to calculate ecliptic longitudinal position of astrological planets

In the title I speak of "astrological" planets. As you know, Sun and Moon aren't planets in astronomy, neither is Pluto, which has been declassified as a planet in recent times. In Astrology, planet means "moving object" and this definition includes Sun, Moon, the solar system planets plus Pluto. Asteroids like Vesta, Chiron, Juno and many other are called asteroids as well in Astrology. Some object are instead considered "fixed", the Zodiac as a whole, and the most visible stars. Other objects are tipically astrological, such as the Ascendant, House cusps, the midpoints, the Arabian parts and some other less frequently used elements. All the related calculus techniques are based on astrological assumptions.

In this post we'll gather all those elements which have the purpose to let us calculate, within the available accuracy and precision, the longitudinal position of planets, i.e. the one and only spatial coordinate that astrology take into account. When you see the zodiac circle in a natal theme, don't think of it as a planar representation of universe. Instead, think of it as a monodimentional representation, like a segment, which has been coiled on itself to join the head and the tail. I'll talk about this argument later, about the inconsistency of the actual methods used to calculate house cusps.

Basic C program and dependencies

Jumping to the start, we must gather a few essential resources:

  1. One bsp file, from this site https://ssd.jpl.nasa.gov/ftp/eph/planets/bsp/

  2. de441.bsp is huge, but we can extract a subset. Don't use your phone to download it, it's more than 3GB large, use your PC.

  3. naif0012.tls file, is a generic kernel which contains the annual leapseconds as they are established by IERS (International Earth Rotation and Reference System Service).You can download it at this link. If you want to download it directly in your phone, use the following command under Termux in your Termux home directory:

    wget https://naif.jpl.nasa.gov/pub/naif/generic_kernels/lsk/naif0012.tls

  4. The file mytrueepoch.tf, we use it to define a non inertial system of coordinates, in essence we refer longitudinal coordinates to an estimated vernal point as in traditional occidental tropical astrology. You can create a file in your home Termux directory in your phone:

    touch mytrueepoch.tf
    

    then copy the following code inside of it:

    KPL/FK 
    
    	private Kernel FK, dynamic, geocentric, time-based. It 
    integrates precession, nutation and ecliptic obliquity models 
    
    \begindata
    
       FRAME_MYTRUEEPOCH = 1987654
       FRAME_1987654_NAME = 'MYTRUEEPOCH'
       FRAME_1987654_CLASS = 5
       FRAME_1987654_CLASS_ID = 1987654
       FRAME_1987654_CENTER = 399
       FRAME_1987654_RELATIVE = 'J2000'
       FRAME_1987654_DEF_STYLE = 'PARAMETERIZED'
       FRAME_1987654_FAMILY = 'MEAN_ECLIPTIC_AND_EQUINOX_OF_DATE'
       FRAME_1987654_PREC_MODEL = 'EARTH_IAU_1976'
       FRAME_1987654_OBLIQ_MODEL = 'EARTH_IAU_1980'
       FRAME_1987654_ROTATION_STATE= 'ROTATING'
    
    \begintext
    
  5. obviously, the library file libcspice.so that you have generated from the jpl toolkit

Once collected the resources (the other important one is in the cspice dir) you are almost ready. Let's go back to the big file you downloaded in yor PC: de441.bsp

As large as it is, it's of no use. We must extract a subset based on a useful date interval (from an astrological point of view, of course) then transfer that newly created bsp file to your phone memory.

While the download is going on, you can take a little time to install a graphical scp. For Windows your winning candidate is WinSCP, simple and fast, for Linux, if you don't like scp and the command line, you can use the connection to server ssh that your file manager (nautilus, caja, nemo, etc) provides.

When the download of the monster de441.bsp ends, you have got in your download directory the mother ephemeris, from which you can extract any time interval you like. For my astrological purposes, an interval of time between 1500 AD and 2100 AD is largely adequate. Now we need some executable files from the cspice collection. You can't use what we have downloaded before. As we have seen, the executables are compiled with cgywin o aarch64 in mind. You may need a 64bit executable for windows of linux or macos, browse again to the jpl toolkit C sources page and download the tarball or the zip file that fits best your architecture. At present I'm using debian, so i'm going to download the PC, Linux, gCC, 64bit tarball cspice.tar.gz. If you are on Windows download the PC, Windows, MS Visual C, 64bit cspice.zip file. If your PC still has a 32bit architecture, choose the appropriate source.

If you unzip the cspice.tar.gz via tar or the cspice.zip via winzip or similar, you will see a replica of what you have already seen inside your smartphone. Change dir to cspice/exe, where you will find the already seen brief or brief.exe and another useful utility called spkmerge or spkmerge.exe. These are compiled for your PC architecture, so they'll surely work.

Copy brief(.exe) and spkmerge(.exe) to the directory where you have that giant 3gb file named de441.bsp

open a terminal in that directory and type:

./brief de441.bsp (in Linux)
brief.exe de441.bsp (in Windows)

You should see something like:

debian@debian11:~/Desktop/cspice$ ./brief de441.bsp
 
BRIEF -- Version 4.0.0, September 8, 2010 -- Toolkit Version N0066
 
 
Summary for: de441.bsp
 
Bodies: MERCURY BARYCENTER (1)  SATURN BARYCENTER (6)   MERCURY (199)
        VENUS BARYCENTER (2)    URANUS BARYCENTER (7)   VENUS (299)
        EARTH BARYCENTER (3)    NEPTUNE BARYCENTER (8)  MOON (301)
        MARS BARYCENTER (4)     PLUTO BARYCENTER (9)    EARTH (399)
        JUPITER BARYCENTER (5)  SUN (10)
        Start of Interval (ET)              End of Interval (ET)
        -----------------------------       -----------------------------
        13201 B.C. MAY 06 00:00:00.000      17191 MAR 15 00:00:00.000
 
debian11@debian11:~/Desktop/cspice$ 

the listed bodies are what serve our purpose. The time interval, as you can appreciate, is humongous, we don't need all that

Let's create an appropriate file to order the extraction of a subinterval, more adequate to our purposes.

Create a plain text file, let's call it extract.txt ad type inside of it:


   LEAPSECONDS_KERNEL  = naif0012.tls

   SPK_KERNEL          = from1501ADto2100AD.bsp
     SOURCE_SPK_KERNEL = de441.bsp
       BEGIN_TIME      = 01 JAN 1501 00:00:00.000
       END_TIME        = 31 DEC 2100 23:59:59.000

As you can see, I give the name of the leapsecond kernel (copy it to the same directory), the name of the destination bsp file (don't use dexxx, you are not allowed, according to the jpl policy) the source bsp file and the begin and end time required.

We're ready, type:

spkmerge extract.txt

In a fraction of time you will see a new file named from1501ADto2100AD.bsp, it's a subset of the big file. Using brief you can inspect it:

debian11@debian11:~/Desktop/cspice$ ./brief from1501ADto2100AD.bsp 
 
BRIEF -- Version 4.0.0, September 8, 2010 -- Toolkit Version N0066
 
 
Summary for: from1501ADto2100AD.bsp
 
Bodies: MERCURY BARYCENTER (1)  SATURN BARYCENTER (6)   MERCURY (199)
        VENUS BARYCENTER (2)    URANUS BARYCENTER (7)   VENUS (299)
        EARTH BARYCENTER (3)    NEPTUNE BARYCENTER (8)  MOON (301)
        MARS BARYCENTER (4)     PLUTO BARYCENTER (9)    EARTH (399)
        JUPITER BARYCENTER (5)  SUN (10)
        Start of Interval (ET)              End of Interval (ET)
        -----------------------------       -----------------------------
        1501 JAN 01 00:00:41.184            2101 JAN 01 00:01:08.183
 
debian11@debian11:~/Desktop/cspice$ 

It's rather large (64mb) but handier than the 3GB one. Using WinSCP o the the linux file manager, copy it to your phone home directory.

Creating a minimal C source file

I'm not an expert at C programming, but I'll try to write something simple and useful

Just a few preprocessor instructions, some functions and a main()

But first let's give a glance to our phone home directory. Access it using ssh from you PC, as we have seen before. It should be something like this:

~ $ ls
cspice      from1501ADto2100AD.bsp  mytrueepoch.tf  storage
cspice.tar  importCSpice.csh        naif0012.tls 	libcspice.so
~ $ 

Let's move our dependency files in a new subdirectory.

~ $ ls
clear   cspice.tar              importCSpice.csh  naif0012.tls
cspice  from1501ADto2100AD.bsp  mytrueepoch.tf    storage
~ $ mkdir build
~ $ mv from1501ADto2100AD.bsp naif0012.tls mytrueepoch.tf libcspice.so build/
~ $ ls
build  cspice  cspice.tar  importCSpice.csh  storage
~ $ cd build
~/build $ ls
from1501ADto2100AD.bsp  mytrueepoch.tf  libcspice.so	naif0012.tls
~/build $ 

Now we need a helper C file with some useful functions. This first example will only include two functions, one for converting radians to degrees (or hours) and decimal fractions, and another for converting the result in a more readeable form, with degrees, minutes and seconds.

We'll call the first r2d and the second ddd2dms. If you have never written a C script before, I'll give some useful hints

Let's begin writing two different files, one header and one source. We'll call the first tools.h and the second tools.c

tools.h

/*In most cases we want to avoid repeated callings to the header files.
  The following preprocessors instructions let you compile the header only once
  and only if not called before.
 */

#ifndef TOOLS_H
#define TOOLS_H

/* The following lines are preprocessor include instructions.
   The first one takes all the declarations contained in our cspice library and
   makes them available to the program.
   The second one calls the stdio component of the standard library. We use it
   mainly for printing the results of calculations.
   The third one supports mathematical calculations.
*/

#include "SpiceUsr.h"
#include <stdio.h>
#include <math.h>

/* Then we declare a structure, which is a complex set of data under one
   new variable type, to represent sign, degrees, minutes and seconds in every
   sexagesimal value.
*/

typedef struct _dms {
	int sign;
	int units;
	int minutes;
	int seconds;
} dms;

// Next we create a function which converts radians to degrees

double r2d (double rads);

// Then a functions which takes a decimal  and converts it to a sexagesimal.

dms ddd2dms(double dec_units);

#endif

tools.c

#include "tools.h"

double r2d (double rads){
		if (rads < 0){
			rads = rads + 2*M_PI;
		}
		return rads/M_PI*180.0;
}

dms ddd2dms(double dec_units){

	dms d2s;
	d2s.sign = '+';
    if (dec_units < 0){
        d2s.sign = '-';
        dec_units *= -1;
    }
    double total_seconds = ceil(dec_units * 3600.0);
    double seconds = fmod(total_seconds, 60.0);
    int total_minutes = (int)((total_seconds - seconds)/60.0);
    double minutes = fmod(total_minutes, 60.0);
    int units = (int)((total_minutes - minutes)/60.0);
    d2s.units = units;
    d2s.minutes = (int)minutes;
    d2s.seconds = (int)seconds;
    return d2s;
}

You can see that the header file explicitly recalls the SpiceUsr.h header of the cspice library. Let's assume that the compiler and the linker know where to find it (it's not true, but we'll get to it a few lines ahead). Apart from the SpiceUsr.h header, we call the stdio.h and math.h from the standard library. The first is used mainly for communications with the standard io, the second for calling mathematical methods. In the source file, tools.c, we simply call tools.h which automatically calls the other library components. As you can see, the header file contains only the declarations, the definitions are in the source file.

Now, just for testing, a little C source file, named test_tools.c

test_tools.c

#include "tools.h"

int main(){

	double radians = 12.3456;
	double decimals = r2d(radians);
	dms sexas;
	printf("radians: %f\n", radians);
	printf("decimals: %f\n", decimals);
	sexas = ddd2dms(decimals);
	printf("sexagesimal: %c %d %d %d\n", sexas.sign, \
		sexas.units, sexas.minutes, sexas.seconds);
	/* Let's call the spice builtin variable for pi and print it
	   just to be sure that the spice library is available
	   Give a glance to "https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/info/mostused.html#Y"
       to learn more about Spice constants
       The jpl toolkit signature for pi is <SpiceDouble pi_c ( void )>
    */
	printf("Pi value: %f\n", pi_c());
	return 0;
}

This source file contains the main routine, which defines a double variable with a sample value, converts it from radians to degrees, than converts the result to a string with sign, degrees, minutes and seconds as a sequence of integer values.

It may seem that the job is done. Actually, even if the cspice library is in the same directory, the SpiceUsr.h file is in another directory, we must tell the compiler where to find it.

Let's write a little script, containing what is needed (I could have written a Makefile, we'll do it in the future). Let's call it test_tools.sh where sh is the standard extension for bash files.

Before going on, let's download the package termux-elf-cleaner from the repository. It removes some boring warnings that come out when we execute the compiled file.

pkg install termux-elf-cleaner

Below a possible test_tools.sh file

test_tools.sh

gcc -L. -g -Wall -O2 -Wl,-rpath=. \
    -o test_tools test_tools.c tools.c \
    -lm -ldl -lcspice \
    -I/data/data/com.termux/files/home/cspice/include/

termux-elf-cleaner test_tools

Let's analyze this script, I have broken it in a few lines to make it easier to understand

  1. gcc is the calling to the compiler (and to the linker in this case)
  2. -L. tells the compiler where to find the shared library in use, in this case libcspice.so in the same directory
  3. -g tells the compiler to generate debugging infos
  4. -Wall for maximizing warnings
  5. -O2 for a very good optimization
  6. -Wl,-rpath=. to embed the location of the shared library in the executable itself, in this case the working directory. It's important for giving access to the shared library during runtime execution. Other methods are available.
  7. -lm -ldl -lcspice are links to math library, libdl is the dynamic link library, lcspice stands for load the libcpice.so library from where you know, lib and .so are conventionally omitted.
  8. The capital I is the location where lies the unfamous SpiceUsr.h header. In this case I prefer to give the full path. If you are wandering how you can obtain that long string, try typing in your command line:
    pwd
    which stays for "print the working directory", and you'll get the full path. Of course, you must modify it to obtain the correct location of the header.

Let's make this script executable using chmod test_tools.sh followed by Enter

Then we launch its execution: ./test_tools.sh (remember to begin with ./, it's not a standard shell command.

~/build $ ./test_tools.sh
termux-elf-cleaner: Replacing unsupported DF_1_* flags 134217729 with 1 in 'test_tools'
~/build $ 

Ok, now we can launch the test_tools executable (if it say Permission denied, it means you have to chmod +x it, as for the .sh file before):

./test_tools

This is what you get:

~/build $ ./test_tools
radians: 12.345600
decimals: 707.350776
sexagesimal: + 707 21 3
Pi value: 3.141593
~/build $ 

We have the demonstration that our new born functions work fine and that the SpiceUsr.h header is correctly addressed. Let me know in your comments. See you in the next post. Happy New Year to all of you.

Wednesday, December 8, 2021

Creating and using a dynamic library from JPL toolkit

Generating a dynamic library

In a previous post I showed how to generate a dynamic library from jpl toolkit, for linux systems (libcspice.so) and for Windows (libcspice.dll).

Before we proceed to the compilation of the library in the Termux environment, let's talk of the programs that we have just compiled from sources launching the script importCSpice.csh.

Open Termux in your phone and activate ssh:

sshd (and press Enter, from now on I take it for granted)

In your PC open your terminal emulator (cmd on Windows, gnome-terminal or whatever in Linux) and type:

ssh u0_a119@192.168.1.120 -p 8022 (use your own coordinates, this is an example, see previous post)

when you are in, type:

~ $ ls -l
total 107956
drwx------ 9 u0_a119 u0_a119      4096 Apr  8  2017 cspice
-rw------- 1 u0_a119 u0_a119 110530560 Apr 10  2017 cspice.tar
-rw------- 1 u0_a119 u0_a119        84 Apr 10  2017 importCSpice.csh
drwx------ 2 u0_a119 u0_a119      4096 Dec  6 19:26 storage
~ $

the directory cspice has been created during the compilation process

it contains several subdirs:

~ $ ls cspice
data  doc  etc  exe  include  lib  makeall.csh  src
~ $

let's look inside cspice/exe

 $ ls cspice/exe
brief        ckbrief.exe   dskexp       inspekt.exe  msopck      spacit.exe    states      tictoc.exe  version
brief.exe    commnt        dskexp.exe   mkdsk        msopck.exe  spkdiff       states.exe  tobin       version.exe
chronos      commnt.exe    frmdiff      mkdsk.exe    simple      spkdiff.exe   subpt       tobin.exe
chronos.exe  dskbrief      frmdiff.exe  mkspk        simple.exe  spkmerge      subpt.exe   toxfr
ckbrief      dskbrief.exe  inspekt      mkspk.exe    spacit      spkmerge.exe  tictoc      toxfr.exe
~ $

all of these files are executables, but built for different systems. The ones without extension are used in aarch64 architecture, those ending with .exe are for Windows.

You can personally check the nature of these executables, but first you must install the package file:

pkg install file

now change to subdirectory cspice/exe:

cd cspice/exe

and type file brief

~/cspice/exe $ file brief
brief: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /system/bin/linker64, not stripped
~/cspice/exe $

this executable is one of our interest. We'll make a good use of it later. If you type file brief.exe instead:

~/cspice/exe $ file brief.exe
brief.exe: PE32+ executable (console) x86-64, for MS Windows
~/cspice/exe $

this exe file won't run in your phone, it's generated for cgywin, which is a posix compatible programming environment for Windows systems.

Just for curiosity, launch brief:

~/cspice/exe $ ./brief
WARNING: linker: /data/data/com.termux/files/home/cspice/exe/brief: unsupported flags DT_FLAGS_1=0x8000001

BRIEF -- Version 4.0.0, September 8, 2010 -- Toolkit Version N0066

   BRIEF is a command-line utility program that displays a summary for
   one or more binary SPK or binary PCK files. The program usage is:

      % brief [-options] file [file ...]

   The most useful options are shown below. For the complete set of
   options, run BRIEF with the -h option. The order of options is not
   significant. The case of option keys is significant: they must be
   lowercase as shown below.

      -c           display centers of motion/relative-to frames
      -t           display summary in a tabular format
      -a           treat all files as a single file
      -utc         display times in UTC calendar date format (needs LSK)
      -utcdoy      display times in UTC day-of-year format (needs LSK)
      -etsec       display times as ET seconds past J2000

   An LSK file must be provided on the command line to display times in
   UTC formats. FK file(s) must be provided on the command line to
   display names of any frames that are not built into the Toolkit.

~/cspice/exe $

Let's proceed to the creation of a dynamic library.

Change directory to cspice/src/cspice

~/cspice/src $ cd
~ $ cd cspice/src/cspice
~/.../src/cspice $ ls
F77_aloc.c     daftb.c     et2lst_c.c         lnknxt.c      rav2xf_c.c  spkr15.c       vnorm_c.c     zzeknres.c
SpiceCK.h      dafus_c.c   et2utc.c           lnkprv.c      rawio.h     spkr17.c       vnormg.c      zzeknrml.c
SpiceCel.h     dafwcr.c    et2utc_c.c         lnksiz.c      raxisa.c    spkr18.c       vnormg_c.c    zzekordc.c
... a lot of files ...
dafrfr_c.c     errint_c.c  lnkfsl.c           r_sqrt.c      spkr08.c    vlcomg_c.c     zzeklled.c
dafrrr.c       errprt.c    lnkhl.c            r_tan.c       spkr09.c    vminug.c       zzekllei.c
dafrs_c.c      errprt_c.c  lnkila.c           r_tanh.c      spkr10.c    vminug_c.c     zzeklltc.c
dafrwa.c       esrchc.c    lnkilb.c           radrec.c      spkr12.c    vminus.c       zzeklltd.c
dafrwd.c       esrchc_c.c  lnkini.c           radrec_c.c    spkr13.c    vminus_c.c     zzekllti.c
daft2b.c       et2lst.c    lnknfn.c           rav2xf.c      spkr14.c    vnorm.c        zzekmloc.c
~/.../src/cspice $

It's a huge amount of C sources, we need to compile them in a standard .so dynamic library.

within the same directory type:

gcc -Wall -O2 -fPIC -shared -o libcspice.so *.c -lm -ldl

You deserve a brief explanation of the options I have given to the compiler:

gcc is the compiler, here used as assembler, compiler and linker altogether, any further consideration is beyond our scope.

-Wall stands for the maximum warning alert level, during compilation you'll see a ton of warnings.

-O2 is the optimization level, set to a good level compatible with robustness.

-fPIC -shared is the combination of two instructions aimed at generating a shared library with Position Independent Code (PIC)

-o libcspice.so is the name I have choosen for the compiled library, "lib" and ".so" are the structural parts.

*.c are all the C sources that are present in this directory

-lm -ldl are linked libraries, math and dynamic loader

After some time and a big amount of warnings, hopefully no errors found, you'll see a new file in your directory, libcspice.so. Move it to your home directory, to have it handy

mv libcspice.so ~/

Ok, it's enough for now, see you in the next post

Tuesday, December 7, 2021

JPL Toolkit on Termux in a cheap chinese smartphone

My laziness is unforgivable. Even if I rarely have some interesting news to offer to readers (as you can see from the following lines, I'm still deeply interested in exploring basic planetary position calculus from an astrological point of view, and I have already talked a lot about JPL toolkit), once in a while I feel the urge to communicate what I have learnt to anyone who can be find some interest in it. Something has changed, however. First of all, I'm going to write next posts in plain English because most of my readers are from abroad, google translator does a good job, but anyway ...

Another important novelty is the fact that I have totally abandoned Python 2, because it is deprecated and Python ver 3 is much richer and interesting. Most of my post in the past utilized the versions 2, I'll try to upgrade them every time it's necessary. But let's talk again about the JPL toolkit, which is the de facto standard for astronomical calculations. Many astrological softwares are based upon the Swiss Ephemeris, which are C libraries that make use of the jpl ephemeris, so I think it's better go with the original, as the development of software with the Swisseph is free only for non commercial products. As you could appreciate from my past posts, building a shared library from the C NASA routines is relatively straightforward, so why not use it? Let's recapitulate the fundamentals, and then let's implement the necessary routines on a specific debian-like platform, named TERMUX, which is usable on most cheap smartphones (mine is Meizu M5 Note, 64bit Arm Android 7 based, rather old phone, but it's fine for my purposes). No need to root your device, you can use it as it is, so you won't have any problem with your bank application or other institutional ones.

Main installation

To install Termux, forget about Google store, the app is unmantained. Install F-Droid app on your Android phone, than download the Termux apk from the F-Droid repository, it's easily recognizable from its icon:

Now launch the application from your screen. You will be welcomed to Termux and you'll see something like this photo:

The keyboard you see in the photo is the Hacker's Keyboard, you can install it from Play Store, but feel free to use the one you feel most comfortable with

Now let's do a few maintaining tasks. At prompt type:

pkg install root-repo
apt update
apt upgrade -y
apt install build-essential binutils wget openssh

N.B.: all four arguments in one line

build-essential and binutils are the minimum tools required to perform compilation and linking, wget is aimed at downloading files from the web, openssh is needed to install a ssh server on our phone.

If you see an error message like "The following signatures couldn't be verified because the public key is not available", don't worry. Write the command termux-change-repo, press enter and select the main repository, than the default repository or any one in the list, than repeat the commands.

To have access to the phone memory, use the following command:

termux-setup-storage and press enter

in your home directory, type ls and you'll see a new storage subdirectory, which contains the access to the main directories used by your phone during normal usage, the most important is storage/downloads. We'll use it later

Create a ssh connection from PC

As you may have ascertained, it's rather difficult to use a phone keyboard. To make this stuff easy, let's give access to Termux to your computer.

If you have installed, as in my previous instructions, the package openssh, it's a matter of seconds activating a ssh access to your phone. Type sshd and press enter. Remember: by default the ssh port in Termus is 8022, not 22.

Now you must find your phone coordinates: type whoami and press enter, this give you your identity, mine, in this exact moment, is u0_a119.

Create a password for ssh: type passwd and press enter. You will be prompted to type a password, then to retype it. Don't create a read only password, if you are prompted to do so.

Connect your phone to your home LAN, and get its address by typing ip addr and pressing enter. Under a long list of devices, you'll find and address like 192.168.1.120 or similar, the most important part are the first two numbers 192.168. Write the full address down, it's your ip coordinate in the LAN.

Now in your PC type in a terminal emulator (cmd in Windows, any terminal available in Linux or Mac): ssh u0_a119@192.168.1.120 -p 8022. Of course, change the argument to represent your real coordinates. If all is fine, you'll be within your phone home directory. You'll receive a warm welcome message, identical to the one you have already seen opening Termux. Hooray! Every new command you'll be typing in your PC terminal, will be executed in your phone memory.

Get the right JPL source

From your browser go to the following web page:

NAIF toolkit C sources.

Browse the following link:

PC, CYGWIN, gCC, 64bit

A new page will open, copy the link address of cspice.tar.gz (if you don't know how to do right click the link and copy the address, not the string).

Then in the Termux terminal you just opened type wget <paste here your link address>

After the download has finished (it will take a while), you will find a cspice.tar.gz file in your home directory

Same procedure for the file importCspice.csh, until you see it in your directory next to the .gz file

Now install tcsh, it's the shell you need to perform some fundamental operations. Type in your terminal apt install tcsh and press enter

Almost done. Unzip your cspice.tar.gz with gunzip cspice.tar.gz, your file is now named cspice.tar

Type tcsh importCSpice.csh and let the script do its job, it will take some time, ignore warnings, and wait until you see the message Toolkit Build Complete.

Where do we go from here?

As I told you a few years ago, the just finished compilation produces a lot of useful function to work with ephemeris files and a static library, not exactly the one we need.

In the upcoming posts we will talk about the creation of a dynamic C library and the linking of it in a basic C program, and how to bind it from a python3 script. In the following posts we will find a way to use these new libraries in a graphical setting, something similar to a true Android application. Let me know if the procedures described in this page are good for you or if you need some help. See you soon ...

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$ 

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...