Search RFC: |                                     
Please support my efforts by ADVERTISING!
About | Sitemap | Homepage Archive
Serving a Pleasant Blend of Yesterday, Today, and Tomorrow™
Vintage Magazines
Electronics World
Popular Electronics
Radio & TV News
QST | Pop Science
Popular Mechanics
Radio-Craft
Radio-Electronics
Short Wave Craft
Electronics | OFA
Saturday Eve Post
Alliance Test | Isotec
Please Support My Advertisers!
RF Cafe Sponsors
Aegis Power | Centric RF | RFCT
Empwr RF | Reactel | SF Circuits

Formulas & Data

Electronics | RF
Mathematics
Mechanics | Physics


Calvin & Phineas

kmblatt83@aol.com

Resources

Articles, Forums, Radar
Magazines, Museum
Radio Service Data
Software, Videos


Artificial Intelligence

Entertainment

Crosswords, Humor Cogitations, Podcast
Quotes, Quizzes

Parts & Services

1000s of Listings

        Software:

Please Donate
RF Cascade Workbook | RF Symbols for Office
RF Symbols for Visio | RF Stencils for Visio
Espresso Engineering Workbook
Werbel Microwave power dividers, couplers - RF Cafe

Chebyshev Filter Equations for Magnitude, Phase, and Group Delay

Espresso Engineering Workbook: Chebyshev Filter Calculator - RF Cafe

Chebyshev Filter Calculator in RF Cafe's Espresso Engineering Workbook, created by Kirt Blattenberger, 2/2/2026.

For decades - literally - I searched in vain for explicit equations for calculating Chebyshev filter phase and group delay, but could never find more than textbook definitions, with instruction to extract phase from the real and imaginary parts of the magnitude equation, and then take the negative first derivative of the phase to get group delay. A lot of good that did - not! I have perused dozens of filter design books, to no avail. Even the filter bible - Zverev's Handbook of Filter Synthesis - did not provide the needed equations. Most online resources present Mathcad, MATLAB, Mathematica, or similar scripts that call the built-in functions, without exposing the gory detail behind them. What I wanted was something I could implement in a spreadsheet or a program.

Finally, with the help of AI (through many iterations of revisions to its mostly erroneous results), I believe I have come up with equations which can be implemented in relatively simple code. In fact, they now live in my Espresso Engineering Workbook (free download). Lowpass, highpass, bandpass, and bandstop filters are included. I checked the lowpass Chebyshev lowpass filter (cutoff frequency of ω = 1 radian/second) group delay against the Zverev response graphs for 0.01 dB and 0.1 dB passband ripple, and got excellent agreement. Please let me know if you experience a problem.

Until people start copying and republishing my work, this is probably the only place you will find this on the Internet. I had Archive.org save a copy of this page to prove my original work. You're welcome.

First, here is a simple explanation of how it works, then the full VBA code is presented.

 

*******************************************************************************

This is a robust, self-contained Excel 2007 VBA suite of functions for analyzing analog Chebyshev Type-I filters (ripple in the passband). It provides magnitude (dB), phase (degrees), and group delay (seconds) for all four standard configurations - lowpass, highpass, bandpass, and bandstop - without requiring any external add-ins or worksheet function calls beyond basic math utilities.

Key Technical Features

  •  Classical Design: The code implements standard analog filter theory. It calculates the ripple parameter ε from the passband ripple (r in dB), distributes N poles on a Chebyshev ellipse in the left-half plane, and applies frequency transformations to derive highpass, bandpass, and bandstop poles.
  • Pole-Sum Method: For phase and group delay, the functions sum contributions from each individual pole (vector geometry). Group delay uses the formula –Re(pole)/|jω – pole|², while phase uses the angle of the vector from the pole to the imaginary axis.
  • Complex Arithmetic: Bandpass and bandstop transformations require solving quadratics with complex coefficients. An inlined a complex square-root algorithm (algebraic method) to keep the code self-contained and fast.
  • Numerical Stability: The bandstop magnitude calculation works in the log-domain (summing log-distances to poles/zeros) to prevent floating-point overflow with high-order filters. Phase results are wrapped to the ±180° range.
  • Excel Optimization: All loops use scalar Double variables (no slow array allocation), and constants (π, ln 10) are hard-coded to avoid repeated lookups. The later functions avoid WorksheetFunction calls entirely, improving recalculation speed.

*******************************************************************************

 

