Beispiel #1
0
def annotate_magnitude(ax, f, H, f0, string, **kw):
    H0 = H[np.argmin(np.abs(f - f0))]

    p0 = ax.transData.transform_point((f[0], db(H[0])))
    p1 = ax.transData.transform_point((f[-1], db(H[-1])))
    dx, dy = p1 - p0
    angle = np.rad2deg(np.arctan2(dy, dx))
    ax.text(f0, db(H0), string, rotation=angle, rotation_mode='anchor', **kw)
Beispiel #2
0
def plot_crest_factor(ax, dir_crest_factor, stimuli, labels, phase_angles,
                      colors, xticks):
    for i, stm in enumerate(stimuli):
        phi, C = np.loadtxt(dir_crest_factor + stm + '.txt')
        phi -= np.pi  # shift angle by -np.pi
        color = colors(i * 0.25)
        label = labels[i]
        Cdiff = util.db(C[-90] / C[0])
        ax.plot(np.rad2deg(phi), util.db(C), c=color, label=label)
        ax.plot(-90, util.db(C[-90]), c=color, marker='o')
        color = (color if i!=3 else 'k')
        ax.text(-90, util.db(C[-90]) + 0.5, '${:+0.1f}$ dB'.format(Cdiff),
                color=color, va='bottom', ha='center')
        ax.text(100, util.db(C[100]) + 0.3, label,
                color=color, va='bottom', ha='center')
        if re.match(re.compile('square.'), stm):
            Cdiff = util.db(C[-45] / C[0])
            ax.plot(-45, util.db(C[-45]), c=color, marker='o')
            ax.text(-45, util.db(C[-45]) + 0.5, '${:+0.1f}$ dB'.format(Cdiff),
                    color=color, va='bottom', ha='left')
    ax.set_xticks(xticks)
    ax.set_xlim(xticks[0], xticks[-1])
    ax.set_xlabel(r'Phase Shift $\varphi$ / $\degree$')
    ax.set_ylabel('Crest Factor (CF) / dB')
    ax.grid(True, color='lightgray')

    color_legend = [0.5, 0.5, 0.5]
    ax.plot([-145, -125], [21, 21], c=color_legend)
    ax.plot(-135, 21, c=color_legend, marker='o')
    ax.text(-135, 21.5, 'CF change \n relative to $0\\degree$',
            color='k', va='bottom', ha='center')
Beispiel #3
0
def plot_magnitude(ax, fmin, fmax, H_mag, color):
    ax.hlines(db(H_mag), fmin, fmax, colors=color)
    ax.set_yticks([-10, 0, 10])
    ax.set_xlim(fmin, fmax)
    ax.set_ylim(-12, 12)
    ax.set_xscale('log')
    ax.set_xlabel('$f$ / Hz')
    ax.set_ylabel('Magnitude / dB')
    ax.grid(True)
    # annotation
    flabel = np.sqrt(fmin * fmax)
    ax.text(flabel,
            0,
            'all-pass',
            fontsize=14,
            ha='center',
            va='bottom',
            color='k')
Beispiel #4
0
# Plots
Glim = -0.21, 0.21
philim = -3, 47
wlim = wmin, wmax
wticks = 2**(np.arange(
    np.ceil(np.log2(w)[0] / 2) * 2,
    np.floor(np.log2(w[-1]) / 2) * 2 + 2, 2))
kw = {'lw': 2, 'alpha': 1, 'basex': 2}
colors = cm.get_cmap('Blues')

# frequency response
fig, ax = plt.subplots(figsize=(10, 4), ncols=2, gridspec_kw={'wspace': 0.25})
for n, (biquad_per_octave, H_n) in enumerate(zip(Biquad_per_octave, H)):
    col = colors((n + 2) / (len(H) + 2))
    ax[0].semilogx(w,
                   db(H_n) - Hmag,
                   c=col,
                   **kw,
                   label='{:0.0f}'.format(biquad_per_octave))

# Zeros and poles
kw_p = dict(c='k', marker='x', ls='none')
kw_z = dict(marker='o', mew=0.75, ls='none', mfc='none')
for n, (zpk) in enumerate(zpks):
    z, p, _ = zpk
    num_pole, num_zero = len(z), len(p)
    voffset = -n
    col = colors((n + 2) / (len(H) + 2))
    ax[1].plot(np.abs(p), voffset * np.ones(num_pole), **kw_p)
    ax[1].plot(np.abs(z), voffset * np.ones(num_zero), c=col, **kw_z)
