diff --git a/geomagio/Controller.py b/geomagio/Controller.py
index 60094a11700cabdb4f778fa1120c73b3b74b507b..f6142619a289060adf0cb79ee185c355775277c9 100644
--- a/geomagio/Controller.py
+++ b/geomagio/Controller.py
@@ -4,7 +4,7 @@
 import argparse
 import sys
 from obspy.core import UTCDateTime
-from Algorithm import Algorithm
+from algorithm import algorithms
 import TimeseriesUtility
 from TimeseriesFactoryException import TimeseriesFactoryException
 from Util import ObjectView
@@ -14,9 +14,6 @@ import iaga2002
 import pcdcp
 import imfv283
 
-from DeltaFAlgorithm import DeltaFAlgorithm
-from XYZAlgorithm import XYZAlgorithm
-
 
 class Controller(object):
     """Controller for geomag algorithms.
@@ -301,17 +298,8 @@ def main(args):
     else:
             print >> sys.stderr, "Missing required output directive"
 
-    if args.xyz is not None:
-        algorithm = XYZAlgorithm(informat=args.xyz[0],
-                outformat=args.xyz[1])
-    elif args.deltaf is not None:
-        algorithm = DeltaFAlgorithm(informat=args.deltaf)
-    else:
-        # TODO get smarter on inchannels/outchannels since input doesn't always
-        # need to use the --inchannels argument, but might (as in iaga2002),
-        # get it from the file.
-        algorithm = Algorithm(inchannels=args.inchannels,
-                outchannels=args.outchannels or args.inchannels)
+    algorithm = algorithms[args.algorithm]()
+    algorithm.configure(args)
 
     # TODO check for unused arguments.
 
@@ -481,14 +469,11 @@ def parse_args(args):
             help='Edge IP #. See --output-edge-* for other optional arguments')
 
     # Algorithms group
-    algorithm_group = parser.add_mutually_exclusive_group()
-    algorithm_group.add_argument('--xyz',
-            nargs=2,
-            choices=['geo', 'mag', 'obs', 'obsd'],
-            help='Enter the geomagnetic orientation(s) you want to read from' +
-                    ' and to respectfully.')
-    algorithm_group.add_argument('--deltaf',
-            choices=['geo', 'obs', 'obsd'],
-            help='Enter the geomagnetic orientation you want to read from')
+    parser.add_argument('--algorithm',
+            choices=[k for k in algorithms],
+            default='default')
+
+    for k in algorithms:
+        algorithms[k].add_arguments(parser)
 
     return parser.parse_args(args)
diff --git a/geomagio/__init__.py b/geomagio/__init__.py
index 4b0f6a79f4f91a58d04ef4f4e7a9fd595ba840de..9e086ee633914218db2d191bafdd1a54bc6f4b4a 100644
--- a/geomagio/__init__.py
+++ b/geomagio/__init__.py
@@ -4,19 +4,14 @@ Geomag Algorithm Module
 import ChannelConverter
 import StreamConverter
 
-from Algorithm import Algorithm
-from AlgorithmException import AlgorithmException
 from Controller import Controller
 from ObservatoryMetadata import ObservatoryMetadata
 from TimeseriesFactory import TimeseriesFactory
 from TimeseriesFactoryException import TimeseriesFactoryException
 import TimeseriesUtility
 import Util
-from XYZAlgorithm import XYZAlgorithm
 
 __all__ = [
-    'Algorithm',
-    'AlgorithmException',
     'ChannelConverter',
     'Controller',
     'DeltaFAlgorithm',
diff --git a/geomagio/Algorithm.py b/geomagio/algorithm/Algorithm.py
similarity index 79%
rename from geomagio/Algorithm.py
rename to geomagio/algorithm/Algorithm.py
index bd7699089ba45eb8da9bd2e7085c057fb341efe1..fed7196b967c626b31472c0ed62abe94b1025365 100644
--- a/geomagio/Algorithm.py
+++ b/geomagio/algorithm/Algorithm.py
@@ -1,6 +1,6 @@
 """Algorithm Interface."""
 
-import TimeseriesUtility
+from .. import TimeseriesUtility
 
 
 class Algorithm(object):
@@ -91,3 +91,25 @@ class Algorithm(object):
                     endtime < input_gap[2]):
                 return False
         return True
+
+    @classmethod
+    def add_arguments(cls, parser):
+        """Add command line arguments to argparse parser.
+
+        Parameters
+        ----------
+        parser: ArgumentParser
+            command line argument parser
+        """
+        pass
+
+    def configure(self, arguments):
+        """Configure algorithm using comand line arguments.
+
+        Parameters
+        ----------
+        arguments: Namespace
+            parsed command line arguments
+        """
+        self._inchannels = arguments.inchannels
+        self._outchannels = arguments.outchannels or arguments.inchannels
diff --git a/geomagio/AlgorithmException.py b/geomagio/algorithm/AlgorithmException.py
similarity index 100%
rename from geomagio/AlgorithmException.py
rename to geomagio/algorithm/AlgorithmException.py
diff --git a/geomagio/DeltaFAlgorithm.py b/geomagio/algorithm/DeltaFAlgorithm.py
similarity index 66%
rename from geomagio/DeltaFAlgorithm.py
rename to geomagio/algorithm/DeltaFAlgorithm.py
index d0619dee63ae90534ec4034063c383219cc90947..616c83d87db39049276edba93466343cfad1cbe8 100644
--- a/geomagio/DeltaFAlgorithm.py
+++ b/geomagio/algorithm/DeltaFAlgorithm.py
@@ -4,7 +4,7 @@
 
 from Algorithm import Algorithm
 from AlgorithmException import AlgorithmException
-import StreamConverter as StreamConverter
+from .. import StreamConverter
 
 # List of channels by geomagnetic observatory orientation.
 # geo represents a geographic north/south orientation
@@ -27,10 +27,10 @@ class DeltaFAlgorithm(Algorithm):
         will be converting from.
     """
 
