TransferFunctionConstruction.py 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import numpy as np
  2. import csv
  3. import matplotlib.pyplot as plt
  4. # [1] This method constructs a transfer function numerator and denominator based on the method
  5. # presented by Abel, J.S. and Smith, J.O., "ROBUST DESIGN OF VERY HIGH-ORDER ALLPASS DISPERSION FILTERS",
  6. # in Proc. of the 9th Int. Conference on Digital Audio Effects (DAFx-06), Montreal, Canada, September 18-20, 2006.
  7. def ConstructTransferFunction(filepath):
  8. # Read the given bounds of the area bands of the group delay.
  9. with open(filepath, 'r') as file:
  10. AllData = list(csv.reader(file, delimiter=';'))
  11. bounds = [float(i) for i in AllData[0]]
  12. # This variable determines the overlap of successive band group delays,
  13. # used to determine the smoothness of the approximation
  14. OverlapBeta = 0.9
  15. SamplesPerSecond = bounds[-1] * 2
  16. SamplingInterval = 1 / SamplesPerSecond
  17. order = len(bounds) - 1
  18. # We calculate a normalized angular frequency for each bound using double the Nyquist limit.
  19. NormAngFreq = [2*np.pi*i / SamplesPerSecond for i in bounds]
  20. BandMidpoints = np.zeros(order)
  21. # Intermediate variables
  22. Deltas = np.zeros(order)
  23. Etas = np.zeros(order)
  24. BetaPrime = np.sqrt(OverlapBeta / (1 - OverlapBeta))
  25. # The pole radius is used to define all-pass factored biquad sections of the transfer function.
  26. PoleRadius = np.zeros(order)
  27. # This radius is assigned using an approximation, that can be used for large filters.
  28. PoleRadius2 = np.zeros(order)
  29. # Following the calculations done in [1].
  30. for i in range(order):
  31. BandMidpoints[i] = (NormAngFreq[i] + NormAngFreq[i+1]) / 2
  32. Deltas[i] = (NormAngFreq[i+1] - NormAngFreq[i]) / 2
  33. Etas[i] = (1 - OverlapBeta * np.cos(Deltas[i])) / (1 - OverlapBeta)
  34. PoleRadius[i] = Etas[i] - np.sqrt(Etas[i]**2 - 1)
  35. PoleRadius2[i] = 1 - BetaPrime * Deltas[i]
  36. PoleFrequency = BandMidpoints
  37. TransferNumerator = np.poly1d(1)
  38. TransferDenominator = np.poly1d(1)
  39. # We calculate the numerator and denominator of the final transfer function separately using polynomial multiplication.
  40. for i in range(order):
  41. # We add only components to the transfer function that will yield a stable system.
  42. if PoleFrequency[i] >= 0.5 * np.pi or PoleFrequency[i] <= -0.5 * np.pi:
  43. a = PoleRadius[i]**2
  44. b = -2 * PoleRadius[i] * np.cos(PoleFrequency[i])
  45. TransferNumerator = TransferNumerator * np.poly1d([1, b, a])
  46. TransferDenominator = TransferDenominator * np.poly1d([a, b, 1])
  47. # We return only the coëfficients in reverse order in a two dimensional array.
  48. result = []
  49. result.append(np.asarray(TransferNumerator))
  50. result.append(np.asarray(TransferDenominator))
  51. PlotGroupDelay(PoleRadius, PoleFrequency, order)
  52. return result, SamplingInterval
  53. # This method plots the group delay that is imposed by a filter with
  54. # the given pole radii and pole frequencies and also plots the group delay imposed
  55. # by each individual all-pass section of the filter transfer function.
  56. def PlotGroupDelay(PoleRadius, PoleFrequency, order):
  57. ydata = []
  58. xdata = []
  59. allydata = []
  60. for j in range(order):
  61. subydata = []
  62. for i in range(1000):
  63. subydata.append((1-PoleRadius[j]**2)/(1+PoleRadius[j]**2-2*PoleRadius[j]*np.cos(i*0.001*np.pi-PoleFrequency[j])))
  64. allydata.append(subydata)
  65. for i in range(1000):
  66. xdata.append(i*0.001*np.pi)
  67. sum = 0
  68. for j in range(len(allydata)):
  69. sum += allydata[j][i]
  70. ydata.append(sum)
  71. plt.plot(xdata, ydata)
  72. for i in range(order):
  73. plt.plot(xdata, allydata[i], 'g')
  74. plt.xlim([0, np.pi])
  75. plt.xlabel('Frequency, $\omega$')
  76. plt.ylabel('Group delay, $\delta(\omega)$')
  77. plt.show()