ax[1].set_xscale('log', basex=2)
Beispiel #5
0
def plot_magnitude(ax, f, H, **kw):
    ax.semilogx(f, db(H), **kw)
Beispiel #6
0
f_control[::2] = f_command
f_control[1::2] = np.sqrt(f_command[:-1] * f_command[1:])
w_command = 2 * np.pi * f_command / fs
w_control = 2 * np.pi * f_control / fs
bandwidth = 1.5 * w_command
bandwidth[6] *= 0.997
bandwidth[7] *= 0.985
bandwidth[8] *= 0.929
bandwidth[9] *= 0.433

gain_factor = 0.29
gain_proto = np.array(
    [13.8, 14.5, 14.5, 14.6, 14.5, 14.5, 14.6, 14.6, 14.5, 13.6])

# 3dB per octave
slope = db(np.sqrt(2))
BWd = 6
fh = 2000
fl = fh * 2**(-BWd)
f_center = fh * 2**(-BWd / 2)
w_center = 2 * np.pi * f_center / fs
gmin, gmax = -9, 9
gain_command = np.clip(np.log2(w_command / w_center) * slope, gmin, gmax)
H_desired = np.clip(np.log2(f / f_center) * slope, gmin, gmax)
b_opt, a_opt = optimized_peq_seg(gain_command, gain_proto, gain_factor,
                                 w_command, w_control, bandwidth)
sos = np.vstack([b_opt, a_opt]).T
H = sosfreqz(sos, worN=f, fs=fs)[1]

# Plots
kw = dict(c='peru', lw=3, alpha=0.75)
Beispiel #7
0
sos_zdomain = zpk2sos(*zpk)
H = sosfreqz(sos_zdomain, worN=f, fs=fs)[1]
h = sosfilt(sos_zdomain, xin)

# Plots
flim = fmin, fmax
fticks = fc * 2.**np.arange(-8, 4, 2)
fticklabels = ['7.8', '31.3', '125', '500', '2k', '8k']
fticks = 1000 * 2.**np.arange(-6, 6, 2)
fticklabels = ['15.6', '62.5', '250', '1k', '4k', '16k']
kw = dict(c='C0', lw=2, alpha=1)

fig, ax = plt.subplots(figsize=(13, 3), ncols=3, gridspec_kw={'wspace': 0.25})

# frequency response
ax[0].semilogx(f, db(H), **kw)
ax[1].semilogx(f, np.angle(H, deg=True), **kw)

# desired response
fl, fh = fc * 2**(-BWd), fc
kw_des = dict(c='k', lw=2, ls=':')
Hmag = np.clip(np.log2(f / fc) * slope, G, 0)
Hphase = 90 * slope / db(2)
ax[0].semilogx(f, Hmag, **kw_des)
ax[1].semilogx((fl, fh), (Hphase, Hphase), **kw_des)
ax[1].text(250, Hphase - 1, '45 degree', va='top', ha='center', fontsize=10)

# desired bandwidth
kw_bw = dict(color='lightgray', alpha=0.5)
fl, fh = fc * 2**(-BWd), fc
Gmin, Gmax = ax[0].get_ylim()
Beispiel #8
0
for i, fo in enumerate(Filter_orders):
    _, h = util.constant_phase_shifter(fo, phase_varN, beta=beta)
    _, H = freqz(h, 1, f, fs=fs)
    gd = fo / 2 / fs
    H *= np.exp(1j * omega * gd)  # compensate group delay
    phase = np.unwrap(np.mod(np.angle(H), 2 * np.pi))
    phase_deg = np.rad2deg(phase)

    vshift = -idx_varN * voffset_tf
    color = colors[idx_varN]
    opacity = 1.2**(-i)

    # Frequency responses for varying filter order
    ax[0].semilogx(f,
                   vshift + db(H),
                   color=color,
                   lw=1.5,
                   ls=':',
                   alpha=opacity)
    ax[1].semilogx(f, phase_deg, color=color, lw=1.5, ls=':', alpha=opacity)

    # Labels: filter order
    ax[0].text(4**(-i) * 30,
               -21.5,
               r'$N={:0.0f}$'.format(fo + 1),
               rotation=33,
               va='center',
               ha='left',
               fontsize=8,
               color='k')
