enode-host 0.1.0__py3-none-any.whl
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.
- enode_host/__init__.py +1 -0
- enode_host/async_socket.py +165 -0
- enode_host/backup/c-wing3.py +226 -0
- enode_host/backup/c.py +226 -0
- enode_host/backup/esp_mesh.py +136 -0
- enode_host/backup/gui_comps.py +113 -0
- enode_host/backup/gui_wx_bk.py +270 -0
- enode_host/backup/load_file.py +27 -0
- enode_host/backup/mesh.py +0 -0
- enode_host/backup/mesh_bk.py +0 -0
- enode_host/backup/s.py +151 -0
- enode_host/backup/sandbox.py +93 -0
- enode_host/backup/shm.py +262 -0
- enode_host/backup/shmtools.py +70 -0
- enode_host/backup/smarts.py +243 -0
- enode_host/backup/test_wxpython_choice.py +49 -0
- enode_host/backup/view-wing3.py +494 -0
- enode_host/backup/wx_example.py +55 -0
- enode_host/backup/wx_test01.py +43 -0
- enode_host/cli.py +192 -0
- enode_host/config.py +8 -0
- enode_host/constants.py +25 -0
- enode_host/framed_mesh.py +237 -0
- enode_host/gui_framed.py +207 -0
- enode_host/model.py +1415 -0
- enode_host/protocol.py +311 -0
- enode_host/psd_recursive.py +139 -0
- enode_host/queues.py +11 -0
- enode_host/resampling.py +206 -0
- enode_host/shm_sigproc.py +47 -0
- enode_host/storage.py +93 -0
- enode_host/timestamping.py +79 -0
- enode_host/types.py +38 -0
- enode_host/view.py +1233 -0
- enode_host-0.1.0.dist-info/METADATA +81 -0
- enode_host-0.1.0.dist-info/RECORD +39 -0
- enode_host-0.1.0.dist-info/WHEEL +5 -0
- enode_host-0.1.0.dist-info/entry_points.txt +2 -0
- enode_host-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# %%
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import matplotlib.dates as mdates
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
df = pd.read_hdf("data/daq-2025_0305_1719", key="df")
|
|
9
|
+
|
|
10
|
+
def select_xrange(self):
|
|
11
|
+
"""
|
|
12
|
+
Plots the current DataFrame (with datetime index),
|
|
13
|
+
shows a vertical line following mouse movement (when zoom tool is NOT active),
|
|
14
|
+
and captures two mouse clicks (when zoom tool is NOT active).
|
|
15
|
+
Returns the subset of the DataFrame between those two clicked x-coordinates.
|
|
16
|
+
"""
|
|
17
|
+
# Create the plot
|
|
18
|
+
fig, ax = plt.subplots()
|
|
19
|
+
self.plot(ax=ax)
|
|
20
|
+
|
|
21
|
+
# Create an initial vertical line at the left edge of our data
|
|
22
|
+
# We'll move it around inside the 'motion_notify_event'
|
|
23
|
+
if len(self.index) == 0:
|
|
24
|
+
print("DataFrame is empty. Returning as-is.")
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
# We'll place the vertical line initially at the first index
|
|
28
|
+
vline = ax.axvline(self.index[0], linestyle='--', color='gray')
|
|
29
|
+
|
|
30
|
+
# We'll store the two click times here
|
|
31
|
+
clicked_times = []
|
|
32
|
+
|
|
33
|
+
def on_mouse_move(event):
|
|
34
|
+
# Only move the line if we're NOT in "zoom rect" mode
|
|
35
|
+
if fig.canvas.manager.toolbar.mode != 'zoom rect':
|
|
36
|
+
if event.inaxes == ax and event.xdata is not None:
|
|
37
|
+
# Move the vertical line to the current mouse x
|
|
38
|
+
vline.set_xdata(event.xdata)
|
|
39
|
+
fig.canvas.draw_idle() # Redraw efficiently
|
|
40
|
+
|
|
41
|
+
def on_click(event):
|
|
42
|
+
# Only record the click if we're NOT in "zoom rect" mode
|
|
43
|
+
if fig.canvas.manager.toolbar.mode != 'zoom rect':
|
|
44
|
+
if event.inaxes == ax and event.xdata is not None:
|
|
45
|
+
# Convert Matplotlib's float date to a Python datetime
|
|
46
|
+
dt = mdates.num2date(event.xdata)
|
|
47
|
+
clicked_times.append(dt)
|
|
48
|
+
|
|
49
|
+
if len(clicked_times) == 2:
|
|
50
|
+
# We got two clicks—stop the event loop
|
|
51
|
+
fig.canvas.stop_event_loop()
|
|
52
|
+
|
|
53
|
+
# Connect the callbacks
|
|
54
|
+
cid_move = fig.canvas.mpl_connect("motion_notify_event", on_mouse_move)
|
|
55
|
+
cid_click = fig.canvas.mpl_connect("button_press_event", on_click)
|
|
56
|
+
|
|
57
|
+
# Show the plot (non‐blocking)
|
|
58
|
+
plt.show(block=False)
|
|
59
|
+
|
|
60
|
+
# Start an event loop that blocks until we have two clicks (or the figure is closed).
|
|
61
|
+
fig.canvas.start_event_loop(timeout=-1)
|
|
62
|
+
|
|
63
|
+
# Disconnect events and close the figure
|
|
64
|
+
fig.canvas.mpl_disconnect(cid_move)
|
|
65
|
+
fig.canvas.mpl_disconnect(cid_click)
|
|
66
|
+
plt.close(fig)
|
|
67
|
+
|
|
68
|
+
# If we didn't get two valid clicks, just return the original DataFrame
|
|
69
|
+
if len(clicked_times) < 2:
|
|
70
|
+
print("Not enough clicks were registered. Returning the original DataFrame.")
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
# Sort the times so t0 < t1
|
|
74
|
+
t0, t1 = sorted(clicked_times)
|
|
75
|
+
|
|
76
|
+
# Slice the DataFrame by those two times (inclusive)
|
|
77
|
+
return self.loc[t0 : t1]
|
|
78
|
+
|
|
79
|
+
# Monkey‐patch the DataFrame class so any DataFrame can call df.select_xrange()
|
|
80
|
+
pd.DataFrame.select_xrange = select_xrange
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ----------------------------------------------------------------------------
|
|
84
|
+
# Example usage:
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
# Create a small example DataFrame with a DatetimeIndex
|
|
87
|
+
# dates = pd.date_range("2023-01-01", periods=30, freq="D")
|
|
88
|
+
# df = pd.DataFrame({"values": range(30)}, index=dates)
|
|
89
|
+
df = pd.read_hdf("data/daq-2025_0305_1719", key="df")
|
|
90
|
+
# Call our new method
|
|
91
|
+
subset = df.select_xrange()
|
|
92
|
+
print("Selected subset:\n", subset)
|
|
93
|
+
|
enode_host/backup/shm.py
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
'''Tools for signal processing of SHM
|
|
2
|
+
'''
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
#
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
# TODO:
|
|
10
|
+
# - identify # of channles
|
|
11
|
+
# - csv or bin
|
|
12
|
+
|
|
13
|
+
# %matplotlib inline
|
|
14
|
+
# %matplotlib notebook
|
|
15
|
+
# %matplotlib widget
|
|
16
|
+
|
|
17
|
+
import struct
|
|
18
|
+
import datetime
|
|
19
|
+
from datetime import timezone
|
|
20
|
+
from numpy import *
|
|
21
|
+
import matplotlib.pyplot as pl
|
|
22
|
+
import matplotlib.dates as mdates
|
|
23
|
+
|
|
24
|
+
from scipy.signal import welch, detrend, cheby1, sosfiltfilt
|
|
25
|
+
from scipy.io import loadmat
|
|
26
|
+
import sys
|
|
27
|
+
import os
|
|
28
|
+
|
|
29
|
+
import pandas as pd
|
|
30
|
+
from bokeh.plotting import figure, show
|
|
31
|
+
from bokeh.embed import components
|
|
32
|
+
from bokeh.models import HoverTool
|
|
33
|
+
from bokeh.models import ColumnDataSource, OpenURL, TapTool
|
|
34
|
+
from bokeh.models import HoverTool
|
|
35
|
+
from bokeh.models import DatetimeTickFormatter
|
|
36
|
+
from bokeh.models import Range1d, LinearAxis
|
|
37
|
+
from bokeh.palettes import Dark2_5 as palette
|
|
38
|
+
import bokeh.layouts
|
|
39
|
+
import pandas_bokeh
|
|
40
|
+
|
|
41
|
+
#import ssi_dat
|
|
42
|
+
import time
|
|
43
|
+
|
|
44
|
+
class SHM:
|
|
45
|
+
|
|
46
|
+
df = []
|
|
47
|
+
t = []
|
|
48
|
+
y = []
|
|
49
|
+
mean = []
|
|
50
|
+
std = []
|
|
51
|
+
|
|
52
|
+
mp = []
|
|
53
|
+
nfft = 2**10
|
|
54
|
+
dt = []
|
|
55
|
+
fs = []
|
|
56
|
+
F = []
|
|
57
|
+
Pxx = []
|
|
58
|
+
|
|
59
|
+
dataPath = None
|
|
60
|
+
file_duration = {'acc': 600, 'wnd': 3600*24} # in seconds
|
|
61
|
+
|
|
62
|
+
db_managers = {'File':None, 'Summary': None}
|
|
63
|
+
|
|
64
|
+
def __init__(self):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
# LOAD ---------------------------------------------------------------------------------
|
|
68
|
+
def load(self, src, *args, **kwargs):
|
|
69
|
+
"""
|
|
70
|
+
load(src = [raw|django-file|django-db])
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def load_bin(fn, nch = 3):
|
|
74
|
+
with open(fn, 'rb') as fp:
|
|
75
|
+
data = fp.read()
|
|
76
|
+
bytes_per_sample = 4*2 + 4*nch
|
|
77
|
+
n = int(len(data)/bytes_per_sample)
|
|
78
|
+
t, y = [], []
|
|
79
|
+
for i in range(n):
|
|
80
|
+
res = list(struct.unpack('<LL' + 'f'*nch, data[i*bytes_per_sample:(i+1)*bytes_per_sample]))
|
|
81
|
+
t.append(res[0]*1000000 + res[1]) # in usec
|
|
82
|
+
y.append(res[2:])
|
|
83
|
+
t = pd.to_datetime(t, unit='us')
|
|
84
|
+
return pd.DataFrame(array(y), index = t)
|
|
85
|
+
|
|
86
|
+
def load_mat(fn, gain = 10):
|
|
87
|
+
mat_contents = loadmat(fn)
|
|
88
|
+
_keys = mat_contents.keys()
|
|
89
|
+
if 't' not in _keys or 'a' not in _keys:
|
|
90
|
+
raise Exception('loading mat failed: keys t and a are not found.')
|
|
91
|
+
t = mat_contents['t']
|
|
92
|
+
y = mat_contents['a']/10/gain # 10V per g, gain=10
|
|
93
|
+
return pd.DataFrame(y, index = t.flatten())
|
|
94
|
+
|
|
95
|
+
def load_django_file(nodeName, sensorType, ti, duration=None, pos='start'):
|
|
96
|
+
if pos == 'start':
|
|
97
|
+
pass
|
|
98
|
+
elif pos == 'centre':
|
|
99
|
+
ti = ti - datetime.timedelta(seconds=duration/2)
|
|
100
|
+
elif pos == 'end':
|
|
101
|
+
ti = ti - datetime.timedelta(seconds=duration)
|
|
102
|
+
else:
|
|
103
|
+
raise Exception('unknown pos variable: {}'.format(pos))
|
|
104
|
+
te = ti + datetime.timedelta(seconds=duration)
|
|
105
|
+
recs = self.db_managers['File'].filter(nodeName = nodeName)\
|
|
106
|
+
.filter(sensorType = sensorType)\
|
|
107
|
+
.filter(timestamp__gte = ti)\
|
|
108
|
+
.filter(timestamp__lt = te)\
|
|
109
|
+
.order_by('timestamp')
|
|
110
|
+
if len(recs) > 0:
|
|
111
|
+
dfs = []
|
|
112
|
+
for rec in recs:
|
|
113
|
+
dfs.append(rec.load())
|
|
114
|
+
return pd.concat(dfs)
|
|
115
|
+
else:
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def read_db(tableName, fields, ti, duration):
|
|
119
|
+
te = ti + datetime.timedelta(seconds = duration)
|
|
120
|
+
recs = self.db_managers[tableName].filter(timestamp__gte = ti)\
|
|
121
|
+
.filter(timestamp__lt = te)\
|
|
122
|
+
.order_by('timestamp')
|
|
123
|
+
t, y, columnnames = [], [], []
|
|
124
|
+
map = {
|
|
125
|
+
'WindDir': ['WindDir'],
|
|
126
|
+
'WindSpeed': ['WindSpeed'],
|
|
127
|
+
'rmsd': ['rmsd_x', 'rmsd_y', 'rmsd_z', 'rmsd_tot'],
|
|
128
|
+
'mean': ['mean_x', 'mean_y', 'mean_z'],
|
|
129
|
+
'freq': ['freq_m11', 'freq_m12', 'freq_m21', 'freq_m22', 'freq_m3'],
|
|
130
|
+
'xi': ['xi_m11', 'xi_m12', 'xi_m21', 'xi_m22', 'xi_m3'],
|
|
131
|
+
'uptime': ['uptime']
|
|
132
|
+
}
|
|
133
|
+
# populate column names
|
|
134
|
+
for field in fields:
|
|
135
|
+
if field in map.keys():
|
|
136
|
+
columnnames += map[field]
|
|
137
|
+
else:
|
|
138
|
+
columnnames += [field]
|
|
139
|
+
# populate data
|
|
140
|
+
for idx, rec in enumerate(recs):
|
|
141
|
+
t.append(rec.timestamp)
|
|
142
|
+
row = []
|
|
143
|
+
for field in fields:
|
|
144
|
+
val = getattr(rec, field)
|
|
145
|
+
# print(field, val)
|
|
146
|
+
|
|
147
|
+
if isinstance(val, list):
|
|
148
|
+
row += val
|
|
149
|
+
else:
|
|
150
|
+
row += [val]
|
|
151
|
+
y.append(row)
|
|
152
|
+
# print(row)
|
|
153
|
+
# print(len(row), len(columnnames))
|
|
154
|
+
return pd.DataFrame(y, index = t, columns = columnnames)
|
|
155
|
+
|
|
156
|
+
if src == 'raw':
|
|
157
|
+
if 'filename' not in kwargs.keys():
|
|
158
|
+
raise Exception("filename not specified")
|
|
159
|
+
else:
|
|
160
|
+
filename = kwargs['filename']
|
|
161
|
+
if 'mat' in filename:
|
|
162
|
+
self.df = load_mat(filename)
|
|
163
|
+
else:
|
|
164
|
+
self.df = load_bin(filename)
|
|
165
|
+
return self.df
|
|
166
|
+
|
|
167
|
+
elif src == 'django-file':
|
|
168
|
+
self.df = load_django_file(*args, **kwargs)
|
|
169
|
+
elif src == 'django-db':
|
|
170
|
+
self.df = read_db(*args)
|
|
171
|
+
if self.df is not None:
|
|
172
|
+
if len(self.df.index) > 1:
|
|
173
|
+
dt = self.df.index[1] - self.df.index[0]
|
|
174
|
+
if isinstance(dt, datetime.timedelta):
|
|
175
|
+
dt = dt.total_seconds()
|
|
176
|
+
self.fs = 1/dt
|
|
177
|
+
else:
|
|
178
|
+
pass
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
# SAVE ---------------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
def save(self, tableName, timestamp, fields, values):
|
|
184
|
+
recs = self.db_managers[tableName].filter(timestamp = timestamp)
|
|
185
|
+
if len(recs) > 0:
|
|
186
|
+
rec = recs[0]
|
|
187
|
+
for field, value in zip(fields, values):
|
|
188
|
+
setattr(rec, field, value)
|
|
189
|
+
rec.save()
|
|
190
|
+
else:
|
|
191
|
+
raise Exception("no record found")
|
|
192
|
+
# SIGNAL PROC ---------------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
def get_statistics(self):
|
|
195
|
+
self.mean = self.df.mean().tolist()
|
|
196
|
+
self.std = self.df.std().tolist()
|
|
197
|
+
|
|
198
|
+
# def ssi(self, max_order = 100, i = 50, err = [0.01, 0.1, 0.99], max_poll = None, freq_ranges = None, **kwargs):
|
|
199
|
+
# ssi_dat.y = self.df.to_numpy().T
|
|
200
|
+
# ssi_dat.fs = self.fs
|
|
201
|
+
# ssi_dat.max_order = max_order
|
|
202
|
+
# ssi_dat.max_poll = max_poll
|
|
203
|
+
# ssi_dat.err = err
|
|
204
|
+
# if i is not None:
|
|
205
|
+
# ssi_dat.i = i
|
|
206
|
+
# ssi_dat.freq_ranges = freq_ranges
|
|
207
|
+
# ssi_dat.show_plots = False
|
|
208
|
+
#
|
|
209
|
+
# print('ssi_dat: i = {}, stab_criteria={}'.format(ssi_dat.i, err))
|
|
210
|
+
# t0 = time.time()
|
|
211
|
+
# ssi_dat.ssi_dat(**kwargs)
|
|
212
|
+
# self.ssi_dat = ssi_dat
|
|
213
|
+
#
|
|
214
|
+
|
|
215
|
+
# PLOT ---------------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
# def plot(self, **kwargs):
|
|
218
|
+
# # self.df.plot(**kwargs)
|
|
219
|
+
# pl.plot(self.t, self.y)
|
|
220
|
+
# pl.grid(True)
|
|
221
|
+
|
|
222
|
+
def plot_bokeh(self, **kwargs):
|
|
223
|
+
pd.set_option('plotting.backend', 'pandas_bokeh')
|
|
224
|
+
p = self.df.plot(**kwargs, show_figure = False)
|
|
225
|
+
p.toolbar.logo = None
|
|
226
|
+
script, div = components(p)
|
|
227
|
+
return script, div
|
|
228
|
+
|
|
229
|
+
def psd(self, plot=True, **kwargs):
|
|
230
|
+
# if 'figsize' in kwargs:
|
|
231
|
+
# figure(figsize=kwargs.get('figsize'))
|
|
232
|
+
# print(kwargs.get('figsize'))
|
|
233
|
+
# del kwargs['figsize']
|
|
234
|
+
f, Pxx_den = welch(self.df.to_numpy(), self.fs, nperseg=self.nfft, noverlap = fix(self.nfft*3/4), axis=0, **kwargs)
|
|
235
|
+
self.F = f
|
|
236
|
+
self.Pxx = Pxx_den
|
|
237
|
+
if plot:
|
|
238
|
+
pl.semilogy(f,sqrt(Pxx_den))
|
|
239
|
+
pl.xlim(0,self.fs/2)
|
|
240
|
+
pl.xlabel('Frequency (Hz)')
|
|
241
|
+
pl.ylabel('PSD g/sq(Hz)')
|
|
242
|
+
pl.grid(True)
|
|
243
|
+
|
|
244
|
+
def lpf(self, r, **kwargs):
|
|
245
|
+
sos = cheby1(8, 0.05, r, output='sos')
|
|
246
|
+
sos = asarray(sos, dtype = self.y.dtype)
|
|
247
|
+
self.y = sosfiltfilt(sos, self.y, **kwargs)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
if __name__ == "__main__":
|
|
252
|
+
s = SHM()
|
|
253
|
+
s.load(src='raw', filename='data/1hr/smartply_acc1_acc_202305022200')
|
|
254
|
+
# s.y = s.y[:2]
|
|
255
|
+
s.plot()
|
|
256
|
+
pl.figure()
|
|
257
|
+
s.ssi(max_order = 100, i = 50, max_poll = 20)
|
|
258
|
+
pl.show()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from matplotlib.pyplot import *
|
|
3
|
+
from numpy import *
|
|
4
|
+
import matplotlib.dates as mdates
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def detrend(self):
|
|
9
|
+
return self - self.mean()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def subset(self):
|
|
13
|
+
if hasattr(self, 't0') and hasattr(self, 't1'):
|
|
14
|
+
mask = (self.index >= self.t0.replace(tzinfo=None)) & (self.index < self.t1.replace(tzinfo=None))
|
|
15
|
+
return self.loc[mask, :]
|
|
16
|
+
else:
|
|
17
|
+
raise Exception('t0 or t1 doesn''t exist')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# _orig_plot = pd.DataFrame.plot
|
|
21
|
+
|
|
22
|
+
# def custom_plot(self, *args, **kwargs):
|
|
23
|
+
# if "figsize" not in kwargs:
|
|
24
|
+
# kwargs["figsize"] = (8, 4)
|
|
25
|
+
# elif "grid" not in kwargs:
|
|
26
|
+
# kwargs['grid'] = True
|
|
27
|
+
|
|
28
|
+
# # Call the original plot method
|
|
29
|
+
# return _orig_plot(self)(*args, **kwargs)
|
|
30
|
+
|
|
31
|
+
# pd.DataFrame.plot = custom_plot
|
|
32
|
+
|
|
33
|
+
def select(self):
|
|
34
|
+
fig, ax = subplots()
|
|
35
|
+
self.plot(ax=ax)
|
|
36
|
+
toolbar = fig.canvas.manager.toolbar
|
|
37
|
+
|
|
38
|
+
vline = ax.axvline(nan, linestyle="--")
|
|
39
|
+
|
|
40
|
+
def on_mouse_move(event):
|
|
41
|
+
if len(toolbar.mode) > 0:
|
|
42
|
+
vline.set_xdata(nan)
|
|
43
|
+
elif event.inaxes == ax and event.xdata is not None:
|
|
44
|
+
vline.set_xdata(event.xdata)
|
|
45
|
+
fig.canvas.draw_idle() # efficient redraw
|
|
46
|
+
|
|
47
|
+
def onclick(event):
|
|
48
|
+
nonlocal self
|
|
49
|
+
# event.xdata and event.ydata give the mouse click in data coordinates
|
|
50
|
+
# print("toolbar.mode", toolbar.mode, ' ', len(toolbar.mode))
|
|
51
|
+
if len(toolbar.mode) == 0:
|
|
52
|
+
if event.button == 1:
|
|
53
|
+
t0 = mdates.num2date(event.xdata)
|
|
54
|
+
self.t0 = t0
|
|
55
|
+
print("x =", event.xdata, ", y =", event.ydata)
|
|
56
|
+
if event.button == 3:
|
|
57
|
+
t1 = mdates.num2date(event.xdata)
|
|
58
|
+
self.t1 = t1
|
|
59
|
+
print("x =", event.xdata, ", y =", event.ydata)
|
|
60
|
+
close()
|
|
61
|
+
|
|
62
|
+
# Connect the event to the figure
|
|
63
|
+
fig.canvas.mpl_connect("motion_notify_event", on_mouse_move)
|
|
64
|
+
fig.canvas.mpl_connect('button_press_event', onclick)
|
|
65
|
+
show()
|
|
66
|
+
|
|
67
|
+
pd.DataFrame.detrend = detrend
|
|
68
|
+
pd.DataFrame.select = select
|
|
69
|
+
pd.DataFrame.subset = subset
|
|
70
|
+
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/local/bin/python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
###########################################################################
|
|
5
|
+
## Python code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
|
|
6
|
+
## http://www.wxformbuilder.org/
|
|
7
|
+
##
|
|
8
|
+
## PLEASE DO *NOT* EDIT THIS FILE!
|
|
9
|
+
###########################################################################
|
|
10
|
+
|
|
11
|
+
import wx
|
|
12
|
+
import wx.xrc
|
|
13
|
+
import wx.grid
|
|
14
|
+
|
|
15
|
+
import gettext
|
|
16
|
+
_ = gettext.gettext
|
|
17
|
+
|
|
18
|
+
###########################################################################
|
|
19
|
+
## Class MyFrame1
|
|
20
|
+
###########################################################################
|
|
21
|
+
|
|
22
|
+
class MyFrame1 ( wx.Frame ):
|
|
23
|
+
|
|
24
|
+
def __init__( self, parent ):
|
|
25
|
+
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = _(u"Wireless Sensor DAQ"), pos = wx.DefaultPosition, size = wx.Size( 1024,600 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
|
|
26
|
+
|
|
27
|
+
self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
|
|
28
|
+
|
|
29
|
+
bSizer1 = wx.BoxSizer( wx.VERTICAL )
|
|
30
|
+
|
|
31
|
+
bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
|
|
32
|
+
|
|
33
|
+
self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, _(u"Sensor Nodes:"), wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
34
|
+
self.m_staticText2.Wrap( -1 )
|
|
35
|
+
|
|
36
|
+
bSizer2.Add( self.m_staticText2, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
|
|
37
|
+
|
|
38
|
+
self.m_textCtrl2 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
39
|
+
bSizer2.Add( self.m_textCtrl2, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
|
|
40
|
+
|
|
41
|
+
self.m_staticText3 = wx.StaticText( self, wx.ID_ANY, _(u"Fs="), wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
42
|
+
self.m_staticText3.Wrap( -1 )
|
|
43
|
+
|
|
44
|
+
bSizer2.Add( self.m_staticText3, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
|
|
45
|
+
|
|
46
|
+
m_choice1Choices = [ _(u"62.5"), _(u"125") ]
|
|
47
|
+
self.m_choice1 = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choice1Choices, 0 )
|
|
48
|
+
self.m_choice1.SetSelection( 0 )
|
|
49
|
+
bSizer2.Add( self.m_choice1, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
|
|
50
|
+
|
|
51
|
+
self.m_staticText4 = wx.StaticText( self, wx.ID_ANY, _(u"Hz"), wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
52
|
+
self.m_staticText4.Wrap( -1 )
|
|
53
|
+
|
|
54
|
+
bSizer2.Add( self.m_staticText4, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
|
|
55
|
+
|
|
56
|
+
self.m_button1 = wx.Button( self, wx.ID_ANY, _(u"Start DAQ"), wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
57
|
+
bSizer2.Add( self.m_button1, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
|
|
58
|
+
|
|
59
|
+
self.m_button2 = wx.Button( self, wx.ID_ANY, _(u"Stop DAQ"), wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
60
|
+
bSizer2.Add( self.m_button2, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
bSizer1.Add( bSizer2, 0, wx.EXPAND, 5 )
|
|
64
|
+
|
|
65
|
+
bSizer3 = wx.BoxSizer( wx.HORIZONTAL )
|
|
66
|
+
|
|
67
|
+
bSizer4 = wx.BoxSizer( wx.VERTICAL )
|
|
68
|
+
|
|
69
|
+
self.m_staticText21 = wx.StaticText( self, wx.ID_ANY, _(u"WiFi Mesh Status"), wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
70
|
+
self.m_staticText21.Wrap( -1 )
|
|
71
|
+
|
|
72
|
+
bSizer4.Add( self.m_staticText21, 0, wx.ALL, 5 )
|
|
73
|
+
|
|
74
|
+
self.m_grid2 = wx.grid.Grid( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
75
|
+
|
|
76
|
+
# Grid
|
|
77
|
+
self.m_grid2.CreateGrid( 5, 6 )
|
|
78
|
+
self.m_grid2.EnableEditing( True )
|
|
79
|
+
self.m_grid2.EnableGridLines( True )
|
|
80
|
+
self.m_grid2.EnableDragGridSize( True )
|
|
81
|
+
self.m_grid2.SetMargins( 0, 0 )
|
|
82
|
+
|
|
83
|
+
# Columns
|
|
84
|
+
self.m_grid2.SetColSize( 0, 70 )
|
|
85
|
+
self.m_grid2.SetColSize( 1, 100 )
|
|
86
|
+
self.m_grid2.SetColSize( 2, 20 )
|
|
87
|
+
self.m_grid2.SetColSize( 3, 40 )
|
|
88
|
+
self.m_grid2.SetColSize( 4, 40 )
|
|
89
|
+
self.m_grid2.SetColSize( 5, 45 )
|
|
90
|
+
self.m_grid2.EnableDragColMove( False )
|
|
91
|
+
self.m_grid2.EnableDragColSize( True )
|
|
92
|
+
self.m_grid2.SetColLabelValue( 0, _(u"Node ID") )
|
|
93
|
+
self.m_grid2.SetColLabelValue( 1, _(u"Status") )
|
|
94
|
+
self.m_grid2.SetColLabelValue( 2, _(u"L") )
|
|
95
|
+
self.m_grid2.SetColLabelValue( 3, _(u"RSSI") )
|
|
96
|
+
self.m_grid2.SetColLabelValue( 4, _(u"PPS") )
|
|
97
|
+
self.m_grid2.SetColLabelValue( 5, _(u"Data") )
|
|
98
|
+
self.m_grid2.SetColLabelSize( wx.grid.GRID_AUTOSIZE )
|
|
99
|
+
self.m_grid2.SetColLabelAlignment( wx.ALIGN_CENTER, wx.ALIGN_CENTER )
|
|
100
|
+
|
|
101
|
+
# Rows
|
|
102
|
+
self.m_grid2.EnableDragRowSize( True )
|
|
103
|
+
self.m_grid2.SetRowLabelSize( wx.grid.GRID_AUTOSIZE )
|
|
104
|
+
self.m_grid2.SetRowLabelAlignment( wx.ALIGN_CENTER, wx.ALIGN_CENTER )
|
|
105
|
+
|
|
106
|
+
# Label Appearance
|
|
107
|
+
|
|
108
|
+
# Cell Defaults
|
|
109
|
+
self.m_grid2.SetDefaultCellAlignment( wx.ALIGN_LEFT, wx.ALIGN_TOP )
|
|
110
|
+
bSizer4.Add( self.m_grid2, 1, wx.ALL|wx.EXPAND, 5 )
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
bSizer3.Add( bSizer4, 0, wx.EXPAND, 5 )
|
|
114
|
+
|
|
115
|
+
self.m_notebook2 = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
116
|
+
self.m_panel1 = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
|
|
117
|
+
self.m_notebook2.AddPage( self.m_panel1, _(u"Time history plot"), True )
|
|
118
|
+
self.m_panel2 = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
|
|
119
|
+
self.m_notebook2.AddPage( self.m_panel2, _(u"PSD plot"), False )
|
|
120
|
+
|
|
121
|
+
bSizer3.Add( self.m_notebook2, 1, wx.ALL|wx.EXPAND, 5 )
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
bSizer1.Add( bSizer3, 1, wx.EXPAND, 5 )
|
|
125
|
+
|
|
126
|
+
self.m_textCtrl21 = wx.TextCtrl( self, wx.ID_ANY, _(u"\"test line 1\ntest2 line|\""), wx.DefaultPosition, wx.Size( -1,100 ), 0 )
|
|
127
|
+
bSizer1.Add( self.m_textCtrl21, 0, wx.ALL|wx.EXPAND, 5 )
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
self.SetSizer( bSizer1 )
|
|
131
|
+
self.Layout()
|
|
132
|
+
|
|
133
|
+
self.Centre( wx.BOTH )
|
|
134
|
+
|
|
135
|
+
# Connect Events
|
|
136
|
+
self.m_textCtrl2.Bind( wx.EVT_TEXT, self.test )
|
|
137
|
+
|
|
138
|
+
def __del__( self ):
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Virtual event handlers, override them in your derived class
|
|
143
|
+
def test( self, event ):
|
|
144
|
+
event.Skip()
|
|
145
|
+
|
|
146
|
+
import wx
|
|
147
|
+
import wx.lib.agw.aui as aui
|
|
148
|
+
import wx.lib.mixins.inspection as wit
|
|
149
|
+
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
|
|
150
|
+
from matplotlib.backends.backend_wxagg import \
|
|
151
|
+
NavigationToolbar2WxAgg as NavigationToolbar
|
|
152
|
+
from matplotlib.figure import Figure
|
|
153
|
+
|
|
154
|
+
class Plot(wx.Panel):
|
|
155
|
+
def __init__(self, parent, id=-1, dpi=None, **kwargs):
|
|
156
|
+
super().__init__(parent, id=id, **kwargs)
|
|
157
|
+
self.figure = Figure(dpi=dpi, figsize=(2, 2))
|
|
158
|
+
self.canvas = FigureCanvas(self, -1, self.figure)
|
|
159
|
+
self.toolbar = NavigationToolbar(self.canvas)
|
|
160
|
+
self.toolbar.Realize()
|
|
161
|
+
|
|
162
|
+
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
163
|
+
sizer.Add(self.canvas, 1, wx.EXPAND)
|
|
164
|
+
sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
|
|
165
|
+
self.SetSizer(sizer)
|
|
166
|
+
|
|
167
|
+
class PlotNotebook(wx.Panel):
|
|
168
|
+
def __init__(self, parent, id=-1):
|
|
169
|
+
super().__init__(parent, id=id)
|
|
170
|
+
self.nb = aui.AuiNotebook(self)
|
|
171
|
+
sizer = wx.BoxSizer()
|
|
172
|
+
sizer.Add(self.nb, 1, wx.EXPAND)
|
|
173
|
+
self.SetSizer(sizer)
|
|
174
|
+
|
|
175
|
+
def add(self, name="plot"):
|
|
176
|
+
page = Plot(self.nb)
|
|
177
|
+
self.nb.AddPage(page, name)
|
|
178
|
+
return page.figure
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
import wx.grid as gridlib
|
|
182
|
+
import configparser
|
|
183
|
+
import os
|
|
184
|
+
|
|
185
|
+
CONFIG_FILE = 'config.ini'
|
|
186
|
+
|
|
187
|
+
def save_config(options):
|
|
188
|
+
config = configparser.ConfigParser()
|
|
189
|
+
|
|
190
|
+
config['Settings'] = {
|
|
191
|
+
'option1': options['option1'],
|
|
192
|
+
'option2': options['option2']
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
with open(CONFIG_FILE, 'w') as configfile:
|
|
196
|
+
config.write(configfile)
|
|
197
|
+
|
|
198
|
+
def load_config():
|
|
199
|
+
config = configparser.ConfigParser()
|
|
200
|
+
|
|
201
|
+
if not os.path.exists(CONFIG_FILE):
|
|
202
|
+
# Return default options if config file doesn't exist
|
|
203
|
+
return {
|
|
204
|
+
'option1': 'default_value1',
|
|
205
|
+
'option2': 'default_value2'
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
config.read(CONFIG_FILE)
|
|
209
|
+
options = {
|
|
210
|
+
'option1': config.get('Settings', 'option1', fallback='default_value1'),
|
|
211
|
+
'option2': config.get('Settings', 'option2', fallback='default_value2')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return options
|
|
215
|
+
|
|
216
|
+
app = wx.App()
|
|
217
|
+
# window = wx.Frame(None, title = "wxPython Frame", size = (300,200))
|
|
218
|
+
frame1 = MyFrame1(None)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
fig1 = Plot(frame1.m_panel1)
|
|
222
|
+
# plotter = PlotNotebook(frame1.m_notebook2)
|
|
223
|
+
axes1 = fig1.figure.add_subplot()
|
|
224
|
+
axes1.plot([1, 2, 3], [2, 1, 4])
|
|
225
|
+
fig2 = Plot(frame1.m_panel2)
|
|
226
|
+
axes2 = fig2.figure.add_subplot()
|
|
227
|
+
axes2.plot([1, 2, 3, 4, 5], [2, 1, 4, 2, 3])
|
|
228
|
+
|
|
229
|
+
canvas = FigureCanvas(frame1.m_panel1, -1, fig1.figure)
|
|
230
|
+
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
231
|
+
sizer.Add(canvas, 1, wx.EXPAND)
|
|
232
|
+
|
|
233
|
+
# Set the sizer for the panel
|
|
234
|
+
frame1.m_panel1.SetSizer(sizer)
|
|
235
|
+
|
|
236
|
+
# Change the background color of cell (1, 1)
|
|
237
|
+
attr = gridlib.GridCellAttr()
|
|
238
|
+
attr.SetBackgroundColour(wx.Colour(255, 255, 0)) # Yellow
|
|
239
|
+
frame1.m_grid2.SetAttr(1, 1, attr)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
frame1.Show(True)
|
|
243
|
+
app.MainLoop()
|