LIB TT
une librairie de prétraitement d'images astronomiques
version 20000415

Manuel du programmeur
Programmer dans libTT


1. Définition générale de TT

1.1. Contraintes de programmation

Le tableau suivant a pour but de résumer le choix des solutions que l'on peut adopter en fonction des contraintes de la librairie et des programmes d'appel :
 
Contraintes sur l'utilisation
Solution pour un standard
  • facilité de déclaration des fonctions externes dans le programme appellant.
  • facilité d'ajout de nouvelles fonctions dans la librairie.
  • un seul point d'entrée externe dans la librairie.
  • numéros de fonctions
  • nombre variable d'arguments
  • programmation facile de la librairie
  • pérénité du langage de programmation de la librairie
  • compilation Windows et Unix
  • langage C ou C++ ANSI
  • canevas avec directives conditionnelles de compilation.
  • allègement du programme d'appel
  • mise à jour de la librairie sans incidence sur les versions antérieures du programme d'appel.
  • librairie dynamique
  • impossiblité de définir de nom du fichier d'une librairie dynamique au cours de l'exécution du programme appelant.
  • le nom de la fonction d'entrée est différent pour chaque librairie (pas de problème ici car il n'y en a qu'une).

Libtt est une librairie remplissant une grande partie des exigences posées aux paragraphes précédents. Nous avons choisi de programmer en C Ansi. De nombreuses structures sont utilisés comme des classes avec des constructeurs et des destructeurs. Ainsi, libtt est programmée en C mais avec une large structuration objet.

1.2. Compilation

La compilation de libtt nécessite d'indiquer le type de système d'exploitation employé en changeant la définition de sysexp.h.

OS_UNIX_CC pour insérer les fichiers C directement dans un programme compilé avec cc sous Unix.
OS_UNIX_CC_HP_SL pour générer libtt.sl avec un HP sous Unix et le compilateur cc.
OS_DOS_WATC pour insérer les fichiers C directement dans un programme compilé avec watcom sous DOS.
OS_UNIX_CC_DECBAG_SO_VADCL pour générer libtt.so avec la Dec Station de Bagnère sous Unix et le compilateur cc.
OS_WIN_VCPP_DLL pour générer libtt.dll avec le compilateur Visual C++ sous Windows.
OS_WIN_BORLB_DLL pour générer libtt.dll avec le compilateur Borland Builder C++ sous Windows.
OS_WIN_BORL_DLL pour générer libtt.dll avec le compilateur Borland 5 C++ sous Windows.
OS_DOS_WATC_LIB pour générer libtt.lib avec le compilateur Watcom sous DOS.
OS_LINUX_GCC_SO pour générer libtt.so avec le compilateur gcc de Linux.

2. Programmer une nouvelle fonction dans TT

Il est possible de programmer sois même une nouvelle fonction en C dans TT, dans le cadre des définitions IMA/SERIES et IMA/STACK. De plus, toute nouvelle fonction crée avec la procédure décrite ici, est automatiquement vue par le mode pointeur et par le mode script.

2.1. Déclaration d'un user dans tt.h

Pour programmer dans la librairie TT il faut d'abord se déclarer un nouveau user dans le fichier tt.h. Il existe déjà au moins un user1 déclaré de façon à montrer un exemple. Nous allons étudier le cas de la déclaration d'un user2. #ifndef __TTUSER1H__
#include "tt_user1.h"
#endif

juste après ces lignes, ajouter :

#ifndef __TTUSER2H__
#include "tt_user2.h"
#endif

TT_USER1_IMA_STACK user1;

juste aprrès cette ligne, ajouter :

TT_USER2_IMA_STACK user2;

TT_USER1_IMA_SERIES user1;

juste aprrès cette ligne, ajouter :

TT_USER2_IMA_SERIES user2;

