diff --git a/src/main/java/org/orekit/rugged/api/Rugged.java b/src/main/java/org/orekit/rugged/api/Rugged.java
index a38a2798a4be7e3457b1392aeb01c0e262149704..ab77fe3e62f517282b910267297c297491bc3338 100644
--- a/src/main/java/org/orekit/rugged/api/Rugged.java
+++ b/src/main/java/org/orekit/rugged/api/Rugged.java
@@ -17,18 +17,67 @@
 package org.orekit.rugged.api;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
 import org.apache.commons.math3.util.Pair;
+import org.orekit.attitudes.Attitude;
+import org.orekit.attitudes.AttitudeProvider;
+import org.orekit.attitudes.TabulatedProvider;
 import org.orekit.bodies.GeodeticPoint;
+import org.orekit.errors.OrekitException;
+import org.orekit.frames.Frame;
+import org.orekit.frames.FramesFactory;
+import org.orekit.frames.Transform;
+import org.orekit.orbits.CartesianOrbit;
+import org.orekit.orbits.Orbit;
+import org.orekit.propagation.Propagator;
+import org.orekit.rugged.core.BasicScanAlgorithm;
+import org.orekit.rugged.core.ExtendedEllipsoid;
+import org.orekit.rugged.core.IgnoreDEMAlgorithm;
+import org.orekit.rugged.core.Sensor;
+import org.orekit.rugged.core.SpacecraftToObservedBody;
+import org.orekit.rugged.core.duvenhage.DuvenhageAlgorithm;
+import org.orekit.rugged.core.raster.IntersectionAlgorithm;
 import org.orekit.time.AbsoluteDate;
+import org.orekit.utils.Constants;
+import org.orekit.utils.IERSConventions;
+import org.orekit.utils.ImmutableTimeStampedCache;
 import org.orekit.utils.PVCoordinates;
+import org.orekit.utils.PVCoordinatesProvider;
 
-/** Main interface to Rugged library.
+/** Main class of Rugged library API.
  * @author Luc Maisonobe
  */
-public interface Rugged {
+public class Rugged {
+
+    /** Reference date. */
+    private AbsoluteDate referenceDate;
+
+    /** Inertial frame. */
+    private Frame frame;
+
+    /** Reference ellipsoid. */
+    private ExtendedEllipsoid ellipsoid;
+
+    /** Converter between spacecraft and body. */
+    private SpacecraftToObservedBody scToBody;
+
+    /** Sensors. */
+    private final Map<String, Sensor> sensors;
+
+    /** DEM intersection algorithm. */
+    private IntersectionAlgorithm algorithm;
+
+    /** Simple constructor.
+     */
+    protected Rugged() {
+        sensors = new HashMap<String, Sensor>();
+    }
 
     /** Set up general context.
      * <p>
@@ -47,25 +96,257 @@ public interface Rugged {
      * @param aInterpolationOrder order to use for attitude interpolation
      * @exception RuggedException if data needed for some frame cannot be loaded
      */
-    void setGeneralContext(AbsoluteDate referenceDate,
-                           AlgorithmId algorithmID, EllipsoidId ellipsoidID,
-                           InertialFrameId inertialFrameID, BodyRotatingFrameId bodyRotatingFrameID,
-                           List<Pair<AbsoluteDate, PVCoordinates>> positionsVelocities, int pvInterpolationOrder,
-                           List<Pair<AbsoluteDate, Rotation>> quaternions, int aInterpolationOrder)
-        throws RuggedException;
+    public  void setGeneralContext(final AbsoluteDate newReferenceDate,
+                                   final AlgorithmId algorithmID, final EllipsoidId ellipsoidID,
+                                   final InertialFrameId inertialFrameID,
+                                   final BodyRotatingFrameId bodyRotatingFrameID,
+                                   final List<Pair<AbsoluteDate, PVCoordinates>> positionsVelocities, final int pvInterpolationOrder,
+                                   final List<Pair<AbsoluteDate, Rotation>> quaternions, final int aInterpolationOrder)
+        throws RuggedException {
+        try {
+
+            // time reference
+            this.referenceDate = newReferenceDate;
+
+            // space reference
+            frame = selectInertialFrame(inertialFrameID);
+            ellipsoid = selectEllipsoid(ellipsoidID, selectBodyRotatingFrame(bodyRotatingFrameID));
+
+            // orbit/attitude to body converter
+            final PVCoordinatesProvider pvProvider = selectPVCoordinatesProvider(positionsVelocities, pvInterpolationOrder);
+            final AttitudeProvider aProvider = selectAttitudeProvider(quaternions, aInterpolationOrder);
+            scToBody = new SpacecraftToObservedBody(frame, ellipsoid.getBodyFrame(), pvProvider, aProvider);
+
+            // intersection algorithm
+            algorithm = selectAlgorithm(algorithmID);
+
+        } catch (OrekitException oe) {
+            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts().clone());
+        }
+    }
+
+    /** Set up general context.
+     * <p>
+     * This method is the first one that must be called, otherwise the
+     * other methods will fail due to uninitialized context.
+     * </p>
+     * @param newReferenceDate reference date from which all other dates are computed
+     * @param algorithmID identifier of algorithm to use for Digital Elevation Model intersection
+     * @param ellipsoidID identifier of reference ellipsoid
+     * @param inertialFrameID identifier of inertial frame
+     * @param bodyRotatingFrameID identifier of body rotating frame
+     * @param propagator global propagator
+     * @exception RuggedException if data needed for some frame cannot be loaded
+     */
+    public void setGeneralContext(final AbsoluteDate newReferenceDate,
+                                  final AlgorithmId algorithmID, final EllipsoidId ellipsoidID,
+                                  final InertialFrameId inertialFrameID,
+                                  final BodyRotatingFrameId bodyRotatingFrameID,
+                                  final Propagator propagator)
+        throws RuggedException {
+        try {
+
+            // time reference
+            this.referenceDate = newReferenceDate;
+
+            // space reference
+            frame = selectInertialFrame(inertialFrameID);
+            ellipsoid = selectEllipsoid(ellipsoidID, selectBodyRotatingFrame(bodyRotatingFrameID));
+
+            // orbit/attitude to body converter
+            scToBody = new SpacecraftToObservedBody(frame, ellipsoid.getBodyFrame(),
+                                                    propagator, propagator.getAttitudeProvider());
+
+            // intersection algorithm
+            algorithm = selectAlgorithm(algorithmID);
+
+        } catch (OrekitException oe) {
+            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts().clone());
+        }
+    }
+
+    /** Get the reference date.
+     * @return reference date
+     */
+    public AbsoluteDate getReferenceDate() {
+        return referenceDate;
+    }
 
     /** Set up the tiles management.
      * @param updater updater used to load Digital Elevation Model tiles
      * @param maxCachedTiles maximum number of tiles stored in the cache
      */
