diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt
index 27847c8f4643b5990fdbef202df6118d1afb67da..165dcb7b30321c20fd2185cadbd8c2d029ef5226 100644
--- a/benchmark/CMakeLists.txt
+++ b/benchmark/CMakeLists.txt
@@ -14,7 +14,8 @@ FetchContent_MakeAvailable(googlebenchmark)
 
 set(BENCHMARKS simulator)
 
-add_executable(microbenchmarks main.cpp predict.cpp directions.cpp spectrum.cpp)
+add_executable(microbenchmarks main.cpp predict.cpp directions.cpp spectrum.cpp
+                               spectrum_cross.cpp)
 
 target_compile_options(
   microbenchmarks PRIVATE $<$<CONFIG:Debug>:-g> $<$<CONFIG:Release>:-O3>
diff --git a/benchmark/spectrum.cpp b/benchmark/spectrum.cpp
index 295b2601c166bae5144563a2c9e7646e39d1d586..4e8007decf90f3a5ebe3384fafc0e5ce563b8dc9 100644
--- a/benchmark/spectrum.cpp
+++ b/benchmark/spectrum.cpp
@@ -61,7 +61,7 @@ protected:
 BENCHMARK_DEFINE_F(SpectrumBenchmark, SpectrumBenchmark)
 (benchmark::State &state) {
   for (auto _ : state) {
-    spectrum->evaluate(frequencies, *computed_spectrum);
+    spectrum->Evaluate(frequencies, *computed_spectrum);
   }
 }
 
diff --git a/benchmark/spectrum_cross.cpp b/benchmark/spectrum_cross.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..23c43f177353b3f2579c3f0e67db1d9919ea7e7a
--- /dev/null
+++ b/benchmark/spectrum_cross.cpp
@@ -0,0 +1,67 @@
+#include <Simulator.h>
+#include <benchmark/benchmark.h>
+#include <random>
+#include <xtensor/xtensor.hpp>
+
+#include <Direction.h>
+#include <PointSource.h>
+#include <Spectrum.h>
+const std::vector<int64_t> n_frequencies = {128, 256, 512};
+
+const double kHz = 1.e3;
+const double MHz = 1.e6;
+namespace {} // namespace
+
+class SpectrumCrossBenchmark : public benchmark::Fixture {
+public:
+  void SetUp(benchmark::State &state) override {
+    n_frequencies = state.range(0);
+    const bool is_log = state.range(1);
+    const bool compute_rotation = state.range(2);
+    frequencies.resize({n_frequencies});
+    for (int i = 0; i < n_frequencies; i++) {
+      frequencies(i) = i * 100. * kHz + MHz;
+    }
+
+    spectrum = std::make_unique<Spectrum>();
+    auto &spec = *spectrum;
+    spec.has_logarithmic_spectral_index = is_log;
+    spec.reference_flux = {1.0, 0.0, 0.0, 0.0};
+    spec.spectral_terms = {1.0e-6, 2.0e-6, 3.0e-6};
+    spec.reference_frequency = MHz;
+    spec.polarization_angle = 0.3;
+    spec.polarization_factor = 0.2;
+    spec.rotation_measure = 0.1;
+    spec.has_rotation_measure = compute_rotation;
+    computed_spectrum =
+        std::make_unique<xt::xtensor<std::complex<double>, 2>>();
+    computed_spectrum->resize({4, n_frequencies});
+  }
+  void TearDown(benchmark::State &) override {
+    spectrum.reset();
+    computed_spectrum.reset();
+  }
+
+protected:
+  std::unique_ptr<Spectrum> spectrum;
+  std::unique_ptr<xt::xtensor<std::complex<double>, 2>> computed_spectrum;
+  size_t n_frequencies;
+  xt::xtensor<double, 1> frequencies;
+};
+
+BENCHMARK_DEFINE_F(SpectrumCrossBenchmark, SpectrumBenchmark)
+(benchmark::State &state) {
+  for (auto _ : state) {
+    spectrum->EvaluateCrossCorrelations(frequencies, *computed_spectrum);
+  }
+}
+
+BENCHMARK_REGISTER_F(SpectrumCrossBenchmark, SpectrumBenchmark)
+    ->ArgsProduct({
+        n_frequencies,
+        {false, true},
+        {false, true},
+
+    })
+    ->ArgNames({"#frequencies", "is_log", "compute_rotation"})
+    ->Unit(benchmark::kNanosecond);
diff --git a/include/Spectrum.h b/include/Spectrum.h
index 94f2154f019d8f836dedaee610570549afe2052b..194bce45413786f20fc01cece8285ddb8fee60d7 100644
--- a/include/Spectrum.h
+++ b/include/Spectrum.h
@@ -13,9 +13,13 @@
 #include <StokesVector.h>
 
 #include <casacore/casa/BasicSL/Constants.h>
+#include <complex>
+#include <xtensor/xcomplex.hpp>
+#include <xtensor/xshape.hpp>
 #include <xtensor/xtensor.hpp>
-
+#include <xtensor/xview.hpp>
 using Stokes = dp3::base::Stokes;
+enum CrossCorrelation { XX = 0, XY = 1, YX = 2, YY = 3 };
 
 class Spectrum {
 public:
@@ -29,7 +33,7 @@ public:
   bool has_rotation_measure;
   bool has_logarithmic_spectral_index;
 
-  void evaluate(const xt::xtensor<double, 1> &frequencies,
+  void Evaluate(const xt::xtensor<double, 1> &frequencies,
                 StokesVector &result) const {
     result.I.resize(frequencies.size());
     result.Q.resize(frequencies.size());
@@ -37,20 +41,26 @@ public:
     result.V.resize(frequencies.size());
 
     double I0 = reference_flux.I;
+    double V0 = reference_flux.V;
 
     if (spectral_terms.size() > 0 && has_logarithmic_spectral_index) {
       for (size_t f_idx = 0; f_idx < frequencies.size(); f_idx++) {
-        result.I[f_idx] = evaluate_log_spectrum(
-            I0, frequencies[f_idx], reference_frequency, spectral_terms);
+        const double spectral_term = EvaluateLogSpectrum(
+            frequencies[f_idx], reference_frequency, spectral_terms);
+        result.I[f_idx] = I0 * spectral_term;
+        result.V[f_idx] = V0 * spectral_term;
       }
     } else if (spectral_terms.size()) {
       for (size_t f_idx = 0; f_idx < frequencies.size(); f_idx++) {
-        result.I[f_idx] = evaluate_spectrum(
-            I0, frequencies[f_idx], reference_frequency, spectral_terms);
+        const double spectral_term = EvaluateSpectrum(
+            frequencies[f_idx], reference_frequency, spectral_terms);
+        result.I[f_idx] = I0 + spectral_term;
+        result.V[f_idx] = V0 + spectral_term;
       }
     } else {
       for (size_t f_idx = 0; f_idx < frequencies.size(); f_idx++) {
         result.I[f_idx] = I0;
+        result.V[f_idx] = V0;
       }
     }
 
@@ -67,10 +77,70 @@ public:
     }
   }
 
+  void EvaluateCrossCorrelations(
+      const xt::xtensor<double, 1> &frequencies,
+      xt::xtensor<std::complex<double>, 2> &result) const {
+    xt::xtensor<double, 1>::shape_type kSpectralCorrectionShape{
+        frequencies.size()};
+    xt::xtensor<double, 2>::shape_type kRotationCoefficientsShape{
+        2, frequencies.size()};
+    xt::xtensor<double, 1> spectral_correction =
+        xt::zeros<double>(kSpectralCorrectionShape);
+    xt::xtensor<double, 2> rotation_coefficients =
+        xt::zeros<double>(kRotationCoefficientsShape);
+
+    result.resize({4, frequencies.size()});
+
+    if (spectral_terms.size() > 0 && has_logarithmic_spectral_index) {
+      for (size_t f_idx = 0; f_idx < frequencies.size(); f_idx++) {
+        spectral_correction[f_idx] = EvaluateLogSpectrum(
+            frequencies[f_idx], reference_frequency, spectral_terms);
+      }
+    } else if (spectral_terms.size() > 0) {
+      for (size_t f_idx = 0; f_idx < frequencies.size(); f_idx++) {
+        spectral_correction[f_idx] = EvaluateSpectrum(
+            frequencies[f_idx], reference_frequency, spectral_terms);
+      }
+    }
+
+    if (has_rotation_measure) {
+      for (size_t f_idx = 0; f_idx < frequencies.size(); f_idx++) {
+        const double kLambda = casacore::C::c / frequencies[f_idx];
+        const double kChi =
+            2.0 * (polarization_angle + (rotation_measure * kLambda * kLambda));
+        const double stokesQU = polarization_factor;
+        rotation_coefficients(0, f_idx) = stokesQU * cos(kChi);
+        rotation_coefficients(1, f_idx) = stokesQU * sin(kChi);
+      }
+    }
+
+    xt::xtensor<double, 1> I;
+    xt::xtensor<double, 1> V;
+
+    if (has_logarithmic_spectral_index) {
+      I = spectral_correction * reference_flux.I;
+      V = spectral_correction * reference_flux.V;
+
+    } else {
+      I = spectral_correction + reference_flux.I;
+      V = spectral_correction + reference_flux.V;
+    }
+    auto Q = I * xt::view(rotation_coefficients, 0, xt::all());
+    auto U = I * xt::view(rotation_coefficients, 1, xt::all());
+
+    xt::real(xt::view(result, CrossCorrelation::XX, xt::all())) = I + Q;
+    xt::imag(xt::view(result, CrossCorrelation::XX, xt::all())) = 0.0;
+    xt::real(xt::view(result, CrossCorrelation::XY, xt::all())) = U;
+    xt::imag(xt::view(result, CrossCorrelation::XY, xt::all())) = V;
+    xt::real(xt::view(result, CrossCorrelation::YX, xt::all())) = U;
+    xt::imag(xt::view(result, CrossCorrelation::YX, xt::all())) = -V;
+    xt::real(xt::view(result, CrossCorrelation::YY, xt::all())) = I - Q;
+    xt::imag(xt::view(result, CrossCorrelation::YY, xt::all())) = 0.0;
+  }
+
 private:
   inline double
-  evaluate_polynomial(double x,
-                      const xt::xtensor<double, 1> &parameters) const {
+  EvaluatePolynomial(double x, const xt::xtensor<double, 1> &parameters) const {
     double partial = 0.0;
     for (auto it = spectral_terms.rbegin(), end = spectral_terms.rend();
          it != end; ++it) {
@@ -79,26 +149,24 @@ private:
     return partial;
   }
   inline double
-  evaluate_log_polynomial(double x,
-                          const xt::xtensor<double, 1> &parameters) const {
-    double base = log10(x);
-    return evaluate_polynomial(base, parameters);
+  EvaluateLogPolynomial(double x,
+                        const xt::xtensor<double, 1> &parameters) const {
+    const double base = log10(x);
+    return EvaluatePolynomial(base, parameters);
   }
 
   inline double
-  evaluate_log_spectrum(double I0, const double frequency,
-                        const double reference_frequency,
-                        const xt::xtensor<double, 1> &parameters) const {
+  EvaluateLogSpectrum(const double frequency, const double reference_frequency,
+                      const xt::xtensor<double, 1> &parameters) const {
     double x = frequency / reference_frequency;
-    return I0 * pow(10, x * evaluate_log_polynomial(x, parameters));
+    return pow(10, x * EvaluateLogPolynomial(x, parameters));
   }
   inline double
-  evaluate_spectrum(double I0, const double frequency,
-                    const double reference_frequency,
-                    const xt::xtensor<double, 1> &parameters) const {
-    double x = frequency / reference_frequency - 1.0;
+  EvaluateSpectrum(const double frequency, const double reference_frequency,
+                   const xt::xtensor<double, 1> &parameters) const {
+    const double x = frequency / reference_frequency - 1.0;
 
-    return I0 + evaluate_polynomial(x, parameters) * x;
+    return EvaluatePolynomial(x, parameters) * x;
   }
 };
 
diff --git a/src/PointSource.cpp b/src/PointSource.cpp
index 4fab35065a00557e6819a670571b6b0c296702ce..c024db982c0d4f1f2f087322ae1db116450ea73a 100644
--- a/src/PointSource.cpp
+++ b/src/PointSource.cpp
@@ -65,6 +65,7 @@ Stokes PointSource::stokes(double freq) const {
       // Compute I * (v / v0) ^ exponent, where I is the value of Stokes
       // I at the reference frequency.
       stokes.I *= pow(10., base * exponent);
+      stokes.V *= pow(10., base * exponent);
     } else {
       double x = freq / itsRefFreq - 1.0;
       typedef std::vector<double>::const_reverse_iterator iterator_type;
@@ -75,6 +76,7 @@ Stokes PointSource::stokes(double freq) const {
         val = val * x + *it;
       }
       stokes.I += val * x;
+      stokes.V += val * x;
     }
   }
 
diff --git a/tests/test_spectrum.cpp b/tests/test_spectrum.cpp
index 74bdaeb95946af02140ec90317d7a021ae3a4362..ad1013f5aaf1fed1e0bcfd917cde8fbd58eff16e 100644
--- a/tests/test_spectrum.cpp
+++ b/tests/test_spectrum.cpp
@@ -45,7 +45,7 @@ BOOST_FIXTURE_TEST_CASE(normal_no_rotation, SpectrumFixture) {
   stokes_nu2 = point_source.stokes(frequencies[2]);
 
   StokesVector result;
-  spectrum.evaluate(frequencies, result);
+  spectrum.Evaluate(frequencies, result);
 
   BOOST_CHECK_CLOSE(result.I[0], stokes_nu0.I, 1.e-6);
   BOOST_CHECK_CLOSE(result.I[1], stokes_nu1.I, 1.e-6);
@@ -82,7 +82,7 @@ BOOST_FIXTURE_TEST_CASE(normal_with_rotation, SpectrumFixture) {
   stokes_nu2 = point_source.stokes(frequencies[2]);
 
   StokesVector result;
-  spectrum.evaluate(frequencies, result);
+  spectrum.Evaluate(frequencies, result);
 
   BOOST_CHECK_CLOSE(result.I[0], stokes_nu0.I, 1.e-6);
   BOOST_CHECK_CLOSE(result.I[1], stokes_nu1.I, 1.e-6);
@@ -117,7 +117,7 @@ BOOST_FIXTURE_TEST_CASE(normal_log_no_rotation, SpectrumFixture) {
   stokes_nu2 = point_source.stokes(frequencies[2]);
 
   StokesVector result;
-  spectrum.evaluate(frequencies, result);
+  spectrum.Evaluate(frequencies, result);
 
   BOOST_CHECK_CLOSE_FRACTION(result.I[0], stokes_nu0.I, 1.e-3);
   BOOST_CHECK_CLOSE_FRACTION(result.I[1], stokes_nu1.I, 1.e-3);
@@ -158,7 +158,7 @@ BOOST_FIXTURE_TEST_CASE(normal_log_with_rotation, SpectrumFixture) {
   stokes_nu2 = point_source.stokes(frequencies[2]);
 
   StokesVector result;
-  spectrum.evaluate(frequencies, result);
+  spectrum.Evaluate(frequencies, result);
 
   BOOST_CHECK_CLOSE_FRACTION(result.I[0], stokes_nu0.I, 1.e-3);
   BOOST_CHECK_CLOSE_FRACTION(result.I[1], stokes_nu1.I, 1.e-3);
@@ -174,4 +174,74 @@ BOOST_FIXTURE_TEST_CASE(normal_log_with_rotation, SpectrumFixture) {
   BOOST_CHECK_CLOSE_FRACTION(result.V[2], stokes_nu2.V, 1.e-3);
 }
 
+BOOST_FIXTURE_TEST_CASE(normal_log_with_rotation_cross, SpectrumFixture) {
+  spectrum.spectral_terms = {1.0e-6, 2.0e-6, 3.0e-6};
+  spectrum.has_logarithmic_spectral_index = true;
+  spectrum.has_rotation_measure = true;
+  spectrum.has_logarithmic_spectral_index = true;
+
+  dp3::base::PointSource point_source(dp3::base::Direction(0.0, 0.0),
+                                      spectrum.reference_flux);
+
+  point_source.setSpectralTerms(
+      spectrum.reference_frequency, spectrum.has_logarithmic_spectral_index,
+      spectrum.spectral_terms.begin(), spectrum.spectral_terms.end());
+
+  point_source.setRotationMeasure(spectrum.polarization_factor,
+                                  spectrum.polarization_angle,
+                                  spectrum.rotation_measure);
+  Stokes stokes_nu0(0, 0, 0, 0);
+  Stokes stokes_nu1(0, 0, 0, 0);
+  Stokes stokes_nu2(0, 0, 0, 0);
+
+  stokes_nu0 = point_source.stokes(frequencies[0]);
+  stokes_nu1 = point_source.stokes(frequencies[1]);
+  stokes_nu2 = point_source.stokes(frequencies[2]);
+
+  xt::xtensor<std::complex<double>, 2> result_complex;
+  spectrum.EvaluateCrossCorrelations(frequencies, result_complex);
+
+  BOOST_CHECK_CLOSE_FRACTION(result_complex(0, 0).real(),
+                             stokes_nu0.I + stokes_nu0.Q, 1.e-3);
+  BOOST_CHECK_CLOSE_FRACTION(result_complex(0, 1).real(),
+                             stokes_nu1.I + stokes_nu1.Q, 1.e-3);
+  BOOST_CHECK_CLOSE_FRACTION(result_complex(0, 2).real(),
+                             stokes_nu2.I + stokes_nu2.Q, 1.e-3);
+}
+
+BOOST_FIXTURE_TEST_CASE(normal_with_rotation_cross, SpectrumFixture) {
+  spectrum.spectral_terms = {1.0e-6, 2.0e-6, 3.0e-6};
+  spectrum.has_logarithmic_spectral_index = true;
+  spectrum.has_rotation_measure = true;
+  spectrum.has_logarithmic_spectral_index = false;
+
+  dp3::base::PointSource point_source(dp3::base::Direction(0.0, 0.0),
+                                      spectrum.reference_flux);
+
+  point_source.setSpectralTerms(
+      spectrum.reference_frequency, spectrum.has_logarithmic_spectral_index,
+      spectrum.spectral_terms.begin(), spectrum.spectral_terms.end());
+
+  point_source.setRotationMeasure(spectrum.polarization_factor,
+                                  spectrum.polarization_angle,
+                                  spectrum.rotation_measure);
+  Stokes stokes_nu0(0, 0, 0, 0);
+  Stokes stokes_nu1(0, 0, 0, 0);
+  Stokes stokes_nu2(0, 0, 0, 0);
+
+  stokes_nu0 = point_source.stokes(frequencies[0]);
+  stokes_nu1 = point_source.stokes(frequencies[1]);
+  stokes_nu2 = point_source.stokes(frequencies[2]);
+
+  xt::xtensor<std::complex<double>, 2> result_complex;
+  spectrum.EvaluateCrossCorrelations(frequencies, result_complex);
+
+  BOOST_CHECK_CLOSE_FRACTION(result_complex(0, 0).real(),
+                             stokes_nu0.I + stokes_nu0.Q, 1.e-3);
+  BOOST_CHECK_CLOSE_FRACTION(result_complex(0, 1).real(),
+                             stokes_nu1.I + stokes_nu1.Q, 1.e-3);
+  BOOST_CHECK_CLOSE_FRACTION(result_complex(0, 2).real(),
+                             stokes_nu2.I + stokes_nu2.Q, 1.e-3);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file