diff --git a/examples/radio_shower.cpp b/examples/radio_shower.cpp
index af34300fa9cdd90fa0b83045de2e4fc973a72604..e85be350e5ec2c7876852e83a5c2a097f024054b 100644
--- a/examples/radio_shower.cpp
+++ b/examples/radio_shower.cpp
@@ -48,6 +48,7 @@
 #include <corsika/modules/Sibyll.hpp>
 #include <corsika/modules/UrQMD.hpp>
 #include <corsika/modules/PROPOSAL.hpp>
+#include <corsika/modules/QGSJetII.hpp>
 
 #include <corsika/modules/radio/RadioProcess.hpp>
 #include <corsika/modules/radio/CoREAS.hpp>
@@ -77,6 +78,7 @@
  */
 #include <corsika/modules/sibyll/Random.hpp>
 #include <corsika/modules/urqmd/Random.hpp>
+#include <corsika/modules/qgsjetII/Random.hpp>
 
 using namespace corsika;
 using namespace std;
@@ -104,6 +106,10 @@ UniformRefractiveIndex<MediumPropertyModel<UniformMagneticField<TInterface>>>;
 //template <typename T>
 //using MyExtraEnv = MediumPropertyModel<UniformMagneticField<T>>;
 
+// argv : 1.number of nucleons, 2.number of protons,
+//        3.total energy in GeV, 4.number of showers,
+//        5.seed (0 by default to generate random values for all)
+
 int main(int argc, char** argv) {
 
   corsika_logger->set_pattern("[%n:%^%-8l%$] %s:%#: %v");
@@ -111,15 +117,20 @@ int main(int argc, char** argv) {
 
   CORSIKA_LOG_INFO("Vertical Radio Shower");
 
-  if (argc < 4) {
-    std::cerr << "usage: vertical_EAS <A> <Z> <energy/GeV> [seed]" << std::endl;
-    std::cerr << "       if no seed is given, a random seed is chosen" << std::endl;
+  if (argc < 5) {
+    std::cerr << "usage: vertical_EAS <A> <Z> <energy/GeV> <Nevt> [seed] \n"
+                 "       if A=0, Z is interpreted as PDG code \n"
+                 "       if no seed is given, a random seed is chosen \n"
+              << std::endl;
     return 1;
   }
   feenableexcept(FE_INVALID);
 
   int seed = 0;
-  if (argc > 4) seed = std::stoi(std::string(argv[4]));
+  int number_showers = std::stoi(std::string(argv[4]));
+
+  if (argc > 5) { seed = std::stoi(std::string(argv[5])); }
+
   // initialize random number sequence(s)
   registerRandomStreams(seed);
 
@@ -129,21 +140,10 @@ int main(int argc, char** argv) {
 //  IRefractiveIndexModel<IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>>;
 //  using EnvType = Environment<EnvironmentInterface>;
 //  EnvType env;
-
   using EnvType = setup::Environment;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
-  // the antenna location
-  const auto point1{Point(env.getCoordinateSystem(), 50_m, 50_m, 50_m)};
-  const auto point2{Point(env.getCoordinateSystem(), 25_m, 25_m, 25_m)};
-  // the antennas
-  TimeDomainAntenna ant1("antenna1", point1, 0_s, 1_s, 1/1e-6_s);
-  TimeDomainAntenna ant2("antenna2", point2, 0_s, 1_s, 1/1e-6_s);
-  // the detector
-  AntennaCollection<TimeDomainAntenna> detector;
-  detector.addAntenna(ant1);
-  detector.addAntenna(ant2);
   auto builder = make_layered_spherical_atmosphere_builder<
       setup::EnvironmentInterface, MyExtraEnv>::create(center,
                                                        constants::EarthRadius::Mean, 1.000327,
@@ -166,60 +166,98 @@ int main(int argc, char** argv) {
   builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km);
   builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km);
   builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km);
-  builder.addLinearLayer(1e9_cm, 112.8_km);
+  builder.addLinearLayer(1e9_cm, 112.8_km + constants::EarthRadius::Mean);
   builder.assemble(env);
 
-  // setup particle stack, and add primary particle
-  setup::Stack stack;
-  stack.clear();
-  const Code beamCode = Code::Nucleus;
+  CORSIKA_LOG_DEBUG(
+      "environment setup: universe={}, layer1={}, layer2={}, layer3={}, layer4={}, "
+      "layer5={}",
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 130_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 110_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 50_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 20_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 5_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 2_km, 0_m, 0_m}))));
+
+  // the antenna locations
+  const auto point1{Point(rootCS, 100_m, 100_m, 0_m)};
+  const auto point2{Point(rootCS, 100_m, -100_m, 0_m)};
+  const auto point3{Point(rootCS, -100_m, -100_m, 0_m)};
+  const auto point4{Point(rootCS, -100_m, 100_m, 0_m)};
+
+  // the antenna time variables
+  const TimeType t1{0_s};
+  const TimeType t2{1e-6_s};
+  const InverseTimeType t3{1e+9_Hz};
+
+  // the antennas
+  TimeDomainAntenna ant1("antenna 1", point1, t1, t2, t3);
+  TimeDomainAntenna ant2("antenna 2", point2, t1, t2, t3);
+  TimeDomainAntenna ant3("antenna 3", point3, t1, t2, t3);
+  TimeDomainAntenna ant4("antenna 4", point4, t1, t2, t3);
+
+  // the detector (aka antenna collection)
+  AntennaCollection<TimeDomainAntenna> detector;
+  detector.addAntenna(ant1);
+  detector.addAntenna(ant2);
+  detector.addAntenna(ant3);
+  detector.addAntenna(ant4);
+
+  // pre-setup particle stack
   unsigned short const A = std::stoi(std::string(argv[1]));
