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.
- {waveforms-1.9.4/waveforms.egg-info → waveforms-2.0.2}/PKG-INFO +4 -13
- {waveforms-1.9.4 → waveforms-2.0.2}/pyproject.toml +1 -11
- {waveforms-1.9.4 → waveforms-2.0.2}/setup.py +2 -2
- waveforms-2.0.2/waveforms/__main__.py +16 -0
- waveforms-2.0.2/waveforms/distortion.py +376 -0
- waveforms-2.0.2/waveforms/utils.py +84 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/version.py +1 -1
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/waveform.py +41 -11
- {waveforms-1.9.4 → waveforms-2.0.2/waveforms.egg-info}/PKG-INFO +4 -13
- waveforms-2.0.2/waveforms.egg-info/SOURCES.txt +25 -0
- waveforms-2.0.2/waveforms.egg-info/requires.txt +17 -0
- waveforms-1.9.4/src/ikcp.c +0 -1466
- waveforms-1.9.4/src/ikcp.h +0 -415
- waveforms-1.9.4/src/kcp.c +0 -184
- waveforms-1.9.4/tests/test_dicttree.py +0 -128
- waveforms-1.9.4/tests/test_kad.py +0 -341
- waveforms-1.9.4/tests/test_namespace.py +0 -137
- waveforms-1.9.4/tests/test_registry.py +0 -95
- waveforms-1.9.4/tests/test_scan_iter.py +0 -375
- waveforms-1.9.4/tests/test_serialize.py +0 -34
- waveforms-1.9.4/waveforms/__main__.py +0 -24
- waveforms-1.9.4/waveforms/autoreload.py +0 -26
- waveforms-1.9.4/waveforms/baseconfig.py +0 -268
- waveforms-1.9.4/waveforms/cache.py +0 -163
- waveforms-1.9.4/waveforms/dicttree.py +0 -511
- waveforms-1.9.4/waveforms/loader.py +0 -97
- waveforms-1.9.4/waveforms/namespace.py +0 -261
- waveforms-1.9.4/waveforms/registry.py +0 -672
- waveforms-1.9.4/waveforms/scan/__init__.py +0 -4
- waveforms-1.9.4/waveforms/scan/base.py +0 -544
- waveforms-1.9.4/waveforms/scan/dataset.py +0 -0
- waveforms-1.9.4/waveforms/scan/expression.py +0 -360
- waveforms-1.9.4/waveforms/scan/scanner.py +0 -223
- waveforms-1.9.4/waveforms/scan/transforms.py +0 -16
- waveforms-1.9.4/waveforms/scan_iter.py +0 -10
- waveforms-1.9.4/waveforms/security/__init__.py +0 -1
- waveforms-1.9.4/waveforms/security/verify.py +0 -43
- waveforms-1.9.4/waveforms/server/__init__.py +0 -21
- waveforms-1.9.4/waveforms/server/__main__.py +0 -41
- waveforms-1.9.4/waveforms/server/echo.py +0 -44
- waveforms-1.9.4/waveforms/storage/__init__.py +0 -0
- waveforms-1.9.4/waveforms/storage/backend/__init__.py +0 -0
- waveforms-1.9.4/waveforms/storage/backend/redis.py +0 -204
- waveforms-1.9.4/waveforms/storage/base_dataset.py +0 -352
- waveforms-1.9.4/waveforms/storage/chunk.py +0 -60
- waveforms-1.9.4/waveforms/storage/dataset.py +0 -127
- waveforms-1.9.4/waveforms/storage/file.py +0 -273
- waveforms-1.9.4/waveforms/storage/models/__init__.py +0 -22
- waveforms-1.9.4/waveforms/storage/models/base.py +0 -4
- waveforms-1.9.4/waveforms/storage/models/config.py +0 -28
- waveforms-1.9.4/waveforms/storage/models/file.py +0 -89
- waveforms-1.9.4/waveforms/storage/models/ipy.py +0 -58
- waveforms-1.9.4/waveforms/storage/models/record.py +0 -161
- waveforms-1.9.4/waveforms/storage/models/report.py +0 -22
- waveforms-1.9.4/waveforms/storage/models/tag.py +0 -93
- waveforms-1.9.4/waveforms/storage/storage.py +0 -44
- waveforms-1.9.4/waveforms/sys/__init__.py +0 -0
- waveforms-1.9.4/waveforms/sys/chat.py +0 -688
- waveforms-1.9.4/waveforms/sys/device/__init__.py +0 -3
- waveforms-1.9.4/waveforms/sys/device/basedevice.py +0 -221
- waveforms-1.9.4/waveforms/sys/device/loader.py +0 -86
- waveforms-1.9.4/waveforms/sys/device/utils.py +0 -46
- waveforms-1.9.4/waveforms/sys/drivers/FakeInstrument.py +0 -52
- waveforms-1.9.4/waveforms/sys/drivers/__init__.py +0 -0
- waveforms-1.9.4/waveforms/sys/ipy_events.py +0 -125
- waveforms-1.9.4/waveforms/sys/net/__init__.py +0 -0
- waveforms-1.9.4/waveforms/sys/net/bencoder.py +0 -205
- waveforms-1.9.4/waveforms/sys/net/cli.py +0 -169
- waveforms-1.9.4/waveforms/sys/net/dhcp.py +0 -543
- waveforms-1.9.4/waveforms/sys/net/dhcpd.py +0 -176
- waveforms-1.9.4/waveforms/sys/net/kad.py +0 -1142
- waveforms-1.9.4/waveforms/sys/net/kcp.py +0 -192
- waveforms-1.9.4/waveforms/sys/progress.py +0 -190
- waveforms-1.9.4/waveforms/sys/storage/__init__.py +0 -0
- waveforms-1.9.4/waveforms/sys/storage/crud.py +0 -205
- waveforms-1.9.4/waveforms/sys/storage/models.py +0 -642
- waveforms-1.9.4/waveforms/units/__init__.py +0 -119
- waveforms-1.9.4/waveforms/utils.py +0 -67
- waveforms-1.9.4/waveforms/visualization/__init__.py +0 -188
- waveforms-1.9.4/waveforms/visualization/__main__.py +0 -71
- waveforms-1.9.4/waveforms/visualization/_autoplot.py +0 -457
- waveforms-1.9.4/waveforms/visualization/plot_layout.py +0 -408
- waveforms-1.9.4/waveforms/visualization/plot_seq.py +0 -90
- waveforms-1.9.4/waveforms/visualization/qdat.py +0 -152
- waveforms-1.9.4/waveforms/visualization/widgets.py +0 -86
- waveforms-1.9.4/waveforms.egg-info/SOURCES.txt +0 -96
- waveforms-1.9.4/waveforms.egg-info/requires.txt +0 -28
- {waveforms-1.9.4 → waveforms-2.0.2}/LICENSE +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/MANIFEST.in +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/README.md +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/setup.cfg +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/src/waveform.h +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/tests/test_multi_drag.py +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/tests/test_waveform.py +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/tests/test_wavevstack.py +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/__init__.py +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/_waveform.pyi +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/_waveform.pyx +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/multy_drag.py +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms/waveform_parser.py +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms.egg-info/dependency_links.txt +0 -0
- {waveforms-1.9.4 → waveforms-2.0.2}/waveforms.egg-info/entry_points.txt +0 -0
- {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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: waveforms
|
|
3
|
-
Version:
|
|
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
|
[](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
|
-
|
|
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,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__ = "
|
|
2
|
+
__version__ = "2.0.2"
|
|
@@ -127,36 +127,38 @@ class Waveform:
|
|
|
127
127
|
self.filters = None
|
|
128
128
|
self.label = None
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
136
|
+
return bounds[i - 1]
|
|
136
137
|
return inf
|
|
137
138
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: waveforms
|
|
3
|
-
Version:
|
|
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
|
[](https://github.com/feihoo87/waveforms/)
|