-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTheOne.py
More file actions
168 lines (135 loc) · 5.35 KB
/
Copy pathTheOne.py
File metadata and controls
168 lines (135 loc) · 5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import numpy as np
import matplotlib.pyplot as plt
def generate_wave(custom_function, samples=100, x_range=(-1, 1)):
"""
Generate a wave based on a user-defined function.
Args:
custom_function (callable): A function to generate the wave. Takes an array of x values as input.
samples (int): Number of points to generate.
x_range (tuple): Range of x values (start, end).
Returns:
np.ndarray: Generated wave values.
"""
x = np.linspace(x_range[0], x_range[1], samples) # Generate x values
y = custom_function(x) # Apply the custom function
return y
def plot_wave_as_events(wave_data):
"""
Plot a waveform four ways, treating the samples as a sequence of
discrete events (moments) rather than continuous time:
1. The raw sequence of amplitudes.
2. Local differences between neighboring moments (invertible, no new
information).
3. Frequency spectrum — a linear transform that needs every moment,
yet remains a sum of independent terms.
4. Spectrum after multiplication by a carrier. The product introduces
sum/difference frequencies present in neither original spectrum.
The panels illustrate three kinds of dependence among moments:
local, global-but-linear, and nonlinear interaction.
Args:
wave_data (np.ndarray): The wave values to plot.
"""
n = np.arange(len(wave_data))
event_indices = range(len(wave_data)) # Use sequential event indices
amplitude_changes = np.diff(wave_data, prepend=wave_data[0]) # Local: 2-sample window
# FFT: global (needs every sample) but still linear
spectrum = np.fft.rfft(wave_data)
freqs = np.fft.rfftfreq(len(wave_data), d=1.0) # cycles per sample
magnitude = np.abs(spectrum)
# Modulation: multiply by a carrier at a quarter of the sampling rate.
# Sum/difference sidebands appear that are in neither input spectrum.
carrier_cycles = max(1, len(wave_data) // 4)
carrier = np.cos(2 * np.pi * carrier_cycles * n / len(wave_data))
modulated = wave_data * carrier
modulated_spectrum = np.abs(np.fft.rfft(modulated))
plt.figure(figsize=(20, 5))
# Plot original wave with event indices
plt.subplot(1, 4, 1)
plt.plot(event_indices, wave_data, label="Amplitude", color="blue")
plt.title("Waveform as Event Sequence")
plt.xlabel("Event Index")
plt.ylabel("Amplitude")
plt.grid(True)
plt.legend()
# Plot only state changes (local)
plt.subplot(1, 4, 2)
plt.stem(event_indices, amplitude_changes, linefmt="orange", markerfmt="o", basefmt="gray")
plt.title("Amplitude Changes (Local)")
plt.xlabel("Event Index")
plt.ylabel("Change in Amplitude")
plt.grid(True)
# Plot the frequency spectrum (global, linear)
plt.subplot(1, 4, 3)
plt.stem(freqs, magnitude, linefmt="green", markerfmt="o", basefmt="gray")
plt.title("Frequency Spectrum (Global)")
plt.xlabel("Frequency (cycles/sample)")
plt.ylabel("Magnitude")
plt.grid(True)
# Plot the modulated spectrum (nonlinear interaction)
plt.subplot(1, 4, 4)
plt.stem(freqs, modulated_spectrum, linefmt="red", markerfmt="o", basefmt="gray")
plt.title("Modulated Spectrum (Nonlinear)")
plt.xlabel("Frequency (cycles/sample)")
plt.ylabel("Magnitude")
plt.grid(True)
plt.tight_layout()
plt.show()
def main():
"""
Main function to coordinate wave generation and plotting.
"""
# Define user-selectable functions
def sine_wave(x):
return np.sin(2 * np.pi * x)
def parabola(x):
return x**2
def cubic(x):
return x**3
def cosine_wave(x):
return np.cos(2 * np.pi * x)
def tangent_wave(x):
return np.tan(2 * np.pi * x) / 10
def exponential(x):
return np.exp(x)
def logarithmic(x):
return np.log(x + 1.1)
def square_wave(x):
return np.sign(np.sin(2 * np.pi * x))
def sawtooth_wave(x):
return 2 * (x - np.floor(x + 0.5))
def quartic(x):
return x**4 - x**2
def noisy_sine_wave(x):
return np.sin(2 * np.pi * x) + 0.2 * np.random.randn(len(x))
def hybrid_sin_exp(x):
return np.sin(2 * np.pi * x) * np.exp(-x)
def piecewise_function(x):
return np.where(x < 0, x**2, np.sin(2 * np.pi * x))
# Map choices to functions
functions = {
"1": ("Sine Wave", sine_wave),
"2": ("Parabola", parabola),
"3": ("Cubic", cubic),
"4": ("Cosine Wave", cosine_wave),
"5": ("Tangent Wave", tangent_wave),
"6": ("Exponential", exponential),
"7": ("Logarithmic", logarithmic),
"8": ("Square Wave", square_wave),
"9": ("Sawtooth Wave", sawtooth_wave),
"10": ("Quartic", quartic),
"11": ("Noisy Sine Wave", noisy_sine_wave),
"12": ("Hybrid Sin-Exp", hybrid_sin_exp),
"13": ("Piecewise Function", piecewise_function)
}
# Display options to the user
print("Choose a function to experiment:")
for key, (name, _) in functions.items():
print(f"{key}: {name}")
# Get user input
choice = input("Enter your choice (1-13): ").strip()
chosen_function = functions.get(choice, ("Sine Wave", sine_wave))[1]
# Generate and plot the wave
wave = generate_wave(custom_function=chosen_function, samples=100, x_range=(-1, 1))
plot_wave_as_events(wave)
if __name__ == "__main__":
main()