diff --git a/Documentation/Examples/CMakeLists.txt b/Documentation/Examples/CMakeLists.txt
index 5a5bd65531b948d1b71536e83f7f88a905c6f4fa..cd7e5251321adc682422d25ecfe992a2d0b63b40 100644
--- a/Documentation/Examples/CMakeLists.txt
+++ b/Documentation/Examples/CMakeLists.txt
@@ -28,6 +28,7 @@ target_link_libraries (cascade_example SuperStupidStack CORSIKAunits CORSIKAlogg
    ProcessSibyll
   CORSIKAcascade
   ProcessStackInspector
+  ProcessTrackWriter
   CORSIKAprocesses
   CORSIKAparticles
   CORSIKAgeometry
diff --git a/Documentation/Examples/cascade_example.cc b/Documentation/Examples/cascade_example.cc
index e8377f80c39576f17323dd284f67fac4be6acf5e..f3ebb57d541bf585312f47083d92b745bf82fc36 100644
--- a/Documentation/Examples/cascade_example.cc
+++ b/Documentation/Examples/cascade_example.cc
@@ -25,6 +25,8 @@
 #include <corsika/process/sibyll/Decay.h>
 #include <corsika/process/sibyll/Interaction.h>
 
+#include <corsika/process/track_writer/TrackWriter.h>
+
 #include <corsika/units/PhysicalUnits.h>
 
 #include <corsika/random/RNGManager.h>
@@ -192,7 +194,6 @@ public:
 // The example main program for a particle cascade
 //
 int main() {
-
   // initialize random number sequence(s)
   corsika::random::RNGManager::GetInstance().RegisterRandomStream("cascade");
 
@@ -222,9 +223,10 @@ int main() {
   corsika::process::sibyll::Interaction sibyll;
   corsika::process::sibyll::Decay decay;
   ProcessCut cut(8_GeV);
+  corsika::process::TrackWriter::TrackWriter trackWriter("tracks.dat");
 
   // assemble all processes into an ordered process list
-  auto sequence = p0 << sibyll << decay << cut;
+  auto sequence = p0 << sibyll << decay << cut << trackWriter;
 
   // cout << "decltype(sequence)=" << type_id_with_cvr<decltype(sequence)>().pretty_name()
   // << "\n";
@@ -232,7 +234,7 @@ int main() {
   // setup particle stack, and add primary particle
   setup::Stack stack;
   stack.Clear();
-  const hep::EnergyType E0 = 1_TeV;
+  const hep::EnergyType E0 = 10_TeV;
   {
     auto particle = stack.NewParticle();
     particle.SetPID(Code::Proton);
@@ -241,7 +243,7 @@ int main() {
     particle.SetEnergy(E0);
     particle.SetMomentum(plab);
     particle.SetTime(0_ns);
-    Point p(rootCS, 0_m, 0_m, 10_km);
+    Point p(rootCS, 0_m, 0_m, 0_m);
     particle.SetPosition(p);
     cout << particle.GetEnergy() / 1_GeV << endl;
   }
diff --git a/Framework/Cascade/Cascade.h b/Framework/Cascade/Cascade.h
index 973c614391299349c0081e50fbb4b19831d1b69f..6aed37d644b868c1ed92d5ce87475db45245225f 100644
--- a/Framework/Cascade/Cascade.h
+++ b/Framework/Cascade/Cascade.h
@@ -57,13 +57,7 @@ namespace corsika::cascade {
     }
 
     void Step(Particle& particle) {
-
       using namespace corsika::units::si;
-      using std::cout;
-      using std::endl;
-      using std::log;
-
-      cout << particle.GetEnergy() / 1_GeV << endl;
 
       // determine geometric tracking
       corsika::setup::Trajectory step = fTracking.GetTrack(particle);
@@ -80,11 +74,15 @@ namespace corsika::cascade {
                 << ", next_interact=" << next_interact << std::endl;
 
       // convert next_step from grammage to length
+      auto const* currentNode =
+          fEnvironment.GetUniverse()->GetContainingNode(particle.GetPosition());
+
+      if (currentNode == &*fEnvironment.GetUniverse()) {
+        throw std::runtime_error("particle entered void universe");
+      }
+
       LengthType const distance_interact =
-          fEnvironment.GetUniverse()
-              ->GetContainingNode(particle.GetPosition())
-              ->GetModelProperties()
-              .ArclengthFromGrammage(step, next_interact);
+          currentNode->GetModelProperties().ArclengthFromGrammage(step, next_interact);
 
       // determine the maximum geometric step length
       LengthType const distance_max = fProcessSequence.MaxStepLength(particle, step);
@@ -116,6 +114,8 @@ namespace corsika::cascade {
       particle.SetPosition(step.PositionFromArclength(min_distance));
       // .... also update time, momentum, direction, ...
 
+      step.LimitEndTo(min_distance);
+
       // apply all continuous processes on particle + track
       corsika::process::EProcessReturn status =
           fProcessSequence.DoContinuous(particle, step, fStack);
@@ -127,11 +127,11 @@ namespace corsika::cascade {
         return;
       }
 
-      std::cout << "sth. happening before geometric limit ?"
+      std::cout << "sth. happening before geometric limit ? "
                 << ((min_distance < distance_max) ? "yes" : "no") << std::endl;
 
       if (min_distance < distance_max) { // interaction to happen within geometric limit
-        // check weather decay or interaction limits this step
+        // check whether decay or interaction limits this step
 
         if (min_distance == distance_interact) {
           std::cout << "collide" << std::endl;
diff --git a/Framework/Geometry/Trajectory.h b/Framework/Geometry/Trajectory.h
index 5a9d1ed56a1e1232bb0591f2646a4f4dad1f6f85..bcaa45fba325367048a2a7b93c6ddb0752428300 100644
--- a/Framework/Geometry/Trajectory.h
+++ b/Framework/Geometry/Trajectory.h
@@ -43,6 +43,10 @@ namespace corsika::geometry {
       assert(t >= 0 * corsika::units::si::second);
       return T::ArcLength(0, t);
     }
+
+    void LimitEndTo(corsika::units::si::LengthType limit) {
+      fTimeLength = T::TimeFromArclength(limit);
+    }
   };
 
 } // namespace corsika::geometry
diff --git a/Processes/CMakeLists.txt b/Processes/CMakeLists.txt
index 41a8277d9c736f387d68b255ff2af7415133c10c..d6b90e70ec3cc7d1efb5266626d41f50624cb019 100644
--- a/Processes/CMakeLists.txt
+++ b/Processes/CMakeLists.txt
@@ -2,6 +2,7 @@
 add_subdirectory (NullModel)
 add_subdirectory (Sibyll)
 add_subdirectory (StackInspector)
+add_subdirectory (TrackWriter)
 add_subdirectory (TrackingLine)
 
 #add_custom_target(CORSIKAprocesses)
diff --git a/Processes/Sibyll/Interaction.h b/Processes/Sibyll/Interaction.h
index 101fbdd7b53aae2c4acbb1ebc071796449a6d299..2b03b26009c1d6f0e4f9c9be75d9452d2715124f 100644
--- a/Processes/Sibyll/Interaction.h
+++ b/Processes/Sibyll/Interaction.h
@@ -75,8 +75,9 @@ namespace corsika::process::sibyll {
       // FOR NOW: assume target is oxygen
       const int kTarget = corsika::particles::Oxygen::GetNucleusA();
 
-      hep::EnergyType Etot =
-          p.GetEnergy() + kTarget * corsika::particles::Proton::GetMass();
+      const hep::MassType nucleon_mass = 0.5 * (corsika::particles::Proton::GetMass() +
+                                                corsika::particles::Neutron::GetMass());
+      hep::EnergyType Etot = p.GetEnergy() + nucleon_mass;
       MomentumVector Ptot(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
       // FOR NOW: assume target is at rest
       MomentumVector pTarget(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
@@ -143,6 +144,18 @@ namespace corsika::process::sibyll {
 
         Point pOrig = p.GetPosition();
         TimeType tOrig = p.GetTime();
+        // sibyll CS has z along particle momentum
+        // FOR NOW: hard coded z-axis for corsika frame
+        QuantityVector<length_d> const zAxis{0_m, 0_m, 1_m};
+        QuantityVector<length_d> const xAxis{1_m, 0_m, 0_m};
+        [[maybe_unused]] auto pt = [](MomentumVector p) {
+          return sqrt(p.GetComponents()[0] * p.GetComponents()[0] +
+                      p.GetComponents()[1] * p.GetComponents()[1]);
+        };
+        double theta = acos(p.GetMomentum().GetComponents()[2] / p.GetMomentum().norm());
+        cout << "ProcessSibyll: polar angle between sibyllCS and rootCS: " << theta
+             << endl;
+        CoordinateSystem sibyllCS = rootCS.rotate(xAxis, theta);
 
         /*
            the target should be defined by the Environment,
@@ -157,11 +170,16 @@ namespace corsika::process::sibyll {
             GetNucleusA(); // env.GetTargetParticle().GetPID();
 
         // FOR NOW: target is always at rest
-        const EnergyType Etarget = 0_GeV + corsika::particles::Proton::GetMass();
+        const hep::MassType nucleon_mass = 0.5 * (corsika::particles::Proton::GetMass() +
+                                                  corsika::particles::Neutron::GetMass());
+        const EnergyType Etarget = 0_GeV + nucleon_mass;
         const auto pTarget = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV);
         cout << "target momentum (GeV/c): " << pTarget.GetComponents() / 1_GeV << endl;
         cout << "beam momentum (GeV/c): " << p.GetMomentum().GetComponents() / 1_GeV
              << endl;
+        cout << "beam momentum in sibyll frame (GeV/c): "
+             << p.GetMomentum().GetComponents(sibyllCS) / 1_GeV << endl;
+
         cout << "position of interaction: " << pOrig.GetCoordinates() << endl;
         cout << "time: " << tOrig << endl;
 
@@ -220,23 +238,31 @@ namespace corsika::process::sibyll {
 
           // add particles from sibyll to stack
           // link to sibyll stack
+          // here we need to pass projectile momentum and energy to define the local
+          // sibyll frame and the boosts to the lab. frame
           SibStack ss;
 
-          // SibStack does not know about momentum yet so we need counter to access
           // momentum array in Sibyll
-          int i = -1;
           MomentumVector Plab_final(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
           EnergyType E_final = 0_GeV, Ecm_final = 0_GeV;
           for (auto& psib : ss) {
-            ++i;
+
             // skip particles that have decayed in Sibyll
-            if (abs(s_plist_.llist[i]) > 100) continue;
+            if (psib.HasDecayed()) continue;
 
             // transform energy to lab. frame, primitve
             // compute beta_vec * p_vec
             // arbitrary Lorentz transformation based on sibyll routines
             const auto gammaBetaComponents = gambet.GetComponents();
-            const auto pSibyllComponents = psib.GetMomentum().GetComponents();
+            // FOR NOW: fill vector in sibCS and then rotate into rootCS
+            // can be done in SibStack by passing sibCS
+            // get momentum vector in sibyllCS
+            const auto pSibyllComponentsSibCS = psib.GetMomentum().GetComponents();
+            // temporary vector in sibyllCS
+            auto SibVector = MomentumVector(sibyllCS, pSibyllComponentsSibCS);
+            // rotatate to rootCS
+            const auto pSibyllComponents = SibVector.GetComponents(rootCS);
+            // boost to lab. frame
             hep::EnergyType en_lab = 0. * 1_GeV;
             hep::MomentumType p_lab_components[3];
             en_lab = psib.GetEnergy() * gamma;
@@ -255,7 +281,6 @@ namespace corsika::process::sibyll {
             auto pnew = s.NewParticle();
             pnew.SetEnergy(en_lab);
             pnew.SetPID(process::sibyll::ConvertFromSibyll(psib.GetPID()));
-
             corsika::geometry::QuantityVector<energy_hep_d> p_lab_c{
                 p_lab_components[0], p_lab_components[1], p_lab_components[2]};
             pnew.SetMomentum(MomentumVector(rootCS, p_lab_c));
diff --git a/Processes/Sibyll/SibStack.h b/Processes/Sibyll/SibStack.h
index 1e44931b2cc79ea64233b3643f5ef64163d33a83..3364e7deac87ca2fda683d188aeaf3495eaa1fb4 100644
--- a/Processes/Sibyll/SibStack.h
+++ b/Processes/Sibyll/SibStack.h
@@ -31,7 +31,7 @@ namespace corsika::process::sibyll {
     void Clear() { s_plist_.np = 0; }
 
     int GetSize() const { return s_plist_.np; }
-#warning check actual capacity of sibyll stack
+
     int GetCapacity() const { return 8000; }
 
     void SetId(const int i, const int v) { s_plist_.llist[i] = v; }
@@ -91,6 +91,9 @@ namespace corsika::process::sibyll {
     corsika::units::hep::EnergyType GetEnergy() const {
       return GetStackData().GetEnergy(GetIndex());
     }
+    bool HasDecayed() const {
+      return abs(GetStackData().GetId(GetIndex())) > 100 ? true : false;
+    }
     void SetPID(const int v) { GetStackData().SetId(GetIndex(), v); }
     corsika::process::sibyll::SibyllCode GetPID() const {
       return static_cast<corsika::process::sibyll::SibyllCode>(
diff --git a/Processes/TrackWriter/CMakeLists.txt b/Processes/TrackWriter/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e9c73d68b7f0f072e07c0e007b0dcd993adbf0ca
--- /dev/null
+++ b/Processes/TrackWriter/CMakeLists.txt
@@ -0,0 +1,65 @@
+
+set (
+  MODEL_SOURCES
+  TrackWriter.cc
+  )
+
+set (
+  MODEL_HEADERS
+  TrackWriter.h
+  )
+
+set (
+  MODEL_NAMESPACE
+  corsika/process/track_writer
+  )
+
+add_library (ProcessTrackWriter STATIC ${MODEL_SOURCES})
+CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessTrackWriter ${MODEL_NAMESPACE} ${MODEL_HEADERS})
+
+set_target_properties (
+  ProcessTrackWriter
+  PROPERTIES
+  VERSION ${PROJECT_VERSION}
+  SOVERSION 1
+#  PUBLIC_HEADER "${MODEL_HEADERS}"
+  )
+
+# target dependencies on other libraries (also the header onlys)
+target_link_libraries (
+  ProcessTrackWriter
+  CORSIKAunits
+  CORSIKAparticles
+  CORSIKAgeometry
+  CORSIKAsetup
+  )
+
+target_include_directories (
+  ProcessTrackWriter 
+  INTERFACE 
+  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
+  $<INSTALL_INTERFACE:include/include>
+  )
+
+install (
+  TARGETS ProcessTrackWriter
+  LIBRARY DESTINATION lib
+  ARCHIVE DESTINATION lib
+#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
+  )
+
+
+# --------------------
+# code unit testing
+#add_executable (testNullModel testNullModel.cc)
+
+#target_link_libraries (
+#  testNullModel  ProcessNullModel
+#  CORSIKAsetup
+#  CORSIKAgeometry
+#  CORSIKAunits
+#  CORSIKAthirdparty # for catch2
+#  )
+
+#add_test (NAME testNullModel COMMAND testNullModel)
+
diff --git a/Processes/TrackWriter/TrackWriter.cc b/Processes/TrackWriter/TrackWriter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ffc67cde8a478486cca12670a7e0bb7838d130fa
--- /dev/null
+++ b/Processes/TrackWriter/TrackWriter.cc
@@ -0,0 +1,20 @@
+/**
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * See file AUTHORS for a list of contributors.
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#include <corsika/process/track_writer/TrackWriter.h>
+#include <string>
+
+void corsika::process::TrackWriter::TrackWriter::Init() {
+  using namespace std::string_literals;
+
+  fFile.open(fFilename);
+  fFile << "# PID, E / eV, start coordinates / m, displacement vector to end / m "s
+        << '\n';
+}
diff --git a/Processes/TrackWriter/TrackWriter.h b/Processes/TrackWriter/TrackWriter.h
new file mode 100644
index 0000000000000000000000000000000000000000..10f2d574f355f72f46e9d681adcd853dac436c4c
--- /dev/null
+++ b/Processes/TrackWriter/TrackWriter.h
@@ -0,0 +1,58 @@
+/**
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * See file AUTHORS for a list of contributors.
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#ifndef _Processes_TrackWriter_h_
+#define _Processes_TrackWriter_h_
+
+#include <corsika/particles/ParticleProperties.h>
+#include <corsika/process/ContinuousProcess.h>
+#include <corsika/setup/SetupTrajectory.h>
+#include <corsika/units/PhysicalUnits.h>
+#include <fstream>
+#include <limits>
+#include <string>
+
+namespace corsika::process::TrackWriter {
+
+  class TrackWriter : public corsika::process::ContinuousProcess<TrackWriter> {
+
+  public:
+    TrackWriter(std::string const& filename)
+        : fFilename(filename) {}
+
+    void Init();
+
+    template <typename Particle, typename Track, typename Stack>
+    corsika::process::EProcessReturn DoContinuous(Particle& p, Track& t, Stack&) {
+      using namespace corsika::units::si;
+      auto const start = t.GetPosition(0).GetCoordinates();
+      auto const delta = t.GetPosition(1).GetCoordinates() - start;
+      auto const& name = corsika::particles::GetName(p.GetPID());
+
+      fFile << name << "    " << p.GetEnergy() / 1_eV << ' ' << start[0] / 1_m << ' '
+            << start[1] / 1_m << ' ' << start[2] / 1_m << "   " << delta[0] / 1_m << ' '
+            << delta[1] / 1_m << ' ' << delta[2] / 1_m << '\n';
+
+      return corsika::process::EProcessReturn::eOk;
+    }
+
+    template <typename Particle, typename Track>
+    corsika::units::si::LengthType MaxStepLength(Particle&, Track&) {
+      return corsika::units::si::meter * std::numeric_limits<double>::infinity();
+    }
+
+  private:
+    std::string const fFilename;
+    std::ofstream fFile;
+  };
+
+} // namespace corsika::process::TrackWriter
+
+#endif
diff --git a/Processes/TrackingLine/TrackingLine.h b/Processes/TrackingLine/TrackingLine.h
index c64522c40349f873f45693f19e37f978972cc13d..31966195751d0a1dfaee6d61a60421a0f80d4676 100644
--- a/Processes/TrackingLine/TrackingLine.h
+++ b/Processes/TrackingLine/TrackingLine.h
@@ -84,7 +84,8 @@ namespace corsika::process {
             p.GetMomentum() / p.GetEnergy() * corsika::units::constants::c;
 
         auto const currentPosition = p.GetPosition();
-
+        std::cout << "TrackingLine pid: " << p.GetPID()
+                  << " , E = " << p.GetEnergy() / 1_GeV << " GeV" << std::endl;
         std::cout << "TrackingLine pos: " << currentPosition.GetCoordinates()
                   << std::endl;
         std::cout << "TrackingLine   E: " << p.GetEnergy() / 1_GeV << " GeV" << std::endl;
diff --git a/Processes/TrackingLine/testTrackingLine.cc b/Processes/TrackingLine/testTrackingLine.cc
index 737b536f2fad7ddae2403870bd94b9e3cab5c7a5..285f275c0ae53ab2b6b128be270860819326cac6 100644
--- a/Processes/TrackingLine/testTrackingLine.cc
+++ b/Processes/TrackingLine/testTrackingLine.cc
@@ -9,6 +9,7 @@
  */
 
 #include <corsika/environment/Environment.h>
+#include <corsika/particles/ParticleProperties.h>
 
 #include <corsika/process/tracking_line/TrackingLine.h>
 
@@ -47,6 +48,7 @@ struct DummyParticle {
   auto GetEnergy() const { return fEnergy; }
   auto GetMomentum() const { return fMomentum; }
   auto GetPosition() const { return fPosition; }
+  auto GetPID() const { return corsika::particles::Code::Unknown; }
 };
 
 struct DummyStack {
diff --git a/Tools/plot_tracks.sh b/Tools/plot_tracks.sh
new file mode 100755
index 0000000000000000000000000000000000000000..135b410dc8d80523c8a5f340bf3c26634e35bb7f
--- /dev/null
+++ b/Tools/plot_tracks.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+#
+# See file AUTHORS for a list of contributors.
+#
+# This software is distributed under the terms of the GNU General Public
+# Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+# the license.
+
+# with this script you can plot an animation of output of TrackWriter
+
+track_dat=$1
+if [ -z "$track_dat" ]; then
+  echo "usage: $0 <track.dat> [output.gif]" >&2
+  exit 1
+fi
+
+output=$2
+if [ -z "$output" ]; then
+  output="$track_dat.gif"
+fi
+
+cat <<EOF | gnuplot
+set term gif animate size 600,600
+set output "$output"
+
+set xlabel "x / m"
+set ylabel "y / m"
+set zlabel "z / m"
+set title "CORSIKA 8 preliminary"
+
+do for [t=0:360:1] {
+	set view 60, t
+        splot "$track_dat" u 3:4:5:6:7:8 w vectors nohead t ""
+}
+EOF
+
+exit $?