added f-omega option to log-fourier transform; closes #134

This commit is contained in:
Dominik Demuth 2023-11-09 17:50:29 +01:00
parent dbb35cdba4
commit 9c5d91918f
5 changed files with 78 additions and 28 deletions

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/integratederive_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/integratederive_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -13,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object): class Ui_Dialog(object):
def setupUi(self, Dialog): def setupUi(self, Dialog):
Dialog.setObjectName("Dialog") Dialog.setObjectName("Dialog")
Dialog.resize(400, 308) Dialog.resize(400, 375)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.listWidget = QtWidgets.QListWidget(Dialog) self.listWidget = QtWidgets.QListWidget(Dialog)
@ -49,6 +50,9 @@ class Ui_Dialog(object):
self.log_checkbox = QtWidgets.QCheckBox(Dialog) self.log_checkbox = QtWidgets.QCheckBox(Dialog)
self.log_checkbox.setObjectName("log_checkbox") self.log_checkbox.setObjectName("log_checkbox")
self.verticalLayout.addWidget(self.log_checkbox) self.verticalLayout.addWidget(self.log_checkbox)
self.freq_box = QtWidgets.QCheckBox(Dialog)
self.freq_box.setObjectName("freq_box")
self.verticalLayout.addWidget(self.freq_box)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1) self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
self.horizontalLayout_2.setSpacing(3) self.horizontalLayout_2.setSpacing(3)
@ -67,8 +71,8 @@ class Ui_Dialog(object):
self.verticalLayout.addWidget(self.buttonBox) self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog): def retranslateUi(self, Dialog):
@ -80,4 +84,5 @@ class Ui_Dialog(object):
self.ft_comboBox.setItemText(1, _translate("Dialog", "Imag")) self.ft_comboBox.setItemText(1, _translate("Dialog", "Imag"))
self.ft_comboBox.setItemText(2, _translate("Dialog", "Complex")) self.ft_comboBox.setItemText(2, _translate("Dialog", "Complex"))
self.log_checkbox.setText(_translate("Dialog", "use logarithmic x axis")) self.log_checkbox.setText(_translate("Dialog", "use logarithmic x axis"))
self.freq_box.setText(_translate("Dialog", "return x axis as f, not omega"))
self.newgraph_checkbox.setText(_translate("Dialog", "New graph")) self.newgraph_checkbox.setText(_translate("Dialog", "New graph"))

View File

@ -1139,13 +1139,14 @@ class UpperManagement(QtCore.QObject):
def logft(self, **kwargs): def logft(self, **kwargs):
new_sets = [] new_sets = []
ft_mode = kwargs['ft_mode'] ft_mode = kwargs['ft_mode']
return_f = kwargs['return_f']
for set_id in kwargs['sets']: for set_id in kwargs['sets']:
data_i = self.data[set_id] data_i = self.data[set_id]
if ft_mode in ['cos', 'sin']: if ft_mode in ['cos', 'sin']:
new_data = Points(*logft(data_i.x, data_i.y, mode=ft_mode)) new_data = Points(*logft(data_i.x, data_i.y, mode=ft_mode, return_f=return_f))
else: else:
new_data = Signal(*logft(data_i.x, data_i.y, mode=ft_mode)) new_data = Signal(*logft(data_i.x, data_i.y, mode=ft_mode, return_f=return_f))
new_sets.append(self.add(new_data, color=data_i['color'], symbol=data_i['symbol'], line=data_i['line'])) new_sets.append(self.add(new_data, color=data_i['color'], symbol=data_i['symbol'], line=data_i['line']))
self.data[new_sets[-1]].update(data_i.data.meta) self.data[new_sets[-1]].update(data_i.data.meta)

View File

@ -16,16 +16,19 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
self.start_lineedit.setValidator(QtGui.QDoubleValidator()) self.start_lineedit.setValidator(QtGui.QDoubleValidator())
self.stop_lineedit.setValidator(QtGui.QDoubleValidator()) self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
self.ft_comboBox.hide() self.ft_comboBox.hide()
self.freq_box.hide()
elif self.mode == 'd': elif self.mode == 'd':
self.setWindowTitle('Differentiation dialog') self.setWindowTitle('Differentiation dialog')
self.widget.hide() self.widget.hide()
self.ft_comboBox.hide() self.ft_comboBox.hide()
self.freq_box.hide()
elif self.mode == 'l': elif self.mode == 'l':
self.setWindowTitle('Logarithmic FT dialog') self.setWindowTitle('Logarithmic FT dialog')
self.log_checkbox.hide() self.log_checkbox.hide()
self.widget.hide() self.widget.hide()
self.freq_box.show()
else: else:
raise ValueError(f'Unknown mode {mode}, use "d", "i", or "l".') raise ValueError(f'Unknown mode {mode}, use "d", "i", or "l".')
@ -54,8 +57,10 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
self.stop_lineedit.setEnabled(full_range != QtCore.Qt.Checked) self.stop_lineedit.setEnabled(full_range != QtCore.Qt.Checked)
def get_options(self): def get_options(self):
opts = {'graph': '' if self.newgraph_checkbox.isChecked() else self.graph_combobox.currentData(), opts = {
'mode': self.mode, 'sets': []} 'graph': '' if self.newgraph_checkbox.isChecked() else self.graph_combobox.currentData(),
'mode': self.mode, 'sets': []
}
if self.mode == 'i': if self.mode == 'i':
start = None start = None
@ -75,6 +80,7 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
if self.mode == 'l': if self.mode == 'l':
opts['ft_mode'] = ['cos', 'sin', 'complex'][self.ft_comboBox.currentIndex()] opts['ft_mode'] = ['cos', 'sin', 'complex'][self.ft_comboBox.currentIndex()]
opts['return_f'] = self.freq_box.isChecked()
else: else:
opts['log'] = self.log_checkbox.isChecked() opts['log'] = self.log_checkbox.isChecked()

