Index: src/sas/sascalc/corfunc/corfunc_calculator.py
===================================================================
--- src/sas/sascalc/corfunc/corfunc_calculator.py	(revision 8d79a8733c6b2a84de677a79908dbf797e1910d9)
+++ src/sas/sascalc/corfunc/corfunc_calculator.py	(revision 8d79a8733c6b2a84de677a79908dbf797e1910d9)
@@ -0,0 +1,157 @@
+"""
+This module implements corfunc
+"""
+import warnings
+import numpy as np
+from scipy.optimize import curve_fit
+from scipy.interpolate import interp1d
+from scipy.fftpack import dct
+from scipy.signal import argrelextrema
+from numpy.linalg import lstsq
+from sas.sascalc.dataloader.data_info import Data1D
+
+class CorfuncCalculator(object):
+
+    # Helper class
+    class _Struct:
+        def __init__(self, **entries):
+            self.__dict__.update(entries)
+
+    class _Interpolator(object):
+        """
+        Interpolates between curve f and curve g over the range start:stop and
+        caches the result of the function when it's called
+
+        :param f: The first curve to interpolate
+        :param g: The second curve to interpolate
+        :param start: The value at which to start the interpolation
+        :param stop: The value at which to stop the interpolation
+        """
+        def __init__(self, f, g, start, stop):
+            self.f = f
+            self.g = g
+            self.start = start
+            self.stop = stop
+            self._lastx = []
+            self._lasty = []
+
+        def __call__(self, x):
+            if self._lastx == [] or x.tolist() != self._lastx.tolist():
+                self._lasty = self._smoothed_function(x)
+                self._lastx = x
+            return self._lasty
+
+        def _smoothed_function(self,x):
+            ys = np.zeros(x.shape)
+            ys[x <= self.start] = self.f(x[x <= self.start])
+            ys[x >= self.stop] = self.g(x[x >= self.stop])
+            with warnings.catch_warnings():
+                # Ignore divide by zero error
+                warnings.simplefilter('ignore')
+                h = 1/(1+(x-self.stop)**2/(self.start-x)**2)
+            mask = np.logical_and(x > self.start, x < self.stop)
+            ys[mask] = h[mask]*self.g(x[mask])+(1-h[mask])*self.f(x[mask])
+            return ys
+
+
+    def __init__(self, data, lowerq, upperq, background=0, scale=1):
+        """
+        Initialize the class.
+
+        :param data: Data of the type DataLoader.Data1D
+        :param background: Background value. Will be subtracted from the data
+            before processing
+        :param scale: Scaling factor for I(q)
+        """
+        print "Before: {}".format(data.y[0])
+        self._data = self._get_data(data, background, scale)
+        print "After: {}".format(self._data.y[0])
+        self.lowerq = lowerq
+        self.upperq = upperq
+
+    def compute_extrapolation(self):
+        q = self._data.x
+        iq = self._data.y
+
+        s2 = self._fit_data(q, iq)
+        qs = np.arange(0, q[-1]*100, (q[1]-q[0]))
+        iqs = s2(qs)
+
+        extrapolation = Data1D(qs, iqs)
+
+        return extrapolation
+
+    def compute_transform(self, extrapolation):
+        """
+        Transform an extrapolated scattering curve into a correlation function.
+        """
+        qs = extrapolation.x
+        iqs = extrapolation.y
+
+        gamma = dct(iqs*qs**2)
+        gamma = gamma / gamma.max()
+        xs = np.pi*np.arange(len(qs),dtype=np.float32)/(q[1]-q[0])/len(qs)
+
+        transform = Data1D(xs, gamma)
+
+        return transform
+
+    def _porod(self, q, K, sigma):
+        """Equation for the Porod region of the data"""
+        return (K*q**(-4))*np.exp(-q**2*sigma**2)
+
+    def _fit_guinier(self, q, iq):
+        """Fit the Guinier region of the curve"""
+        A = np.vstack([q**2, np.ones(q.shape)]).T
+        return lstsq(A, np.log(iq))
+
+    def _get_data(self, data, background, scale):
+        """
+        Prepares the data for analysis
+
+        :return: new_data = data * scale - background
+        """
+        # Only process data of the class Data1D
+        if not issubclass(data.__class__, Data1D):
+            raise ValueError, "Data must be of the type DataLoader.Data1D"
+
+        # Prepare the data
+        new_data = (scale * data)
+        new_data.y -= background
+
+        # Check the vector lengths are equal
+        assert len(new_data.x) == len(new_data.y)
+
+        # Ensure the errors are set correctly
+        if new_data.dy is None or len(new_data.x) != len(new_data.dy) or \
+            (min(new_data.dy) == 0 and max(new_data.dy) == 0):
+            new_data.dy = np.ones(len(new_data.x))
+
+        return new_data
+
+    def _fit_data(self, q, iq):
+        """Given a data set, extrapolate out to large q with Porod
+        and to q=0 with Guinier"""
+        mask = np.logical_and(q > self.upperq[0], q < self.upperq[1])
+
+        # Returns an array where the 1st and 2nd elements are the values of k
+        # and sigma for the best-fit Porod function
+        fitp = curve_fit(lambda q, k, sig: self._porod(q, k, sig)*q**2,
+                         q[mask], iq[mask]*q[mask]**2)[0]
+
+        # Smooths between the best-fit porod function and the data to produce a
+        # better fitting curve
+        data = interp1d(q, iq)
+        s1 = self._Interpolator(data, lambda x: self._porod(x, fitp[0], fitp[1]),
+            self.upperq[0], q[-1])
+
+        mask = np.logical_and(q < self.lowerq, 0 < q)
+
+        # Returns parameters for the best-fit Guinier function
+        g = self._fit_guinier(q[mask], iq[mask])[0]
+
+        # Smooths between the best-fit Guinier function and the Porod curve
+        s2 = self._Interpolator((lambda x: (np.exp(g[1]+g[0]*x**2))), s1, q[0],
+            self.lowerq)
+
+        return s2
Index: src/sas/sasgui/perspectives/corfunc/corfunc.py
===================================================================
--- src/sas/sasgui/perspectives/corfunc/corfunc.py	(revision 31aa3a3146b9e36c813639c3f4352834c7370c67)
+++ src/sas/sasgui/perspectives/corfunc/corfunc.py	(revision 8d79a8733c6b2a84de677a79908dbf797e1910d9)
@@ -5,4 +5,5 @@
 import sys
 import logging