'======================================================================
' FULL SELF-CONTAINED CHEBYSHEV TYPE-I FILTER SUITE (Hz inputs)
' GD(sec)/PH(deg)/Mag(dB) for LP/HP/BP/BS. NO EXTERNAL CALLS. MAX SPEED.
' Inline CSqrt (alg), ChebyT (recursive). Analytical Mag (fastest/exact).
' Pole-sum GD/PH (unavoidable).  Copyright Kirt Blattenberger February 2026
'=======================================================================

'===============================================================================
' INLINE CSQRT (ALG): Used only in BP/BS GD/PH (copied per func for no calls)
'===============================================================================
' Private helper not needed: duplicated inline.

'************************************
'******** CHEBYSHEV LOWPASS ********
'************************************

Function ChebyLPGroupDelay(f As Double, fCo As Double, r As Double, N As Integer) As Double
Const PI As Double = 3.14159265358979
Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, omega As Double, OmegaCo As Double
Dim s As Double, theta As Double, sinh_val As Double, cosh_val As Double
Dim alpha As Double, beta As Double, pole_real As Double, pole_imag As Double
Dim numer As Double, denom As Double, GD_sum As Double, k As Integer
If fCo <= 0 Or N <= 0 Or r <= 0 Then ChebyLPGroupDelay = 0: Exit Function
OmegaCo = TWOPI * fCo: omega = TWOPI * f
epsilon = Sqr(10 ^ (r / 10) - 1)
s = Application.WorksheetFunction.Asinh(1 / epsilon) / N
sinh_val = Application.WorksheetFunction.Sinh(s): cosh_val = Application.WorksheetFunction.Cosh(s)
GD_sum = 0
For k = 1 To N
theta = PI * (2 * k - 1) / (2 * N)
alpha = -sinh_val * Sin(theta): beta = cosh_val * Cos(theta)
pole_real = alpha * OmegaCo: pole_imag = beta * OmegaCo
numer = pole_real: denom = pole_real ^ 2 + (omega - pole_imag) ^ 2
GD_sum = GD_sum + numer / denom
Next k
ChebyLPGroupDelay = -GD_sum
End Function

Function ChebyLPPhase(f As Double, fCo As Double, r As Double, N As Integer) As Double
Const PI As Double = 3.14159265358979
Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, omega As Double, OmegaCo As Double
Dim s As Double, theta As Double, sinh_val As Double, cosh_val As Double
Dim alpha As Double, beta As Double, pole_real As Double, pole_imag As Double
Dim phase_sum As Double, angle_k As Double, k As Integer
If fCo <= 0 Or N <= 0 Or r <= 0 Then ChebyLPPhase = 0: Exit Function
OmegaCo = TWOPI * fCo: omega = TWOPI * f
epsilon = Sqr(10 ^ (r / 10) - 1)
s = Application.WorksheetFunction.Asinh(1 / epsilon) / N
sinh_val = Application.WorksheetFunction.Sinh(s): cosh_val = Application.WorksheetFunction.Cosh(s)
phase_sum = 0
For k = 1 To N
theta = PI * (2 * k - 1) / (2 * N)
alpha = -sinh_val * Sin(theta): beta = cosh_val * Cos(theta)
pole_real = alpha * OmegaCo: pole_imag = beta * OmegaCo
angle_k = Application.WorksheetFunction.Atan2((omega - pole_imag), -pole_real)
phase_sum = phase_sum + angle_k
Next k
ChebyLPPhase = -phase_sum * 180 / PI
Do While ChebyLPPhase > 180: ChebyLPPhase = ChebyLPPhase - 360: Loop
Do While ChebyLPPhase < -180: ChebyLPPhase = ChebyLPPhase + 360: Loop
End Function

Function ChebyLPMag(f As Double, fCo As Double, r As Double, N As Integer) As Double
Const LN10 As Double = 2.30258509299405: Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, zeta As Double, arg As Double, omega As Double, OmegaCo As Double
Dim T0 As Double, T1 As Double, Tn As Double, i As Integer
If fCo <= 0 Or N <= 0 Or r <= 0 Then ChebyLPMag = 0: Exit Function
OmegaCo = TWOPI * fCo: omega = TWOPI * f
epsilon = Sqr(10 ^ (r / 10) - 1): zeta = omega / OmegaCo
If N = 0 Then arg = 1#: GoTo MagLog
If N = 1 Then Tn = zeta: GoTo MagLog
T0 = 1#: T1 = zeta
For i = 2 To N
Tn = 2# * zeta * T1 - T0: T0 = T1: T1 = Tn
Next i
MagLog:
arg = 1# + epsilon * epsilon * Tn * Tn
ChebyLPMag = -10# * Log(arg) / LN10
End Function

