fix
This commit is contained in:
parent
2d5a2bb53b
commit
04d9f40ab3
|
|
@ -0,0 +1,47 @@
|
|||
# Measurement tool
|
||||
Configure in main.py file. Any Python expression may be used.
|
||||
|
||||
## ARGS
|
||||
In any place, where quick changing of parameters is desired, command line arguments might be used instead of fixed configuration.
|
||||
When program is run with:
|
||||
|
||||
python main.py ABC DEF
|
||||
|
||||
then ARGS[1] = "ABC" and ARGS[2] = "DEF"
|
||||
|
||||
## CONN_PARAMS
|
||||
This parameter contains data required to connect to thrust stand and microphone.
|
||||
- nor_addr - microphone IP address
|
||||
- nor_ftp_user - micropphone FTP username
|
||||
- nor_ftp_pass - microphone FTP password
|
||||
- nor_recordings_dir - microphone recorded data path
|
||||
- stand_tty - serial port, trust stand is attached to, COM... for windows, /dev/tty... for linux
|
||||
|
||||
## PWM_RANGE
|
||||
Sets the sequence of throttle levels, that will be included in measurement.
|
||||
Use any Python that will evaluate to Iterable[int].
|
||||
|
||||
Examples:
|
||||
- PWM_RANGE = range(1100, 2000, 100) - 1000, 1100, ..., 1800, 1900 - range from 1100 to 2000 with step of 100. Note it does NOT include the end value.
|
||||
- PWM_RANGE = list(range(1100, 1500, 50)) + list(range(1500, 2000, 50)) - 1000, 1100, 1200, 1300, 1400, 1500, 1550, ..., 1900, 1950. - Ranges may be joined to achieve not uniform distribution.
|
||||
- PWM_RANGE = [1300, 1800, 1900, 1950, 1975, 1990, 2000] - Fully custom measurement may be defined by specifying list of throttle levels manually.
|
||||
|
||||
|
||||
## OUTPUT_FILE
|
||||
This parameter specifies name of the directory, that will be created and written with measurement series data.
|
||||
Convenient to use with ARGS.
|
||||
|
||||
Examples:
|
||||
- OUTPUT_FILE = 'tests/baseline_r1_a0' - measurement will be saved to specified directory
|
||||
- OUTPUT_FILE = ARGS[1] - first argument will be used as output name. Run program with 'python main.py tests/baseline_r1_a0' to achieve the same effect as above
|
||||
|
||||
|
||||
# Data visualizer
|
||||
|
||||
Run with:
|
||||
python visualizer.py [list of measurement series to visualize]
|
||||
|
||||
Examples:
|
||||
- python visualizer.py tests/baseline_r1_a0 tests/baseline_r1_a90 - plots these two series
|
||||
- python visualizer.py tests/* - plots all series in 'tests' directory
|
||||
- python visualizer.py tests/baseline_r*_a0 - plots all series matching the expression - baseline_r1_a0, baseline_r2_a0, etc.
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
from measure import measurement
|
||||
from measurement_station import ConnectionParams
|
||||
import sys
|
||||
|
||||
ARGS = sys.argv
|
||||
|
||||
# Configuration start
|
||||
# See README
|
||||
|
||||
CONN_PARAMS = ConnectionParams(
|
||||
stand_tty='/dev/ttyUSB0',
|
||||
# stand_tty='COM7',
|
||||
nor_addr='10.145.1.1',
|
||||
nor_ftp_user='AAAA',
|
||||
nor_ftp_pass='1234',
|
||||
nor_recordings_dir='/SD Card/NorMeas/Nor14530408/TEST'
|
||||
)
|
||||
|
||||
|
||||
# PWM_RANGE = list(range(1200, 1500, 100)) + list(range(1500,1800,50))
|
||||
PWM_RANGE = range(1100, 1800, 100)
|
||||
# PWM_RANGE = [1100, 1200, 1300, 1350, 1375, 1400, 1425, 1450, 1475, 1500]
|
||||
|
||||
# OUTPUT_FILE = folder1/subfolderfolder
|
||||
OUTPUT_FILE = ARGS[1]
|
||||
|
||||
# Configuration end
|
||||
|
||||
measurement(CONN_PARAMS, PWM_RANGE, OUTPUT_FILE)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import asyncio
|
||||
from measurement_station import ConnectionParams, meas_series
|
||||
import pickle
|
||||
|
||||
|
||||
def measurement(params: ConnectionParams, pwm_range, filename: str):
|
||||
x = asyncio.run(meas_series(params, pwm_range))
|
||||
|
||||
with open(filename, 'wb') as f:
|
||||
pickle.dump(x, f)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import asyncio
|
||||
|
||||
async def c1(r: asyncio.StreamReader):
|
||||
data = await r.readexactly(1)
|
||||
print(data)
|
||||
print('1')
|
||||
|
||||
async def c2(r: asyncio.StreamReader):
|
||||
while True:
|
||||
data = await r.readexactly(1)
|
||||
print('2')
|
||||
print(data)
|
||||
|
||||
|
||||
async def main():
|
||||
r, w = await asyncio.open_connection('localhost', 9999)
|
||||
t1 = asyncio.create_task(c1(r))
|
||||
t2 = asyncio.create_task(c2(r))
|
||||
while True:
|
||||
await asyncio.sleep(100000)
|
||||
|
||||
asyncio.run(main())
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
from dataclasses import fields
|
||||
from bokeh.io import curdoc
|
||||
from bokeh.layouts import column, row
|
||||
from bokeh.models import ColumnDataSource, MultiChoice, Select
|
||||
from bokeh.plotting import figure
|
||||
import pickle
|
||||
from measurement_station import OpPointData
|
||||
import math
|
||||
from bokeh.server.server import Server
|
||||
from bokeh.application import Application
|
||||
from bokeh.application.handlers.function import FunctionHandler
|
||||
import sys
|
||||
|
||||
|
||||
def oppoint_data_src(p: OpPointData) -> dict[str, float]:
|
||||
ret = {}
|
||||
ret.update({k: v for k, v in field_entries(p.data_thrust_stand_avg).items()})
|
||||
ret.update({k: v for k, v in p.data_accustic_avg.items()})
|
||||
|
||||
return ret
|
||||
|
||||
def field_entries(datacls) -> dict[str, float]:
|
||||
fnames = [f.name for f in fields(datacls)]
|
||||
return {n: getattr(datacls, n) for n in fnames}
|
||||
|
||||
|
||||
def read_series(fname: str) -> dict[int, OpPointData]:
|
||||
with open(fname, 'rb') as f:
|
||||
return pickle.load(f)
|
||||
|
||||
def series_dataclosrc(series: dict[int, OpPointData]) -> ColumnDataSource:
|
||||
entries = [oppoint_data_src(p) for p in series.values()]
|
||||
keys = entries[0].keys()
|
||||
data = { key: [e[key] for e in entries] for key in keys }
|
||||
return ColumnDataSource(data)
|
||||
|
||||
def fft_datasrc(op: OpPointData) -> ColumnDataSource:
|
||||
fft_data = op.nor_report_parsed.glob_fft
|
||||
freqs = sorted(fft_data.keys())
|
||||
vals = [fft_data[f] for f in freqs]
|
||||
data = { "FREQ": freqs, "POWER": vals }
|
||||
return data
|
||||
|
||||
|
||||
def make_doc(doc, files):
|
||||
series = [ read_series(f) for f in files ]
|
||||
sources = [series_dataclosrc(s) for s in series]
|
||||
keys = list(sources[0].data.keys())
|
||||
|
||||
p = figure(title='', x_axis_label='X', y_axis_label='Y', tools=['hover', 'pan', 'xwheel_zoom'])
|
||||
p.sizing_mode = 'scale_both' # type: ignore
|
||||
|
||||
multi_choice = MultiChoice(title="Y Axis", options=keys)
|
||||
xsel = Select(title="X axis:", value="", options=keys)
|
||||
srcsel = MultiChoice(title="Source:", value=files, options=files)
|
||||
|
||||
colors = ['blue', 'green', 'red', 'yellow', 'orange', 'purple']
|
||||
|
||||
pfft = figure(title='', x_axis_label='X', y_axis_label='Y', tools=['hover', 'pan', 'xwheel_zoom'])
|
||||
pfft.sizing_mode = 'scale_both' # type: ignore
|
||||
|
||||
def update_plot(attr, _, new_values):
|
||||
p.renderers = [] # type: ignore
|
||||
|
||||
p.xaxis.axis_label = xsel.value
|
||||
|
||||
ymin = math.inf
|
||||
ymax = -math.inf
|
||||
|
||||
for source, fname, color in zip(sources, files, colors):
|
||||
if fname not in srcsel.value: #type: ignore
|
||||
continue
|
||||
for column in multi_choice.value: # type: ignore
|
||||
p.line(x=xsel.value, y=column, source=source, legend_label=f'{fname} {column}', line_width=2, color=color)
|
||||
|
||||
ymin = min(min(source.data[column]), ymin)
|
||||
ymax = max(max(source.data[column]), ymax)
|
||||
|
||||
margin = (ymax-ymin) * 0.1
|
||||
p.y_range.start = ymin - margin
|
||||
p.y_range.end = ymax + margin
|
||||
|
||||
|
||||
pfft.renderers = [] # type: ignore
|
||||
for serie, fname, color in zip(series, files, colors):
|
||||
if fname not in srcsel.value: #type: ignore
|
||||
continue
|
||||
data = fft_datasrc(serie[1500])
|
||||
pfft.line(x='FREQ', y='POWER', source=data, legend_label=f'{fname} FFT', line_width=2, color=color)
|
||||
|
||||
|
||||
|
||||
|
||||
multi_choice.on_change('value', update_plot)
|
||||
xsel.on_change('value', update_plot)
|
||||
srcsel.on_change('value', update_plot)
|
||||
|
||||
layout = column(column(srcsel, multi_choice, xsel), p, pfft)
|
||||
layout.sizing_mode = 'scale_both' # type: ignore
|
||||
|
||||
doc.add_root(layout)
|
||||
|
||||
if __name__ == "__main__":
|
||||
files = sys.argv[1:]
|
||||
if len(files) == 0:
|
||||
print(f'Usage: {sys.argv[0]} [measurement directory/directories]')
|
||||
print(f'Examples:')
|
||||
print(f'- {sys.argv[0]} benchmark1/shroud_1m')
|
||||
print(f'- {sys.argv[0]} benchmark1/shroud_*')
|
||||
print(f'- {sys.argv[0]} benchmark1/*')
|
||||
exit()
|
||||
apps = {'/': Application(FunctionHandler(lambda doc: make_doc(doc, files)))}
|
||||
server = Server(apps, port=5000)
|
||||
print('Visualizer started. navigate to http://localhost:5000/ to continue')
|
||||
print('Exit with interrupt (Ctrl+C)')
|
||||
server.run_until_shutdown()
|
||||
|
||||
Loading…
Reference in New Issue