-    void setUpTilesManagement(TileUpdater updater, int maxCachedTiles);
+    public void setUpTilesManagement(final TileUpdater updater, final int maxCachedTiles) {
+        algorithm.setUpTilesManagement(updater, maxCachedTiles);
+    }
 
     /** Set up line sensor model.
      * @param name name of the line sensor.
      * @param pixels lines of sight for each pixels
      * @param datationModel model to use for dating sensor lines
      */
-    void setLineSensor(String name, List<PixelLOS> pixels, LineDatation datationModel);
+    public void setLineSensor(final String sensorName, final List<PixelLOS> linesOfSigth, final LineDatation datationModel) {
+        final List<Vector3D> positions = new ArrayList<Vector3D>(linesOfSigth.size());
+        final List<Vector3D> los       = new ArrayList<Vector3D>(linesOfSigth.size());
+        for (final PixelLOS plos : linesOfSigth) {
+            positions.add(new Vector3D(plos.getPx(), plos.getPy(), plos.getPz()));
+            los.add(new Vector3D(plos.getDx(), plos.getDy(), plos.getDz()));
+        }
+        final Sensor sensor = new Sensor(sensorName, referenceDate, datationModel, positions, los);
+        sensors.put(sensor.getName(), sensor);
+    }
+
+    /** Select inertial frame.
+     * @param inertialFrame inertial frame identifier
+     * @return inertial frame
+     * @exception OrekitException if data needed for some frame cannot be loaded
+     */
+    private Frame selectInertialFrame(final InertialFrameId inertialFrame)
+        throws OrekitException {
+
+        // set up the inertial frame
+        switch (inertialFrame) {
+        case GCRF :
+            return FramesFactory.getGCRF();
+        case EME2000 :
+            return FramesFactory.getEME2000();
+        case MOD :
+            return FramesFactory.getMOD(IERSConventions.IERS_1996);
+        case TOD :
+            return FramesFactory.getTOD(IERSConventions.IERS_1996, true);
+        case VEIS1950 :
+            return FramesFactory.getVeis1950();
+        default :
+            // this should never happen
+            throw RuggedException.createInternalError(null);
+        }
+
+    }
+
+    /** Select body rotating frame.
+     * @param bodyRotatingFrame body rotating frame identifier
+     * @return body rotating frame
+     * @exception OrekitException if data needed for some frame cannot be loaded
+     */
+    private Frame selectBodyRotatingFrame(final BodyRotatingFrameId bodyRotatingFrame)
+        throws OrekitException {
+
+        // set up the rotating frame
+        switch (bodyRotatingFrame) {
+        case ITRF :
+            return FramesFactory.getITRF(IERSConventions.IERS_2010, true);
+        case ITRF_EQUINOX :
+            return FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true);
+        case GTOD :
+            return FramesFactory.getGTOD(IERSConventions.IERS_1996, true);
+        default :
+            // this should never happen
+            throw RuggedException.createInternalError(null);
+        }
+
+    }
+
+    /** Select ellipsoid.
+     * @param ellipsoidID reference ellipsoid identifier
+     * @param bodyFrame body rotating frame
+     * @return selected ellipsoid
+     * @exception OrekitException if data needed for some frame cannot be loaded
+     */
+    private ExtendedEllipsoid selectEllipsoid(final EllipsoidId ellipsoidID, final Frame bodyFrame)
+        throws OrekitException {
+
+        // set up the ellipsoid
+        switch (ellipsoidID) {
+        case GRS80 :
+            return new ExtendedEllipsoid(6378137.0, 1.0 / 298.257222101, bodyFrame);
+        case WGS84 :
+            return new ExtendedEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
+                                         Constants.WGS84_EARTH_FLATTENING,
+                                         bodyFrame);
+        case IERS96 :
+            return new ExtendedEllipsoid(6378136.49, 1.0 / 298.25645, bodyFrame);
+        case IERS2003 :
+            return new ExtendedEllipsoid(6378136.6, 1.0 / 298.25642, bodyFrame);
+        default :
+            // this should never happen
+            throw RuggedException.createInternalError(null);
+        }
+
+    }
+
+    /** Select attitude provider.
+     * @param quaternions satellite quaternions
+     * @param interpolationOrder order to use for interpolation
+     * @return selected attitude provider
+     * @exception OrekitException if data needed for some frame cannot be loaded
+     */
+    private AttitudeProvider selectAttitudeProvider(final List<Pair<AbsoluteDate, Rotation>> quaternions,
+                                                    final int interpolationOrder)
+        throws OrekitException {
+
+        // set up the attitude provider
+        final List<Attitude> attitudes = new ArrayList<Attitude>(quaternions.size());
+        for (final Pair<AbsoluteDate, Rotation> q : quaternions) {
+            attitudes.add(new Attitude(q.getFirst(), frame, q.getSecond(), Vector3D.ZERO));
+        }
+        return new TabulatedProvider(attitudes, interpolationOrder, false);
+
+    }
+
+    /** Select position/velocity provider.
+     * @param positionsVelocities satellite position and velocity
+     * @param interpolationOrder order to use for interpolation
+     * @return selected position/velocity provider
+     * @exception OrekitException if data needed for some frame cannot be loaded
+     */
+    private PVCoordinatesProvider selectPVCoordinatesProvider(final List<Pair<AbsoluteDate, PVCoordinates>> positionsVelocities,
+                                                              final int interpolationOrder)
+        throws OrekitException {
+
+        // set up the ephemeris
+        final List<Orbit> orbits = new ArrayList<Orbit>(positionsVelocities.size());
+        for (final Pair<AbsoluteDate, PVCoordinates> pv : positionsVelocities) {
+            final CartesianOrbit orbit = new CartesianOrbit(pv.getSecond(), frame, pv.getFirst(), Constants.EIGEN5C_EARTH_MU);
+            orbits.add(orbit);
+        }
+
+        final ImmutableTimeStampedCache<Orbit> cache =
+                new ImmutableTimeStampedCache<Orbit>(interpolationOrder, orbits);
+        return new PVCoordinatesProvider() {
+
+            /** {@inhritDoc} */
+            @Override
+            public PVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f)
+                throws OrekitException {
+                final List<Orbit> sample = cache.getNeighbors(date);
+                final Orbit interpolated = sample.get(0).interpolate(date, sample);
+                return interpolated.getPVCoordinates(date, f);
+            }
+
+        };
+
+    }
+
+    /** Select DEM intersection algorithm.
+     * @param algorithmID intersection algorithm identifier
+     * @return selected algorithm
+     */
+    private IntersectionAlgorithm selectAlgorithm(final AlgorithmId algorithmID) {
+
+        // set up the algorithm
+        switch (algorithmID) {
+        case DUVENHAGE :
+            return new DuvenhageAlgorithm();
+        case BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY :
+            return new BasicScanAlgorithm();
+        case IGNORE_DEM_USE_ELLIPSOID :
+            return new IgnoreDEMAlgorithm();
+        default :
+            // this should never happen
+            throw RuggedException.createInternalError(null);
+        }
+
+    }
 
     /** Direct localization of a sensor line.
      * @param name name of the line sensor
@@ -76,8 +357,43 @@ public interface Rugged {
      * not been called beforehand, or if {@link #setOrbitAndAttitude(List, List)} has not
      * been called beforehand, or sensor is unknown
      */