-    def __init__(self, informat=None):
+    def __init__(self, informat='obs'):
         Algorithm.__init__(self, inchannels=CHANNELS[informat],
                 outchannels=['G'])
-        self.informat = informat
+        self._informat = informat
 
     def check_stream(self, timeseries):
         """checks a stream to make certain all the required channels
@@ -61,10 +61,35 @@ class DeltaFAlgorithm(Algorithm):
         """
         self.check_stream(timeseries)
         out_stream = None
-
-        if self.informat == 'geo':
+        informat = self._informat
+        if informat == 'geo':
             out_stream = StreamConverter.get_deltaf_from_geo(timeseries)
-        elif self.informat == 'obs' or self.informat == 'obsd':
+        elif informat == 'obs' or informat == 'obsd':
             out_stream = StreamConverter.get_deltaf_from_obs(timeseries)
 
         return out_stream
+
+    @classmethod
+    def add_arguments(cls, parser):
+        """Add command line arguments to argparse parser.
+
+        Parameters
+        ----------
+        parser: ArgumentParser
+            command line argument parser
+        """
+        parser.add_argument('--deltaf-from',
+                choices=['geo', 'obs', 'obsd'],
+                default='obs',
+                help='Geomagnetic orientation to read from')
+
+    def configure(self, arguments):
+        """Configure algorithm using comand line arguments.
+
+        Parameters
+        ----------
+        arguments: Namespace
+            parsed command line arguments
+        """
+        self._informat = arguments.deltaf_from
+        self._inchannels = CHANNELS[self._informat]
diff --git a/geomagio/XYZAlgorithm.py b/geomagio/algorithm/XYZAlgorithm.py
similarity index 62%
rename from geomagio/XYZAlgorithm.py
rename to geomagio/algorithm/XYZAlgorithm.py
index 12c3a1e0cc195558843a0decc8443d2a214faac6..64d1e91587b64f396125bdb98327c5cb7f2c74c2 100644
--- a/geomagio/XYZAlgorithm.py
+++ b/geomagio/algorithm/XYZAlgorithm.py
@@ -5,7 +5,7 @@
 
 from Algorithm import Algorithm
 from AlgorithmException import AlgorithmException
