waveforms 1.9.4__tar.gz → 2.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. {waveforms-1.9.4/waveforms.egg-info → waveforms-2.0.2}/PKG-INFO +4 -13
  2. {waveforms-1.9.4 → waveforms-2.0.2}/pyproject.toml +1 -11
  3. {waveforms-1.9.4 → waveforms-2.0.2}/setup.py +2 -2
  4. waveforms-2.0.2/waveforms/__main__.py +16 -0
  5. waveforms-2.0.2/waveforms/distortion.py +376 -0
  6. waveforms-2.0.2/waveforms/utils.py +84 -0
  7. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/version.py +1 -1
  8. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/waveform.py +41 -11
  9. {waveforms-1.9.4 → waveforms-2.0.2/waveforms.egg-info}/PKG-INFO +4 -13
  10. waveforms-2.0.2/waveforms.egg-info/SOURCES.txt +25 -0
  11. waveforms-2.0.2/waveforms.egg-info/requires.txt +17 -0
  12. waveforms-1.9.4/src/ikcp.c +0 -1466
  13. waveforms-1.9.4/src/ikcp.h +0 -415
  14. waveforms-1.9.4/src/kcp.c +0 -184
  15. waveforms-1.9.4/tests/test_dicttree.py +0 -128
  16. waveforms-1.9.4/tests/test_kad.py +0 -341
  17. waveforms-1.9.4/tests/test_namespace.py +0 -137
  18. waveforms-1.9.4/tests/test_registry.py +0 -95
  19. waveforms-1.9.4/tests/test_scan_iter.py +0 -375
  20. waveforms-1.9.4/tests/test_serialize.py +0 -34
  21. waveforms-1.9.4/waveforms/__main__.py +0 -24
  22. waveforms-1.9.4/waveforms/autoreload.py +0 -26
  23. waveforms-1.9.4/waveforms/baseconfig.py +0 -268
  24. waveforms-1.9.4/waveforms/cache.py +0 -163
  25. waveforms-1.9.4/waveforms/dicttree.py +0 -511
  26. waveforms-1.9.4/waveforms/loader.py +0 -97
  27. waveforms-1.9.4/waveforms/namespace.py +0 -261
  28. waveforms-1.9.4/waveforms/registry.py +0 -672
  29. waveforms-1.9.4/waveforms/scan/__init__.py +0 -4
  30. waveforms-1.9.4/waveforms/scan/base.py +0 -544
  31. waveforms-1.9.4/waveforms/scan/dataset.py +0 -0
  32. waveforms-1.9.4/waveforms/scan/expression.py +0 -360
  33. waveforms-1.9.4/waveforms/scan/scanner.py +0 -223
  34. waveforms-1.9.4/waveforms/scan/transforms.py +0 -16
  35. waveforms-1.9.4/waveforms/scan_iter.py +0 -10
  36. waveforms-1.9.4/waveforms/security/__init__.py +0 -1
  37. waveforms-1.9.4/waveforms/security/verify.py +0 -43
  38. waveforms-1.9.4/waveforms/server/__init__.py +0 -21
  39. waveforms-1.9.4/waveforms/server/__main__.py +0 -41
  40. waveforms-1.9.4/waveforms/server/echo.py +0 -44
  41. waveforms-1.9.4/waveforms/storage/__init__.py +0 -0
  42. waveforms-1.9.4/waveforms/storage/backend/__init__.py +0 -0
  43. waveforms-1.9.4/waveforms/storage/backend/redis.py +0 -204
  44. waveforms-1.9.4/waveforms/storage/base_dataset.py +0 -352
  45. waveforms-1.9.4/waveforms/storage/chunk.py +0 -60
  46. waveforms-1.9.4/waveforms/storage/dataset.py +0 -127
  47. waveforms-1.9.4/waveforms/storage/file.py +0 -273
  48. waveforms-1.9.4/waveforms/storage/models/__init__.py +0 -22
  49. waveforms-1.9.4/waveforms/storage/models/base.py +0 -4
  50. waveforms-1.9.4/waveforms/storage/models/config.py +0 -28
  51. waveforms-1.9.4/waveforms/storage/models/file.py +0 -89
  52. waveforms-1.9.4/waveforms/storage/models/ipy.py +0 -58
  53. waveforms-1.9.4/waveforms/storage/models/record.py +0 -161
  54. waveforms-1.9.4/waveforms/storage/models/report.py +0 -22
  55. waveforms-1.9.4/waveforms/storage/models/tag.py +0 -93
  56. waveforms-1.9.4/waveforms/storage/storage.py +0 -44
  57. waveforms-1.9.4/waveforms/sys/__init__.py +0 -0
  58. waveforms-1.9.4/waveforms/sys/chat.py +0 -688
  59. waveforms-1.9.4/waveforms/sys/device/__init__.py +0 -3
  60. waveforms-1.9.4/waveforms/sys/device/basedevice.py +0 -221
  61. waveforms-1.9.4/waveforms/sys/device/loader.py +0 -86
  62. waveforms-1.9.4/waveforms/sys/device/utils.py +0 -46
  63. waveforms-1.9.4/waveforms/sys/drivers/FakeInstrument.py +0 -52
  64. waveforms-1.9.4/waveforms/sys/drivers/__init__.py +0 -0
  65. waveforms-1.9.4/waveforms/sys/ipy_events.py +0 -125
  66. waveforms-1.9.4/waveforms/sys/net/__init__.py +0 -0
  67. waveforms-1.9.4/waveforms/sys/net/bencoder.py +0 -205
  68. waveforms-1.9.4/waveforms/sys/net/cli.py +0 -169
  69. waveforms-1.9.4/waveforms/sys/net/dhcp.py +0 -543
  70. waveforms-1.9.4/waveforms/sys/net/dhcpd.py +0 -176
  71. waveforms-1.9.4/waveforms/sys/net/kad.py +0 -1142
  72. waveforms-1.9.4/waveforms/sys/net/kcp.py +0 -192
  73. waveforms-1.9.4/waveforms/sys/progress.py +0 -190
  74. waveforms-1.9.4/waveforms/sys/storage/__init__.py +0 -0
  75. waveforms-1.9.4/waveforms/sys/storage/crud.py +0 -205
  76. waveforms-1.9.4/waveforms/sys/storage/models.py +0 -642
  77. waveforms-1.9.4/waveforms/units/__init__.py +0 -119
  78. waveforms-1.9.4/waveforms/utils.py +0 -67
  79. waveforms-1.9.4/waveforms/visualization/__init__.py +0 -188
  80. waveforms-1.9.4/waveforms/visualization/__main__.py +0 -71
  81. waveforms-1.9.4/waveforms/visualization/_autoplot.py +0 -457
  82. waveforms-1.9.4/waveforms/visualization/plot_layout.py +0 -408
  83. waveforms-1.9.4/waveforms/visualization/plot_seq.py +0 -90
  84. waveforms-1.9.4/waveforms/visualization/qdat.py +0 -152
  85. waveforms-1.9.4/waveforms/visualization/widgets.py +0 -86
  86. waveforms-1.9.4/waveforms.egg-info/SOURCES.txt +0 -96
  87. waveforms-1.9.4/waveforms.egg-info/requires.txt +0 -28
  88. {waveforms-1.9.4 → waveforms-2.0.2}/LICENSE +0 -0
  89. {waveforms-1.9.4 → waveforms-2.0.2}/MANIFEST.in +0 -0
  90. {waveforms-1.9.4 → waveforms-2.0.2}/README.md +0 -0
  91. {waveforms-1.9.4 → waveforms-2.0.2}/setup.cfg +0 -0
  92. {waveforms-1.9.4 → waveforms-2.0.2}/src/waveform.h +0 -0
  93. {waveforms-1.9.4 → waveforms-2.0.2}/tests/test_multi_drag.py +0 -0
  94. {waveforms-1.9.4 → waveforms-2.0.2}/tests/test_waveform.py +0 -0
  95. {waveforms-1.9.4 → waveforms-2.0.2}/tests/test_wavevstack.py +0 -0
  96. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/__init__.py +0 -0
  97. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/_waveform.pyi +0 -0
  98. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/_waveform.pyx +0 -0
  99. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/multy_drag.py +0 -0
  100. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/waveform_parser.py +0 -0
  101. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms.egg-info/dependency_links.txt +0 -0
  102. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms.egg-info/entry_points.txt +0 -0
  103. {waveforms-1.9.4 → waveforms-2.0.2}/waveforms.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: waveforms