-    GeodeticPoint[] directLocalization(String name, double lineNumber)
-        throws RuggedException;
+    public GeodeticPoint[] directLocalization(final String sensorName, final double lineNumber)
+        throws RuggedException {
+        try {
+
+            checkContext();
+
+            // select the sensor
+            final Sensor sensor = getSensor(sensorName);
+
+            // compute the approximate transform between spacecraft and observed body
+            final AbsoluteDate date        = sensor.getDate(lineNumber);
+            final Transform    scToInert   = scToBody.getScToInertial(date);
+            final Transform    inertToBody = scToBody.getInertialToBody(date);
+            final Transform    approximate = new Transform(date, scToInert, inertToBody);
+
+            // compute localization of each pixel
+            final GeodeticPoint[] gp = new GeodeticPoint[sensor.getNbPixels()];
+            for (int i = 0; i < gp.length; ++i) {
+
+                // fix light travel time
+                final Vector3D  sP     = approximate.transformPosition(sensor.getPosition(i));
+                final Vector3D  sL     = approximate.transformVector(sensor.getLos(i));
+                final Vector3D  eP     = ellipsoid.transform(ellipsoid.pointOnGround(sP, sL));
+                final double    deltaT = eP.distance(sP) / Constants.SPEED_OF_LIGHT;
+                final Transform fixed  = new Transform(date, scToInert, inertToBody.shiftedBy(-deltaT));
+
+                gp[i] = algorithm.intersection(ellipsoid,
+                                               fixed.transformPosition(sensor.getPosition(i)),
+                                               fixed.transformVector(sensor.getLos(i)));
+            }
+
+            return gp;
+
+        } catch (OrekitException oe) {
+            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts());
+        }
+    }
 
     /** Inverse localization of a ground point.
      * @param name name of the line  sensor
@@ -88,7 +404,37 @@ public interface Rugged {
      * not been called beforehand, or if {@link #setOrbitAndAttitude(List, List)} has not
      * been called beforehand, or sensor is unknown
      */