Beispiel #9
0
# Plots
col = 'C0'

fig, ax = plt.subplots(figsize=(12, 4), ncols=2, gridspec_kw={'wspace': 0.33})

# impulse response and decay curve
ax[0].stem(n, h, markerfmt=col + 'o', linefmt=col, basefmt=col, label='$h[n]$')
ax[0].plot(n_dense_neg, envelope_neg, 'k--', zorder=0, label=r'$2 / \pi n$')
ax[0].plot(n_dense_pos, envelope_pos, 'k--', zorder=0)
ax[0].set_ylim(-0.8, 0.8)
ax[0].set_xlabel('$n$ / sample')
ax[0].legend()

# decay curve in log-log axes with quantization errors
ax[1].semilogx(sample, util.db(envelope), 'k--', label=r'$|2 / \pi n|$')
ax[1].hlines(util.db(E16), nmin, nmax, colors='C1', zorder=0)
ax[1].hlines(util.db(E24), nmin, nmax, colors='C3', zorder=0)
ax[1].grid(color='lightgray')
ax[1].set_xlabel('$n$ / sample')
ax[1].set_ylabel('Amplitude / dB')
ax[1].set_xlim(nmin, nmax)
p0 = ax[1].transData.transform_point((sample[0], util.db(envelope[0])))
p1 = ax[1].transData.transform_point((sample[-1], util.db(envelope[-1])))
dx, dy = p1 - p0
angle = np.rad2deg(np.arctan2(dy, dx))
ax[1].text(1000, -60, r'$\frac{2}{\pi n}$', rotation=angle, fontsize=25)
ax[1].text(100, util.db(E16), r'16-bit noise floor', va='bottom')
ax[1].text(100, util.db(E24), r'24-bit noise floor', va='bottom')

# secondary x-axis in seconds
Beispiel #10
0
# %%
from datetime import datetime, date, time

from numpy import TooHardError
from util import file, stg, tool, db, indicator as ind

import matplotlib.pyplot as plt

import pandas as pd
from plotly.offline import iplot, init_notebook_mode
import plotly.graph_objs as go

import talib

sql = "SELECT StockID, TradeDate, Open, High, Low, Close, Volume FROM dailyholc WHERE StockID in ('2330', '2303')"
stkdailyDF = db().selectDatatoDF(sql_statment = sql)
stkdailyDF = ind(stkdailyDF).addMFIvalueToDF()
# %%


stkdailyDF = ind(stkdailyDF).getSignalByIndicator(inds = ["MACD"])


stkdailyDF = ind(stkdailyDF).addMAvalueToDF()
stkdailyDF = ind(stkdailyDF).addSARvalueToDF(acc = 0.02, max = 0.2)   # Default acc = 0.02, max = 0.2
stkdailyDF = ind(stkdailyDF).addSARvalueToDF(acc = 0.03, max = 0.3)   # Default acc = 0.02, max = 0.2
stkdailyDF = ind(stkdailyDF).addBBANDvalueToDF()
stkdailyDF = ind(stkdailyDF).addMAXMINvalueToDF()
stkdailyDF = ind(stkdailyDF).getSignalByIndicator(inds = ["SMA", "SAR", "MAXMIN", "BBands"])

# %%
Beispiel #11
0
Glim = -9.5, 9.5
philim = -21, 21
wlim = wmin, wmax
wticks = 2**(np.arange(np.ceil(np.log2(w)[0]/2)*2,
                       np.floor(np.log2(w[-1])/2)*2 + 2, 2))
kw_pos = dict(lw=1.5, c='C3', alpha=0.5, basex=2)
kw_neg = dict(lw=1.5, c='C0', alpha=1, basex=2)

fig, ax = plt.subplots(figsize=(10, 4), ncols=2, gridspec_kw={'wspace': 0.25})

# frequency response
w_mag, w_ph = 2**-9, 2**-3
for n, (Gd, H_n) in enumerate(zip(Desired_gain, H)):
    kw = kw_pos if Gd > 0 else kw_neg
#    col = 'C3' if Gd > 0 else 'C0'
    ax[0].semilogx(w, db(H_n), **kw)
    ax[1].semilogx(w, np.rad2deg(np.angle(H_n)), **kw)
    label = '{:+0.0f} dB'.format(Gd)
    phi = nearest_value(w_ph, w, np.angle(H_n, deg=True))
    ax[0].annotate(label, (w_mag, Gd), ha='left', va='bottom', fontsize=10)
    ax[1].annotate(label, (w_ph, phi), ha='center', va='bottom', fontsize=10)

