diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..19932bff5a7864fd22ac1439a9acbcf5a4bfc429
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include aossceilo/ceilo.ncml
diff --git a/README.rst b/README.rst
index b39f6c5a66652e5a98b59f8edf8adeef97830db7..9273b26c7eca374dfefe74723d61d95ef5080bde 100644
--- a/README.rst
+++ b/README.rst
@@ -6,4 +6,4 @@ and higher level product generation.
 
 NOTE: Most of the code in this repository was copied from the old subversion
 repository. This code was executed on python 2.6/2.7 environments, but will
-be run from python 3.6 from now on. Some scripts may not work out of the box.
+be run from python 3.6+ from now on. Some scripts may not work out of the box.
diff --git a/aossceilo/__init__.py b/aossceilo/__init__.py
index 885b15d64004148f7c4d30cf2415e21af0a1725c..e5f64fa9976d92c1f00564fd169e7467beb0b323 100644
--- a/aossceilo/__init__.py
+++ b/aossceilo/__init__.py
@@ -1,7 +1,6 @@
 __import__('pkg_resources').declare_namespace(__name__)
 __docformat__ = 'Epytext'
 
-import os
 from aossceilo import CONFIG as c
 
 instrument_name = 'ceilo'
diff --git a/aossceilo/message.py b/aossceilo/message.py
index 17e3c6c3116da3578af47a69bbcff82c0a9f4882..9d411049b2adf6c725b04d9c02f087f948a76f3e 100644
--- a/aossceilo/message.py
+++ b/aossceilo/message.py
@@ -4,8 +4,7 @@ For processing Ceilometer CT25K Messages
 import logging
 from calendar import timegm
 from datetime import datetime
-#from urllib2 import urlopen
-from metobs.util.gen_open import open_ascii as urlopen
+from metobscommon.util.gen_open import open_ascii as urlopen
 
 from numpy import array
 
@@ -82,7 +81,7 @@ ALARM_MSGS = [
         "VOLTAGE FAILURE", "RECEIVER FAILURE", "LASER FAILURE",
         "LASER TEMPERATURE SHUT_OFF"]
 
-class MessageError(StandardError):
+class MessageError(RuntimeError):
     """General message error."""
 
 
@@ -268,7 +267,7 @@ class Message2(object):
 
         @return: 0 if the flag is not set, otherwise the value of the flag.
         """
-        return long(self.status_string, 16) & flag
+        return int(self.status_string, 16) & flag
     
     def get_status_messages(self):
         """
