diff --git a/geomagio/Controller.py b/geomagio/Controller.py
index df3b841951608a4da5310954bbf88b82b57b0851..370170a9b4eb8a9809d3577c8e900f26958d0dc2 100644
--- a/geomagio/Controller.py
+++ b/geomagio/Controller.py
@@ -17,6 +17,7 @@ import imfv283
 # factories for new filetypes
 import temperature
 import vbf
+import binlog
 
 
 class Controller(object):
@@ -406,13 +407,16 @@ def get_output_factory(args):
         elif output_url is not None:
             output_factory = temperature.TEMPFactory(
                     **output_factory_args)
-    elif output_type == 'vbf' or output_type == 'binlog':
+    elif output_type == 'vbf':
         if output_stream is not None:
             output_factory = vbf.StreamVBFFactory(
-                    output=output_type,
+                    **output_factory_args)
+    elif output_type == 'binlog':
+        if output_stream is not None:
+            output_factory = binlog.StreamBinLogFactory(
                     **output_factory_args)
         elif output_url is not None:
-            output_factory = vbf.VBFFactory(
+            output_factory = binlog.BinLogFactory(
                     output=output_type,
                     **output_factory_args)
     return output_factory
diff --git a/geomagio/binlog/BinLogFactory.py b/geomagio/binlog/BinLogFactory.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee46eba857e2039a185367c3ed5f8a4fed8fe970
--- /dev/null
+++ b/geomagio/binlog/BinLogFactory.py
@@ -0,0 +1,36 @@
+"""Factory that creates BinLog Files."""
+
+from ..TimeseriesFactory import TimeseriesFactory
+from BinLogWriter import BinLogWriter
+
+
+class BinLogFactory(TimeseriesFactory):
+    """TimeseriesFactory for BinLog formatted files.
+
+    Parameters
+    ----------
+    output : bin-change report.
+
+    All other named parameters passed to TimeseriesFactory.
+
+    See Also
+    --------
+    TimeseriesFactory
+    """
+
+    def __init__(self, **kwargs):
+        TimeseriesFactory.__init__(self, **kwargs)
+
+    def write_file(self, fh, timeseries, channels):
+        """Write timeseries data to the given file object.
+
+        Parameters
+        ----------
+        fh : writable
+            file handle where data is written.
+        timeseries : obspy.core.Stream
+            stream containing traces to store.
+        channels : list
+            list of channels to store.
+        """
+        BinLogWriter().write(fh, timeseries, channels)
diff --git a/geomagio/binlog/BinLogWriter.py b/geomagio/binlog/BinLogWriter.py
new file mode 100644
index 0000000000000000000000000000000000000000..ced87b800ce74ff3623f3b6abcca1b79be84e61d
--- /dev/null
+++ b/geomagio/binlog/BinLogWriter.py
@@ -0,0 +1,215 @@
+
+import numpy
+from cStringIO import StringIO
+from datetime import datetime
+from .. import ChannelConverter, TimeseriesUtility
+from ..TimeseriesFactoryException import TimeseriesFactoryException
+from obspy.core import Stream
+
+
+# For binlog, need to track previous volt/bin values.
+h_prev = [99.999999, 999]
+e_prev = [99.999999, 999]
+z_prev = [99.999999, 999]
+# Use seperate HEZ buffers to group binlog output by component.
+Hbuf = []
+Ebuf = []
+Zbuf = []
+
+
+class BinLogWriter(object):
+    """BinLog writer.
+    """
+
+    def __init__(self):
+        return
+
+    def write(self, out, timeseries, channels):
+        """Write parsed timeseries info to binlog file.
+
+        Parameters
+        ----------
+            out : file object
+                File object to be written to. Could be stdout.
+            timeseries : obspy.core.stream
+                Timeseries object with data to be written.
+            channels : array_like
+                Channels to be written from timeseries object.
+        """
+        for channel in channels:
+            if timeseries.select(channel=channel).count() == 0:
+                raise TimeseriesFactoryException(
+                    'Missing channel "%s" for output, available channels %s' %
+                    (channel, str(TimeseriesUtility.get_channels(timeseries))))
+        stats = timeseries[0].stats
+
+        out.write(self._format_header(stats))
+
+        self._format_data(timeseries, channels)
+
+        if (len(Hbuf) + len(Ebuf) + len(Zbuf)) > 0:
+            out.write(' C  Date       Time     DaySec     Bin change'
+            '    Voltage change\n')
+            out.write(''.join(Hbuf))
+            out.write('\n')
+            out.write(''.join(Ebuf))
+            out.write('\n')
+            out.write(''.join(Zbuf))
+        else:
+            out.write('*** No Bin Changes Found ***\n')
+
+    def _format_header(self, stats):
+        """format headers for BinLog file
+
+        Parameters
+        ----------
+            stats : List
+                An object with the header values to be written.
+
+        Returns
+        -------
+        str
+            A string formatted to be a single header line in a BinLog file.
+        """
+        buf = []
+
+        observatory = stats.station
+        sttdate = stats.starttime.strftime("%d-%b-%y")
+        enddate = stats.endtime.strftime("%d-%b-%y")
+
+        buf.append('Bin Change Report: ' + observatory + '  Start Day: ' +
+                    sttdate + ' End Day: ' + enddate + '\n\n')
+
+        return ''.join(buf)
+
+    def _format_data(self, timeseries, channels):
+        """Format all data lines.
+
+        Parameters
+        ----------
+            timeseries : obspy.core.Stream
+                Stream containing traces with channel listed in channels
+            channels : sequence
+                List and order of channel values to output.
+
+        Returns
+        -------
+        str
+            A string formatted to be the data lines in a BinLog file.
+        """
+
+        # create new stream
+        timeseriesLocal = Stream()
+        # Use a copy of the trace so that we don't modify the original.
+        for trace in timeseries:
+            traceLocal = trace.copy()
+            if traceLocal.stats.channel == 'D':
+                traceLocal.data = \
+                    ChannelConverter.get_minutes_from_radians(traceLocal.data)
+
+            # TODO - we should look into multiplying the trace all at once
+            # like this, but this gives an error on Windows at the moment.
+            # traceLocal.data = \
+            #     numpy.round(numpy.multiply(traceLocal.data, 100)).astype(int)
+
+            timeseriesLocal.append(traceLocal)
+
+        traces = [timeseriesLocal.select(channel=c)[0] for c in channels]
+        starttime = float(traces[0].stats.starttime)
+        delta = traces[0].stats.delta
+
+        for i in xrange(len(traces[0].data)):
+            self._format_values(
+                datetime.utcfromtimestamp(starttime + i * delta),
+                (t.data[i] for t in traces))
+
+        return
+
+    def _format_values(self, time, values):
+        """Format one line of data values.
+
+        Parameters
+        ----------
+            time : datetime
+                Timestamp for values.
+            values : sequence
+                List and order of channel values to output.
+
+        Returns
+        -------
+        unicode
+            Formatted line containing values.
+        """
+
+        tt = time.timetuple()
+        totalMinutes = int(tt.tm_hour * 3600 + tt.tm_min * 60 + tt.tm_sec)
+
+        timestr = '{0.tm_year:0>4d}-{0.tm_mon:0>2d}-{0.tm_mday:0>2d} ' \
+                  '{0.tm_hour:0>2d}:{0.tm_min:0>2d}:{0.tm_sec:0>2d}' \
+                  ' ({1:0>5d})'. \
+                    format(tt, totalMinutes)
+
+        # init volt/bin vals to dead
+        vdead = 99.999999
+        bdead = 999
+        vblist = [vdead, bdead, vdead, bdead, vdead, bdead]
+
+        # now "un-dead" the non-nans, format volts as float, bins as int
+        for idx, valx in enumerate(values):
+            if ~numpy.isnan(valx):
+                if idx == 0 or idx == 2 or idx == 4:
+                    vblist[idx] = valx / 1000.
+                else:
+                    vblist[idx] = int(valx)
+
+        if vblist[1] != 999 and h_prev[1] != 999 and vblist[1] != h_prev[1]:
+            Hbuf.append('{0: >3s} {1:>s}  '
+            '{2: >4d} to {3: >4d}  {4: >10.6f} to {5: >10.6f}\n'.
+            format('(H)', timestr, h_prev[1],
+                    vblist[1], h_prev[0], vblist[0]))
+
+        if vblist[3] != 999 and e_prev[1] != 999 and vblist[3] != e_prev[1]:
+            Ebuf.append('{0: >3s} {1:>s}  '
+            '{2: >4d} to {3: >4d}  {4: >10.6f} to {5: >10.6f}\n'.
+            format('(E)', timestr, e_prev[1],
+                    vblist[3], e_prev[0], vblist[2]))
+
+        if vblist[5] != 999 and z_prev[1] != 999 and vblist[5] != z_prev[1]:
+            Zbuf.append('{0: >3s} {1:>s}  '
+            '{2: >4d} to {3: >4d}  {4: >10.6f} to {5: >10.6f}\n'.
+            format('(Z)', timestr, z_prev[1],
+                    vblist[5], z_prev[0], vblist[4]))
+
+        h_prev[0] = vblist[0]
+        h_prev[1] = vblist[1]
+
+        e_prev[0] = vblist[2]
+        e_prev[1] = vblist[3]
+
+        z_prev[0] = vblist[4]
+        z_prev[1] = vblist[5]
+
+        return
+
+    @classmethod
+    def format(self, timeseries, channels):
+        """Get a BinLog formatted string.
+
+        Calls write() with a StringIO, and returns the output.
+
+        Parameters
+        ----------
+            timeseries : obspy.core.Stream
+                Stream containing traces with channel listed in channels
+            channels : sequence
+                List and order of channel values to output.
+
+        Returns
+        -------
+        unicode
+          BinLog formatted string.
+        """
+        out = StringIO()
+        writer = BinLogWriter()
+        writer.write(out, timeseries, channels)
+        return out.getvalue()
diff --git a/geomagio/binlog/StreamBinLogFactory.py b/geomagio/binlog/StreamBinLogFactory.py
new file mode 100644
index 0000000000000000000000000000000000000000..6fda2de572692126a23cdadb8ad9f89668b821e9
--- /dev/null
+++ b/geomagio/binlog/StreamBinLogFactory.py
@@ -0,0 +1,34 @@
+"""Factory to load BinLog files from an input StreamBinLogFactory."""
+
+from BinLogFactory import BinLogFactory
+
+
+class StreamBinLogFactory(BinLogFactory):
+    """Timeseries Factory for BinLog files loaded via a stream.
+        normally either a single file, or stdio.
+
+    Parameters
+    ----------
+    stream: file object
+        io stream, normally either a file, or stdio
+
+    See Also
+    --------
+    BinLogFactory
+    Timeseriesfactory
+    """
+
+    def __init__(self, stream, **kwargs):
+        BinLogFactory.__init__(self, **kwargs)
+        self._stream = stream
+
+    def put_timeseries(self, timeseries, starttime=None, endtime=None,
+            channels=None, type=None, interval=None):
+        """Implements put_timeseries
+
+        Notes: Calls BinLogFactory.write_file in place of
+            BinLogFactory.put_timeseries. This can result in a
+            non-standard BinLog file, specifically one of longer than
+            expected length.
+        """
+        BinLogFactory.write_file(self, self._stream, timeseries, channels)
diff --git a/geomagio/binlog/__init__.py b/geomagio/binlog/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..533042e79b16c99b066e94973962c869be95bbda
--- /dev/null
+++ b/geomagio/binlog/__init__.py
@@ -0,0 +1,13 @@
+"""IO Module for BinLog Format
+"""
+
+from BinLogFactory import BinLogFactory
+from StreamBinLogFactory import StreamBinLogFactory
+from BinLogWriter import BinLogWriter
+
+
+__all__ = [
+    'BinLogFactory',
+    'StreamBinLogFactory',
+    'BinLogWriter'
+]
diff --git a/geomagio/vbf/StreamVBFFactory.py b/geomagio/vbf/StreamVBFFactory.py
index eba950fa2eb176615c3c5b495f050e631c51e6c9..100150b6e98383d77d003e07d33cd1b4c7fb9aba 100644
--- a/geomagio/vbf/StreamVBFFactory.py
+++ b/geomagio/vbf/StreamVBFFactory.py
@@ -18,7 +18,6 @@ class StreamVBFFactory(VBFFactory):
     Timeseriesfactory
     """
 
-    # Flag "output" used for vbf file versus bin-change log.
     def __init__(self, stream, **kwargs):
         VBFFactory.__init__(self, **kwargs)
         self._stream = stream
diff --git a/geomagio/vbf/VBFFactory.py b/geomagio/vbf/VBFFactory.py
index e2b91ce0add44ebe942904351550b423514e4dfa..4c4d821eaf0426d12394e0c45f33eee818d2b237 100644
--- a/geomagio/vbf/VBFFactory.py
+++ b/geomagio/vbf/VBFFactory.py
@@ -13,8 +13,7 @@ class VBFFactory(TimeseriesFactory):
 
     Parameters
     ----------
-    output : {'binlog', 'vbf'}
-        bin-change or vbf style output.
+    output : vbf style output.
 
     All other named parameters passed to TimeseriesFactory.
 
@@ -23,9 +22,8 @@ class VBFFactory(TimeseriesFactory):
     TimeseriesFactory
     """
 
-    def __init__(self, output='vbf', **kwargs):
+    def __init__(self, **kwargs):
         TimeseriesFactory.__init__(self, **kwargs)
-        self.output = output
 
     def write_file(self, fh, timeseries, channels):
         """Write timeseries data to the given file object.
@@ -39,7 +37,4 @@ class VBFFactory(TimeseriesFactory):
         channels : list
             list of channels to store.
         """
-        if self.output == 'binlog':
-            VBFWriter().write_change_log(fh, timeseries, channels)
-        else:
-            VBFWriter().write(fh, timeseries, channels)
+        VBFWriter().write(fh, timeseries, channels)
diff --git a/geomagio/vbf/VBFWriter.py b/geomagio/vbf/VBFWriter.py
index 9b3cfbff3eec6d2ad2bcd6ffe59ca08a3ff213ff..45d9cc2786fbefb75efa2f8d13a92b8f28648833 100644
--- a/geomagio/vbf/VBFWriter.py
+++ b/geomagio/vbf/VBFWriter.py
@@ -7,16 +7,6 @@ from ..TimeseriesFactoryException import TimeseriesFactoryException
 from obspy.core import Stream
 
 
-# For a binlog, need to save previous time and volts.
-h_prev = [99.999999, 999]
-e_prev = [99.999999, 999]
-z_prev = [99.999999, 999]
-# Use seperate HEZ buffers to group binlog output by component.
-Hbuf = []
-Ebuf = []
-Zbuf = []
-
-
 class VBFWriter(object):
     """VBF writer.
     """
@@ -151,180 +141,6 @@ class VBFWriter(object):
         return '{0:0>5d} {1: >10.6f} {2: >4d} {3: >10.6f} {4: >4d} ' \
                 '{5: >10.6f} {6: >4d}\n'.format(totalMinutes, *vblist)
 
-    # ===============================================
-    # CODE BELOW IS FOR MAKING A BIN CHANGE LOG.
-    # VBFFactory calls the '_change_' version of the
-    # procedures rather than the "usual" procedures
-    # ===============================================
-
-    def write_change_log(self, out, timeseries, channels):
-        """Write timeseries to vbf file.
-
-        Parameters
-        ----------
-            out : file object
-                File object to be written to. Could be stdout.
-            timeseries : obspy.core.stream
-                Timeseries object with data to be written.
-            channels : array_like
-                Channels to be written from timeseries object.
-        """
-        for channel in channels:
-            if timeseries.select(channel=channel).count() == 0:
-                raise TimeseriesFactoryException(
-                    'Missing channel "%s" for output, available channels %s' %
-                    (channel, str(TimeseriesUtility.get_channels(timeseries))))
-        stats = timeseries[0].stats
-
-        out.write(self._format_change_header(stats))
-
-        self._format_change_data(timeseries, channels)
-
-        if (len(Hbuf) + len(Ebuf) + len(Zbuf)) > 0:
-            out.write(' C  Date       Time     DaySec     Bin change'
-            '    Voltage change\n')
-            out.write(''.join(Hbuf))
-            out.write('\n')
-            out.write(''.join(Ebuf))
-            out.write('\n')
-            out.write(''.join(Zbuf))
-        else:
-            out.write('*** No Bin Changes Found ***\n')
-
-    def _format_change_header(self, stats):
-        """format headers for VBF file
-
-        Parameters
-        ----------
-            stats : List
-                An object with the header values to be written.
-
-        Returns
-        -------
-        str
-            A string formatted to be a single header line in a VBF file.
-        """
-        buf = []
-
-        observatory = stats.station
-        sttdate = stats.starttime.strftime("%d-%b-%y")
-        enddate = stats.endtime.strftime("%d-%b-%y")
-
-        buf.append('Bin Change Report: ' + observatory + '  Start Day: ' +
-                    sttdate + ' End Day: ' + enddate + '\n\n')
-
-        return ''.join(buf)
-
-    def _format_change_data(self, timeseries, channels):
-        """Format all data lines.
-
-        Parameters
-        ----------
-            timeseries : obspy.core.Stream
-                Stream containing traces with channel listed in channels
-            channels : sequence
-                List and order of channel values to output.
-
-        Returns
-        -------
-        str
-            A string formatted to be the data lines in a VBF file.
-        """
-
-        # create new stream
-        timeseriesLocal = Stream()
-        # Use a copy of the trace so that we don't modify the original.
-        for trace in timeseries:
-            traceLocal = trace.copy()
-            if traceLocal.stats.channel == 'D':
-                traceLocal.data = \
-                    ChannelConverter.get_minutes_from_radians(traceLocal.data)
-
-            # TODO - we should look into multiplying the trace all at once
-            # like this, but this gives an error on Windows at the moment.
-            # traceLocal.data = \
-            #     numpy.round(numpy.multiply(traceLocal.data, 100)).astype(int)
-
-            timeseriesLocal.append(traceLocal)
-
-        traces = [timeseriesLocal.select(channel=c)[0] for c in channels]
-        starttime = float(traces[0].stats.starttime)
-        delta = traces[0].stats.delta
-
-        for i in xrange(len(traces[0].data)):
-            self._format_change_values(
-                datetime.utcfromtimestamp(starttime + i * delta),
-                (t.data[i] for t in traces))
-
-        return
-
-    def _format_change_values(self, time, values):
-        """Format one line of data values.
-
-        Parameters
-        ----------
-            time : datetime
-                Timestamp for values.
-            values : sequence
-                List and order of channel values to output.
-                If value is NaN, self.empty_value is output in its place.
-
-        Returns
-        -------
-        unicode
-            Formatted line containing values.
-        """
-
-        tt = time.timetuple()
-        totalMinutes = int(tt.tm_hour * 3600 + tt.tm_min * 60 + tt.tm_sec)
-
-        timestr = '{0.tm_year:0>4d}-{0.tm_mon:0>2d}-{0.tm_mday:0>2d} ' \
-                  '{0.tm_hour:0>2d}:{0.tm_min:0>2d}:{0.tm_sec:0>2d}' \
-                  ' ({1:0>5d})'. \
-                    format(tt, totalMinutes)
-
-        # init volt/bin vals to dead
-        vdead = 99.999999
-        bdead = 999
-        vblist = [vdead, bdead, vdead, bdead, vdead, bdead]
-
-        # now "un-dead" the non-nans, format volts as float, bins as int
-        for idx, valx in enumerate(values):
-            if ~numpy.isnan(valx):
-                if idx == 0 or idx == 2 or idx == 4:
-                    vblist[idx] = valx / 1000.
-                else:
-                    vblist[idx] = int(valx)
-
-        if vblist[1] != 999 and h_prev[1] != 999 and vblist[1] != h_prev[1]:
-            Hbuf.append('{0: >3s} {1:>s}  '
-            '{2: >4d} to {3: >4d}  {4: >10.6f} to {5: >10.6f}\n'.
-            format('(H)', timestr, h_prev[1],
-                    vblist[1], h_prev[0], vblist[0]))
-
-        if vblist[3] != 999 and e_prev[1] != 999 and vblist[3] != e_prev[1]:
-            Ebuf.append('{0: >3s} {1:>s}  '
-            '{2: >4d} to {3: >4d}  {4: >10.6f} to {5: >10.6f}\n'.
-            format('(E)', timestr, e_prev[1],
-                    vblist[3], e_prev[0], vblist[2]))
-
-        if vblist[5] != 999 and z_prev[1] != 999 and vblist[5] != z_prev[1]:
-            Zbuf.append('{0: >3s} {1:>s}  '
-            '{2: >4d} to {3: >4d}  {4: >10.6f} to {5: >10.6f}\n'.
-            format('(Z)', timestr, z_prev[1],
-                    vblist[5], z_prev[0], vblist[4]))
-
-        h_prev[0] = vblist[0]
-        h_prev[1] = vblist[1]
-
-        e_prev[0] = vblist[2]
-        e_prev[1] = vblist[3]
-
-        z_prev[0] = vblist[4]
-        z_prev[1] = vblist[5]
-
-        return
-
     @classmethod
     def format(self, timeseries, channels):
         """Get an VBF formatted string.