/* 
 * @(#)Telem.java   1.0 01/05/20 Aimé Vareille  aime.vareille@wanadoo.fr *
 *  Copyleft (c) 2001 Aimé Vareille LGPL Licence for the code inserted
Licence GNU FDL for texts : http://www.gnu.org/copyleft/copyleft.html
Considérons 3 longueurs d'ondes L1, L2 et L3. 
La première distance D qui donne la même mesure interférométrique nulle s'écrit si les mantisses des longueurs d'ondes sont premières entre
elles : 
D = k1 * L1 = k2 * L2 = k3 * L3 
avec : 
k1 = mantisse(L2) * mantisse(L3) 
k2 = mantisse(L1) * mantisse(L3) 
k3 = mantisse(L1) * mantisse(L2) 
Pour fixer les idées, considérons les longueurs d'ondes de 300 nm, 500 nm et 700 nm de mantisses premières entre elles ; la distance D de
détermination totale est de 5 * 7 * 300 nm soit 10,5 microns. 
Pour les longueurs d'ondes 310 nm, 530 nm et 710 nm cette distance D est de 53 * 71 * 310 nm, soit 1,16653 mm. 
Pour les longueurs d'ondes 311 nm, 541 nm et 719 nm cette distance D est de 719 * 541 * 311 nm, soit 0,12097247 m. 
Pour les longueurs d'ondes 311.9 nm, 541.3 nm et 719.3 nm cette distance D est de 7193 * 5413 * 311.9 nm, soit 12,144048 m. 
Pour les longueurs d'ondes 311.93 nm, 541.33 nm et 719.33 nm cette distance D est de 71933 * 54133 * 311.93 nm soit 1,21439 km. 
Des finesses de raies de 0.01 nm sont techniquement accessibles : le picomètre sur le kilomètre semble atteignable avec simplement 3
longueurs d'ondes dont les mantisses sont premières entre elles à 10-6près. Malheureusement, les picomètres se perdent en route : le fait
d'ajouter 71933*54133 (= 3,893949 109) fois 311.93 nm entraîne que les longueurs d'ondes doivent être connues à mieux que 10-15, ce qui nous
ramène de fait à la même difficulté technique qu'avec 2 longueurs d'ondes pour la précision : c'est pas facile de dépasser le kilomètre en
gardant une précision du picomètre, les longueurs de cohérence des lumières utilisées doivent être de l'ordre de grandeur de la distance mesurée
(le kilomètre), ce qui requiert des lasers de grande stabilité. C'est plutôt le nombre de fois que l'on additionne les longueurs d'ondes qui donne la
précision de définition des raies lumineuses : pour 1 millimètre, une longueur d'onde de 500 nanomètres doit être connue à mieux que 2
10-4picomètre. 

L'applet Telem permet d'expérimenter le problème de détermination pour des mantisses inférieures à 1E5, l'algorithme explore la coïncidence
des 3 mesures interférométriques modulo les longueurs d'ondes en partant de l'égalité de chemins (zéro) et simultanément devant et derrière
l'image du miroir de référence (distances D positive et négative). Le test de coïncidence se fait à une valeur de tolérance dt près exprimée dans
l'unité de distance choisie commune à toutes les valeurs homogènes à des distances exprimées dans l'applet (tout en nm ou en microns ...). 

Le bouton "Theory" lance le calcul des restes r1, r2 et r3 qui correspondent à la distance théorique D modulo les longueurs d'ondes L1, L2 et L2
; la distance D' est ensuite recalculée à partir des mesures r1, r2 et r3. 
Le bouton "Mesure" lance le calcul de la distance D' à partir des mesures r1, r2 et r3 introduites dans les 3 fenêtres de la même ligne du bas. 
Le bouton "go" lance le calcul pour des distances D, longueurs d'ondes L1, L2, L3 ou tolérance dt modifiées dans l'une des fenêtres de la ligne
du haut en gardant l'option précédente "Theory" ou "Mesure" (e.g. si on a choisi l'option "Mesure" précédemment et que la distance D est
modifiée, le clic sur "go" prend bien en compte la nouvelle distance théorique D mais ne fait le calcul qu'à partir des mesures r1, r2 et r3, il faut
cliquer le coup suivant sur le bouton "Theory" pour que le calcul des restes r1, r2 et r3 puis la régression pour retrouver D' s'effectuent ...). 

Bien que n'ayant pas besoin de mesurer des couches minces d'un kilomètre d'épaisseur, c'est cet algorithme (sans l'exploration des distances
négatives) que nous utilisons pour nos ellipsomètres à 3 longueurs d'ondes en remplaçant les longueurs d'ondes par les périodes des couches
transparentes à mesurer : 
periode = L/2/sqrt(ni^^2-sin(theta)^^2)
où "ni" est l'indice de réfraction de la couche à mesurer, il est recommandé de fixer ces indices "ni" pour extraire des mesures d'épaisseurs r1,
r2 et r3  cohérentes permettant la levée d'indétermination ; l'angle d'incidence élevé au sinus joue ici un rôle similaire à la longueur d'onde et
permet une levée d'indétermination sur la mesure des épaisseurs, c'est ce que permettent les ellipsomètres Woollam et Rudolph à angles
d'incidences variables.  La valeur choisie pour la tolérance intègre alors les erreurs du modèle de l'ellipsomètre (modèle théorique, valeurs de
calibration et caractérisques des faisceaux lumineux (divergence, largeur de raie, taux de polarisation ...)), les erreurs de modèle multicouche
(approximation d'interfaces, de rugosités, de gradients d'indices ...) et les erreurs de mesure : c'est très commode de pouvoir synthétiser toutes
ces imperfections en un seul chiffre, en plus, le calcul est sûr (pourvu qu'on borne les itérations) et rustique. 

 * @(#)ArcTest.java     1.6 98/06/29                                                                  
 * Copyright (c) 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

// import graph.*;
/**
 * An interactive test of the Graphics.drawArc and Graphics.fillArc
 * routines. Can be run either as a standalone application by
 * typing "java Telem" or as an applet in the AppletViewer.
 */
public class Telem extends Applet {
    ArcOptions options;   // The options for marking and filling arcs
    ArcControls controls;   // The controls for the telemetric values
    ArcCanvas canvas;       // The drawing area to display arcs

    public void init() {
	setLayout(new BorderLayout());
	canvas = new ArcCanvas();
	add("Center", canvas);
	add("North", controls = new ArcControls(canvas));
	add("South", options = new ArcOptions(canvas));
    }

    public void destroy() {
        remove(options);
        remove(controls);
        remove(canvas);
    }

    public void start() {
	controls.setEnabled(true);
    }

    public void stop() {
	controls.setEnabled(false);
    }

    public void processEvent(AWTEvent e) {
        if (e.getID() == Event.WINDOW_DESTROY) {
            System.exit(0);
        }
    }

    public static void main(String args[]) {
	Frame f = new Frame("Telem");
	Telem	telem = new Telem();

	telem.init();
	telem.start();

	f.add("Center", telem);
	f.setSize(650, 300);
	f.show();
    }

    public String getAppletInfo() {
        return "An interactive test of the Graphics.drawArc and \nGraphics.fillArc routines. Can be run \neither as a standalone application by typing 'java Telem' \nor as an applet in the AppletViewer.";
    }
}

class ArcCanvas extends Canvas {
    float	distance = 1000.0F;
    float	determine = 0.0F;
    float	ordre1 = 45.0F;
    float	pi = 3.14159F;
    float	reste1 = 0.0F;
    float	lambda1 = 0.31F;
    float	ordre2 = 45.0F;
    float	reste2 = 0.0F;
    float	lambda2 = 0.53F;
    float	ordre3 = 45.0F;
    float	reste3 = 0.0F;
    float	lambda3 = 0.71F;
    float	tolerance = 0.00001F;
//    boolean	filled = false;
    boolean	filled = true;
    Font	font;
    float[]    lmbd = new float[4];
    float[]    rst = new float[4];
    float[]    gobelet = new float[3];
    float[]    ordre = new float[4];
    float[]    chemin = new float[4];
    int        crit = 0;
    float      temp = 0.0F;
    float[]    ordrem = new float[4];
    float[]    chemoin = new float[4];
    float      tempm = 0.0F;
    int        critm = 0;
    boolean    sortie = false;
    public void paint(Graphics g) {

	Rectangle r = getBounds();
	int hlines = r.height / 14;
	int vlines = r.width / 14;
	g.setColor(Color.pink);
//	g.setColor(Color.yellow);
	for (int i = 1; i <= hlines; i++) {
	    g.drawLine(0, i * 14, r.width, i * 14);
	}
	for (int i = 1; i <= vlines; i++) {
	    g.drawLine(i * 14, 0, i * 14, r.height);
	}

	g.setColor(Color.black);
	g.setFont(font);
	//	g.drawLine(0, r.height / 2, r.width, r.height / 2);
	//	g.drawLine(r.width / 2, 0, r.width / 2, r.height);
	//	g.drawLine(0, 0, r.width, r.height);
	//	g.drawLine(r.width, 0, 0, r.height);
	int sx = 10;
	int sy = r.height - 168;
        int milieux = r.width / 2 + 30;
	// calcul des restes à partir de la distance
	// sinon détermination de la distance uniquement à partir des restes
	if (filled) {
        ordre1=(float)(long)(distance/lambda1);
	reste1=distance-ordre1*lambda1;
        ordre2=(float)(long)(distance/lambda2);
	reste2=distance-ordre2*lambda2;
        ordre3=(float)(long)(distance/lambda3);
	reste3=distance-ordre3*lambda3;
	}
	// tri des longueurs d'ondes : la plus petite en premier
        rst[1]=reste1;
        lmbd[1]=lambda1;
	rst[2]=reste2;
        lmbd[2]=lambda2;
        lmbd[3]=lambda3;
	rst[3]=reste3;
        gobelet[1]=reste1;
        gobelet[2]=lambda1;
        for (int i = 1; i <= 2 ; i++)
	    {for (int j = i+1; j <= 3 ; j++)
		{if(lmbd[j] < lmbd[i])
		    { gobelet[1]=rst[j];
                      gobelet[2]=lmbd[j];
                      rst[j]=rst[i];
                      lmbd[j]=lmbd[i];
                      rst[i]=gobelet[1];
                      lmbd[i]=gobelet[2];
                    }
		}
	    }
        reste1=rst[1];
        lambda1=lmbd[1];
        reste2=rst[2];
        lambda2=lmbd[2];
        reste3=rst[3];
        lambda3=lmbd[3];
	// Itération de recherche de l'égalité des chemins dans la tolérance
        crit=0;
        ordre[1]=0.0F;
        ordre[2]=0.0F;
        ordre[3]=0.0F;
        ordrem[1]=0.0F;
        ordrem[2]=0.0F;
        ordrem[3]=0.0F;
        critm=0;
        sortie=true;
	//        while (crit != 2)
        while (sortie)
	    { crit=0;
	    chemin[1]=rst[1]+ordre[1]*lmbd[1];
            ordre[2]=(float)(long)(ordre[1]*lmbd[1]/lmbd[2]-1.0);
            ordre[3]=(float)(long)(ordre[1]*lmbd[1]/lmbd[3]-1.0);
	    chemin[2]=rst[2]+ordre[2]*lmbd[2];
	    chemin[3]=rst[3]+ordre[3]*lmbd[3];
            critm=0;
	    chemoin[1]=rst[1]+ordrem[1]*lmbd[1];
            ordrem[2]=(float)(long)(ordrem[1]*lmbd[1]/lmbd[2]+1.0);
            ordrem[3]=(float)(long)(ordrem[1]*lmbd[1]/lmbd[3]+1.0);
	    chemoin[2]=rst[2]+ordrem[2]*lmbd[2];
	    chemoin[3]=rst[3]+ordrem[3]*lmbd[3];
	    // Ecart 1-2 positif
            temp=chemin[2];
            while (chemin[2] < chemin[1])
		{
        	    temp=chemin[2];
                    ordre[2]=(float)(ordre[2]+1.0);
	            chemin[2]=rst[2]+ordre[2]*lmbd[2];
                }
	    // Dépassement
            if ((chemin[2]-chemin[1]) < tolerance)
		{ crit=crit+1; }
	    // Retour d'un
            if (temp != chemin[2])
		{
		    if ((chemin[1]-temp) < tolerance)
			{
			    crit=crit+1;
                            chemin[2]=temp;
                            ordre[2]=(float)(ordre[2]-1.0);
                        }
                }
	    // Ecart 1-3 positif
            temp=chemin[3];
            while (chemin[3] < chemin[1])
		{
        	    temp=chemin[3];
                    ordre[3]=(float)(ordre[3]+1.0);
	            chemin[3]=rst[3]+ordre[3]*lmbd[3];
                }
	    // Dépassement
            if ((chemin[3]-chemin[1]) < tolerance)
		{ crit=crit+1; }
	    // Retour d'un
            if (temp != chemin[3])
		{
		    if ((chemin[1]-temp) < tolerance)
			{
			    crit=crit+1;
                            chemin[3]=temp;
                            ordre[3]=(float)(ordre[3]-1.0);
                        }
                }
	    // Ecart 1-2 négatif
            tempm=chemoin[2];
            while (chemoin[2] > chemoin[1])
		{
        	    tempm=chemoin[2];
                    ordrem[2]=(float)(ordrem[2]-1.0);
	            chemoin[2]=rst[2]+ordrem[2]*lmbd[2];
                }
	    // Dépassement
            if ((chemoin[1]-chemoin[2]) < tolerance)
		{ critm=critm+1; }
	    // Retour d'un
            if (tempm != chemoin[2])
		{
		    if ((tempm-chemoin[1]) < tolerance)
			{
			    critm=critm+1;
                            chemoin[2]=tempm;
                            ordrem[2]=(float)(ordrem[2]+1.0);
                        }
                }
	    // Ecart 1-3 négatif
            tempm=chemoin[3];
            while (chemoin[3] > chemoin[1])
		{
        	    tempm=chemoin[3];
                    ordrem[3]=(float)(ordrem[3]-1.0);
	            chemoin[3]=rst[3]+ordrem[3]*lmbd[3];
                }
	    // Dépassement
            if ((chemoin[1]-chemoin[3]) < tolerance)
		{ critm=critm+1; }
	    // Retour d'un
            if (tempm != chemoin[3])
		{
		    if ((tempm-chemoin[1]) < tolerance)
			{
			    critm=critm+1;
                            chemoin[3]=tempm;
                            ordrem[3]=(float)(ordrem[3]+1.0);
                        }
                }
	    // Tour suivant
            if (crit == 2 || critm == 2)
		{ sortie=false;}
            else
		{ ordre[1]=(float)(ordre[1]+1.0); 
                  ordrem[1]=(float)(ordrem[1]-1.0); }
            }
        if (crit == 2) {
        determine=chemin[1];
        ordre1=ordre[1];
        ordre2=ordre[2];
        ordre3=ordre[3];
	}
        else {
        determine=chemoin[1];
        ordre1=ordrem[1];
        ordre2=ordrem[2];
        ordre3=ordrem[3];
	}

	//        ordre3=(float)(critm);
	// Affichages
	//       	g.drawString("D = " + distance, sx, sy);
       	g.drawString("D = " + distance, milieux, sy);
	g.drawString("L1 = " + lambda1, milieux, sy + 14);
	g.drawString("L2 = " + lambda2, milieux, sy + 28);
	g.drawString("L3 = " + lambda3, milieux, sy + 42);
	g.drawString("dt = " + tolerance, milieux, sy + 56);
	g.drawString("k1 = " + ordre1, milieux, sy + 70);
	g.drawString("k2 = " + ordre2, milieux, sy + 84);
	g.drawString("k3 = " + ordre3, milieux, sy + 98);
	g.drawString("r1 = " + reste1, milieux, sy + 112);
	g.drawString("r2 = " + reste2, milieux, sy + 126);
	g.drawString("r3 = " + reste3, milieux, sy + 140);
	g.drawString("D' = " + determine, milieux, sy + 154);
	// Notice
       	g.drawString("En haut de gauche a droite :", sx, sy-14);
	g.drawString("Distance theorique D", sx, sy);
	g.drawString("Premiere longueur d'onde L1", sx, sy + 14);
	g.drawString("Deuxieme longueur d'onde L2", sx, sy + 28);
	g.drawString("Troisieme longueur d'onde L3", sx, sy + 42);
	g.drawString("Tolerance dt", sx, sy + 56);
	g.drawString("| D' - ( ki * Li + ri ) | < dt ; {i = 1,2,3}", sx, sy + 70);
	g.drawString("D, Li, ri, dt, D' en nm ou microns, ki entiers", sx, sy + 84);
	g.drawString("En bas de gauche a droite :", sx, sy + 98);
	g.drawString("Premiere mesure r1 a la longueur d'onde L1", sx, sy + 112);
	g.drawString("Deuxieme mesure r2 a la longueur d'onde L2", sx, sy + 126);
	g.drawString("Troisieme mesure r3 a la longueur d'onde L3", sx, sy + 140);
	g.drawString("Distance mesuree D'", sx, sy + 154);

    }
    

