Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add save/load for impedance, add save/load tests for scope and impedance #20

Merged
merged 18 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]


## [0.2.0] - 2024-12-xx
Many improvements have been included in the update.
This has changed the API. Please check the documentation and the new examples.
### Added
- read lecroy scopes over ethernet
- unit and integration tests
- read, modify and display impedance plots from impedance analyzers
### Changed
- API and method names (check documentation!)

## [0.1.0] - 2023-05-19
Initial toolbox functionality
Expand All @@ -16,5 +25,6 @@ Initial toolbox functionality
- Basic functions for modifying data
- Basic plotting functions

[unreleased]: https://github.com/upb-lea/pySignalScope/compare/0.1.0...HEAD
[unreleased]: https://github.com/upb-lea/pySignalScope/compare/0.2.0...HEAD
[0.2.0]: https://github.com/upb-lea/pySignalScope/compare/0.1.0...0.2.0
[0.1.0]: https://github.com/upb-lea/pySignalScope/releases/tag/0.1.0
31 changes: 28 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,35 @@ The lower plot shows the post-processing of the filtered signal. This is multipl

.. image:: docs/source/figures/function_overview.png

Have a look at the `Scope example <https://github.com/upb-lea/pySignalScope/blob/main/examples/scope_example.py>`__ and at the `Impedance example <https://github.com/upb-lea/pySignalScope/blob/main/examples/impedance_example.py>`__ to see what you can do with this toolbox.

Naming convention
-------------------
This toolbox is divided into two modules: The functionality of an oscilloscope (``Scope``) and the functionality of an impedance analyzer (``Impedance``).

Scope
#####
The ``Scope`` module provides functionalities for editing and evaluating individual channels that are also provided by a real oscilloscope - just on a PC.
``Scope`` creates, imports, edits or evaluates ``Channels``. The following prefixes apply:

- ``generate_``: Generates a new ``Channel``
- ``no prefix``: Is applied to a ``Channel`` and results in a new ``Channel`` (e.g. ``add()`` adds two channels)
- ``from_``: Generates a ``Channel`` from an oscilloscope data set, a simulation program or a calculation (e.g. ``from_tektronix`` generates a ``Channel`` from a tektronix scope file)
- ``calc_``: Calculates individual values from a ``Channel`` (e.g. ``calc_rms()`` calculates the RMS from a given ``Channel``)
- ``plot_``: Plots channels in the desired arrangement (e.g. ``plot_channels()`` plots the given ``Channels``)

Impedance
#########
The ``Impedance`` module provides functionalities to evaluate impedance curves.
``Impedance`` creates, imports, edits or evaluates ``ImpedanceChannel``.

- ``generate_``: Generates a new ``ImpedanceChannel``
- ``no prefix``: Is applied to a ``ImpedanceChannel`` and results in a new ``ImpedanceChannel`` (e.g. ``modify()`` modifies an ``ImpedanceChannel``)
- ``from_``: Generates a ``ImpedanceChannel`` from an impedance analyzer data set, a simulation program or a calculation (e.g. ``from_waynekerr`` generates a ``ImpedanceChannel`` from a real measurement file)
- ``calc_``: Calculates individual values from a ``ImpedanceChannel`` (e.g. ``calc_rlc()`` calculates the equivalent resistance, inductance and capacitance)
- ``plot_``: Plots ``ImpedanceChannel`` (e.g. ``plot_impedance()`` plots the given ``ImpedanceChannels``)


Examples
--------
Have a look at the `example <examples/scope_example.py>`__, to see what you can do with this toolbox.

Documentation
---------------------------------------
Expand Down
48 changes: 39 additions & 9 deletions docs/source/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Bring measurements from the oscilloscope and the circuit simulator into a standa

.. image:: figures/introduction.png


Getting started
---------------
Install this repository into your virtual environment (venv) or jupyter notebook:
Expand All @@ -39,36 +40,65 @@ The lower plot shows the post-processing of the filtered signal. This is multipl

.. image:: figures/function_overview.png