# decorations
ax[0].set_xlim(wmin, wmax)
ax[0].set_ylim(Glim)
ax[0].set_xticks(wticks)
ax[0].grid(True)
ax[0].yaxis.set_major_locator(MultipleLocator(3))
ax[0].yaxis.set_minor_locator(MultipleLocator(1))
ax[0].set_xlabel(r'$\omega$ / $\omega_\textrm{\footnotesize u}$')
ax[0].set_ylabel('Level in dB')
Beispiel #12
0
    sos = low_shelving_2nd_cascade(w0, Gb, num_biquad, biquad_per_octave)
    H[n] = sosfreqs(sos, worN=w)[1]

# Plots
wlim = wmin, wmax
wticks = 2**(np.arange(
    np.ceil(np.log2(w)[0] / 2) * 2,
    np.floor(np.log2(w[-1]) / 2) * 2 + 2, 2))
kw = {'lw': 1.5, 'alpha': 0.75, 'basex': 2}

fig, ax = plt.subplots(figsize=(10, 4), ncols=2, gridspec_kw={'wspace': 0.25})

# frequency response
for n, (BWd, H_n) in enumerate(zip(Desired_bandwidth, H)):
    col = 'C3' if Gain[n] > 0 else 'C0'
    ax[0].semilogx(w, db(H_n), c=col, **kw)
    ax[1].semilogx(w, np.angle(H_n, deg=True), c=col, **kw)

# desired response
Hmag = np.clip(np.log2(w / w0) * slope, G, 0)
Hphase = 90 * slope / db(2)
ax[0].semilogx(w, Hmag, 'k:', **kw)
ax[1].semilogx((wmin, wmax), (Hphase, Hphase), 'k:', **kw)