-    SensorPixel inverseLocalization(String name, GeodeticPoint groundPoint)
-        throws RuggedException;
+    public SensorPixel inverseLocalization(final String sensorName, final GeodeticPoint groundPoint)
+        throws RuggedException {
+
+        checkContext();
+        final Sensor sensor = getSensor(sensorName);
+
+        // TODO: implement direct localization
+        throw RuggedException.createInternalError(null);
+
+    }
+
+    /** Check if context has been initialized.
+     * @exception RuggedException if context has not been initialized
+     */
+    private void checkContext() throws RuggedException {
+        if (frame == null) {
+            throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT);
+        }
+    }
+
+    /** Get a sensor.
+     * @param sensorName sensor name
+     * @return selected sensor
+     * @exception RuggedException if sensor is not known
+     */
+    private Sensor getSensor(final String sensorName) throws RuggedException {
+        final Sensor sensor = sensors.get(sensorName);
+        if (sensor == null) {
+            throw new RuggedException(RuggedMessages.UNKNOWN_SENSOR, sensorName);
+        }
+        return sensor;
+    }
 
 }
diff --git a/src/main/java/org/orekit/rugged/core/RuggedImpl.java b/src/main/java/org/orekit/rugged/core/RuggedImpl.java
deleted file mode 100644
index 8a37a610637a159b572c3b67312fe19855ed9a92..0000000000000000000000000000000000000000
--- a/src/main/java/org/orekit/rugged/core/RuggedImpl.java
+++ /dev/null
@@ -1,411 +0,0 @@
-/* Copyright 2013-2014 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 org.orekit.rugged.core;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
-import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
-import org.apache.commons.math3.util.Pair;
-import org.orekit.attitudes.Attitude;
-import org.orekit.attitudes.AttitudeProvider;
-import org.orekit.attitudes.TabulatedProvider;
-import org.orekit.bodies.GeodeticPoint;
-import org.orekit.errors.OrekitException;
-import org.orekit.frames.Frame;
-import org.orekit.frames.FramesFactory;
-import org.orekit.frames.Transform;
-import org.orekit.orbits.CartesianOrbit;
-import org.orekit.orbits.Orbit;
-import org.orekit.propagation.Propagator;
-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.LineDatation;
-import org.orekit.rugged.api.PixelLOS;
-import org.orekit.rugged.api.Rugged;
-import org.orekit.rugged.api.RuggedException;
-import org.orekit.rugged.api.RuggedMessages;
-import org.orekit.rugged.api.SensorPixel;
-import org.orekit.rugged.api.TileUpdater;
-import org.orekit.rugged.core.duvenhage.DuvenhageAlgorithm;
-import org.orekit.rugged.core.raster.IntersectionAlgorithm;
-import org.orekit.time.AbsoluteDate;
-import org.orekit.utils.Constants;
-import org.orekit.utils.IERSConventions;
-import org.orekit.utils.ImmutableTimeStampedCache;
-import org.orekit.utils.PVCoordinates;
-import org.orekit.utils.PVCoordinatesProvider;
-
-/** Top level Rugged class.
- * @author Luc Maisonobe
- */
-public class RuggedImpl implements Rugged {
-
-    /** Reference date. */
-    private AbsoluteDate referenceDate;
-
-    /** Inertial frame. */
-    private Frame frame;
-
-    /** Reference ellipsoid. */
-    private ExtendedEllipsoid ellipsoid;
-
-    /** Converter between spacecraft and body. */
-    private SpacecraftToObservedBody scToBody;
-
-    /** Sensors. */
-    private final Map<String, Sensor> sensors;
-
-    /** DEM intersection algorithm. */
-    private IntersectionAlgorithm algorithm;
-
-    /** Simple constructor.
-     */
-    protected RuggedImpl() {
-        sensors = new HashMap<String, Sensor>();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public  void setGeneralContext(final AbsoluteDate newReferenceDate,
-                                   final AlgorithmId algorithmID, final EllipsoidId ellipsoidID,
-                                   final InertialFrameId inertialFrameID,
-                                   final BodyRotatingFrameId bodyRotatingFrameID,
-                                   final List<Pair<AbsoluteDate, PVCoordinates>> positionsVelocities, final int pvInterpolationOrder,
-                                   final List<Pair<AbsoluteDate, Rotation>> quaternions, final int aInterpolationOrder)
-        throws RuggedException {
-        try {
-
-            // time reference
-            this.referenceDate = newReferenceDate;
-
-            // space reference
-            frame = selectInertialFrame(inertialFrameID);
-            ellipsoid = selectEllipsoid(ellipsoidID, selectBodyRotatingFrame(bodyRotatingFrameID));
-
-            // orbit/attitude to body converter
-            final PVCoordinatesProvider pvProvider = selectPVCoordinatesProvider(positionsVelocities, pvInterpolationOrder);
-            final AttitudeProvider aProvider = selectAttitudeProvider(quaternions, aInterpolationOrder);
-            scToBody = new SpacecraftToObservedBody(frame, ellipsoid.getBodyFrame(), pvProvider, aProvider);
-
-            // intersection algorithm
-            algorithm = selectAlgorithm(algorithmID);
-
-        } catch (OrekitException oe) {
-            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts().clone());
-        }
-    }
-
-    /** Set up general context.
-     * <p>
-     * This method is the first one that must be called, otherwise the
-     * other methods will fail due to uninitialized context.
-     * </p>
-     * @param newReferenceDate reference date from which all other dates are computed
-     * @param algorithmID identifier of algorithm to use for Digital Elevation Model intersection
-     * @param ellipsoidID identifier of reference ellipsoid
-     * @param inertialFrameID identifier of inertial frame
-     * @param bodyRotatingFrameID identifier of body rotating frame
-     * @param propagator global propagator
-     * @exception RuggedException if data needed for some frame cannot be loaded
-     */
-    public void setGeneralContext(final AbsoluteDate newReferenceDate,
-                                  final AlgorithmId algorithmID, final EllipsoidId ellipsoidID,
-                                  final InertialFrameId inertialFrameID,
-                                  final BodyRotatingFrameId bodyRotatingFrameID,
-                                  final Propagator propagator)
-        throws RuggedException {
-        try {
-
-            // time reference
-            this.referenceDate = newReferenceDate;
-
-            // space reference
-            frame = selectInertialFrame(inertialFrameID);
-            ellipsoid = selectEllipsoid(ellipsoidID, selectBodyRotatingFrame(bodyRotatingFrameID));
-
-            // orbit/attitude to body converter
-            scToBody = new SpacecraftToObservedBody(frame, ellipsoid.getBodyFrame(),
-                                                    propagator, propagator.getAttitudeProvider());
-
-            // intersection algorithm
-            algorithm = selectAlgorithm(algorithmID);
-
-        } catch (OrekitException oe) {
-            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts().clone());
-        }
-    }
-
-    /** Get the reference date.
-     * @return reference date
-     */
-    public AbsoluteDate getReferenceDate() {
-        return referenceDate;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setUpTilesManagement(final TileUpdater updater, final int maxCachedTiles) {
-        algorithm.setUpTilesManagement(updater, maxCachedTiles);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setLineSensor(final String sensorName, final List<PixelLOS> linesOfSigth, final LineDatation datationModel) {
-        final List<Vector3D> positions = new ArrayList<Vector3D>(linesOfSigth.size());
-        final List<Vector3D> los       = new ArrayList<Vector3D>(linesOfSigth.size());
-        for (final PixelLOS plos : linesOfSigth) {
-            positions.add(new Vector3D(plos.getPx(), plos.getPy(), plos.getPz()));
-            los.add(new Vector3D(plos.getDx(), plos.getDy(), plos.getDz()));
-        }
-        final Sensor sensor = new Sensor(sensorName, referenceDate, datationModel, positions, los);
-        sensors.put(sensor.getName(), sensor);
-    }
-
-    /** Select inertial frame.
-     * @param inertialFrame inertial frame identifier
-     * @return inertial frame
-     * @exception OrekitException if data needed for some frame cannot be loaded
-     */
-    private Frame selectInertialFrame(final InertialFrameId inertialFrame)
-        throws OrekitException {
-
-        // set up the inertial frame
-        switch (inertialFrame) {
-        case GCRF :
-            return FramesFactory.getGCRF();
-        case EME2000 :
-            return FramesFactory.getEME2000();
-        case MOD :
-            return FramesFactory.getMOD(IERSConventions.IERS_1996);
-        case TOD :
-            return FramesFactory.getTOD(IERSConventions.IERS_1996, true);
-        case VEIS1950 :
-            return FramesFactory.getVeis1950();
-        default :
-            // this should never happen
-            throw RuggedException.createInternalError(null);
-        }
-
-    }
-
-    /** Select body rotating frame.
-     * @param bodyRotatingFrame body rotating frame identifier
-     * @return body rotating frame
-     * @exception OrekitException if data needed for some frame cannot be loaded
-     */
-    private Frame selectBodyRotatingFrame(final BodyRotatingFrameId bodyRotatingFrame)
-        throws OrekitException {
-
-        // set up the rotating frame
-        switch (bodyRotatingFrame) {
-        case ITRF :
-            return FramesFactory.getITRF(IERSConventions.IERS_2010, true);
-        case ITRF_EQUINOX :
-            return FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true);
-        case GTOD :
-            return FramesFactory.getGTOD(IERSConventions.IERS_1996, true);
-        default :
-            // this should never happen
-            throw RuggedException.createInternalError(null);
-        }
-
-    }
-
-    /** Select ellipsoid.
-     * @param ellipsoidID reference ellipsoid identifier
-     * @param bodyFrame body rotating frame
-     * @return selected ellipsoid
-     * @exception OrekitException if data needed for some frame cannot be loaded
-     */
-    private ExtendedEllipsoid selectEllipsoid(final EllipsoidId ellipsoidID, final Frame bodyFrame)
-        throws OrekitException {
-
-        // set up the ellipsoid
-        switch (ellipsoidID) {
-        case GRS80 :
-            return new ExtendedEllipsoid(6378137.0, 1.0 / 298.257222101, bodyFrame);
-        case WGS84 :
-            return new ExtendedEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
-                                         Constants.WGS84_EARTH_FLATTENING,
-                                         bodyFrame);
-        case IERS96 :
-            return new ExtendedEllipsoid(6378136.49, 1.0 / 298.25645, bodyFrame);
-        case IERS2003 :
-            return new ExtendedEllipsoid(6378136.6, 1.0 / 298.25642, bodyFrame);
-        default :
-            // this should never happen
-            throw RuggedException.createInternalError(null);
-        }
-
-    }
-
-    /** Select attitude provider.
-     * @param quaternions satellite quaternions
-     * @param interpolationOrder order to use for interpolation
-     * @return selected attitude provider
-     * @exception OrekitException if data needed for some frame cannot be loaded
-     */
-    private AttitudeProvider selectAttitudeProvider(final List<Pair<AbsoluteDate, Rotation>> quaternions,
-                                                    final int interpolationOrder)
-        throws OrekitException {
-
-        // set up the attitude provider
-        final List<Attitude> attitudes = new ArrayList<Attitude>(quaternions.size());
-        for (final Pair<AbsoluteDate, Rotation> q : quaternions) {
-            attitudes.add(new Attitude(q.getFirst(), frame, q.getSecond(), Vector3D.ZERO));
-        }
-        return new TabulatedProvider(attitudes, interpolationOrder, false);
-
-    }
-
-    /** Select position/velocity provider.
-     * @param positionsVelocities satellite position and velocity
-     * @param interpolationOrder order to use for interpolation
-     * @return selected position/velocity provider
-     * @exception OrekitException if data needed for some frame cannot be loaded
-     */
-    private PVCoordinatesProvider selectPVCoordinatesProvider(final List<Pair<AbsoluteDate, PVCoordinates>> positionsVelocities,
-                                                              final int interpolationOrder)
-        throws OrekitException {
-
-        // set up the ephemeris
-        final List<Orbit> orbits = new ArrayList<Orbit>(positionsVelocities.size());
-        for (final Pair<AbsoluteDate, PVCoordinates> pv : positionsVelocities) {
-            final CartesianOrbit orbit = new CartesianOrbit(pv.getSecond(), frame, pv.getFirst(), Constants.EIGEN5C_EARTH_MU);
-            orbits.add(orbit);
-        }
-
-        final ImmutableTimeStampedCache<Orbit> cache =
-                new ImmutableTimeStampedCache<Orbit>(interpolationOrder, orbits);
-        return new PVCoordinatesProvider() {
-
-            /** {@inhritDoc} */
-            @Override
-            public PVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f)
-                throws OrekitException {
-                final List<Orbit> sample = cache.getNeighbors(date);
-                final Orbit interpolated = sample.get(0).interpolate(date, sample);
-                return interpolated.getPVCoordinates(date, f);
-            }
-
-        };
-
-    }
-
-    /** Select DEM intersection algorithm.
-     * @param algorithmID intersection algorithm identifier
-     * @return selected algorithm
-     */
-    private IntersectionAlgorithm selectAlgorithm(final AlgorithmId algorithmID) {
-
-        // set up the algorithm
-        switch (algorithmID) {
-        case DUVENHAGE :
-            return new DuvenhageAlgorithm();
-        case BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY :
-            return new BasicScanAlgorithm();
-        case IGNORE_DEM_USE_ELLIPSOID :
-            return new IgnoreDEMAlgorithm();
-        default :
-            // this should never happen
-            throw RuggedException.createInternalError(null);
-        }
-
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public GeodeticPoint[] directLocalization(final String sensorName, final double lineNumber)
-        throws RuggedException {
-        try {
-
-            checkContext();
-
-            // select the sensor
-            final Sensor sensor = getSensor(sensorName);
-
-            // compute the approximate transform between spacecraft and observed body
-            final AbsoluteDate date        = sensor.getDate(lineNumber);
-            final Transform    scToInert   = scToBody.getScToInertial(date);
-            final Transform    inertToBody = scToBody.getInertialToBody(date);
-            final Transform    approximate = new Transform(date, scToInert, inertToBody);
-
-            // compute localization of each pixel
-            final GeodeticPoint[] gp = new GeodeticPoint[sensor.getNbPixels()];
-            for (int i = 0; i < gp.length; ++i) {
-
-                // fix light travel time
-                final Vector3D  sP     = approximate.transformPosition(sensor.getPosition(i));
-                final Vector3D  sL     = approximate.transformVector(sensor.getLos(i));
-                final Vector3D  eP     = ellipsoid.transform(ellipsoid.pointOnGround(sP, sL));
-                final double    deltaT = eP.distance(sP) / Constants.SPEED_OF_LIGHT;
-                final Transform fixed  = new Transform(date, scToInert, inertToBody.shiftedBy(-deltaT));
-
-                gp[i] = algorithm.intersection(ellipsoid,
-                                               fixed.transformPosition(sensor.getPosition(i)),
-                                               fixed.transformVector(sensor.getLos(i)));
-            }
-
-            return gp;
-
-        } catch (OrekitException oe) {
-            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts());
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public SensorPixel inverseLocalization(final String sensorName, final GeodeticPoint groundPoint)
-        throws RuggedException {
-
-        checkContext();
-        final Sensor sensor = getSensor(sensorName);
-
-        // TODO: implement direct localization
-        throw RuggedException.createInternalError(null);
-
-    }
-
-    /** Check if context has been initialized.
-     * @exception RuggedException if context has not been initialized
-     */
-    private void checkContext() throws RuggedException {
-        if (frame == null) {
-            throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT);
-        }
-    }
-
-    /** Get a sensor.
-     * @param sensorName sensor name
-     * @return selected sensor
-     * @exception RuggedException if sensor is not known
-     */
-    private Sensor getSensor(final String sensorName) throws RuggedException {
-        final Sensor sensor = sensors.get(sensorName);
-        if (sensor == null) {
-            throw new RuggedException(RuggedMessages.UNKNOWN_SENSOR, sensorName);
-        }
-        return sensor;
-    }
-
-}
diff --git a/src/main/java/org/orekit/rugged/core/Sensor.java b/src/main/java/org/orekit/rugged/core/Sensor.java
index 9758afc8a3ee95dd598fd8b8fb35556a409905b3..e1364c1570a8da3737a1c59f74de85d03a0532fc 100644
--- a/src/main/java/org/orekit/rugged/core/Sensor.java
+++ b/src/main/java/org/orekit/rugged/core/Sensor.java
@@ -28,7 +28,7 @@ import org.orekit.time.AbsoluteDate;
  * </p>
  * @author Luc Maisonobe
  */
