diff --git a/src/tutorials/java/AffinagePleiades/AffinageRugged.java b/src/tutorials/java/AffinagePleiades/AffinageRugged.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1ba241675dee2f023c4e8af3ca8c8afdd485e43
--- /dev/null
+++ b/src/tutorials/java/AffinagePleiades/AffinageRugged.java
@@ -0,0 +1,223 @@
+/* Copyright 2013-2016 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package AffinagePleiades;
+
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.FastMath;
+import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.Collections;
+
+
+
+import org.orekit.bodies.BodyShape;
+import org.orekit.bodies.GeodeticPoint;
+import org.orekit.data.DataProvidersManager;
+import org.orekit.data.DirectoryCrawler;
+import org.orekit.errors.OrekitException;
+import org.orekit.errors.OrekitExceptionWrapper;
+import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
+
+import org.orekit.orbits.Orbit;
+import org.orekit.rugged.api.AlgorithmId;
+import org.orekit.rugged.api.BodyRotatingFrameId;
+import org.orekit.rugged.api.EllipsoidId;
+import org.orekit.rugged.api.InertialFrameId;
+import org.orekit.rugged.api.Rugged;
+import org.orekit.rugged.api.RuggedBuilder;
+import org.orekit.rugged.errors.RuggedException;
+import org.orekit.rugged.linesensor.LineSensor;
+
+import org.orekit.time.AbsoluteDate;
+import org.orekit.utils.AngularDerivativesFilter;
+import org.orekit.utils.CartesianDerivativesFilter;
+import org.orekit.utils.PVCoordinates;
+
+/**
+ * Parameter estimation context 
+ * @author Jonathan Guinet
+ *
+ */
+public class AffinageRugged {
+
+    public static void main(String[] args) {
+
+        try {
+
+            // Initialize Orekit, assuming an orekit-data folder is in user home directory
+            File home       = new File(System.getProperty("user.home"));
+            File orekitData = new File(home, "workspace/data/orekit-data");
+            DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(orekitData));
+
+            
+            //CreateOrbit
+            OrbitModel orbitmodel =  new OrbitModel();
+            BodyShape earth = orbitmodel.createEarth();
+            NormalizedSphericalHarmonicsProvider gravityField = orbitmodel.createGravityField();
+
+            //create Pleiades Viewing Model
+            PleiadesViewingModel pleiadesViewingModel = new PleiadesViewingModel();
+            AbsoluteDate minDate =  pleiadesViewingModel.getMinDate();
+            AbsoluteDate maxDate =  pleiadesViewingModel.getMaxDate();
+            AbsoluteDate refDate = pleiadesViewingModel.getDatationReference();
+
+            Orbit      orbit                                  = orbitmodel.createOrbit(gravityField.getMu(), refDate);
+            
+            PVCoordinates PV = orbit.getPVCoordinates(earth.getBodyFrame());
+            GeodeticPoint gp = earth.transform(PV.getPosition(), earth.getBodyFrame(), orbit.getDate());
+            
+            
+            
+            System.out.format(" **** Orbit Definition **** %n");
+            
+            System.out.format(Locale.US, "Geodetic Point at date %s : φ = %8.10f °, λ = %8.10f %n",orbit.getDate().toString(),
+                    FastMath.toDegrees(gp.getLatitude()),
+                    FastMath.toDegrees(gp.getLongitude()));
+            
+
+            System.out.format(" **** Build Viewing Model **** %n");       
+            
+           
+            // Build Rugged ...
+            RuggedBuilder ruggedBuilder = new RuggedBuilder();
+    
+            LineSensor lineSensor =  pleiadesViewingModel.getLineSensor(); 
+            ruggedBuilder.addLineSensor(lineSensor);
+            
+      
+            ruggedBuilder.setAlgorithm(AlgorithmId.IGNORE_DEM_USE_ELLIPSOID);
+            ruggedBuilder.setEllipsoid(EllipsoidId.WGS84, BodyRotatingFrameId.ITRF);
+            ruggedBuilder.setTimeSpan(minDate,maxDate, 0.001, 5.0).
+            		setTrajectory(InertialFrameId.EME2000,
+                    orbitmodel.orbitToPV(orbit, earth, minDate.shiftedBy(-0.0), maxDate.shiftedBy(+0.0), 0.25),
+                    8, CartesianDerivativesFilter.USE_PV,
+                    orbitmodel.orbitToQ(orbit, earth, minDate.shiftedBy(-0.0), maxDate.shiftedBy(+0.0), 0.25),
+                    2, AngularDerivativesFilter.USE_R);                  
+            Rugged rugged = ruggedBuilder.build();
+            
+
+           
+            
+            Vector3D position = lineSensor.getPosition(); // This returns a zero vector since we set the relative position of the sensor w.r.T the satellite to 0.
+            AbsoluteDate firstLineDate = lineSensor.getDate(pleiadesViewingModel.dimension/2);
+            System.out.format("date %s %n",firstLineDate.toString());
+            Vector3D los = lineSensor.getLOS(firstLineDate,0);
+            GeodeticPoint upLeftPoint = rugged.directLocation(firstLineDate, position, los);
+            System.out.format(Locale.US, "upper left point: φ = %8.10f °, λ = %8.10f °, h = %8.3f m%n",
+                              FastMath.toDegrees(upLeftPoint.getLatitude()),
+                              FastMath.toDegrees(upLeftPoint.getLongitude()),
+                              upLeftPoint.getAltitude());
+            los = lineSensor.getLOS(firstLineDate,pleiadesViewingModel.dimension-1);
+            GeodeticPoint upRightPoint = rugged.directLocation(firstLineDate, position, los);
+            System.out.format(Locale.US, "upper right point: φ = %8.10f °, λ = %8.10f °, h = %8.3f m%n",
+                              FastMath.toDegrees(upRightPoint.getLatitude()),
+                              FastMath.toDegrees(upRightPoint.getLongitude()),upRightPoint.getAltitude());                   
+            los = lineSensor.getLOS(firstLineDate,pleiadesViewingModel.dimension/2);
+            GeodeticPoint centerPoint = rugged.directLocation(firstLineDate, position, los);
+            System.out.format(Locale.US, "center point: φ = %8.10f °, λ = %8.10f °, h = %8.3f m%n",
+                              FastMath.toDegrees(centerPoint.getLatitude()),
+                              FastMath.toDegrees(centerPoint.getLongitude()),centerPoint.getAltitude());                   
+            
+            
+            System.out.format(" **** Add roll and pitch values **** %n");       
+            
+            
+            double rollValue =  FastMath.toRadians( 0.1);
+            double pitchValue = FastMath.toRadians(-0.3);
+                        
+            System.out.format("roll : %3.5f pitch : %3.5f \n",rollValue,pitchValue);
+
+            
+            rugged.
+            getLineSensor("line").
+            getParametersDrivers().
+            filter(driver -> driver.getName().equals("roll")).
+            findFirst().get().setValue(rollValue);
+
+            rugged.
+            getLineSensor("line").
+            getParametersDrivers().
+            filter(driver -> driver.getName().equals("pitch")).
+            findFirst().get().setValue(pitchValue);
+
+            
+            System.out.format(" **** Generate Measures **** %n");
+            
+            MeasureGenerator measure = new MeasureGenerator(pleiadesViewingModel,rugged);
+            measure.CreateMeasure(1000, 1000);
+
+            System.out.format(" **** Reset Roll/Pitch **** %n");
+            rugged.
+            getLineSensor(pleiadesViewingModel.getSensorName()).
+            getParametersDrivers().
+            filter(driver -> driver.getName().equals("roll") || driver.getName().equals("pitch")).
+            forEach(driver -> {
+                try {
+                    driver.setSelected(true);
+                    driver.setValue(0.0);
+                } catch (OrekitException e) {
+                    throw new OrekitExceptionWrapper(e);
+                }
+            });
+            
+            
+            
+            System.out.format(" **** Start optimization  **** %n");
+            // perform parameters estimation
+            int maxIterations = 15;
+            double convergenceThreshold =  1.0e-9;
+            
+            System.out.format("iterations max %d convergence threshold  %3.6e \n",maxIterations, convergenceThreshold);
+
+            
+            Optimum optimum = rugged.estimateFreeParameters(Collections.singletonList(measure.getMapping()), maxIterations,convergenceThreshold);
+            
+   
+            System.out.format(" Optimization performed in %d interation \n",optimum.getEvaluations());
+
+            // check estimated values
+            double estimatedRoll = rugged.getLineSensor(pleiadesViewingModel.getSensorName()).
+                                          getParametersDrivers().
+                                          filter(driver -> driver.getName().equals("roll")).
+                                          findFirst().get().getValue();
+            double rollError = (estimatedRoll - rollValue);
+            System.out.format("Estimated roll %3.5f roll error %3.6e  %n", estimatedRoll, rollError);
+
+            double estimatedPitch = rugged.getLineSensor(pleiadesViewingModel.getSensorName()).
+                                           getParametersDrivers().
+                                           filter(driver -> driver.getName().equals("pitch")).
+                                           findFirst().get().getValue();
+            double pitchError = (estimatedPitch - pitchValue);
+            System.out.format("Estimated pitch %3.5f pitch  error %3.6e  %n ", estimatedPitch, pitchError);
+            
+            
+
+        } catch (OrekitException oe) {
+            System.err.println(oe.getLocalizedMessage());
+            System.exit(1);
+        } catch (RuggedException re) {
+            System.err.println(re.getLocalizedMessage());
+            System.exit(1);
+        }
+
+    }
+
+
+
+}
diff --git a/src/tutorials/java/AffinagePleiades/MeasureGenerator.java b/src/tutorials/java/AffinagePleiades/MeasureGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..30ced6c94e7cd2b856a24b39cdb0742b94459b9e
--- /dev/null
+++ b/src/tutorials/java/AffinagePleiades/MeasureGenerator.java
@@ -0,0 +1,84 @@
+/* Copyright 2013-2016 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package AffinagePleiades;
+
+
+import org.orekit.rugged.api.SensorToGroundMapping;
+import org.orekit.rugged.api.Rugged;
+import org.orekit.rugged.linesensor.LineSensor;
+import org.orekit.rugged.linesensor.SensorPixel;
+import org.orekit.rugged.errors.RuggedException;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.bodies.GeodeticPoint;
+
+/** class for measure generation
+ * @author Jonathan Guinet
+ */
+public class MeasureGenerator {
+
+
+    /** mapping */
+    private SensorToGroundMapping mapping;
+
+    private Rugged rugged;
+    
+    private LineSensor sensor;
+
+    private PleiadesViewingModel viewingModel;
+    
+    
+    
+    /** Simple constructor.
+     * <p>
+     *
+     * </p>
+     */
+    public MeasureGenerator(PleiadesViewingModel viewingModel, Rugged rugged) throws RuggedException
+    {
+	
+    // generate reference mapping
+    String sensorName = viewingModel.getSensorName();	
+    mapping = new SensorToGroundMapping(sensorName);
+    this.rugged = rugged;
+    this.viewingModel = viewingModel;
+    sensor = rugged.getLineSensor(mapping.getSensorName());
+    
+    }
+    
+    public SensorToGroundMapping getMapping() {
+    	return mapping;
+    }
+    
+    
+    public void CreateMeasure(final int lineSampling,final int pixelSampling)  throws RuggedException
+    {
+    
+    for (double line = 0; line < viewingModel.dimension; line += lineSampling) {
+    	
+        AbsoluteDate date = sensor.getDate(line);
+        for (int pixel = 0; pixel < sensor.getNbPixels(); pixel += pixelSampling) {
+            GeodeticPoint gp2 = rugged.directLocation(date, sensor.getPosition(),
+                                                      sensor.getLOS(date, pixel));
+            mapping.addMapping(new SensorPixel(line, pixel), gp2);
+        }
+    }
+    }
+	
+
+    
+}
+
diff --git a/src/tutorials/java/AffinagePleiades/OrbitModel.java b/src/tutorials/java/AffinagePleiades/OrbitModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..4379aefa0dbcb97c5b9c6d438c2fc8305b77e937
--- /dev/null
+++ b/src/tutorials/java/AffinagePleiades/OrbitModel.java
@@ -0,0 +1,203 @@
+/* Copyright 2013-2016 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package AffinagePleiades;
+
+
+import org.hipparchus.geometry.euclidean.threed.Rotation;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
+import org.hipparchus.util.FastMath;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.orekit.attitudes.AttitudeProvider;
+import org.orekit.attitudes.NadirPointing;
+import org.orekit.attitudes.YawCompensation;
+import org.orekit.bodies.BodyShape;
+import org.orekit.bodies.CelestialBodyFactory;
+import org.orekit.bodies.OneAxisEllipsoid;
+import org.orekit.errors.OrekitException;
+import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
+import org.orekit.forces.gravity.ThirdBodyAttraction;
+import org.orekit.forces.gravity.potential.GravityFieldFactory;
+import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
+import org.orekit.frames.Frame;
+import org.orekit.frames.FramesFactory;
+import org.orekit.frames.Transform;
+import org.orekit.orbits.CircularOrbit;
+import org.orekit.orbits.Orbit;
+import org.orekit.orbits.OrbitType;
+import org.orekit.orbits.PositionAngle;
+import org.orekit.propagation.Propagator;
+import org.orekit.propagation.SpacecraftState;
+import org.orekit.propagation.analytical.KeplerianPropagator;
+import org.orekit.propagation.numerical.NumericalPropagator;
+import org.orekit.propagation.sampling.OrekitFixedStepHandler;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.TimeScale;
+import org.orekit.utils.Constants;
+import org.orekit.utils.IERSConventions;
+import org.orekit.utils.PVCoordinates;
+import org.orekit.utils.TimeStampedAngularCoordinates;
+import org.orekit.utils.TimeStampedPVCoordinates;
+
+
+/**
+ * Orbit Model class to generate PV and Q 
+ * @author jguinet
+ *
+ */
+
+public class OrbitModel {
+
+    public static void addSatellitePV(TimeScale gps, Frame eme2000, Frame itrf,
+                                      ArrayList<TimeStampedPVCoordinates> satellitePVList,
+                                      String absDate,
+                                      double px, double py, double pz, double vx, double vy, double vz)
+        throws OrekitException {
+        AbsoluteDate ephemerisDate = new AbsoluteDate(absDate, gps);
+        Vector3D position = new Vector3D(px, py, pz);
+        Vector3D velocity = new Vector3D(vx, vy, vz);
+        PVCoordinates pvITRF = new PVCoordinates(position, velocity);
+        Transform transform = itrf.getTransformTo(eme2000, ephemerisDate);
+        Vector3D pEME2000 = transform.transformPosition(pvITRF.getPosition());
+        Vector3D vEME2000 = transform.transformVector(pvITRF.getVelocity());
+        satellitePVList.add(new TimeStampedPVCoordinates(ephemerisDate, pEME2000, vEME2000, Vector3D.ZERO));
+    }
+
+    public void addSatelliteQ(TimeScale gps, ArrayList<TimeStampedAngularCoordinates> satelliteQList,
+                                     String absDate, double q0, double q1, double q2, double q3) {
+        AbsoluteDate attitudeDate = new AbsoluteDate(absDate, gps);
+        Rotation rotation = new Rotation(q0, q1, q2, q3, true);
+        TimeStampedAngularCoordinates pair =
+                new TimeStampedAngularCoordinates(attitudeDate, rotation, Vector3D.ZERO, Vector3D.ZERO);
+        satelliteQList.add(pair);
+    }
+
+    public  BodyShape createEarth()
+       throws OrekitException {
+        return new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
+                                    Constants.WGS84_EARTH_FLATTENING,
+                                    FramesFactory.getITRF(IERSConventions.IERS_2010, true));
+    }
+
+    public NormalizedSphericalHarmonicsProvider createGravityField()
+        throws OrekitException {
+        return GravityFieldFactory.getNormalizedProvider(12, 12);
+    }
+
+    public Orbit createOrbit(double mu,AbsoluteDate date)
+        throws OrekitException {
+        // the following orbital parameters have been computed using
+        // Orekit tutorial about phasing, using the following configuration:
+        //
+        //  orbit.date                          = 2012-01-01T00:00:00.000
+        //  phasing.orbits.number               = 143
+        //  phasing.days.number                 =  10
+        //  sun.synchronous.reference.latitude  = 0
+        //  sun.synchronous.reference.ascending = false
+        //  sun.synchronous.mean.solar.time     = 10:30:00
+        //  gravity.field.degree                = 12
+        //  gravity.field.order                 = 12
+        
+        Frame eme2000 = FramesFactory.getEME2000();
+        //return new CircularOrbit(7173352.811913891,
+        return new CircularOrbit(694000.0 + Constants.WGS84_EARTH_EQUATORIAL_RADIUS,//ajouter Rayon 
+                                 -4.029194321683225E-4, 0.0013530362644647786,
+                                 FastMath.toRadians(98.2), //pleiades inclinaison 98.2
+                                 FastMath.toRadians(-86.47),
+                                 FastMath.toRadians(135.9), PositionAngle.TRUE,
+                                 eme2000, date, mu);
+    }
+
+    public Propagator createPropagator(BodyShape earth,
+                                              NormalizedSphericalHarmonicsProvider gravityField,
+                                              Orbit orbit)
+        throws OrekitException {
+
+        AttitudeProvider yawCompensation = new YawCompensation(orbit.getFrame(), new NadirPointing(orbit.getFrame(), earth));
+        SpacecraftState state = new SpacecraftState(orbit,
+                                                    yawCompensation.getAttitude(orbit,
+                                                                                orbit.getDate(),
+                                                                                orbit.getFrame()),
+                                                    1180.0);
+
+        // numerical model for improving orbit
+        OrbitType type = OrbitType.CIRCULAR;
+        double[][] tolerances = NumericalPropagator.tolerances(0.1, orbit, type);
+        DormandPrince853Integrator integrator =
+                new DormandPrince853Integrator(1.0e-4 * orbit.getKeplerianPeriod(),
+                                               1.0e-1 * orbit.getKeplerianPeriod(),
+                                               tolerances[0], tolerances[1]);
+        integrator.setInitialStepSize(1.0e-2 * orbit.getKeplerianPeriod());
+        NumericalPropagator numericalPropagator = new NumericalPropagator(integrator);
+        numericalPropagator.addForceModel(new HolmesFeatherstoneAttractionModel(earth.getBodyFrame(), gravityField));
+        numericalPropagator.addForceModel(new ThirdBodyAttraction(CelestialBodyFactory.getSun()));
+        numericalPropagator.addForceModel(new ThirdBodyAttraction(CelestialBodyFactory.getMoon()));
+        numericalPropagator.setOrbitType(type);
+        numericalPropagator.setInitialState(state);
+        numericalPropagator.setAttitudeProvider(yawCompensation);
+        return numericalPropagator;
+
+    }
+
+
+    public List<TimeStampedPVCoordinates> orbitToPV(Orbit orbit, BodyShape earth,
+                                                     AbsoluteDate minDate, AbsoluteDate maxDate,
+                                                     double step)
+        throws OrekitException {
+        Propagator propagator = new KeplerianPropagator(orbit);
+        propagator.setAttitudeProvider(new YawCompensation(orbit.getFrame(), new NadirPointing(orbit.getFrame(), earth)));
+        propagator.propagate(minDate);
+        final List<TimeStampedPVCoordinates> list = new ArrayList<TimeStampedPVCoordinates>();
+        propagator.setMasterMode(step, new OrekitFixedStepHandler() {
+            public void init(SpacecraftState s0, AbsoluteDate t) {
+            }   
+            public void handleStep(SpacecraftState currentState, boolean isLast) {
+                list.add(new TimeStampedPVCoordinates(currentState.getDate(),
+                                                      currentState.getPVCoordinates().getPosition(),
+                                                      currentState.getPVCoordinates().getVelocity(),
+                                                      Vector3D.ZERO));
+            }
+        });
+        propagator.propagate(maxDate);
+        return list;
+    }
+
+    public List<TimeStampedAngularCoordinates> orbitToQ(Orbit orbit, BodyShape earth,
+                                                         AbsoluteDate minDate, AbsoluteDate maxDate,
+                                                         double step)
+        throws OrekitException {
+        Propagator propagator = new KeplerianPropagator(orbit);
+        propagator.setAttitudeProvider(new YawCompensation(orbit.getFrame(), new NadirPointing(orbit.getFrame(), earth)));
+        propagator.propagate(minDate);
+        final List<TimeStampedAngularCoordinates> list = new ArrayList<TimeStampedAngularCoordinates>();
+        propagator.setMasterMode(step, new OrekitFixedStepHandler() {
+            public void init(SpacecraftState s0, AbsoluteDate t) {
+            }   
+            public void handleStep(SpacecraftState currentState, boolean isLast) {
+                list.add(new TimeStampedAngularCoordinates(currentState.getDate(),
+                                                           currentState.getAttitude().getRotation(),
+                                                           Vector3D.ZERO, Vector3D.ZERO));
+            }
+        });
+        propagator.propagate(maxDate);
+        return list;
+    }
+
+}
+
diff --git a/src/tutorials/java/AffinagePleiades/PleiadesViewingModel.java b/src/tutorials/java/AffinagePleiades/PleiadesViewingModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..60b46b21bbe89465f013c2e6be4b85a49dccbc9b
--- /dev/null
+++ b/src/tutorials/java/AffinagePleiades/PleiadesViewingModel.java
@@ -0,0 +1,165 @@
+/* Copyright 2013-2016 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package AffinagePleiades;
+
+
+
+import org.hipparchus.geometry.euclidean.threed.Rotation;
+import org.hipparchus.geometry.euclidean.threed.RotationConvention;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+
+import org.hipparchus.util.FastMath;
+import java.util.ArrayList;
+import java.util.List;
+
+
+import org.orekit.rugged.los.FixedRotation;
+import org.orekit.rugged.los.LOSBuilder;
+import org.orekit.rugged.los.TimeDependentLOS;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.TimeScale;
+import org.orekit.time.TimeScalesFactory;
+import org.orekit.rugged.linesensor.LinearLineDatation;
+import org.orekit.rugged.linesensor.LineDatation;
+import org.orekit.rugged.linesensor.LineSensor;
+
+import org.orekit.rugged.errors.RuggedException;
+import org.orekit.errors.OrekitException;
+
+
+public class PleiadesViewingModel {
+	
+	public  double fov = 1.6; // 20km - alt 694km
+	public  double angle = 0.0;
+	public  double incidence = 15.0; 
+	public  LineSensor lineSensor;	
+	public  int dimension = 40000;
+	public  String date = "2016-01-01T00:00:00.0";
+	
+	private String sensorName;
+	
+    /** Simple constructor.
+     * <p>
+     *
+     * </p>
+     */
+    public PleiadesViewingModel() throws RuggedException, OrekitException {	
+    	sensorName = "line";
+    	this.createLineSensor();    	
+    }
+	
+	public   LOSBuilder rawLOS(Vector3D center, Vector3D normal, double halfAperture, int n)
+	{
+	
+        List<Vector3D> list = new ArrayList<Vector3D>(n);
+        for (int i = 0; i < n; ++i) {
+            double alpha = (halfAperture * (2 * i + 1 - n)) / (n - 1);
+            list.add(new Rotation(normal, alpha, RotationConvention.VECTOR_OPERATOR).applyTo(center));
+        }
+        return new LOSBuilder(list);
+    }	
+	
+	
+	public  TimeDependentLOS buildLOS()
+	{
+	
+ 
+	
+    // one line sensor
+    // position: 1.5m in front (+X) and 20 cm above (-Z) of the S/C center of mass
+    // los: swath in the (YZ) plane, looking at 50° roll, 2.6" per pixel
+	LOSBuilder losBuilder = rawLOS(new Rotation(Vector3D.PLUS_I,
+            FastMath.toRadians(angle),
+            RotationConvention.VECTOR_OPERATOR).applyTo(Vector3D.PLUS_K), Vector3D.PLUS_I, FastMath.toRadians(fov/2), dimension); 	
+
+   
+    losBuilder.addTransform(new FixedRotation("roll",  Vector3D.MINUS_I, 0.0));
+    losBuilder.addTransform(new FixedRotation("pitch", Vector3D.MINUS_J, 0.0));
+      
+    return  losBuilder.build();
+	}
+    
+	
+	public  AbsoluteDate getDatationReference() throws OrekitException
+	{
+	    // We use Orekit for handling time and dates, and Rugged for defining the datation model:
+	    TimeScale utc = TimeScalesFactory.getUTC();
+	    return new AbsoluteDate(date, utc);
+	}
+	
+
+	
+
+	
+	public  AbsoluteDate getMinDate() throws RuggedException
+	{
+		return lineSensor.getDate(0);
+	}
+	
+	public  AbsoluteDate  getMaxDate() throws RuggedException
+	{
+		return lineSensor.getDate(dimension);
+	}
+
+	public  LineSensor  getLineSensor() {
+			return lineSensor;
+	}
+
+	public  String getSensorName() {
+			return sensorName;
+	}
+	
+	
+	private  void  createLineSensor()
+			throws RuggedException, OrekitException {
+	
+		//System.out.println("add line sensor");
+		
+		
+	      
+	    // TBN: refining data are read only for level L1B or L1C
+		//System.out.println("refining info");
+	      
+        // Offset of the MSI from center of mass of satellite
+		//System.out.println("MSI offset from center of mass of satellite");
+        // one line sensor
+        // position: 1.5m in front (+X) and 20 cm above (-Z) of the S/C center of mass
+        // los: swath in the (YZ) plane, looking at 50° roll, 2.6" per pixel
+        //Vector3D msiOffset = new Vector3D(1.5, 0, -0.2);
+		Vector3D msiOffset = new Vector3D(0, 0, 0);
+        
+
+        
+        // to do build complex los 
+        TimeDependentLOS lineOfSight = buildLOS();
+        
+        
+        double linePeriod =  1.0 / 1.5e-3;
+        // linear datation model: at reference time we get the middle line, and the rate is one line every 1.5ms
+
+        
+        
+        LineDatation lineDatation = new LinearLineDatation(getDatationReference(), dimension / 2, linePeriod);
+        //LineDatation lineDatation = new LinearLineDatation(absDate, 1d, 20);
+        lineSensor = new LineSensor(sensorName, lineDatation, msiOffset, lineOfSight);
+        
+
+	}
+
+
+}
+