import java.lang.*;
import java.util.*;

/**
 * Calcul du tableau de pixels resultat du ccd
 * @author Phillippe Rousselot, adpaté en java par Delphine Dard
 */
public class Ccd {
    /** La frame qui va utilise ce ccd */ 
    public ResultatFrame resultatFrame;

    /* Donnees modifiables par l'utilisateur*/

    /** magnitude de l'objet (objet ponctuel) */
    public float magnitude = 21.0f; 
    /** Valeur du seeing, en arcsecondes (entre 0.3 et 3.0) */
    public float seeing = 0.5f;
    /** Diametre du telescope, en metres */
    public float diametre = 2.0f;   
    /** temps d'integration, en secondes */
    public float temps = 300f;     
    /** Rapport focale/diametre (sans dimensions) */
    public float focale = 20.0f;    
    /** gain du CCD (electrons/ADU) */
    public float gain = 8.0f; 
    /** 
     * Nombre de jours ecoules par rapport a
     * la nouvelle Lune (= 0, 3, 7, 10, 14) 
     */
    public int lune = 14;     


    /* Parametre fixes */
    
    /** Point zero du flux pour la bande V */
    public static float zBand = 7.4425f;
    /** Longueur d'onde centrale du filtre (V) */
    public static float lambda0 = 5500f;
    /** Bande passante du filtre (en Angstroems) */
    public static float delta = 1150f;
    /** Taille d'un pixel, en micrometres */
    public static float pix = 15f;
    /** Bruit de lecture, en electrons */
    public static float RON =4.0f;
    /** Rendement quantique du CCD */
    public static float eta = 0.80f;
    /** Transmission filtre + telescope */
    public static float transmission = 0.80f; 
    /** Masse d'air */
    public static float airmass = 1.5f;
    /** Magnitudes d'absorption/masse d'air */
    public static float absorption = 0.20f; 
    
    /* ADU */
    int ADU;

    /* Differentes variables ...*/
    float integrale,k,step,Ia,Ib,xx;
    float taille, distance, ciel, I0, sigmaseeing, sigma, intensite;
    float flux_tot, flux_ciel, flux, x;
    double Ri; 
  
    /** Tableau de pixels de l'image entiere */
    public int resultat[] = new int[41*41];
    /** Tableau de pixels de la ligne 21 */
    public int ligne21[] = new int[41];

    /** indique la saturation de l'image (vrai si saturée) */
    public boolean saturation = false;
    /** A vrai si la taille de la focale est trop grande */
    public boolean tailleFocale = false;

    /** 
     * Constructeur avec le resultatFrame
     */
    public Ccd(ResultatFrame resultatFrame){
	this.resultatFrame = resultatFrame;
    }

    /**
     * Obtention de l'image
     * @return le tableau de pixels de l'image
     */
    public int[] intensitePixels(){
	calculTaille();

	verifieFocale();

	magnitudeCiel();

	fluxGlobal();
	
	intensiteMaxi();

	intensiteFondCiel();
  
	tableauIntensite();
	
	if (tailleFocale) {
	    resultatFrame.tailleFocaleMessage();
	    tailleFocale = false;
	}
	if (saturation) {
	    resultatFrame.saturationMessage();
	    saturation = false;
	}

	return resultat;
    }

    /**
     * Calcul taille d'un pixel sur le ciel (arcsec) 
     */
    public void calculTaille(){
	taille = new Float(15e-6/focale*180/Math.PI*3600).floatValue(); 
    }

    /** 
     * Verifie la grandeur de la focale
     */
    public void verifieFocale(){
	if (taille > (seeing/2))  {
	    tailleFocale = true;
	}
    }
  
    /**
     * determination de la magnitude du fond de ciel/ arcsec carre 
     */
    public void magnitudeCiel(){
	if (lune==0) 
	    ciel = 21.8f;
	if (lune==3) 
	    ciel = 21.7f;
	if (lune==7) 
	    ciel = 21.4f;
	if (lune==10) 
	    ciel = 20.7f;
	if (lune==14) 
	    ciel = 20.0f;
    }
  
    /**
     * calcul du flux global de l'objet, en electrons 
     */
    public void fluxGlobal(){
	float magnitude2 = magnitude + airmass * absorption;
	flux_tot = new Float(5.035e10*lambda0*Math.pow(10,-0.4*magnitude2)*Math.pow(10,-1*zBand)).floatValue();
	flux_tot = flux_tot * delta * eta * transmission;
	flux_tot = new Float(flux_tot * 0.96 * Math.PI * diametre * diametre /4).floatValue();
	flux_tot = flux_tot * temps;
    }