-class Sensor {
+public class Sensor {
 
     /** Name of the sensor. */
     private final String name;
diff --git a/src/main/java/org/orekit/rugged/core/SpacecraftToObservedBody.java b/src/main/java/org/orekit/rugged/core/SpacecraftToObservedBody.java
index 044471c8152c587ad7c39bb77ff6f2a0e5fdef0c..43e46016639c99d6a58c0dcda00fe19caecb9d11 100644
--- a/src/main/java/org/orekit/rugged/core/SpacecraftToObservedBody.java
+++ b/src/main/java/org/orekit/rugged/core/SpacecraftToObservedBody.java
@@ -28,7 +28,7 @@ import org.orekit.utils.PVCoordinatesProvider;
 /** Provider for observation transforms.
  * @author Luc Maisonobe
  */
-class SpacecraftToObservedBody {
+public class SpacecraftToObservedBody {
 
     /** Inertial frame. */
     private Frame inertialFrame;
@@ -48,9 +48,9 @@ class SpacecraftToObservedBody {
      * @param pvProvider orbit propagator/interpolator
      * @param aProvider attitude propagator/interpolator
      */
-    protected SpacecraftToObservedBody(final Frame inertialFrame, final Frame bodyFrame,
-                                       final PVCoordinatesProvider pvProvider,
-                                       final AttitudeProvider aProvider) {
+    public SpacecraftToObservedBody(final Frame inertialFrame, final Frame bodyFrame,
+                                    final PVCoordinatesProvider pvProvider,
+                                    final AttitudeProvider aProvider) {
         this.inertialFrame = inertialFrame;
         this.bodyFrame     = bodyFrame;
         this.pvProvider    = pvProvider;
diff --git a/src/test/java/org/orekit/rugged/core/RuggedImplTest.java b/src/test/java/org/orekit/rugged/api/RuggedTest.java
similarity index 99%
rename from src/test/java/org/orekit/rugged/core/RuggedImplTest.java
rename to src/test/java/org/orekit/rugged/api/RuggedTest.java
index 33cfa2f2d33d1bbfafbe17539bc2479da9818aab..3132e1cb5b7b89263ba2d2c8b1d90df09579c7fa 100644
--- a/src/test/java/org/orekit/rugged/core/RuggedImplTest.java
+++ b/src/test/java/org/orekit/rugged/api/RuggedTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.orekit.rugged.core;
+package org.orekit.rugged.api;
 
 
 import java.io.File;
@@ -77,7 +77,7 @@ import org.orekit.utils.Constants;
 import org.orekit.utils.IERSConventions;
 import org.orekit.utils.PVCoordinates;
 
-public class RuggedImplTest {
+public class RuggedTest {
 
     @Rule
     public TemporaryFolder tempFolder = new TemporaryFolder();
@@ -138,7 +138,7 @@ public class RuggedImplTest {
             createQ(t0,19.000, 0.522119033749, -0.395304129256,  0.577847874330,  0.487050504694),
             createQ(t0,20.000, 0.522421006719, -0.395049578765,  0.577574493570,  0.487257453954));
 
-        RuggedImpl rugged = new RuggedImpl();
+        Rugged rugged = new Rugged();
         rugged.setGeneralContext(t0,
                                  AlgorithmId.DUVENHAGE,
                                  EllipsoidId.WGS84,
@@ -162,7 +162,7 @@ public class RuggedImplTest {
         Orbit      orbit                                  = createOrbit(gravityField);
         Propagator propagator                             = createPropagator(earth, gravityField, orbit);
 
-        RuggedImpl rugged = new RuggedImpl();
+        Rugged rugged = new Rugged();
         rugged.setGeneralContext(propagator.getInitialState().getDate(),
                                  AlgorithmId.DUVENHAGE,
                                  EllipsoidId.WGS84,
@@ -213,7 +213,7 @@ public class RuggedImplTest {
         propagator.propagate(crossing.shiftedBy(lineDatation.getDate(lastLine) + 1.0));
         Propagator ephemeris = propagator.getGeneratedEphemeris();
 
-        RuggedImpl rugged = new RuggedImpl();
+        Rugged rugged = new Rugged();
         rugged.setGeneralContext(crossing,
                                  AlgorithmId.DUVENHAGE,
                                  EllipsoidId.WGS84,