11from typing import Optional
22
33import numpy as np
4+ import param
45
56from 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, \n it 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+ "\n The 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