'************************************
'******** CHEBYSHEV HIGHPASS ********
'************************************

Function ChebyHPGroupDelay(f As Double, fCo As Double, r As Double, N As Integer) As Double
Const PI As Double = 3.14159265358979: Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, omega As Double, OmegaCo As Double
Dim s As Double, theta As Double, sinh_val As Double, cosh_val As Double
Dim alpha As Double, beta As Double, mag2 As Double
Dim pole_real As Double, pole_imag As Double, numer As Double, denom As Double, GD_sum As Double, k As Integer
If fCo <= 0 Or N <= 0 Or r <= 0 Then ChebyHPGroupDelay = 0: Exit Function
OmegaCo = TWOPI * fCo: omega = TWOPI * f
epsilon = Sqr(10 ^ (r / 10) - 1)
s = Application.WorksheetFunction.Asinh(1 / epsilon) / N
sinh_val = Application.WorksheetFunction.Sinh(s): cosh_val = Application.WorksheetFunction.Cosh(s)
GD_sum = 0
For k = 1 To N
theta = PI * (2 * k - 1) / (2 * N)
alpha = -sinh_val * Sin(theta): beta = cosh_val * Cos(theta)
mag2 = alpha * alpha + beta * beta
pole_real = OmegaCo * alpha / mag2: pole_imag = OmegaCo * (-beta) / mag2
numer = pole_real: denom = pole_real ^ 2 + (omega - pole_imag) ^ 2
GD_sum = GD_sum + numer / denom
Next k
ChebyHPGroupDelay = -GD_sum
End Function

Function ChebyHPPhase(f As Double, fCo As Double, r As Double, N As Integer) As Double
Const PI As Double = 3.14159265358979: Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, omega As Double, OmegaCo As Double
Dim s As Double, theta As Double, sinh_val As Double, cosh_val As Double
Dim alpha As Double, beta As Double, mag2 As Double
Dim pole_real As Double, pole_imag As Double, phase_sum As Double, angle_k As Double, k As Integer
If fCo <= 0 Or N <= 0 Or r <= 0 Then ChebyHPPhase = 0: Exit Function
OmegaCo = TWOPI * fCo: omega = TWOPI * f
epsilon = Sqr(10 ^ (r / 10) - 1)
s = Application.WorksheetFunction.Asinh(1 / epsilon) / N
sinh_val = Application.WorksheetFunction.Sinh(s): cosh_val = Application.WorksheetFunction.Cosh(s)
phase_sum = 0
For k = 1 To N
theta = PI * (2 * k - 1) / (2 * N)
alpha = -sinh_val * Sin(theta): beta = cosh_val * Cos(theta)
mag2 = alpha * alpha + beta * beta
pole_real = OmegaCo * alpha / mag2: pole_imag = OmegaCo * (-beta) / mag2
angle_k = Application.WorksheetFunction.Atan2((omega - pole_imag), -pole_real)
phase_sum = phase_sum + angle_k
Next k
ChebyHPPhase = -phase_sum * 180 / PI
Do While ChebyHPPhase > 180: ChebyHPPhase = ChebyHPPhase - 360: Loop
Do While ChebyHPPhase < -180: ChebyHPPhase = ChebyHPPhase + 360: Loop
End Function

Function ChebyHPMag(f As Double, fCo As Double, r As Double, N As Integer) As Double
Const LN10 As Double = 2.30258509299405: Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, zeta As Double, arg As Double, omega As Double, OmegaCo As Double
Dim T0 As Double, T1 As Double, Tn As Double, i As Integer
If fCo <= 0 Or N <= 0 Or r <= 0 Then ChebyHPMag = 0: Exit Function
OmegaCo = TWOPI * fCo: omega = TWOPI * f
If omega = 0 Then ChebyHPMag = -200: Exit Function
epsilon = Sqr(10 ^ (r / 10) - 1): zeta = OmegaCo / omega
If N = 0 Then arg = 1#: GoTo MagLogHP
If N = 1 Then Tn = zeta: GoTo MagLogHP
T0 = 1#: T1 = zeta
For i = 2 To N
Tn = 2# * zeta * T1 - T0: T0 = T1: T1 = Tn
Next i
MagLogHP:
arg = 1# + epsilon * epsilon * Tn * Tn
ChebyHPMag = -10# * Log(arg) / LN10
End Function

'************************************
'******** CHEBYSHEV BANDPASS ********
'************************************

