diff --git a/corsika/detail/framework/geometry/Point.inl b/corsika/detail/framework/geometry/Point.inl
index 156723dd149102ecec8ee88ab773dafa5cfec84d..97f932e2ddb942af8e9c88a67bd7bd86cbe35d90 100644
--- a/corsika/detail/framework/geometry/Point.inl
+++ b/corsika/detail/framework/geometry/Point.inl
@@ -35,6 +35,10 @@ namespace corsika {
     return getCoordinates(pCS).getZ();
   }
 
+  inline LengthType Point::distance_to(Point const& point) const {
+    return (*this - point).getNorm();
+  }
+
   /// this always returns a QuantityVector as triple
   inline QuantityVector<length_d> Point::getCoordinates(
       CoordinateSystemPtr const& pCS) const {
diff --git a/corsika/modules/radio/CoREAS.hpp b/corsika/modules/radio/CoREAS.hpp
new file mode 100755
index 0000000000000000000000000000000000000000..ca1ea7e6266e3acd03dbd34a55d628f0a9f32445
--- /dev/null
+++ b/corsika/modules/radio/CoREAS.hpp
@@ -0,0 +1,67 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * 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.
+ */
+#pragma once
+
+#include <corsika/modules/radio/RadioProcess.hpp>
+#include <corsika/modules/radio/propagators/StraightPropagator.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  /**
+   * 
+   */
+  template <typename TPropagator = StraightPropagator>
+  class CoREAS final : public RadioProcess<CoREAS> {
+
+
+  public:
+    /**
+     * Simulate the radio emission from a particle across a track.
+     *
+     * This must be provided by the TRadioImpl.
+     *
+     * @param particle    The current particle.
+     * @param track       The current track.
+     *
+     */
+    template <typename Particle, typename Track>
+    EProcessReturn simulate(Particle&, Track const&) const {
+
+        // loop over every antenna
+        for (auto& antenna : getAntennas()) {
+
+            // use TPropagator to calculate Path from track to antenna
+            auto paths = ;/*  a collection of paths */
+
+            // do endpoint + ZHS formalism
+
+            // give the electric field and the direction to the antenna
+            antenna.receive(...); // << something
+        }
+
+    }
+
+    /**
+     * Return the maximum step length for this particle and track.
+     *
+     * This must be provided by the TRadioImpl.
+     *
+     * @param particle    The current particle.
+     * @param track       The current track.
+     *
+     * @returns The maximum length of this track.
+     */
+    template <typename Particle, typename Track>
+    LengthType MaxStepLength(Particle const& particle,
+                                        Track const& track) const;
+
+  }; // END: class RadioProcess
+
+} // namespace corsika
diff --git a/corsika/modules/radio/RadioProcess.hpp b/corsika/modules/radio/RadioProcess.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..03f93f9f50e2132589b9aa1f28a01a9de7ea9d83
--- /dev/null
+++ b/corsika/modules/radio/RadioProcess.hpp
@@ -0,0 +1,84 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * 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.
+ */
+#pragma once
+
+#include <corsika/framework/process/ContinuousProcess.hpp>
+
+namespace corsika {
+
+  /**
+   * The base interface for radio emission processes.
+   *
+   * TRadioImpl is the concrete implementation of the radio algorithm.
+   * TRadioDetector is the detector instance that stores antennas
+   * and is responsible for managing the output writing.
+   */
+  template <typename TRadioDetector, typename TRadioImpl, typename TPropagator>
+  class RadioProcess : public ContinuousProcess<
+                           RadioProcess<TRadioDetector, TRadioImpl, TPropagator>> {
+
+    /**
+     * Get a reference to the underlying radio implementation.
+     */
+    TRadioImpl& implementation() { return static_cast<TRadioImpl&>(*this); }
+
+    /*
+     *  Get a const reference to the underlying implementation.
+     */
+    TRadioImpl const& implementation() const {
+      return static_cast<TRadioImpl const&>(*this);
+    }
+
+  protected:
+    TRadioDetector& detector_; ///< The radio detector we store into.
+    TPropagator propagator_;   ///< The propagator implementation.
+
+  public:
+    /**
+     * Construct a new RadioProcess.
+     */
+    template <typename... TArgs>
+    RadioProcess(TRadioDetector& detector, TArgs&&... args)
+        : detector_(detector)
+        , propagator_(args...) {}
+
+    /**
+     * Perform the continuous process (radio emission).
+     *
+     * This handles filtering individual particle tracks
+     * before passing them to `Simulate`.`
+     *
+     * @param particle    The current particle.
+     * @param track       The current track.
+     */
+    template <typename Particle, typename Track>
+    ProcessReturn DoContinuous(Particle& particle, Track const& track) const {
+      // we wrap Simulate() in DoContinuous as the plan is to add particle level
+      // filtering or thinning for calculation of the radio emission. This is
+      // important for controlling the runtime of radio (by ignoring particles
+      // that aren't going to contribute i.e. heavy hadrons)
+      return this->implementation().simulate(particle, track);
+    }
+
+      /**
+       * TODO: This is placeholder so we can use text output while
+       * we wait for the true output formatting to be ready.
+       **/
+      bool writeOutput() const {
+
+        // how this method should work:
+        // 1. Loop over the antennas in the collection
+        // 2. Get their waveforms
+        // 3. Create a textfile for each antenna
+        // 4. and write out two columns, time and field.
+
+      }
+
+  }; // END: class RadioProcess
+
+} // namespace corsika::process::radio
diff --git a/corsika/modules/radio/antennas/Antenna.hpp b/corsika/modules/radio/antennas/Antenna.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8596d576334b6a8f66bddd6a0c5ad647b5d570f3
--- /dev/null
+++ b/corsika/modules/radio/antennas/Antenna.hpp
@@ -0,0 +1,79 @@
+/*
+ * (c) Copyright 2020 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.
+ */
+#pragma once
+
+#include <corsika/framework/geometry/Point.hpp>
+
+namespace corsika {
+
+  /**
+   * A common abstract interface for radio antennas.
+   *
+   * All concrete antenna implementations should be of
+   * type Antenna<T> where T is a concrete antenna implementation.
+   *
+   */
+  template <typename AntennaImpl>
+  class Antenna {
+
+    std::string const name_;         ///< The name/identifier of this antenna.
+    Point const location_; ///< The location of this antenna.
+
+  public:
+    // this stores the polarization vector of an electric field
+    using ElectricFieldVector =
+        QuantityVector<ElectricFieldType>;
+    using MagneticFieldVector =
+        QuantityVector<MagneticFieldType>;
+
+    // a dimensionless vector used for the incident direction
+    using Vector = QuantityVector<dimensionless_d>;
+
+    /**
+     * \brief Construct a base antenna instance.
+     *
+     * @param name    A name for this antenna.
+     * @param location    The location of this antenna.
+     *
+     */
+    Antenna(std::string const& name, Point const& location)
+        : name_(name)
+        , location_(location){};
+
+    /**
+     * Receive a signal at this antenna.
+     *
+     * This is a general implementation call that must be specialized
+     * for the particular antenna implementation and usage.
+     *
+     */
+    // template <typename... TVArgs>
+    // void Receive(TVArgs&& args...);
+
+    /**
+     * Get the location of this antenna.
+     */
+    Point const& getLocation() const { return location_; };
+
+    /**
+     * Get the name of this name antenna.
+     *
+     * This is used in producing the output data file.
+     */
+    std::string const& getName() const { return name_; };
+
+    /**
+     * Reset the antenna before starting a new simulation.
+     */
+    void reset();
+
+  }; // END: class Antenna final
+
+} // namespace corsika
diff --git a/corsika/modules/radio/antennas/TimeDomainAntenna.hpp b/corsika/modules/radio/antennas/TimeDomainAntenna.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ce0c681e6acb8934aae5d55038c08b104e4eff9
--- /dev/null
+++ b/corsika/modules/radio/antennas/TimeDomainAntenna.hpp
@@ -0,0 +1,86 @@
+/*
+ * (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.
+ */
+#pragma once
+
+#include <corsika/modules/radio/antennas/Antenna.hpp>
+
+namespace corsika {
+
+  /**
+   * An implementation of a time-domain antenna that has a customized
+   * start time, sampling period, and waveform duration.
+   *
+   */
+  class TimeDomainAntenna final : public Antenna<TimeDomainAntenna> {
+
+    using Array = std::vector<double>; ///< The type used to store arrays.
+
+    TimeType const start_time_;      ///< The start time of this waveform.
+    TimeType const duration_;        ///< The duration of this waveform.
+    TimeType const sampling_period_; ///< The sampling period of this antenna.
+
+    Array waveform_; ///< The waveform stored by this antenna.
+
+  protected:
+    // expose the CRTP interfaces constructor
+
+  public:
+    // import the methods from the antenna
+    using Antenna<TimeDomainAntenna>::Antenna;
+    using Antenna<TimeDomainAntenna>::getName;
+
+    /**
+     * Construct a new TimeDomainAntenna.
+     *
+     * @param name               The name of this antenna.
+     * @param location           The location of this antenna.
+     * @param start_time         The starting time of this waveform.
+     * @param duration           The duration of this waveform.
+     * @param sampling_period    The sampling period of this waveform.
+     *
+     */
+    TimeDomainAntenna(std::string const& name, Point const& location,
+                      TimeType const& start_time,
+                      TimeType const& duration,
+                      TimeType const& sampling_period)
+        : Antenna(name, location)
+        , start_time_(start_time)
+        , duration_(duration)
+        , sampling_period_(sampling_period){};
+
+    /**
+     * Receive an electric field at this antenna.
+     *
+     * This assumes that the antenna will receive
+     *  an *instantaneous* electric field modeled as a delta function.
+     *
+     * @param time        The (global) time at which this
+     * @param field       The incident electric field vector.
+     *
+     */
+    void receive(TimeType const time, ElectricFieldVector const& efield) const;
+
+    /**
+     * Get the current waveform for this antenna.
+     *
+     * NOTE: Currently returns ns and V/m but this should be UNITful
+     *
+     * @returns A pair of the sample times, and the field
+     */
+    std::pair<Array, Array> getWaveform() const;
+
+    /**
+     * Reset the antenna before starting a new simulation.
+     */
+    void reset() { waveform_.clear(); };
+
+  }; // END: class Antenna final
+
+} // namespace corsika
diff --git a/corsika/modules/radio/detectors/RadioDetector.hpp b/corsika/modules/radio/detectors/RadioDetector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7c21d7e48631a1f63b733177988b136d47db9ffa
--- /dev/null
+++ b/corsika/modules/radio/detectors/RadioDetector.hpp
@@ -0,0 +1,50 @@
+/*
+ * (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.
+ */
+#pragma once
+
+namespace corsika {
+
+  /**
+   * The base interface for radio detectors.
+   */
+
+  template <typename TAntennaImpl, typename TDetectorImpl>
+  class RadioDetector {
+
+    /**
+     * The collection of antennas used in this simulation.
+     */
+    std::vector<TAntennaImpl> antennas_;
+
+  public:
+    /**
+     * Add an antenna to this radio process.
+     *
+     * @param antenna    The antenna to add
+     */
+    auto addAntenna(TAntennaImpl const& antenna) -> void { antennas_.push_back(antenna); }
+
+    /**
+     * Get a *non*-const reference to the collection of antennas.
+     *
+     * @returns    An iterable mutable reference to the antennas.
+     */
+    std::vector<TAntennaImpl> const& getAntennas() { return antennas_; }
+
+    /**
+     * Reset all the antenna waveforms.
+     */
+    auto reset() -> void {
+      std::for_each(antennas_.begin(), antennas_.end(), std::mem_fn(&TAntennaImpl::Reset));
+    };
+
+  }; // END: class RadioDetector
+
+} // namespace corsika
diff --git a/corsika/modules/radio/detectors/TimeDomainDetector.hpp b/corsika/modules/radio/detectors/TimeDomainDetector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..23974262f92b63d5eb1417110aa18712ae295dc2
--- /dev/null
+++ b/corsika/modules/radio/detectors/TimeDomainDetector.hpp
@@ -0,0 +1,27 @@
+/*
+ * (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.
+ */
+#pragma once
+
+#include <vector>
+
+#include "corsika/modules/radio/detectors/RadioDetector.hpp"
+
+namespace corsika {
+
+  /**
+   * A common interface for radio detectors.
+   */
+  template <typename TAntennaImpl>
+  class TimeDomainDetector final : public RadioDetector<TAntennaImpl,TimeDomainDetector<TAntennaImpl>> {
+
+  public:
+  }; // END: class RadioDetector
+
+} // namespace corsika
diff --git a/corsika/modules/radio/propagators/RadioPropagator.hpp b/corsika/modules/radio/propagators/RadioPropagator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..394e2570c30217c2887e5b2b8d44a52b7fc9e492
--- /dev/null
+++ b/corsika/modules/radio/propagators/RadioPropagator.hpp
@@ -0,0 +1,46 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * 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.
+ */
+#pragma once
+
+#include <vector>
+
+#include <corsika/framework/geometry/Path.hpp>
+#include <corsika/modules/radio/propagators/SignalPath.hpp>
+
+namespace corsika {
+
+  /**
+   * Radio propagators are used to calculate the propagation
+   * paths from particles to antennas. Any class that wants
+   * to be used as a RadioPropagator must implement the
+   * following methods:
+   *
+   *  SignalPathCollection Propagate(Point const& start,
+   *                                 Point const& end,
+   *                                 LengthType const stepsize);
+   */
+  template <typename TImpl, typename TEnvironment>
+  class RadioPropagator {
+
+  protected:
+    // Since we typically know roughly how many paths will
+    // be computed for a given propagator, we use a std::vector here.
+    using SignalPathCollection = std::vector<SignalPath> const;
+
+    TEnvironment const& env_; ///< The environment.
+
+  public:
+    /**
+     * Construct a new RadioPropagator instance.
+     */
+    RadioPropagator(TEnvironment const& env)
+        : env_(env) {}
+
+  }; // class RadioPropagator
+
+} // namespace corsika
diff --git a/corsika/modules/radio/propagators/SignalPath.hpp b/corsika/modules/radio/propagators/SignalPath.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..76157d899473b5fd34d53bc9c8e47a1299fe8e6b
--- /dev/null
+++ b/corsika/modules/radio/propagators/SignalPath.hpp
@@ -0,0 +1,45 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <corsika/framework/geometry/Path.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  /**
+   * Store the photon signal path between two points.
+   *
+   * This is basically a container class
+   */
+  struct SignalPath final : private Path {
+
+    using path = std::deque<Point>;
+
+    TimeType const time_;    ///< The total propagation time.
+    double const average_refractivity_; ///< The average refractivity.
+    Vector<dimensionless_d> const emit_;    ///< The (unit-length) emission vector.
+    Vector<dimensionless_d> const receive_; ///< The (unit-length) receive vector.
+    path const points_;  ///< A collection of points that make up the geometrical path.
+
+    /**
+     * Create a new SignalPath instance.
+     */
+    SignalPath(TimeType const time, double const average_refractivity,
+               Vector<dimensionless_d> const emit, Vector<dimensionless_d> const receive,
+               path const& points)
+        : Path(points)
+        , time_(time)
+        , average_refractivity_(average_refractivity)
+        , emit_(emit)
+        , receive_(receive) {}
+
+  }; // class SignalPath
+
+} // namespace corsika
diff --git a/corsika/modules/radio/propagators/StraightPropagator.hpp b/corsika/modules/radio/propagators/StraightPropagator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3ee2f3bf075c795766b30afacc82908de0293860
--- /dev/null
+++ b/corsika/modules/radio/propagators/StraightPropagator.hpp
@@ -0,0 +1,129 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * 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.
+ */
+#pragma once
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalConstants.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/modules/radio/propagators/RadioPropagator.hpp>
+
+namespace corsika {
+
+  /**
+   * This class implements a basic propagator that uses
+   * the straight-line (vector) between the particle
+   * location and the antenna as the trajectory.
+   *
+   * This is what is used in ZHAireS and CoREAS in C7.
+   */
+  template <typename TEnvironment>
+  class StraightPropagator final
+      : public RadioPropagator<StraightPropagator<TEnvironment>, TEnvironment> {
+
+    using Base = RadioPropagator<StraightPropagator<TEnvironment>, TEnvironment>;
+    using SignalPathCollection = typename Base::SignalPathCollection;
+
+  public:
+
+    /**
+     * Construct a new StraightPropagator with a given environment.
+     *
+     */
+    StraightPropagator(TEnvironment const& env)
+        : RadioPropagator<StraightPropagator, TEnvironment>(env){};
+    // TODO: maybe the constructor doesn't take any arguments for the environment (?)
+
+    /**
+     * Return the collection of paths from `start` to `end`.
+     * or from 'source' which is the emission point to 'destination'
+     * which is the location of the antenna
+     */
+    SignalPathCollection propagate(Point const& source,
+                                   Point const& destination,
+                                   LengthType const stepsize) const {
+
+      /**
+       * get the normalized (unit) vector from `source` to `destination'.
+       * this is also the `emit` and `receive` vectors in the SignalPath class.
+       * in this case emit and receive unit vectors should be the same
+       * so they are both called direction
+       */
+      auto direction{(destination - source).normalized()};
+
+      // the step is the direction vector with length `stepsize`
+      auto step{direction * stepsize};
+
+      //calculate the number of points (roughly) for the numerical integration.
+      auto n_points {(destination - source).getNorm() / stepsize};
+
+      // get the universe for this environment
+      auto const* const universe{Base::env_.getUniverse().get()};
+
+      // the points that we build along the way for the numerical integration
+      std::deque<Point> points;
+
+      // store value of the refractive index at points
+      std::vector<double> rindex;
+      rindex.reserve(n_points);
+
+      // loop from `source` to `destination` to store values before Simpson's rule.
+      // this loop skips the last point 'destination'
+      for (auto point = source; (point - destination).getNorm() > 0.6 * stepsize;
+           point = point + step) {
+
+         // get the environment node at this specific 'point'
+         auto const* node{universe->getContainingNode(point)};
+
+         // get the associated refractivity at 'point'
+         auto const refractive_index{node->getModelProperties().getRefractiveIndex(point)};
+         rindex.push_back(refractive_index);
+
+         // add this 'point' to our deque collection
+         points.push_back(point);
+      }
+
+      //add the refractive index of last point 'destination' and store it
+      auto const* node{universe->getContainingNode(destination)};
+      auto const refractive_index{node->getModelProperties().getRefractiveIndex(destination)};
+      rindex.push_back(refractive_index);
+      points.push_back(destination);
+
+      // Apply Simpson's rule
+      auto N = rindex.size();
+      std::size_t index = 0;
+      double sum = rindex.at(index);
+      auto refra_ = rindex.at(index);
+      auto h = ((destination - source).getNorm()) / (N - 1);
+      for (std::size_t index = 1; index < (N - 1); index += 2) {
+        sum += 4 * rindex.at(index);
+        refra_ += rindex.at(index);
+      }
+      for (std::size_t index = 2; index < (N - 1); index += 2) {
+        sum += 2 * rindex.at(index);
+        refra_ += rindex.at(index);
+      }
+      index = N - 1;
+      sum = sum + rindex.at(index);
+      refra_ += rindex.at(index);
+
+      // compute the total time delay.
+      TimeType time = sum * (h / (3 * constants::c));
+
+      // compute the average refractivity.
+      auto average_refractivity = refra_ / N;
+
+      // realize that emission and receive vector are 'direction' in this case.
+      return { SignalPath(time, average_refractivity, direction , direction, points) };
+
+    } // END: propagate()
+
+  }; // End: StraightPropagator
+
+} // namespace corsika
\ No newline at end of file
diff --git a/tests/modules/testRadio.cpp b/tests/modules/testRadio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b7321e86ad986453885afa2bf403baeb4c3a9dd
--- /dev/null
+++ b/tests/modules/testRadio.cpp
@@ -0,0 +1,280 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * 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 <catch2/catch.hpp>
+
+#include <corsika/modules/radio/ZHS.hpp>
+#include <corsika/modules/radio/antennas/TimeDomainAntenna.hpp>
+#include <corsika/modules/radio/detectors/TimeDomainDetector.hpp>
+#include <corsika/modules/radio/propagators/StraightPropagator.hpp>
+#include <corsika/modules/radio/propagators/SignalPath.hpp>
+#include <corsika/modules/radio/propagators/RadioPropagator.hpp>
+
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/IRefractiveIndexModel.hpp>
+#include <corsika/media/LayeredSphericalAtmosphereBuilder.hpp>
+#include <corsika/media/UniformRefractiveIndex.hpp>
+#include <corsika/media/ExponentialRefractiveIndex.hpp>
+#include <corsika/media/VolumeTreeNode.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/PhysicalConstants.hpp>
+
+using namespace corsika;
+
+double constexpr absMargin = 1.0e-7;
+
+TEST_CASE("Radio", "[processes]") {
+
+//  logging::set_level(logging::level::info);
+//  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+
+  // check that I can create and reset a TimeDomain process
+  SECTION("TimeDomainDetector") {
+
+    // construct a time domain detector
+    TimeDomainDetector<TimeDomainAntenna> detector;
+
+    //   // and construct some time domain radio process
+    //   TimeDomain<ZHS<>, TimeDomainDetector<TimeDomainAntenna>> process(detector);
+    //   TimeDomain<ZHS<CPU>, TimeDomainDetector<TimeDomainAntenna>> process2(detector);
+    //   TimeDomain<ZHS<CPU>, decltype(detector)> process3(detector);
+  }
+
+  // check that I can create time domain antennas
+  SECTION("TimeDomainDetector") {
+
+    // create an environment so we can get a coordinate system
+    Environment<IMediumModel> env;
+
+    // the antenna location
+    const auto point{Point(env.getCoordinateSystem(), 1_m, 2_m, 3_m)};
+
+    // check that I can create an antenna at (1, 2, 3)
+    const auto ant{TimeDomainAntenna("antenna_name", point)};
+
+    // assert that the antenna name is correct
+    REQUIRE(ant.getName() == "antenna_name");
+
+    // and check that the antenna is at the right location
+    REQUIRE((ant.getLocation() - point).getNorm() < 1e-12 * 1_m);
+
+    // construct a radio detector instance to store our antennas
+    TimeDomainDetector<TimeDomainAntenna> detector;
+
+    // add this antenna to the process
+    detector.addAntenna(ant);
+  }
+
+  // check that I can create working Straight Propagators in different environments
+  SECTION("Straight Propagator w/ Uniform Refractive Index") {
+
+    // create an environment with uniform refractive index of 1
+    using UniRIndex =
+        UniformRefractiveIndex<HomogeneousMedium<IRefractiveIndexModel<IMediumModel>>>;
+
+    using EnvType = Environment<IRefractiveIndexModel<IMediumModel>>;
+    EnvType env;
+
+    // get a coordinate system
+    const CoordinateSystemPtr rootCS = env.getCoordinateSystem();
+
+    auto Medium = EnvType::createNode<Sphere>(
+        Point{rootCS, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity());
+
+    auto const props = Medium->setModelProperties<UniRIndex>(
+        1, 1_kg / (1_m * 1_m * 1_m),
+        NuclearComposition(
+            std::vector<Code>{Code::Nitrogen},
+            std::vector<float>{1.f}));
+
+    env.getUniverse()->addChild(std::move(Medium));
+
+    // get some points
+    Point p0(rootCS, {0_m, 0_m, 0_m});
+    //    Point p1(rootCS, {0_m, 0_m, 1_m});
+    //    Point p2(rootCS, {0_m, 0_m, 2_m});
+    //    Point p3(rootCS, {0_m, 0_m, 3_m});
+    //    Point p4(rootCS, {0_m, 0_m, 4_m});
+    //    Point p5(rootCS, {0_m, 0_m, 5_m});
+    //    Point p6(rootCS, {0_m, 0_m, 6_m});
+    //    Point p7(rootCS, {0_m, 0_m, 7_m});
+    //    Point p8(rootCS, {0_m, 0_m, 8_m});
+    //    Point p9(rootCS, {0_m, 0_m, 9_m});
+    Point p10(rootCS, {0_m, 0_m, 10_m});
+
+    // get a unit vector
+    Vector<dimensionless_d> v1(rootCS, {0, 0, 1});
+
+    //    // get a geometrical path of points
+    //    Path P1({p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10});
+
+    // construct a Straight Propagator given the uniform refractive index environment
+    StraightPropagator SP(env);
+
+    // store the outcome of the Propagate method to paths_
+    auto const paths_ = SP.propagate(p0, p10, 1_m);
+
+    // perform checks to paths_ components
+    for (auto const& path : paths_) {
+      CHECK((path.time_ / 1_s) - ((34_m / (3 * constants::c)) / 1_s) ==
+            Approx(0).margin(absMargin));
+      CHECK(path.average_refractivity_ == Approx(1));
+      CHECK(path.emit_.getComponents() == v1.getComponents());
+      CHECK(path.receive_.getComponents() == v1.getComponents());
+      //      CHECK(std::equal(P1.begin(), P1.end(), path.points_.begin(),[]
+      //      (Point a, Point b) { return (a - b).norm() / 1_m < 1e-5;}));
+      //TODO:THINK ABOUT THE POINTS IN THE SIGNALPATH.H
+    }
+
+    CHECK(paths_.size() == 1);
+  }
+
+    SECTION("Straight Propagator w/ Exponential Refractive Index") {
+
+//    logging::set_level(logging::level::info);
+//    corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+     // create an environment with exponential refractive index (n_0 = 1 & lambda = 0)
+    using ExpoRIndex = ExponentialRefractiveIndex<HomogeneousMedium
+        <IRefractiveIndexModel<IMediumModel>>>;
+
+    using EnvType = Environment<IRefractiveIndexModel<IMediumModel>>;
+    EnvType env1;
+
+    //get another coordinate system
+    const CoordinateSystemPtr rootCS1 = env1.getCoordinateSystem();
+
+    auto Medium1 = EnvType::createNode<Sphere>(
+        Point{rootCS1, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity());
+
+    auto const props1 =
+        Medium1
+            ->setModelProperties<ExpoRIndex>( 1, 0 / 1_m,
+               1_kg / (1_m * 1_m * 1_m),
+               NuclearComposition(
+               std::vector<Code>{Code::Nitrogen},
+               std::vector<float>{1.f}));
+
+    env1.getUniverse()->addChild(std::move(Medium1));
+
+    // get some points
+    Point pp0(rootCS1, {0_m, 0_m, 0_m});
+//    Point pp1(rootCS1, {0_m, 0_m, 1_m});
+//    Point pp2(rootCS1, {0_m, 0_m, 2_m});
+//    Point pp3(rootCS1, {0_m, 0_m, 3_m});
+//    Point pp4(rootCS1, {0_m, 0_m, 4_m});
+//    Point pp5(rootCS1, {0_m, 0_m, 5_m});
+//    Point pp6(rootCS1, {0_m, 0_m, 6_m});
+//    Point pp7(rootCS1, {0_m, 0_m, 7_m});
+//    Point pp8(rootCS1, {0_m, 0_m, 8_m});
+//    Point pp9(rootCS1, {0_m, 0_m, 9_m});
+    Point pp10(rootCS1, {0_m, 0_m, 10_m});
+
+    // get a unit vector
+    Vector<dimensionless_d> vv1(rootCS1, {0, 0, 1});
+
+    // construct a Straight Propagator given the exponential refractive index environment
+    StraightPropagator SP1(env1);
+
+    // store the outcome of Propagate method to paths1_
+    auto const paths1_ = SP1.propagate(pp0, pp10, 1_m);
+
+    // perform checks to paths1_ components (this is just a sketch for now)
+    for (auto const& path :paths1_) {
+      CHECK( (path.time_ / 1_s)  - ((34_m / (3 * constants::c)) / 1_s)
+             == Approx(0).margin(absMargin) );
+      CHECK( path.average_refractivity_ == Approx(1) );
+      CHECK( path.emit_.getComponents() == vv1.getComponents() );
+      CHECK( path.receive_.getComponents() == vv1.getComponents() );
+    }
+
+    CHECK( paths1_.size() == 1 );
+
+    /*
+    * A second environment with another exponential refractive index
+    */
+
+    // create an environment with exponential refractive index (n_0 = 2 & lambda = 2)
+    using ExpoRIndex = ExponentialRefractiveIndex<HomogeneousMedium
+        <IRefractiveIndexModel<IMediumModel>>>;
+
+    using EnvType = Environment<IRefractiveIndexModel<IMediumModel>>;
+    EnvType env2;
+
+    //get another coordinate system
+    const CoordinateSystemPtr rootCS2 = env2.getCoordinateSystem();
+
+    auto Medium2 = EnvType::createNode<Sphere>(
+        Point{rootCS2, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity());
+
+    auto const props2 =
+        Medium2
+            ->setModelProperties<ExpoRIndex>( 2, 2 / 1_m,
+                                              1_kg / (1_m * 1_m * 1_m),
+                                              NuclearComposition(
+                                                  std::vector<Code>{Code::Nitrogen},
+                                                  std::vector<float>{1.f}));
+
+    env2.getUniverse()->addChild(std::move(Medium2));
+
+    // get some points
+    Point ppp0(rootCS2, {0_m, 0_m, 0_m});
+    Point ppp10(rootCS2, {0_m, 0_m, 10_m});
+
+    // get a unit vector
+    Vector<dimensionless_d> vvv1(rootCS2, {0, 0, 1});
+
+    // construct a Straight Propagator given the exponential refractive index environment
+    StraightPropagator SP2(env2);
+
+    // store the outcome of Propagate method to paths1_
+    auto const paths2_ = SP2.propagate(ppp0, ppp10, 1_m);
+
+    // perform checks to paths1_ components (this is just a sketch for now)
+    for (auto const& path :paths2_) {
+      CHECK( (path.time_ / 1_s)  - ((3.177511688_m / (3 * constants::c)) / 1_s)
+             == Approx(0).margin(absMargin) );
+      CHECK( path.average_refractivity_ == Approx(0.210275935) );
+      CHECK( path.emit_.getComponents() == vvv1.getComponents() );
+      CHECK( path.receive_.getComponents() == vvv1.getComponents() );
+    }
+
+    CHECK( paths2_.size() == 1 );
+
+    }
+
+//    SECTION("Construct a ZHS process.") {
+//
+//        // TODO: construct the environment for the propagator
+//
+//        // here we just use a deque to store the antenna
+//        std::deque<TimeDomainAntenna> antennas;
+//
+//        // TODO: add time domain antennas to the deque
+//
+//        // and now create ZHS with this antenna collection
+//        // and with a straight propagator
+//        ZHS<decltype(antennas), StraightPropagator> zhs(antennas, env);
+//        // TODO: do we need the explicit type declaration?
+//
+//        // here is where the simulation happens
+//
+//        // and call zhs.writeOutput() at the end.
+//    }
+
+} // END: TEST_CASE("Radio", "[processes]")