Have a look at the `Scope example <https://github.com/upb-lea/pySignalScope/blob/main/examples/scope_example.py>`__ and at the `Impedance example <https://github.com/upb-lea/pySignalScope/blob/main/examples/impedance_example.py>`__ to see what you can do with this toolbox.

Naming convention
-------------------
This toolbox is divided into two modules: The functionality of an oscilloscope (``Scope``) and the functionality of an impedance analyzer (``Impedance``).

Scope
#####
The ``Scope`` module provides functionalities for editing and evaluating individual channels that are also provided by a real oscilloscope - just on a PC.
``Scope`` creates, imports, edits or evaluates ``Channels``. The following prefixes apply:

- ``generate_``: Generates a new ``Channel``
- ``no prefix``: Is applied to a ``Channel`` and results in a new ``Channel`` (e.g. ``add()`` adds two channels)
- ``from_``: Generates a ``Channel`` from an oscilloscope data set, a simulation program or a calculation (e.g. ``from_tektronix`` generates a ``Channel`` from a tektronix scope file)
- ``calc_``: Calculates individual values from a ``Channel`` (e.g. ``calc_rms()`` calculates the RMS from a given ``Channel``)
- ``plot_``: Plots channels in the desired arrangement (e.g. ``plot_channels()`` plots the given ``Channels``)

Impedance
#########
The ``Impedance`` module provides functionalities to evaluate impedance curves.
``Impedance`` creates, imports, edits or evaluates ``ImpedanceChannel``.

- ``generate_``: Generates a new ``ImpedanceChannel``
- ``no prefix``: Is applied to a ``ImpedanceChannel`` and results in a new ``ImpedanceChannel`` (e.g. ``modify()`` modifies an ``ImpedanceChannel``)
- ``from_``: Generates a ``ImpedanceChannel`` from an impedance analyzer data set, a simulation program or a calculation (e.g. ``from_waynekerr`` generates a ``ImpedanceChannel`` from a real measurement file)
- ``calc_``: Calculates individual values from a ``ImpedanceChannel`` (e.g. ``calc_rlc()`` calculates the equivalent resistance, inductance and capacitance)
- ``plot_``: Plots ``ImpedanceChannel`` (e.g. ``plot_impedance()`` plots the given ``ImpedanceChannels``)



Documentation
---------------------------------------

Find the documentation `here <https://upb-lea.github.io/pySignalScope/intro.html>`__.

Examples
--------
Have a look at the `example <https://github.com/upb-lea/pySignalScope/blob/main/examples/scope_example.py>`__, to see what you can do with this toolbox.

Bug Reports
-----------
Please use the issues report button within GitHub to report bugs.

Changelog
---------
Find the changelog `here <https://github.com/upb-lea/pySignalScope/blob/main/CHANGELOG.md>`__.

Find the changelog `here <CHANGELOG.md>`__.


pySignalScope function documentation
==================================================
.. currentmodule:: pysignalscope.scope

.. autoclass:: pysignalscope.Scope
.. autoclass:: pysignalscope.Channel
:members:

.. autoclass:: pysignalscope.HandleScope
.. autoclass:: pysignalscope.Scope
:members:

.. autoclass:: pysignalscope.Impedance
.. autoclass:: pysignalscope.ImpedanceChannel
:members:

.. autoclass:: pysignalscope.HandleImpedance
.. autoclass:: pysignalscope.Impedance
:members:

.. automodule:: pysignalscope.functions
Expand Down
20 changes: 12 additions & 8 deletions examples/impedance_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@
import pysignalscope as pss

# import data as a channel object
example_data_0mm5 = pss.HandleImpedance.from_waynekerr('impedance_example_data_0mm5.csv', '0.5mm air gap')
example_data_1mm5 = pss.HandleImpedance.from_waynekerr('impedance_example_data1mm5.csv', '1.5mm air gap')
example_data_rlc = pss.HandleImpedance.from_rlc('l', 1000, 500e-6, 10e-12)
example_data_0mm5 = pss.Impedance.from_waynekerr('impedance_example_data_0mm5.csv', '0.5mm air gap')
example_data_1mm5 = pss.Impedance.from_waynekerr('impedance_example_data1mm5.csv', '1.5mm air gap')
example_data_rlc = pss.Impedance.from_rlc('l', 1000, 500e-6, 10e-12)