Function ChebyBPMag(f As Double, fL As Double, fU As Double, r As Double, N As Integer) As Double
Const LN10 As Double = 2.30258509299405: Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, zeta As Double, arg As Double, omega As Double, OmegaL As Double, OmegaU As Double, w0 As Double, BW As Double
Dim T0 As Double, T1 As Double, Tn As Double, i As Integer
If fL <= 0 Or fU <= fL Or N <= 0 Or r <= 0 Then ChebyBPMag = 0: Exit Function
OmegaL = TWOPI * fL: OmegaU = TWOPI * fU: omega = TWOPI * f
If omega = 0 Then ChebyBPMag = -200: Exit Function
w0 = Sqr(OmegaL * OmegaU): BW = OmegaU - OmegaL
epsilon = Sqr(10 ^ (r / 10) - 1): zeta = (omega * omega - w0 * w0) / (BW * omega)
If N = 0 Then arg = 1#: GoTo BPMagLog
If N = 1 Then Tn = zeta: GoTo BPMagLog
T0 = 1#: T1 = zeta
For i = 2 To N
Tn = 2# * zeta * T1 - T0: T0 = T1: T1 = Tn
Next i
BPMagLog:
arg = 1# + epsilon * epsilon * Tn * Tn
ChebyBPMag = -10# * Log(arg) / LN10
End Function

Function ChebyBPGroupDelay(f As Double, fL As Double, fU As Double, r As Double, N As Integer) As Double
Const PI As Double = 3.14159265358979: Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, omega As Double, OmegaL As Double, OmegaU As Double, w0 As Double, BW As Double
Dim lp_s As Double, theta As Double, sinh_val As Double, cosh_val As Double
Dim alpha As Double, beta As Double, gamma_re As Double, gamma_im As Double
Dim delta_re As Double, delta_im As Double, mag As Double, sqrt_re As Double, sqrt_im As Double
Dim p1_re As Double, p1_im As Double, p2_re As Double, p2_im As Double
Dim GD_contrib As Double, GD_sum As Double, k As Integer
If fL <= 0 Or fU <= fL Or N <= 0 Or r <= 0 Then ChebyBPGroupDelay = 0: Exit Function
OmegaL = TWOPI * fL: OmegaU = TWOPI * fU: omega = TWOPI * f
w0 = Sqr(OmegaL * OmegaU): BW = OmegaU - OmegaL
epsilon = Sqr(10 ^ (r / 10) - 1)
lp_s = Application.WorksheetFunction.Asinh(1 / epsilon) / N
sinh_val = Application.WorksheetFunction.Sinh(lp_s): cosh_val = Application.WorksheetFunction.Cosh(lp_s)
GD_sum = 0
For k = 1 To N
theta = PI * (2 * k - 1) / (2 * N)
alpha = -sinh_val * Sin(theta): beta = cosh_val * Cos(theta)
gamma_re = BW * alpha: gamma_im = BW * beta
delta_re = gamma_re * gamma_re - gamma_im * gamma_im - 4 * w0 * w0
delta_im = 2 * gamma_re * gamma_im
' INLINE CSQRT: principal Re>=0
mag = Sqr(delta_re * delta_re + delta_im * delta_im)
If mag = 0 Then
sqrt_re = 0
sqrt_im = 0
Else
sqrt_re = Sqr((mag + delta_re) / 2)
If delta_im < 0 Then
sqrt_im = -Sqr((mag - delta_re) / 2)
Else
sqrt_im = Sqr((mag - delta_re) / 2)
End If
End If
p1_re = (gamma_re + sqrt_re) / 2: p1_im = (gamma_im + sqrt_im) / 2
p2_re = (gamma_re - sqrt_re) / 2: p2_im = (gamma_im - sqrt_im) / 2
If p1_re >= 0 Or p2_re >= 0 Then
sqrt_re = -sqrt_re: sqrt_im = -sqrt_im
p1_re = (gamma_re + sqrt_re) / 2: p1_im = (gamma_im + sqrt_im) / 2
p2_re = (gamma_re - sqrt_re) / 2: p2_im = (gamma_im - sqrt_im) / 2
End If
GD_contrib = p1_re / (p1_re * p1_re + (omega - p1_im) ^ 2): GD_sum = GD_sum + GD_contrib
GD_contrib = p2_re / (p2_re * p2_re + (omega - p2_im) ^ 2): GD_sum = GD_sum + GD_contrib
Next k
ChebyBPGroupDelay = -GD_sum
End Function