    /**
     *  calcul de l'intensite maxi, en electrons/ arcsec 2 
     */
    public void intensiteMaxi(){
	sigmaseeing = new Float(seeing / (2 * Math.sqrt(Math.log(2)))).floatValue(); 
	I0 = new Float(flux_tot / ( Math.PI * sigmaseeing * sigmaseeing )).floatValue();
    }

    /** 
     * calcul de l'intensite du fond de ciel en electrons / pixel 
     */
    public void intensiteFondCiel(){
	flux_ciel = new Float(5.035e10*lambda0*Math.pow(10,-0.4*ciel)*Math.pow(10,-1*zBand)).floatValue();
	flux_ciel = flux_ciel * delta * eta * transmission;
	flux_ciel = new Float(flux_ciel * 0.96 * Math.PI * diametre * diametre / 4 * temps).floatValue();
	flux_ciel = flux_ciel * taille * taille;
    }

    /**
     * Calcul du tableau des pixels (intensites)
     */
    public void tableauIntensite(){
	/* calcul du tableau de l'intensite des differents pixels ****
	   i designe la ligne (en partant du bas) et j la colonne */
	int index = 0;
	int index21 = 0;
	for (int i=1; i<=41; i++) {
	    for (int j=1; j<=41; j++) { 
		/* calcul de la distance du pixel considere au centre de
		   l'image de l'objet (en arcsec) */
		distance= new Float(Math.sqrt((21-i)*(21-i)+(21-j)*(21-j))*taille).floatValue(); 
		
		/* calcul du flux theorique recu par le pixel en provenance
		   de l'objet */
		flux = new Float(I0 * Math.exp (-1*distance*distance/(sigmaseeing*sigmaseeing))).floatValue();
		flux = flux * taille * taille;
		
		/* calcul de l'intensite effective du pixel due a l'objet,
		   au fond de ciel et aux differents bruits (ciel + objet +
		   bruit de lecture) : */
		
		Ri = Math.random();
		
		sigma = new Float(Math.sqrt(flux+flux_ciel+RON)).floatValue();
		
		integrale = 0;
		k = new Float((1/(sigma*Math.sqrt(2*Math.PI)))*2).floatValue();
		step = sigma/100;
		Ia = 1;
		for(xx=step; xx<=4*sigma; xx=xx+step) {
		  Ib = new Float(Math.exp(-1*xx*xx/(2*sigma*sigma))).floatValue();
		  integrale = integrale+(Ia+Ib)/2*step;
		  if ((integrale*k) > Ri) {
		    break;
		  }
		  Ia=Ib;
		}

		x = xx;

		Ri = Math.random();
		
		if (Ri <= 0.5) 
		    x = -1 * x;   /* x represente le bruit */
		
		intensite = (flux + flux_ciel + x)/gain;
		
		ADU=(int)(intensite);
		if (ADU>65535) {
		    ADU=65535; 
		    saturation = true;
		} 
		resultat[index++] = ADU;
		if (i == 21)
		    ligne21[index21++] = ADU;
	    }
	}
    }
    
    /**
     * Obtention de la ligne de pixels 21
     * @return le tableau de pixels de la ligne21
     */
    public int[] getLigne21(){
	return (ligne21);
    }
        
    /**
     * Obtention de la magnitude
     * @return la magnitude
     */
    public float getMagnitude(){
	return magnitude;
    }
    
    /**
     * Obtention du seeing
     * @return le seeing
     */
    public float getSeeing(){
	return seeing;
    }
    
    /**
     * Obtention du diametre
     * @return le diametre
     */
    public float getDiametre(){
	return diametre;
    }

    /** 
     * Obtention de l'integration
     * @return l'integration
     */
    public float getIntegration(){
	return temps;
    }

    /**
     * Obtention de la focale
     * @return la focale
     */
    public float getFocale(){
	return focale;
    }

    /**
     * Obtention du nombre de jour depuis la nouvelle lune
     * @return le nombre de jours
     */
    public int getLune(){
	return lune;
    }
    
    /**
     * mets les nouvelles valeurs des variables modifiables
     * @param magnitude la magnitude
     * @param seeing le seeing
     * @param diametre le diametre
     * @param integration le temps d'integration
     * @param focale la focale
     * @param gain le gain obtenu
     * @param lune le nombre de jour de la nouvelle lune
     */
    public void setValeur(float magnitude,
			  float seeing,
			  float diametre,
			  float integration,
			  float focale,
			  float gain,
			  int lune){
	this.magnitude = magnitude;
	this.seeing = seeing;
	this.diametre = diametre;
	this.temps = integration;
	this.focale = focale;
	this.gain = gain;
	this.lune = lune;
    }
    
}