# modify the data, e.g. change the color.
# For color changes, you can use standard colors or colors from the leapythontoolbox, see this example
example_data_0mm5 = pss.HandleImpedance.modify(example_data_0mm5, channel_color="red", channel_unit="random unit")
example_data_1mm5 = pss.HandleImpedance.modify(example_data_1mm5, channel_color=pss.gnome_colors["blue"])
example_data_rlc = pss.HandleImpedance.modify(example_data_rlc, channel_color=pss.gnome_colors["green"], channel_label="from rlc")
example_data_0mm5 = pss.Impedance.modify(example_data_0mm5, channel_color="red", channel_unit="random unit")
example_data_1mm5 = pss.Impedance.modify(example_data_1mm5, channel_color=pss.gnome_colors["blue"])
example_data_rlc = pss.Impedance.modify(example_data_rlc, channel_color=pss.gnome_colors["green"], channel_label="from rlc")

# # recalculate rlc data from a impedance curve
recalculated_r, recalculated_l, recalculated_c = (pss.HandleImpedance.calc_rlc(example_data_rlc, 'l', f_calc_l=10e3, f_calc_c=10e7, plot_figure=True))
recalculated_r, recalculated_l, recalculated_c = (pss.Impedance.calc_rlc(example_data_rlc, 'l', f_calc_l=10e3, f_calc_c=10e7, plot_figure=True))
print(f"{recalculated_r=}")
print(f"{recalculated_l=}")
print(f"{recalculated_c=}")

# plot multiple channel data
pss.HandleImpedance.plot_impedance([example_data_0mm5, example_data_1mm5, example_data_rlc])
pss.Impedance.plot_impedance([example_data_0mm5, example_data_1mm5, example_data_rlc])

# save and load an impedance object
pss.Impedance.save(example_data_rlc, "example")
loaded_impedance_object = pss.Impedance.load("example.pkl")
67 changes: 33 additions & 34 deletions examples/scope_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@
# ------------------------------------------

# Read curves from scope csv file
[voltage_prim, voltage_sec, current_prim, current_sec] = pss.HandleScope.from_tektronix('scope_example_data_tek.csv')
[voltage_prim, voltage_sec, current_prim, current_sec] = pss.Scope.from_tektronix('scope_example_data_tek.csv')

# Add labels and units to channels
voltage_prim = pss.HandleScope.modify(voltage_prim, channel_label='voltage primary', channel_unit='V')
voltage_sec = pss.HandleScope.modify(voltage_sec, channel_label='voltage secondary', channel_unit='V')
current_prim = pss.HandleScope.modify(current_prim, channel_label='current primary', channel_unit='A')
current_sec = pss.HandleScope.modify(current_sec, channel_label='current secondary', channel_unit='A')
voltage_prim = pss.Scope.modify(voltage_prim, channel_label='voltage primary', channel_unit='V')
voltage_sec = pss.Scope.modify(voltage_sec, channel_label='voltage secondary', channel_unit='V')
current_prim = pss.Scope.modify(current_prim, channel_label='current primary', channel_unit='A')
current_sec = pss.Scope.modify(current_sec, channel_label='current secondary', channel_unit='A')

# Show gain and DC-offset
current_sec = pss.HandleScope.modify(current_sec, channel_data_factor=1.3, channel_data_offset=10)
current_sec = pss.Scope.modify(current_sec, channel_data_factor=1.3, channel_data_offset=10)

# Plot channels
fig1 = pss.HandleScope.plot_channels([voltage_prim, voltage_sec], [current_prim, current_sec], timebase='us')
pss.HandleScope.save_figure(fig1, 'test')
fig1 = pss.Scope.plot_channels([voltage_prim, voltage_sec], [current_prim, current_sec], timebase='us')
pss.Scope.save_figure(fig1, 'test')

# Shift first two channels and plot the shift of these channels
shiftlist = pss.HandleScope.plot_shiftchannels([voltage_prim, voltage_sec])
shiftlist = pss.Scope.plot_shiftchannels([voltage_prim, voltage_sec])

