Skip to content

Commit

Permalink
Added new accepted file specifications, added sine fit, created windo…
Browse files Browse the repository at this point in the history
…ws EXE installer
  • Loading branch information
thomas-schweich committed Jun 26, 2017
1 parent 17e8840 commit 838118a
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .idea/dataManipulation.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 1 addition & 11 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 46 additions & 5 deletions Graph.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import matplotlib

matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np
Expand All @@ -14,6 +15,8 @@
class Graph:
__author__ = "Thomas Schweich"

non_serializable_attrs = {'rawXData', 'rawYData', 'graphWindow', 'window', 'subplot', 'radioVar', 'chainData'}

def __init__(self, window=None, title="", xLabel="", yLabel="", rawXData=np.array([0]), rawYData=np.array([0]),
xMagnitude=0, yMagnitude=0, autoScaleMagnitude=False, subplot=None):
"""Creates a Graph of specified data including a wide variety of methods for manipulating the data.
Expand Down Expand Up @@ -63,9 +66,7 @@ def getMetaData(self):
def getMetaData(self):
"""Returns a dict of all class data which is not a function and not a numpy array"""
return {key: value for key, value in self.__dict__.items() if not key.startswith("__") and
not callable(key) and not (key == "rawXData" or key == "rawYData" or key == "graphWindow" or
key == "window" or key == "subplot" or key == "radioVar" or
key == "chainData")}
not callable(key) and key not in Graph.non_serializable_attrs}

def useMetaFrom(self, other):
"""Sets the metadata of this graph to the metadata of other"""
Expand Down Expand Up @@ -200,6 +201,44 @@ def getCurveFit(self, fitFunction):
autoScaleMagnitude=self.autoScaleMagnitude, title="Fit for " + self.title, xLabel=self.xLabel,
yLabel=self.yLabel)

def getSinFit(self):
"""Returns a Graph of a sine wave most closely fitting this graph"""
tt = self.getRawData()[0]
yy_raw = self.getRawData()[1]

# Subtract a linear fit from the function
line_func = lambda x, a, b: a * x + b
popt, pcov = curve_fit(line_func, tt, yy_raw)
slope, intercept = popt
linear_fit = line_func(tt, *popt)

yy = yy_raw - linear_fit

avg_delta = (tt.max() - tt.min()) / (len(tt) - 1) # Find average interval between points
ff = np.fft.fftfreq(len(tt), avg_delta) # assume uniform spacing
Fyy = abs(np.fft.fft(yy))
guess_freq = abs(
ff[np.argmax(Fyy[1:]) + 1]) # excluding the zero frequency "peak", which is related to offset
guess_amp = np.std(yy) * 2. ** 0.5
guess_offset = np.mean(yy)
guess = np.array([guess_amp, 2. * np.pi * guess_freq, 0., guess_offset])

def sinfunc(t, A, w, p, c):
return A * np.sin(w * t + p) + c

popt, pcov = curve_fit(sinfunc, tt, yy, p0=guess)
A, w, p, c = popt
fitfunc = lambda t: A * np.sin(w * t + p) + c

newY = fitfunc(self.getRawData()[0])

# Add back the linear fit
newY += linear_fit

return Graph(self.window, rawXData=np.array(self.getRawData()[0]), rawYData=newY,
autoScaleMagnitude=self.autoScaleMagnitude, title="Fit for " + self.title, xLabel=self.xLabel,
yLabel=self.yLabel)

def getFFT(self):
"""Returns a Graph of the Single-Sided Amplitude Spectrum of y(t)"""
x, y = self.getRawData()
Expand Down Expand Up @@ -241,8 +280,10 @@ def slice(self, begin=0, end=None, step=1):
"""
end = len(self.getRawData()[0]) - 1 if not end else end
return Graph(self.window, title=str(self.title) + " from point " + str(int(begin)) + " to " + str(int(end)),
xLabel=self.xLabel, yLabel=self.yLabel, rawXData=self.getRawData()[0][int(begin):int(end):int(step)],
rawYData=self.getRawData()[1][int(begin):int(end):int(step)], autoScaleMagnitude=self.autoScaleMagnitude)
xLabel=self.xLabel, yLabel=self.yLabel,
rawXData=self.getRawData()[0][int(begin):int(end):int(step)],
rawYData=self.getRawData()[1][int(begin):int(end):int(step)],
autoScaleMagnitude=self.autoScaleMagnitude)

def onClick(self, event):
"""Opens this Graph's GraphWindow if the event is within its axes and was a double click"""
Expand Down
13 changes: 13 additions & 0 deletions GraphWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ def populate(self):
self.fitBox = self.addWidget(Tk.Radiobutton, command=self.refreshOptions, text="Fit Options",
variable=self.radioVar, value=0)
self.fitBox.val = 0
self.addWidget(Tk.Button, parent=self.fitBox, command=self.sinFit, text="Sinusoidal Fit")
self.addWidget(Tk.Button, parent=self.fitBox, command=self.quarticFit, text="Quartic Fit")
self.addWidget(Tk.Button, parent=self.fitBox, command=self.cubicFit, text="Cubic Fit")
self.addWidget(Tk.Button, parent=self.fitBox, command=self.quadraticFit, text="Quadratic Fit")
Expand Down Expand Up @@ -342,6 +343,18 @@ def safeFit(self, fitFunction):
tkMessageBox.showerror("Fit", "Couldn't fit function.\n" + str(r))
self.window.lift()

def safeSinFit(self):
""" Safely returns a sinusoidal fit """
try:
return self.graph.getSinFit()
except RuntimeError as r:
tkMessageBox.showerror("Fit", "Couldn't fit function.\n" + str(r))
self.window.lift()

def sinFit(self):
"""Plots a sine fit of the Graph's data with reference"""
self.plotWithReference(self.safeSinFit())

def quarticFit(self):
"""Plots a quartic fit of the Graph's data with reference"""
self.plotWithReference(self.safeFit(
Expand Down
12 changes: 11 additions & 1 deletion WIZ.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,25 @@ def loadRawData(self, callFunc=None):
for i, line in enumerate(f):
if i == 0:
firstline = line
if i == 1:
secondline = line
lines.append(line)
if i == 10:
break
regex = r'-?\ *[0-9]+\.?[0-9]*(?:[Ee]\ *-?\ *[0-9]+)?|\w+'
regex = r'[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?'
try:
numbers = re.findall(regex, firstline)
print 'First Line: "%s"\nNumbers: %s' % (firstline, str(numbers))
except NameError:
self.error.pack()
return
try:
if len(numbers) == 0:
numbers = re.findall(regex, secondline)
print 'Second Line "%s"\nNumbers (second line): %s' % (secondline, str(numbers))
except NameError:
self.eror.pack()
return
tree = ttk.Treeview(self.newFrame, height=10)
tree.pack()
cols = ()
Expand Down
Binary file added dist/WIZ/res/WIZ.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion programSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"Icon Location": "res/WIZ.ico",
"Max Preview Points": 100000,
"Plot Chunk Size": 100000,
"DPI": 150
"DPI": 271
}

0 comments on commit 838118a

Please sign in to comment.