diff --git a/libraries/dsp/si/src/python/si.py b/libraries/dsp/si/src/python/si.py new file mode 100755 index 0000000000000000000000000000000000000000..7254574cb4c906f39b910421c1f7dad41ce3529d --- /dev/null +++ b/libraries/dsp/si/src/python/si.py @@ -0,0 +1,116 @@ +#! /usr/bin/env python3 +############################################################################### +# +# Copyright 2022 +# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############################################################################### + +# Author: Eric Kooistra +# Date: Nov 2022 +# Purpose: +# Derive RF frequency of subband center dependend on subband index, Nyquist +# zone and spectral inversion. +# Description: +# The > 1 st Nyquist zones are digitized using sub sampling. The analog +# filter in the receiver blocks the other Nyquist zones, so that only the +# wanted Nyquist zone is digitized. +# The sub sampling act as a mixer that shifts all Nyquist zones down to the +# first Nyquist zone. The sub sampling causes that the frequency band for +# all even Nyquist zones gets flipped from hi to lo. This frequency flip can +# be undone by enabling the spectral inversion for those even Nyquist zones. +# For ADC sample frequenc f_sample = 200 MHz: +# 1st Nyquist zone: f = 0 - 100 MHz +# 2nd Nyquist zone: f = 100 - 200 MHz +# 3rd Nyquist zone: f = 200 - 300 MHz + +import argparse +import textwrap + +import numpy as np +import matplotlib +matplotlib.use('tkagg') +import matplotlib.pyplot as plt + +# Parse arguments to derive user parameters +_parser = argparse.ArgumentParser( + description="".join(textwrap.dedent("""\ + Calculate RF center frequency for subbands dependend on the Nyquist + zone and the spectral inversion control. + + Apply spectral inversion for 2nd Nyquist zone to have increasing + subband index correspond to increasing RF frequency: + + > python si.py --si 0 --zi 0 -N 16 + > python si.py --si 1 --zi 1 -N 16 + > python si.py --si 0 --zi 2 -N 16 + + \n""")), + formatter_class=argparse.RawTextHelpFormatter) +_parser.add_argument('--si', default=0, type=int, help='Spectral inversion control 0 = keep band, 1 = flip band') +_parser.add_argument('--zi', default=0, type=int, help='Nyquist zone index 0 = 1st, 1 = 2nd, 2 = 3rd, etc') +_parser.add_argument('-N', default=512, type=int, help='N_sub') +args = _parser.parse_args() + +spectral_inv = args.si +nyquist_zone_index = args.zi +N_sub = args.N + +subbands = np.arange(N_sub) # subband indices + +# SDP parameters +N_complex = 2 # Two complex parts, real and imag +N_fft = N_sub * N_complex # Number of points of subband filterbank FFT +f_sample = 200e6 # Hz (ADC sample frequency) +BW_RF = f_sample / N_complex # = 100 MHz, sampled RF band width of each + # Nyquist zone, +f_sub = f_sample / N_fft # = 195312.5 Hz, subband frequency + +# Determine netto result of sub sampling and spectral inversion control +zone_inv = nyquist_zone_index % N_complex + +def boolean_xor(a, b): + return (a and not b) or (b and not a) + +def natural_xor(a, b): + if boolean_xor(a, b): + return 1 + else: + return 0 + +bw_inv = natural_xor(spectral_inv, zone_inv) + +# Subband center RF frequency +f_lo = nyquist_zone_index * BW_RF + +n = subbands +if bw_inv: + n = N_sub - 1 - subbands + +f_sub_rf = f_lo + n * f_sub + +# Plot results +figNr = 0 + +figNr += 1 +plt.figure(figNr) +plt.plot(subbands, f_sub_rf / 1e6, 'o') +plt.title("spectral_inv = %d, nyquist_zone_index = %d" % (spectral_inv, nyquist_zone_index)) +plt.xlabel("Subband index (range 0:%d)" % (N_sub - 1)) +plt.ylabel("Subband RF frequency [MHz]") +plt.grid() + +plt.show()