diff --git a/corsika/detail/framework/geometry/LeapFrogTrajectory.inl b/corsika/detail/framework/geometry/LeapFrogTrajectory.inl
index 9ff574321434aa6c7c495ea3fc8db432e4a47879..fd0e29d44532fa19ad5e71569b9bbceee41acfbc 100644
--- a/corsika/detail/framework/geometry/LeapFrogTrajectory.inl
+++ b/corsika/detail/framework/geometry/LeapFrogTrajectory.inl
@@ -35,7 +35,9 @@ namespace corsika {
   }
 
   inline VelocityVector LeapFrogTrajectory::getVelocity(double const u) const {
-    return initialVelocity_ + initialVelocity_.cross(magneticfield_) * timeStep_ * u * k_;
+    return (initialDirection_ +
+            initialDirection_.cross(magneticfield_) * timeStep_ * u * k_) *
+           initialVelocity_.getNorm();
   }
 
   inline DirectionVector LeapFrogTrajectory::getDirection(double const u) const {
@@ -44,18 +46,23 @@ namespace corsika {
 
   inline TimeType LeapFrogTrajectory::getDuration(double const u) const {
     return u * timeStep_ *
-           (double(getVelocity(u).getNorm() / initialVelocity_.getNorm()) + 1.0) / 2;
+           (1. + fabs(0.5 * initialDirection_.cross(magneticfield_).getNorm() * u *
+                      timeStep_ * k_));
   }
 
   inline LengthType LeapFrogTrajectory::getLength(double const u) const {
-    return timeStep_ * initialVelocity_.getNorm() * u;
+    return getDuration(u) * initialVelocity_.getNorm();
   }
 
   inline void LeapFrogTrajectory::setLength(LengthType const limit) {
-    if (initialVelocity_.getNorm() == 0_m / 1_s) setDuration(0_s);
+    if (initialVelocity_.getNorm() == SpeedType::zero()) setDuration(0_s);
     setDuration(limit / initialVelocity_.getNorm());
   }
 
-  inline void LeapFrogTrajectory::setDuration(TimeType const limit) { timeStep_ = limit; }
+  inline void LeapFrogTrajectory::setDuration(TimeType const limit) {
+    double const correction =
+        (1. + fabs(0.5 * initialDirection_.cross(magneticfield_).getNorm() * limit * k_));
+    timeStep_ = limit / correction;
+  }
 
 } // namespace corsika
diff --git a/corsika/detail/framework/utility/CubicSolver.inl b/corsika/detail/framework/utility/CubicSolver.inl
index edfce000082c34a6645c459325f8b45f0a6fcb4e..a48a0da0c64d7c8836ee4761d7788b5410b2b71d 100644
--- a/corsika/detail/framework/utility/CubicSolver.inl
+++ b/corsika/detail/framework/utility/CubicSolver.inl
@@ -288,8 +288,8 @@ namespace corsika {
 
       CORSIKA_LOG_TRACE("niter={}", niter);
       if (niter >= maxiter) {
-        CORSIKA_LOG_TRACE("failure, no solution");
-        //        return std::vector<double>{};
+        // CORSIKA_LOG_TRACE("failure, no solution");
+        // return std::vector<double>{};
       }
     }
 
diff --git a/corsika/detail/modules/ObservationPlane.inl b/corsika/detail/modules/ObservationPlane.inl
index 967b9314198af14e7eb76b9e99d654d60c00c573..2cb39341246d5f6b8bf3fab96515d27fa75516f1 100644
--- a/corsika/detail/modules/ObservationPlane.inl
+++ b/corsika/detail/modules/ObservationPlane.inl
@@ -63,8 +63,9 @@ namespace corsika {
   inline LengthType ObservationPlane<TTracking, TOutput>::getMaxStepLength(
       TParticle const& particle, TTrajectory const& trajectory) {
 
-    CORSIKA_LOG_TRACE("particle={}, pos={}, dir={}, plane={}", particle.asString(),
-                      particle.getPosition(), particle.getDirection(), plane_.asString());
+    CORSIKA_LOG_TRACE("getMaxStepLength, particle={}, pos={}, dir={}, plane={}",
+                      particle.asString(), particle.getPosition(),
+                      particle.getDirection(), plane_.asString());
 
     auto const intersection = TTracking::intersect(particle, plane_);
 
diff --git a/corsika/detail/modules/energy_loss/BetheBlochPDG.inl b/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
index a2f643cc58d5f72f77e64579157e9a351aa411ed..7772f26609fdd65a3a1216235f4788ba5eda31a8 100644
--- a/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
+++ b/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
@@ -73,7 +73,7 @@ namespace corsika {
     // Sternheimer parameterization, density corrections towards high energies
     // NOTE/TODO: when Cbar is 0 it needs to be approximated from parameterization ->
     // MISSING
-    CORSIKA_LOG_TRACE("BetheBloch p.getMomentum().getNorm()/m{}=",
+    CORSIKA_LOG_TRACE("BetheBloch p.getMomentum().getNorm()/m={}",
                       p.getMomentum().getNorm() / m);
     double const x = log10(p.getMomentum().getNorm() / m);
     double delta = 0;
diff --git a/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl b/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
index 749693bc7c7a327732ea30d77872006f63eff5da..009be7c63610710d1ab7c1d0113518ff38dc7b68 100644
--- a/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
+++ b/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
@@ -94,6 +94,8 @@ namespace corsika {
            particle.getMomentum().getParallelProjectionOnto(magneticfield))
               .getNorm();
 
+      CORSIKA_LOG_TRACE("p_perp={} eV", p_perp / 1_eV);
+
       if (p_perp < 1_eV) {
         // particle travel along, parallel to magnetic field. Rg is
         // "0", but for purpose of step limit we return infinity here.
@@ -121,12 +123,17 @@ namespace corsika {
       decltype(1 / (tesla * second)) const k =
           charge / p_norm *
           initialVelocity.getNorm(); // since we use steps in time and not length
-      // units: C * s / m / kg * m/s = 1 / (T*m) * m/s = 1/(T s)
+      // units: C * s / m / kg * m/s = 1 / (T*m) * m/s = 1 / (T*s)
+
+      // correction factor to move from leap-frog length L to half-step deltaL/2
+      double const correction =
+          1. + fabs(0.5 * particle.getDirection().cross(magneticfield).getNorm() * k *
+                    minTime);
 
       return std::make_tuple(
           LeapFrogTrajectory(position, initialVelocity, magneticfield, k,
-                             minTime), // trajectory
-          minNode);                    // next volume node
+                             minTime / correction), // --> trajectory
+          minNode);                                 // --> next volume node
     }
 
     template <typename TParticle>
@@ -164,7 +171,7 @@ namespace corsika {
           constants::c * convert_HEP_to_SI<MassType::dimension_type>(
                              particle.getMomentum().getNorm()); // km * m /s
       // this is: k = q/|p|
-      auto const k = charge / p_norm;
+      decltype(1 / (tesla * meter)) const k = charge / p_norm;
 
       MagneticFieldVector const direction_x_B = directionBefore.cross(magneticfield);
       auto const denom = 4. / (direction_x_B.getSquaredNorm() * k * k);
@@ -176,6 +183,10 @@ namespace corsika {
                        (deltaPosLength - sphere.getRadius()) * denom /
                        (1_m * 1_m * 1_m * 1_m);
       CORSIKA_LOG_TRACE("denom={}, b={}, c={}, d={}", denom, b, c, d);
+      // solutions of deltaL are obtained from quartic equation. Note, deltaL/2 is the
+      // length of each half step, however, the second half step is slightly longer
+      // because of the non-conservation of norm/velocity.
+      // The leap-frog length L is deltaL/2 * (1+|u_{n+1}|)
       std::vector<double> solutions = solve_quartic_real(1, 0, b, c, d);
       if (!solutions.size()) {
         return tracking_line::Tracking::intersect<TParticle>(particle, sphere);
@@ -184,7 +195,11 @@ namespace corsika {
       int first = 0, first_entry = 0, first_exit = 0;
       for (auto solution : solutions) {
         LengthType const dist = solution * 1_m;
-        CORSIKA_LOG_TRACE("Solution (real) for current Volume: {} ", dist);
+        CORSIKA_LOG_TRACE(
+            "Solution (real) for current Volume: deltaL/2*2={} (deltaL/2*2/v={}, "
+            "corrected={}) ",
+            dist, dist / absVelocity,
+            dist / absVelocity * (1. + fabs(0.5 * direction_x_B.getNorm() * k * dist)));
         if (numericallyInside) {
           // there must be an entry (negative) and exit (positive) solution
           if (dist < 0.0001_m) { // security margin to assure
@@ -242,7 +257,12 @@ namespace corsika {
             first_entry, first_exit);
         return Intersections();
       }
-      return Intersections(d_enter / absVelocity, d_exit / absVelocity);
+      // return in units of time, correct to leap-frog length L
+      double const corr_enter = 1. + fabs(0.5 * direction_x_B.getNorm() * k * d_enter);
+      double const corr_exit = 1. + fabs(0.5 * direction_x_B.getNorm() * k * d_exit);
+
+      return Intersections(d_enter * corr_enter / absVelocity,
+                           d_exit * corr_exit / absVelocity);
     }
 
     template <typename TParticle>
@@ -253,7 +273,7 @@ namespace corsika {
 
       ElectricChargeType const charge = particle.getCharge();
 
-      if (charge != 0 * constants::e) {
+      if (charge != ElectricChargeType::zero()) {
 
         auto const* currentLogicalVolumeNode = particle.getNode();
         VelocityVector const velocity = particle.getVelocity();
@@ -284,6 +304,10 @@ namespace corsika {
              plane.getNormal().dot(position - plane.getCenter())) // unit: kg*m/s *m
             / (1_m * 1_m * 1_kg) * 1_s;
 
+        // deltaL from quadratic solution return half-step length deltaL/2 for leap-frog
+        // algorithmus. Note, the leap-frog length L is longer by (1+|u_{n_1}|)/2 because
+        // the direction norm of the second half step is >1.
+
         std::vector<double> const deltaLs = solve_quadratic_real(denom, p, q);
 
         CORSIKA_LOG_TRACE("deltaLs=[{}]", fmt::join(deltaLs, ", "));
@@ -310,17 +334,14 @@ namespace corsika {
           return Intersections(std::numeric_limits<double>::infinity() * 1_s);
         }
 
-        CORSIKA_LOG_TRACE("maxStepLength={}", maxStepLength);
-
-        // with final length correction
-        auto const corr =
-            (direction + direction_x_B * maxStepLength * charge / (p_norm * 2))
-                .getNorm() /
-            absVelocity; // unit: s/m
+        CORSIKA_LOG_TRACE("maxStepLength={} s", maxStepLength / 1_s);
 
-        return Intersections(maxStepLength * corr); // unit: s
+        // with final length correction, |direction| becomes >1 during step
+        double const corr =
+            1. + fabs(0.5 * direction_x_B.getNorm() * maxStepLength * charge / p_norm);
 
-      } // no charge
+        return Intersections(maxStepLength * corr / absVelocity); // unit: s
+      }                                                           // no charge
 
       CORSIKA_LOG_TRACE("(plane) straight tracking with  charge={}, B={}", charge,
                         particle.getNode()->getModelProperties().getMagneticField(
diff --git a/corsika/modules/tracking/TrackingLeapFrogCurved.hpp b/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
index 513ffbaf509d2c05a7020f038d0ab320592c4712..c4ed5aafd3883b1ff4f5ba859c8a418018fc50b3 100644
--- a/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
+++ b/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
@@ -33,8 +33,8 @@ namespace corsika {
      *
      * Performs one leap-frog step consistent of two halve-steps with steplength/2
      * The step is caluculated analytically precisely to reach to the next volume
-     *boundary.
-     **/
+     * boundary.
+     */
     template <typename TParticle>
     auto make_LeapFrogStep(TParticle const& particle, LengthType steplength);
 
@@ -59,16 +59,42 @@ namespace corsika {
       template <typename TParticle>
       auto getTrack(TParticle const& particle);
 
-      //! find intersection of Sphere with Track
+      /**
+       *  find intersection of Sphere with Track
+       *
+       * Returns intersection of particle assuming a curved leap-frog step, with a sphere.
+       * Entry and exit times are calculated, where the velocity is constant and the
+       * steplength is the geometric steplength of the leap-frog.
+       *
+       * @param particle Current particle state
+       * @param sphere Sphere object
+       */
       template <typename TParticle>
       static Intersections intersect(TParticle const& particle, Sphere const& sphere);
 
-      //! find intersection of Volume node with Track of particle
+      /**
+       *  find intersection of any Volume node with particle
+       *
+       * The intersection time(s) of a particle, assuming a curved leap-frog
+       * step, are calculated for any volume type.
+       *
+       */
       template <typename TParticle, typename TBaseNodeType>
       static Intersections intersect(TParticle const& particle,
                                      TBaseNodeType const& node);
 
-      //! find intersection of Plane with Track
+      /**
+       *  find intersection of Plane with Track
+       *
+       * Intersection times of particle are caculated with a plane, assuming a curved leap
+       * frog trajectory. The intersection time is assuming constant velocity (no change)
+       * along the geometric leap-frog step.
+       *
+       * @tparam TParticle Type of particle object on stack.
+       * @param particle Particle initial state.
+       * @param plane Plane.
+       * @return Intersections in time units.
+       */
       template <typename TParticle>
       static Intersections intersect(TParticle const& particle, Plane const& plane);
 
@@ -80,7 +106,6 @@ namespace corsika {
        * Use internally stored class tracking_line::Tracking to
        * perform a straight line tracking, if no magnetic bendig was
        * detected.
-       *
        */
       template <typename TParticle>
       auto getLinearTrajectory(TParticle& particle);
diff --git a/examples/hybrid_MC.cpp b/examples/hybrid_MC.cpp
index abafdf0c3ab305d008c0ef6d13cb3885e6d08810..f283679183598d43151fed5841ec2cc69581f516 100644
--- a/examples/hybrid_MC.cpp
+++ b/examples/hybrid_MC.cpp
@@ -202,8 +202,8 @@ int main(int argc, char** argv) {
             << std::endl;
 
   OutputManager output("hybrid_MC_outputs");
-  ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.02, env, true,
-                              1000};
+  ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.02, env,
+                              false, 1000};
 
   // setup processes, decays and interactions