-  unsigned short Z = std::stoi(std::string(argv[2]));
-  auto const mass = get_nucleus_mass(A, Z);
-  const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[3]));
-  double theta = 0.;
+  Code beamCode;
+  HEPEnergyType mass;
+  unsigned short Z = 0;
+  if (A > 0) {
+    beamCode = Code::Nucleus;
+    Z = std::stoi(std::string(argv[2]));
+    mass = get_nucleus_mass(A, Z);
+  } else {
+    int pdg = std::stoi(std::string(argv[2]));
+    beamCode = convert_from_PDG(PDGCode(pdg));
+    mass = get_mass(beamCode);
+  }
+  HEPEnergyType const E0 = 1_GeV * std::stof(std::string(argv[3]));
+  double theta = 20.;
+  double phi = 180.;
   auto const thetaRad = theta / 180. * M_PI;
+  auto const phiRad = phi / 180. * M_PI;
 
   auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
     return sqrt((Elab - m) * (Elab + m));
   };
   HEPMomentumType P0 = elab2plab(E0, mass);
-  auto momentumComponents = [](double thetaRad, HEPMomentumType ptot) {
-    return std::make_tuple(ptot * sin(thetaRad), 0_eV, -ptot * cos(thetaRad));
+  auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
+    return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
+                           -ptot * cos(theta));
   };
-
-  auto const [px, py, pz] = momentumComponents(thetaRad, P0);
+  auto const [px, py, pz] = momentumComponents(thetaRad, phiRad, P0);
   auto plab = MomentumVector(rootCS, {px, py, pz});
   cout << "input particle: " << beamCode << endl;
-  cout << "input angles: theta=" << theta << endl;
+  cout << "input angles: theta=" << theta << ",phi=" << phi << endl;
   cout << "input momentum: " << plab.getComponents() / 1_GeV
        << ", norm = " << plab.getNorm() << endl;
 
   auto const observationHeight = 0_km + builder.getEarthRadius();
-  auto const injectionHeight = 112.75_km + builder.getEarthRadius();
+  auto const injectionHeight = 111.75_km + builder.getEarthRadius();
   auto const t = -observationHeight * cos(thetaRad) +
                  sqrt(-static_pow<2>(sin(thetaRad) * observationHeight) +
                       static_pow<2>(injectionHeight));
   Point const showerCore{rootCS, 0_m, 0_m, observationHeight};
   Point const injectionPos =
