I want to take my Python (currently Version 3.9.7) programming skills to a next level. Up to now I just wrote some small scripts for myself, that no one hat to review or reuse. Now, I want to write code that can be considered as "clean" and can be reused by others. For this purpose, I am writing my own signal processing module with which I can generate high- and lowpassfilters in order to filter signals. I have no experience with structuring packages / modules, so I have some questions regarding code structure.
Up to now, I have a class sim_lowpass:
# -*- coding: utf-8 -*-
"""
Created on Wed Jun 22 10:37:19 2022
@author: ilja
"""
from matplotlib import pyplot as plt
import math
class sim_lowpass:
""" Lowpass Simulation Class """
def __init__(self, cutoff: int, order: int, fs: int) -> None:
self.fs = fs
self.nyq = int(0.5 * fs)
self.cutoff = cutoff
self.order = order
def _transfer_func(self,f: float) -> float:
""" Transfer function in the z-domain """
if self.order == 1:
return 1/(1+(f/(2*math.pi*self.cutoff)))
def transfer_func(self) -> list[float]:
""" Transfer function in the z-domain """
if self.order == 1:
# f = np.linspace(self.df, self.nyq, self.N/2)
f = list(range(int(self.nyq)))
return [self._transfer_func(i) for i in f]
def propagate(self, x_hat):
filtered = [i*j for i,j in zip(x_hat, self.impulse_response())]
return filtered
def bode_plot(self, tr_func: list[float]) -> None:
fig, (ax1, ax2) = plt.subplots(2, 1, constrained_layout=True,
figsize = (8,5))
ax1.plot(list(range(self.nyq)), tr_func)
#ax1.set_title('Magnitude')
ax1.set_xscale('log')
ax1.set_yscale('log')
ax1.set_ylabel('Magnitude (dB)')
ax1.grid(True)
# ax2.plot(list(range(self.nyq)), tr_func) # TODO
# ax2.set_title('Phase')
ax2.set_xscale('log')
ax2.set_yscale('log')
ax2.set_xlabel('Frequency (Hz)')
ax2.set_ylabel('Phase (deg)')
ax2.grid(True)
fig.suptitle('Bode Plot', fontsize=16)
def main() -> None:
# define filter params
cutoff = 100
order = 1
fs = 4e6
# create filter
lp = sim_lowpass(cutoff, order, fs)
tf = lp.transfer_func()
lp.bode_plot(tf)
if __name__ == '__main__':
main()
Questions:
First of all: Is the code up to now well structured (in terms of scalability, testability, ... what else is there?)
Second: Now I want to create the class sim_lowpass. How do I continue without copy-pasting the parts I can reuse from the highpass class?
Third: Where do I place this file (and what would be a meaningful name) inside the package hierarchy?
Last but not least: Any other tips for improvement?
I usually get inspiration for code-structure from real projects. For example, since you are using matplotlib, their github could be a place to start: https://github.com/matplotlib/matplotlib/tree/main/lib/matplotlib