Skip to content

Commit 1db375e

Browse files
committed
WIP: scaling factor for repeated tendencies
1 parent f0eabeb commit 1db375e

File tree

1 file changed

+82
-29
lines changed

1 file changed

+82
-29
lines changed

waveform_editor/tendencies/repeat.py

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Optional
22

33
import numpy as np
4+
import param
45

56
from waveform_editor.tendencies.base import BaseTendency
67

@@ -10,11 +11,25 @@ class RepeatTendency(BaseTendency):
1011
Tendency class for a repeated signal.
1112
"""
1213

14+
user_frequency = param.Number(
15+
default=None,
16+
bounds=(0, None),
17+
inclusive_bounds=(False, True),
18+
doc="The frequency of the tendency, as provided by the user.",
19+
)
20+
user_period = param.Number(
21+
default=None,
22+
bounds=(0, None),
23+
inclusive_bounds=(False, True),
24+
doc="The period of the tendency, as provided by the user.",
25+
)
26+
1327
def __init__(self, **kwargs):
1428
waveform = kwargs.pop("user_waveform", []) or []
1529
from waveform_editor.waveform import Waveform
1630

1731
self.waveform = Waveform(waveform=waveform, is_repeated=True)
32+
self.period = 1
1833
super().__init__(**kwargs)
1934
if not self.waveform.tendencies:
2035
error_msg = "There are no tendencies in the repeated waveform.\n"
@@ -39,19 +54,44 @@ def __init__(self, **kwargs):
3954
self.waveform.tendencies[0].set_previous_tendency(self.waveform.tendencies[-1])
4055
self.waveform.tendencies[-1].set_next_tendency(self.waveform.tendencies[0])
4156

42-
self._set_bounds()
57+
self._set_frequency()
58+
self.values_changed = True
4359
self.annotations.add_annotations(self.waveform.annotations)
4460

45-
def _set_bounds(self):
46-
"""Sets the start and end values, as well as derivatives"""
47-
_, start_values = self.get_value(np.array([self.start]))
48-
self.start_value = start_values[0]
49-
start_derivatives = self.get_derivative(np.array([self.start]))
50-
self.start_derivative = start_derivatives[0]
51-
_, end_values = self.get_value(np.array([self.end]))
52-
self.end_value = end_values[0]
53-
end_derivatives = self.get_derivative(np.array([self.end]))
54-
self.end_derivative = end_derivatives[0]
61+
def _set_frequency(self):
62+
has_freq_param = self.user_frequency is not None or self.user_period is not None
63+
start = self.waveform.tendencies[0].start
64+
end = self.waveform.tendencies[-1].end
65+
66+
if has_freq_param and (start != 0 or end != 1):
67+
error_msg = (
68+
"If the period of frequency of the repeated signal is provided, \nit is"
69+
"advised that the tendencies start at 0, and end at 1.\n"
70+
)
71+
self.annotations.add(self.line_number, error_msg, is_warning=True)
72+
73+
if self.user_frequency is not None:
74+
if self.user_period is not None and not np.isclose(
75+
self.user_frequency, 1 / self.user_period
76+
):
77+
error_msg = (
78+
"The frequency and period do not match! (freq != 1 / period).\n"
79+
"The period will be ignored and only the frequency is used.\n"
80+
)
81+
self.annotations.add(self.line_number, error_msg, is_warning=True)
82+
self.period = 1 / self.user_frequency
83+
elif self.user_period is not None:
84+
self.period = self.user_period
85+
else:
86+
self.period = self.waveform.tendencies[-1].end
87+
88+
if has_freq_param and self.period > self.duration:
89+
error_msg = (
90+
"If the period of repeated tendency must be shorter than its duration. "
91+
"\nThe frequency or period will be ignored\n"
92+
)
93+
self.annotations.add(self.line_number, error_msg, is_warning=True)
94+
self.period = self.waveform.tendencies[-1].end
5595

5696
def get_value(
5797
self, time: Optional[np.ndarray] = None
@@ -69,27 +109,36 @@ def get_value(
69109
if not self.waveform.tendencies:
70110
return np.array([0]), np.array([0])
71111
length = self.waveform.calc_length()
112+
repeat_factor = self.period / length
113+
72114
if time is None:
73115
time, values = self.waveform.get_value()
74-
repeat = int(np.ceil(self.duration / length))
75-
repetition_array = np.arange(repeat) * length
76-
time = (time + repetition_array[:, np.newaxis]).flatten() + self.start
77-
values = np.tile(values, repeat)
78116

79-
# cut off everything after self.end
80-
assert time[-1] >= self.end
117+
# Compute how many full cycles fit in duration
118+
repeat_count = int(np.round(self.duration / self.period))
119+
repetition_array = np.arange(repeat_count) * self.period
120+
121+
time = (
122+
(time * repeat_factor) + repetition_array[:, np.newaxis]
123+
).flatten() + self.start
124+
values = np.tile(values, repeat_count)
125+
126+
# Ensure we don't go beyond self.end
127+
time = np.clip(time, self.start, self.end)
81128
cut_index = np.argmax(time >= self.end)
82-
time = time[: cut_index + 1]
83129

84-
values = values[: cut_index + 1]
85-
if time[-1] != self.end:
86-
time[-1] = self.end
87-
_, end_array = self.waveform.get_value(
88-
np.array([(self.end - self.start) % length])
89-
)
90-
values[-1] = end_array[0]
130+
if cut_index > 0:
131+
time = time[: cut_index + 1]
132+
values = values[: cut_index + 1]
133+
134+
if time[-1] != self.end:
135+
time[-1] = self.end
136+
_, end_array = self.waveform.get_value(
137+
np.array([(self.end - self.start) % self.period])
138+
)
139+
values[-1] = end_array[0]
91140
else:
92-
relative_times = (time - self.start) % length
141+
relative_times = ((time - self.start) % self.period) / repeat_factor
93142
_, values = self.waveform.get_value(relative_times)
94143
return time, values
95144

@@ -104,7 +153,11 @@ def get_derivative(self, time: np.ndarray) -> np.ndarray:
104153
"""
105154
if not self.waveform.tendencies:
106155
return np.array([0])
107-
length = self.waveform.calc_length()
108-
relative_times = (time - self.start) % length
109-
derivatives = self.waveform.get_derivative(relative_times)
156+
157+
base_length = self.waveform.calc_length()
158+
repeat_factor = self.period / base_length
159+
160+
relative_times = ((time - self.start) % self.period) / repeat_factor
161+
derivatives = self.waveform.get_derivative(relative_times) / repeat_factor
162+
110163
return derivatives

0 commit comments

Comments
 (0)