Function ChebyBPPhase(f As Double, fL As Double, fU As Double, r As Double, N As Integer) As Double
Const PI As Double = 3.14159265358979: Const TWOPI As Double = 6.28318530717959
Dim epsilon As Double, omega As Double, OmegaL As Double, OmegaU As Double, w0 As Double, BW As Double
Dim lp_s As Double, theta As Double, sinh_val As Double, cosh_val As Double
Dim alpha As Double, beta As Double, gamma_re As Double, gamma_im As Double
Dim delta_re As Double, delta_im As Double, mag As Double, sqrt_re As Double, sqrt_im As Double
Dim p1_re As Double, p1_im As Double, p2_re As Double, p2_im As Double
Dim phase_sum As Double, angle_k As Double, k As Integer
If fL <= 0 Or fU <= fL Or N <= 0 Or r <= 0 Then ChebyBPPhase = 0: Exit Function
OmegaL = TWOPI * fL: OmegaU = TWOPI * fU: omega = TWOPI * f
w0 = Sqr(OmegaL * OmegaU): BW = OmegaU - OmegaL
epsilon = Sqr(10 ^ (r / 10) - 1)
lp_s = Application.WorksheetFunction.Asinh(1 / epsilon) / N
sinh_val = Application.WorksheetFunction.Sinh(lp_s): cosh_val = Application.WorksheetFunction.Cosh(lp_s)
phase_sum = 0
For k = 1 To N
theta = PI * (2 * k - 1) / (2 * N)
alpha = -sinh_val * Sin(theta): beta = cosh_val * Cos(theta)
gamma_re = BW * alpha: gamma_im = BW * beta
delta_re = gamma_re * gamma_re - gamma_im * gamma_im - 4 * w0 * w0
delta_im = 2 * gamma_re * gamma_im
mag = Sqr(delta_re * delta_re + delta_im * delta_im)
If mag = 0 Then
sqrt_re = 0
sqrt_im = 0
Else
sqrt_re = Sqr((mag + delta_re) / 2)
If delta_im < 0 Then
sqrt_im = -Sqr((mag - delta_re) / 2)
Else
sqrt_im = Sqr((mag - delta_re) / 2)
End If
End If
p1_re = (gamma_re + sqrt_re) / 2: p1_im = (gamma_im + sqrt_im) / 2
p2_re = (gamma_re - sqrt_re) / 2: p2_im = (gamma_im - sqrt_im) / 2
If p1_re >= 0 Or p2_re >= 0 Then
sqrt_re = -sqrt_re: sqrt_im = -sqrt_im
p1_re = (gamma_re + sqrt_re) / 2: p1_im = (gamma_im + sqrt_im) / 2
p2_re = (gamma_re - sqrt_re) / 2: p2_im = (gamma_im - sqrt_im) / 2
End If
angle_k = Application.WorksheetFunction.Atan2((omega - p1_im), -p1_re): phase_sum = phase_sum + angle_k
angle_k = Application.WorksheetFunction.Atan2((omega - p2_im), -p2_re): phase_sum = phase_sum + angle_k
Next k
ChebyBPPhase = -phase_sum * 180 / PI
Do While ChebyBPPhase > 180: ChebyBPPhase = ChebyBPPhase - 360: Loop
Do While ChebyBPPhase < -180: ChebyBPPhase = ChebyBPPhase + 360: Loop
End Function


'************************************
'******** CHEBYSHEV BANDSTOP ********
'************************************

Function ChebyBSMag(f As Double, fL As Double, fU As Double, r As Double, N As Integer) As Double
'===============================================================================
' ChebyBSMag - Chebyshev Bandstop Magnitude |H(j2pf)| in dB
' Self-contained: no external helper / WorksheetFunction calls.
'===============================================================================
Const PI As Double = 3.14159265358979
Const TWOPI As Double = 6.28318530717959
Const LN10 As Double = 2.30258509299405

Dim epsilon As Double
Dim omega As Double, OmegaL As Double, OmegaU As Double, w0 As Double, BW As Double
Dim lp_s As Double, theta As Double, sinh_val As Double, cosh_val As Double
Dim alpha As Double, beta As Double, qmag2 As Double
Dim gamma_re As Double, gamma_im As Double
Dim delta_re As Double, delta_im As Double
Dim sqrt_re As Double, sqrt_im As Double
Dim mag As Double
Dim p1_re As Double, p1_im As Double, p2_re As Double, p2_im As Double
Dim log_num_sum As Double, log_den_sum As Double
Dim dist_sq As Double, zero_mag As Double
Dim tmp As Double
Dim k As Integer