int tt_user1_ima_series_builder1(char *keys10,TT_IMA_SERIES *pseries);
int tt_user1_ima_series_builder2(TT_IMA_SERIES *pseries);
int tt_user1_ima_series_builder3(char *mot,char *argu,TT_IMA_SERIES *pseries);
int tt_user1_ima_series_dispatch1(TT_IMA_SERIES *pseries,int *fct_found, int *msg);
int tt_user1_ima_stack_builder1(char *keys10,TT_IMA_STACK *pstack);
int tt_user1_ima_stack_builder2(TT_IMA_STACK *pstack);
int tt_user1_ima_stack_builder3(char *mot,char *argu,TT_IMA_STACK *pstack);
int tt_user1_ima_stack_dispatch1(TT_IMA_STACK *pstack,int *fct_found, int *msg);

juste aprrès cette ligne, ajouter :

int tt_user2_ima_series_builder1(char *keys10,TT_IMA_SERIES *pseries);
int tt_user2_ima_series_builder2(TT_IMA_SERIES *pseries);
int tt_user2_ima_series_builder3(char *mot,char *argu,TT_IMA_SERIES *pseries);
int tt_user2_ima_series_dispatch1(TT_IMA_SERIES *pseries,int *fct_found, int *msg);
int tt_user2_ima_stack_builder1(char *keys10,TT_IMA_STACK *pstack);
int tt_user2_ima_stack_builder2(TT_IMA_STACK *pstack);
int tt_user2_ima_stack_builder3(char *mot,char *argu,TT_IMA_STACK *pstack);
int tt_user2_ima_stack_dispatch1(TT_IMA_STACK *pstack,int *fct_found, int *msg);

2.2. Fichier .h

Dans le cas du user2, le fichier d'entête doit s'appeler tt_user2.h et contiendra, par exemple, les définitions suivantes, concernant l'ajout de deux nouvelles fonctions.

/***************************************************************************/
/* definitions perso du user1 visibles de tout libtt.                      */
/***************************************************************************/
#ifndef __TTUSER2H__
#define __TTUSER2H__

/* --- autorized between 2001 and 2999 ---*/
#define TT_IMASERIES_USER1_OFFSET_TOTO 2001

/* --- autorized between 2001 and 2999 ---*/
#define TT_IMASTACK_USER1_TUTU 2001

/* --- Ajout de parametres pour la classe ima/series --- */
typedef struct {
   double param1;
} TT_USER2_IMA_SERIES;

/* --- Ajout de parametres pour la classe ima/stack --- */
typedef struct {
   double param1;
} TT_USER2_IMA_STACK;

#endif

Ici, TT_IMASERIES_USER2_OFFSET_TOTO est le numéro de fonction, interne à TT, qui permettra de suivre la nouvelle fonction OFFSET_TOTO de IMA/SERIES. Pour le user 2, les numéros autorisés vont de 2001 à 2999, pour le user2 de 3001 à 3999, etc...

TT_IMASTACK_USER2_TUTU est le numéro de fonction, interne à TT, qui permettra de suivre la nouvelle fonction TUTU de IMA/STACK.