View File

@ -27,10 +27,13 @@ More details can be found in [Blo]
""" """
def logft_cos(x, y, new_x=None): def logft_cos(
n = x.size x: np.ndarray,
if new_x is None: y: np.ndarray,
new_x = 2*np.pi * np.geomspace(1/np.max(x), 1/np.min(x), num=n) new_x: np.ndarray = None,
return_f: bool = True,
):
n, new_x, divide_by = _prepare_calculation(x, new_x, return_f)
dydx = np.diff(y) / np.diff(x) dydx = np.diff(y) / np.diff(x)
@ -48,13 +51,16 @@ def logft_cos(x, y, new_x=None):
ret_val /= new_x**2 ret_val /= new_x**2
return new_x, ret_val return new_x/divide_by, ret_val
def logft_sin(x, y, new_x=None): def logft_sin(
n = x.size x: np.ndarray,
if new_x is None: y: np.ndarray,
new_x = 2*np.pi * np.geomspace(1/np.max(x), 1/np.min(x), num=n) new_x: np.ndarray = None,
return_f: bool = True,
):
n, new_x, divide_by = _prepare_calculation(x, new_x, return_f)
dydx = np.diff(y) / np.diff(x) dydx = np.diff(y) / np.diff(x)
@ -73,13 +79,20 @@ def logft_sin(x, y, new_x=None):
ret_val /= new_x**2 ret_val /= new_x**2
ret_val += y[0] / new_x ret_val += y[0] / new_x
return new_x, ret_val return new_x/divide_by, ret_val
def logft_cmplx(x, y, new_x=None, backward: bool = False): def logft_cmplx(
n = x.size x: np.ndarray,
if new_x is None: y: np.ndarray,
new_x = 2 * np.pi * np.geomspace(1/np.max(x), 1/np.min(x), num=n) new_x: np.ndarray = None,
backward: bool = False,
return_f: bool = True,
):
if backward:
return_f = False
n, new_x, divide_by = _prepare_calculation(x, new_x, return_f)
dydx = np.diff(y) / np.diff(x) dydx = np.diff(y) / np.diff(x)
@ -98,16 +111,22 @@ def logft_cmplx(x, y, new_x=None, backward: bool = False):
ret_val /= new_x**2 ret_val /= new_x**2
ret_val += sign * 1j*y[0]/new_x ret_val += sign * 1j*y[0]/new_x
return new_x, ret_val return new_x/divide_by, ret_val
def logft(x, y, new_x=None, mode='cos'): def logft(
x: np.ndarray,
y: np.ndarray,
new_x: np.ndarray = None,
mode: str = 'cos',
return_f: bool = True,
):
if mode not in ['cos', 'sin', 'complex']: if mode not in ['cos', 'sin', 'complex']:
raise ValueError(f'Unknown mode {mode}, use "cos", "sin", "complex".') raise ValueError(f'Unknown mode {mode}, use "cos", "sin", "complex".')
ft_func = {'cos': logft_cos, 'sin': logft_sin, 'complex': logft_cmplx}[mode] ft_func = {'cos': logft_cos, 'sin': logft_sin, 'complex': logft_cmplx}[mode]
return ft_func(x, y, new_x=new_x) return ft_func(x, y, new_x=new_x, return_f=return_f)
def logift(x, y, new_x=None, mode='cos'): def logift(x, y, new_x=None, mode='cos'):
@ -120,3 +139,15 @@ def logift(x, y, new_x=None, mode='cos'):
return logft_cos(x, y, new_x=new_x) return logft_cos(x, y, new_x=new_x)
else: else:
return logft_sin(x, y, new_x=new_x) return logft_sin(x, y, new_x=new_x)
def _prepare_calculation(x: np.ndarray, new_x: np.ndarray | None, return_f: bool):
divide_by = 1.
n = x.size
if new_x is None:
if return_f:
divide_by = 2*np.pi
new_x = 2 * np.pi * np.geomspace(1 / np.max(x), 1 / np.min(x), num=n)
return n, new_x, divide_by

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>308</height> <height>375</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -92,6 +92,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="freq_box">
<property name="text">
<string>return x axis as f, not omega</string>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing"> <property name="spacing">