3
- Version: 1.9.4
3
+ Version: 2.0.2
4
4
  Summary: Edit waveforms used in experiment
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -20,27 +20,17 @@ Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Transla
20
20
  Classifier: Programming Language :: Python
21
21
  Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
23
24
  Requires-Python: >=3.10
24
25
  Description-Content-Type: text/markdown
25
26
  License-File: LICENSE
26
27
  Requires-Dist: click>=7.1.2
27
28
  Requires-Dist: dill>=0.3.6
28
- Requires-Dist: GitPython>=3.1.14
29
- Requires-Dist: matplotlib>=3.7.2
30
29
  Requires-Dist: numpy>=1.13.3
31
30
  Requires-Dist: ply>=3.11
32
31
  Requires-Dist: scipy>=1.0.0
33
32
  Provides-Extra: full
34
- Requires-Dist: antlr4-python3-runtime==4.12.0; extra == "full"
35
- Requires-Dist: blinker>=1.4; extra == "full"
36
- Requires-Dist: cryptography>=3.4.7; extra == "full"
37
- Requires-Dist: matplotlib>=3.4.1; extra == "full"
38
33
  Requires-Dist: msgpack>=1.0.5; extra == "full"
39
- Requires-Dist: portalocker>=1.4.0; extra == "full"
40
- Requires-Dist: SQLAlchemy>=1.4.11; extra == "full"
41
- Requires-Dist: scikit-learn>=0.24.1; extra == "full"
42
- Requires-Dist: scikit-optimize>=0.8.1; extra == "full"
43
- Requires-Dist: tenacity>=8.2.2; extra == "full"
44
34
  Provides-Extra: test