La structure TT_USER2_IMA_SERIES contient les variables des options de IMA/SERIES (ici concerne la valeur de l'option param1 de la fonction OFFSET_TOTO.

La structure TT_USER2_IMA_STACK contient les variables des options de IMA/STACK (ici concerne la valeur de l'option param1 de la fonction TUTU.

2.3. Fichier .c

Dans le cas du user2, le fichier de source C doit s'appeler tt_user2.c. Continuons à analyser le même exemple. Les premières lignes du fichier .c contiennent l'inclusion de tt.h et la déclarations des deux fonctions appelées concernant IMA/SERIES OFFSET_TOTO et IMA/STACK TUTU.

#include "tt.h"

/***** prototypes des fonctions internes du user1 ***********/
int tt_ima_series_offset_toto(TT_IMA_SERIES *pseries);
int tt_ima_stack_tutu(TT_IMA_STACK *pstack);

Toute nouvelle fonction doit aparaître obligatoirement dans le constructeur builder1 suivant :

int tt_user2_ima_series_builder1(char *keys10,TT_IMA_SERIES *pseries)
/**************************************************************************/
/* Definition du nom externe de la fonction et son lien interne a libtt.  */
/**************************************************************************/
{
   if (strcmp(keys10,"OFFSET_TOTO")==0) { pseries->numfct=TT_IMASERIES_USER2_OFFSET_TOTO; }
   return(OK_DLL);
}

Si la nouvelle fonction contient des options, alors il faut initiliser ses valeurs par défaut dans le constructeur builder2 et convertir l'argument de chaîne en son type interne avec le constructeur builder3.

int tt_user2_ima_series_builder2(TT_IMA_SERIES *pseries)
/**************************************************************************/
/* Valeurs par defaut des parametres de la ligne de commande.             */
/**************************************************************************/
{
   pseries->user2.param1=0.;
   return(OK_DLL);
}

int tt_user2_ima_series_builder3(char *mot,char *argu,TT_IMA_SERIES *pseries)
/**************************************************************************/
/* Decodage de la valeur des parametres de la ligne de commande.          */
/**************************************************************************/
{
   if (strcmp(mot,"OFFSET_VALUE")==0) {
      pseries->user2.param1=(double)(atof(argu));
   }
   return(OK_DLL);
}

Le distributeur dispatch1 permet d'assigner le nom de la fonction C concernant le calcul de la nouvelle fonction :

int tt_user2_ima_series_dispatch1(TT_IMA_SERIES *pseries,int *fct_found, int *msg)
/*****************************************************************************/
/* Appel aux fonctions C qui vont effectuer le calcul des fonctions externes */
/*****************************************************************************/
{
   if (pseries->numfct==TT_IMASERIES_USER1_OFFSET_TOTO) {
      *msg=tt_ima_series_offset_toto(pseries);
      *fct_found=TT_YES;
   }
   return(OK_DLL);
}

Dans notre cas, on remarque l'utilisateur connaît la fonction OFFSET_TOTO (mot clé définit dans le builder1) et que le programmeur lui associe la fonction C tt_ima_series_offset_toto pour effectuer les calculs. La fonction écrite en C peut être la suivante :

/**************************************************************************/
/**************************************************************************/
/* exemple de la fonction user OFFSET_TOTO implantee dans IMA/SERIES      */
/**************************************************************************/
/* Fonction Offset2 (equivalent a Qmips32)                                */
/**************************************************************************/
int tt_ima_series_offset_toto(TT_IMA_SERIES *pseries)
{
   TT_IMA *p_in,*p_out;
   long nelem;
   double value,offset;
   int kkk,index;

   /* --- intialisations ---*/
   p_in=pseries->p_in;
   p_out=pseries->p_out;
   nelem=pseries->nelements;
   index=pseries->index;
   offset=pseries->user1.param1;

   /* --- calcul de la fonction ---*/
   tt_imabuilder(p_out);
   tt_imacreater(p_out,p_in->naxis1,p_in->naxis2);
   for (kkk=0;kkk<(int)(nelem);kkk++) {
      value=p_in->p[kkk]+offset;
      p_out->p[kkk]=(TT_PTYPE)(value);
   }

   /* --- calcul des temps (pour l'entete fits) ---*/
   pseries->jj_stack=pseries->jj[index-1];
   pseries->exptime_stack=pseries->exptime[index-1];

   return(OK_DLL);
}

L'étape d'initialisation consiste à récupérer les informations utiles dans la structure TT_IMA_SERIES.

L'étape de calcul montre que l'image de sortie doit être écrite dans le pointeur pseries->p_out. Ce pointeur est lui même est structure d'image qu'il faut construire avec la fonction tt_imabuilder. Il n'est pas besoin de détruire pseries->p_out à la fin du calcul, cela est réalisé automatiquement par libTT. Après avoir construit le pointeur d'image de sortie, il faut le dimensionner avec tt_imacreater.

Le calcul lui même ne pose pas de difficultés. L'adressage en coordonnées (x,y) du pointeur de l'image de dimension (naxis1,naxis2) est le suivant : p[y*naxis1+x].

Les deux dernières lignes concernant les élements jj_stack et exptime_stack de la structure TT_IMA_SERIES, doivent toujours être écrites de cette façon afin que libTT puisse gérer correctement les mots clés Fits DATE-OBS et EXPTIME.

De la même façon que pour IMA/SERIES, il existe des constructeurs et un distributeur pour les fonctions de type IMA/STACK. Dans tous les cas, on consultera avec attention les fichiers tt_user1.h et tt_user1.c présents dans les sources de libTT.