    //    public void redraw(boolean filled, float d, float l1, float l2, float l3, float t) {
    public void redraw(float d, float l1, float l2, float l3, float t) {
	//	this.filled = filled;
	this.distance = d;
	this.lambda1 = l1;
	this.lambda2 = l2;
	this.lambda3 = l3;
	this.tolerance = t;
	repaint();
    }
    public void reoption(boolean filled, float r1, float r2, float r3) {
	this.filled = filled;
	//	this.distance = d;
	//	this.lambda1 = l1;
	//	this.lambda2 = l2;
	//	this.lambda3 = l3;
	//	this.tolerance = t;
	this.reste1 = r1;
	this.reste2 = r2;
	this.reste3 = r3;
	repaint();
    }

}

class ArcControls extends Panel
                  implements ActionListener {
    TextField d;
    TextField l1;
    TextField l2;
    TextField l3;
    TextField t;
    ArcCanvas canvas;

    public ArcControls(ArcCanvas canvas) {
	Button b = null;

	this.canvas = canvas;
	add(d = new TextField("1000.0", 12));
	add(l1 = new TextField("0.31", 12));
	add(l2 = new TextField("0.53", 12));
	add(l3 = new TextField("0.71", 12));
	add(t = new TextField("0.00001", 12));
	b = new Button("go");
	b.addActionListener(this);
	add(b);
	//	b = new Button("Draw");
	//	b.addActionListener(this);
	//	add(b);
    }
    public void actionPerformed(ActionEvent ev) {
	String label = ev.getActionCommand();

	//	canvas.redraw(label.equals("Fill"),
	canvas.redraw(Float.valueOf(d.getText()).floatValue(),
	              Float.valueOf(l1.getText()).floatValue(),
	              Float.valueOf(l2.getText()).floatValue(),
	              Float.valueOf(l3.getText()).floatValue(),
	              Float.valueOf(t.getText()).floatValue());
    }
}

class ArcOptions extends Panel
                  implements ActionListener {
    //    TextField d;
    private String restes;
    TextField r1;
    TextField r2;
    TextField r3;
    //    TextField t;
    ArcCanvas canvas;

    public ArcOptions(ArcCanvas canvas) {
	Button b = null;
        restes = "MESURES";
	this.canvas = canvas;
        add(new Label(restes));
	//	add(d = new TextField("10000.0", 12));
	add(r1 = new TextField("0.01", 12));
	add(r2 = new TextField("-0.02", 12));
	add(r3 = new TextField("0.03", 12));
	//	add(t = new TextField("0.00001", 12));
	b = new Button("Theory");
	b.addActionListener(this);
	add(b);
	b = new Button("Mesure");
	b.addActionListener(this);
	add(b);
    }

    public void actionPerformed(ActionEvent ev) {
	String label = ev.getActionCommand();

	canvas.reoption(label.equals("Theory"),
		      //	              Float.valueOf(d.getText()).floatValue(),
		      Float.valueOf(r1.getText()).floatValue(),
		      Float.valueOf(r2.getText()).floatValue(),
		      Float.valueOf(r3.getText()).floatValue());
		      //	              Float.valueOf(t.getText()).floatValue());
    }
}

