diff --git a/applications/lofar2/model/rtdsp/multirate.py b/applications/lofar2/model/rtdsp/multirate.py
index 908dff640294a7290d6c7684a629d7eeee4af7d1..d2481d2c6b7beee69246ecc72cbbed5f96a9290b 100644
--- a/applications/lofar2/model/rtdsp/multirate.py
+++ b/applications/lofar2/model/rtdsp/multirate.py
@@ -612,13 +612,35 @@ def resample(x, Nup, Ndown, coefs, verify=False, verbosity=1):  # interpolate an
 # Single bandpass channel up and down sampling and up and down conversion
 ###############################################################################
 
-def phasor_arr(k, Ndft, sign):
-    """Return array of phasors: exp(+-j 2pi k / Ndft) for k in 0 : Ndft - 1
+def unit_circle_loops_phasor_arr(k, N, sign):
+    """Return array of N phasors on k loops along the unit circle.
+
+    Polyphase dependent phase offsets for bin k, when N = Ndown = Ndft [HARRIS
+    Eq 6.8]. For k = 1 this yields the roots of unity.
+
+    Return:
+    . pArr: exp(+-j 2pi k / N) for k in 0 : N - 1
     """
     if sign == 'positive':
-        return np.array([np.exp(2j * np.pi * p * k / Ndft) for p in range(Ndft)])
+        pArr = np.array([np.exp(2j * np.pi * p * k / N) for p in range(N)])
     else:  # 'negative'
-        return np.array([np.exp(-2j * np.pi * p * k / Ndft) for p in range(Ndft)])
+        pArr = np.array([np.exp(-2j * np.pi * p * k / N) for p in range(N)])
+    return pArr
+
+
+def time_shift_phasor_arr(k, Ndown, Ndft, Msamples):
+    """Return array of Msamples phasors in time to compensate for oversampling
+    time shift.
+
+    The time shift due to downsampling causes a frequency component of
+    k * Ndown / Ndft. With oversampling Ndown < Ndft, and then after
+    downsampling there remains a frequency offset [HARRIS Eq 9.3].
+
+    Return:
+    . mArr = exp(2j pi k * Ndown / Ndft * m),  for m in 0 : Msamples - 1
+    """
+    mArr = np.exp(-2j * np.pi * k * Ndown / Ndft * np.arange(Msamples))
+    return mArr
 
 
 def maximal_downsample_bpf(x, Ndown, k, coefs, verbosity=1):
@@ -649,9 +671,9 @@ def maximal_downsample_bpf(x, Ndown, k, coefs, verbosity=1):
     # Phase rotate per polyphase for bin k, due to delay line at branch inputs
     # [HARRIS Eq 6.8]
     polyYC = np.zeros((Ndown, Nxp), dtype='cfloat')
-    phasors = phasor_arr(k, Ndown, 'positive')
+    kPhasors = unit_circle_loops_phasor_arr(k, Ndown, 'positive')
     for p in range(Ndown):
-        polyYC[p] = polyY[p] * phasors[p]  # row = row * scalar
+        polyYC[p] = polyY[p] * kPhasors[p]  # row = row * scalar
 
     # Sum the branch outputs to get single downsampled and downconverted output
     # complex baseband value yc.
@@ -707,15 +729,19 @@ def non_maximal_downsample_bpf(x, Ndown, k, Ndft, coefs, verbosity=1):
 
     # PFS with Ndft polyphases
     pfs = PolyPhaseFirFilterStructure(Ndft, coefs)
-    phasors = phasor_arr(k, Ndft, 'positive')
+    kPhasors = unit_circle_loops_phasor_arr(k, Ndft, 'positive')  # [HARRIS Eq 6.8]
+
+    # Oversampling time shift compensation via frequency dependent phase shift
+    tPhasors = time_shift_phasor_arr(k, Ndown, Ndft, Nblocks)  # [HARRIS Eq 9.3]
 
     for b in range(Nblocks):
         # Filter block
         inData = xBlocks[:, b]
         pfsData = pfs.filter_block(inData, flipped=True)
-        # Phase rotate polyphases for bin k [HARRIS Eq 6.8]
-        pfsBinData = pfsData * phasors
-        # Sum the polyphases to get single downsampled and downconverted output value
+        # Phase rotate polyphases for bin k
+        pfsBinData = pfsData * kPhasors * tPhasors[b]  # [HARRIS Eq 6.8, 9.3]
+        # Sum the polyphases to get single downsampled and downconverted output
+        # value
         yc[b] = np.sum(pfsBinData)
 
     if verbosity: