diff --git a/cmlreaders/base_reader.py b/cmlreaders/base_reader.py
index 9a6da5d..f754de5 100644
--- a/cmlreaders/base_reader.py
+++ b/cmlreaders/base_reader.py
@@ -165,7 +165,8 @@ class BaseCMLReader(object, metaclass=_MetaReader):
             except:  # noqa
                 data_type = "dtype"
 
-        reader = cls(data_type, subject, experiment, session, file_path=path)
+        reader = cls(data_type, subject=subject, experiment=experiment,
+                     session=session, file_path=path)
         return reader.load()
 
     def _load_no_cache(self):
diff --git a/cmlreaders/readers/readers.py b/cmlreaders/readers/readers.py
index ac415ba..4f03cf8 100644
--- a/cmlreaders/readers/readers.py
+++ b/cmlreaders/readers/readers.py
@@ -1,5 +1,8 @@
 import json
+import warnings
+
 import pandas as pd
+from pandas.errors import ParserWarning
 from pandas.io.json import json_normalize
 import scipy.io as sio
 
@@ -9,7 +12,6 @@ from cmlreaders.exc import (
 )
 
 
-# TODO: separate into a base class so that we can use this for ltp
 class TextReader(BaseCMLReader):
     """ Generic reader class for reading RAM text files """
     data_types = ['voxel_coordinates', 'jacksheet', 'classifier_excluded_leads',
@@ -26,20 +28,24 @@ class TextReader(BaseCMLReader):
         'area': ['lead_label', 'surface_area'],
     }
 
-    def __init__(self, data_type, subject, localization, file_path=None,
-                 rootdir="/", **kwargs):
-        super(TextReader, self).__init__(data_type, subject=subject,
-                                         localization=localization,
-                                         file_path=file_path,
-                                         rootdir=rootdir)
+    def __init__(self, data_type: str, subject: str,
+                 **kwargs):
+        super(TextReader, self).__init__(data_type, subject=subject, **kwargs)
         self._headers = self.headers[data_type]
 
     def as_dataframe(self):
         if self.data_type == "jacksheet":
-            sep = " "
+            sep = None  # autodetect if separated by space or tab
         else:
             sep = ","  # read_csv's default value
-        df = pd.read_csv(self.file_path, sep=sep, names=self._headers)
+
+        # When sep is None, we get a warning that the Python parser is slower,
+        # but for jacksheet files, it automatically DTRT and the file is small
+        # enough for speed to not be an issue.
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=ParserWarning)
+            df = pd.read_csv(self.file_path, sep=sep, names=self._headers)
+
         return df
 
 

Jacksheets load incorrectly with tabs
Example:

```python
In [6]: reader = CMLReader("R1406M")

In [7]: reader.load("jacksheet").head()
Out[7]:
    number  label
0  1\tLFG1    NaN
1  2\tLFG2    NaN
2  3\tLFG3    NaN
3  4\tLFG4    NaN
4  5\tLFG5    NaN
```
diff --git a/cmlreaders/test/data/R1406M_jacksheet.txt b/cmlreaders/test/data/R1406M_jacksheet.txt
new file mode 100644
index 0000000..b705444
--- /dev/null
+++ b/cmlreaders/test/data/R1406M_jacksheet.txt
@@ -0,0 +1,88 @@
+1	LFG1
+2	LFG2
+3	LFG3
+4	LFG4
+5	LFG5
+6	LFG6
+7	LFG7
+8	LFG8
+9	LFG9
+10	LFG10
+11	LFG11
+12	LFG12
+13	LFG13
+14	LFG14
+15	LFG15
+16	LFG16
+17	LFG17
+18	LFG18
+19	LFG19
+20	LFG20
+21	LFG21
+22	LFG22
+23	LFG23
+24	LFG24
+25	LFG25
+26	LFG26
+27	LFG27
+28	LFG28
+29	LFG29
+30	LFG30
+31	LFG31
+32	LFG32
+33	LTG1
+34	LTG2
+35	LTG3
+36	LTG4
+37	LTG5
+38	LTG6
+39	LTG7
+40	LTG8
+41	LTG9
+42	LTG10
+43	LTG11
+44	LTG12
+45	LTG13
+46	LTG14
+47	LTG15
+48	LTG16
+49	LTG17
+50	LTG18
+51	LTG19
+52	LTG20
+53	LTG21
+54	LTG22
+55	LTG23
+56	LTG24
+57	LTG25
+58	LTG26
+59	LTG27
+60	LTG28
+61	LTG29
+62	LTG30
+63	LTG31
+64	LTG32
+65	LIF1
+66	LIF2
+67	LIF3
+68	LIF4
+69	LIF5
+70	LIF6
+71	LIF7
+72	LIF8
+73	LSF1
+74	LSF2
+75	LSF3
+76	LSF4
+77	LSF5
+78	LSF6
+79	LSF7
+80	LSF8
+81	LAI1
+82	LAI2
+83	LAI3
+84	LAI4
+85	LPI1
+86	LPI2
+87	LPI3
+88	LPI4
\ No newline at end of file
diff --git a/cmlreaders/test/test_readers.py b/cmlreaders/test/test_readers.py
index e785219..88f528c 100644
--- a/cmlreaders/test/test_readers.py
+++ b/cmlreaders/test/test_readers.py
@@ -7,13 +7,24 @@ from unittest.mock import patch
 import numpy as np
 import pandas as pd
 
-from cmlreaders.readers import (
-    BaseJSONReader, TextReader, RAMCSVReader,
-    ElectrodeCategoriesReader, EventReader, LocalizationReader, MontageReader,
-    RamulatorEventLogReader, RAMReportSummaryDataReader, BaseRAMReportDataReader,
-    ClassifierContainerReader
-)
 from cmlreaders.exc import UnsupportedRepresentation, UnsupportedExperimentError
+from cmlreaders.readers.readers import (
+    BaseJSONReader,
+    ClassifierContainerReader,
+    EventReader,
+    TextReader,
+    RAMCSVReader,
+    RamulatorEventLogReader,
+)
+from cmlreaders.readers.electrodes import (
+    ElectrodeCategoriesReader,
+    LocalizationReader,
+    MontageReader
+)
+from cmlreaders.readers.reports import (
+    BaseRAMReportDataReader,
+    RAMReportSummaryDataReader
+)
 
 datafile = functools.partial(resource_filename, 'cmlreaders.test.data')
 
@@ -28,7 +39,7 @@ class TestTextReader:
     ])
     def test_as_methods(self, method, data_type, subject, localization):
         file_path = datafile(data_type + ".txt")
-        reader = TextReader(data_type, subject, localization,
+        reader = TextReader(data_type, subject, localization=localization,
                             file_path=file_path)
         expected_types = {
             'dataframe': pd.DataFrame,
@@ -41,15 +52,17 @@ class TestTextReader:
         assert data is not None
         assert type(data) == expected_types[method]
 
-    def test_read_jacksheet(self):
-        file_path = datafile("jacksheet.txt")
-        reader = TextReader("jacksheet", "R1389J", 0, file_path=file_path)
-        js = reader.load()
+    @pytest.mark.parametrize("subject,filename,sep", [
+        ("R1389J", datafile("jacksheet.txt"), " "),
+        ("R1406M", datafile("R1406M_jacksheet.txt"), "\t"),
+    ])
+    def test_read_jacksheet(self, subject, filename, sep):
+        js = TextReader.fromfile(filename, subject=subject, data_type="jacksheet")
 
         assert "number" in js.columns
         assert "label" in js.columns
 
-        data = np.loadtxt(file_path, delimiter=" ", dtype=[
+        data = np.loadtxt(filename, delimiter=sep, dtype=[
             ("number", "<i8"),
             ("label", "|U32"),
         ])
@@ -57,6 +70,7 @@ class TestTextReader:
         np.testing.assert_equal(data["number"], js.number)
         np.testing.assert_equal(data["label"], js.label)
 
+    @pytest.mark.xfail
     @pytest.mark.parametrize("method", ['json', 'csv'])
     @pytest.mark.parametrize("data_type", [
         "voxel_coordinates", "leads", "classifier_excluded_leads", "good_leads",
@@ -66,6 +80,8 @@ class TestTextReader:
     ])
     def test_to_methods(self, method, data_type, subject, localization,
                         rhino_root, tmpdir):
+        localization = int(localization)
+
         file_path = datafile(data_type + ".txt")
         reader = TextReader(data_type, subject, localization,
                             file_path=file_path, rootdir=rhino_root)
@@ -80,6 +96,7 @@ class TestTextReader:
         # Check that data can be reloaded
         re_reader = TextReader(data_type, subject, localization,
                                file_path=exp_output)
+
         reread_data = re_reader.as_dataframe()
         assert reread_data is not None
 

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
async-generator==1.10
attrs==22.2.0
Babel==2.11.0
bleach==4.1.0
cached-property==1.5.2
certifi==2021.5.30
charset-normalizer==2.0.12
-e git+https://github.com/pennmem/cmlreaders.git@98323aea9eb7810171ad1c4f410bc532c76f630b#egg=cmlreaders
codecov==2.1.13
coverage==6.2
cycler==0.11.0
decorator==5.1.1
defusedxml==0.7.1
docutils==0.18.1
entrypoints==0.4
flake8==3.9.2
h5py==3.1.0
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
iniconfig==1.1.1
ipython-genutils==0.2.0
Jinja2==3.0.3
jsonschema==3.2.0
jupyter-client==7.1.2
jupyter-core==4.9.2
jupyterlab-pygments==0.1.2
kiwisolver==1.3.1
MarkupSafe==2.0.1
matplotlib==3.3.4
mccabe==0.6.1
mistune==0.8.4
mne==0.23.4
nbclient==0.5.9
nbconvert==6.0.7
nbformat==5.1.3
nbsphinx==0.8.8
nest-asyncio==1.6.0
numpy==1.19.5
packaging==21.3
pandas==1.1.5
pandocfilters==1.5.1
Pillow==8.4.0
pluggy==1.0.0
py==1.11.0
pycodestyle==2.7.0
pyflakes==2.3.1
Pygments==2.14.0
pyparsing==3.1.4
pyrsistent==0.18.0
pytest==7.0.1
pytest-cov==4.0.0
python-dateutil==2.9.0.post0
pytz==2025.2
pyzmq==25.1.2
requests==2.27.1
scipy==1.5.4
six==1.17.0
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinx-rtd-theme==2.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
testpath==0.6.0
tomli==1.2.3
tornado==6.1
traitlets==4.3.3
typing_extensions==4.1.1
urllib3==1.26.20
webencodings==0.5.1
zipp==3.6.0

name: cmlreaders
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - async-generator==1.10
      - attrs==22.2.0
      - babel==2.11.0
      - bleach==4.1.0
      - cached-property==1.5.2
      - charset-normalizer==2.0.12
      - codecov==2.1.13
      - coverage==6.2
      - cycler==0.11.0
      - decorator==5.1.1
      - defusedxml==0.7.1
      - docutils==0.18.1
      - entrypoints==0.4
      - flake8==3.9.2
      - h5py==3.1.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - ipython-genutils==0.2.0
      - jinja2==3.0.3
      - jsonschema==3.2.0
      - jupyter-client==7.1.2
      - jupyter-core==4.9.2
      - jupyterlab-pygments==0.1.2
      - kiwisolver==1.3.1
      - markupsafe==2.0.1
      - matplotlib==3.3.4
      - mccabe==0.6.1
      - mistune==0.8.4
      - mne==0.23.4
      - nbclient==0.5.9
      - nbconvert==6.0.7
      - nbformat==5.1.3
      - nbsphinx==0.8.8
      - nest-asyncio==1.6.0
      - numpy==1.19.5
      - packaging==21.3
      - pandas==1.1.5
      - pandocfilters==1.5.1
      - pillow==8.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.7.0
      - pyflakes==2.3.1
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pyrsistent==0.18.0
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyzmq==25.1.2
      - requests==2.27.1
      - scipy==1.5.4
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinx-rtd-theme==2.0.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - testpath==0.6.0
      - tomli==1.2.3
      - tornado==6.1
      - traitlets==4.3.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - webencodings==0.5.1
      - zipp==3.6.0
prefix: /opt/conda/envs/cmlreaders

cmlreaders/test/test_readers.py::TestTextReader::test_read_jacksheet[R1389J-/cmlreaders/cmlreaders/test/data/jacksheet.txt- cmlreaders/test/test_readers.py::TestTextReader::test_read_jacksheet[R1406M-/cmlreaders/cmlreaders/test/data/R1406M_jacksheet.txt-\t]
cmlreaders/test/test_readers.py::TestMontageReader::test_load[R1405E-0-0-contacts] cmlreaders/test/test_readers.py::TestMontageReader::test_load[R1405E-0-0-pairs] cmlreaders/test/test_readers.py::TestMontageReader::test_load[R1006P-0-0-contacts] cmlreaders/test/test_readers.py::TestMontageReader::test_load[R1006P-0-0-pairs] cmlreaders/test/test_readers.py::TestMontageReader::test_load[R1006P-0-1-contacts] cmlreaders/test/test_readers.py::TestMontageReader::test_load[R1006P-0-1-pairs] cmlreaders/test/test_readers.py::TestElectrodeCategoriesReader::test_load[R1111M-lens0] cmlreaders/test/test_readers.py::TestElectrodeCategoriesReader::test_load[R1052E-lens1] cmlreaders/test/test_readers.py::TestClassifierContainerReader::test_as_methods[baseline_classifier-pyobject] cmlreaders/test/test_readers.py::TestClassifierContainerReader::test_as_methods[used_classifier-pyobject] cmlreaders/test/test_readers.py::TestClassifierContainerReader::test_to_methods[baseline_classifier-binary] cmlreaders/test/test_readers.py::TestClassifierContainerReader::test_to_methods[used_classifier-binary]
cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-voxel_coordinates-dataframe] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-voxel_coordinates-recarray] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-voxel_coordinates-dict] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-leads-dataframe] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-leads-recarray] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-leads-dict] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-classifier_excluded_leads-dataframe] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-classifier_excluded_leads-recarray] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-classifier_excluded_leads-dict] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-good_leads-dataframe] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-good_leads-recarray] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-good_leads-dict] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-jacksheet-dataframe] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-jacksheet-recarray] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-jacksheet-dict] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-area-dataframe] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-area-recarray] cmlreaders/test/test_readers.py::TestTextReader::test_as_methods[R1389J-0-area-dict] cmlreaders/test/test_readers.py::TestTextReader::test_failures cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-electrode_coordinates-dataframe] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-electrode_coordinates-recarray] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-electrode_coordinates-dict] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-prior_stim_results-dataframe] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-prior_stim_results-recarray] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-prior_stim_results-dict] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-target_selection_table-dataframe] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-target_selection_table-recarray] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_as_methods[R1409D-0-target_selection_table-dict] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_to_methods[R1409D-0-electrode_coordinates-json] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_to_methods[R1409D-0-electrode_coordinates-csv] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_to_methods[R1409D-0-prior_stim_results-json] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_to_methods[R1409D-0-prior_stim_results-csv] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_to_methods[R1409D-0-target_selection_table-json] cmlreaders/test/test_readers.py::TestRAMCSVReader::test_to_methods[R1409D-0-target_selection_table-csv] cmlreaders/test/test_readers.py::TestRamulatorEventLogReader::test_as_methods[R1409D-catFR1-1-event_log-dataframe] cmlreaders/test/test_readers.py::TestRamulatorEventLogReader::test_as_methods[R1409D-catFR1-1-event_log-recarray] cmlreaders/test/test_readers.py::TestRamulatorEventLogReader::test_as_methods[R1409D-catFR1-1-event_log-dict] cmlreaders/test/test_readers.py::TestRamulatorEventLogReader::test_to_methods[R1409D-catFR1-1-event_log-json] cmlreaders/test/test_readers.py::TestRamulatorEventLogReader::test_to_methods[R1409D-catFR1-1-event_log-csv] cmlreaders/test/test_readers.py::TestBaseJSONReader::test_load cmlreaders/test/test_readers.py::TestEventReader::test_load_json cmlreaders/test/test_readers.py::TestEventReader::test_load_matlab[all_events] cmlreaders/test/test_readers.py::TestEventReader::test_load_matlab[task_events] cmlreaders/test/test_readers.py::TestEventReader::test_load_matlab[math_events] cmlreaders/test/test_readers.py::TestLocalizationReader::test_load cmlreaders/test/test_readers.py::test_fromfile[ElectrodeCategoriesReader-/cmlreaders/cmlreaders/test/data/electrode_categories.txt-dict] cmlreaders/test/test_readers.py::test_fromfile[MontageReader-/cmlreaders/cmlreaders/test/data/pairs.json-DataFrame] cmlreaders/test/test_readers.py::test_fromfile[MontageReader-/cmlreaders/cmlreaders/test/data/contacts.json-DataFrame] cmlreaders/test/test_readers.py::test_fromfile[RamulatorEventLogReader-/cmlreaders/cmlreaders/test/data/event_log.json-DataFrame]
cmlreaders/readers/readers.py cmlreaders/base_reader.py
cmlreaders/readers/readers.py cmlreaders/base_reader.py