# print the list
ch_i = 0
Expand All @@ -36,61 +36,60 @@
ch_i = ch_i+1

# short channels to a single period, perform FFT for current waveforms
current_prim = pss.HandleScope.short_to_period(current_prim, f0=200000)
current_prim = pss.HandleScope.modify(current_prim, channel_time_shift=5e-6)
pss.HandleScope.fft(current_prim)
current_prim = pss.Scope.short_to_period(current_prim, f0=200000)
current_prim = pss.Scope.modify(current_prim, channel_time_shift=5e-6)
pss.Scope.fft(current_prim)

# ------------------------------------------
# Example 2: Read curves from LeCroy csv-Files and GeckoCIRCUITS. Compare these signals.
# ------------------------------------------

meas_v_ob, meas_il_ib, meas_il_ob = pss.HandleScope.from_lecroy('scope_example_data_lecroy_1.csv',
'scope_example_data_lecroy_2.csv',
'scope_example_data_lecroy_3.csv')
meas_v_ob, meas_il_ib, meas_il_ob = pss.Scope.from_lecroy('scope_example_data_lecroy_1.csv', 'scope_example_data_lecroy_2.csv',
'scope_example_data_lecroy_3.csv')

meas_v_ob = pss.HandleScope.modify(meas_v_ob, channel_label='vl_ob_meas', channel_unit='V')
meas_il_ib = pss.HandleScope.modify(meas_il_ib, channel_label='il_ib_meas', channel_unit='A', channel_color='b')
meas_il_ob = pss.HandleScope.modify(meas_il_ob, channel_label='il_ob_meas', channel_unit='A', channel_color='m', channel_data_offset=1.5)
meas_v_ob = pss.Scope.modify(meas_v_ob, channel_label='vl_ob_meas', channel_unit='V')
meas_il_ib = pss.Scope.modify(meas_il_ib, channel_label='il_ib_meas', channel_unit='A', channel_color='b')
meas_il_ob = pss.Scope.modify(meas_il_ob, channel_label='il_ob_meas', channel_unit='A', channel_color='m', channel_data_offset=1.5)

# read dataset from gecko simulation
gecko_data = pss.HandleScope.from_geckocircuits('scope_example_data_gecko', f0=200000)
gecko_data = pss.Scope.from_geckocircuits('scope_example_data_gecko', f0=200000)

gecko_il_ib = gecko_data[0]
gecko_il_ib = pss.HandleScope.modify(gecko_il_ib, channel_label='il_ib_gecko', channel_unit='A', channel_color='r', channel_linestyle="--")
gecko_il_ib = pss.Scope.modify(gecko_il_ib, channel_label='il_ib_gecko', channel_unit='A', channel_color='r', channel_linestyle="--")
gecko_il_ob = gecko_data[-1]
gecko_il_ob = pss.HandleScope.modify(gecko_il_ob, channel_data_factor=-1, channel_label='il_ob_gecko', channel_unit='A', channel_color='g')
gecko_il_ob = pss.Scope.modify(gecko_il_ob, channel_data_factor=-1, channel_label='il_ob_gecko', channel_unit='A', channel_color='g')

# compare both waveforms
pss.HandleScope.compare_channels(meas_il_ib, gecko_il_ib, meas_il_ob, gecko_il_ob, shift=[-67.53e-6, 0, -67.53e-6, 0], timebase='us')
pss.Scope.compare_channels(meas_il_ib, gecko_il_ib, meas_il_ob, gecko_il_ob, shift=[-67.53e-6, 0, -67.53e-6, 0], timebase='us')

# Shift data
meas_il_ob = pss.HandleScope.modify(meas_il_ob, channel_time_shift=-67.53e-6)
meas_il_ob = pss.Scope.modify(meas_il_ob, channel_time_shift=-67.53e-6)
# Calculate sum
sum_il_ob = pss.HandleScope.add(meas_il_ob, meas_il_ob)
sum_il_ob = pss.Scope.add(meas_il_ob, meas_il_ob)