+import copy
 from sas.sasgui.guiframe.plugin_base import PluginBase
 from sas.sasgui.guiframe.gui_manager import MDIFrame
@@ -18,6 +19,7 @@
 
 
-GROUP_ID_IQ_DATA = r"$I_{obs}(q)$"
+GROUP_ID_IQ_DATA = r"$I(q)$"
 IQ_DATA_LABEL = r"$I_{obs}(q)$"
+IQ_EXTRAPOLATED_DATA_LABEL = r"$I_{extrap}(q)$"
 
 
@@ -117,5 +119,5 @@
                         info='error'))
 
-    def show_data(self, path=None, data=None, reset=False):
+    def show_data(self, data, label, path=None, reset=False):
         """
         Show data read from a file
@@ -125,15 +127,15 @@
         :param reset: If True, all other plottables will be cleared
         """
-        if data.dy is not None:
-            new_plot = Data1D(data.x, data.y, dy=data.dy)
-        else:
-            new_plot = Data1D(data.x, data.y)
+        new_plot = Data1D(data.x, data.y, dy=data.dy)
+
+        if label == IQ_DATA_LABEL or label == IQ_EXTRAPOLATED_DATA_LABEL:
+            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
+            new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+
         new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
-        new_plot.name = IQ_DATA_LABEL
-        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
-        new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+        new_plot.id = label
+        new_plot.name = label
         new_plot.interactive = True
         new_plot.group_id = GROUP_ID_IQ_DATA
-        new_plot.id = self.data_id
         new_plot.title = "I(q)"
         # Show data on a linear scale
Index: src/sas/sasgui/perspectives/corfunc/corfunc_panel.py
===================================================================
--- src/sas/sasgui/perspectives/corfunc/corfunc_panel.py	(revision ebc5470b49de6d511ce9f9c80e57bda81f65b5aa)
+++ src/sas/sasgui/perspectives/corfunc/corfunc_panel.py	(revision 8d79a8733c6b2a84de677a79908dbf797e1910d9)
@@ -6,8 +6,11 @@
 from sas.sasgui.guiframe.panel_base import PanelBase
 from sas.sasgui.guiframe.utils import check_float