If fL <= 0 Or fU <= fL Or N <= 0 Or r <= 0 Then
ChebyBSMag = 0
Exit Function
End If

On Error GoTo ErrorHandler

OmegaL = TWOPI * fL
OmegaU = TWOPI * fU
omega = TWOPI * f
w0 = Sqr(OmegaL * OmegaU)
BW = OmegaU - OmegaL

epsilon = Sqr(10 ^ (r / 10) - 1)

' lp_s = asinh(1/epsilon) / N
tmp = 1# / epsilon
lp_s = Log(tmp + Sqr(tmp * tmp + 1#)) / N

' sinh(lp_s), cosh(lp_s)
tmp = Exp(lp_s)
sinh_val = 0.5 * (tmp - 1# / tmp)
cosh_val = 0.5 * (tmp + 1# / tmp)

' num: |w0^2 - Omega^2|^N => use logs
zero_mag = Abs(w0 * w0 - omega * omega)
If zero_mag <= 1E-300 Then
ChebyBSMag = -1E+99
Exit Function
End If
log_num_sum = N * 0.5 * Log(zero_mag * zero_mag)

log_den_sum = 0#

For k = 1 To N
theta = PI * (2# * k - 1#) / (2# * N)
alpha = -sinh_val * Sin(theta)
beta = cosh_val * Cos(theta)

qmag2 = alpha * alpha + beta * beta
gamma_re = BW * alpha / qmag2
gamma_im = BW * (-beta) / qmag2

delta_re = gamma_re * gamma_re - gamma_im * gamma_im - 4# * w0 * w0
delta_im = 2# * gamma_re * gamma_im

' Complex sqrt of delta (improved for real/near-real cases)
If Abs(delta_im) < 0.00000000000001 * (Abs(delta_re) + 1#) Then ' Treat as real
If delta_re >= 0# Then
sqrt_re = Sqr(delta_re)
sqrt_im = 0#
Else
sqrt_re = 0#
sqrt_im = Sqr(-delta_re)
End If
Else
mag = Sqr(delta_re * delta_re + delta_im * delta_im)
If mag = 0# Then
sqrt_re = 0#
sqrt_im = 0#
Else
sqrt_re = Sqr((mag + delta_re) / 2#)
If delta_im >= 0# Then
sqrt_im = Sqr((mag - delta_re) / 2#)
Else
sqrt_im = -Sqr((mag - delta_re) / 2#)
End If
End If
End If

p1_re = (gamma_re + sqrt_re) / 2#
p1_im = (gamma_im + sqrt_im) / 2#
p2_re = (gamma_re - sqrt_re) / 2#
p2_im = (gamma_im - sqrt_im) / 2#

' Enforce LHP poles
If p1_re >= 0# Or p2_re >= 0# Then
sqrt_re = -sqrt_re
sqrt_im = -sqrt_im
p1_re = (gamma_re + sqrt_re) / 2#
p1_im = (gamma_im + sqrt_im) / 2#
p2_re = (gamma_re - sqrt_re) / 2#
p2_im = (gamma_im - sqrt_im) / 2#
End If

dist_sq = (-p1_re) * (-p1_re) + (omega - p1_im) * (omega - p1_im)
log_den_sum = log_den_sum + 0.5 * Log(dist_sq)

dist_sq = (-p2_re) * (-p2_re) + (omega - p2_im) * (omega - p2_im)
log_den_sum = log_den_sum + 0.5 * Log(dist_sq)
Next k

ChebyBSMag = 20# * (log_num_sum - log_den_sum) / LN10
Exit Function

ErrorHandler:
ChebyBSMag = 0
End Function


Function ChebyBSGroupDelay(f As Double, fL As Double, fU As Double, r As Double, N As Integer) As Double
'===============================================================================
' ChebyBSGroupDelay - Chebyshev Bandstop Group Delay (Hz inputs: f, fL, fU in Hertz)
' Self-contained: no external helper / WorksheetFunction calls.
'===============================================================================
Const PI As Double = 3.14159265358979
Const TWOPI As Double = 6.28318530717959

Dim epsilon As Double
Dim omega As Double
Dim OmegaL As Double, OmegaU As Double
Dim w0 As Double
Dim BW As Double
Dim lp_s As Double
Dim theta As Double
Dim sinh_val As Double
Dim cosh_val As Double
Dim alpha As Double, beta As Double ' q_lp = alpha + j beta
Dim qmag2 As Double
Dim gamma_re As Double, gamma_im As Double
Dim delta_re As Double, delta_im As Double
Dim sqrt_re As Double, sqrt_im As Double
Dim mag As Double
Dim p1_re As Double, p1_im As Double
Dim p2_re As Double, p2_im As Double
Dim GD_contrib As Double
Dim GD_sum As Double
Dim tmp As Double
Dim k As Integer

If fL <= 0 Or fU <= fL Or N <= 0 Or r <= 0 Then
ChebyBSGroupDelay = 0
Exit Function
End If

On Error GoTo ErrorHandler

' Convert Hz to rad/s
OmegaL = TWOPI * fL
OmegaU = TWOPI * fU
omega = TWOPI * f

epsilon = Sqr(10 ^ (r / 10) - 1)
w0 = Sqr(OmegaL * OmegaU)
BW = OmegaU - OmegaL

' lp_s = asinh(1/epsilon) / N
tmp = 1# / epsilon
lp_s = Log(tmp + Sqr(tmp * tmp + 1#)) / N

' sinh(lp_s), cosh(lp_s)
tmp = Exp(lp_s)
sinh_val = 0.5 * (tmp - 1# / tmp)
cosh_val = 0.5 * (tmp + 1# / tmp)

GD_sum = 0#

For k = 1 To N
theta = PI * (2# * k - 1#) / (2# * N)
alpha = -sinh_val * Sin(theta)
beta = cosh_val * Cos(theta)

qmag2 = alpha * alpha + beta * beta
gamma_re = BW * alpha / qmag2
gamma_im = BW * (-beta) / qmag2 ' 1/q = conj(q)/|q|^2

' delta = gamma^2 - 4 w0^2 (complex)
delta_re = gamma_re * gamma_re - gamma_im * gamma_im - 4# * w0 * w0
delta_im = 2# * gamma_re * gamma_im

' Complex sqrt of delta (principal branch, Re >= 0)
mag = Sqr(delta_re * delta_re + delta_im * delta_im)
If mag = 0# Then
sqrt_re = 0#
sqrt_im = 0#
Else
sqrt_re = Sqr((mag + delta_re) / 2#)
If delta_im > 0# Then
sqrt_im = Sqr((mag - delta_re) / 2#)
ElseIf delta_im < 0# Then
sqrt_im = -Sqr((mag - delta_re) / 2#)
Else
sqrt_im = 0#
End If
End If

' Roots: [gamma +/- sqrt(delta)] / 2
p1_re = (gamma_re + sqrt_re) / 2#
p1_im = (gamma_im + sqrt_im) / 2#
p2_re = (gamma_re - sqrt_re) / 2#
p2_im = (gamma_im - sqrt_im) / 2#

' Ensure LHP poles (flip sqrt branch if needed)
If p1_re >= 0# Or p2_re >= 0# Then
sqrt_re = -sqrt_re
sqrt_im = -sqrt_im
p1_re = (gamma_re + sqrt_re) / 2#
p1_im = (gamma_im + sqrt_im) / 2#
p2_re = (gamma_re - sqrt_re) / 2#
p2_im = (gamma_im - sqrt_im) / 2#
End If

' GD from p1
GD_contrib = p1_re / (p1_re * p1_re + (omega - p1_im) * (omega - p1_im))
GD_sum = GD_sum + GD_contrib

' GD from p2
GD_contrib = p2_re / (p2_re * p2_re + (omega - p2_im) * (omega - p2_im))
GD_sum = GD_sum + GD_contrib
Next k

ChebyBSGroupDelay = -GD_sum
Exit Function

ErrorHandler:
ChebyBSGroupDelay = 0
End Function

Function ChebyBSPhase(f As Double, fL As Double, fU As Double, r As Double, N As Integer) As Double
'===============================================================================
' ChebyBSPhase - Chebyshev Bandstop Phase (poles only, degrees)
' Self-contained: no external helper / WorksheetFunction calls.
'===============================================================================
Const PI As Double = 3.14159265358979
Const TWOPI As Double = 6.28318530717959

Dim epsilon As Double
Dim omega As Double
Dim OmegaL As Double, OmegaU As Double
Dim w0 As Double
Dim BW As Double
Dim lp_s As Double
Dim theta As Double
Dim sinh_val As Double
Dim cosh_val As Double
Dim alpha As Double, beta As Double
Dim qmag2 As Double
Dim gamma_re As Double, gamma_im As Double
Dim delta_re As Double, delta_im As Double
Dim sqrt_re As Double, sqrt_im As Double
Dim mag As Double
Dim p1_re As Double, p1_im As Double
Dim p2_re As Double, p2_im As Double
Dim phase_sum As Double
Dim angle_k As Double
Dim x1 As Double, y1 As Double
Dim tmp As Double
Dim k As Integer

If fL <= 0 Or fU <= fL Or N <= 0 Or r <= 0 Then
ChebyBSPhase = 0
Exit Function
End If

On Error GoTo ErrorHandler

' Convert Hz to rad/s
OmegaL = TWOPI * fL
OmegaU = TWOPI * fU
omega = TWOPI * f

epsilon = Sqr(10 ^ (r / 10) - 1)
w0 = Sqr(OmegaL * OmegaU)
BW = OmegaU - OmegaL

' lp_s = asinh(1/epsilon) / N
tmp = 1# / epsilon
lp_s = Log(tmp + Sqr(tmp * tmp + 1#)) / N

' sinh(lp_s), cosh(lp_s)
tmp = Exp(lp_s)
sinh_val = 0.5 * (tmp - 1# / tmp)
cosh_val = 0.5 * (tmp + 1# / tmp)

phase_sum = 0#

For k = 1 To N
theta = PI * (2# * k - 1#) / (2# * N)
alpha = -sinh_val * Sin(theta)
beta = cosh_val * Cos(theta)

qmag2 = alpha * alpha + beta * beta
gamma_re = BW * alpha / qmag2
gamma_im = BW * (-beta) / qmag2

delta_re = gamma_re * gamma_re - gamma_im * gamma_im - 4# * w0 * w0
delta_im = 2# * gamma_re * gamma_im

' Complex sqrt of delta (principal)
mag = Sqr(delta_re * delta_re + delta_im * delta_im)
If mag = 0# Then
sqrt_re = 0#
sqrt_im = 0#
Else
sqrt_re = Sqr((mag + delta_re) / 2#)
If delta_im > 0# Then
sqrt_im = Sqr((mag - delta_re) / 2#)
ElseIf delta_im < 0# Then
sqrt_im = -Sqr((mag - delta_re) / 2#)
Else
sqrt_im = 0#
End If
End If

p1_re = (gamma_re + sqrt_re) / 2#
p1_im = (gamma_im + sqrt_im) / 2#
p2_re = (gamma_re - sqrt_re) / 2#
p2_im = (gamma_im - sqrt_im) / 2#

' Ensure LHP
If p1_re >= 0# Or p2_re >= 0# Then
sqrt_re = -sqrt_re
sqrt_im = -sqrt_im
p1_re = (gamma_re + sqrt_re) / 2#
p1_im = (gamma_im + sqrt_im) / 2#
p2_re = (gamma_re - sqrt_re) / 2#
p2_im = (gamma_im - sqrt_im) / 2#
End If

' angle_k = Atan2(Omega - p_im, -p_re) (inline atan2)
' ---- pole p1 ----
y1 = omega - p1_im
x1 = -p1_re
If x1 > 0# Then
angle_k = Atn(y1 / x1)
ElseIf x1 < 0# Then
If y1 >= 0# Then
angle_k = Atn(y1 / x1) + PI
Else
angle_k = Atn(y1 / x1) - PI
End If
Else
If y1 > 0# Then
angle_k = PI / 2#
ElseIf y1 < 0# Then
angle_k = -PI / 2#
Else
angle_k = 0#
End If
End If
phase_sum = phase_sum + angle_k

' ---- pole p2 ----
y1 = omega - p2_im
x1 = -p2_re
If x1 > 0# Then
angle_k = Atn(y1 / x1)
ElseIf x1 < 0# Then
If y1 >= 0# Then
angle_k = Atn(y1 / x1) + PI
Else
angle_k = Atn(y1 / x1) - PI
End If
Else
If y1 > 0# Then
angle_k = PI / 2#
ElseIf y1 < 0# Then
angle_k = -PI / 2#
Else
angle_k = 0#
End If
End If
phase_sum = phase_sum + angle_k
Next k

ChebyBSPhase = -phase_sum * 180# / PI

' Wrap to [-180, 180]
Do While ChebyBSPhase > 180#
ChebyBSPhase = ChebyBSPhase - 360#
Loop
Do While ChebyBSPhase < -180#
ChebyBSPhase = ChebyBSPhase + 360#
Loop

Exit Function
 

Werbel Microwave power dividers, couplers - RF Cafe