Languages

CommunityCategory: XMODELSciPy를 활용하여 필터 계수 설계하는 법

XMODEL

SciPy를 활용하여 필터 계수 설계하는 법

SA Support Team Staff 2019-05-24

SciPy의 signal processing 라이브러리를 활용하면 Butterworth, Bessel, Chebyshev I/II, elliptic 등 다양한 타잎의 필터들을 조건에 맞게 설계할 수 있습니다. 이렇게 설계한 필터의 계수들을 XMODEL의 filter primitive의 파라메터로 옮기는 방법을 예로 보여줄 수 있나요?

2 Answers
Best Answer
SA Support Team Staff 2019-05-24

물론입니다. 아래는 scipy.signal 라이브러리의 cheb2ord()cheby2() 함수를 사용해서, passband와 stopband 조건을 충족하는 Chebyshev-II type low-pass 필터를 설계하는 예입니다. 이 함수들의 보다 자세한 설명은 SciPy 문서를 참고하세요 (https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.signal.cheb2ord.html).

#!/usr/bin/env xmodelpy

import scipy.signal
import numpy as np

# design of low-pass chebyshev-II filter
wp = 2*np.pi * 0.9e6    # passband edge frequency (rad/s)
ws = 2*np.pi * 1.1e6    # stopband edge frequency (rad/s)
rp = 3                  # maximum attenuation within pass band in dB
rs = 40                 # minimum attenuation within stop band in dB

N, wn = scipy.signal.cheb2ord(wp, ws, rp, rs, analog=True)
z,p,k = scipy.signal.cheby2(N, rs, wn, 'lowpass', analog=True, output='zpk')

그리고 아래는 Chebyshev-II type의 band-pass 필터를 설계하는 예입니다.

# design of bass-pass chebyshev-II filter
wp = [2*np.pi*1.1e6, 2*np.pi*2.9e6]     # passband edge frequency (rad/s)
ws = [2*np.pi*0.9e6, 2*np.pi*3.1e6]     # stopband edge frequency (rad/s)
rp = 3                                  # maximum attenuation within pass band in dB
rs = 40                                 # minimum attenuation within stop band in dB

N, wn = scipy.signal.cheb2ord(wp, ws, rp, rs, analog=True)
z,p,k = scipy.signal.cheby2(N, rs, wn, 'bandpass', analog=True, output='zpk')

비슷한 방법으로 SciPy 라이브러리의 cheb1ord()와 cheby1() 함수를 사용해서 Chebyshev-I 타잎의 필터를, buttord()butter() 함수를 사용해서 Butterworth 타잎 필터를, 그리고 ellipord()ellip() 함수를 사용해서 elliptic 타잎 필터를 각각 설계할 수 있습니다. 위키피디아 문서에는 이들 선형 필터들의 차이가 잘 설명되어 있으니 참고하시기 바랍니다 (https://en.wikipedia.org/wiki/Chebyshev_filter).

위의 예제에서 z, p, k 변수의 결과값은 각각 zero들의 주파수, pole들의 주파수, 그리고 gain값에 해당합니다. 다만, 이들 값을 XMODEL의 filter primitive에서 사용하는 'gain', 'poles', 'zeros' 파라메터들의 값으로 사용하기 위해서는 아래의 몇가지 사항들을 고려하셔야 합니다.

1. XMODEL filter primitive의 poles와 zeros 파라메터에서는 pole과 zero의 주파수를 Hertz 단위로 표현합니다. SciPy의 결과값은 rad/s 단위로 표현되어 있습니다.
2. XMODEL filter primitive에서는 pole과 zero의 주파수의 실수부 부호가 반대입니다. 즉, XMODEL은 left-half plane (LHP) pole/zero의 실수부를 양수로, right-half plane (RHP) pole/zero의 실수부를 음수로 표현하는데 반해, SciPy는 LHP pole/zero의 실수부를 음수로, RHP pole/zero의 실수부를 양수로 표현합니다.
3. XMODEL filter primitive의 poles와 zeros 파라메터에서는 각 pole 또는 zero 주파수의 실수부와 허수부를 아래처럼 별도 항목으로 나열합니다. 이는 SystemVerilog가 복소수를 직접 표현하지 못하기 때문입니다.

poles = '{real(fp1), imag(fp1), real(fp2), imag(fp2), ..., real(fpM), imag(fpM)}
zeros = '{real(fz1), imag(fz1), real(fz2), imag(fz2), ..., real(fzN), imag(fzN)}

4. XMODEL filter primitive의 poles와 zeros 파라메터에 나열된 복소수 값의 pole/zero들은 반드시 정확한 conjugate 쌍을 이루어야 합니다.
5. XMODEL filter primitive에서는 zero가 전혀 없는 전달함수를 표현하기 위해서 zeros 파라메터를 '{0.0}으로 정의합니다.
6. XMODEL filter primitive는 strictly proper한 시스템만 구현할 수 있기 때문에, pole의 수가 항상 zero의 수보다 많아야 합니다. 이 조건을 만족시키기 위해, 충분히 높은 주파수를 가져 필터 특성에 영향을 주지 않는 dummy pole을 추가할 수도 있습니다 (예를 들면 1THz pole).

예를 들면, 아래의 z, p, k 값들이 구해지면:

z = [ -3.72529030e-09+16418754.44763247j  -5.23868948e-09-16418754.44763249j
        1.92618690e-09 +6800870.76948295j   7.78721962e-11 -6800870.76948295j]
p = [ -1075430.76302337+2991438.64240786j -3170099.69183802+1512931.2492777j
      -3170099.69183801-1512931.2492777j  -1075430.76302337-2991438.64240787j]
k = (0.01+0j)

이를 아래의 gain, poles, zeros 파라메터 값을 갖는 filter primitive 인스턴스로 변환할 수 있습니다:

filter #(.gain(0.01),
    .poles('{171160, 476102, 504537, 240790, 504537, -240790, 171160, -476102, 1e12, 0.0}),
    .zeros('{0.0, 2613125.9, 0.0, -2613125.9, 0.0, 1082392.2, 0.0, -1082392.2})) 
    filter_inst (.in(in), .out(out));

복소수 pole 또는 zero들이 conjugate 쌍끼리 모이도록 배치를 좀 바꾸었습니다. 그리고, strictly proper한 시스템을 위해서, 다른 pole/zero 주파수들보다 월등히 높은 주파수인 1THz에 dummy pole을 추가하였습니다.

아래 Python 스크립트는 z, p, k 값을 XMODEL filter primitive의 gain, poles, zeros 파라메터 값으로 변환하는 한가지 예입니다.

    def sort_pz(p):
        res = []
        for w in sorted(p, key=np.real):
            f = w / (2*np.pi)
            if f.imag == 0.0:
                res += [f.real, 0.0]
            elif f.imag > 0.0:
                res += [f.real, f.imag, f.real, -f.imag]
        return res

    gain = (k / np.prod(-p) * np.prod(-z)).real
    poles = sort_pz(-p)
    zeros = sort_pz(-z)
    if len(p) <= len(z): poles += [1e12, 0.0] * (len(z)-len(p)+1)
    if len(z) == 0: zeros += [0.0]

    print("gain  : %g" % gain)
    print("poles : %s" % ', '.join(['%g' % v for v in poles]))
    print("zeros : %s" % ', '.join(['%g' % v for v in zeros]))
SA Support Team Staff 2022-09-30

첨부의 filter_design.py 파이썬 스크립트는 원하는 필터의 스펙을 옵션으로 지정하면, 그 스펙을 만족하는 필터를 설계하고, 그 필터의 전달함수를 XMODEL 'filter' primitive의 파라메터 값으로 출력해줍니다. 따라서, 이 스크립트를 사용하면 좀더 간편하게 원하는 필터들을 XMODEL 상에서 모델링할 수 있습니다.

우선, 이 filter_design.py스크립트의 사용법은 커맨드라인에서 '-h' 옵션을 주어 실행하면 화면에 표시됩니다.

xmodelpy filter_design.py -h

실행결과:

Usage: filter_design.py [options]
Design a common-class filter.

Options:
  -h, --help            show this help message and exit
  -f FREQ_or_FREQ1,FREQ2, --freq=FREQ_or_FREQ1,FREQ2
                        Critical frequency or frequencies in Hz (default:
                        1.0). For lowpass and highpass filters, it is a single
                        frequency value defining the cut-off; for bandpass and
                        bandstop filters, it is a pair of frequency values
                        separated by a comma (,) defining the low-side and
                        high-side cut-offs.
  -n ORDER, --order=ORDER
                        Order of the filter (default: 2).
  -t FILTER_TYPE, --type=FILTER_TYPE
                        Filter type (choices: lowpass, highpass, bandpass,
                        bandstop); the default is 'lowpass'.
  -c FILTER_CLASS, --class=FILTER_CLASS
                        Filter class (choices: butter, cheby1, cheby2, ellip,
                        bessel); the default is 'butter'.
  --rpass=MAX_ATTEN_PASS, --ripple-pass=MAX_ATTEN_PASS
                        Maximum attenuation allowed in passband in dB
                        (default: 3.0); used for cheby1 and ellip filters.
  --rstop=MIN_ATTEN_STOP, --ripple-stop=MIN_ATTEN_STOP
                        Minimum attenuation required in stopband in dB
                        (default: 30.0); used for cheby2 and ellip filters.
  --plot, --show-plot   Show the bode plot of the designed filter.

아래 예제는 1GHz의 cut-off 주파수를 갖는 low-pass 타입의 4차 Bessel 필터를 설계하는 예입니다. 마지막에 쓰인 --plot 옵션을 사용하면, 스크립트는 설계된 필터의 전달함수를 Bode plot 형식으로 표시해줍니다.

xmodelpy filter_design.py --freq 1e9 --type lowpass --order 4 --class bessel --plot

실행결과:

*** FILTER PRIMITIVE PARAMETERS:
gain  : 1
poles : 6.57211e+08, 8.30161e+08, 6.57211e+08, -8.30161e+08, 9.04759e+08, 2.70919e+08, 9.04759e+08, -2.70919e+08
zeros : 0

다음 예제는 0.1GHz와 1GHz의 cut-off 주파수를 갖는 band-pass 타입의 6차 Elliptic 필터를 설계하는 예입니다. Elliptic 필터의 경우, passband와 stopband의 gain에 ripple이 있을 수 있는데, '--rpass' 옵션은 passband에서 허용되는 최대 감쇄이득을, '--rstop' 옵션은 stopband에서 허용되는 최소 감쇄이득을 dB 단위로 정의합니다.

xmodelpy filter_design.py --freq 0.1e9,1e9 --type bandpass --order 6 --class ellip \
--rpass 3 --rstop 40 --plot

실행결과:

*** FILTER PRIMITIVE PARAMETERS:
gain  : 0.0100029
poles : 964446, 1.00503e+08, 964446, -1.00503e+08, 6.5194e+06, 1.10333e+08, 6.5194e+06, -1.10333e+08, 9.54733e+06, 9.94906e+08, 9.54733e+06, -9.94906e+08, 4.35826e+07, 1.71065e+08, 4.35826e+07, -1.71065e+08, 5.33682e+07, 9.03193e+08, 5.33682e+07, -9.03193e+08, 1.39856e+08, 5.48943e+08, 1.39856e+08, -5.48943e+08, 1e+12, 0
zeros : -4.37263e-08, 2.41733e+09, -4.37263e-08, -2.41733e+09, -4.08238e-08, 9.45706e+07, -4.08238e-08, -9.45706e+07, -2.99276e-08, 1.05741e+09, -2.99276e-08, -1.05741e+09, 5.3513e-09, 4.1368e+07, 5.3513e-09, -4.1368e+07, 1.45881e-07, 8.45223e+07, 1.45881e-07, -8.45223e+07, 4.9254e-07, 1.18312e+09, 4.9254e-07, -1.18312e+09

Attachment: filter_design.py