-      showerCore + DirectionVector{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
+      showerCore + DirectionVector{rootCS,
+                                   {-sin(thetaRad) * cos(phiRad),
+                                    -sin(thetaRad) * sin(phiRad), cos(thetaRad)}} *
+                   t;
 
   std::cout << "point of injection: " << injectionPos.getCoordinates() << std::endl;
 
-  if (A != 1) {
-    stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns, A, Z));
-
-  } else {
-    if (Z == 1) {
-      stack.addParticle(std::make_tuple(Code::Proton, E0, plab, injectionPos, 0_ns));
-    } else if (Z == 0) {
-      stack.addParticle(std::make_tuple(Code::Neutron, E0, plab, injectionPos, 0_ns));
-    } else {
-      std::cerr << "illegal parameters" << std::endl;
-      return EXIT_FAILURE;
-    }
-  }
-
   // we make the axis much longer than the inj-core distance since the
   // profile will go beyond the core, depending on zenith angle
   std::cout << "shower axis length: " << (showerCore - injectionPos).getNorm() * 1.5
@@ -229,6 +267,7 @@ int main(int argc, char** argv) {
 
   // setup processes, decays and interactions
 
+  // corsika::qgsjetII::Interaction qgsjet;
   corsika::sibyll::Interaction sibyll;
   InteractionCounter sibyllCounted(sibyll);
 
@@ -264,13 +303,8 @@ int main(int argc, char** argv) {
   corsika::proposal::Interaction emCascade(env);
   corsika::proposal::ContinuousProcess emContinuous(env);
   InteractionCounter emCascadeCounted(emCascade);
-  // put radio process here
-  RadioProcess<decltype(detector), CoREAS<decltype(detector),
-      decltype(StraightPropagator(env))>, decltype(StraightPropagator(env))>
-      coreas(detector, env);
 
   OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false);
-  TrackWriter trackWriter("tracks.dat");
 
   LongitudinalProfile longprof{showerAxis};
 
@@ -280,56 +314,98 @@ int main(int argc, char** argv) {
 
   corsika::urqmd::UrQMD urqmd;
   InteractionCounter urqmdCounted{urqmd};
-  StackInspector<setup::Stack> stackInspect(1000, false, E0);
+  StackInspector<setup::Stack> stackInspect(50000, false, E0);
 
   // assemble all processes into an ordered process list
   struct EnergySwitch {
     HEPEnergyType cutE_;
     EnergySwitch(HEPEnergyType cutE)
         : cutE_(cutE) {}
-    SwitchResult operator()(const Particle& p) {
-      if (p.getEnergy() < cutE_)
-        return SwitchResult::First;
-      else
-        return SwitchResult::Second;
-    }
+    bool operator()(const Particle& p) { return (p.getEnergy() < cutE_); }
   };
-  auto hadronSequence = make_select(
-      urqmdCounted, make_sequence(sibyllNucCounted, sibyllCounted), EnergySwitch(55_GeV));
+  auto hadronSequence = make_select(EnergySwitch(55_GeV), urqmdCounted,
+                                    make_sequence(sibyllNucCounted, sibyllCounted));
   auto decaySequence = make_sequence(decayPythia, decaySibyll);
-  auto sequence =
-      make_sequence(stackInspect, hadronSequence, reset_particle_mass, decaySequence,
-                    emContinuous, cut, coreas, trackWriter, observationLevel, longprof);
 
-  // define air shower object, run simulation
-  setup::Tracking tracking;
-  Cascade EAS(env, tracking, sequence, stack);
+  for (int i_shower = 1; i_shower < number_showers + 1; i_shower++) {
+
+    // directory for outputs
+    string const labHist_file = "inthist_lab_verticalEAS_" + to_string(i_shower) + ".npz";
+    string const cMSHist_file = "inthist_cms_verticalEAS_" + to_string(i_shower) + ".npz";
+    string const longprof_file = "longprof_verticalEAS_" + to_string(i_shower) + ".txt";
+    string const tracks_file = "tracks_" + to_string(i_shower) + ".dat";
+    string const particles_file = "particles_" + to_string(i_shower) + ".dat";
+
+    std::cout << std::endl;
+    std::cout << "Shower " << i_shower << "/" << number_showers << std::endl;
+
+    // setup particle stack, and add primary particle
+    setup::Stack stack;
+    stack.clear();
 
-  // to fix the point of first interaction, uncomment the following two lines:
-  //  EAS.forceInteraction();
+    if (A > 1) {
+      stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns, A, Z));
 
-  EAS.run();
+    } else {
+      if (A == 1) {
+        if (Z == 1) {
+          stack.addParticle(std::make_tuple(Code::Proton, E0, plab, injectionPos, 0_ns));
+        } else if (Z == 0) {
+          stack.addParticle(std::make_tuple(Code::Neutron, E0, plab, injectionPos, 0_ns));
+        } else {
+          std::cerr << "illegal parameters" << std::endl;
+          return EXIT_FAILURE;
+        }
+      } else {
+        stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns));
+      }
+    }
+
+    // put radio process here
+    RadioProcess<decltype(detector), CoREAS<decltype(detector),
+        decltype(StraightPropagator(env))>, decltype(StraightPropagator(env))>
+        coreas(detector, env);
+    TrackWriter trackWriter(tracks_file);
+    ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}),
+                                      particles_file);
+
+    auto sequence =
+        make_sequence(stackInspect, hadronSequence, reset_particle_mass, decaySequence,
+                      emContinuous, cut, coreas, trackWriter, observationLevel, longprof);
+
+    // define air shower object, run simulation
+    setup::Tracking tracking;
+    Cascade EAS(env, tracking, sequence, stack);
 