45
35
  Requires-Dist: pytest>=7.4.0; extra == "test"
46
36
  Requires-Dist: pytest-asyncio>=0.21.1; extra == "test"
@@ -48,6 +38,7 @@ Provides-Extra: docs
48
38
  Requires-Dist: Sphinx; extra == "docs"
49
39
  Requires-Dist: sphinxcontrib-napoleon; extra == "docs"
50
40
  Requires-Dist: sphinxcontrib-zopeext; extra == "docs"
41
+ Dynamic: license-file
51
42
 
52
43
  # waveforms
53
44
  [![View build status](https://github.com/feihoo87/waveforms/actions/workflows/workflow.yml/badge.svg)](https://github.com/feihoo87/waveforms/)
@@ -26,13 +26,12 @@ classifiers = [
26
26
  "Programming Language :: Python",
27
27
  "Programming Language :: Python :: 3.10",
28
28
  "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
29
30
  ]
30
31
  requires-python = ">= 3.10"
31
32
  dependencies = [
32
33
  "click>=7.1.2",
33
34
  "dill>=0.3.6",
34
- "GitPython>=3.1.14",
35
- "matplotlib>=3.7.2",
36
35
  "numpy>=1.13.3",
37
36
  "ply>=3.11",
38
37
  "scipy>=1.0.0"
@@ -41,16 +40,7 @@ dynamic = ["version"]
41
40
 
42
41
  [project.optional-dependencies]
43
42
  full = [
44
- "antlr4-python3-runtime==4.12.0",
45
- "blinker>=1.4",
46
- "cryptography>=3.4.7",
47
- "matplotlib>=3.4.1",
48
43
  "msgpack>=1.0.5",
49
- "portalocker>=1.4.0",
50
- "SQLAlchemy>=1.4.11",
51
- "scikit-learn>=0.24.1",
52
- "scikit-optimize>=0.8.1",
53
- "tenacity>=8.2.2",
54
44
  ]
55
45
  test = [
56
46
  "pytest>=7.4.0",
@@ -20,8 +20,8 @@ def get_extensions():
20
20
  #from pathlib import Path
21
21
 
22
22
  extensions = [
23
- Extension('waveforms.sys.net._kcp', ['src/kcp.c', 'src/ikcp.c'],
24
- include_dirs=['src']),
23
+ # Extension('waveforms.sys.net._kcp', ['src/kcp.c', 'src/ikcp.c'],
24
+ # include_dirs=['src']),
25
25
  # Extension(
26
26
  # 'waveforms.math.npufunc',
27
27
  # ['src/multi_type_logit.c'],
@@ -0,0 +1,16 @@
1
+ import click
2
+
3
+
4
+ @click.group()
5
+ def main():
6
+ pass
7
+
8
+
9
+ @main.command()
10
+ def hello():
11
+ """Print hello world."""
12
+ click.echo('hello, world')
13
+
14
+
15
+ if __name__ == '__main__':
16
+ main()
@@ -0,0 +1,376 @@
1
+ import warnings
2
+ from itertools import repeat, zip_longest
3
+ from typing import Sequence
4
+
5
+ import numpy as np
6
+ from scipy.fftpack import fft, fftfreq, ifft, ifftshift
7
+ from scipy.optimize import curve_fit
8
+ from scipy.signal import fftconvolve, lfilter, lfiltic, tf2zpk, zpk2sos, zpk2tf
9
+
10
+
11
+ def shift(signal: np.ndarray, delay: float, dt: float) -> np.ndarray:
12
+ """
13
+ delay a signal
14
+
15
+ Args:
16
+ signal (np.ndarray): input signal
17
+ delay (float): delayed time
18
+ dt (float): time step of signal samples
19
+
20
+ Returns:
21
+ np.ndarray: delayed signal
22
+ """
23
+ points = int(delay // dt)
24
+ delta = delay / dt - points
25
+
26
+ if delta > 0:
27
+ ker = np.array([0, 1 - delta, delta])
28
+ signal = np.convolve(signal, ker, mode='same')
29
+
30
+ if points == 0:
31
+ return signal
32
+
33
+ ret = np.zeros_like(signal)
34
+ if points < 0:
35
+ ret[:points] = signal[-points:]
36
+ else:
37
+ ret[points:] = signal[:-points]
38
+ return ret
39
+
40
+
41
+ def extractKernel(sig_in, sig_out, sample_rate, bw=None, skip=0):
42
+ corr = fft(sig_in) / fft(sig_out)
43
+ ker = np.real(ifftshift(ifft(corr)))
44
+ if bw is not None and bw < 0.5 * sample_rate:
45
+ k = np.exp(-0.5 * np.linspace(-3.0, 3.0, int(2 * sample_rate / bw))**2)
46
+ ker = np.convolve(ker, k / k.sum(), mode='same')
47
+ return ker[int(skip):len(ker) - int(skip)]
48
+
49
+
50
+ def zDistortKernel(dt: float, params: Sequence[tuple]) -> np.ndarray:
51
+ t = 3 * np.asarray(params)[:, 0].max()
52
+ omega = 2 * np.pi * fftfreq(int(t / dt) + 1, dt)
53
+
54
+ H = 1
55
+ for tau, A in params:
56
+ H += (1j * A * omega * tau) / (1j * omega * tau + 1)
57
+
58
+ ker = ifftshift(ifft(1 / H)).real
59
+ return ker
60
+
61
+
62
+ def high_pass_filter(tau, sample_rate):
63
+ """
64
+ high pass filter
65
+ """
66
+ k = 2.0 * tau * sample_rate
67
+ a = [1.0, (1 - k) / (1 + k)]
68
+ b = [k / (1 + k), -k / (1 + k)]
69
+ return b, a
70
+
71
+
72
+ def exp_decay_filter_old(amp, tau, sample_rate):
73
+ """
74
+ exp decay filter
75
+
76
+ A
77
+ H(w) = --------------------
78
+ 1 - 1j / (w * tau)
79
+
80
+ Args:
81
+ amp (float): amplitude of the filter
82
+ tau (float): decay time
83
+ sample_rate (float): sampling rate
84
+ """
85
+
86
+ alpha = 1 - np.exp(-1 / (abs(sample_rate * tau) * (1 + amp)))
87
+
88
+ if amp >= 0:
89
+ k = amp / (1 + amp - alpha)
90
+ a = [(1 - k + k * alpha), -(1 - k) * (1 - alpha)]
91
+ else:
92
+ k = -amp / (1 + amp) / (1 - alpha)
93
+ a = [(1 + k - k * alpha), -(1 + k) * (1 - alpha)]
94
+
95
+ b = [1 / a[0], -(1 - alpha) / a[0]]
96
+ a = [1, a[1] / a[0]]
97
+
98
+ return b, a
99
+
100
+
101
+ def exp_decay_filter(amp: float | Sequence[float],
102
+ tau: float | Sequence[float],
103
+ sample_rate: float,
104
+ inv: bool = False,
105
+ output='ba') -> tuple[np.ndarray, np.ndarray]:
106
+ """
107
+ exp decay filter
108
+
109
+ Infinite impulse response as multiexponential decay. When input signal
110
+ is the Heaviside theta function u(t), the output signal is:
111
+ out(t) = u(t) * (1 - A_1 * exp(-t / tau_1) - A_2 * exp(-t / tau_2) ...)
112
+ where A_i and tau_i are the amplitude and decay time of the i-th
113
+ exponential decay.
114
+
115
+ The transfer function of the filter is:
116
+
117
+ H(w) = 1 - H_1(w) - H_2(w) - ... - H_n(w)
118
+
119
+ where
120
+ A_i
121
+ H_i(w) = --------------------------
122
+ 1 - 1 / (1j * w * tau_i)
123
+
124
+ Args:
125
+ amp (float): amplitude of the filter
126
+ tau (float): decay time
127
+ sample_rate (float): sampling rate
128
+ inv (bool): if True, the filter is inverted
129
+ output (str): output type, 'ba' for numerator (b) and denominator (a)
130
+ polynomials, 'sos' for second-order sections, 'zpk' for zeros (z),
131
+ poles (p) and gain (k). See scipy.signal.lfilter for more.
132
+
133
+ Returns:
134
+ tuple: (b, a) array like, numerator (b) and denominator (a)
135
+ polynomials of the IIR filter. See scipy.signal.lfilter for more.
136
+ """
137
+
138
+ if isinstance(amp, (int, float, complex)):
139
+ amp = [amp]
140
+ tau = [tau]
141
+ numerator, denominator = np.poly1d([0.0]), np.poly1d([1.0])
142
+ for i, (A, t) in enumerate(zip(amp, tau)):
143
+ denominator = denominator * np.poly1d([1, -1 / t])
144
+ n = np.poly1d([-A, 0.0])
145
+ for j, t_ in enumerate(tau):
146
+ if j != i:
147
+ n = n * np.poly1d([1, -1 / t_])
148
+ numerator = numerator + n
149
+ numerator = numerator + denominator
150
+
151
+ z = np.exp(-numerator.roots / sample_rate)
152
+ p = np.exp(-denominator.roots / sample_rate)
153
+ if inv:
154
+ z, p = p, z
155
+ k = numerator(0) / denominator(0) * np.prod(1 - p) / np.prod(1 - z)
156
+
157
+ if output == 'sos':
158
+ return zpk2sos(z, p, k)
159
+ elif output == 'ba':
160
+ return zpk2tf(z, p, k)
161
+ elif output == 'zpk':
162
+ return z, p, k
163
+
164
+
165
+ def reflection_filter(f, A, tau):
166
+ """
167
+ reflection filter
168
+
169
+ Infinite impulse response as reflection. When input signal
170
+ is in(t), the output signal is:
171
+ out(t) = in(t) + A * in(t - tau) + A^2 * in(t - 2 * tau) + ...
172
+
173
+ The transfer function of the filter is:
174
+ 1 - A
175
+ H(w) = ----------------------------
176
+ 1 - A * exp(- i * w * tau)
177
+ Args:
178
+ f (float): frequency
179
+ A (float): amplitude of the reflection
180
+ tau (float): delay time
181
+ """
182
+ return (1 - A) / (1 - A * np.exp(-2j * np.pi * f * tau))
183
+
184
+
185
+ def reflection(sig, A, tau, sample_rate):
186
+ freq = np.fft.fftfreq(len(sig), 1 / sample_rate)
187
+ return np.fft.ifft(np.fft.fft(sig) * reflection_filter(freq, A, tau)).real
188
+
189
+
190
+ def correct_reflection(sig, A, tau, sample_rate=None):
191
+ from waveforms.waveform import Waveform
192
+
193
+ if isinstance(sig, Waveform):
194
+ return 1 / (1 - A) * sig - A / (1 - A) * (sig >> tau)
195
+ if sample_rate is not None:
196
+ freq = np.fft.fftfreq(len(sig), 1 / sample_rate)
197
+ return np.fft.ifft(np.fft.fft(sig) /
198
+ reflection_filter(freq, A, tau)).real
199
+ else:
200
+ raise ValueError('sample_rate is not given')
201
+
202
+
203
+ def combine_filters(
204
+ filters: list[tuple[np.ndarray,
205
+ np.ndarray]]) -> tuple[np.ndarray, np.ndarray]:
206
+ """
207
+ combine filters
208
+
209
+ Args:
210
+ filters (list): list of (b, a) array like, numerator (b) and denominator
211
+ (a) polynomials of the IIR filter. See scipy.signal.lfilter for more.
212
+
213
+ Returns:
214
+ tuple: (b, a) array like, numerator (b) and denominator (a)
215
+ polynomials of the combined filter. See scipy.signal.lfilter for more.
216
+ """
217
+ b, a = np.poly1d([1.0]), np.poly1d([1.0])
218
+ for b_, a_ in filters:
219
+ b = b * np.poly1d(b_)
220
+ a = a * np.poly1d(a_)
221
+ return b.coeffs, a.coeffs
222
+
223
+
224
+ def factor_filter(b, a):
225
+ """
226
+ factor filter
227
+
228
+ Args:
229
+ b (array_like): numerator polynomial of the IIR filter.
230
+ a (array_like): denominator polynomial of the IIR filter.
231
+
232
+ Returns:
233
+ list: list of (b, a) array like, numerator (b) and denominator
234
+ """
235
+ b, a = np.poly1d(b), np.poly1d(a)
236
+ p = a.roots
237
+ q = b.roots
238
+ b_amp = (b[0] / a[0])**(1 / max(len(q), len(p)))
239
+ filters = []
240
+ for a_, b_ in zip_longest(p, q, fillvalue=0):
241
+ filters.append(([b_amp, -b_amp * b_], [1, -a_]))
242
+ return filters
243
+
244
+
245
+ def stable_filter(exp_decay_filters: list, sample_rate: float):
246
+ """
247
+ check if the filter is stable
248
+
249
+ Args:
250
+ exp_decay_filters (list): list of (amp, tau) pairs
251
+ """
252
+ filters = []
253
+ for amp, tau in exp_decay_filters:
254
+ a, b = exp_decay_filter(amp, tau, sample_rate)
255
+ filters.append((b, a))
256
+
257
+ b, a = combine_filters(filters)
258
+ z, p, k = tf2zpk(b, a)
259
+ if np.all(np.abs(p) < 1):
260
+ return True
261
+ else:
262
+ return False
263
+
264
+
265
+ def predistort(sig: np.ndarray,
266
+ filters: list = None,
267
+ ker: np.ndarray = None,
268
+ initial: float = 0.0,
269
+ initial_x: np.ndarray | None = None,
270
+ initial_y: np.ndarray | None = None,
271
+ zi: np.ndarray | None = None,
272
+ return_zf: bool = False) -> np.ndarray:
273
+ if filters is not None:
274
+ b, a = combine_filters(filters)
275
+ z, p, k = tf2zpk(b, a)
276
+ if np.all(np.abs(p) < 1):
277
+ pass
278
+ else:
279
+ warnings.warn('Warning: filter is unstable')
280
+
281
+ if zi is None:
282
+ if initial_x is None:
283
+ initial_x = np.full((len(b) - 1, ), initial)
284
+ else:
285
+ initial_x = np.asarray(initial_x)[:len(b) - 1]
286
+ if initial_y is None:
287
+ initial_y = np.full((len(a) - 1, ), initial)
288
+ else:
289
+ initial_y = np.asarray(initial_y)[:len(a) - 1]
290
+ zi = lfiltic(
291
+ b,
292
+ a,
293
+ initial_y,
294
+ initial_x,
295
+ )
296
+ sig, zf = lfilter(b, a, sig, zi=zi)
297
+
298
+ if ker is None:
299
+ if return_zf:
300
+ return sig, zf
301
+ else:
302
+ return sig
303
+
304
+ size = len(sig)
305
+ sig = np.hstack((np.zeros_like(sig), sig, np.zeros_like(sig)))
306
+ start = size + len(ker) // 2
307
+ stop = start + size
308
+ points = fftconvolve(sig, ker, mode='full')[start:stop]
309
+ if return_zf:
310
+ return points, zf
311
+ else:
312
+ return points
313
+
314
+
315
+ def distort(points, params, sample_rate, initial=0.0):
316
+ filters = []
317
+ for amp, tau in np.asarray(params).reshape(-1, 2):
318
+ b, a = exp_decay_filter(amp, abs(tau), sample_rate)
319
+ filters.append((b, a))
320
+ return predistort(points, filters, initial=initial)
321
+
322
+
323
+ def phase_curve(t, params, df_dphi, pulse_width, start, wav, sample_rate):
324
+ lim = max(np.max(np.abs(t)), 20e-6)
325
+ num = round(2 * lim * sample_rate)
326
+ tlist = np.arange(num) / sample_rate - lim
327
+ points = wav(tlist)
328
+
329
+ pulse_points = round(pulse_width * sample_rate)
330
+ start_points = round((start + pulse_width) * sample_rate) - 1
331
+
332
+ ker = np.hstack(
333
+ [np.ones(pulse_points) / sample_rate,
334
+ np.zeros(start_points)])
335
+
336
+ points = np.convolve(2 * np.pi * df_dphi *
337
+ distort(points, params, sample_rate),
338
+ ker,
339
+ mode='same')
340
+ return np.interp(t, tlist, points)
341
+
342
+
343
+ if __name__ == '__main__':
344
+ import matplotlib.pyplot as plt
345
+ from waveforms import square
346
+
347
+ data = np.load('Z_distortion.npz')
348
+
349
+ x = data['time'] * 1e-6
350
+ y = data['phase']
351
+ df_dphi = 4343.313e6
352
+
353
+ sample_rate = 2e9
354
+ wav = 0.1 * (square(2e-6) << 1e-6)
355
+
356
+ def f(t, *params):
357
+ return phase_curve(t, params, df_dphi, 10e-9, 25e-9)
358
+
359
+ params = [-0.03, 0.1e-6, 0.02, 0.3e-6]
360
+ popt, pcov = curve_fit(f, x, y, p0=params)
361
+
362
+ plt.plot(x / 1e-6, y, 'o')
363
+ plt.semilogx(
364
+ x / 1e-6,
365
+ phase_curve(x,
366
+ params,
367
+ df_dphi,
368
+ 10e-9,
369
+ 0,
370
+ wav=wav,
371
+ sample_rate=sample_rate))
372
+ plt.plot(x / 1e-6, f(x, *popt))
373
+
374
+ plt.xlabel('delay [us]')
375
+ plt.ylabel('phase')
376
+ plt.show()
@@ -0,0 +1,84 @@
1
+ from itertools import repeat
2
+ from typing import Optional, Sequence
3
+
4
+ import numpy as np
5
+
6
+
7
+ def getFTMatrix(fList: Sequence[float],
8
+ numOfPoints: int,
9
+ phaseList: Optional[Sequence[float]] = None,
10
+ weight: Optional[np.ndarray] = None,
11
+ sampleRate: float = 1e9) -> np.ndarray:
12
+ """
13
+ get a matrix for Fourier transform
14
+
15
+ Args:
16
+ fList (Sequence[float]): list of frequencies
17
+ numOfPoints (int): size of signal frame
18
+ phaseList (Optional[Sequence[float]], optional): list of phase. Defaults to None.
19
+ weight (Optional[np.ndarray], optional): weight or list of weight. Defaults to None.
20
+ sampleRate (float, optional): sample rate of signal. Defaults to 1e9.
21
+
22
+ Returns:
23
+ numpy.ndarray: exp matrix
24
+
25
+ >>> shots, numOfPoints, sampleRate = 100, 1000, 1e9
26
+ >>> f1, f2 = -12.7e6, 32.8e6
27
+ >>> signal = np.random.randn(shots, numOfPoints)
28
+ >>> e = getFTMatrix([f1, f2], numOfPoints, sampleRate=sampleRate)
29
+ >>> ret = signal @ e
30
+ >>> ret.shape
31
+ (100, 2)
32
+ >>> t = np.arange(numOfPoints) / sampleRate
33
+ >>> signal = 0.8 * np.sin(2 * np.pi * f1 * t) + 0.2 * np.cos(2 * np.pi * f2 * t)
34
+ >>> signal @ e
35
+ array([-0.00766509-0.79518987j, 0.19531432+0.00207068j])
36
+ >>> spec = 2 * np.fft.fft(signal) / numOfPoints
37
+ >>> freq = np.fft.fftfreq(numOfPoints)
38
+ >>> e = getFTMatrix(freq, numOfPoints, sampleRate=1)
39
+ >>> np.allclose(spec, signal @ e)
40
+ True
41
+ """
42
+ e = []
43
+ t = np.linspace(0, numOfPoints / sampleRate, numOfPoints, endpoint=False)
44
+ if weight is None or len(weight) == 0:
45
+ weight = np.full(numOfPoints, 2 / numOfPoints)
46
+ if phaseList is None or len(phaseList) == 0:
47
+ phaseList = np.zeros_like(fList)
48
+ if weight.ndim == 1:
49
+ weightList = repeat(weight)
50
+ else:
51
+ weightList = weight
52
+ for f, phase, weight in zip(fList, phaseList, weightList):
53
+ e.append(weight * np.exp(-1j * (2 * np.pi * f * t + phase)))
54
+ return np.asarray(e).T
55
+
56
+
57
+ def shift(signal: np.ndarray, delay: float, dt: float) -> np.ndarray:
58
+ """
59
+ delay a signal
60
+
61
+ Args:
62
+ signal (np.ndarray): input signal
63
+ delay (float): delayed time
64
+ dt (float): time step of signal samples
65
+
66
+ Returns:
67
+ np.ndarray: delayed signal
68
+ """
69
+ points = int(delay // dt)
70
+ delta = delay / dt - points
71
+
72
+ if delta > 0:
73
+ ker = np.array([0, 1 - delta, delta])
74
+ signal = np.convolve(signal, ker, mode='same')
75
+
76
+ if points == 0:
77
+ return signal
78
+
79
+ ret = np.zeros_like(signal)
80
+ if points < 0:
81
+ ret[:points] = signal[-points:]
82
+ else:
83
+ ret[points:] = signal[:-points]
84
+ return ret
@@ -1,2 +1,2 @@
1
1
  """Define version number here and read it from setup.py automatically"""
2
- __version__ = "1.9.4"
2
+ __version__ = "2.0.2"
@@ -127,36 +127,38 @@ class Waveform:
127
127
  self.filters = None
128
128
  self.label = None
129
129
 
130
- def _begin(self):
131
- for i, s in enumerate(self.seq):
130
+ @staticmethod
131
+ def _begin(bounds, seq):
132
+ for i, s in enumerate(seq):
132
133
  if s is not _zero:
133
134
  if i == 0:
134
135
  return -inf
135
- return self.bounds[i - 1]
136
+ return bounds[i - 1]
136
137
  return inf
137
138
 
138
- def _end(self):
139
- N = len(self.bounds)
140
- for i, s in enumerate(self.seq[::-1]):
139
+ @staticmethod
140
+ def _end(bounds, seq):
141
+ N = len(bounds)
142
+ for i, s in enumerate(seq[::-1]):
141
143
  if s is not _zero:
142
144
  if i == 0:
143
145
  return inf
144
- return self.bounds[N - i - 1]
146
+ return bounds[N - i - 1]
145
147
  return -inf
146
148
 
147
149
  @property
148
150
  def begin(self):
149
151
  if self.start is None:
150
- return self._begin()
152
+ return self._begin(self.bounds, self.seq)
151
153
  else:
152
- return max(self.start, self._begin())
154
+ return max(self.start, self._begin(self.bounds, self.seq))
153
155
 
154
156
  @property
155
157
  def end(self):
156
158
  if self.stop is None:
157
- return self._end()
159
+ return self._end(self.bounds, self.seq)
158
160
  else:
159
- return min(self.stop, self._end())
161
+ return min(self.stop, self._end(self.bounds, self.seq))
160
162
 
161
163
  def sample(self,
162
164
  sample_rate=None,
@@ -611,6 +613,34 @@ class WaveVStack(Waveform):
611
613
  self.label = None
612
614
  self.function_lib = None
613
615
 
616
+ def __begin(self):
617
+ if self.wlist:
618
+ v = [self._begin(bounds, seq) for bounds, seq in self.wlist]
619
+ return min(v)
620
+ else:
621
+ return -inf
622
+
623
+ def __end(self):
624
+ if self.wlist:
625
+ v = [self._end(bounds, seq) for bounds, seq in self.wlist]
626
+ return max(v)
627
+ else:
628
+ return inf
629
+
630
+ @property
631
+ def begin(self):
632
+ if self.start is None:
633
+ return self.__begin()
634
+ else:
635
+ return max(self.start, self.__begin())
636
+
637
+ @property
638
+ def end(self):
639
+ if self.stop is None:
640
+ return self.__end()
641
+ else:
642
+ return min(self.stop, self.__end())
643
+
614
644
  def __call__(self, x, frag=False, out=None, function_lib=None):
615
645
  assert frag is False, 'WaveVStack does not support frag mode'
616
646
  out = np.full_like(x, self.offset, dtype=complex)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: waveforms
3
- Version: 1.9.4
3
+ Version: 2.0.2
4
4
  Summary: Edit waveforms used in experiment
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -20,27 +20,17 @@ Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Transla
20
20
  Classifier: Programming Language :: Python
21
21
  Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
23
24
  Requires-Python: >=3.10
24
25
  Description-Content-Type: text/markdown
25
26
  License-File: LICENSE
26
27
  Requires-Dist: click>=7.1.2
27
28
  Requires-Dist: dill>=0.3.6
28
- Requires-Dist: GitPython>=3.1.14
29
- Requires-Dist: matplotlib>=3.7.2
30
29
  Requires-Dist: numpy>=1.13.3
31
30
  Requires-Dist: ply>=3.11
32
31
  Requires-Dist: scipy>=1.0.0
33
32
  Provides-Extra: full
34
- Requires-Dist: antlr4-python3-runtime==4.12.0; extra == "full"
35
- Requires-Dist: blinker>=1.4; extra == "full"
36
- Requires-Dist: cryptography>=3.4.7; extra == "full"
37
- Requires-Dist: matplotlib>=3.4.1; extra == "full"
38
33
  Requires-Dist: msgpack>=1.0.5; extra == "full"
39
- Requires-Dist: portalocker>=1.4.0; extra == "full"
40
- Requires-Dist: SQLAlchemy>=1.4.11; extra == "full"
41
- Requires-Dist: scikit-learn>=0.24.1; extra == "full"
42
- Requires-Dist: scikit-optimize>=0.8.1; extra == "full"
43
- Requires-Dist: tenacity>=8.2.2; extra == "full"
44
34
  Provides-Extra: test
45
35
  Requires-Dist: pytest>=7.4.0; extra == "test"
46
36
  Requires-Dist: pytest-asyncio>=0.21.1; extra == "test"
@@ -48,6 +38,7 @@ Provides-Extra: docs
48
38
  Requires-Dist: Sphinx; extra == "docs"
49
39
  Requires-Dist: sphinxcontrib-napoleon; extra == "docs"
50
40
  Requires-Dist: sphinxcontrib-zopeext; extra == "docs"
41
+ Dynamic: license-file
51
42
 
52
43
  # waveforms
53
44
  [![View build status](https://github.com/feihoo87/waveforms/actions/workflows/workflow.yml/badge.svg)](https://github.com/feihoo87/waveforms/)