# decorations
ax[0].set_xlim(wmin, wmax)
ax[0].set_xticks(wticks)
ax[0].yaxis.set_major_locator(MultipleLocator(3))
ax[0].yaxis.set_minor_locator(MultipleLocator(1))
ax[0].grid(True)
ax[0].set_xlabel(r'$\omega$ / $\omega_\textrm{\footnotesize u}$')
Beispiel #13
0
x_Mp = util.periodic_signal(x_1p, long_repetition)
y_fir = np.zeros((len(phase_angles), N))  # selection of long signal
Y_fir = np.zeros((len(phase_angles), N // 2 + 1), dtype='complex128')
for i, phi in enumerate(phase_angles):
    h = util.constant_phase_shifter(filter_order, phi, beta=8.6)[1]
    y = util.acausal_filter(x_Mp, h)[n_start:n_stop]
    y_fir[i] = y
    Y_fir[i] = np.fft.rfft(y)

# I. Float64

# Waveforms - DFT vs FIR methods
t = np.arange(len(y_fir[0])) / fs * 1000
fig, ax = plt.subplots(figsize=(12, 5), ncols=3, sharey=True)
for i, (phi, yd, yf) in enumerate(zip(phase_angles, y_dft, y_fir)):
    ax[i].plot(t, util.db(yd), c='lightgray', label='DFT')
    ax[i].plot(t, util.db(yd - yf), label='diff')
    ax[i].set_xlabel('$t$ / ms')
    ax[i].set_title('{:0.0f}'.format(np.rad2deg(phi)))
    ax[i].grid(True)
ax[0].set_ylabel('Amplitude / dB')
ax[0].legend(loc='upper right')
fig.suptitle('[FLOAT] Waveforms - DFT vs FIR')

# Spectra - DFT vs FIR methods
Nrfft = N // 2 + 1
f = np.arange(Nrfft) / N * fs
fig, ax = plt.subplots(figsize=(12, 5), ncols=3, sharey=True)
for i, (phi, Yd, Yf) in enumerate(zip(phase_angles, Y_dft, Y_fir)):
    ax[i].plot(f, util.db(Yd), 'lightgray', label='DFT')
    ax[i].plot(f, util.db(Yd - Yf), label='diff')
Beispiel #14
0
                       sharex=True,
                       sharey=True)

for i, stml in enumerate(stimuli):
    s0 = sf.read(dir_stimuli + stml + '_phi000.wav')[0][:, 0]
    try:
        sp = sf.read(dir_stimuli + stml + '_phi270.wav')[0][:, 0]
    except:
        sp = sf.read(dir_stimuli + stml + '_phi315.wav')[0][:, 0]
    S0 = np.fft.rfft(s0)
    Sp = np.fft.rfft(sp)
    Smax = np.max(np.abs(S0))
    N = len(s0)
    f = fs * np.arange(N // 2 + 1) / N
    axi = ax.flat[i]
    axi.plot(f, db(S0 / Smax), 'C0', lw=1, label='Original Signal')
    axi.plot(f,
             db((np.abs(S0) - np.abs(Sp)) / Smax),
             'C3',
             lw=2,
             label='Magnitude Deviation')
    axi.set_xlim(-5, 120)
    axi.set_ylim(-150, 10)
    axi.set_title(a_label[i])
    axi.grid(True)
ax[1, 0].set_xlim()
ax[1, 0].legend(loc='upper left')
for axi in ax[1]:
    axi.set_xlabel('$f$ / Hz')
for axi in ax[:, 0]:
    axi.set_ylabel('Magnitude / dB')
Beispiel #15
0
wticks = 2**(np.arange(
    np.ceil(np.log2(w)[0] / 2) * 2,
    np.floor(np.log2(w[-1]) / 2) * 2 + 2, 2))
kw = {'lw': 2, 'alpha': 1, 'basex': 2}
labels = [
    '{:0.0f}'.format(biquad_per_octave)
    for biquad_per_octave in Biquad_per_octave
]
colors = cm.get_cmap('Blues')

# frequency response
fig, ax = plt.subplots(figsize=(10, 4), ncols=2, gridspec_kw={'wspace': 0.25})
for n, (biquad_per_octave, H_n) in enumerate(zip(Biquad_per_octave, H)):
    col = colors((n + 3) / (len(H) + 3))
    ax[0].semilogx(w,
                   db(H_n),
                   c=col,
                   **kw,
                   label='{:0.0f}'.format(biquad_per_octave))
    ax[1].semilogx(w, np.angle(H_n, deg=True), c=col, **kw)

# desired response
wl, wh = w0 * 2**(-BWd), w0
Hmag = np.clip(np.log2(w / w0) * slope, G, 0)
Hphase = 90 * slope / db(2)
ax[0].semilogx(w, Hmag, 'k:', **kw)
ax[1].semilogx((wl, w0), (Hphase, Hphase), 'k:', **kw)
ax[1].text(np.sqrt(wl * w0),
           Hphase - 1,
           '{:0.0f} deg'.format(Hphase),
           fontsize=10,
Beispiel #16
0
# %%
from datetime import date, timedelta
import pandas as pd
# from datetime import date, timedelta, datetime
from util import con, cfg, db, tool

# day = date.today().strftime("%Y%m%d")
# minDF = db().selectDatatoDF(sql_statment = f"SELECT * FROM dailyminsholc WHERE TradeDate = {day}")

day = date.today().strftime("%Y-%m-%d")
# day = "2021-10-15"
api = con().LoginToServerForStock(simulate=False)
tb = cfg().getValueByConfigFile(key="tb_mins")
# %%

stock_lst = tool.DFcolumnToList(db().selectDatatoDF(
    cfg().getValueByConfigFile(key="tb_basic")),
                                colname="StockID")

DFtoDBmin = pd.DataFrame()
for id in stock_lst:
    stkDF = con(api).getKarData(stkid=id, sdate=day, edate=day)
    stkDF = stkDF.filter(items=[
        "StockID", "TradeDate", "TradeTime", "Open", "High", "Low", "Close",
        "Volume"
    ])
    print(id)

    db().updateDFtoDB(stkDF, tb_name=tb)

# %%
Beispiel #17
0
H = sosfreqs(sos, worN=w)[1]

# Plots
Glim = -9.5, 1
wlim = wmin, wmax
wticks = 2**(np.arange(
    np.ceil(np.log2(w)[0] / 2) * 2,
    np.floor(np.log2(w[-1]) / 2) * 2 + 2, 2))
kw = dict(lw=2, alpha=1, basex=2)
kw_dotted = dict(color='gray', linestyle=':', linewidth=1)
kw_artist = dict(edgecolor='gray', linestyle=':', linewidth=1)
colors = cm.get_cmap('Blues')

# frequency response
fig, ax = plt.subplots(figsize=(10, 4), ncols=2, gridspec_kw={'wspace': 0.1})
ax[0].semilogx(w, db(H_biquads.T), c='gray', **kw, zorder=2)
ax[0].semilogx(w, Hmag, 'k:', **kw)
ax[0].semilogx(w, db(H), c='C0', **kw, zorder=3)

ax[1].plot([-1, 0], [1, 0], 'C7:', lw=1)

# Pole zero plot
kw_z = dict(c='C0', marker='o', ms=9, ls='none', mew=1, mfc='none', alpha=1)
kw_p = dict(c='k', marker='x', ms=9, ls='none', mew=1)
kw_dot = dict(marker='.', ms=10)
ylim = ax[0].get_ylim()
for n, sosi in enumerate(sos):
    z, p, _ = sos2zpk(sosi[np.newaxis, :])
    z, p = z[0], p[0]
    wc = np.abs(np.sqrt(z * p))
Beispiel #18
0
    H[n] = sosfreqs(sos, worN=w)[1]
    shelving_filters.append(sos)

# Plots
wlim = wmin, wmax
wticks = 2**(np.arange(np.ceil(np.log2(w)[0]/2)*2,
                       np.floor(np.log2(w[-1])/2)*2 + 2, 2))
kw = {'lw': 2, 'alpha': 1, 'basex': 2}
colors = cm.get_cmap('Blues')

fig, ax = plt.subplots(figsize=(10, 4), ncols=2, gridspec_kw={'wspace': 0.25})

# frequency response
for n, H_n in enumerate(H):
    col = colors((n + 3) / (len(H) + 3))
    ax[0].semilogx(w, db(H_n), c=col, **kw)
    ax[1].semilogx(w, np.rad2deg(np.unwrap(np.angle(H_n))), c=col, **kw)

# desired response
wl, wh = w0 * 2**(-BWd), w0
Hmag = np.clip(np.log2(w/w0) * slope, G, 0)
Hphase = np.round(90 * slope / db(2), decimals=0)
ax[0].semilogx(w, Hmag, 'k:', **kw)
ax[1].semilogx((wl, w0), (Hphase, Hphase), 'k:', **kw)
ax[1].text(np.sqrt(wl * w0), Hphase - 10, '{:0.0f} deg'.format(Hphase),
           fontsize=10, ha='center', va='top')

# decorations
ax[0].set_xlim(wmin, wmax)
ax[0].set_xticks(wticks)
ax[0].grid(True)
Beispiel #19
0
wticks = 2**(np.arange(
    np.ceil(np.log2(w)[0] / 4) * 4,
    np.floor(np.log2(w[-1]) / 4) * 4 + 4, 4))
kw = dict(linewidth=2, alpha=1, basex=2)
kw_z = dict(c='C0', marker='o', ms=9, ls='none', mew=1, mfc='none', alpha=1)
kw_p = dict(c='k', marker='x', ms=9, ls='none', mew=1)
kw_artist = dict(edgecolor='gray', linestyle=':', linewidth=1)
colors = [
    cm.get_cmap('Oranges')(x)[:3]
    for x in np.linspace(0.33, 1, num=max_order, endpoint=False)
]

fig, ax = plt.subplots(figsize=(13, 3), ncols=3, gridspec_kw={'wspace': 0.3})

for Hi, ci in zip(H, colors):
    ax[0].semilogx(w / wc, db(Hi), c=ci, **kw)
    ax[1].semilogx(w / wc, np.angle(Hi, deg=True), c=ci, **kw)
ax[0].set_xlim(wlim)
ax[0].set_xticks(wticks)
ax[0].grid(True)
ax[0].set_xlabel(r'$\omega$ / $\omega_\textrm{\footnotesize c}$')
ax[0].set_ylabel('Level in dB')
ax[0].yaxis.set_major_locator(MultipleLocator(3))
ax[0].yaxis.set_minor_locator(MultipleLocator(1))
ax[0].legend(orders, title='Filter order', facecolor='w')

ax[1].set_xlim(wlim)
ax[1].set_xticks(wticks)
ax[1].grid(True)
ax[1].set_xlabel(r'$\omega$ / $\omega_\textrm{\footnotesize c}$')
ax[1].set_ylabel('Phase in degree')