Audela |
![]() |
L'extension libaudela permet de créer des "objets" caméras et télescopes. Lorsque l'on crée un tel objet, libaudela va charger une extension Tcl qui contient les fonctions de pilotage qui lui correspondent. Cette extension est communément appelée libcam pour piloter une caméra et libtel dans le cas du pilotage d'une monture de télescope.
Cette page explique, en détails, comment programmer une librairie d'entension pour piloter une caméra ou une monture de télescope sous Audela. Il est indispensable d'avoir déjà compris le fonctionnement d'une extension classique.
Les fichiers sources ont été nommés de façon à garder une grande clareté de lecture et pour garder une logique certaine. Il est nécessaire de garder cette architechture pour contruire des librairies libcam ou libtel lisibles par tout le monde. C'est pour cette raison que les conseils donnés ici sont décrits comme des consignes à respecter.
L'instanciation est la procédure qui consiste à enregistrer un driver d'instrument dans l'interpréteur Tcl de Audela. Les extensions libcam et libtel ne sont jamais appellées directement par l'interpréteur Tcl. Elles sont appellées par libaudela, au moment de la création de l'objet correspondant. Le principe d'instanciation est assez complexe mais permet d'écrire faiclement une nouvelle extension de pilotage.
Pour une caméra, la fonction d'appel Tcl est ::cam::create.
::cam::create audine lpt1 -ccd kaf400
la fonction ::cam::create est définie dans libaudela. Elle charge la librairie nommée par le premier argument de la fonction, précédée du préfixe lib. Ainsi, dans l'exemple ci-dessus, libaudela va charger la librairie d'extension libaudine et va ajouter ses fonctions à l'objet qui vient d'être crée. La fonction ::cam::create retourne un numéro identifiant l'objet qui vient d'être crée. En général, la création d'un objet est effectué de la façon suivante :
set num [::cam::create audine lpt1 -ccd kaf400]
La variable Tcl num contient le numéro de l'objet crée. On peut appelé une fonction associée à l'objet en utilisant la fonction cam suivie du numéro de l'objet. Par exemple :
cam$num info
retourne une chaîne de caractères contenant des informations. Dans la suite, on emploiera la fonction cam1 (num=1) pour désigner l'appel d'une fonction d'un objet d'une libcam quelconque.
La figure suivante montre le chemin suivi dans les librairies libaudela et libcam pour enregistrer un nouveau driver d'instrument. Nous avons choisi l'exemple de libcam=libaudine.
Dans libaudela, la fonction ::cam::create est associée à la fonction CmdCreatePoolItem. La fonction CmdCreatePoolItemest générique à tous les objets de Audela : cam, tel mais aussi buf et visu. L'enregistrement des fonctions associées à l'objet se déroule en trois temps : ouverture de l'extension du pilote (interprétation de "load libaudine"), assignation d'un numéro à l'objet (effectué dans CmdCreatePoolItem mais non représenté sur le schéma ci-dessus), puis enregistrement des fonctions de l'objet (interprétation de "audine cam1 lpt1..."). Nous allons détailler chacune de ces étapes.
A partir de la fonction CmdCreatePoolItem, l'ouverture de l'extension du pilote est réalisée par la fonction load de Tcl. On ouvre ainsi l'extension du pilote associée au nom du premier argument de la fonction ::cam::create (ici audine, donc on ouvre l'extension libaudine). Le nom du point d'entrée de l'extension libaudine (Audine_Init) est défini dans le fichier libname.h. Le point d'entrée est écrit dans libcam.c. L'écriture du code C est tel que libcam.c est un fichier valable pour tous les pilotes de caméras. Pour cette raison, le fichier source libcam.c est placé dans le dossier dev/common/libcam. Le point d'entrée ne crée qu'une seule nouvelle commande Tcl qui porte le nom du pilote. Ici "audine". On retourne alors dans la fonction CmdCreatePoolItem.
Dans la fonction CmdCreatePoolItem, un objet du langage C++ de l'extension libaudela est instancié et renvoie un numéro. On utilise ce numéro, dans l'interpréteur Tcl, pour identifier la caméra qui vient d'être crée. Dans l'exemple ci dessus, c'est le numéro 1 qui est attribué.
Dans la fonction CmdCreatePoolItem, on effectue l'interprétation de la fonction audine crée précédemment. Cette fonction pointe sur la fonction cmdCamCreate dans le fichier libcam.c. De nombreux arguments arguments sont passés à la fonction audine (cf. schéma ci-dessus). cam1 est le nom de la commande à créer (le numéro 1 vient de l'étape précédente). Les autres arguments sont ceux passés par la fonction ::cam::create. Dans la fonction cmdCamCreate du fichier libcam.c, on enregistre effectivement la fonction cam1 dans l'interpréteur Tcl. La fonction cam1 de l'interpréteur pointe alors sur la fonction cmdCam. Cette fonction sera donc appelée à chaque fois qu'une commande cam1 sera passée à l'interpréteur Tcl. Il faut noter que la fonction cmdCamCreate effectue les initialisations du pilote. L'étape d'initialisation consiste à remplir les éléments de la variable cam qui est une structure contenant toutes les informations sur la caméra (nombre de pixels, etc.). L'initialisation invoque aussi l'exécution de la fonction cam_init du fichier camera.c. Cette fonction effectue les initialisations spécifiques à la caméra demandée. Si l'initialisation échoue, l'ensemble de la procédure ::cam::create est annulée. Nous détaillerons le contenu du fichier camera.c plus loin. Notons que la fonction TclCreateCommand de la fonction cmdCamCreate, passe le pointeur de la variable cam à la fonction cam1 (par l'argument de type ClienData qui est en fait un void*). Ceci est important car la variable cam (donc les caractéristiques de la caméra) est ainsi transmise à toutes les fonctions invoquées par cam1.
A partir de ce moment, toute fonction cam1 passée à l'interpréteur Tcl va pointer sur la fonction cmdCam. Cette fonction analyse le mot du premier argument de la fonction cam1. Par exemple, si l'on a passé la fonction cam1 info, le premier argument est le mot info (=argv[1]). Ce mot permet à la fonction cmdCam d'exécuter la fonction C correspondante de la librairie. Nous verrons, au paragraphe suivant, comment cette partie fonctionne.
Si l'on analyse la syntaxe du fichier libcam.c, on remarque que seules les définitions du fichier libname.h sont différentes d'un pilote à l'autre. Ainsi, pour créer une nouvelle extension de pilote, il suffit de recopier les fichiers d'une extension existante et de changer les quatre lignes des définitions du fichier libname.h. Nous verrons plus loin que d'autres fichiers doivent être aussi changés, mais jamais libcam.c.
Une fois la commande ::cam::create effectuée, l'utilisateur de l'interpréteur peut invoquer la fonction cam1. Chaque appel à la commande cam1 provoque l'exécution de la fonction cmdCam du fichier libcam.c. Le but de la fonction cmdCam est d'exécuter une fonction C correspondant à la chaîne de caractères argv[1] (premier argument de cam1). Le coeur de cette fonction cmdCam est composé des lignes suivantes :
struct cmditem *cmd; for(cmd=cmdlist;cmd->cmd!=NULL;cmd++) { if(strcmp(cmd->cmd,argv[1])==0) { retour = (*cmd->func)(clientData, interp, argc, argv); break; } }
La structure cmditem est définie dans le fichier libcam.h (le type Tcl_CmdProc est de type int) :
struct cmditem { char *cmd; Tcl_CmdProc *func; };
La variable globale cmdlist est définie dans le fichier camcmd.h :
static struct cmditem cmdlist[] = { /* === Common commands for all cameras ===*/ COMMON_CMDLIST /* === Specific commands for that camera ===*/ {"scan", cmdCamScan}, /* === Last function terminated by NULL pointers ===*/ {NULL, NULL} };
Enfin, COMMON_CMDLIST est défini dans le fichier libcam.h :
#define COMMON_CMDLIST \ {"drivername", cmdCamDrivername},\ {"name", cmdCamName},\ {"ccd", cmdCamCcd},\ {"nbcells", cmdCamNbcells},\ {"nbpix", cmdCamNbpix},\ {"celldim", cmdCamCelldim},\ {"pixdim", cmdCamPixdim},\ {"maxdyn", cmdCamMaxdyn},\ {"fillfactor", cmdCamFillfactor},\ {"rgb", cmdCamRgb},\ {"info", cmdCamInfo},\ {"port", cmdCamPort},\ {"timer", cmdCamTimer},\ {"gain", cmdCamGain},\ {"readnoise", cmdCamReadnoise},\ {"bin", cmdCamBin},\ {"exptime", cmdCamExptime},\ {"buf", cmdCamBuf},\ {"window", cmdCamWindow},\ {"acq", cmdCamAcq},\ {"stop", cmdCamStop},\ {"tel", cmdCamTel},\ {"shutter", cmdCamShutter},\ {"cooler", cmdCamCooler},\ {"temperature", cmdCamTemperature},\ {"foclen", cmdCamFoclen},\ {"interrupt", cmdCamInterrupt},\ {"overscan", cmdCamOverscan},
La boucle for sur la variable cmd, analyse séquentiellement les éléments de la structure cmdlist. Cette structure établit le lien entre une chaîne de caractères (cmd->cmd) et le pointeur de la fonction C correspondante (cmd->func). Au cours de la boucle, si l'élément cmd->cmd est égal à arg[1] alors la fonction cmd->func est exécutée. Les fonctions exécutées ont les mêmes arguments que la syntaxe classique des fonctions C appelées par l'interpréteur Tcl (ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]).
L'ensemble des fonctions définies dans COMMON_CMDLIST du fichier libcam.h sont présentes dans le fichier libcam.c. Ces fonctions sont communes à tous les drivers de caméras. Rappelons qu'il ne faut jamais modifier libcam.c et libcam.h, même si des fonctions vous semblent inutiles pour votre caméra.
Le fichier camcmd.h permet d'ajouter des fonctions spécifiques à la caméra à piloter. Dans l'exemple précité, on a ajouté la fonction {"scan", cmdCamScan}. L'usage veut que les fonctions C spécifiées dans camcmd.h (ici cmdCamScan) soient écrites dans le fichier camtcl.c.
Notons que certaines caméras ont besoin d'une procédure de fermeture spécifique lorsque l'on veut les déconnecter (ports USB, Ethernet, etc.). Pour cela, la fonction ::cam::delete de libaudela appelle systématiquement la fonction cam1 close si elle existe. Dans le cas de ces caméras, la fonction cmdCamClose devra être définie dans camtcl.c, et la ligne {"close", cmdCamClose} ajoutée dans camcmd.h.
Nous avons déjà vu qu'une partie des codes sources d'une extension de pilote de caméra est commune à toutes les caméras. Il s'agit des fichiers libcam.c, libcam.h, util.c et util.h. Les fichiers util.c et util.h fournissent des fonctions d'accès aux ports parallèles, de remise à l'heure de l'horloge, etc.
Le fichier le plus important d'une extension de caméra est camera.c. Il rassemble les codes spécifiques à la caméra. Néanmoins, le code de camera.c comporte des fonctions dont la présence est obligatoire, même si elles ne font rien. Il s'agit des fonctions suivantes :
int cam_init(struct camprop *cam, int argc, char **argv) void cam_start_exp(struct camprop *cam,char *amplionoff) void cam_stop_exp(struct camprop *cam) void cam_read_ccd(struct camprop *cam, unsigned short *p) void cam_shutter_on(struct camprop *cam) void cam_shutter_off(struct camprop *cam) void cam_ampli_on(struct camprop *cam) void cam_ampli_off(struct camprop *cam) void cam_measure_temperature(struct camprop *cam) void cam_cooler_on(struct camprop *cam) void cam_cooler_off(struct camprop *cam) void cam_cooler_check(struct camprop *cam) void cam_set_binning(int binx, int biny,struct camprop *cam) void cam_set_exptime(float exptime,struct camprop *cam) void cam_update_window(struct camprop *cam)
Ces fonctions sont appelées par libcam.c. Au pire on les laisse vide. Le tableau ci-dessous donne la liste des fonctions Tcl qui appellent ces fonctions :
| Fonction Tcl | Fonction dans libcam.c | Fonction dans camera.c |
| ::cam::create | cmdCamCreate | cam_init |
| cam1 acq | cmdCamAcq | cam_start_exp cam_stop_exp cam_read_ccd |
| cam1 stop | cmdCamStop | cam_stop_exp cam_read_ccd |
| cam1 shutter | cmdCamShutter | cam_shutter_on cam_shutter_off |
| cam1 ampli | cmdCamAmpli | cam_ampli_on cam_ampli_off |
| cam1 temp | cmdCamTemperature | cam_measure_temperature |
| cam1 cooler | cmdCamCooler | cam_cooler_on cam_cooler_off cam_cooler_check |
| cam1 binning | cmdCamBin | cam_set_binning |
| cam1 exptime | cmdCamExptime | cam_set_exptime |
| cam1 window | cmdCamWindow | cam_update_window |
Comme une librairie d'extension peut piloter plusieurs caméras, il est possible de décrire leurs caractéristiques principales, au début du fichier camera.c, dans la structure camini. Par exemple, pour la caméra Audine Kaf-401 :
struct camini cam_ini[] = { {"audine", /* camera name */ "kaf401", /* ccd name */ 768,512, /* maxx maxy */ 14,14, /* overscans x */ 4,4, /* overscans y*/ 9e-6,9e-6, /* photosite dim (m) */ 32767., /* observed saturation */ 1., /* filling factor */ 11., /* gain (e/adu) */ 11., /* readnoise (e) */ 1,1, /* default bin x,y */ 1., /* default exptime */ 1, /* default state of shutter (1=synchro) */ 1, /* default num buf for the image */ 1, /* default num tel for the coordinates taken */ 0, /* default port index (0=lpt1) */ 1, /* default cooler index (1=on) */ -15., /* default value for temperature checked */ 1, /* default color mask if exists (1=cfa) */ 0, /* default overscan taken in acquisition (0=no) */ 1. /* default focal lenght of front optic system */ }, ...
Enfin, le reste du fichier camera.c contient des fonctions de pilotage diverses et spécifiques à cette caméra.
html : contient des fichiers au format HTML (manuel d'instructions, etc.). linux : contient le fichier Makefile pour construire la librairie sous Linux avec gcc (faire make). vc40 : contient le fichier projet libcamxx..mdp pour construire sous Windows avec Visual C++ 4.0. Ce fichier est compatibles pour les version de Visual C++ plus récentes. src : contient les fichiers source,
libname.h : définitions des noms de la librairie. camcmd.h : définition de la variable globale cmdlist de structure cmditem (telcmd.h pour une libtel). camtcl.c : fonctions d'interfacage entre le l'interpréteur Tcl et le C, spécifiques à la caméra (teltcl.c pour une libtel) . camtcl.h : entêtes des fonctions décrites dans le fichier camtcl.c (telcmd.h pour une libtel). camera.c : fonctions de pilotage (telescop.c pour une libtel). camera.h : entêtes des fonctions décrites dans le fichier camera.c (telescop.h pour une libtel).
pSous windows, il faut aussi recréer un nouveau projet si l'on utilise le compilateur Visual C++ 4.0. Dans le cas du projet libcamxx, on procèdera ainsi, dans l'ordre suivant pour créer le fichier projet libcamxx.mdp (et libcamxx.mak) :
L'architecture générale des fichiers de libcamxx est organisée pour ajouter facilement une nouvelle fonction. Si la fonction cam1 close n'était pas encore programmée, voici comment il faudrait procéder:
{"close", cmdCamClose},
{
...corps de la fonction...
}
int cmdCamClose(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);
void close() { ...corps de la fonction... }
void close(void);
Il faut noter que les deux dernières étapes ne sont toujours indispensables. Certaines fonctions simples n'exigent pas d'appel autre qu'à la fonction "C/Tcl".