From 52c61ee9f6cbb38d119f7878d9f96e191ea9d168 Mon Sep 17 00:00:00 2001 From: Eric Kooistra <kooistra@astron.nl> Date: Wed, 11 Dec 2024 13:11:03 +0100 Subject: [PATCH] Update TODO in non_maximal_upsample_bpf(). --- .../lofar2/model/rtdsp/singlechannel.py | 102 ++++++++---------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/applications/lofar2/model/rtdsp/singlechannel.py b/applications/lofar2/model/rtdsp/singlechannel.py index ced9bcf3b2..e51cbac059 100644 --- a/applications/lofar2/model/rtdsp/singlechannel.py +++ b/applications/lofar2/model/rtdsp/singlechannel.py @@ -264,28 +264,52 @@ def non_maximal_downsample_bpf(x, Ndown, k, Ndft, coefs, verbosity=1): def non_maximal_upsample_bpf(xBase, Nup, k, Ndft, coefs, verbosity=1): """BPF xBase at bin k in range(Nup), and upsample xBase by factor U = Nup. - Implement nonmaximal upsampling upconverter for one bin. The maximal - upsampling upconverter for one bin has the kPhasors per polyphase - branch similar of [HARRIS Fig 7.16]. For nonmaximal this needs to be - extended with the tPhasors per polyphase branch similar as for down in - [HARRIS Eq. 9.3], to compensate for the oversampling time shift. - - The BPF is centered at w_k = 2pi k / Ndft, where Ndft is number of - frequency bins, is DFT size. The polyphase FIR structure has Nphases = Ndft - branches, to fit the requested number of bins. The polyphase FIR structure - is maximally upsampled (= critically sampled) for Nup = Ndft, but it - can support any Nup <= Ndft. The output data shifts out per Nup samples, - so it appears from different branches when Nup < Ndft and a new block is - shifted out. Therefore the output data cannot be FIR filtered per branch - for the whole input xBase. Instead it needs to be FIR filtered per block of - Nup output samples, using pfs.polyDelays in pfs.filter_up(). - TODO - . This code only runs ok for Ros = 1, 2 when Ndft = 16, but not for - Ros = 4, so need to fix that. According to [CROCHIERE 7.2.7] the - polyphase structure is only suitable for Ros is integer >= 1. For other - Ros > 1 the weighted overlap-add (WOLA) structure is suitable, so - need to add WOLA. + . According to [CROCHIERE 7.2.7] the polyphase structure is only suitable + for Ros is integer >= 1. + . For other Ros > 1 the weighted overlap-add (WOLA) structure is suitable, + so need to add WOLA. + + Remark: + . It is difficult to transpose the WOLA approach to a PFS approach that + suits any Ros > 1. For example, with blocks a, b, c, d, f, ... of Ndft + samples and block a = a0 a1 for Ros = 2, and replicating the block Ntaps + = 2 times in time, WOLA using rows: + + a0 a1 a0 a1 + b0 b1 b0 b1 + c0 c1 c0 c1 + d0 d1 d0 d1 + e0 e1 e0 e1 + f0 f1 f0 f1 + g0 g1 g0 g1 --> time + + results in PFS using columns: + + d0 e0 f0 g0 + c1 d1 e1 f1 + b0 c0 d0 e0 + a1 b1 c1 d1 + + For fractional Ros, e.g. Ros = 4/3, it can be tried to find a pattern + processing per column, but that is much more complicated then processing + per row as with WOLA (Ntaps = 2): + + a0 a1 a2 a3 a0 a1 a2 a3 + b0 b1 b2 b3 b0 b1 b2 b3 + c0 c1 c2 c3 c0 c1 c2 c3 + d0 d1 d2 d3 d0 d1 d2 d3 + e0 e1 e2 e3 e0 e1 e2 e3 + f0 f1 f2 f3 f0 f1 f2 f3 + results in PFS using columns: + + c0 c1 c2 d0 d1 d2 e0 e1 e2 f0 f1 + b3 b0 b1 c3 c0 c1 d3 d0 d1 e3 e0 + a2 a3 b2 b3 c2 c3 d2 d3 + + Extra complication is for e.g. Ros = 32 / 27, because 32 does not divide + (32 - 27). Anyway, for fractional Ros PFS approach seems not useful, + because the PFS is then changing for every block in time. Input: . xBase: Input equivalent baseband signal xBase[m] @@ -305,46 +329,12 @@ def non_maximal_upsample_bpf(xBase, Nup, k, Ndft, coefs, verbosity=1): # print('WARNING: Only support integer Ros') # return None - Nblocks = len(xBase) - - # Prepare output - polyYc = np.zeros((Nup, Nblocks), dtype='cfloat') - - # Adjust LO phase for group delay of analysis LPF and synthesis LPF in - # series. This is not needed in practise, but is needed to be able to - # exactly compare reconstructed signal with original input time series - # signal. Assume coefs has group delay (len(coefs) - 1) / 2. - hPairGroupDelay = len(coefs) - 1 - hPhasor = np.exp(-2j * np.pi * k / Ndft * hPairGroupDelay) - - # Oversampling time shift compensation via frequency dependent phase shift - tPhasors = time_shift_phasor_arr(k, Nup, Ndft, Nblocks, -1) # [HARRIS Eq 9.3] - - # PFS with Ndft polyphases - pfs = PolyPhaseFirFilterStructure(Ndft, coefs, cmplx=True) - kPhasors = unit_circle_loops_phasor_arr(k, Ndft, 1) # [HARRIS Eq 7.8] - - # Polyphase FIR filter input xBase - for b in range(Nblocks): - # Phase rotate polyphases for bin k - inData = xBase[b] * hPhasor * tPhasors[b] * kPhasors - - # Filter block [HARRIS Fig 9.15, CROCHIERE Fig 7.16]] - pfsData = pfs.filter_block(inData) - - # Output Nup samples yc[n] for every input sample xBase[m] - polyYc[:, b] = Ndft * pfsData - - yc = polyYc.T.reshape(1, Nup * Nblocks)[0] - if verbosity: print('> non_maximal_upsample_bpf():') print(' . len(xBase) =', str(len(xBase))) - print(' . Nblocks =', str(Nblocks)) - print(' . len(yc) =', str(len(yc))) # = Nblocks print(' . Ros =', str(Ros)) print(' . Nup =', str(Nup)) print(' . Ndft =', str(Ndft)) print(' . k =', str(k)) print('') - return yc + return None -- GitLab