# Calculate difference
diff_il_ob = pss.HandleScope.subtract(sum_il_ob, meas_il_ob)
diff_il_ob = pss.Scope.subtract(sum_il_ob, meas_il_ob)

# Copy values to new variable
abs_diff_il_ob = pss.HandleScope.copy(diff_il_ob)
abs_diff_il_ob = pss.Scope.copy(diff_il_ob)
# Calculate absolute values
pss.HandleScope.abs(abs_diff_il_ob)
pss.Scope.calc_abs(abs_diff_il_ob)
# Copy values to new variable
sqr_diff_il_ob = pss.HandleScope.copy(diff_il_ob)
sqr_diff_il_ob = pss.Scope.copy(diff_il_ob)
# Calculate square
pss.HandleScope.square(sqr_diff_il_ob)
pss.Scope.square(sqr_diff_il_ob)
# Calculate values:
# Root means square
rms_diff_il_ob = pss.HandleScope.rms(diff_il_ob)
rms_diff_il_ob = pss.Scope.calc_rms(diff_il_ob)
# Average value
mean_diff_il_ob = pss.HandleScope.mean(diff_il_ob)
mean_diff_il_ob = pss.Scope.calc_mean(diff_il_ob)
# Average of absolute values
absmean_diff_il_ob = pss.HandleScope.absmean(diff_il_ob)
absmean_diff_il_ob = pss.Scope.calc_absmean(diff_il_ob)
# Print calculated values
print(f"Root mean square = {rms_diff_il_ob}\n"
f"Average value = {mean_diff_il_ob}\n"
f"Absolute average value={absmean_diff_il_ob}\n")
# Plot results
pss.HandleScope.plot_channels([diff_il_ob])
pss.Scope.plot_channels([diff_il_ob])
# Plot results
pss.HandleScope.plot_channels([diff_il_ob, abs_diff_il_ob, sqr_diff_il_ob])
pss.Scope.plot_channels([diff_il_ob, abs_diff_il_ob, sqr_diff_il_ob])
2 changes: 1 addition & 1 deletion pysignalscope/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
from pysignalscope.functions import *
from pysignalscope.colors import *
from pysignalscope.generalplotsettings import *
from pysignalscope.handleimpedance import *
from pysignalscope.impedance import *
from pysignalscope.impedance_dataclass import *
8 changes: 4 additions & 4 deletions pysignalscope/channelshift.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from matplotlib import pyplot as plt
# own libraries
from pysignalscope.logconfig import setup_logging
from pysignalscope.scope_dataclass import Scope
from pysignalscope.scope_dataclass import Channel
# python libraries
import logging
# Interactive shift plot
Expand Down Expand Up @@ -96,7 +96,7 @@ class Zoom_State(Enum):
zoom_state = Zoom_State.NoZoom

@staticmethod
def channel_shift(channels: List['Scope'], shiftstep_x: float, shiftstep_y: float, \
def channel_shift(channels: List['Channel'], shiftstep_x: float, shiftstep_y: float, \
displayrange_x: Tuple[float, float], displayrange_y: Tuple[float, float]):
"""
Interactive plot of channel datasets.
Expand All @@ -122,7 +122,7 @@ def channel_shift(channels: List['Scope'], shiftstep_x: float, shiftstep_y: floa
Minimal shift step in x-direction is the minimal difference of 2 points of all provided channels

:param channels: list of datasets
:type channels: list[Scope]
:type channels: list[Channel]
:param shiftstep_x: shift step in x-direction
:type shiftstep_x: float
:param shiftstep_y: shift step in y-direction
Expand All @@ -145,7 +145,7 @@ def channel_shift(channels: List['Scope'], shiftstep_x: float, shiftstep_y: floa

# Read channel data for the plot
for channel in channels:
cur_channelplot, = ScopeChShift.zoom_ax.plot(channel.channel_time, channel.channel_data, label=channel.channel_label, color=channel.channel_color)
cur_channelplot, = ScopeChShift.zoom_ax.plot(channel.time, channel.data, label=channel.label, color=channel.color)
ScopeChShift.channelplotlist.append(cur_channelplot)
ScopeChShift.shiftlist.append([0, 0])

Expand Down
Loading
Loading