-  cut.showResults();
-  emContinuous.showResults();
-  observationLevel.showResults();
-  const HEPEnergyType Efinal = cut.getCutEnergy() + cut.getInvEnergy() +
-                               cut.getEmEnergy() + emContinuous.getEnergyLost() +
-                               observationLevel.getEnergyGround();
-  cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
-       << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
-  observationLevel.reset();
-  cut.reset();
-  emContinuous.reset();
+    // to fix the point of first interaction, uncomment the following two lines:
+    //  EAS.forceInteraction();
 
-  // get radio output
-  coreas.writeOutput();
+    EAS.run();
 
-  auto const hists = sibyllCounted.getHistogram() + sibyllNucCounted.getHistogram() +
-                     urqmdCounted.getHistogram();
+    cut.showResults();
+    emContinuous.showResults();
+    observationLevel.showResults();
+    const HEPEnergyType Efinal = cut.getCutEnergy() + cut.getInvEnergy() +
+                                 cut.getEmEnergy() + emContinuous.getEnergyLost() +
+                                 observationLevel.getEnergyGround();
+    cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
+         << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
+    // get radio output
+    coreas.writeOutput();
 
-  save_hist(hists.labHist(), "inthist_lab_verticalEAS.npz", true);
-  save_hist(hists.CMSHist(), "inthist_cms_verticalEAS.npz", true);
-  longprof.save("longprof_verticalEAS.txt");
+    // reset antenna collection
+    detector.reset();
 
+    observationLevel.reset();
+    cut.reset();
+    emContinuous.reset();
+
+
+    auto const hists = sibyllCounted.getHistogram() + sibyllNucCounted.getHistogram() +
+                       urqmdCounted.getHistogram();
+
+    save_hist(hists.labHist(), labHist_file, true);
+    save_hist(hists.CMSHist(), cMSHist_file, true);
+    longprof.save(longprof_file);
+  }
 }
diff --git a/tests/modules/testRadio.cpp b/tests/modules/testRadio.cpp
index 7b1ba2be7db64704eea4e3cfaa2d0c4a41d71a31..081c50fcb1680c7b95cb3fb1fc73bea6487fa639 100644
--- a/tests/modules/testRadio.cpp
+++ b/tests/modules/testRadio.cpp
@@ -1137,9 +1137,9 @@ TEST_CASE("Radio", "[processes]") {
       CHECK( path.receive_.getComponents() == vvv2.getComponents() );
       CHECK( path.R_distance_ == 10_m );
     }
-//
-//    CHECK( paths2_.size() == 1 );
-//
-//    }
+
+    CHECK( paths2_.size() == 1 );
+
+    }
 
   } // END: TEST_CASE("Radio", "[processes]")