@@ -278,13 +277,13 @@ class Message2(object):
         """
         messages = {'alarms':[], 'warnings':[], 'informational':[]}
         for idx in range(len(WARNING_FLAGS)):
-            if long(self.status_string, 16) & WARNING_FLAGS[idx]:
+            if int(self.status_string, 16) & WARNING_FLAGS[idx]:
                 messages['warnings'].append(WARNING_MSGS[idx])
         for idx in range(len(ALARM_FLAGS)):
-            if long(self.status_string, 16) & ALARM_FLAGS[idx]:
+            if int(self.status_string, 16) & ALARM_FLAGS[idx]:
                 messages['alarms'].append(ALARM_MSGS[idx])
         for idx in range(len(INFO_FLAGS)):
-            if long(self.status_string, 16) & INFO_FLAGS[idx]:
+            if int(self.status_string, 16) & INFO_FLAGS[idx]:
                 messages['informational'].append(INFO_MSGS[idx])
         return messages
     
@@ -362,9 +361,9 @@ def parse_sky_condition(line, feet=False):
 
     return sky_cond
 
-def parse_backscatter(data):
 
-    if isinstance(data, (str, unicode)):
+def parse_backscatter(data):
+    if isinstance(data, str):
         lines = data.split('\n')
     else:
         lines = data
@@ -459,7 +458,7 @@ def load_messages(url, on_error=None):
         stamp = datetime.fromtimestamp(int(lines[0]))
         try:
             messages.append(Message2([l.strip() for l in lines[1:]], stamp))
-        except Exception, e:
+        except Exception as e:
             if hasattr(on_error, '__call__'):
                 on_error(lines, e)
         # disregard possible garbage between messages
diff --git a/aossceilo/nc.py b/aossceilo/nc.py
index ad149ba36a5c273402cc4e594a9b2ba70fe97aef..dc9633fdc89c8b06940d8489e30f97911e7e37fc 100644
--- a/aossceilo/nc.py
+++ b/aossceilo/nc.py
@@ -12,7 +12,7 @@ from calendar import timegm
 from datetime import datetime, timedelta
 
 import pkg_resources
-from numpy import array, zeros, int32, int8, uint16
+from numpy import array, zeros, int32
 
 from metobscommon.util import mytime
 from metobscommon.util.gen_open import open_ascii as urlopen
@@ -214,7 +214,7 @@ def make_ceilo_files(begin, basedir=None, end=None, loc=ssec_loc, site="rig", de
         url = get_ascii_url(dt, site=site, description=description)
         try:
             urlopen(url)
-        except StandardError, e:
+        except Exception as e:
             _log.warn('%s: %s', e, url)
             continue
         _log.debug('Creating %s', fpth)
diff --git a/aossceilo/quicklook/CONFIG.py b/aossceilo/quicklook/CONFIG.py
new file mode 100644
index 0000000000000000000000000000000000000000..9ec0d09751c16300d924fd2a3415520ad3121fd4
--- /dev/null
+++ b/aossceilo/quicklook/CONFIG.py
@@ -0,0 +1,111 @@
+"""Set of variables used by metobs.tower.quicklook to manipulate data and plot
+format.
+
+Any other options not seen in this CONFIG file are probably available in the
+metobs.tower.quicklook script flags.
+"""
+
+__author__ = 'David Hoese, SSEC'
+__docformat__ = 'Epytext'
+from aossceilo import CONFIG as c
+
+"""What visual backend matplotlib should use"""
+MATPLOTLIB_BACKEND='agg'
+"""Use pycdf instead of the opendap dap module"""
+USE_PYCDF = True
+
+"""Do not change the key(first) value of the dictionary values.
+ Will be used to get the data for each NetCDF variable.
+ If a variable is not present in the NetCDF file manually specify the "ptype"
+ with the proper script flag when running image generation.
+"""
+CDF_VARS = {'time_var':'time',
+            'backscatter_var':'backscatter',
+            'fcbh_var':'first_cbh',
+            'scbh_var':'second_cbh',
+            'tcbh_var':'third_cbh'}
+
+"""Valid min and max values are used to hide bad values that were measured by
+    an instrument.  If there is an attribute of the NetCDF variable that has
+    this information set the value to a string of the attributes name. Otherwise
+    specify the integer or float value boundaries for the data. Valid min and
+    max must be in the units that the data was measured in.
+"""
+# Masked where data equals BACK_VMIN
+BACK_VMIN=0 # 'valid_min'
+# BACK_VMAX='valid_max'
+FCBH_VMIN=-9999
+#FCBH_VMAX=7000
+SCBH_VMIN=-9999
+#SCBH_VMAX=7000
+TCBH_VMIN=-9999
+#TCBH_VMAX=7000
+
+
+### Data Conversions ###
+"""Time can not be converted it will be calculated using epoch time and
+    taking (time - time.min())/3600.0 to give a 0 - 24 time range.
+"""
+"""The MEAS variables represent what the data was measured in, the CONV
+    is the units to convert the data to.
+"""
+"""'m' only"""
+# ALT_MEAS='m' # Meters
+# ALT_CONV='m' # Meters
+
+
+"""Make sure to label the plots correctly in the plot settings section below"""
+
+"""Thumbnail size is in inches."""
+TN_SIZE=(1,1)
+
+### Plot Settings ###
+
+### Backscatter Settings ###
+"""The title to place at the top of the backscatter figure
+
+    If you would like to place the date in the format MM/DD/YYYY put the string
+    in single quotes 'date1'
+
+    ex. BACK_TITLE="RIG Tower Meteorogram 'date1'"
+
+    These settings will only effect the quicklook because the thumbnail has no
+    labels.
+"""
+BACK_TITLE="Ceilometer Backscatter 'date1'"
+BACK_YLABEL="Altitude (km)"
+#BACK_YLABEL_SIZE=8
+BACK_XLABEL="Time (UTC)"
+#BACK_XLABEL_SIZE=8
+
+"""Cloud base height colors and edge colors.
+"""
+FCBH_MARKER = 'o'
+SCBH_MARKER = 'o'
+TCBH_MARKER = 'o'
+FCBH_COLOR =  "red"
+SCBH_COLOR =  "#003300" # Green
+TCBH_COLOR =  "purple"
+FCBH_ECOLOR = "white"
+SCBH_ECOLOR = "white"
+TCBH_ECOLOR = "white"
+FCBH_MSIZE = 3
+SCBH_MSIZE = 3
+TCBH_MSIZE = 3
+
+### File Settings ###
+"""GET_IMG_DIR fucntion will be used to save the images to the directory:
+    RETURNED_PATH/image.png
+    the function must take a datetime object as its only parameter
+"""
+def get_img_dir(dt):
+    """Temp. function for returning the current image/cache directory"""
+    return c.get_cache_dir("img", dt)
+GET_IMG_DIR = get_img_dir
+
+def get_img_filename(begin, end, ptype=1, tag="", site="rig", description=""):
+    """Intermediate function for returning a standard image filename"""
+    return c.get_img_filename(begin, end, ptype, tag=tag, site=site, description=description)
+GET_IMG_FILENAME = get_img_filename
+
+GET_NC_URL = c.get_nc_url
diff --git a/aossceilo/quicklook/__init__.py b/aossceilo/quicklook/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..261cddc1a14cadd1d208f39bc1f6aa7c6e4f3057
--- /dev/null
+++ b/aossceilo/quicklook/__init__.py
@@ -0,0 +1,469 @@
+"""Create plots of the ceilometer instrument data
+
+
+Functions
+=========
+
+main()
+
+make_plot()
+
+@author: 'David Hoese'
+@organization: 'SSEC'
+@contact: 'david.hoese@ssec.wisc.edu'
+@version: '1.5'
+"""
+
+import sys, os, logging
+from metobscommon.util import mytime
+import aossceilo.quicklook.CONFIG as c
+import numpy
+
+if c.USE_PYCDF:
+    UCDF=True
+    from pycdf import CDF, CDFError
+else:
+    UCDF=False
+    from dap import client as dap
+
+# Must set backend for matplotlib before importing pylab
+import matplotlib as mpl
+mpl.use(c.MATPLOTLIB_BACKEND)
+import matplotlib.pylab as plt
+
+_log = logging.getLogger(__name__)
+
+def make_plot(ncfile=None, numplots=2, quality=1, site="rig", description="", format="%m/%d/%Y",
+    datestring=None, dt=None, blank=True):
+    """Creates a plot using ceilometer data from the NetCDF file or date
+    specified
+
+    If no arguments are provided plots of the current day will be created.
+    Specifying a datetime object for C{dt} AND a filename for C{filename} will
+    results in a C{ValueError}.
+    A blank plot will only be created if a datestring or datetime object is
+    specified otherwise there is no way of finding the date of the attempted
+    plot.
+
+    Logging
+    =======
+
+        Log messages will be logged to I{stdout}.
+
+        Log messages occur for the following:
+            - Specifying a date or filename for which the file does not exist
+            - Specifying a date or filename for a non-NetCDF file
+            - Specifying a date or filename for a NetCDF file that does not have
+            the correct variables
+            - Specifying a date or filename for a NetCDF file that has corrupt
+            data
+            - Any problem while plotting the data
+
+    Plotting
+    ========
+
+        This function will create 2 large quicklook images and 2 small
+        (1 in. x 1 in. default) thumbnail images by default with a black
+        background.
+        The number of plots can be changed by specifying a value for
+        C{numplots}.  For example, if C{numplots=2}, 2 plots would be created
+        per day per type.  So 2 larges quicklook images, 00:00:00 to 11:59:59
+        and 12:00:00 to 23:59:59 and the same time frames for the 2 thumbnail
+        images.
+
+        First the large quicklook images are created, in order from the
+        beginning of the day to the end, number of plots depends on the value of
+        C{numplots} as described above.  After all large quicklook images are
+        created the thumbnail images are created in the same manner as the large
+        quicklook images.
+
+        Image Labels:
+
+            1. The Y-axis:
+                - Units: Kilometers
+                - Range: 0 km to 7 km
+            2. The X-axis:
+                - Units: Hours (UTC Timezone)
+                - Range: I{Varies according to C{numplots}}
+            3. The Colorbar:
+                - Units: I{Arbitrary}
+                - Range: 0 to 10
+            4. Title:
+                - Value: "Ceilometer Backscatter MM/DD/YYYY"
+                - I{Note: Thumbnails have no title by default and are different
+                than the title of the large quicklook images."}
+                    - Set thumbnails to have a title by setting C{title} to
+                    C{True}.
+
+        I{Note: Currently this code can only generate one day of data at a time}
+
+    @param filename: The complete filename of the NetCDF file containing the
+    ceilometer backscatter data.
+
+        NetCDF Variables required:
+            - time
+            - backscatter
+    @param numplots: The number of plot segments to split one day into. The
+    default 2 produces one 0-12 plot and one 12-24 plot with 2 thumbnails of
+    the same time separation.
+    @param title: Specify whether or not to have a title at the top of the
+    thumbnail images. The defualt False produces no title.
+    @param quality: Backscatter will be used every C{quality} piece of data.
+    The default C{1} uses every piece of data.
+    @param format: To be used with datestring to specify a format that
+    datestring follows. If no datestring is provided the current date will be
+    used.
+    @param datestring: Used with format to specify the date parsed from a
+    string following the format string of format.
+    @param dt: Datetime object to be used
+    @param backend: Specify the backend for matplotlib to use in plotting.
+    The default 'agg' is used for systems without displays.
+    @param blank: Create a blank when corrupt or no data is found for a
+    C{filename}; C{datestring} or datetime object required.
+    @type filename: str
+    @type numplots: integer
+    @type title: boolean
+    @type quality: integer
+    @type format: str
+    @type datestring: str
+    @type dt: datetime
+    @type backend: str
+    @type blank: boolean
+    """
+    now = mytime.utc_now()
+
+    # Handle all of the parameters, Read Documentation for more information
+    if ncfile == None and dt == None:
+        if datestring == None:
+            datestring=now.strftime("%m/%d/%Y")
+        now = mytime.datetime.strptime(datestring, format)
+        ncfile = c.GET_NC_URL(now, site=site, description=description)
+    elif ncfile != None and dt != None:
+        raise ValueError("Specify either a ncfile or a datetime object, not both")
+    elif ncfile != None:
+        if datestring == None and blank:
+            raise ValueError("A datestring must be specified with blank or no blank plot can be created")
+        elif datestring != None:
+                now = mytime.datetime.strptime(datestring, format)
+    elif dt != None:
+        ncfile = c.GET_NC_URL(dt, site=site, description=description)
+        now = dt
+        datestring = now.strftime("%m/%d/%Y")
+    if numplots <= 0:
+        raise ValueError("numplots must be greater than zero")
+
+    d = []
+    try:
+        if UCDF:
+            d.append( CDF(ncfile) )
+            x =    d[0].var(c.CDF_VARS['time_var']).get()
+            z =    d[0].var(c.CDF_VARS['backscatter_var']).get()
+            fcbh = d[0].var(c.CDF_VARS['fcbh_var']).get()
+            scbh = d[0].var(c.CDF_VARS['scbh_var']).get()
+            tcbh = d[0].var(c.CDF_VARS['tcbh_var']).get()
+        else:
+            d.append( dap.open(ncfile) )
+            x =    d[0][c.CDF_VARS['time_var']][::]
+            z =    d[0][c.CDF_VARS['backscatter_var']][::]
+            fcbh = d[0][c.CDF_VARS['fcbh_var']][::]
+            scbh = d[0][c.CDF_VARS['scbh_var']][::]
+            tcbh = d[0][c.CDF_VARS['tcbh_var']][::]
+        x,y,z,fcbh,scbh,tcbh = _prepare_data(x, z, fcbh, scbh, tcbh)
+    except CDFError as e:
+        _log.exception("File Not Found or Not a properly formatted NetCDF file on %s" % ncfile)
+        if blank:
+            x = y = fcbh = scbh = tcbh = numpy.zeros((100))
+            z = numpy.zeros((100,100))
+            _log.warning("Blank plot will be created for %s" % ncfile)
+        else: raise e
+    finally:
+        if UCDF:
+            if d: [ cdf.close() for cdf in d ]
+            else: pass
+
+    created_files = []
+    try:
+        created_files = created_files + _plot_quicklook(plt, x, y, z, fcbh, scbh, tcbh, now, numplots, quality, c.GET_IMG_DIR(now), c.GET_IMG_FILENAME, site=site, description=description)
+        created_files = created_files + _plot_thumbnail(plt, x, y, z, now, numplots, quality, c.GET_IMG_DIR(now), c.GET_IMG_FILENAME, site=site, description=description)
+    except Exception as e:
+        _log.exception("Error in matplotlib or pylab during plotting")
+        raise e
+    finally:
+        # Just in case a figure was not closed
+        plt.close('all')
+    return created_files
+
+def _prepare_data(x, z, fcbh, scbh, tcbh):
+    """Prepare the x, y, and z data to be used in plotting
+
+    This private function should only be used by the make_plot() function.
+    This prepares data by doing the following:
+
+        1. X Data - Converted to a 0-24 scale
+        2. Y Data - A array of heights from 0 - 7 kilometers
+        3. Z Data - Masks data less than or equal to 0 and then finds the
+        absolute value of the log of the values.
+
+    @param x: The time data retrieved from the NetCDF file in make_plot()
+    @param z: The backscatter data retrieved from the NetCDF file in make_plot()
+    @type x: 1-D Numpy Array
+    @type z: 2-D Numpy Array
+    @return : x, y, z
+    """
+    y = numpy.arange(0, z[0,:].size*30, 30)/1000.0
+    z = z.transpose()
+    x = x/3600.0
+
+    zshape = numpy.shape(z)
+    z = numpy.concatenate(z)
+    indp = numpy.nonzero(numpy.greater(z,c.BACK_VMIN))[0]
+    z[indp] = abs(numpy.log(z[indp]))
+    z = numpy.reshape(z,zshape)
+    z = numpy.ma.masked_where(z <= c.BACK_VMIN, z)
+    fcbh = numpy.ma.masked_where(fcbh == c.FCBH_VMIN, fcbh)
+    fcbh = fcbh/1000.0
+    scbh = numpy.ma.masked_where(scbh == c.SCBH_VMIN, scbh)
+    scbh = scbh/1000.0
+    tcbh = numpy.ma.masked_where(tcbh == c.TCBH_VMIN, tcbh)
+    tcbh = tcbh/1000.0
+
+    return x,y,z,fcbh,scbh,tcbh
+
+def _plot_quicklook(plt, x, y, z, fcbh, scbh, tcbh, now, numplots, quality, image_dir, get_fn, site="rig", description=""):
+    """Plot the large quicklook image
+
+    This private function should only be run from the make_plot() function.
+    Creates all large quicklook images so there are C{numplots} images.
+    See make_plot() for details.
+
+    @param x: The time data gathered from the NetCDF file in make_plot()
+    @param y: The altitude data created in the _prepare_Data()
+    @param z: The backscatter data gathered from the NetCDF file in make_plot()
+    @param now: The datetime object passed from make_plot()
+    @param numplots: The number of plots per day passed from make_plot()
+    @param quality: The number to take every other data point from make_plot()
+    @param image_dir: The directory to put the created image into.
+    @type x: 1-D Numpy Array
+    @type y: 1-D Numpy Array
+    @type z: 2-D Numpy Array
+    @type now: datetime object
+    @type numplots: int
+    @type quality: int
+    @type image_dir: str
+    """
+    created_files = []
+    fig0 = plt.figure()
+    sp1 = plt.subplot(1,1,1,axisbg='black')
+    end = mytime.datetime(now.year, now.month, now.day, 24/numplots, 0, 0)
+    begin = end - mytime.timedelta(hours=24/numplots)
+    base_day = begin.day
+    if end.hour == 0:
+        end_hour = 24
+    else:
+        end_hour = end.hour
+
+    plt.pcolor(x[0:-1:quality], y, z[:,0:-1:quality], shading = 'flat', vmin=0, vmax=10)
+    plt.colorbar()
+    plt.plot(x[0:-1:quality], fcbh[0:-1:quality], marker=c.FCBH_MARKER,
+            color=c.FCBH_COLOR, markeredgecolor=c.FCBH_ECOLOR, linewidth=0,
+            markersize=c.FCBH_MSIZE)
+    plt.plot(x[0:-1:quality], scbh[0:-1:quality], marker=c.SCBH_MARKER,
+            color=c.SCBH_COLOR, markeredgecolor=c.SCBH_ECOLOR, linewidth=0,
+            markersize=c.SCBH_MSIZE)
+    plt.plot(x[0:-1:quality], tcbh[0:-1:quality], marker=c.TCBH_MARKER,
+            color=c.TCBH_COLOR, markeredgecolor=c.TCBH_ECOLOR, linewidth=0,
+            markersize=c.TCBH_MSIZE)
+    plt.axis([begin.hour,end_hour,0,7])
+    title = c.BACK_TITLE.replace("'date1'", "%02d/%02d/%04d" % (now.month,now.day,now.year))
+    plt.title(title)
+    plt.ylabel(c.BACK_YLABEL)
+    plt.xlabel(c.BACK_XLABEL)
+
+    if not os.path.exists(image_dir): os.makedirs(image_dir)
+
+    filename = get_fn(begin, end - mytime.timedelta(seconds=1), ptype=1, tag="", site=site, description=description)
+    file = os.path.join(image_dir, filename)
+    plt.savefig(file)
+    _log.debug("Ceilometer Backscatter Quicklook saved as %s" % file)
+    created_files.append(file)
+
+    end = end + mytime.timedelta(hours=24/numplots)
+    begin = end - mytime.timedelta(hours=24/numplots)
+    while begin.day == base_day:
+        if end.hour == 0:
+            end_hour = 24
+        else:
+            end_hour = end.hour
+        plt.axis([begin.hour,end_hour,0,7])
+        filename = get_fn(begin, end - mytime.timedelta(seconds=1), ptype=1, tag="", site=site, description=description)
+        file = os.path.join(image_dir, filename)
+        plt.savefig(file)
+        _log.debug("Ceilometer Backscatter Quicklook saved as %s" % file)
+        created_files.append(file)
+
+        end =   end + mytime.timedelta(hours=24/numplots)
+        begin = end - mytime.timedelta(hours=24/numplots)
+    plt.close()
+    return created_files
+
+def _plot_thumbnail(plt, x, y, z, now, numplots, quality, image_dir, get_fn, site="rig", description=""):
+    """Plot the smaller thumbnail quicklook images
+
+    This private function should only be run from the make_plot() function.
+    Creates all thumbnail images so there are C{numplots} images.
+    See make_plot() for details.
+
+    @param x: The time data gathered from the NetCDF file in make_plot()
+    @param y: The altitude data created in the _prepare_Data()
+    @param z: The backscatter data gathered from the NetCDF file in make_plot()
+    @param now: The datetime object passed from make_plot()
+    @param numplots: The number of plots per day passed by make_plot()
+    @param quality: The number to take every other data point from make_plot()
+    @param image_dir: The directory to put the created image into
+    @param tn_size: The figure size of the thumbnail image
+    @param title: Specify to have a title or not
+    @type x: 1-D Numpy Array
+    @type y: 1-D Numpy Array
+    @type z: 2-D Numpy Array
+    @type now: datetime object
+    @type numplots: int
+    @type quality: int
+    @type image_dir: str
+    @type tn_size: 2-element integer tuple
+    @type title: boolean
+    """
+    created_files = []
+    end = mytime.datetime(now.year, now.month, now.day, 24/numplots, 0, 0)
+    begin = end - mytime.timedelta(hours=24/numplots)
+    base_day = begin.day
+    
+    fig1 = plt.figure(2, figsize=c.TN_SIZE)
+    spt = plt.subplot(1,1,1,axisbg='black')
+    plt.subplots_adjust(left=0,right=1,bottom=0,top=1)
+    if end.hour == 0:
+        end_hour = 24
+    else:
+        end_hour = end.hour
+
+    plt.pcolor(x[0:-1:quality], y, z[:,0:-1:quality], shading='flat', vmin=0, vmax=10)
+    plt.axis([begin.hour,end_hour,0,7])
+    tl = spt.yaxis.get_ticklines()
+    for t in tl:
+        t.set_visible(False)
+
+    if not os.path.exists(image_dir): os.makedirs(image_dir)
+
+    filename = get_fn(begin, end - mytime.timedelta(seconds=1), ptype=1, tag="tn", site=site, description=description)
+    file = os.path.join(image_dir, filename)
+    plt.savefig(file)
+    _log.debug("Ceilometer Backscatter Thumbnail saved as %s" % file)
+    created_files.append(file)
+
+    end = end + mytime.timedelta(hours=24/numplots)
+    begin = end - mytime.timedelta(hours=24/numplots)
+    while begin.day == base_day:
+        if end.hour == 0:
+            end_hour = 24
+        else:
+            end_hour = end.hour
+        plt.axis([begin.hour,end_hour,0,7])
+        filename = get_fn(begin, end - mytime.timedelta(seconds=1), ptype=1, tag="tn", site=site, description=description)
+        file = os.path.join(image_dir, filename)
+        plt.savefig(file)
+        _log.debug("Ceilometer Backscatter Thumbnail saved as %s" % file)
+        created_files.append(file)
+        
+        end = end + mytime.timedelta(hours=24/numplots)
+        begin = end - mytime.timedelta(hours=24/numplots)
+    plt.close()
+    return created_files
+
+def main():
+    """Handles command line arguments if run as a script
+
+    Run first from command-line.
+
+    No Arguments or Run as Module
+    =============================
+    Creates, by default and by running the make_plot() function, full size quicklook plot of Ceilometer data
+    and a thumbnail quicklook image without a title of the same plot.
+
+
+    Run Script
+    ==========
+    Usage::
+        quicklook_ceilo.py [-t | -f filename | --numplots=2 | --quality=1 | --date="MM/DD/YYYY" | --blank]
+        Try `python quicklook_ceilo.py -h' for more information.
+        Example: "python quicklook_ceilo.py -tf $HOME/my_ceilo_data.nc"
+
+    """
+
+    import optparse
+    from metobscommon.util import file
+    now = mytime.utc_now()
+
+    # Parse Arguments
+    usage="""ceilometer.py [-t | -f filename | --numplots=2 | --quality=1 | --date="MM/DD/YYYY" | --blank]
+    Try `python quicklook_ceilo.py -h' for more information.
+    Creates a full size quicklook plot of Ceilometer data and a thumbnail image of the same plot
+    Example: "python quicklook_ceilo.py -tf "$HOME/my_ceilo_data.nc"
+
+    Created by:
+    David Hoese
+    SSEC
+    Madison, WI 53706
+    """
+
+    parser = optparse.OptionParser(usage)
+    parser.add_option("-f", "--file", dest="filename",
+                    default=None,
+                    help="specify NetCDF file to plot")
+    parser.add_option("--numplots", dest="numplots",
+                    default="2",
+                    help="specify the number of the plots per day")
+    parser.add_option("--quality", dest="quality",
+                    default="1",
+                    help="specify the number of data points to skip by")
+    parser.add_option("--date", dest="datetoplot",
+                    default=None,
+                    help="specify the date of the data to be plotted")
+    parser.add_option("--blank", action="store_false", dest="blank", default=True,
+                    help="create a blank plot if data file is not found")
+    parser.add_option("-l", dest="lockfile", default="ceilo_quicklook.lock",
+                    help="specify the lockfile to use")
+    parser.add_option("--site", dest="site", default="rig",
+                    help="the site name used in filenames")
+    parser.add_option("--description", dest="description", default="",
+                    help="the description used in filenames")
+    (options, args) = parser.parse_args()
+
+    filename = options.filename
+    numplots = int(options.numplots)
+    quality = int(options.quality)
+    datetoplot = options.datetoplot
+    site = options.site
+    description = options.description
+
+    # If a filename is specified then a datetoplot string must also be specified
+    if filename != None and datetoplot == None:
+        _log.error("Filename must also be specified with a datetoplot")
+        sys.exit()
+
+    # Set up datetoplot string if there was nothing specified to plot (ie. Current Day)
+    if datetoplot == None and filename == None:
+        datetoplot = now.strftime("%m/%d/%Y")
+
+    try:
+        lock = file.FileLock(options.lockfile)
+        # Run the make_plot function to create the images with options and arguments specified at command-line
+        make_plot(filename, numplots, quality, site=site, description=description,
+                format="%m/%d/%Y",  datestring=datetoplot, blank=options.blank)
+    except file.FileLockedError as e:
+        _log.warn("function already locked with %s", e.filename)
+    finally:
+        _log.warn("terminating", exc_info=1)
+        logging.shutdown()
+
+if __name__ == '__main__':
+        logging.basicConfig(level=logging.INFO)
+        sys.exit(main())
diff --git a/aossceilo/tidy.py b/aossceilo/tidy.py
index 71d75af047ae51d794494db5c88eca5f0dffdad0..2aecdaafccf27139e1a616b05c93b717e88b8ebc 100644
--- a/aossceilo/tidy.py
+++ b/aossceilo/tidy.py
@@ -21,11 +21,10 @@ Created by davidh on 2009-10-08.
 Copyright (c) 2009 University of Wisconsin SSEC. All rights reserved.
 """
 
-import os, sys, logging, shutil
-from subprocess import check_call as sh
+import os, sys, logging
 from datetime import datetime, timedelta
-from metobs.ceilo import quicklook, nc
-from metobs.ceilo import CONFIG as c
+from aossceilo import quicklook, nc
+from aossceilo import CONFIG as c
 from metobscommon.util import raw_manager
 rel = raw_manager.relativedir
 import re
@@ -244,9 +243,9 @@ run "%prog help" to list commands
             # print first line of docstring
             for cmd in commands:
                 ds = commands[cmd].__doc__.split('\n')[0]
-                print "%-16s %s" % (cmd,ds)
+                print("%-16s %s" % (cmd,ds))
         else:
-            print commands[command].__doc__
+            print(commands[command].__doc__)
 
     def test():
         "run tests"
diff --git a/environment.yaml b/environment.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..9751c5f2932d9999e6cefee11138a78b96d663e9
--- /dev/null
+++ b/environment.yaml
@@ -0,0 +1,8 @@
+name: aoss-cameras
+channels:
+  - conda-forge
+dependencies:
+  - numpy
+  - pytz
+  - sh
+  - pyserial
diff --git a/scripts/archive_ceilo.sh b/scripts/archive_ceilo.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c2e7bd2456410338d12cbb12e2998bbc9711c40d
--- /dev/null
+++ b/scripts/archive_ceilo.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# Moves ceilometer data in raw (inst-data) and cache
+
+ENV=/opt/metobs/aoss_ceilo
+LOGDIR=$ENV/log
+LOCK=$ENV/lock/$(basename $0).lock
+
+source $ENV/scripts/metobs_config.sh
+
+(
+    flock -x -n 200 || exit $?
+    logfile=${LOGDIR}/ceilo-tidy-archive.log
+
+    # Run today
+    $ENV/bin/python -m aossceilo.tidy --debug incoming >> $logfile 2>&1
+
+) 200>$LOCK
+
diff --git a/scripts/do_ceilo_image_gen.sh b/scripts/do_ceilo_image_gen.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e734817c4dd0c3efd4fec307690fae7c3a83d90c
--- /dev/null
+++ b/scripts/do_ceilo_image_gen.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# Create ceilo images by running metobs.ceilo.tidy quicklook
+
+echo "NOT UPDATED FOR PYTHON 3"
+exit 1
+
+ENV=/data1/software/aoss-ceilo
+LOGDIR=$ENV/logs
+LOCK=$ENV/locks/$(basename $0).lock
+
+source $ENV/scripts/metobs_config.sh
+
+(
+    flock -x -n 200 || exit $?
+    logfile=${LOGDIR}/ceilo-tidy-quicklook.log
+
+    # Run Yesterday's Image Generation if the current time is 12:10 AM.
+    if [ `date +%H:%M` == "00:10" ]; then
+        $ENV/bin/python -m metobs.ceilo.tidy --debug --date=`date +%m/%d/%Y --date="yesterday"` quicklook >> $logfile 2>&1
+    fi
+
+    # Run metobs.ceilo.tidy quicklook
+    $ENV/bin/python -m metobs.ceilo.tidy --debug quicklook >> $logfile 2>&1
+
+) 200>$LOCK
+
diff --git a/scripts/do_ceilo_nc_gen.sh b/scripts/do_ceilo_nc_gen.sh
new file mode 100755
index 0000000000000000000000000000000000000000..88350560aab3bb36dd7cfd0e2d3995863c8364f5
--- /dev/null
+++ b/scripts/do_ceilo_nc_gen.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+# Generates NetCDF files of RIG Ceilometer data and puts them in the cache directory.
+
+echo "NOT UPDATED FOR PYTHON 3"
+exit 1
+
+ENV=/data1/software/aoss-ceilo
+LOGDIR=$ENV/logs
+LOCK=$ENV/locks/$(basename $0).lock
+
+source $ENV/scripts/metobs_config.sh
+
+(
+    flock -x -n 200 || exit $?
+    logfile=${LOGDIR}/ceilo-tidy-nc.log
+
+    # Runs metobs.ceilo.tidy nc
+    $ENV/bin/python -m metobs.ceilo.tidy --debug nc >> $logfile 2>&1
+
+) 200>$LOCK
+
diff --git a/scripts/metobs_config.sh b/scripts/metobs_config.sh
new file mode 100644
index 0000000000000000000000000000000000000000..0e4c6069cc3bd98b5546dc87137c411ccf2dfb92
--- /dev/null
+++ b/scripts/metobs_config.sh
@@ -0,0 +1,17 @@
+
+#
+# These values should override values from the python module metobs.ceilo.CONFIG
+#
+# where the ceilo ascii files get pushed to
+export CEILO_INCOMING_DIR=/data1/incoming/aoss/ceilo
+# The archived location
+export CEILO_PRAW_DIR=/data1/raw/aoss/ceilo
+# Where generated products go
+export CEILO_CACHE_DIR=/data1/cache/aoss/ceilo
+# Where the links for the latest images go
+export CEILO_LATEST_DIR=/data1/cache/aoss/ceilo
+# Where ASCII files are located
+export CEILO_ASCII_LOC=$CEILO_CACHE_DIR
+# Where NetCDF files are located
+export CEILO_NC_LOC=$CEILO_CACHE_DIR
+