+from sas.sasgui.guiframe.dataFitting import Data1D
 from sas.sasgui.perspectives.invariant.invariant_widgets import OutputTextCtrl
 from sas.sasgui.perspectives.invariant.invariant_widgets import InvTextCtrl
 from sas.sasgui.perspectives.fitting.basepage import ModelTextCtrl
 from sas.sasgui.perspectives.corfunc.corfunc_state import CorfuncState
+import sas.sasgui.perspectives.corfunc.corfunc
+from sas.sascalc.corfunc.corfunc_calculator import CorfuncCalculator
 
 if sys.platform.count("win32") > 0:
@@ -36,4 +39,5 @@
         self._manager = manager
         self._data = data # The data to be analysed
+        self._extrapolated_data = None # The extrapolated data set
         self._data_name_box = None # Text box to show name of file
         self._background_input = None
@@ -52,4 +56,5 @@
         self._qmax1_input.Bind(wx.EVT_TEXT, self._on_enter_input)
         self._qmax2_input.Bind(wx.EVT_TEXT, self._on_enter_input)
+        self._background_input.Bind(wx.EVT_TEXT, self._on_enter_input)
 
     def set_state(self, state=None, data=None):
@@ -67,5 +72,5 @@
         if data is not None:
             self.state.data = data
-        self.set_data(self.state.data, set_qrange=False)
+        self.set_data(data, set_qrange=False)
         if self.state.qmin is not None:
             self.set_qmin(self.state.qmin)
@@ -107,5 +112,6 @@
         self._data = data
         if self._manager is not None:
-            self._manager.show_data(data=data, reset=True)
+            from sas.sasgui.perspectives.corfunc.corfunc import IQ_DATA_LABEL
+            self._manager.show_data(data, IQ_DATA_LABEL, reset=True)
         if set_qrange:
             lower = data.x[-1]*0.05
@@ -118,4 +124,29 @@
     def get_data(self):
         return self._data
+
+    def compute_extrapolation(self, event=None):
+        if self._data is None:
+            msg = "Data must be loaded in order to perform an extrapolation."
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+            return
+        if not self._validate_inputs:
+            msg = "Invalid Q range entered."
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+            return
+        calculator = CorfuncCalculator(self._data, self.qmin, self.qmax,
+            background=self.background)
+        self._extrapolated_data = calculator.compute_extrapolation()
+        # TODO: Find way to set xlim and ylim so full range of data can be
+        # plotted
+        maxq = self._data.x.max()
+        mask = self._extrapolated_data.x <= maxq
+        numpts = len(self._extrapolated_data.x[mask]) + 250
+        plot_x = self._extrapolated_data.x[0:numpts]
+        plot_y = self._extrapolated_data.y[0:numpts]
+        to_plot = Data1D(plot_x, plot_y)
+        from sas.sasgui.perspectives.corfunc.corfunc import\
+            IQ_EXTRAPOLATED_DATA_LABEL
+        self._manager.show_data(to_plot, IQ_EXTRAPOLATED_DATA_LABEL)
+
 
     def save_project(self, doc=None):
@@ -165,11 +196,11 @@
         self.qmax = (new_qmax1, new_qmax2)
         self.background = float(self._background_input.GetValue())
-        data_id = self._manager.data_id
-        from sas.sasgui.perspectives.corfunc.corfunc import GROUP_ID_IQ_DATA
+        from sas.sasgui.perspectives.corfunc.corfunc import GROUP_ID_IQ_DATA,\
+            IQ_DATA_LABEL
         group_id = GROUP_ID_IQ_DATA
         if event is not None:
             wx.PostEvent(self._manager.parent, PlotQrangeEvent(
                 ctrl=[self._qmin_input, self._qmax1_input, self._qmax2_input],
-                active=event.GetEventObject(), id=data_id, group_id=group_id,
+                active=event.GetEventObject(), id=IQ_DATA_LABEL, group_id=group_id,
                 leftdown=False))
 
@@ -201,5 +232,5 @@
                 qmin_valid = False
             elif background < 0 or background > self._data.y.max():
-                msg = "background must be positive and less than highest I value"
+                msg = "background must be positive and less than highest I"
                 background_valid = False
         if not qmin_valid:
@@ -365,4 +396,6 @@
         compute_btn = wx.Button(self, wx.NewId(), "Compute Measuments")
 
+        extrapolate_btn.Bind(wx.EVT_BUTTON, self.compute_extrapolation)
+
         controls_sizer.Add(extrapolate_btn, wx.CENTER | wx.EXPAND)
         controls_sizer.Add(transform_btn, wx.CENTER | wx.EXPAND)