-import StreamConverter as StreamConverter
+from .. import StreamConverter
 
 # List of channels by geomagnetic observatory orientation.
 # geo represents a geographic north/south orientation
@@ -33,11 +33,11 @@ class XYZAlgorithm(Algorithm):
         be converting to.
     """
 
-    def __init__(self, informat=None, outformat=None):
+    def __init__(self, informat='obs', outformat='geo'):
         Algorithm.__init__(self, inchannels=CHANNELS[informat],
                 outchannels=CHANNELS[outformat])
-        self.informat = informat
-        self.outformat = outformat
+        self._informat = informat
+        self._outformat = outformat
 
     def check_stream(self, timeseries):
         """checks an stream to make certain all the required channels
@@ -69,36 +69,69 @@ class XYZAlgorithm(Algorithm):
         """
         self.check_stream(timeseries)
         out_stream = None
-        if self.outformat == 'geo':
-            if self.informat == 'geo':
+        informat = self._informat
+        outformat = self._outformat
+        if outformat == 'geo':
+            if informat == 'geo':
                 out_stream = timeseries
-            elif self.informat == 'mag':
+            elif informat == 'mag':
                 out_stream = StreamConverter.get_geo_from_mag(timeseries)
-            elif self.informat == 'obs' or self.informat == 'obsd':
+            elif informat == 'obs' or informat == 'obsd':
                 out_stream = StreamConverter.get_geo_from_obs(timeseries)
-        elif self.outformat == 'mag':
-            if self.informat == 'geo':
+        elif outformat == 'mag':
+            if informat == 'geo':
                 out_stream = StreamConverter.get_mag_from_geo(timeseries)
-            elif self.informat == 'mag':
+            elif informat == 'mag':
                 out_stream = timeseries
-            elif self.informat == 'obs' or self.informat == 'obsd':
+            elif informat == 'obs' or informat == 'obsd':
                 out_stream = StreamConverter.get_mag_from_obs(timeseries)
-        elif self.outformat == 'obs':
-            if self.informat == 'geo':
+        elif outformat == 'obs':
+            if informat == 'geo':
                 out_stream = StreamConverter.get_obs_from_geo(timeseries)
-            elif self.informat == 'mag':
+            elif informat == 'mag':
                 out_stream = StreamConverter.get_obs_from_mag(timeseries)
-            elif self.informat == 'obs' or self.informat == 'obsd':
+            elif informat == 'obs' or informat == 'obsd':
                 out_stream = StreamConverter.get_obs_from_obs(timeseries,
                         include_e=True)
-        elif self.outformat == 'obsd':
-            if self.informat == 'geo':
+        elif outformat == 'obsd':
+            if informat == 'geo':
                 out_stream = StreamConverter.get_obs_from_geo(timeseries,
                         include_d=True)
-            elif self.informat == 'mag':
+            elif informat == 'mag':
                 out_stream = StreamConverter.get_obs_from_mag(timeseries,
                         include_d=True)
-            elif self.informat == 'obs' or self.informat == 'obsd':
+            elif informat == 'obs' or informat == 'obsd':
                 out_stream = StreamConverter.get_obs_from_obs(timeseries,
                         include_d=True)
         return out_stream
+
+    @classmethod
+    def add_arguments(cls, parser):
+        """Add command line arguments to argparse parser.
+
+        Parameters
+        ----------
+        parser: ArgumentParser
+            command line argument parser
+        """
+        parser.add_argument('--xyz-from',
+                choices=['geo', 'mag', 'obs', 'obsd'],
+                default='obs',
+                help='Geomagnetic orientation to read from')
+        parser.add_argument('--xyz-to',
+                choices=['geo', 'mag', 'obs', 'obsd'],
+                default='geo',
+                help='Geomagnetic orientation to convert to')
+
+    def configure(self, arguments):
+        """Configure algorithm using comand line arguments.
+
+        Parameters
+        ----------
+        arguments: Namespace
+            parsed command line arguments
+        """
+        self._informat = arguments.xyz_from
+        self._outformat = arguments.xyz_to
+        self._inchannels = CHANNELS[self._informat]
+        self._outchannels = CHANNELS[self._outformat]
diff --git a/geomagio/algorithm/__init__.py b/geomagio/algorithm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..caaabb0c7a2ab733eb7cd9b3405a27aa00a2ecd3
--- /dev/null
+++ b/geomagio/algorithm/__init__.py
@@ -0,0 +1,28 @@
+"""
+Geomag Algorithms module
+"""
+
+# base classes
+from Algorithm import Algorithm
+from AlgorithmException import AlgorithmException
+# algorithms
+from DeltaFAlgorithm import DeltaFAlgorithm
+from XYZAlgorithm import XYZAlgorithm
+
+
+# algorithms is used by Controller to auto generate arguments
+algorithms = {
+    'identity': Algorithm,
+    'deltaf': DeltaFAlgorithm,
+    'xyz': XYZAlgorithm
+}
+
+
+__all__ = [
+    # base classes
+    'Algorithm',
+    'AlgorithmException',
+    # algorithms
+    'DeltaFAlgorithm',
+    'XYZAlgorithm'
+]
diff --git a/test/Controller_test.py b/test/Controller_test.py
index e407a9ecaa9463508a72b8cdea09fc083842e542..4b1992c98daa32abac7c47ffa35f9c806f773465 100644
--- a/test/Controller_test.py
+++ b/test/Controller_test.py
@@ -1,5 +1,6 @@
 #! /usr/bin/env python
-from geomagio import Algorithm, Controller, TimeseriesFactory
+from geomagio import Controller, TimeseriesFactory
+from geomagio.algorithm import Algorithm
 from nose.tools import assert_is_instance
 
 
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/test/Algorithm_test.py b/test/algorithm_test/Algorithm_test.py
similarity index 95%
rename from test/Algorithm_test.py
rename to test/algorithm_test/Algorithm_test.py
index cf4d2e3cbf249b8a50b4b75068abcb28c881a9ae..8ce878f1dd34cd6561cd9010d120d0d242c614c7 100644
--- a/test/Algorithm_test.py
+++ b/test/algorithm_test/Algorithm_test.py
@@ -2,7 +2,7 @@
 from obspy.core.stream import Stream
 from nose.tools import assert_equals
 from nose.tools import assert_is_instance
-from geomagio import Algorithm
+from geomagio.algorithm import Algorithm
 
 
 def test_algorithm_process():
diff --git a/test/XYZAlgorithm_test.py b/test/algorithm_test/XYZAlgorithm_test.py
similarity index 92%
rename from test/XYZAlgorithm_test.py
rename to test/algorithm_test/XYZAlgorithm_test.py
index b84f87198174e311879f46789c64a792d1dbdbbb..0ffcfb3ec543c6c1d8875c76798473bc4906a97c 100644
--- a/test/XYZAlgorithm_test.py
+++ b/test/algorithm_test/XYZAlgorithm_test.py
@@ -2,8 +2,8 @@
 from obspy.core.stream import Stream
 from nose.tools import assert_equals
 from nose.tools import assert_is
-from geomagio import XYZAlgorithm
-from StreamConverter_test import __create_trace
+from geomagio.algorithm import XYZAlgorithm
+from ..StreamConverter_test import __create_trace
 
 
 def test_xyzalgorithm_process():
diff --git a/test/algorithm_test/__init__.py b/test/algorithm_test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/test/edge_test/__init__.py b/test/edge_test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/test/iaga2002_test/__init__.py b/test/iaga2002_test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/test/imfv283_test/__init__.py b/test/imfv283_test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/test/pcdcp_test/__init__.py b/test/pcdcp_test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391