Last active
November 22, 2020 12:31
-
-
Save brunifrancesco/2a2d14016b8c064f4135d181ad29fbc9 to your computer and use it in GitHub Desktop.
Signal and wave modelling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import abstractmethod | |
from signals_wave import WaveM | |
class Modulation: | |
@abstractmethod | |
def modulate(self, signal_wave: WaveM, carrier_wave: WaveM) -> WaveM: | |
raise NotImplementedError("You need to implement this method before calling it!") | |
@abstractmethod | |
def demodulate(self, signal_wave: WaveM, carrier_wave: WaveM) -> WaveM: | |
raise NotImplementedError("You need to implement this method before calling it!") | |
class AmplitudeModulation(Modulation): | |
def modulate(self, signal_wave: WaveM, carrier_wave: WaveM) -> WaveM: | |
return signal_wave * carrier_wave | |
def demodulate(self, modulated_wave: WaveM, carrier_wave: WaveM) -> WaveM: | |
return carrier_wave * modulated_wave |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
import matplotlib.pyplot as plt | |
from IPython.display import Audio | |
import math | |
PI2 = math.pi * 2 | |
class Wave: | |
def __init__(self, ys, ts, framerate): | |
self.ys = ys | |
self.ts = ts | |
self.framerate = framerate | |
def play_wave(self): | |
return Audio(data=self.ys, rate=self.framerate) | |
def plot(self): | |
fig = plt.figure() | |
plt.plot(self.ts[0:1000], self.ys[0:1000], '-') | |
plt.title('Wave sample in domain time') | |
plt.xlabel('time') | |
plt.ylabel('values') | |
class Signal: | |
def __init__(self, freq=400, amp=1.0, offset=0, function=np.sin): | |
self.freq = freq | |
self.amp = amp | |
self.offset = offset | |
self.periodic_function = function | |
def print_period(self): | |
print("Period (s): %f" %(1.0/self.freq)) | |
def compute_phases(self, ts): | |
return PI2 * self.freq * ts + self.offset | |
def evaluate(self, ts): | |
ts = np.asarray(ts) | |
phases = self.compute_phases(ts) | |
ys = self.amp * self.periodic_function(phases) | |
return ys | |
def create_wave(self, duration=2, start=0, framerate=3000): | |
n = round(duration * framerate) | |
ts = start + np.arange(n) / framerate | |
ys = self.evaluate(ts) | |
return Wave(ys, ts, framerate=framerate) | |
class WaveI(Wave): | |
def __init__(self, ys, ts, framerate): | |
super().__init__(ys, ts, framerate) | |
if ts is None: | |
self.ts = np.arange(len(ys)) / self.framerate | |
else: | |
self.ts = np.asanyarray(ts) | |
def plot_fft(self): | |
n = len(self.ys) | |
d = 1 / self.framerate | |
hs = np.fft.fft(self.ys) | |
fs = np.fft.fftfreq(n, d) | |
hs = np.asanyarray(hs) | |
fs = np.asanyarray(fs) | |
hs = np.fft.fftshift(hs) | |
amps = np.abs(hs) | |
fs = np.fft.fftshift(fs) | |
plt.plot(fs, amps ** 2) | |
plt.title('Wave in the frequency domain (real part)') | |
plt.xlabel('Frequency') | |
plt.ylabel('Power') | |
class FunctionBasedSignal(Signal): | |
def __init__(self, freq, function): | |
super().__init__(freq=freq) | |
self.periodic_function = function | |
def create_wave(self, duration=2, start=0, framerate=25000): | |
n = round(duration * framerate) | |
ts = start + np.arange(n) / framerate | |
ys = self.evaluate(ts) | |
return WaveI(ys, ts, framerate=framerate) | |
class HalfScalingFactor: | |
SCALE_FACTOR = np.float64(0.5) | |
@classmethod | |
def get_scale_factor(cls): | |
return cls.SCALE_FACTOR | |
@classmethod | |
def print_factor_scale(cls): | |
print(cls.SCALE_FACTOR) | |
class WaveModOps: | |
def __len__(self): | |
""" | |
Lenght of the wave | |
""" | |
return len(self.ys) | |
def __mul__(self, other): | |
""" | |
Multiplies two waves element-wise | |
""" | |
assert self.framerate == other.framerate | |
assert len(self) == len(other) | |
ys = self.ys * other.ys | |
return WaveM(ys, self.ts, self.framerate) | |
@property | |
def duration(self): | |
"""Duration (property). | |
returns: float duration in seconds | |
""" | |
return len(self.ys) / self.framerate | |
class Spectrum: | |
def __init__(self, hs, fs, framerate): | |
self.hs = np.asanyarray(hs) | |
self.fs = np.asanyarray(fs) | |
self.framerate = framerate | |
def render_full(self, high=None): | |
"""Extracts amps and fs from a full spectrum. | |
high: cutoff frequency | |
returns: fs, amps | |
""" | |
hs = np.fft.fftshift(self.hs) | |
amps = np.abs(hs) | |
fs = np.fft.fftshift(self.fs) | |
i = 0 if high is None else find_index(-high, fs) | |
j = None if high is None else find_index(high, fs) + 1 | |
return fs[i:j], amps[i:j] | |
def plot(self, high=None, **options): | |
"""Plots amplitude vs frequency. | |
Note: if this is a full spectrum, it ignores low and high | |
high: frequency to cut off at | |
""" | |
fs, amps = self.render_full(high) | |
plt.plot(fs, amps, **options) | |
def low_pass(self, cutoff, factor=0): | |
"""Attenuate frequencies above the cutoff. | |
cutoff: frequency in Hz | |
factor: what to multiply the magnitude by | |
""" | |
self.hs[abs(self.fs) > cutoff] *= factor | |
def make_wave(self, duration=1, start=0, framerate=11025): | |
"""Makes a Wave object. | |
duration: float seconds | |
start: float seconds | |
framerate: int frames per second | |
returns: Wave | |
""" | |
ys = np.fft.ifft(self.hs) | |
# NOTE: whatever the start time was, we lose it when | |
# we transform back; we could fix that by saving start | |
# time in the Spectrum | |
# ts = self.start + np.arange(len(ys)) / self.framerate | |
return WaveM(ys, ts=None, framerate=self.framerate) | |
class WaveM(WaveI, HalfScalingFactor, WaveModOps): | |
def scale(self): | |
self.ys = np.float64(self.ys) | |
self.ys *= self.get_scale_factor() | |
def make_spectrum(self): | |
n = len(self.ys) | |
d = 1 / self.framerate | |
hs = np.fft.fft(self.ys) | |
fs = np.fft.fftfreq(n, d) | |
return Spectrum(hs, fs, self.framerate) | |
@staticmethod | |
def convert( wave): | |
return WaveM(wave.ys, wave.ts, wave.framerate) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment