[med-svn] [python-mne] 01/07: Imported Upstream version 0.12+dfsg

Yaroslav Halchenko debian at onerussian.com
Sun Jul 10 22:22:45 UTC 2016


This is an automated email from the git hooks/post-receive script.

yoh pushed a commit to branch master
in repository python-mne.

commit 947cbfbf7834381e8f5287ff3068c7f7f4a1c5cc
Author: Yaroslav Halchenko <debian at onerussian.com>
Date:   Fri May 13 17:38:10 2016 -0400

    Imported Upstream version 0.12+dfsg
---
 .gitignore                                         |   9 +
 .mailmap                                           |   3 +
 .travis.yml                                        |  10 +-
 LICENSE.txt                                        |   4 +-
 MANIFEST.in                                        |   1 +
 Makefile                                           |  18 +-
 README.rst                                         |  27 +-
 appveyor.yml                                       |   1 +
 circle.yml                                         |  83 ++
 dictionary.txt                                     | 273 ++++++-
 doc/Makefile                                       |  34 +-
 doc/_static/style.css                              | 107 ++-
 doc/_templates/function.rst                        |   4 +
 doc/advanced_setup.rst                             | 163 ----
 doc/cite.rst                                       |   4 +-
 doc/cited.rst                                      | 112 +++
 doc/conf.py                                        | 166 ++--
 doc/contributing.rst                               |  22 +-
 doc/faq.rst                                        | 185 ++++-
 doc/getting_started.rst                            | 376 +++------
 doc/git_links.inc                                  |   8 +-
 doc/index.rst                                      |  74 +-
 doc/{getting_started.rst => install_mne_c.rst}     | 173 ++--
 doc/install_mne_python.rst                         | 277 +++++++
 doc/manual/appendix/bem_model.rst                  |   4 +-
 doc/manual/appendix/c_EULA.rst                     |   4 +-
 doc/manual/appendix/c_release_notes.rst            |  10 +-
 doc/manual/c_reference.rst                         |  53 +-
 doc/manual/channel_interpolation.rst               |  75 ++
 doc/manual/cookbook.rst                            |  18 +-
 doc/manual/datasets_index.rst                      |  11 +-
 doc/manual/decoding.rst                            |   3 +-
 doc/manual/gui/analyze.rst                         |   2 +-
 doc/manual/gui/browse.rst                          |   4 +-
 doc/manual/index.rst                               |  93 ++-
 doc/manual/io.rst                                  |  35 +-
 doc/manual/matlab.rst                              |  11 +-
 doc/manual/memory.rst                              |   9 +-
 doc/manual/migrating.rst                           |  54 ++
 doc/manual/pitfalls.rst                            |  26 +-
 doc/manual/preprocessing/maxwell.rst               |  76 ++
 doc/manual/preprocessing/ssp.rst                   |  21 +-
 doc/manual/sample_dataset.rst                      |   9 +-
 doc/manual/source_localization/covariance.rst      |   5 -
 doc/manual/source_localization/forward.rst         |  12 +-
 doc/manual/source_localization/inverse.rst         |  84 +-
 doc/manual/time_frequency.rst                      |  10 +-
 doc/martinos.rst                                   |  35 +
 doc/mne_cpp.rst                                    |  10 +-
 doc/python_reference.rst                           |  85 +-
 doc/references.rst                                 |  14 +-
 doc/sphinxext/cited_mne.py                         | 252 ++++++
 doc/sphinxext/flow_diagram.py                      |   2 +-
 doc/sphinxext/{commands.py => gen_commands.py}     |  23 +-
 doc/this_project.inc                               |   6 +-
 doc/tutorials.rst                                  | 196 +++--
 doc/tutorials/command_line.rst                     |  26 +-
 doc/tutorials/mne-report.png                       | Bin 172544 -> 159506 bytes
 doc/tutorials/report.rst                           |  66 +-
 doc/tutorials/seven_stories_about_mne.rst          | 171 ++++
 doc/whats_new.rst                                  | 389 +++++++--
 examples/README.txt                                |   5 +-
 .../connectivity/plot_cwt_sensor_connectivity.py   |   4 +-
 .../plot_mne_inverse_coherence_epochs.py           |   5 +-
 .../plot_mne_inverse_connectivity_spectrum.py      |   5 +-
 .../plot_mne_inverse_label_connectivity.py         |   5 +-
 .../connectivity/plot_mne_inverse_psi_visual.py    |   3 +-
 examples/connectivity/plot_sensor_connectivity.py  |   4 +-
 examples/datasets/plot_brainstorm_data.py          |  16 +-
 examples/datasets/plot_megsim_data.py              |   4 +-
 examples/datasets/plot_spm_faces_dataset.py        |  28 +-
 examples/decoding/plot_decoding_csp_eeg.py         |   2 +-
 examples/decoding/plot_decoding_csp_space.py       |  14 +-
 .../plot_decoding_spatio_temporal_source.py        |   5 +-
 .../decoding/plot_decoding_time_generalization.py  |  55 --
 ...plot_decoding_time_generalization_conditions.py |   2 +-
 examples/decoding/plot_decoding_xdawn_eeg.py       |   2 +-
 examples/decoding/plot_ems_filtering.py            |   2 +-
 examples/decoding/plot_linear_model_patterns.py    |   6 +-
 examples/forward/plot_bem_contour_mri.py           |  27 -
 examples/forward/plot_coregistration_transform.py  |  33 -
 ...forward.py => plot_forward_sensitivity_maps.py} |  27 +-
 examples/forward/plot_read_forward.py              |  67 --
 .../plot_compute_mne_inverse_epochs_in_label.py    |   5 +-
 .../plot_compute_mne_inverse_raw_in_label.py       |   3 +-
 examples/inverse/plot_covariance_whitening_dspm.py | 116 ++-
 examples/inverse/plot_dics_beamformer.py           |   3 +-
 examples/inverse/plot_dics_source_power.py         |   3 +-
 examples/inverse/plot_dipole_fit.py                |  43 -
 examples/inverse/plot_lcmv_beamformer.py           |  24 +-
 examples/inverse/plot_lcmv_beamformer_volume.py    |  16 +-
 examples/inverse/plot_make_inverse_operator.py     |  84 --
 examples/inverse/plot_tf_dics.py                   |  21 +-
 examples/inverse/plot_tf_lcmv.py                   |  31 +-
 examples/io/README.txt                             |   3 +-
 examples/io/plot_objects_from_arrays.py            |  15 +-
 examples/io/plot_read_and_write_raw_data.py        |   2 +-
 examples/io/plot_read_epochs.py                    |   2 +-
 examples/io/plot_read_evoked.py                    |   5 +
 examples/plot_compute_mne_inverse.py               |  60 --
 examples/plot_extract_events_from_raw.py           |  41 -
 examples/plot_from_raw_to_epochs_to_evoked.py      |  77 --
 examples/preprocessing/plot_corrmap_detection.py   |  76 --
 .../preprocessing/plot_define_target_events.py     |   2 +-
 .../preprocessing/plot_eog_artifact_histogram.py   |   2 +-
 .../plot_estimate_covariance_matrix_baseline.py    |  55 --
 .../plot_estimate_covariance_matrix_raw.py         |  38 -
 examples/preprocessing/plot_find_ecg_artifacts.py  |   2 +-
 examples/preprocessing/plot_find_eog_artifacts.py  |   2 +-
 .../preprocessing/plot_movement_compensation.py    |  53 ++
 examples/preprocessing/plot_rereference_eeg.py     |   2 +-
 examples/preprocessing/plot_resample.py            |  16 +-
 examples/preprocessing/plot_run_ica.py             |   3 +-
 examples/preprocessing/plot_xdawn_denoising.py     |   2 +-
 examples/realtime/ftclient_rt_compute_psd.py       |   4 +-
 examples/realtime/plot_compute_rt_average.py       |   3 +-
 examples/realtime/plot_compute_rt_decoder.py       |   2 +-
 examples/realtime/rt_feedback_server.py            |   2 +-
 examples/simulation/plot_simulate_evoked_data.py   |  20 +-
 examples/simulation/plot_simulate_raw_data.py      |   7 +-
 examples/stats/plot_cluster_stats_evoked.py        |   4 +-
 examples/stats/plot_fdr_stats_evoked.py            |   2 +-
 examples/stats/plot_linear_regression_raw.py       |  51 +-
 examples/stats/plot_sensor_permutation_test.py     |   4 +-
 examples/stats/plot_sensor_regression.py           |   2 +-
 .../plot_compute_raw_data_spectrum.py              |  42 +-
 .../plot_compute_source_psd_epochs.py              |   3 +-
 examples/time_frequency/plot_epochs_spectra.py     |  45 --
 .../plot_source_label_time_frequency.py            |   2 +-
 .../time_frequency/plot_source_power_spectrum.py   |   2 +-
 .../plot_source_space_time_frequency.py            |   2 +-
 examples/time_frequency/plot_stockwell.py          |  50 --
 examples/time_frequency/plot_temporal_whitening.py |   4 +-
 .../plot_time_frequency_multitaper_sensors.py      |  55 --
 .../time_frequency/plot_time_frequency_sensors.py  |  66 --
 .../visualization/plot_channel_epochs_image.py     |   2 +-
 examples/visualization/plot_clickable_image.py     |   2 +-
 examples/visualization/plot_evoked_delayed_ssp.py  |  95 ---
 examples/visualization/plot_evoked_erf_erp.py      |  51 --
 examples/visualization/plot_evoked_topomap.py      |   3 +
 .../plot_evoked_topomap_delayed_ssp.py             |  62 --
 examples/visualization/plot_evoked_whitening.py    |   2 +-
 examples/visualization/plot_meg_eeg_fields_3d.py   |  46 --
 examples/visualization/plot_meg_sensors.py         |  42 +
 .../plot_ssp_projs_sensitivity_map.py              |   5 +-
 examples/visualization/plot_ssp_projs_topomaps.py  |  29 -
 .../plot_topo_channel_epochs_image.py              |  55 --
 .../visualization/plot_topo_compare_conditions.py  |   3 +-
 examples/visualization/plot_topo_customized.py     |   8 +-
 examples/visualization/plot_topography.py          |  31 -
 mne/__init__.py                                    |  16 +-
 mne/annotations.py                                 | 113 +++
 mne/baseline.py                                    |  99 +--
 mne/beamformer/_dics.py                            |  10 +-
 mne/beamformer/_lcmv.py                            |  30 +-
 mne/beamformer/tests/test_dics.py                  |   5 +-
 mne/beamformer/tests/test_lcmv.py                  |  93 +--
 mne/bem.py                                         | 211 +++--
 mne/channels/channels.py                           | 181 +++--
 .../data/layouts/Vectorview-grad_norm.lout         | 103 +++
 mne/channels/interpolation.py                      |  42 +-
 mne/channels/layout.py                             |  93 ++-
 mne/channels/montage.py                            | 151 +++-
 mne/channels/tests/test_channels.py                |  37 +-
 mne/channels/tests/test_interpolation.py           |  23 +-
 mne/channels/tests/test_layout.py                  |  78 +-
 mne/channels/tests/test_montage.py                 |  63 +-
 mne/chpi.py                                        | 659 ++++++++++------
 mne/commands/mne_browse_raw.py                     |   5 +-
 mne/commands/mne_bti2fiff.py                       |   3 +-
 mne/commands/mne_clean_eog_ecg.py                  |   2 +-
 mne/commands/mne_compute_proj_ecg.py               |  15 +-
 mne/commands/mne_compute_proj_eog.py               |   4 +-
 mne/commands/mne_coreg.py                          |   2 +-
 mne/commands/mne_freeview_bem_surfaces.py          |   2 +-
 mne/commands/mne_kit2fiff.py                       |   2 +-
 mne/commands/mne_make_scalp_surfaces.py            |   3 +-
 mne/commands/mne_maxfilter.py                      |   2 +-
 mne/commands/mne_show_info.py                      |  34 +
 mne/commands/mne_surf2bem.py                       |   2 +-
 mne/commands/tests/test_commands.py                |  12 +-
 mne/connectivity/spectral.py                       |   9 +-
 mne/cov.py                                         | 389 +++++----
 mne/cuda.py                                        |  74 +-
 mne/data/coil_def.dat                              |  29 +-
 mne/datasets/__init__.py                           |   8 +-
 mne/datasets/brainstorm/bst_auditory.py            |   2 +-
 mne/datasets/brainstorm/bst_raw.py                 |   2 +-
 mne/datasets/brainstorm/bst_resting.py             |   2 +-
 mne/datasets/megsim/megsim.py                      |   2 +-
 mne/datasets/megsim/urls.py                        |   7 +
 mne/datasets/misc/__init__.py                      |   4 +
 mne/datasets/misc/_misc.py                         |  18 +
 mne/datasets/spm_face/__init__.py                  |   2 +-
 mne/datasets/spm_face/spm_data.py                  |  14 +-
 mne/datasets/testing/__init__.py                   |   2 +-
 mne/datasets/utils.py                              | 181 +++--
 mne/decoding/base.py                               |  10 +-
 mne/decoding/csp.py                                |  10 +-
 mne/decoding/ems.py                                |  19 +-
 mne/decoding/tests/test_csp.py                     |  17 +-
 mne/decoding/tests/test_ems.py                     |   6 +-
 mne/decoding/tests/test_time_gen.py                | 251 ++++--
 mne/decoding/tests/test_transformer.py             |   8 +-
 mne/decoding/time_gen.py                           | 868 +++++++++++++--------
 mne/decoding/transformer.py                        |  34 +-
 mne/defaults.py                                    |  23 +-
 mne/dipole.py                                      | 517 +++++++++---
 mne/epochs.py                                      | 665 +++++++++++-----
 mne/event.py                                       | 129 ++-
 mne/evoked.py                                      | 650 +++++++++------
 mne/filter.py                                      |  97 ++-
 mne/fixes.py                                       |  95 ++-
 mne/forward/__init__.py                            |   7 +-
 mne/forward/_compute_forward.py                    |  28 +-
 mne/forward/_field_interpolation.py                |  27 +-
 mne/forward/_lead_dots.py                          |  60 +-
 mne/forward/_make_forward.py                       | 225 +++++-
 mne/forward/forward.py                             | 211 +++--
 mne/forward/tests/test_field_interpolation.py      |  45 +-
 mne/forward/tests/test_forward.py                  |  29 +-
 mne/forward/tests/test_make_forward.py             | 260 +++---
 mne/gui/__init__.py                                |  19 +-
 mne/gui/_backend.py                                |  27 +
 mne/inverse_sparse/_gamma_map.py                   |   7 +-
 mne/inverse_sparse/mxne_optim.py                   |  17 +-
 mne/inverse_sparse/tests/test_gamma_map.py         |  53 +-
 mne/inverse_sparse/tests/test_mxne_inverse.py      |   2 +-
 mne/io/__init__.py                                 |   8 +-
 mne/io/array/array.py                              |  15 +-
 mne/io/array/tests/test_array.py                   |  12 +-
 mne/io/base.py                                     | 576 ++++++++------
 mne/io/brainvision/brainvision.py                  | 171 ++--
 mne/io/brainvision/tests/test_brainvision.py       | 160 ++--
 mne/io/bti/bti.py                                  | 137 ++--
 mne/io/bti/read.py                                 |  12 -
 mne/io/bti/tests/test_bti.py                       |  88 ++-
 mne/io/cnt/__init__.py                             |   1 +
 mne/io/cnt/cnt.py                                  | 371 +++++++++
 mne/io/cnt/tests/__init__.py                       |   0
 mne/io/cnt/tests/test_cnt.py                       |  34 +
 mne/io/compensator.py                              |   3 +-
 mne/io/constants.py                                |  44 +-
 mne/io/ctf/ctf.py                                  |  14 +-
 mne/io/ctf/eeg.py                                  |  48 +-
 mne/io/ctf/info.py                                 |  65 +-
 mne/io/ctf/tests/test_ctf.py                       |  60 +-
 mne/io/edf/edf.py                                  |  36 +-
 mne/io/edf/tests/test_edf.py                       |  15 +
 mne/io/eeglab/eeglab.py                            | 240 ++++--
 mne/io/eeglab/tests/test_eeglab.py                 |  53 +-
 mne/io/egi/egi.py                                  |  74 +-
 mne/io/egi/tests/test_egi.py                       |   7 +-
 mne/io/fiff/__init__.py                            |   2 +-
 mne/io/fiff/raw.py                                 |  77 +-
 mne/io/fiff/tests/test_raw_fiff.py                 | 322 +++++---
 mne/io/kit/constants.py                            |   2 +-
 mne/io/kit/kit.py                                  | 138 ++--
 mne/io/matrix.py                                   |  15 +-
 mne/io/meas_info.py                                | 226 ++++--
 mne/io/nicolet/nicolet.py                          |  36 +-
 mne/io/pick.py                                     | 110 ++-
 mne/io/proc_history.py                             |   4 +-
 mne/io/proj.py                                     |  98 ++-
 mne/io/reference.py                                |  30 +-
 mne/io/tag.py                                      | 552 ++++++-------
 mne/io/tests/test_meas_info.py                     | 124 ++-
 mne/io/tests/test_pick.py                          |  98 ++-
 mne/io/tests/test_proc_history.py                  |   4 +-
 mne/io/tests/test_raw.py                           |  10 +-
 mne/io/tests/test_reference.py                     |  25 +-
 mne/io/utils.py                                    |  80 +-
 mne/io/write.py                                    |   4 +-
 mne/label.py                                       | 208 +++--
 mne/minimum_norm/inverse.py                        |  38 +-
 mne/minimum_norm/tests/test_inverse.py             |  16 +-
 mne/minimum_norm/tests/test_time_frequency.py      |  14 +-
 mne/minimum_norm/time_frequency.py                 |  30 +-
 mne/parallel.py                                    |  11 +-
 mne/preprocessing/ecg.py                           |  21 +-
 mne/preprocessing/eog.py                           |   7 +-
 mne/preprocessing/ica.py                           | 252 +++---
 mne/preprocessing/infomax_.py                      |   8 +-
 mne/preprocessing/maxfilter.py                     |   5 +-
 mne/preprocessing/maxwell.py                       | 833 ++++++++++++++------
 mne/preprocessing/ssp.py                           |   6 +-
 mne/preprocessing/stim.py                          |  11 +-
 mne/preprocessing/tests/test_ctps.py               |   9 +-
 mne/preprocessing/tests/test_eeglab_infomax.py     |   4 +-
 mne/preprocessing/tests/test_ica.py                |  52 +-
 mne/preprocessing/tests/test_maxwell.py            | 318 ++++++--
 mne/preprocessing/tests/test_ssp.py                |   6 +-
 mne/preprocessing/tests/test_xdawn.py              |   3 +-
 mne/preprocessing/xdawn.py                         |   6 +-
 mne/proj.py                                        |  22 +-
 mne/realtime/epochs.py                             |   4 +-
 mne/realtime/fieldtrip_client.py                   |  23 +-
 mne/realtime/tests/test_mockclient.py              |   6 +-
 mne/report.py                                      |  32 +-
 mne/selection.py                                   | 117 +--
 mne/simulation/evoked.py                           |   2 +-
 mne/simulation/raw.py                              |  47 +-
 mne/simulation/source.py                           |   8 +-
 mne/simulation/tests/test_raw.py                   |  54 +-
 mne/source_estimate.py                             |  54 +-
 mne/source_space.py                                |  76 +-
 mne/stats/cluster_level.py                         |  20 +-
 mne/stats/parametric.py                            |   7 +-
 mne/stats/regression.py                            | 121 ++-
 mne/stats/tests/test_cluster_level.py              |  19 +-
 mne/stats/tests/test_regression.py                 |  21 +-
 mne/surface.py                                     |  18 +-
 mne/tests/common.py                                |  93 ++-
 mne/tests/test_annotations.py                      |  58 ++
 mne/tests/test_bem.py                              | 124 ++-
 mne/tests/test_chpi.py                             | 165 ++--
 mne/tests/test_cov.py                              | 133 +++-
 mne/tests/test_dipole.py                           | 173 ++--
 mne/tests/test_docstring_parameters.py             |   1 +
 mne/tests/test_epochs.py                           | 348 ++++++---
 mne/tests/test_event.py                            | 125 ++-
 mne/tests/test_evoked.py                           |  60 +-
 mne/tests/test_filter.py                           |  17 +-
 mne/tests/test_label.py                            |  85 +-
 mne/tests/test_proj.py                             |  48 +-
 mne/tests/test_selection.py                        |  31 +-
 mne/tests/test_source_estimate.py                  |   7 +-
 mne/tests/test_source_space.py                     |  17 +-
 mne/tests/test_surface.py                          |   8 +-
 mne/tests/test_transforms.py                       |  68 +-
 mne/tests/test_utils.py                            | 125 ++-
 mne/time_frequency/__init__.py                     |   2 +-
 mne/time_frequency/_stockwell.py                   |   9 +-
 mne/time_frequency/csd.py                          |  13 +-
 mne/time_frequency/multitaper.py                   | 175 +++--
 mne/time_frequency/psd.py                          | 283 ++++++-
 mne/time_frequency/tests/test_ar.py                |   2 +-
 mne/time_frequency/tests/test_multitaper.py        |  17 +-
 mne/time_frequency/tests/test_psd.py               | 217 +++---
 mne/time_frequency/tests/test_stockwell.py         |  18 +-
 mne/time_frequency/tests/test_tfr.py               |  92 ++-
 mne/time_frequency/tfr.py                          | 323 ++++----
 mne/transforms.py                                  | 127 +++
 mne/utils.py                                       | 388 +++++++--
 mne/viz/_3d.py                                     | 189 +++--
 mne/viz/__init__.py                                |   7 +-
 mne/viz/circle.py                                  |   4 +-
 mne/viz/decoding.py                                |  12 +-
 mne/viz/epochs.py                                  | 163 ++--
 mne/viz/evoked.py                                  | 398 ++++++++--
 mne/viz/ica.py                                     |  30 +-
 mne/viz/misc.py                                    |  17 +-
 mne/viz/raw.py                                     | 177 +++--
 mne/viz/tests/test_3d.py                           |  53 +-
 mne/viz/tests/test_decoding.py                     |   3 +-
 mne/viz/tests/test_epochs.py                       |  30 +-
 mne/viz/tests/test_evoked.py                       |  14 +-
 mne/viz/tests/test_ica.py                          |  32 +-
 mne/viz/tests/test_misc.py                         |   5 +-
 mne/viz/tests/test_raw.py                          |  39 +-
 mne/viz/tests/test_topo.py                         |  39 +-
 mne/viz/tests/test_topomap.py                      |  29 +-
 mne/viz/tests/test_utils.py                        |  34 +-
 mne/viz/topo.py                                    | 479 ++++++++----
 mne/viz/topomap.py                                 | 595 ++++++++++----
 mne/viz/utils.py                                   | 295 ++++++-
 setup.cfg                                          |   2 +-
 setup.py                                           |   4 +
 tutorials/plot_artifacts_correction_filtering.py   |  98 +++
 tutorials/plot_artifacts_correction_ica.py         | 201 +++++
 .../plot_artifacts_correction_maxwell_filtering.py |  28 +-
 tutorials/plot_artifacts_correction_rejection.py   | 197 +++++
 tutorials/plot_artifacts_correction_ssp.py         |  88 +++
 tutorials/plot_artifacts_detection.py              | 134 ++++
 tutorials/plot_brainstorm_auditory.py              | 357 +++++++++
 tutorials/plot_compute_covariance.py               | 138 ++++
 tutorials/plot_creating_data_structures.py         |  19 +-
 tutorials/plot_dipole_fit.py                       |  85 ++
 tutorials/plot_eeg_erp.py                          | 191 +++++
 tutorials/plot_epoching_and_averaging.py           | 158 ++++
 tutorials/plot_epochs_to_data_frame.py             |   8 +-
 tutorials/plot_forward.py                          | 181 +++++
 tutorials/plot_ica_from_raw.py                     |   7 +-
 tutorials/plot_info.py                             |  22 +-
 tutorials/plot_introduction.py                     |  38 +-
 ...ics.py => plot_mne_dspm_source_localization.py} |  73 +-
 tutorials/plot_modifying_data_inplace.py           |   2 +-
 ...lot_epochs_objects.py => plot_object_epochs.py} |  68 +-
 tutorials/plot_object_evoked.py                    |  74 ++
 .../{plot_raw_objects.py => plot_object_raw.py}    |  43 +-
 tutorials/plot_python_intro.py                     |  46 ++
 .../plot_sensors_decoding.py                       |  60 +-
 tutorials/plot_sensors_time_frequency.py           | 133 ++++
 ...lot_stats_cluster_1samp_test_time_frequency.py} |   5 +-
 ...s_tutorial.py => plot_stats_cluster_methods.py} |  35 +-
 ...al.py => plot_stats_cluster_spatio_temporal.py} |  58 +-
 ...=> plot_stats_cluster_spatio_temporal_2samp.py} |  13 +-
 ...ter_spatio_temporal_repeated_measures_anova.py} |  94 ++-
 ...ncy.py => plot_stats_cluster_time_frequency.py} |   4 +-
 ...ster_time_frequency_repeated_measures_anova.py} |  84 +-
 ... plot_stats_spatio_temporal_cluster_sensors.py} |  39 +-
 tutorials/plot_visualize_epochs.py                 |  54 ++
 tutorials/plot_visualize_evoked.py                 | 158 ++++
 tutorials/plot_visualize_raw.py                    |  92 +++
 404 files changed, 20096 insertions(+), 8931 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8b91f82..d7cff50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@
 *.egg*
 *.tmproj
 *.png
+*.dat
 .DS_Store
 events.eve
 foo-lh.label
@@ -30,11 +31,13 @@ tmp-*.w
 tmtags
 auto_examples
 MNE-eegbci-data*
+MNE-misc-data*
 MNE-sample-data*
 MNE-somato-data*
 MNE-spm-face*
 MNE-testing-data*
 MNE-brainstorm-data*
+MNE-misc-data*
 MEGSIM*
 build
 coverage
@@ -53,3 +56,9 @@ doc/samples
 cover
 
 *.orig
+
+# PyCharm
+.idea/**
+
+# sublime
+*-e
diff --git a/.mailmap b/.mailmap
index ccdd620..c951ce4 100644
--- a/.mailmap
+++ b/.mailmap
@@ -63,6 +63,7 @@ Jona Sassenhagen <jona.sassenhagen at staff.uni-marburg.de>
 Jona Sassenhagen <jona.sassenhagen at gmail.com> jona-sassenhagen <jona.sassenhagen at gmail.com>
 Jona Sassenhagen <jona.sassenhagen at gmail.com> jona <jona.sassenhagen at gmail.com>
 Jona Sassenhagen <jona.sassenhagen at gmail.com> sassenha <sassenha at fiebach1.rz.uni-frankfurt.de>
+Jona Sassenhagen <jona.sassenhagen at gmail.com> jona.sassenhagen at gmail.com <jona.sassenhagen at gmail.com>
 Yousra Bekhti <yousra.bekhti at gmail.com> Yousra BEKHTI <yousra.bekhti at gmail.com>
 Ross Maddox <rkmaddox at uw.edu> unknown <rkmaddox at uw.edu>
 Jaakko Leppakangas <jaeilepp at student.jyu.fi> jaeilepp <jaeilepp at student.jyu.fi>
@@ -70,3 +71,5 @@ Jair Montoya <montoya.jair.m at gmail.com> jmontoyam <montoya.jair.m at gmail.com>
 Natalie Klein <neklein at andrew.cmu.edu> natalieklein <neklein at andrew.cmu.edu>
 Daniel McCloy <dan.mccloy at gmail.com> drammock <dan.mccloy at gmail.com>
 Fede Raimondo <slashack at gmail.com> Fede <slashack at gmail.com>
+Emily Stephen <emilyps14 at gmail.com> emilyps14 <emilyps14 at gmail.com>
+Marian Dovgialo <mdovgialo at fabrizzio.zfb.fuw.edu.pl>
diff --git a/.travis.yml b/.travis.yml
index 2ef2c7b..b942197 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,7 +24,7 @@ env:
     #
     # Conda currently has packaging bug with mayavi/traits/numpy where 1.10 can't be used
     # but breaks sklearn on install; hopefully eventually the NUMPY=1.9 on 2.7 full can be removed
-    - PYTHON=2.7 DEPS=full TEST_LOCATION=src NUMPY="=1.9" SCIPY="=0.16"
+    - PYTHON=2.7 DEPS=full TEST_LOCATION=src NUMPY="=1.9" SCIPY="=0.17"
     - PYTHON=2.7 DEPS=nodata TEST_LOCATION=src MNE_DONTWRITE_HOME=true MNE_FORCE_SERIAL=true MNE_SKIP_NETWORK_TEST=1  # also runs flake8
     - PYTHON=3.5 DEPS=full TEST_LOCATION=install MNE_STIM_CHANNEL=STI101
     - PYTHON=2.6 DEPS=full TEST_LOCATION=src NUMPY="=1.7" SCIPY="=0.11" MPL="=1.1" LIBPNG="=1.5" SKLEARN="=0.11" PANDAS="=0.8"
@@ -68,6 +68,11 @@ install:
       fi;
     - if [ "${DEPS}" == "nodata" ]; then
         pip install -q flake8;
+        wget -q https://github.com/lucasdemarchi/codespell/archive/v1.8.tar.gz;
+        tar xzf v1.8.tar.gz;
+        cp codespell-1.8/codespell.py ~/miniconda/envs/testenv/bin;
+        rm v1.8.tar.gz;
+        rm -r codespell-1.8;
       fi;
     - pip install -q coveralls nose-timer
     # check our versions for the major packages
@@ -135,6 +140,9 @@ script:
     - if [ "${DEPS}" == "nodata" ]; then
         make flake;
       fi;
+    - if [ "${DEPS}" == "nodata" ]; then
+        make codespell-error;
+      fi;
 
 after_success:
     # Need to run from source dir to exectue "git" commands
diff --git a/LICENSE.txt b/LICENSE.txt
index 32a7bc5..56a4b94 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright © 2011-2014, authors of MNE-Python
+Copyright © 2011-2016, authors of MNE-Python
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -21,4 +21,4 @@ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
index 761a7a9..0dbcc6f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -17,6 +17,7 @@ recursive-include mne/channels/data/neighbors *.mat
 recursive-include mne/preprocessing/tests/data *.mat
 recursive-include mne/html *.js
 recursive-include mne/html *.css
+recursive-include mne/gui/help *.json
 recursive-exclude examples/MNE-sample-data *
 recursive-exclude examples/MNE-testing-data *
 recursive-exclude examples/MNE-spm-face *
diff --git a/Makefile b/Makefile
index b71fe32..a01dc85 100755
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,9 @@
 PYTHON ?= python
 NOSETESTS ?= nosetests
 CTAGS ?= ctags
-
+# The *.fif had to be there twice to be properly ignored (!)
+CODESPELL_SKIPS ?= "*.fif,*.fif,*.eve,*.gz,*.tgz,*.zip,*.mat,*.stc,*.label,*.w,*.bz2,*.annot,*.sulc,*.log,*.local-copy,*.orig_avg,*.inflated_avg,*.gii,*.pyc,*.doctree,*.pickle,*.inv,*.png,*.edf,*.touch,*.thickness,*.nofix,*.volume,*.defect_borders,*.mgh,lh.*,rh.*,COR-*,FreeSurferColorLUT.txt,*.examples,.xdebug_mris_calc,bad.segments,BadChannels,*.hist,empty_file,*.orig"
+CODESPELL_DIRS ?= mne/ doc/ tutorials/ examples/
 all: clean inplace test test-doc
 
 clean-pyc:
@@ -40,6 +42,10 @@ test: in
 	rm -f .coverage
 	$(NOSETESTS) -a '!ultra_slow_test' mne
 
+test-verbose: in
+	rm -f .coverage
+	$(NOSETESTS) -a '!ultra_slow_test' mne --verbose
+
 test-fast: in
 	rm -f .coverage
 	$(NOSETESTS) -a '!slow_test' mne
@@ -87,16 +93,18 @@ upload-pipy:
 flake:
 	@if command -v flake8 > /dev/null; then \
 		echo "Running flake8"; \
-		flake8 --count mne examples; \
+		flake8 --count mne examples tutorials; \
 	else \
 		echo "flake8 not found, please install it!"; \
 		exit 1; \
 	fi;
 	@echo "flake8 passed"
 
-codespell:
-	# The *.fif had to be there twice to be properly ignored (!)
-	codespell.py -w -i 3 -S="*.fif,*.fif,*.eve,*.gz,*.tgz,*.zip,*.mat,*.stc,*.label,*.w,*.bz2,*.coverage,*.annot,*.sulc,*.log,*.local-copy,*.orig_avg,*.inflated_avg,*.gii" ./dictionary.txt -r .
+codespell:  # running manually
+	@codespell.py -w -i 3 -q 3 -S $(CODESPELL_SKIPS) -D ./dictionary.txt $(CODESPELL_DIRS)
+
+codespell-error:  # running on travis
+	@codespell.py -i 0 -q 7 -S $(CODESPELL_SKIPS) -D ./dictionary.txt $(CODESPELL_DIRS) | tee /dev/tty | wc -l | xargs test 0 -eq
 
 manpages:
 	@echo "I: generating manpages"
diff --git a/README.rst b/README.rst
index e584a82..543268a 100644
--- a/README.rst
+++ b/README.rst
@@ -18,9 +18,9 @@
 `mne-python <http://mne-tools.github.io/>`_
 =======================================================
 
-This package is designed for sensor- and source-space analysis of M-EEG
-data, including frequency-domain and time-frequency analyses and
-non-parametric statistics. This package is presently evolving quickly and
+This package is designed for sensor- and source-space analysis of [M/E]EG
+data, including frequency-domain and time-frequency analyses, MVPA/decoding
+and non-parametric statistics. This package is presently evolving quickly and
 thanks to the adopted open development environment user contributions can
 be easily incorporated.
 
@@ -47,14 +47,14 @@ To get the latest code using git, simply type::
 
     git clone git://github.com/mne-tools/mne-python.git
 
-If you don't have git installed, you can download a zip or tarball
-of the latest code: https://github.com/mne-tools/mne-python/archives/master
+If you don't have git installed, you can download a zip
+of the latest code: https://github.com/mne-tools/mne-python/archive/master.zip
 
 Install mne-python
 ^^^^^^^^^^^^^^^^^^
 
-As any Python packages, to install MNE-Python, go in the mne-python source
-code directory and do::
+As any Python packages, to install MNE-Python, after obtaining the source code
+(e.g. from git), go in the mne-python source code directory and do::
 
     python setup.py install
 
@@ -69,6 +69,10 @@ You can also install the latest release version with easy_install::
 
 or with pip::
 
+    pip install mne
+    
+for an update of an already installed version use::
+
     pip install mne --upgrade
 
 or for the latest development version (the most up to date)::
@@ -82,14 +86,15 @@ The required dependencies to build the software are python >= 2.6,
 NumPy >= 1.6, SciPy >= 0.7.2 and matplotlib >= 0.98.4.
 
 Some isolated functions require pandas >= 0.7.3.
+Decoding relies on scikit-learn >= 0.15.
 
 To run the tests you will also need nose >= 0.10.
 and the MNE sample dataset (will be downloaded automatically
-when you run an example ... but be patient)
+when you run an example ... but be patient).
 
-To use NVIDIA CUDA for FFT FIR filtering, you will also need to install
-the NVIDIA CUDA SDK, pycuda, and scikits.cuda. The difficulty of this varies
-by platform; consider reading the following site for help getting pycuda
+To use NVIDIA CUDA for resampling and FFT FIR filtering, you will also need
+to install the NVIDIA CUDA SDK, pycuda, and scikits.cuda. The difficulty of this
+varies by platform; consider reading the following site for help getting pycuda
 to work (typically the most difficult to configure):
 
 http://wiki.tiker.net/PyCuda/Installation/
diff --git a/appveyor.yml b/appveyor.yml
index 9011b9b..3953842 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -27,6 +27,7 @@ install:
   - "pip install nose-timer nibabel nitime"
   - "python setup.py develop"
   - "SET MNE_SKIP_NETWORK_TESTS=1"
+  - "SET MNE_FORCE_SERIAL=true"  # otherwise joblib will bomb
   - "SET MNE_LOGGING_LEVEL=warning"
   - "python -c \"import mne; mne.datasets.testing.data_path()\""
 
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 0000000..5a933a2
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,83 @@
+machine:
+  environment:
+    # We need to set this variable to let Anaconda take precedence
+    PATH: "/home/ubuntu/miniconda/envs/circleenv/bin:/home/ubuntu/miniconda/bin:$PATH"
+    MNE_DATA: "/home/ubuntu/mne_data"
+    DISPLAY: ":99.0"
+
+dependencies:
+  cache_directories:
+    - "/home/ubuntu/miniconda"
+    - "/home/ubuntu/.mne"
+    - "/home/ubuntu/mne_data"
+    - "/home/ubuntu/mne-tools.github.io"
+  # Various dependencies
+  pre:
+    # Get a running Python
+    - cd ~;
+    # Disable pyenv (no cleaner way provided by CircleCI as it prepends pyenv version to PATH)
+    - rm -rf ~/.pyenv;
+    - rm -rf ~/virtualenvs;
+    # Get Anaconda and conda-based requirements
+    - >
+      if [ ! -d "/home/ubuntu/miniconda" ]; then
+        echo "Setting up conda";
+        wget -q http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O ~/miniconda.sh;
+        chmod +x ~/miniconda.sh;
+        ~/miniconda.sh -b -p /home/ubuntu/miniconda;
+        conda update --yes --quiet conda;
+        conda create -n circleenv --yes pip python=2.7 pip numpy scipy scikit-learn mayavi matplotlib sphinx pillow six IPython pandas;
+        sed -i "s/ENABLE_USER_SITE = .*/ENABLE_USER_SITE = False/g" /home/ubuntu/miniconda/envs/circleenv/lib/python2.7/site.py;
+      else
+        echo "Conda already set up.";
+      fi
+    - ls -al /home/ubuntu/miniconda;
+    - ls -al /home/ubuntu/miniconda/bin;
+    - echo $PATH;
+    - which python;
+    - which pip;
+    - git clone https://github.com/sphinx-gallery/sphinx-gallery.git;
+    - cd sphinx-gallery && pip install -r requirements.txt && python setup.py develop;
+    - cd /home/ubuntu && git clone https://github.com/enthought/pyface.git && cd pyface && python setup.py develop;
+    - pip install sphinx_bootstrap_theme PySurfer nilearn neo;
+
+  override:
+    - cd /home/ubuntu/mne-python && python setup.py develop;
+    - if [ "$CIRCLE_BRANCH" == "master" ]; then
+        mkdir -p ~/mne_data;
+        python -c "import mne; mne.datasets._download_all_example_data()";
+      fi
+    - python -c "import mne; mne.sys_info()";
+    - >
+      if [ ! -d "/home/ubuntu/mne-tools.github.io" ]; then
+        cd .. && git clone https://github.com/mne-tools/mne-tools.github.io.git && cd mne-tools.github.io;
+      fi;
+    - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1400x900x24 -ac +extension GLX +render -noreset;
+
+test:
+  override:
+    - if [ "$CIRCLE_BRANCH" == "master" ]; then
+        make test-doc;
+      else
+        cd doc && make html_dev-noplot;
+      fi
+    - if [ "$CIRCLE_BRANCH" == "master" ]; then cd doc && make html_dev; fi:
+          timeout: 1500
+
+general:
+  # branches:
+  #   only:
+  #     - master
+  # Open the doc to the API
+  artifacts:
+    - "doc/_build/html"
+
+deployment:
+  production:
+    branch: master
+    commands:
+      - git config --global user.email "circle at mne.com"
+      - git config --global user.name "Circle Ci"
+      - cd ../mne-tools.github.io && git checkout master && git pull origin master
+      - cd doc/_build/html && cp -rf * ~/mne-tools.github.io/dev
+      - cd ../mne-tools.github.io && git add -A && git commit -m 'Automated update of dev docs.' && git push origin master
diff --git a/dictionary.txt b/dictionary.txt
index 25279c3..c41691c 100644
--- a/dictionary.txt
+++ b/dictionary.txt
@@ -1,4 +1,5 @@
 abandonned->abandoned
+abbout->about
 aberation->aberration
 abilties->abilities
 abilty->ability
@@ -8,16 +9,24 @@ abondoning->abandoning
 abondons->abandons
 aborigene->aborigine
 abortificant->abortifacient
+abotu->about
+abouta->about a
+aboutit->about it
+aboutthe->about the
 abreviate->abbreviate
 abreviated->abbreviated
 abreviation->abbreviation
 abritrary->arbitrary
 absail->abseil
 absailing->abseiling
+absance->absence
+abscence->absence
 absense->absence
 absolutly->absolutely
 absorbsion->absorption
 absorbtion->absorption
+absymal->abysmal
+abudance->abundance
 abundacies->abundances
 abundancies->abundances
 abundunt->abundant
@@ -31,7 +40,10 @@ accelleration->acceleration
 accension->accession, ascension,
 acceptence->acceptance
 acceptible->acceptable
+acces->access
+accesories->accessories
 accessable->accessible
+accidant->accident
 accidentaly->accidentally
 accidently->accidentally
 acclimitization->acclimatization
@@ -48,6 +60,7 @@ accomodates->accommodates
 accomodating->accommodating
 accomodation->accommodation
 accomodations->accommodations
+accompagnied->accompanied
 accompanyed->accompanied
 accordeon->accordion
 accordian->accordion
@@ -70,10 +83,11 @@ acheivment->achievement
 acheivments->achievements
 achievment->achievement
 achievments->achievements
-achive->achieve
+achive->achieve, archive,
 achived->achieved, archived,
 achivement->achievement
 achivements->achievements
+acident->accident
 acknowldeged->acknowledged
 acknowledgeing->acknowledging
 ackward->awkward, backward,
@@ -97,8 +111,11 @@ acuracy->accuracy
 acused->accused
 acustom->accustom
 acustommed->accustomed
+acutal->actual
 adavanced->advanced
 adbandon->abandon
+addional->additional
+addionally->additionally
 additinally->additionally
 additionaly->additionally
 additon->addition
@@ -179,6 +196,7 @@ agreeement->agreement
 agreemnt->agreement
 agregate->aggregate
 agregates->aggregates
+agregation->aggregation
 agreing->agreeing
 agression->aggression
 agressive->aggressive
@@ -204,6 +222,7 @@ alchol->alcohol
 alcholic->alcoholic
 alcohal->alcohol
 alcoholical->alcoholic
+aleady->already
 aledge->allege
 aledged->alleged
 aledges->alleges
@@ -214,6 +233,7 @@ algebraical->algebraic
 algorhitms->algorithms
 algoritm->algorithm
 algoritms->algorithms
+algorythm->algorithm
 alientating->alienating
 alledge->allege
 alledged->alleged
@@ -233,15 +253,16 @@ allready->already
 allthough->although
 alltime->all-time
 alltogether->altogether
+allways->always
 almsot->almost
 alochol->alcohol
 alomst->almost
-alot->a lot
+alot->a lot, allot,
 alotted->allotted
 alowed->allowed
 alowing->allowing
 alreayd->already
-alse->else
+alse->also, else
 alsot->also
 alternitives->alternatives
 altho->although
@@ -276,6 +297,7 @@ anarchistm->anarchism
 anbd->and
 ancestory->ancestry
 ancilliary->ancillary
+andd->and
 androgenous->androgynous
 androgeny->androgyny
 anihilation->annihilation
@@ -324,6 +346,7 @@ appealling->appealing, appalling,
 appeareance->appearance
 appearence->appearance
 appearences->appearances
+appeneded->appended
 appenines->apennines, Apennines,
 apperance->appearance
 apperances->appearances
@@ -351,6 +374,7 @@ approximitely->approximately
 aprehensive->apprehensive
 apropriate->appropriate
 apropriately->appropriately
+aproval->approval
 aproximate->approximate
 aproximately->approximately
 aquaduct->aqueduct
@@ -366,6 +390,7 @@ aranged->arranged
 arangement->arrangement
 arbitarily->arbitrarily
 arbitary->arbitrary
+archaelogical->archaeological
 archaelogists->archaeologists
 archaelogy->archaeology
 archaoelogy->archeology, archaeology,
@@ -393,6 +418,7 @@ argubly->arguably
 arguement->argument
 arguements->arguments
 arised->arose
+arithmentic->arithmetic
 arival->arrival
 armamant->armament
 armistace->armistice
@@ -401,7 +427,10 @@ arogent->arrogant
 aroud->around
 arrangment->arrangement
 arrangments->arrangements
+arrengement->arrangement
+arrengements->arrangements
 arround->around
+artcile->article
 artical->article
 artice->article
 articel->article
@@ -412,6 +441,7 @@ arund->around
 asetic->ascetic
 asfar->as far
 asign->assign
+asigned->assigned
 aslo->also
 asociated->associated
 asorbed->absorbed
@@ -431,12 +461,14 @@ asside->aside
 assisnate->assassinate
 assit->assist
 assitant->assistant
+assocaited->associated
 assocation->association
 assoicate->associate
 assoicated->associated
 assoicates->associates
 assosication->assassination
 asssassans->assassins
+asssertion->assertion
 assualt->assault
 assualted->assaulted
 assymetric->asymmetric
@@ -447,6 +479,7 @@ asthetical->aesthetical
 asthetically->aesthetically
 asume->assume
 aswell->as well
+asychronously->asynchronously
 atain->attain
 atempting->attempting
 atheistical->atheistic
@@ -478,6 +511,7 @@ auromated->automated
 austrailia->Australia
 austrailian->Australian
 auther->author
+authetication->authentication
 authobiographic->autobiographic
 authobiography->autobiography
 authorative->authoritative
@@ -495,6 +529,7 @@ automonomous->autonomous
 autor->author
 autority->authority
 auxilary->auxiliary
+auxiliar->auxiliary
 auxillaries->auxiliaries
 auxillary->auxiliary
 auxilliaries->auxiliaries
@@ -536,12 +571,14 @@ beautyfull->beautiful
 becamae->became
 becames->becomes, became,
 becasue->because
+becausee->because
 beccause->because
 becomeing->becoming
 becomming->becoming
 becouse->because
 becuase->because
 bedore->before
+beeing->being
 befoer->before
 beggin->begin, begging,
 begginer->beginner
@@ -575,12 +612,13 @@ Bernouilli->Bernoulli
 beseige->besiege
 beseiged->besieged
 beseiging->besieging
+beteen->between
 betwen->between
 beween->between
 bewteen->between
+bianry->binary
 bilateraly->bilaterally
 billingualism->bilingualism
-bianry->binary
 binominal->binomial
 bizzare->bizarre
 blaim->blame
@@ -606,6 +644,7 @@ breif->brief
 breifly->briefly
 brethen->brethren
 bretheren->brethren
+brigth->bright
 briliant->brilliant
 brillant->brilliant
 brimestone->brimstone
@@ -621,9 +660,11 @@ buisnessman->businessman
 buoancy->buoyancy
 buring->burying, burning, burin, during,
 burried->buried
+busines->business
 busineses->business, businesses,
 busness->business
 bussiness->business
+cacheing->caching
 caculater->calculator
 cacuses->caucuses
 cahracters->characters
@@ -631,6 +672,7 @@ calaber->caliber
 calander->calendar, calender, colander,
 calculater->calculator
 calculs->calculus
+calender->calendar
 calenders->calendars
 caligraphy->calligraphy
 caluclate->calculate
@@ -642,8 +684,11 @@ calulated->calculated
 calulater->calculator
 Cambrige->Cambridge
 camoflage->camouflage
+campagin->campaign
 campain->campaign
+campaing->campaign
 campains->campaigns
+cancelation->cancellation
 candadate->candidate
 candiate->candidate
 candidiate->candidate
@@ -663,6 +708,7 @@ captial->capital
 captued->captured
 capturd->captured
 carachter->character
+caracter->character
 caracterized->characterized
 carcas->carcass, Caracas,
 carefull->careful
@@ -690,6 +736,10 @@ casette->cassette
 casion->caisson
 cassawory->cassowary
 cassowarry->cassowary
+casue->cause
+casued->caused
+casues->causes
+casuing->causing
 casulaties->casualties
 casulaty->casualty
 catagories->categories
@@ -715,6 +765,8 @@ catterpilars->caterpillars
 catterpillar->caterpillar
 catterpillars->caterpillars
 cattleship->battleship
+caugt->caught
+cauhgt->caught
 causalities->casualties
 Ceasar->Caesar
 Celcius->Celsius
@@ -728,7 +780,9 @@ censur->censor, censure,
 cententenial->centennial
 centruies->centuries
 centruy->century
-ceratin->certain
+centuties->centuries
+centuty->century
+ceratin->certain, keratin,
 cerimonial->ceremonial
 cerimonies->ceremonies
 cerimonious->ceremonious
@@ -743,6 +797,7 @@ challanged->challenged
 challege->challenge
 Champange->Champagne
 changable->changeable
+characer->character
 charachter->character
 charachters->characters
 charactersistic->characteristic
@@ -764,6 +819,7 @@ childbird->childbirth
 childen->children
 choosed->chose, chosen,
 choosen->chosen
+chould->should, could,
 chracter->character
 chuch->church
 churchs->churches
@@ -790,7 +846,9 @@ clinicaly->clinically
 cmo->com, disabled due to lots of false positives
 cmoputer->computer
 co-incided->coincided
+Coca Cola->Coca-Cola
 coctail->cocktail
+codespel->codespell
 coform->conform
 cognizent->cognizant
 coincedentally->coincidentally
@@ -815,6 +873,7 @@ comapany->company
 comback->comeback
 combanations->combinations
 combinatins->combinations
+combinded->combined
 combusion->combustion
 comdemnation->condemnation
 comemmorates->commemorates
@@ -858,6 +917,8 @@ committe->committee
 committment->commitment
 committments->commitments
 commmemorated->commemorated
+commnad->command
+commnads->commands
 commongly->commonly
 commonweath->commonwealth
 commuications->communications
@@ -865,7 +926,10 @@ commuinications->communications
 communciation->communication
 communiation->communication
 communites->communities
+comonent->component
 compability->compatibility
+compair->compare
+compairs->compares
 comparision->comparison
 comparisions->comparisons
 comparitive->comparative
@@ -877,6 +941,7 @@ compatablities->compatibilities
 compatablity->compatibility
 compatiable->compatible
 compatibilty->compatibility
+compatibily->compatibility
 compatiblities->compatibilities
 compatiblity->compatibility
 compeitions->competitions
@@ -955,7 +1020,7 @@ conservitive->conservative
 consiciousness->consciousness
 consicousness->consciousness
 considerd->considered
-consideres->considers
+consideres->considered, considers,
 consious->conscious
 consistant->consistent
 consistantly->consistently
@@ -981,6 +1046,7 @@ constituants->constituents
 constituion->constitution
 constituional->constitutional
 constructes->constructs
+construtor->constructor
 consttruction->construction
 constuction->construction
 consulant->consultant
@@ -994,15 +1060,19 @@ contempoary->contemporary
 contemporaneus->contemporaneous
 contempory->contemporary
 contendor->contender
+contian->contain
+contians->contains
 contibute->contribute
 contibuted->contributed
 contibutes->contributes
 contigent->contingent
 contigious->contiguous
 contined->continued
+continiously->continuously
 continous->continuous
 continously->continuously
 continueing->continuing
+contiuguous->contiguous
 contravercial->controversial
 contraversy->controversy
 contributer->contributor
@@ -1021,6 +1091,7 @@ controversey->controversy
 controvertial->controversial
 controvery->controversy
 contruction->construction
+contstruction->construction
 conveinent->convenient
 convenant->covenant
 convential->conventional
@@ -1030,6 +1101,7 @@ convery->convert
 conveyer->conveyor
 conviced->convinced
 convienient->convenient
+convinience->convenience
 coordiantion->coordination
 coorperation->cooperation, corporation,
 coorperations->corporations
@@ -1044,6 +1116,7 @@ corperations->corporations
 correcters->correctors
 correponding->corresponding
 correposding->corresponding
+correspoinding->corresponding
 correspondant->correspondent
 correspondants->correspondents
 corresponsing->corresponding
@@ -1094,17 +1167,22 @@ culiminating->culminating
 cumulatative->cumulative
 curch->church
 curcuit->circuit
+curcumstance->circumstance
+curcumstances->circumstances
 currenly->currently
 curriculem->curriculum
 cxan->cyan
 cyclinder->cylinder
 dacquiri->daiquiri
+daed->dead
 dael->deal, dial, dahl,
 dalmation->dalmatian
 damenor->demeanor
 dammage->damage
 Dardenelles->Dardanelles
 daugher->daughter
+deamon->daemon
+deamons->daemons
 debateable->debatable
 decendant->descendant
 decendants->descendants
@@ -1146,6 +1224,7 @@ delagates->delegates
 delapidated->dilapidated
 delerious->delirious
 delevopment->development
+delevopp->develop
 deliberatly->deliberately
 delusionally->delusively
 demenor->demeanor
@@ -1158,13 +1237,19 @@ densly->densely
 deparment->department
 deparmental->departmental
 deparments->departments
+depdendencies->dependencies
+depdendency->dependency
 dependance->dependence
+dependancies->dependencies
 dependancy->dependency
 dependant->dependent
+deployement->deployment
 deram->dram, dream,
+derectory->directory
 deriviated->derived
 derivitive->derivative
 derogitory->derogatory
+derprecated->deprecated
 descendands->descendants
 descibed->described
 descision->decision
@@ -1188,10 +1273,12 @@ desoriented->disoriented
 desparate->desperate, disparate,
 despict->depict
 despiration->desperation
+desription->description
 dessicated->desiccated
 dessigned->designed
 destablized->destabilized
 destory->destroy
+destuction->destruction
 detailled->detailed
 detatched->detached
 deteoriated->deteriorated
@@ -1207,7 +1294,6 @@ develpment->development
 devels->delves
 devestated->devastated
 devestating->devastating
-deviatio->deviation
 devide->divide
 devided->divided
 devistating->devastating
@@ -1222,9 +1308,11 @@ dicovered->discovered
 dicovering->discovering
 dicovers->discovers
 dicovery->discovery
+dictionarys->dictionaries
+dictionnary->dictionary
 dicussed->discussed
-didnt'->didn't, didn\'t,
-didnt->didn't, didn\'t,
+didnt'->didn't
+didnt->didn't
 diea->idea, die,
 dieing->dying, dyeing,
 dieties->deities
@@ -1250,6 +1338,7 @@ diphtongs->diphthongs
 diplomancy->diplomacy
 dipthong->diphthong
 dipthongs->diphthongs
+directoty->directory
 dirived->derived
 disagreeed->disagreed
 disapeared->disappeared
@@ -1311,6 +1400,7 @@ distructive->destructive
 ditributed->distributed
 diversed->diverse, diverged,
 divice->device
+divinition->definition,divination,
 divison->division
 divisons->divisions
 doccument->document
@@ -1327,6 +1417,7 @@ dominaton->domination
 dominent->dominant
 dominiant->dominant
 donig->doing
+dont->don't
 dosen't->doesn't
 dosent'->doesn't
 doub->doubt, daub,
@@ -1341,11 +1432,13 @@ drnik->drink
 druming->drumming
 drummless->drumless
 dstination->destination
+dum->dumb
 dupicate->duplicate
 durig->during
 durring->during
 duting->during
 dyas->dryas
+dynamicaly->dynamically
 eahc->each
 ealier->earlier
 earlies->earliest
@@ -1376,16 +1469,19 @@ electon->election, electron,
 electrial->electrical
 electricly->electrically
 electricty->electricity
+elemenet->element
+elemenets->elements
 elementay->elementary
 eleminated->eliminated
 eleminating->eliminating
-eles->eels
+eles->else
 eletricity->electricity
 elicided->elicited
 eligable->eligible
 elimentary->elementary
 ellected->elected
 elphant->elephant
+emabaroged->embargoed
 embarass->embarrass
 embarassed->embarrassed
 embarassing->embarrassing
@@ -1424,6 +1520,7 @@ emphaised->emphasised
 emphsis->emphasis
 emphysyma->emphysema
 empirial->empirical, imperial,
+emporer->emperor
 emprisoned->imprisoned
 enameld->enameled
 enchancement->enhancement
@@ -1449,12 +1546,14 @@ enlargments->enlargements
 Enlish->English, enlist,
 enourmous->enormous
 enourmously->enormously
+enque->enqueue
 ensconsed->ensconced
 entaglements->entanglements
 enteratinment->entertainment
 enthousiasm->enthusiasm
 enthusiatic->enthusiastic
 entitity->entity
+entitiy->entity
 entitlied->entitled
 entrepeneur->entrepreneur
 entrepeneurs->entrepreneurs
@@ -1605,6 +1704,7 @@ exressed->expressed
 extemely->extremely
 extention->extension
 extentions->extensions
+exteral->external
 extered->exerted
 extermist->extremist
 extint->extinct, extant,
@@ -1628,6 +1728,7 @@ facillitate->facilitate
 facinated->fascinated
 facist->fascist
 familes->families
+familiies->families
 familliar->familiar
 famoust->famous
 fanatism->fanaticism
@@ -1639,6 +1740,7 @@ feasable->feasible
 Febuary->February
 Feburary->February
 fedreally->federally
+femminist->feminist
 feromone->pheromone
 fertily->fertility
 fianite->finite
@@ -1658,6 +1760,8 @@ financialy->financially
 firends->friends
 firts->flirts, first,
 fisionable->fissionable
+flaged->flagged
+flakyness->flakiness
 flamable->flammable
 flawess->flawless
 fleed->fled, freed,
@@ -1665,6 +1769,7 @@ Flemmish->Flemish
 florescent->fluorescent
 flourescent->fluorescent
 flourine->fluorine
+flourishment->flourishing
 fluorish->flourish
 follwoing->following
 folowing->following
@@ -1675,7 +1780,7 @@ fontrier->fontier
 foootball->football
 forbad->forbade
 forbiden->forbidden
-foreward->foreword
+foreward->foreword, forward,
 forfiet->forfeit
 forhead->forehead
 foriegn->foreign
@@ -1683,6 +1788,7 @@ Formalhaut->Fomalhaut
 formallize->formalize
 formallized->formalized
 formaly->formally, formerly,
+formated->formatted
 formelly->formerly
 formidible->formidable
 formost->foremost
@@ -1700,6 +1806,7 @@ fourties->forties
 fourty->forty
 fouth->fourth
 foward->forward
+fragement->fragment
 Fransiscan->Franciscan
 Fransiscans->Franciscans
 freind->friend
@@ -1715,6 +1822,9 @@ fufilled->fulfilled
 fulfiled->fulfilled
 fullfill->fulfill
 fullfilled->fulfilled
+funciton->function
+functino->function
+functionnality->functionality
 fundametal->fundamental
 fundametals->fundamentals
 funguses->fungi
@@ -1822,6 +1932,7 @@ habeus->habeas
 Habsbourg->Habsburg
 haemorrage->haemorrhage
 haev->have, heave,
+halarious->hilarious
 Hallowean->Hallowe'en, Halloween,
 halp->help
 hapen->happen
@@ -1874,6 +1985,7 @@ hertiage->heritage
 hertzs->hertz
 hesistant->hesitant
 heterogenous->heterogeneous
+hexidecimal->hexadecimal
 hieght->height
 hierachical->hierarchical
 hierachies->hierarchies
@@ -1895,6 +2007,7 @@ hismelf->himself
 histocompatability->histocompatibility
 historicians->historians
 hitsingles->hit singles
+holf->hold
 holliday->holiday
 homestate->home state
 homogeneize->homogenize
@@ -1960,6 +2073,7 @@ imagenary->imaginary
 imagin->imagine
 imaginery->imaginary, imagery,
 imanent->eminent, imminent,
+imcoming->incoming
 imcomplete->incomplete
 imediately->immediately
 imense->immense
@@ -1984,6 +2098,8 @@ impliment->implement
 implimented->implemented
 imploys->employs
 importamt->important
+impovements->improvements
+impressario->impresario
 imprioned->imprisoned
 imprisonned->imprisoned
 improvision->improvisation
@@ -2008,6 +2124,7 @@ incidently->incidentally
 inclreased->increased
 includ->include
 includng->including
+incomming->incoming
 incompatabilities->incompatibilities
 incompatability->incompatibility
 incompatable->incompatible
@@ -2020,6 +2137,7 @@ incompetant->incompetent
 incomptable->incompatible
 incomptetent->incompetent
 inconsistant->inconsistent
+inconsitent->inconsistent
 incoroporated->incorporated
 incorperation->incorporation
 incorportaed->incorporated
@@ -2028,6 +2146,7 @@ incorruptable->incorruptible
 incramentally->incrementally
 increadible->incredible
 incredable->incredible
+incremeantal->incremental
 inctroduce->introduce
 inctroduced->introduced
 incuding->including
@@ -2082,15 +2201,22 @@ informtion->information
 infrantryman->infantryman
 infrigement->infringement
 ingenius->ingenious
+ingration->integration
 ingreediants->ingredients
 inhabitans->inhabitants
 inherantly->inherently
 inheritage->heritage, inheritance,
 inheritence->inheritance
+inifinite->infinite
 inital->initial
+initalise->initialise
+initalization->initialization
+initalize->initialize
+initalizer->initializer
 initally->initially
 initation->initiation
 initiaitive->initiative
+initializiation->initialization
 initilize->initialize
 inlcuding->including
 inmigrant->immigrant
@@ -2105,6 +2231,7 @@ inprisonment->imprisonment
 inproving->improving
 insectiverous->insectivorous
 insensative->insensitive
+insensetive->insensitive
 inseperable->inseparable
 insistance->insistence
 insitution->institution
@@ -2119,6 +2246,7 @@ instuments->instruments
 instutionalized->institutionalized
 instutions->intuitions
 insurence->insurance
+intead->instead
 intelectual->intellectual
 inteligence->intelligence
 inteligent->intelligent
@@ -2126,10 +2254,12 @@ intenational->international
 intented->intended, indented,
 intepretation->interpretation
 intepretator->interpretor
+interal->internal, integral,
 interational->international
 interbread->interbreed, interbred,
 interchangable->interchangeable
 interchangably->interchangeably
+intercontinential->intercontinental
 intercontinetal->intercontinental
 intered->interred, interned,
 interelated->interrelated
@@ -2149,6 +2279,7 @@ interupt->interrupt
 intervines->intervenes
 intevene->intervene
 intial->initial
+intialization->initialization
 intially->initially
 intrduced->introduced
 intrest->interest
@@ -2163,6 +2294,7 @@ intutive->intuitive
 intutively->intuitively
 inudstry->industry
 inumerable->enumerable, innumerable,
+invaild->invalid
 inventer->inventor
 invertibrates->invertebrates
 investingate->investigate
@@ -2184,6 +2316,7 @@ isnt'->isn't
 isnt->isn't
 Israelies->Israelis
 issueing->issuing
+itnernal->internal
 itnroduced->introduced
 iunior->junior
 iwll->will
@@ -2195,6 +2328,7 @@ jaques->jacques
 jeapardy->jeopardy
 jewllery->jewellery
 Johanine->Johannine
+jorunal->journal
 Jospeh->Joseph
 jouney->journey
 journied->journeyed
@@ -2287,6 +2421,7 @@ liuke->like
 livley->lively
 lmits->limits
 loev->love
+loger->logger, longer,
 lonelyness->loneliness
 longitudonal->longitudinal
 longuer->longer
@@ -2303,6 +2438,8 @@ maching->machine, marching, matching,
 mackeral->mackerel
 magasine->magazine
 magincian->magician
+magisine->magazine
+magizine->magazine
 magnificient->magnificent
 magolia->magnolia
 mailny->mainly
@@ -2329,7 +2466,7 @@ manisfestations->manifestations
 mannor->manner
 manoeuverability->maneuverability
 manouver->maneuver, manoeuvre,
-manouverability->maneuverability, manoeuvrability, maneuverability,
+manouverability->maneuverability, manoeuvrability, manoeuverability,
 manouverable->maneuverable, manoeuvrable,
 manouvers->maneuvers, manoeuvres,
 mantained->maintained
@@ -2343,7 +2480,7 @@ manuver->maneuver
 mapp->map
 mariage->marriage
 marjority->majority
-markes->marks
+markes->marks, marked,
 marketting->marketing
 marmelade->marmalade
 marrage->marriage
@@ -2403,6 +2540,7 @@ milennia->millennia
 milennium->millennium
 mileu->milieu
 miliary->military
+miligram->milligram
 milion->million
 miliraty->military
 millenia->millennia
@@ -2417,7 +2555,9 @@ miltary->military
 minature->miniature
 minerial->mineral
 MingGW->MinGW
+miniscule->minuscule
 ministery->ministry
+minsitry->ministry
 minstries->ministries
 minstry->ministry
 minumum->minimum
@@ -2450,7 +2590,6 @@ mkae->make
 mkaes->makes
 mkaing->making
 mkea->make
-modeled->modelled
 moderm->modem
 modle->model
 moduel->module
@@ -2530,6 +2669,7 @@ necessiate->necessitate
 negitive->negative
 neglible->negligible
 negligable->negligible
+negligble->negligible
 negociate->negotiate
 negociation->negotiation
 negociations->negotiations
@@ -2559,6 +2699,7 @@ nkow->know
 nkwo->know
 nmae->name
 noncombatents->noncombatants
+nonexistant->nonexistent
 nonsence->nonsense
 nontheless->nonetheless
 noone->no one
@@ -2575,6 +2716,7 @@ noticable->noticeable
 noticably->noticeably
 noticeing->noticing
 noticible->noticeable
+notifcation->notification
 notwhithstanding->notwithstanding
 noveau->nouveau
 Novermber->November
@@ -2625,6 +2767,7 @@ occure->occur
 occured->occurred
 occurence->occurrence
 occurences->occurrences
+occures->occurs
 occuring->occurring
 occurr->occur
 occurrance->occurrence
@@ -2649,6 +2792,7 @@ officialy->officially
 offred->offered
 oftenly->often
 oging->going, ogling,
+olny->only
 omision->omission
 omited->omitted
 omiting->omitting
@@ -2691,8 +2835,9 @@ organistion->organisation
 organiztion->organization
 orgin->origin, organ,
 orginal->original
+orginal->orignal
 orginally->originally
-orginize->organise
+orginize->organise, organize,
 oridinarily->ordinarily
 origanaly->originally
 originall->original, originally,
@@ -2707,6 +2852,8 @@ oublisher->publisher
 ouevre->oeuvre
 oustanding->outstanding
 oveerun->overrun
+overlayed->overlaid
+overriddden->overridden
 overshaddowed->overshadowed
 overthere->over there
 overwelming->overwhelming
@@ -2733,12 +2880,14 @@ paralel->parallel
 paralell->parallel
 paralelly->parallelly
 paralely->parallelly
+parallell->parallel
 parallely->parallelly
 paramter->parameter
 paramters->parameters
 paranthesis->parenthesis
 paraphenalia->paraphernalia
 parellels->parallels
+parisitic->parasitic
 parituclar->particular
 parliment->parliament
 parrakeets->parakeets
@@ -2782,6 +2931,7 @@ penninsular->peninsular
 pennisula->peninsula
 Pennyslvania->Pennsylvania
 pensinula->peninsula
+pensle->pencil
 peom->poem
 peoms->poems
 peopel->people
@@ -2795,6 +2945,9 @@ perfomance->performance
 perfomers->performers
 performence->performance
 performes->performed, performs,
+perfrom->perform
+perfromance->performance
+perfroms->performs
 perhasp->perhaps
 perheaps->perhaps
 perhpas->perhaps
@@ -2809,6 +2962,7 @@ permissable->permissible
 perogative->prerogative
 peronal->personal
 perosnality->personality
+perpertrated->perpetrated
 perphas->perhaps
 perpindicular->perpendicular
 perseverence->perseverance
@@ -2847,7 +3001,9 @@ philosphies->philosophies
 philosphy->philosophy
 Phonecian->Phoenecian
 phongraph->phonograph
+phsyically->physically
 phylosophical->philosophical
+physcial->physical
 physicaly->physically
 piblisher->publisher
 pich->pitch
@@ -2856,6 +3012,8 @@ pilgrimmages->pilgrimages
 pinapple->pineapple
 pinnaple->pineapple
 pinoneered->pioneered
+plaftorm->platform
+plaftorms->platforms
 plagarism->plagiarism
 planation->plantation
 planed->planned
@@ -2895,6 +3053,8 @@ popoulation->population
 popularaty->popularity
 populare->popular
 populer->popular
+porshan->portion
+porshon->portion
 portait->portrait
 portayed->portrayed
 portraing->portraying
@@ -2962,7 +3122,11 @@ predicitons->predictions
 predomiantly->predominately
 prefered->preferred
 prefering->preferring
+preferrable->preferable
 preferrably->preferably
+preform->perform
+preformance->performance
+preforms->performs
 pregancies->pregnancies
 preiod->period
 preliferation->proliferation
@@ -2980,10 +3144,12 @@ preperation->preparation
 preperations->preparations
 prepresent->represent
 preriod->period
+presance->presence
 presedential->presidential
 presense->presence
 presidenital->presidential
 presidental->presidential
+presist->persist
 presitgious->prestigious
 prespective->perspective
 prestigeous->prestigious
@@ -3002,6 +3168,12 @@ primative->primitive
 primatively->primitively
 primatives->primitives
 primordal->primordial
+principaly->principality
+principial->principal
+principlaity->principality
+principly->principally
+prinicipal->principal
+printting->printing
 privalege->privilege
 privaleges->privileges
 priveledges->privileges
@@ -3047,6 +3219,7 @@ proffesional->professional
 proffesor->professor
 profilic->prolific
 progessed->progressed
+progidy->prodigy
 programable->programmable
 progrom->pogrom, program,
 progroms->pogroms, programs,
@@ -3057,6 +3230,7 @@ prominant->prominent
 prominantly->prominently
 prominately->prominently, predominately,
 promiscous->promiscuous
+promiss->promise
 promotted->promoted
 pronomial->pronominal
 pronouced->pronounced
@@ -3153,12 +3327,14 @@ rarified->rarefied
 reaccurring->recurring
 reacing->reaching
 reacll->recall
+readible->readable
 readmition->readmission
 realitvely->relatively
 realsitic->realistic
 realtions->relations
 realy->really
 realyl->really
+reaons->reasons
 reasearch->research
 rebiulding->rebuilding
 rebllions->rebellions
@@ -3203,6 +3379,7 @@ recomended->recommended
 recomending->recommending
 recomends->recommends
 recommedations->recommendations
+recompence->recompense
 reconaissance->reconnaissance
 reconcilation->reconciliation
 reconized->recognized
@@ -3281,6 +3458,7 @@ relinqushment->relinquishment
 relitavely->relatively
 relized->realised, realized,
 relpacement->replacement
+relys->relies
 remaing->remaining
 remeber->remember
 rememberable->memorable
@@ -3304,6 +3482,10 @@ rentors->renters
 reoccurrence->recurrence
 reorganision->reorganisation
 repatition->repetition, repartition,
+repblic->republic
+repblican->republican
+repblicans->republicans
+repblics->republics
 repectively->respectively
 repeition->repetition
 repentence->repentance
@@ -3316,12 +3498,27 @@ reponses->responses
 reponsible->responsible
 reportadly->reportedly
 represantative->representative
+representiative->representative
 representive->representative
 representives->representatives
+represnted->represented
+reproducabely->reproducibly
 reproducable->reproducible
 reprtoire->repertoire
 repsectively->respectively
 reptition->repetition
+repubic->republic
+repubican->republican
+repubicans->republicans
+repubics->republics
+republi->republic
+republian->republican
+republians->republicans
+republis->republics
+repulic->republic
+repulican->republican
+repulicans->republicans
+repulics->republics
 reqest->request
 requirment->requirement
 requred->required
@@ -3335,6 +3532,7 @@ resignement->resignment
 resistable->resistible
 resistence->resistance
 resistent->resistant
+resopnse->response
 respectivly->respectively
 responce->response
 responibilities->responsibilities
@@ -3376,11 +3574,14 @@ resurecting->resurrecting
 retalitated->retaliated
 retalitation->retaliation
 retreive->retrieve
+retuns->returns
 returnd->returned
 revaluated->reevaluated
 reveiw->review
 reveral->reversal
 reversable->reversible
+revison->revision
+revisons->revisions
 revolutionar->revolutionary
 rewitten->rewritten
 rewriet->rewrite
@@ -3416,6 +3617,7 @@ rythmic->rhythmic
 rythyms->rhythms
 sacrafice->sacrifice
 sacreligious->sacrilegious
+Sacremento->Sacramento
 sacrifical->sacrificial
 saftey->safety
 safty->safety
@@ -3447,6 +3649,7 @@ scavanged->scavenged
 schedual->schedule
 scholarhip->scholarship
 scholarstic->scholastic, scholarly,
+schould->should
 scientfic->scientific
 scientifc->scientific
 scientis->scientist
@@ -3455,6 +3658,7 @@ scinece->science
 scirpt->script
 scoll->scroll
 screenwrighter->screenwriter
+scriping->scripting
 scrutinity->scrutiny
 scuptures->sculptures
 seach->search
@@ -3468,6 +3672,7 @@ secretery->secretary
 sedereal->sidereal
 seeked->sought
 segementation->segmentation
+segmentaion->segmentation
 seguoys->segues
 seige->siege
 seing->seeing
@@ -3477,6 +3682,7 @@ senarios->scenarios
 sence->sense, since,
 senstive->sensitive
 sensure->censure
+sepcial->special
 seperate->separate
 seperated->separated
 seperately->separately
@@ -3489,6 +3695,7 @@ sepina->subpoena
 sepulchure->sepulchre, sepulcher,
 sepulcre->sepulchre, sepulcher,
 sergent->sergeant
+setted->set
 settelement->settlement
 settlment->settlement
 severeal->several
@@ -3499,6 +3706,7 @@ shadasloo->shadaloo
 shaddow->shadow
 shadoloo->shadaloo
 shamen->shaman, shamans,
+shashes->slashes
 sheat->sheath, sheet, cheat,
 sheild->shield
 sherif->sheriff
@@ -3550,6 +3758,7 @@ Skagerak->Skagerrak
 skateing->skating
 slaugterhouses->slaughterhouses
 slighly->slightly
+slippy->slippery
 slowy->slowly
 smae->same
 smealting->smelting
@@ -3570,6 +3779,7 @@ soluable->soluble
 somene->someone
 somtimes->sometimes
 somwhere->somewhere
+sonething->something
 sophicated->sophisticated
 sophmore->sophomore
 sorceror->sorcerer
@@ -3582,6 +3792,7 @@ sould->could, should, sold,
 sountrack->soundtrack
 sourth->south
 sourthern->southern
+southbrige->southbridge
 souvenier->souvenir
 souveniers->souvenirs
 soveits->soviets
@@ -3616,6 +3827,7 @@ sprech->speech
 spred->spread
 spriritual->spiritual
 spritual->spiritual
+spurrious->spurious
 sqaure->square
 stablility->stability
 stainlees->stainless
@@ -3662,6 +3874,7 @@ strictist->strictest
 strikely->strikingly
 strnad->strand
 stroy->story, destroy,
+structered->structured
 structual->structural
 stubborness->stubbornness
 stucture->structure
@@ -3690,6 +3903,7 @@ substracted->subtracted
 substracting->subtracting
 substraction->subtraction
 substracts->subtracts
+subsytems->subsystems
 subtances->substances
 subterranian->subterranean
 suburburban->suburban
@@ -3704,6 +3918,7 @@ succesfuly->successfully
 succesion->succession
 succesive->successive
 successfull->successful
+successfuly->successfully
 successully->successfully
 succsess->success
 succsessfull->successful
@@ -3809,11 +4024,13 @@ syncronization->synchronization
 synonomous->synonymous
 synonymns->synonyms
 synphony->symphony
+synronous->synchronous
 syphyllis->syphilis
 sypmtoms->symptoms
 syrap->syrup
 sysmatically->systematically
 sytem->system
+sytems->systems
 sytle->style
 tabacco->tobacco
 tahn->than
@@ -3861,6 +4078,7 @@ territorist->terrorist
 territoy->territory
 terroist->terrorist
 testiclular->testicular
+testomony->testimony
 tghe->the
 thast->that, that's,
 theather->theater
@@ -3869,10 +4087,11 @@ theif->thief
 theives->thieves
 themselfs->themselves
 themslves->themselves
-ther->there, their, the,
+ther->there, their, the, other,
 therafter->thereafter
 therby->thereby
 theri->their
+theshold->threshold
 theyre->they're,
 thgat->that
 thge->the
@@ -3899,7 +4118,8 @@ throrough->thorough
 throughly->thoroughly
 throught->thought, through, throughout,
 througout->throughout
-ths->this
+thru->through
+ths->the, this,
 thsi->this
 thsoe->those
 thta->that
@@ -3964,7 +4184,12 @@ transistion->transition
 translater->translator
 translaters->translators
 transmissable->transmissible
+transmited->transmitted
 transporation->transportation
+transtion->transition
+trasaction->transaction
+trascation->transaction
+trasnaction->transaction
 tremelo->tremolo
 tremelos->tremolos
 triguered->triggered
@@ -4019,6 +4244,7 @@ undreground->underground
 uneccesary->unnecessary
 unecessary->unnecessary
 unequalities->inequalities
+unflaged->unflagged
 unforetunately->unfortunately
 unforgetable->unforgettable
 unforgiveable->unforgivable
@@ -4061,7 +4287,7 @@ unregnized->unrecognized
 unrepentent->unrepentant
 unrepetant->unrepentant
 unrepetent->unrepentant
-unsed->unused
+unsed->unused, used
 unsubstanciated->unsubstantiated
 unsuccesful->unsuccessful
 unsuccesfully->unsuccessfully
@@ -4088,8 +4314,10 @@ unviersity->university
 unwarrented->unwarranted
 unweildly->unwieldy
 unwieldly->unwieldy
+unwrritten->unwritten
 upcomming->upcoming
 upgradded->upgraded
+upsteam->upstream
 upto->up to
 usally->usually
 useage->usage
@@ -4146,8 +4374,10 @@ violentce->violence
 virtualy->virtually
 virutal->virtual
 virutally->virtually
+visability->visibility
 visable->visible
 visably->visibly
+visibile->visible
 visting->visiting
 vistors->visitors
 vitories->victories
@@ -4170,9 +4400,10 @@ vulnerablility->vulnerability
 vyer->very
 vyre->very
 waht->what
-wanna->want to, disabled because one might want to allow informal pronunciation
+wanna->want to, disabled because one might want to allow informal spelling
 warantee->warranty
 wardobe->wardrobe
+waring->warning
 warrent->warrant
 warrriors->warriors
 wasnt'->wasn't
@@ -4213,6 +4444,7 @@ wih->with
 wiht->with
 wille->will
 willingless->willingness
+willk->will
 wirting->writing
 withdrawl->withdrawal, withdraw,
 witheld->withheld
@@ -4229,11 +4461,13 @@ wohle->whole
 wokr->work
 wokring->working
 wonderfull->wonderful
+wont->won't, wont,
 wordlwide->worldwide
 workststion->workstation
 worls->world
 worstened->worsened
 woudl->would
+wouldnt->wouldn't
 wresters->wrestlers
 wriet->write
 writen->written
@@ -4259,6 +4493,7 @@ yersa->years
 yotube->youtube
 youseff->yousef
 youself->yourself
+yrea->year
 ytou->you
 yuo->you
 zeebra->zebra
diff --git a/doc/Makefile b/doc/Makefile
index 7053c66..d236bd1 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -15,18 +15,19 @@ ALLSPHINXOPTS   = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 
 help:
 	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html_stable  to make standalone HTML files (stable version)"
-	@echo "  html_dev     to make standalone HTML files (dev version)"
-	@echo "  *-noplot     to make standalone HTML files without plotting"
-	@echo "  dirhtml      to make HTML files named index.html in directories"
-	@echo "  pickle       to make pickle files"
-	@echo "  json         to make JSON files"
-	@echo "  htmlhelp     to make HTML files and a HTML help project"
-	@echo "  qthelp       to make HTML files and a qthelp project"
-	@echo "  latex        to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  changes      to make an overview of all changed/added/deprecated items"
-	@echo "  linkcheck    to check all external links for integrity"
-	@echo "  doctest      to run all doctests embedded in the documentation (if enabled)"
+	@echo "  html_stable      to make standalone HTML files (stable version)"
+	@echo "  html_dev         to make standalone HTML files (dev version)"
+	@echo "  html_dev-pattern to make standalone HTML files for one example dir (dev version)"
+	@echo "  *-noplot     	  to make standalone HTML files without plotting"
+	@echo "  dirhtml          to make HTML files named index.html in directories"
+	@echo "  pickle           to make pickle files"
+	@echo "  json             to make JSON files"
+	@echo "  htmlhelp         to make HTML files and a HTML help project"
+	@echo "  qthelp           to make HTML files and a qthelp project"
+	@echo "  latex            to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes          to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck        to check all external links for integrity"
+	@echo "  doctest          to run all doctests embedded in the documentation (if enabled)"
 
 clean:
 	-rm -rf _build/*
@@ -38,12 +39,17 @@ clean:
 	-rm -rf *.nii.gz
 
 html_stable:
-	$(SPHINXBUILD) -D raise_gallery=1 -b html $(ALLSPHINXOPTS) build/html_stable
+	$(SPHINXBUILD) -D raise_gallery=1 -b html $(ALLSPHINXOPTS) _build/html_stable
 	@echo
 	@echo "Build finished. The HTML pages are in _build/html_stable."
 
 html_dev:
-	BUILD_DEV_HTML=1 $(SPHINXBUILD) -D raise_gallery=1 -b html $(ALLSPHINXOPTS) _build/html
+	BUILD_DEV_HTML=1 $(SPHINXBUILD) -D raise_gallery=1 -D abort_on_example_error=1 -b html $(ALLSPHINXOPTS) _build/html
+	@echo
+	@echo "Build finished. The HTML pages are in _build/html"
+
+html_dev-pattern:
+	BUILD_DEV_HTML=1 $(SPHINXBUILD) -D plot_gallery=1 -D sphinx_gallery_conf.filename_pattern=$(PATTERN) -b html $(ALLSPHINXOPTS) _build/html
 	@echo
 	@echo "Build finished. The HTML pages are in _build/html"
 
diff --git a/doc/_static/style.css b/doc/_static/style.css
index 858288b..e1e9e25 100644
--- a/doc/_static/style.css
+++ b/doc/_static/style.css
@@ -7,12 +7,32 @@ body {
 }
 
 a {
-    color: rgb(255,2,16);
+    color: rgb(41, 122, 204);
     text-decoration: none;
 }
 
 a:hover {
-    color: rgb(255,2,16);
+    color: rgb(102, 179, 255);
+}
+
+h2 a {
+    color: rgb(0,0,0);
+    text-decoration: none;
+}
+
+h1 {
+    font-weight: 1000;
+}
+
+h2 {
+    background-color: rgb(255,255,255);
+    font-size: 140%;
+    font-weight: 800;
+}
+
+h3 {
+    font-size: 110%;
+    font-weight: 600;
 }
 
 blockquote {
@@ -20,12 +40,12 @@ blockquote {
 }
 
 code {
-    color: #49759c !important;
-    background-color: #f3f5f9 !important;
+    color: rgb(61, 107, 153) !important;
+    background-color: rgb(230, 242, 255) !important;
 }
 
 .devbar {
-    background-color: red;
+    background-color: rgb(242, 80, 80);
     color: white;
     font-weight:bold;
     text-align: center;
@@ -34,13 +54,82 @@ code {
 }
 
 .devbar a {
-    color: #b8bec4;
+    color: rgb(255, 238, 0);
+    text-decoration: none;
+}
+
+.devbar a:hover {
+    color: black;
+    text-decoration: underline;
 }
 
 #navbar a:hover {
-    color: rgb(255,2,16);
+    color: rgb(242, 80, 80);
 }
 
 .note a {
-    color: rgb(255,236,0);
-}
\ No newline at end of file
+    color: rgb(0,0,255);
+}
+
+.note {
+    background-color: rgb(204, 230, 255);
+    color: rgb(0, 0, 0);
+    border: none;
+}
+
+.note pre {
+    background-color: rgb(242, 249, 255);
+}
+
+blockquote {
+  padding: 0 0 0 15px;
+  margin: 0 0 20px;
+  border-left: 5px solid #eeeeee;
+}
+
+.span.box {
+    width: 47%;
+    height: 230px;
+    float: left;
+    border-radius: 20px;
+    background: rgb(242, 249, 255);
+    border-style: solid;
+    border-color: rgb(143, 173, 204);
+    margin: 5px;
+    padding: 0px 0px 0px 10px;
+}
+
+.span h2 {
+    background: rgb(242, 249, 255);
+}
+
+.span.box code {
+    background-color: rgb(204, 230, 255) !important;
+}
+
+/* OVERRIDES */
+.sphx-glr-download {
+    background-color: rgb(204, 230, 255);
+    border-radius: 10px;
+    border-style: solid;
+    border-color: rgb(143, 173, 204);
+    max-width: 55ex;
+}
+.highlight pre {
+    background-color: rgb(242, 249, 255) !important;
+}
+.warning {
+    background-color: rgb(230, 57, 57);
+    color: white;
+}
+.warning a {
+    color: rgb(255, 238, 0);
+}
+dt:target, .highlighted {
+    background-color: rgb(255, 238, 0);
+}
+dt:target code {
+    color: inherit !important;
+    background-color: inherit !important;
+}
+
diff --git a/doc/_templates/function.rst b/doc/_templates/function.rst
index 317222f..bdde242 100644
--- a/doc/_templates/function.rst
+++ b/doc/_templates/function.rst
@@ -5,4 +5,8 @@
 
 .. autofunction:: {{ objname }}
 
+.. include:: {{module}}.{{objname}}.examples
 
+.. raw:: html
+
+    <div style='clear:both'></div>
diff --git a/doc/advanced_setup.rst b/doc/advanced_setup.rst
deleted file mode 100644
index 3cf5093..0000000
--- a/doc/advanced_setup.rst
+++ /dev/null
@@ -1,163 +0,0 @@
-.. _detailed_notes:
-
-Advanced installation and setup
-===============================
-
-MNE is written in pure Python making it easy to setup on
-any machine with Python >=2.6, NumPy >= 1.6, SciPy >= 0.7.2
-and matplotlib >= 1.1.0.
-
-Some isolated functions (e.g. filtering with firwin2) require SciPy >= 0.9.
-
-To run all documentation examples the following additional packages are required:
-
-    * PySurfer (for visualization of source estimates on cortical surfaces)
-
-    * scikit-learn (for supervised and unsupervised machine learning functionality)
-
-    * pandas >= 0.8 (for export to tabular data structures like excel files)
-
-    * h5py (for reading and writing HDF5-formatted files)
-
-Note. For optimal performance we recommend installing recent versions of
-NumPy (> 1.7), SciPy (> 0.10) and scikit-learn (>= 0.14).
-
-Development Environment
-^^^^^^^^^^^^^^^^^^^^^^^
-
-Note that we explicitly support the following Python setups since they reflect
-our development environments and functionality is best tested for them:
-
-    * Anaconda (Mac, Linux, Windows)
-
-    * Debian / Ubuntu standard system Python + Scipy stack
-
-    * EPD 7.3 (Mac, Linux)
-
-    * Canopy >= 1.0 (Mac, Linux)
-
-CUDA Optimization
-^^^^^^^^^^^^^^^^^
-
-If you want to use NVIDIA CUDA for filtering (can yield 3-4x speedups), you'll
-need to install the NVIDIA toolkit on your system, and then both pycuda and
-scikits.cuda, see:
-
-https://developer.nvidia.com/cuda-downloads
-
-http://mathema.tician.de/software/pycuda
-
-http://wiki.tiker.net/PyCuda/Installation/
-
-https://github.com/lebedov/scikits.cuda
-
-To initialize mne-python cuda support, after installing these dependencies
-and running their associated unit tests (to ensure your installation is correct)
-you can run:
-
-    >>> mne.cuda.init_cuda() # doctest: +SKIP
-
-If you have everything installed correctly, you should see an INFO-level log
-message telling you your CUDA hardware's available memory. To have CUDA
-initialized on startup, you can do:
-
-    >>> mne.utils.set_config('MNE_USE_CUDA', 'true') # doctest: +SKIP
-
-You can test if MNE CUDA support is working by running the associated test:
-
-    $ nosetests mne/tests/test_filter.py
-
-If all tests pass with none skipped, then mne-python CUDA support works.
-
-Multi-threading
-^^^^^^^^^^^^^^^
-
-For optimal performance we recommend using numpy / scipy with the
-multi-threaded ATLAS, gotoblas2, or intel MKL. For example, the Enthought
-Canopy and the Anaconda distributions ship with tested MKL-compiled
-numpy / scipy versions. Depending on the use case and your system
-this may speed up operations by a factor greater than 10.
-
-matplotlib
-^^^^^^^^^^
-
-For the setups listed above we would strongly recommend to use the Qt
-matplotlib backend for fast and correct rendering::
-
-    $ ipython --matplotlib=qt
-
-On Linux, for example, QT is the only matplotlib backend for which 3D rendering
-will work correctly. On Mac OS X for other backends certain matplotlib
-functions might not work as expected.
-
-IPython notebooks
-^^^^^^^^^^^^^^^^^
-
-To take full advantage of mne-python's visualization capacities in combination
-with IPython notebooks and inline displaying, please explicitly add the
-following magic method invocation to your notebook or configure your notebook
-runtime accordingly.
-
-    %matplotlib inline
-
-If you use another Python setup and you encounter some difficulties please
-report them on the MNE mailing list or on github to get assistance.
-
-Installing Mayavi
-^^^^^^^^^^^^^^^^^
-
-Mayavi is only available for Python2.7. If you have Anaconda installed (recommended), the easiest way to install `mayavi` is to do::
-
-    $ conda install mayavi
-
-On Ubuntu, it is also possible to install using::
-
-    $ easy_install "Mayavi[app]"
-
-If you use this method, be sure to install the dependencies first: `python-vtk` and `python-configobj`::
-
-    $ sudo apt-get install python-vtk python-configobj
-
-Make sure the `TraitsBackendQt`_ has been installed as well. For other methods of installation, please consult
-the `Mayavi documentation`_.
-
-Configuring PySurfer
-^^^^^^^^^^^^^^^^^^^^
-
-Some users may need to configure PySurfer before they can make full use of our visualization
-capabilities. Please refer to the `PySurfer installation page`_ for up to date information.
-
-.. _inside_martinos:
-
-Inside the Martinos Center
---------------------------
-
-For people within the MGH/MIT/HMS Martinos Center mne is available on the network.
-
-In a terminal do::
-
-    $ setenv PATH /usr/pubsw/packages/python/anaconda/bin:${PATH}
-
-If you use Bash replace the previous instruction with::
-
-    $ export PATH=/usr/pubsw/packages/python/anaconda/bin:${PATH}
-
-Then start the python interpreter with:
-
-    $ ipython
-
-Then type::
-
-    >>> import mne
-
-If you get a new prompt with no error messages, you should be good to go. 
-
-We encourage all Martinos center Python users to subscribe to the Martinos Python mailing list:
-
-https://mail.nmr.mgh.harvard.edu/mailman/listinfo/martinos-python
-
-.. _Pysurfer installation page: https://pysurfer.github.io/install.html
-
-.. _TraitsBackendQt: http://pypi.python.org/pypi/TraitsBackendQt
-
-.. _Mayavi documentation: http://docs.enthought.com/mayavi/mayavi/installation.html
diff --git a/doc/cite.rst b/doc/cite.rst
index 57ffc7d..393e7a2 100644
--- a/doc/cite.rst
+++ b/doc/cite.rst
@@ -1,7 +1,7 @@
 .. _cite:
 
-Cite MNE
---------
+How to cite MNE
+---------------
 
 If you use in your research the implementations provided by the MNE software you should cite:
 
diff --git a/doc/cited.rst b/doc/cited.rst
new file mode 100644
index 0000000..b641148
--- /dev/null
+++ b/doc/cited.rst
@@ -0,0 +1,112 @@
+.. _cited:
+
+Publications from MNE users
+===========================
+
+Papers citing MNE as extracted from Google Scholar (on February 29, 2016).
+
+1. `Linkage mapping with paralogs exposes regions of residual tetrasomic inheritance in chum salmon (Oncorhynchus keta). <http://onlinelibrary.wiley.com/doi/10.1111/1755-0998.12394/full>`_. RK Waples,LW Seeb,JE Seeb- Molecular ecology resources, 2016 - Wiley Online Library.
+2. `Examining the N400m in affectively negative sentences: A magnetoencephalography study. <http://onlinelibrary.wiley.com/doi/10.1111/psyp.12601/full>`_. L Parkes,C Perry, P Goodin - Psychophysiology, 2016 - Wiley Online Library.
+3. `Selective maintenance mechanisms of seen and unseen sensory features in the human brain. <http://www.biorxiv.org/content/early/2016/02/18/040030.abstract>`_. K Jean-Remi, N Pescetelli, S Dehaene - bioRxiv, 2016 - biorxiv.org.
+4. `Early-latency categorical speech sound representations in the left inferior frontal gyrus. <http://www.sciencedirect.com/science/article/pii/S1053811916000227>`_. J Alho, BM Green, PJC May,M Sams, H Tiitinen… - NeuroImage, 2016 - Elsevier.
+5. `Spectral and source structural development of mu and alpha rhythms from infancy through adulthood. <http://www.sciencedirect.com/science/article/pii/S1388245715001698>`_. SG Thorpe,EN Cannon,NA Fox- Clinical Neurophysiology, 2016 - Elsevier.
+6. `Previous exposure to intact speech increases intelligibility of its digitally degraded counterpart as a function of stimulus complexity. <http://www.sciencedirect.com/science/article/pii/S1053811915009398>`_. M Hakonen, PJC May,J Alho, P Alku, E Jokinen… - NeuroImage, 2016 - Elsevier.
+7. `Development of a Group Dynamic Functional Connectivity Pipeline for Magnetoencephalography Data and its Application to the Human Face Processing Network. <http://repository.unm.edu/handle/1928/31729>`_. P Lysne - 2016 - repository.unm.edu.
+8. `Test‐retest reliability of resting‐state magnetoencephalography power in sensor and source space. <http://onlinelibrary.wiley.com/doi/10.1002/hbm.23027/full>`_. MC Martín‐Buro,P Garcés,F Maestú- Human brain mapping, 2016 - Wiley Online Library.
+9. `The inhibition/excitation ratio related to task-induced oscillatory modulations during a working memory task: A multimodal-imaging study using MEG and MRS. <http://www.sciencedirect.com/science/article/pii/S1053811916000069>`_. Y Takei, K Fujihara, M Tagawa, N Hironaga,J Near… - NeuroImage, 2016 - Elsevier.
+10. `Interacting parallel pathways associate sounds with visual identity in auditory cortices. <http://www.sciencedirect.com/science/article/pii/S1053811915008599>`_. J Ahveninen, S Huang,SP Ahlfors,M Hämäläinen… - NeuroImage, 2016 - Elsevier.
+11. `Within-and between-session replicability of cognitive brain processes: An MEG study with an N-back task. <http://www.sciencedirect.com/science/article/pii/S0031938416300506>`_. L Ahonen, M Huotilainen, E Brattico - Physiology & behavior, 2016 - Elsevier.
+12. `Neuromagnetic evidence for hippocampal modulation of auditory processing. <http://www.sciencedirect.com/science/article/pii/S1053811915008034>`_. H Chatani, K Hagiwara, N Hironaga, K Ogata… - NeuroImage, 2016 - Elsevier.
+13. `Fast optimal transport averaging of neuroimaging data. <http://link.springer.com/chapter/10.1007/978-3-319-19992-4_20>`_. A Gramfort,G Peyré,M Cuturi- Information Processing in Medical Imaging, 2015 - Springer.
+14. `低频振荡电位的能量和相位稳定性与偶极子电流活动相关性的仿真. <http://wulixb.iphy.ac.cn/fileup/PDF/2015-14-148701.pdf>`_. 葛曼玲, 魏孟佳, 师鹏飞, 陈营, 付晓璇, 郭宝强… - 物理学报, 2015 - wulixb.iphy.ac.cn.
+15. `Enhanced neural synchrony between left auditory and premotor cortex is associated with successful phonetic categorization. <https://books.google.co.in/books?hl=en&lr=&id=GX2PCgAAQBAJ&oi=fnd&pg=PA8&ots=RkkQ_HWwLc&sig=0H-rc0sfU5pSPb-tsMyWLZdLJCE>`_. J Alho,FH Lin,M Sato, H Tiitinen,M Sams… - … in speech perception, 2015 - books.google.com.
+16. `Estimating Learning Effects: A Short-Time Fourier Transform Regression Model for MEG Source Localization. <http://arxiv.org/abs/1512.00899>`_. Y Yang,MJ Tarr,RE Kass- arXiv preprint arXiv:1512.00899, 2015 - arxiv.org.
+17. `Evidence for morphological composition in compound words using MEG. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4412057/>`_. TL Brooks, DC de Garcia - Frontiers in human neuroscience, 2015 - ncbi.nlm.nih.gov.
+18. `Leveraging anatomical information to improve transfer learning in brain–computer interfaces. <http://iopscience.iop.org/article/10.1088/1741-2560/12/4/046027/meta>`_. M Wronkiewicz, E Larson,AKC Lee- Journal of neural  …, 2015 - iopscience.iop.org.
+19. `Influence of Intracranial Electrode Density and Spatial Configuration on Interictal Spike Localization: A Case Study. <http://journals.lww.com/clinicalneurophys/Abstract/2015/10000/Influence_of_Intracranial_Electrode_Density_and.14.aspx>`_. OV Lie,AM Papanastassiou,JE Cavazos… - Journal of Clinical  …, 2015 - journals.lww.com.
+20. `Facilitated early cortical processing of nude human bodies. <http://www.sciencedirect.com/science/article/pii/S0301051115001039>`_. J Alho,N Salminen,M Sams, JK Hietanen… - Biological  …, 2015 - Elsevier.
+21. `Evidence of syntactic working memory usage in MEG data. <http://www.ling.ohio-state.edu/%7Evanschm/resources/uploads/cmcl/proceedings/cdrom/pdf/CMCL9.pdf>`_. M van Schijndel,B Murphy, W Schuler - Proceedings of CMCL, 2015 - ling.ohio-state.edu.
+22. `Flexible multi-layer sparse approximations of matrices and applications. <http://arxiv.org/abs/1506.07300>`_. LL Magoarou,R Gribonval- arXiv preprint arXiv:1506.07300, 2015 - arxiv.org.
+23. `EEGNET: An Open Source Tool for Analyzing and Visualizing M/EEG Connectome. <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0138297>`_. M Hassan, M Shamas,M Khalil,W El Falou… - PloS one, 2015 - journals.plos.org.
+24. `Exploring spatio-temporal neural correlates of face learning. <http://www.ml.cmu.edu/research/dap-papers/dap_yang_ying.pdf>`_. Y Yang - 2015 - ml.cmu.edu.
+25. `FAμST: speeding up linear transforms for tractable inverse problems. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=7362838>`_. L Le Magoarou,R Gribonval… - …  (EUSIPCO), 2015 23rd  …, 2015 - ieeexplore.ieee.org.
+26. `Early visual word processing is flexible: Evidence from spatiotemporal brain dynamics. <http://www.mitpressjournals.org/doi/abs/10.1162/jocn_a_00815>`_. Y Chen,MH Davis,F Pulvermüller,O Hauk- Journal of cognitive  …, 2015 - MIT Press.
+27. `EEG can track the time course of successful reference resolution in small visual worlds. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4653275/>`_. C Brodbeck,L Gwilliams,L Pylkkänen- Frontiers in psychology, 2015 - ncbi.nlm.nih.gov.
+28. `EEG functional connectivity is partially predicted by underlying white matter connectivity. <http://www.sciencedirect.com/science/article/pii/S1053811914010258>`_. CJ Chu, N Tanaka, J Diaz,BL Edlow, O Wu… - Neuroimage, 2015 - Elsevier.
+29. `Automated model selection in covariance estimation and spatial whitening of MEG and EEG signals. <http://www.sciencedirect.com/science/article/pii/S1053811914010325>`_. DA Engemann,A Gramfort- NeuroImage, 2015 - Elsevier.
+30. `A hierarchical Krylov–Bayes iterative inverse solver for MEG with physiological preconditioning. <http://iopscience.iop.org/article/10.1088/0266-5611/31/12/125005/meta>`_. D Calvetti,A Pascarella,F Pitolli,E Somersalo… - Inverse  …, 2015 - iopscience.iop.org.
+31. `A multi-subject, multi-modal human neuroimaging dataset. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4412149/>`_. DG Wakeman,RN Henson- Scientific data, 2015 - ncbi.nlm.nih.gov.
+32. `Accumulated source imaging of brain activity with both low and high-frequency neuromagnetic signals. <https://books.google.co.in/books?hl=en&lr=&id=j9BnCwAAQBAJ&oi=fnd&pg=PA302&ots=tzXYPVPctS&sig=fXSvk-cevK2GivxxjOI9CaiapXk>`_. J Xiang, Q Luo, R Kotecha, A Korman… - …  Advances and the  …, 2015 - books.google.com.
+33. `An internet-based real-time audiovisual link for dual meg recordings. <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0128485>`_. A Zhdanov,J Nurminen, P Baess, L Hirvenkari… - PloS one, 2015 - journals.plos.org.
+34. `AnyWave: a cross-platform and modular software for visualizing and processing electrophysiological signals. <http://www.sciencedirect.com/science/article/pii/S0165027015000187>`_. B Colombet, M Woodman, JM Badier… - Journal of neuroscience  …, 2015 - Elsevier.
+35. `Attention drives synchronization of alpha and beta rhythms between right inferior frontal and primary sensory neocortex. <http://www.jneurosci.org/content/35/5/2074.short>`_. MD Sacchet, RA LaPlante, Q Wan… - The Journal of  …, 2015 - Soc Neuroscience.
+36. `Automated Measurement and Prediction of Consciousness in Vegetative and Minimally Conscious Patients. <https://hal.inria.fr/hal-01225254/>`_. D Engemann,F Raimondo,JR King,M Jas… - ICML Workshop on  …, 2015 - hal.inria.fr.
+37. `Bayesian Structured Sparsity Priors for EEG Source Localization Technical Report. <http://arxiv.org/abs/1509.04576>`_. F Costa, H Batatia, T Oberlin,JY Tourneret- arXiv preprint arXiv: …, 2015 - arxiv.org.
+38. `Magnetoencephalography for Clinical Pediatrics: Recent Advances in Hardware, Methods, and Clinical Applications. <https://www.thieme-connect.com/products/ejournals/html/10.1055/s-0035-1563726>`_. W Gaetz, RS Gordon,C Papadelis… - Journal of Pediatric  …, 2015 - thieme-connect.com.
+39. `Decoding covert shifts of attention induced by ambiguous visuospatial cues. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4471354/>`_. RE Trachel,M Clerc,TG Brochier- Frontiers in human  …, 2015 - ncbi.nlm.nih.gov.
+40. `Deep Feature Learning for EEG Recordings. <http://arxiv.org/abs/1511.04306>`_. S Stober,A Sternin,AM Owen,JA Grahn- arXiv preprint arXiv:1511.04306, 2015 - arxiv.org.
+41. `Design and implementation of a brain computer interface system. <https://depositonce.tu-berlin.de/handle/11303/4734>`_. B Venthur- 2015 - depositonce.tu-berlin.de.
+42. `Developmental evaluation of atypical auditory sampling in dyslexia: Functional and structural evidence. <http://onlinelibrary.wiley.com/doi/10.1002/hbm.22986/full>`_. M Lizarazu, M Lallier,N Molinaro… - Human brain  …, 2015 - Wiley Online Library.
+43. `Distinct Effects of Memory Retrieval and Articulatory Preparation when Learning and Accessing New Word Forms. <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0126652>`_. A Nora, H Renvall, JY Kim,R Salmelin- PloS one, 2015 - journals.plos.org.
+44. `Distinct cortical codes and temporal dynamics for conscious and unconscious percepts. <http://elifesciences.org/content/4/e05652.abstract>`_. M Salti, S Monto,L Charles,JR King,L Parkkonen… - Elife, 2015 - elifesciences.org.
+45. `Does the mismatch negativity operate on a consciously accessible memory trace?. <http://advances.sciencemag.org/content/1/10/e1500677.abstract>`_. AR Dykstra, A Gutschalk - Science advances, 2015 - advances.sciencemag.org.
+46. `MEM-diffusion MRI framework to solve MEEG inverse problem. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=7362709>`_. B Belaoucha, JM Lina,M Clerc… - … (EUSIPCO), 2015 23rd …, 2015 - ieeexplore.ieee.org.
+47. `基于有限元方法的 theta 节律能量与导电媒质关系的研究. <http://www.cqvip.com/qk/96363x/201504/665924065.html>`_. 葛曼玲, 郭宝强, 闫志强, 王向阳, 陈盛华, 孙英… - 北京生物医学 …, 2015 - cqvip.com.
+48. `Non-linear processing of a linear speech stream: The influence of morphological structure on the recognition of spoken Arabic words. <http://www.sciencedirect.com/science/article/pii/S0093934X15000929>`_. L Gwilliams,A Marantz- Brain and language, 2015 - Elsevier.
+49. `The role of temporal predictability in semantic expectation: An MEG investigation. <http://www.sciencedirect.com/science/article/pii/S0010945215000945>`_. EF Lau, E Nguyen - Cortex, 2015 - Elsevier.
+50. `The New York Head—A precise standardized volume conductor model for EEG source localization and tES targeting. <http://www.sciencedirect.com/science/article/pii/S1053811915011325>`_. Y Huang,LC Parra,S Haufe- NeuroImage, 2015 - Elsevier.
+51. `Medidas espectrales y de conectividad funcional con magnetoencefalografía: fiabilidad y aplicaciones a deterioro cognitivo leve. <http://eprints.ucm.es/33593/>`_. P Garcés López - 2015 - eprints.ucm.es.
+52. `Reference-free removal of EEG-fMRI ballistocardiogram artifacts with harmonic regression. <http://www.sciencedirect.com/science/article/pii/S1053811915005935>`_. P Krishnaswamy,G Bonmassar, C Poulsen, ET Pierce… - NeuroImage, 2015 - Elsevier.
+53. `Real-time machine learning of MEG: Decoding signatures of selective attention. <https://aaltodoc.aalto.fi/handle/123456789/15550>`_. M Jas- 2015 - aaltodoc.aalto.fi.
+54. `Real-Time Magnetoencephalography for Neurofeedback and Closed-Loop Experiments. <http://link.springer.com/chapter/10.1007/978-4-431-55037-2_17>`_. L Parkkonen- Clinical Systems Neuroscience, 2015 - Springer.
+55. `Real-Time MEG Source Localization Using Regional Clustering. <http://link.springer.com/article/10.1007/s10548-015-0431-9>`_. C Dinh, D Strohmeier,M Luessi, D Güllmar… - Brain topography, 2015 - Springer.
+56. `Physiological consequences of abnormal connectivity in a developmental epilepsy. <http://onlinelibrary.wiley.com/doi/10.1002/ana.24343/full>`_. MM Shafi,M Vernet, D Klooster,CJ Chu… - Annals of  …, 2015 - Wiley Online Library.
+57. `Online visualization of brain connectivity. <http://www.sciencedirect.com/science/article/pii/S0165027015003222>`_. M Billinger,C Brunner,GR Müller-Putz- Journal of neuroscience methods, 2015 - Elsevier.
+58. `Occipital MEG activity in the early time range (< 300 ms) predicts graded changes in perceptual consciousness. <https://cercor.oxfordjournals.org/content/early/2015/05/24/cercor.bhv108.full>`_. LM Andersen, MN Pedersen,K Sandberg… - Cerebral  …, 2015 - Oxford Univ Press.
+59. `Somatosensory cortex functional connectivity abnormalities in autism show opposite trends, depending on direction and spatial scale. <http://brain.oxfordjournals.org/content/early/2015/03/11/brain.awv043.abstract>`_. S Khan,K Michmizos, M Tommerdahl, S Ganesan… - Brain, 2015 - Oxford Univ Press.
+60. `Neuroplasticity in Human Alcoholism: Studies of Extended Abstinence with Potential Treatment Implications. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4476599/>`_. G Fein,VA Cardenas- Alcohol research: current reviews, 2015 - ncbi.nlm.nih.gov.
+61. `Sparse EEG Source Localization Using Bernoulli Laplacian Priors. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=7134742>`_. F Costa, H Batatia,L Chaari… - … , IEEE Transactions on, 2015 - ieeexplore.ieee.org.
+62. `Towards music imagery information retrieval: Introducing the openmiir dataset of eeg recordings from music perception and imagination. <http://bib.sebastianstober.de/ismir2015.pdf>`_. S Stober,A Sternin,AM Owen… - … International Society for …, 2015 - bib.sebastianstober.de.
+63. `Transcutaneous Vagus Nerve Stimulation Modulates Tinnitus-Related Beta-and Gamma-Band Activity. <http://journals.lww.com/ear-hearing/Abstract/2015/05000/Transcutaneous_Vagus_Nerve_Stimulation_Modulates.12.aspx>`_. P Hyvärinen, S Yrttiaho, J Lehtimäki… - Ear and  …, 2015 - journals.lww.com.
+64. `Neuromagnetic Decomposition of Social Interaction. <http://kups.ub.uni-koeln.de/6262/1/thesis_engemann_da.pdf>`_. DA Engemann- 2015 - kups.ub.uni-koeln.de.
+65. `Mind the noise covariance when localizing brain sources with M/EEG. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=7270835>`_. D Engemann, D Strohmeier, E Larson… - Pattern Recognition  …, 2015 - ieeexplore.ieee.org.
+66. `Neuroimaging, neural population models for. <http://link.springer.com/10.1007/978-1-4614-6675-8_70>`_. I Bojak,M Breakspear- Encyclopedia of Computational Neuroscience, 2015 - Springer.
+67. `Wyrm: A brain-computer interface toolbox in Python. <http://link.springer.com/article/10.1007/s12021-015-9271-8>`_. B Venthur,S Dähne,J Höhne, H Heller,B Blankertz- Neuroinformatics, 2015 - Springer.
+68. `Modulation of the~ 20‐Hz motor‐cortex rhythm to passive movement and tactile stimulation. <http://onlinelibrary.wiley.com/doi/10.1002/brb3.328/full>`_. E Parkkonen, K Laaksonen,H Piitulainen… - Brain and  …, 2015 - Wiley Online Library.
+69. `Decoding perceptual thresholds from MEG/EEG. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=6858510>`_. Y Bekhti, N Zilber,F Pedregosa… - Pattern Recognition  …, 2014 - ieeexplore.ieee.org.
+70. `Spatiotemporal Signatures of Lexical–Semantic Prediction. <https://cercor.oxfordjournals.org/content/early/2014/10/14/cercor.bhu219.full>`_. EF Lau,K Weber,A Gramfort,MS Hämäläinen… - Cerebral  …, 2014 - Oxford Univ Press.
+71. `Supramodal processing optimizes visual perceptual learning and plasticity. <http://www.sciencedirect.com/science/article/pii/S1053811914001165>`_. N Zilber,P Ciuciu,A Gramfort, L Azizi… - Neuroimage, 2014 - Elsevier.
+72. `The connectome visualization utility: Software for visualization of human brain networks. <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0113838>`_. RA LaPlante,L Douw, W Tang,SM Stufflebeam- PloS one, 2014 - journals.plos.org.
+73. `Whole brain functional connectivity using phase locking measures of resting state magnetoencephalography. <http://d-scholarship.pitt.edu/24758/1/fnins-08-00141.pdf>`_. BT Schmidt,AS Ghuman, TJ Huppert - Front. Neurosci, 2014 - d-scholarship.pitt.edu.
+74. `Voxel-wise resting-state MEG source magnitude imaging study reveals neurocircuitry abnormality in active-duty service members and veterans with PTSD. <http://www.sciencedirect.com/science/article/pii/S2213158214001132>`_. MX Huang, KA Yurgil, A Robb, A Angeles… - NeuroImage: Clinical, 2014 - Elsevier.
+75. `Vector ℓ 0 latent-space principal component analysis. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=6854399>`_. M Luessi, MS Hamalainen… - Acoustics, Speech and  …, 2014 - ieeexplore.ieee.org.
+76. `The iterative reweighted Mixed-Norm Estimate for spatio-temporal MEG/EEG source reconstruction. <https://hal.archives-ouvertes.fr/hal-01079530/>`_. D Strohmeier,J Haueisen,A Gramfort- 2014 - hal.archives-ouvertes.fr.
+77. `Blind denoising with random greedy pursuits. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=6847117>`_. M Moussallam,A Gramfort,L Daudet… - Signal Processing  …, 2014 - ieeexplore.ieee.org.
+78. `Covariance shrinkage for autocorrelated data. <http://papers.nips.cc/paper/5399-covariance-shrinkage-for-autocorrelated-data>`_. D Bartz,KR Müller- Advances in Neural Information Processing  …, 2014 - papers.nips.cc.
+79. `Two distinct dynamic modes subtend the detection of unexpected sounds. <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0085791>`_. JR King,A Gramfort,A Schurger,L Naccache… - PloS one, 2014 - journals.plos.org.
+80. `Cortical oscillations as temporal reference frames for perception. <https://tel.archives-ouvertes.fr/tel-01069219/>`_. A Kosem - 2014 - tel.archives-ouvertes.fr.
+81. `Auditory Conflict Resolution Correlates with Medial–Lateral Frontal Theta/Alpha Phase Synchrony. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4208834/>`_. S Huang, S Rossi, M Hämäläinen, J Ahveninen - PloS one, 2014 - ncbi.nlm.nih.gov.
+82. `Brain network connectivity during language comprehension: Interacting linguistic and perceptual subsystems. <http://cercor.oxfordjournals.org/content/early/2014/12/01/cercor.bhu283.short>`_. E Fonteneau, M Bozic,WD Marslen-Wilson- Cerebral Cortex, 2014 - Oxford Univ Press.
+83. `Mapping tonotopic organization in human temporal cortex: representational similarity analysis in EMEG source space. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4228977/>`_. L Su, I Zulfiqar, F Jamshed, E Fonteneau… - Frontiers in  …, 2014 - ncbi.nlm.nih.gov.
+84. `Early parallel activation of semantics and phonology in picture naming: Evidence from a multiple linear regression MEG study. <https://cercor.oxfordjournals.org/content/early/2014/07/08/cercor.bhu137.full>`_. M Miozzo,F Pulvermüller,O Hauk- Cerebral Cortex, 2014 - Oxford Univ Press.
+85. `Functional Roles of 10 Hz Alpha-Band Power Modulating Engagement and Disengagement of Cortical Networks in a Complex Visual Motion Task. <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0107715>`_. KD Rana,LM Vaina- PloS one, 2014 - journals.plos.org.
+86. `Online Distributed Source Localization from EEG/MEG Data. <http://www.computingonline.net/index.php/computing/article/view/617>`_. C Pieloth,TR Knosche, B Maess… - International Journal of  …, 2014 - computingonline.net.
+87. `Localization of MEG human brain responses to retinotopic visual stimuli with contrasting source reconstruction approaches. <https://www.researchgate.net/profile/Kristine_Krug/publication/262931429_Localization_of_MEG_human_brain_responses_to_retinotopic_visual_stimuli_with_contrasting_source_reconstruction_approaches/links/00b4953b50d323bd8e000000.pdf>`_. N Cicmil,H Bridge,AJ Parker,MW Woolrich… - Front.  …, 2014 - researchgate.net.
+88. `MoBILAB: an open source toolbox for analysis and visualization of mobile brain/body imaging data. <https://books.google.co.in/books?hl=en&lr=&id=DpogBQAAQBAJ&oi=fnd&pg=PA50&ots=rlaZ5bB6Bg&sig=fXHwX96mBQCSXItdK3gHSqx4WWA>`_. A Ojeda,N Bigdely-Shamlo,S Makeig- Front. Hum. Neurosci, 2014 - books.google.com.
+89. `Integrating neuroinformatics tools in TheVirtualBrain. <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4001068/>`_. MM Woodman, L Pezard, L Domide… - Frontiers in  …, 2014 - ncbi.nlm.nih.gov.
+90. `Infants' brain responses to speech suggest analysis by synthesis. <http://www.pnas.org/content/111/31/11238.short>`_. PK Kuhl, RR Ramírez, A Bosseler… - Proceedings of the  …, 2014 - National Acad Sciences.
+91. `Improving spatial localization in MEG inverse imaging by leveraging intersubject anatomical differences. <http://faculty.washington.edu/rkmaddox/papers/Larson_2014_Improving_spatial.pdf>`_. E Larson,RK Maddox,AKC Lee- Front. Neurosci, 2014 - faculty.washington.edu.
+92. `Improved MEG/EEG source localization with reweighted mixed-norms. <http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=6858545>`_. D Strohmeier,J Haueisen… - Pattern Recognition in  …, 2014 - ieeexplore.ieee.org.
+93. `A finite-element reciprocity solution for EEG forward modeling with realistic individual head models. <http://www.sciencedirect.com/science/article/pii/S1053811914007307>`_. E Ziegler, SL Chellappa, G Gaggioni, JQM Ly… - NeuroImage, 2014 - Elsevier.
+94. `Protocoles d'interaction cerveau-machine pour améliorer la performance d'attention visuo-spatiale chez l'homme. <https://tel.archives-ouvertes.fr/tel-01077931/>`_. R Trachel - 2014 - tel.archives-ouvertes.fr.
+95. `Encoding of event timing in the phase of neural oscillations. <http://www.sciencedirect.com/science/article/pii/S1053811914001013>`_. A Kösem,A Gramfort,V van Wassenhove- NeuroImage, 2014 - Elsevier.
+96. `Encoding cortical dynamics in sparse features. <https://books.google.co.in/books?hl=en&lr=&id=THImCwAAQBAJ&oi=fnd&pg=PA78&ots=ymsuynCDU2&sig=UQK7Z-7wMlx4wq90_3pA9RYrI-4>`_. S Khan,J Lefèvre,S Baillet,KP Michmizos, S Ganesan… - 2014 - books.google.com.
+97. `ERF and scale-free analyses of source-reconstructed MEG brain signals during a multisensory learning paradigm. <http://www.theses.fr/2014PA112040>`_. N Zilber - 2014 - theses.fr.
+98. `MEG and EEG data analysis with MNE-Python. <http://dash.harvard.edu/handle/1/11879699>`_. A Gramfort,M Luessi, E Larson,DA Engemann… - 2013 - dash.harvard.edu.
+99. `Interoperability of Free Software Packages to Analyze Functional Human Brain Data. <http://www.synesisjournal.com/vol4_g/Sander_2013_G85-89.pdf>`_. T Sander-Thömmes, A Schlögl - 2010 - synesisjournal.com.
+100. `Neuroplasticity in Human Alcoholism: Studies of Extended Abstinence with Potential Treatment Implications George Fein1, 2 and Valerie A. Cardenas1  …. <http://www.nbresearch.com/PDF/2014/Neuroplasticity%20in%20Human%20Alcoholism-%20Studies%20of%20Extended%20Abstinence%20with%20Potential%20Treatment%20Implications_Fein%20G,%20Cardenas%20V.pdf>`_. G Fein, AMP Center - nbresearch.com.
+101. `Signal Processing and visualization of neuroscience data in a web browser. <https://www.researchgate.net/profile/Chirag_Deepak_Agrawal/publication/277954533_Signal_Processing_and_visualization_of_neuroscience_data_in_a_web_browser/links/55771da708aeacff20004656.pdf>`_. C Agrawal,A Gramfort- researchgate.net.
+102. `VECTOR l0 LATENT-SPACE PRINCIPAL COMPONENT ANALYSIS. <http://www.mirlab.org/conference_papers/International_Conference/ICASSP%202014/papers/p4262-luessi.pdf>`_. M Luessi, MS Hämäläinen, V Solo - mirlab.org.
+103. `Tempo Estimation from the EEG signal during perception and imagination of music. <http://bib.sebastianstober.de/bcmi2015.pdf>`_. A Sternin,S Stober,AM Owen,JA Grahn- bib.sebastianstober.de.
+104. `Règles de sélection de variables pour accélerer la localisation de sources en MEG et EEG sous contrainte de parcimonie. <http://www.josephsalmon.eu/papers/gretsi2015.pdf>`_. O FERCOQ,A GRAMFORT,J SALMON- josephsalmon.eu.
+105. `MEG connectivity and power detections with Minimum Norm Estimates require different regularization parameters. <http://downloads.hindawi.com/journals/cin/aip/541897.pdf>`_. AS Hincapié, J Kujala, J Mattout, S Daligault… - downloads.hindawi.com.
diff --git a/doc/conf.py b/doc/conf.py
index 112e2ce..481f1a0 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -14,53 +14,45 @@
 
 import sys
 import os
-import os.path as op
 from datetime import date
-
-try:
-    import sphinx_gallery as sg
-    sg_extension = 'sphinx_gallery.gen_gallery'
-except ImportError:
-    import sphinxgallery as sg
-    sg_extension = 'sphinxgallery.gen_gallery'
+import sphinx_gallery
 import sphinx_bootstrap_theme
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-curdir = op.dirname(__file__)
-sys.path.append(op.abspath(op.join(curdir, '..', 'mne')))
-sys.path.append(op.abspath(op.join(curdir, 'sphinxext')))
+curdir = os.path.dirname(__file__)
+sys.path.append(os.path.abspath(os.path.join(curdir, '..', 'mne')))
+sys.path.append(os.path.abspath(os.path.join(curdir, 'sphinxext')))
 
 import mne
 
 # -- General configuration ------------------------------------------------
 
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 import numpy_ext.numpydoc
-extensions = ['sphinx.ext.autodoc',
-              'sphinx.ext.autosummary',
-              'sphinx.ext.pngmath',
-              'sphinx.ext.mathjax',
-              'numpy_ext.numpydoc',
-            #   'sphinx.ext.intersphinx',
-              # 'flow_diagram',
-              sg_extension]
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.autosummary',
+    'sphinx.ext.doctest',
+    'sphinx.ext.intersphinx',
+    'sphinx.ext.todo',
+    'sphinx.ext.coverage',
+    'sphinx.ext.mathjax',
+    'sphinx_gallery.gen_gallery',
+]
 
-autosummary_generate = True
+extensions += ['numpy_ext.numpydoc'] 
+extensions += ['gen_commands']  # auto generate the doc for the python commands
+# extensions += ['flow_diagram]  # generate flow chart in cookbook
 
+autosummary_generate = True
 autodoc_default_flags = ['inherited-members']
 
-# extensions = ['sphinx.ext.autodoc',
-#               'sphinx.ext.doctest',
-#               'sphinx.ext.todo',
-#               'sphinx.ext.pngmath',
-#               'sphinx.ext.inheritance_diagram',
-#               'numpydoc',
-#               'ipython_console_highlighting',
-#               'only_directives']
-
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
@@ -68,14 +60,16 @@ templates_path = ['_templates']
 source_suffix = '.rst'
 
 # The encoding of source files.
-# source_encoding = 'utf-8'
+#source_encoding = 'utf-8-sig'
 
 # The master toctree document.
 master_doc = 'index'
 
 # General information about the project.
 project = u'MNE'
-copyright = u'2012-%s, MNE Developers' % date.today().year
+td = date.today()
+copyright = u'2012-%s, MNE Developers. Last updated on %s' % (td.year,
+                                                              td.isoformat())
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -88,16 +82,16 @@ release = version
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-# language = None
+#language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-# today = ''
+#today = ''
 # Else, today_fmt is used as the format for a strftime call.
-# today_fmt = '%B %d, %Y'
+#today_fmt = '%B %d, %Y'
 
 # List of documents that shouldn't be included in the build.
-unused_docs = ['config_doc.rst']
+unused_docs = []
 
 # List of directories, relative to source directory, that shouldn't be searched
 # for source files.
@@ -106,18 +100,18 @@ exclude_patterns = ['source/generated']
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
-# default_role = None
+#default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-# add_function_parentheses = True
+#add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-# add_module_names = True
+#add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-# show_authors = False
+#show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
@@ -125,9 +119,11 @@ pygments_style = 'sphinx'
 # A list of ignored prefixes for module index sorting.
 modindex_common_prefix = ['mne.']
 
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
 
-# -- Options for HTML output --------------------------------------------------
 
+# -- Options for HTML output ----------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
@@ -142,13 +138,14 @@ html_theme_options = {
     'bootswatch_theme': "flatly",
     'navbar_sidebarrel': False,
     'bootstrap_version': "3",
-    'navbar_links': [("Tutorials", "tutorials"),
-                     ("Gallery", "auto_examples/index"),
-                     ("Manual", "manual/index"),
-                     ("API", "python_reference"),
-                     ("FAQ", "faq"),
-                     ("Cite", "cite"),
-                     ],
+    'navbar_links': [
+        ("Get started", "getting_started"),
+        ("Tutorials", "tutorials"),
+        ("Gallery", "auto_examples/index"),
+        ("API", "python_reference"),
+        ("Manual", "manual/index"),
+        ("FAQ", "faq"),
+    ],
     }
 
 # Add any paths that contain custom themes here, relative to this directory.
@@ -156,10 +153,10 @@ html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-# html_title = None
+#html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-# html_short_title = None
+#html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
@@ -168,36 +165,41 @@ html_logo = "_static/mne_logo_small.png"
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-html_favicon = "favicon.ico"
+html_favicon = "_static/favicon.ico"
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static', '_images', sg.glr_path_static()]
+html_static_path = ['_static', '_images']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-# html_last_updated_fmt = '%b %d, %Y'
+#html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-# html_use_smartypants = True
+#html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-# html_sidebars = {}
+#html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-# html_additional_pages = {}
+#html_additional_pages = {}
 
 # If false, no module index is generated.
-# html_domain_indices = True
+#html_domain_indices = True
 
 # If false, no index is generated.
-# html_use_index = True
+#html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-# html_split_index = False
+#html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
 html_show_sourcelink = False
@@ -206,15 +208,12 @@ html_show_sourcelink = False
 html_show_sphinx = False
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-html_show_copyright = True
+#html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-# html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-# html_file_suffix = None
+#html_use_opensearch = ''
 
 # variables to pass to HTML templating engine
 build_dev_html = bool(int(os.environ.get('BUILD_DEV_HTML', False)))
@@ -222,19 +221,14 @@ build_dev_html = bool(int(os.environ.get('BUILD_DEV_HTML', False)))
 html_context = {'use_google_analytics': True, 'use_twitter': True,
                 'use_media_buttons': True, 'build_dev_html': build_dev_html}
 
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-# html_use_opensearch = ''
-
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-# html_file_suffix = ''
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
 
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'mne-doc'
 
 
-# -- Options for LaTeX output ------------------------------------------------
+# -- Options for LaTeX output ---------------------------------------------
 
 # The paper size ('letter' or 'a4').
 # latex_paper_size = 'letter'
@@ -265,23 +259,35 @@ latex_use_parts = True
 # latex_appendices = []
 
 # If false, no module index is generated.
-latex_use_modindex = True
+#latex_domain_indices = True
 
 trim_doctests_flags = True
 
 # Example configuration for intersphinx: refer to the Python standard library.
 intersphinx_mapping = {'http://docs.python.org/': None}
 
-sphinxgallery_conf = {
-    'examples_dirs'   : ['../examples', '../tutorials'],
-    'gallery_dirs'    : ['auto_examples', 'auto_tutorials'],
-    'doc_module': ('sphinxgallery', 'numpy'),
+examples_dirs = ['../examples', '../tutorials']
+gallery_dirs = ['auto_examples', 'auto_tutorials']
+
+try:
+    from mayavi import mlab
+    find_mayavi_figures = True
+    # Do not pop up any mayavi windows while running the
+    # examples. These are very annoying since they steal the focus.
+    mlab.options.offscreen = True
+except Exception:
+    find_mayavi_figures = False
+
+sphinx_gallery_conf = {
+    'doc_module': ('mne',),
     'reference_url': {
         'mne': None,
         'matplotlib': 'http://matplotlib.org',
-        'numpy': 'http://docs.scipy.org/doc/numpy-1.9.1',
-        'scipy': 'http://docs.scipy.org/doc/scipy-0.11.0/reference',
+        'numpy': 'http://docs.scipy.org/doc/numpy-1.10.1',
+        'scipy': 'http://docs.scipy.org/doc/scipy-0.17.0/reference',
         'mayavi': 'http://docs.enthought.com/mayavi/mayavi'},
-    'find_mayavi_figures': True,
-    'default_thumb_file': '_static/mne_helmet.png',
+    'examples_dirs': examples_dirs,
+    'gallery_dirs': gallery_dirs,
+    'find_mayavi_figures': find_mayavi_figures,
+    'default_thumb_file': os.path.join('_static', 'mne_helmet.png'),
     }
diff --git a/doc/contributing.rst b/doc/contributing.rst
index e4b756b..0656389 100644
--- a/doc/contributing.rst
+++ b/doc/contributing.rst
@@ -1,7 +1,7 @@
 .. _contributing:
 
-Contributing to MNE project
-===========================
+Contribute to MNE
+=================
 
 .. contents:: Contents
    :local:
@@ -41,7 +41,7 @@ As always, Vim or Emacs will suffice as well.
 #. Other useful packages: pysurfer_, nitime_, pandas_, PIL_, PyDICOM_,
    joblib_, nibabel_, h5py_, and scikit-learn_
 
-#. `MNE command line utilities`_ and Freesurfer_ are optional but will allow you
+#. `MNE command line utilities`_ and FreeSurfer_ are optional but will allow you
    to make the best out of MNE. Yet they will require a Unix (Linux or Mac OS)
    system. If you are on Windows, you can install these applications inside a
    Unix virtual machine.
@@ -50,7 +50,7 @@ General code guidelines
 -----------------------
 
 * We highly recommend using a code editor that uses both `pep8`_ and
-  `pyflakes`_, such as `spyder`_. Standard python style guidelines are
+  `pyflakes`_, such as `Spyder`_. Standard python style guidelines are
   followed, with very few exceptions.
 
   You can also manually check pyflakes and pep8 warnings as::
@@ -804,6 +804,17 @@ Add the example to the correct subfolder in the ``examples/`` directory and
 prefix the file with ``plot_``. To make sure that the example renders correctly,
 run ``make html`` in the ``doc/`` folder
 
+Building a subset of examples
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To build only a subset of examples, it is possible to provide a regular expression
+which searches on the full pathname of the file. For example, you can do::
+
+  make html_dev-pattern PATTERN='/decoding/plot_'
+
+It will run only the examples in the ``decoding`` folder. Consult the `sphinx gallery documentation`_
+for more details.
+
 Editing \*.rst files
 ^^^^^^^^^^^^^^^^^^^^
 
@@ -841,4 +852,5 @@ handler doing an exit()``, try backing up or removing .ICEauthority::
 
 .. include:: links.inc
 
-.. _Sphinx documentation: http://sphinx-doc.org/rest.html
\ No newline at end of file
+.. _Sphinx documentation: http://sphinx-doc.org/rest.html
+.. _sphinx gallery documentation: http://sphinx-gallery.readthedocs.org/en/latest/advanced_configuration.html
\ No newline at end of file
diff --git a/doc/faq.rst b/doc/faq.rst
index 9caf70f..9424b1c 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -1,3 +1,5 @@
+.. include:: links.inc
+
 .. _faq:
 
 ==========================
@@ -8,6 +10,149 @@ Frequently Asked Questions
    :local:
 
 
+General MNE-Python issues
+=========================
+
+Help! I can't get Python and MNE-Python working!
+------------------------------------------------
+
+Check out our section on how to get Anaconda up and running over at the
+:ref:`getting started page <install_interpreter>`.
+
+I'm not sure how to do *X* analysis step with my *Y* data...
+------------------------------------------------------------
+
+Knowing "the right thing" to do with EEG and MEG data is challenging.
+We use the `MNE mailing list`_ to discuss
+how to deal with different bits of data. It's worth searching the archives
+to see if there have been relevant discussions before.
+
+I think I found a bug, what do I do?
+------------------------------------
+
+Please report any problems you find while using MNE-Python to the
+`GitHub issues page`_.
+Try :ref:`using the latest master version <installing_master>` to
+see if the problem persists before reporting the bug, as it may have
+been fixed since the latest release.
+
+It is helpful to include system information with bug reports, so it can be
+useful to include the output of the :func:`mne.sys_info` command when
+reporting a bug, which should look something like this::
+
+    >>> import mne
+    >>> mne.sys_info()  # doctest:+SKIP
+    Platform:      Linux-4.2.0-27-generic-x86_64-with-debian-jessie-sid
+    Python:        2.7.11 |Continuum Analytics, Inc.| (default, Dec  6 2015, 18:08:32)  [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]
+    Executable:    /home/larsoner/miniconda/bin/python
+
+    mne:           0.12.dev0
+    numpy:         1.10.2 {lapack=mkl_lapack95_lp64, blas=mkl_intel_lp64}
+    scipy:         0.16.1
+    matplotlib:    1.5.1
+
+    sklearn:       Not found
+    nibabel:       Not found
+    nitime:        Not found
+    mayavi:        Not found
+    nose:          1.3.7
+    pandas:        Not found
+    pycuda:        Not found
+    skcuda:        Not found
+
+
+Why is it dangerous to "pickle" my MNE-Python objects and data for later use?
+-----------------------------------------------------------------------------
+`Pickling <https://docs.python.org/3/library/pickle.html>`_ data and
+MNE-Python objects for later use can be tempting due to its simplicity
+and generality, but it is usually not the best option. Pickling is not
+designed for stable persistence, and it is likely that you will not be
+able to read your data in the not-too-distant future. For details, see:
+
+- http://www.benfrederickson.com/dont-pickle-your-data/
+- http://stackoverflow.com/questions/21752259/python-why-pickle
+
+MNE-Python is designed to provide its own file saving formats
+(often based on the FIF standard) for its objects usually via a
+``save`` method or ``write_*`` method, e.g. :func:`mne.Raw.save`,
+:func:`mne.Epochs.save`, :func:`mne.write_evokeds`,
+:func:`mne.SourceEstimate.save`. If you have some data that you
+want to save but can't figure out how, shoot an email to the
+`MNE mailing list`_ or post it to the `GitHub issues page`_.
+
+If you want to write your own data to disk (e.g., subject behavioral
+scores), we strongly recommend using `h5io <https://github.com/h5io/h5io>`_,
+which is based on the
+`HDF5 format <https://en.wikipedia.org/wiki/Hierarchical_Data_Format>`_ and
+`h5py <http://www.h5py.org/>`_,
+to save data in a fast, future-compatible, standard format.
+
+
+Resampling and decimating data
+==============================
+
+What are all these options for resampling, decimating, and binning data?
+------------------------------------------------------------------------
+
+There are many functions in MNE-Python for changing the effective sampling
+rate of data. We'll discuss some major ones here, with some of their
+implications:
+
+- :func:`mne.io.Raw.resample` is used to resample (typically downsample) raw
+  data. Resampling is the two-step process of applying a low-pass FIR filter
+  and subselecting samples from the data.
+
+  Using this function to resample data before forming :class:`mne.Epochs`
+  for final analysis is generally discouraged because doing so effectively
+  loses precision of (and jitters) the event timings, see
+  `this gist <https://gist.github.com/Eric89GXL/01642cb3789992fbca59>`_ as
+  a demonstration. However, resampling raw data can be useful for
+  (at least):
+
+    - Computing projectors in low- or band-passed data
+    - Exploring data
+
+- :func:`mne.preprocessing.ICA.fit` decimates data without low-passing,
+  but is only used for fitting a statistical model to the data.
+
+- :func:`mne.Epochs.decimate`, which does the same thing as the
+  ``decim`` parameter in the :class:`mne.Epochs` constructor, sub-selects every
+  :math:`N^{th}` sample before and after each event. This should only be
+  used when the raw data have been sufficiently low-passed e.g. by
+  :func:`mne.io.Raw.filter` to avoid aliasing artifacts.
+
+- :func:`mne.Epochs.resample`, :func:`mne.Evoked.resample`, and
+  :func:`mne.SourceEstimate.resample` all resample data.
+  This process avoids potential aliasing artifacts because the
+  resampling process applies a low-pass filter. However, this filtering
+  introduces edge artifacts. Edge artifacts also exist when using
+  :func:`mne.io.Raw.resample`, but there the edge artifacts are constrained
+  to two times: the start and end of the recording. With these three methods,
+  edge artifacts are introduced to the start and end of every epoch
+  of data (or the start and end of the :class:`mne.Evoked` or
+  :class:`mne.SourceEstimate` data), which often has a more pronounced
+  effect on the data.
+
+- :func:`mne.SourceEstimate.bin` can be used to decimate, with or without
+  "binning" (averaging across data points). This is equivalent to applying
+  a moving-average (boxcar) filter to the data and decimating. A boxcar in
+  time is a `sinc <https://en.wikipedia.org/wiki/Sinc_function>`_ in
+  frequency, so this acts as a simplistic, non-ideal low-pass filter;
+  this will reduce but not eliminate aliasing if data were not sufficiently
+  low-passed. In the case where the "filter" or bin-width is a single sample
+  (i.e., an impulse) this operation simplifies to decimation without filtering.
+
+Resampling raw data is taking forever! What do I do?
+----------------------------------------------------
+
+:func:`mne.io.Raw.resample` was significantly sped up for version 0.12 by
+using the parameter ``npad=='auto'``. Try it, it might help!
+
+If you have an NVIDIA GPU you could also try using :ref:`CUDA`, which can
+sometimes speed up filtering and resampling operations by an order of
+magnitude.
+
+
 Inverse Solution
 ================
 
@@ -17,8 +162,8 @@ How should I regularize the covariance matrix?
 The estimated covariance can be numerically
 unstable and tends to induce correlations between estimated source amplitudes
 and the number of samples available. The MNE manual therefore suggests to regularize the noise covariance matrix (see
-:ref:`CBBHEGAB`), especially if only few samples are available. Unfortunately
-it is not easy to tell the effective number of samples, hence, to chose the appropriate regularization.
+:ref:`cov_regularization`), especially if only few samples are available. Unfortunately
+it is not easy to tell the effective number of samples, hence, to choose the appropriate regularization.
 In MNE-Python, regularization is done using advanced regularization methods
 described in [1]_. For this the 'auto' option can be used. With this
 option cross-validation will be used to learn the optimal regularization::
@@ -53,7 +198,7 @@ reconstructed from the same SSS basis vectors with the same numerical rank.
 This also implies that both sensor types are not any longer linearly independent.
 
 These methods for evaluation can be used to assess model violations. Additional
-introductory materials can be found [here](https://speakerdeck.com/dengemann/eeg-sensor-covariance-using-cross-validation).
+introductory materials can be found `here <https://speakerdeck.com/dengemann/eeg-sensor-covariance-using-cross-validation>`_.
 
 For expert use cases or debugging the alternative estimators can also be compared::
 
@@ -64,12 +209,38 @@ For expert use cases or debugging the alternative estimators can also be compare
 This will plot the whitened evoked for the optimal estimator and display the GFPs
 for all estimators as separate lines in the related panel.
 
+Morphing data
+=============
+
+Should I morph my source estimates using ``morph`` or ``morph_precomputed``?
+----------------------------------------------------------------------------
+The two functions :func:`mne.SourceEstimate.morph` and
+:func:`mne.SourceEstimate.morph_precomputed` perform the same operation:
+taking surface-based source space data from one subject and
+morphing it to another using a smoothing procedure. However, they can
+take different amounts of time to perform the computation.
+
+To use :func:`mne.SourceEstimate.morph_precomputed`, you must first
+precompute a morphing matrix with :func:`mne.compute_morph_matrix` which
+can take some time, but then the actual morphing operation carried out by
+:func:`mne.SourceEstimate.morph_precomputed` is very fast, even for
+:class:`mne.SourceEstimate` objects with many time points. The method
+:func:`mne.SourceEstimate.morph`, by contrast, smooths the data by operating
+directly on the data, which can be **very slow** with many time points.
+If there are thousands of time points, then
+:func:`mne.SourceEstimate.morph_precomputed` will be much faster; if there
+are a few time points, then :func:`mne.SourceEstimate.morph` will be faster.
+For data sizes in between, we advise testing to determine which is best,
+although some developers choose to always use
+:func:`mne.SourceEstimate.morph_precomputed` since it will rarely take
+a long time.
+
 References
 ----------
 
 .. [1] Engemann D. and Gramfort A. (2015) Automated model selection in
-    covariance estimation and spatial whitening of MEG and EEG signals,
-    vol. 108, 328-342, NeuroImage.
+       covariance estimation and spatial whitening of MEG and EEG signals,
+       vol. 108, 328-342, NeuroImage.
 
-.. [2] Taulu, S., Simola, J., Kajola, M., 2005. Applications of the signal space
-    separation method. IEEE Trans. Signal Proc. 53, 3359–3372.
+.. [2] Taulu, S., Simola, J., Kajola, M., 2005. Applications of the signal
+       space separation method. IEEE Trans. Signal Proc. 53, 3359–3372.
diff --git a/doc/getting_started.rst b/doc/getting_started.rst
index 5fedf75..ada2ab3 100644
--- a/doc/getting_started.rst
+++ b/doc/getting_started.rst
@@ -1,321 +1,123 @@
+.. include:: links.inc
+
 .. _getting_started:
 
-Getting Started
+Getting started
 ===============
 
-.. contents:: Contents
-   :local:
-   :depth: 2
-
-.. XXX do a Getting for both C and Python
+.. _introduction_to_mne:
 
-MNE is an academic software package that aims to provide data analysis
+**MNE** is an academic software package that aims to provide data analysis
 pipelines encompassing all phases of M/EEG data processing.
-It consists of two subpackages which are fully integrated
-and compatible: the original MNE-C (distributed as compiled C code)
-and MNE-Python. A basic :ref:`ch_matlab` is also available mostly
-to allow reading and write MNE files. For source localization
-the software depends on anatomical MRI processing tools provided
-by the `FreeSurfer`_ software.
-
-.. _FreeSurfer: http://surfer.nmr.mgh.harvard.edu
-
-Downloading and installing the Unix commands
---------------------------------------------
-
-.. note::
-
-    If you are working at the Martinos Center see :ref:`setup_martinos`
-    for instructions to work with MNE and to access the Neuromag software.
-
-The MNE Unix commands can be downloaded at:
-
-* `Download <http://www.nmr.mgh.harvard.edu/martinos/userInfo/data/MNE_register/index.php>`_ MNE
-
-:ref:`c_reference` gives an overview of the command line
-tools provided with MNE.
 
-System requirements
-###################
+MNE started as tool written in C by Matti Hämäläinen while at MGH in Boston.
+MNE was then extended with the Python programming language to implement
+nearly all MNE-C’s functionality, offer transparent scripting, and
+:ref:`extend MNE-C’s functionality considerably <what_can_you_do>`.
 
-The MNE Unix commands runs on Mac OSX and LINUX operating systems.
-The hardware and software requirements are:
+A basic :ref:`ch_matlab` is also available mostly
+to allow reading and write MNE files. The sister :ref:`mne_cpp` project
+aims to provide modular and open-source tools for acquisition,
+visualization, and analysis.
 
-- Mac OSX version 10.5 (Leopard) or later.
+.. note:: This package is based on the FIF file format from Neuromag. But, it
+          can read and convert CTF, BTI/4D, KIT and various EEG formats to
+          FIF (see :ref:`IO functions <ch_convert>`).
 
-- LINUX kernel 2.6.9 or later
-
-- On both LINUX and Mac OSX 32-bit and 64-bit Intel platforms
-  are supported. PowerPC version on Mac OSX can be provided upon request.
-
-- At least 2 GB of memory, 4 GB or more recommended.
-
-- Disk space required for the MNE software: 80 MB
-
-- Additional open source software on Mac OSX, see :ref:`BABDBCJE`.
+          If you have being using MNE-C, there is no need to convert your fif
+          files to a new system or database -- MNE-Python works nicely with
+          the historical fif files.
 
 Installation
-############
-
-The MNE software is distributed as a compressed tar archive
-(Mac OSX and LINUX) or a Mac OSX disk image (dmg).
-
-The file names follow the convention:
-
-MNE-* <*version*>*- <*rev*> -* <*Operating
-system*>*-* <*Processor*>*.* <*ext*>*
-
-The present version number is 2.7.0. The <*rev*> field
-is the SVN revision number at the time this package was created.
-The <*Operating system*> field
-is either Linux or MacOSX. The <*processor*> field
-is either i386 or x86_64. The <*ext*> field
-is 'gz' for compressed tar archive files and 'dmg' for
-Mac OSX disk images.
-
-Installing from a compressed tar archive
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Go to the directory where you want the software to be installed:
-
-``cd`` <*dir*>
-
-Unpack the tar archive:
-
-``tar zxvf`` <*software package*>
-
-The name of the software directory under <*dir*> will
-be the same as the package file without the .gz extension.
-
-Installing from a Mac OSX disk image
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- Double click on the disk image file.
-  A window opens with the installer package ( <*name*> .pkg)
-  inside.
-
-- Double click the the package file. The installer starts.
-
-- Follow the instructions in the installer.
-
-.. note::
-
-    The software will be installed to /Applications/ <*name*> by default.
-    If you want another location, select Choose Folder... on the Select a
-    Destination screen in the installer.
-
-.. _user_environment:
-
-Setting up MNE Unix commands environment
-########################################
-
-The system-dependent location of the MNE Software will be
-here referred to by the environment variable MNE_ROOT. There are
-two scripts for setting up user environment so that the software
-can be used conveniently:
-
-``$MNE_ROOT/bin/mne_setup_sh``
-
-and
-
-``$MNE_ROOT/bin/mne_setup``
-
-compatible with the POSIX and csh/tcsh shells, respectively. Since
-the scripts set environment variables they should be 'sourced' to
-the present shell. You can find which type of a shell you are using
-by saying
-
-``echo $SHELL``
-
-If the output indicates a POSIX shell (bash or sh) you should issue
-the three commands:
-
-``export MNE_ROOT=`` <*MNE*> ``export MATLAB_ROOT=`` <*Matlab*> ``. $MNE_ROOT/bin/mne_setup_sh``
-
-with <*MNE*> replaced
-by the directory where you have installed the MNE software and <*Matlab*> is
-the directory where Matlab is installed. If you do not have Matlab,
-leave MATLAB_ROOT undefined. If Matlab is not available, the utilities
-mne_convert_mne_data , mne_epochs2mat , mne_raw2mat ,
-and mne_simu will not work.
-
-For csh/tcsh the corresponding commands are:
-
-``setenv MNE_ROOT`` <*MNE*> ``setenv MATLAB_ROOT`` <*Matlab*> ``source $MNE_ROOT/bin/mne_setup``
-
-For BEM mesh generation using the watershed algorithm or
-on the basis of multi-echo FLASH MRI data (see :ref:`create_bem_model`) and
-for accessing the tkmedit program
-from mne_analyze, see :ref:`CACCHCBF`,
-the MNE software needs access to a FreeSurfer license
-and software. Therefore, to use these features it is mandatory that
-you set up the FreeSurfer environment
-as described in the FreeSurfer documentation.
-
-The environment variables relevant to the MNE software are
-listed in :ref:`CIHDGFAA`.
-
-.. tabularcolumns:: |p{0.3\linewidth}|p{0.55\linewidth}|
-.. _CIHDGFAA:
-.. table:: Environment variables
-
-    +-------------------------+--------------------------------------------+
-    | Name of the variable    |   Description                              |
-    +=========================+============================================+
-    | MNE_ROOT                | Location of the MNE software, see above.   |
-    +-------------------------+--------------------------------------------+
-    | FREESURFER_HOME         | Location of the FreeSurfer software.       |
-    |                         | Needed during FreeSurfer reconstruction    |
-    |                         | and if the FreeSurfer MRI viewer is used   |
-    |                         | with mne_analyze, see :ref:`CACCHCBF`.     |
-    +-------------------------+--------------------------------------------+
-    | SUBJECTS_DIR            | Location of the MRI data.                  |
-    +-------------------------+--------------------------------------------+
-    | SUBJECT                 | Name of the current subject.               |
-    +-------------------------+--------------------------------------------+
-    | MNE_TRIGGER_CH_NAME     | Name of the trigger channel in raw data,   |
-    |                         | see :ref:`mne_process_raw`.                |
-    +-------------------------+--------------------------------------------+
-    | MNE_TRIGGER_CH_MASK     | Mask to be applied to the trigger channel  |
-    |                         | values, see :ref:`mne_process_raw`.        |
-    +-------------------------+--------------------------------------------+
-
-.. _BABDBCJE:
-
-Additional software
-###################
-
-MNE uses the 'Netpbm' package (http://netpbm.sourceforge.net/)
-to create image files in formats other than tif and rgb from mne_analyze and mne_browse_raw .
-This package is usually present on LINUX systems. On Mac OSX, you
-need to install the netpbm package. The recommended way to do this
-is to use the MacPorts Project tools, see http://www.macports.org/:
-
-- If you have not installed the MacPorts
-  software, goto http://www.macports.org/install.php and follow the
-  instructions to install MacPorts.
-
-- Install the netpbm package by saying: ``sudo port install netpbm``
-
-MacPorts requires that you have the XCode developer tools
-and X11 windowing environment installed. X11 is also needed by MNE.
-For Mac OSX Leopard, we recommend using XQuartz (http://xquartz.macosforge.org/).
-As of this writing, XQuartz does not yet exist for SnowLeopard;
-the X11 included with the operating system is sufficient.
-
-.. _CIHIIBDA:
-
-Testing the performance of your OpenGL graphics
-###############################################
-
-The graphics performance of mne_analyze depends
-on your graphics software and hardware configuration. You get the
-best performance if you are using mne_analyze locally
-on a computer and the hardware acceleration capabilities are in
-use. You can check the On GLX... item
-in the help menu of mne_analyze to
-see whether the hardware acceleration is in effect. If the dialog
-popping up says Direct rendering context ,
-you are using hardware acceleration. If this dialog indicates Nondirect rendering context , you are either using software
-emulation locally, rendering to a remote display, or employing VNC
-connection. If you are rendering to a local display and get an indication
-of Nondirect rendering context ,
-software emulation is in effect and you should contact your local
-computer support to enable hardware acceleration for GLX. In some
-cases, this may require acquiring a new graphics display card. Fortunately,
-relatively high-performance OpenGL-capable graphics cards very inexpensive.
-
-There is also an utility mne_opengl_test to
-assess the graphics performance more quantitatively. This utility
-renders an inflated brain surface repeatedly, rotating it by 5 degrees
-around the *z* axis between redraws. At each
-revolution, the time spent for the full revolution is reported on
-the terminal window where mne_opengl_test was
-started from. The program renders the surface until the interrupt
-key (usually control-c) is pressed on the terminal window.
-
-mne_opengl_test is located
-in the ``bin`` directory and is thus started as:
-
-``$MNE_ROOT/bin/mne_opengl_test``
-
-On the fastest graphics cards, the time per revolution is
-well below 1 second. If this time longer than 10 seconds either
-the graphics hardware acceleration is not in effect or you need
-a faster graphics adapter.
-
-Obtain FreeSurfer
-#################
-
-The MNE software relies on the FreeSurfer software for cortical
-surface reconstruction and other MRI-related tasks. Please consult
-the FreeSurfer home page site at ``http://surfer.nmr.mgh.harvard.edu/`` .
-
-
-Downloading and installing MNE-Python
--------------------------------------
-
-.. note::
-
-    If you are at the Martinos Center, please see this section :ref:`inside_martinos`.
-
-New to the Python programming language?
-#######################################
+------------
 
-This is a very good place to get started: http://scipy-lectures.github.io.
+To get started with MNE, visit the installation instructions for
+:ref:`MNE-Python <install_python_and_mne_python>` and
+:ref:`MNE-C <install_mne_c>`:
 
-Installing the Python interpreter
-#################################
+.. container:: span box
 
-For a fast and up to date scientific Python environment that resolves all
-dependencies, we recommend the Anaconda Python distribution:
+  .. raw:: html
 
-https://store.continuum.io/cshop/anaconda/
+    <h3>MNE-Python</h3>
 
-Anaconda is free for academic purposes.
+  .. toctree::
+    :maxdepth: 2
 
-To test that everything works properly, open up IPython::
+    install_mne_python
 
-    $ ipython --matplotlib=qt
+.. container:: span box
 
-Now that you have a working Python environment you can install MNE-Python.
+  .. raw:: html
 
-mne-python installation
-#######################
+    <h3>MNE-C</h3>
 
-Most users should start with the "stable" version of mne-python, which can
-be installed this way:
+  .. toctree::
+    :maxdepth: 2
 
-    $ pip install mne --upgrade
+    install_mne_c
 
-For the newest features (and potentially more bugs), you can instead install
-the development version by:
 
-    $ pip install -e git+https://github.com/mne-tools/mne-python#egg=mne-dev
+.. _what_can_you_do:
 
-If you plan to contribute to the project, please follow the git instructions: 
-:ref:`contributing`.
+What can you do with MNE using Python?
+--------------------------------------
 
-If you would like to use a custom installation of python (or have specific
-questions about integrating special tools like IPython notebooks), please
-see this section :ref:`detailed_notes`.
+   - **Raw data visualization** to visualize recordings
+     (see :ref:`general_examples` for more).
+   - **Epoching**: Define epochs, baseline correction, handle conditions etc.
+   - **Averaging** to get Evoked data.
+   - **Compute SSP projectors** to remove ECG and EOG artifacts.
+   - **Compute ICA** to remove artifacts or select latent sources.
+   - **Maxwell filtering** to remove environmental noise.
+   - **Boundary Element Modeling**: single and three-layer BEM model
+     creation and solution computation.
+   - **Forward modeling**: BEM computation and mesh creation
+     (see :ref:`ch_forward`).
+   - **Linear inverse solvers** (dSPM, sLORETA, MNE, LCMV, DICS).
+   - **Sparse inverse solvers** (L1/L2 mixed norm MxNE, Gamma Map,
+     Time-Frequency MxNE, RAP-MUSIC).
+   - **Connectivity estimation** in sensor and source space.
+   - **Visualization of sensor and source space data**
+   - **Time-frequency** analysis with Morlet wavelets (induced power,
+     intertrial coherence, phase lock value) also in the source space.
+   - **Spectrum estimation** using multi-taper method.
+   - **Mixed Source Models** combining cortical and subcortical structures.
+   - **Dipole Fitting**
+   - **Decoding** multivariate pattern analysis of M/EEG topographies.
+   - **Compute contrasts** between conditions, between sensors, across
+     subjects etc.
+   - **Non-parametric statistics** in time, space and frequency
+     (including cluster-level).
+   - **Scripting** (batch and parallel computing)
 
-Checking your installation
-##########################
 
-To check that everything went fine, in ipython, type::
+Is that all you can do with MNE-Python?
+---------------------------------------
 
-    >>> import mne
+Short answer is **No**! You can also do:
 
-If you get a new prompt with no error messages, you should be good to go!
-Consider reading the :ref:`detailed_notes` for more advanced options and
-speed-related enhancements.
+    - detect heart beat QRS component
+    - detect eye blinks and EOG artifacts
+    - compute SSP projections to remove ECG or EOG artifacts
+    - compute Independent Component Analysis (ICA) to remove artifacts or
+      select latent sources
+    - estimate noise covariance matrix from Raw and Epochs
+    - visualize cross-trial response dynamics using epochs images
+    - compute forward solutions
+    - estimate power in the source space
+    - estimate connectivity in sensor and source space
+    - morph stc from one brain to another for group studies
+    - compute mass univariate statistics base on custom contrasts
+    - visualize source estimates
+    - export raw, epochs, and evoked data to other python data analysis
+      libraries e.g. pandas
+    - Raw movement compensation as you would do with Elekta Maxfilter™
+    - and many more things ...
 
-Going beyond
-------------
 
-Now you're ready to read our:
+What you're not supposed to do with MNE-Python
+----------------------------------------------
 
-  * :ref:`tutorials`
-  * `Examples <auto_examples/index.html>`_
-  * :ref:`manual`
+    - **Brain and head surface segmentation** for use with BEM
+      models -- use Freesurfer_.
diff --git a/doc/git_links.inc b/doc/git_links.inc
index 617072d..48fdb44 100644
--- a/doc/git_links.inc
+++ b/doc/git_links.inc
@@ -77,16 +77,12 @@
 
 .. python editors
 .. _atom: https://atom.io/
-.. _spyder: http://spyder-ide.blogspot.com/
+.. _Spyder: http://pythonhosted.org/spyder/
 .. _anaconda: http://www.continuum.io/downloads
 .. _EPD: https://www.enthought.com/products/epd/
 .. _sublime text: http://www.sublimetext.com/
 
-.. mne stuff
-.. _mne command line utilities: http://www.nmr.mgh.harvard.edu/martinos/userInfo/data/MNE_register/
-.. _mne-scripts: https://github.com/mne-tools/mne-scripts/
-
-.. _Freesurfer: http://surfer.nmr.mgh.harvard.edu/fswiki/DownloadAndInstall/
+.. _FreeSurfer: http://surfer.nmr.mgh.harvard.edu/fswiki/DownloadAndInstall/
 
 .. |emdash| unicode:: U+02014
 
diff --git a/doc/index.rst b/doc/index.rst
index 3553bd9..8f7c4a5 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -1,4 +1,4 @@
-.. title:: Home
+.. title:: MNE
 
 .. raw:: html
 
@@ -19,11 +19,12 @@
    <div class="col-md-8">
    <br>
 
-MNE is a community-driven software package designed for for **processing
+MNE is a community-driven software package designed for **processing
 electroencephalography (EEG) and magnetoencephalography (MEG) data**
-providing comprehensive tools and workflows for:
+providing comprehensive tools and workflows for
+(:ref:`among other things <what_can_you_do>`):
 
-1. Preprocessing
+1. Preprocessing and denoising
 2. Source estimation
 3. Time–frequency analysis
 4. Statistical testing
@@ -31,16 +32,16 @@ providing comprehensive tools and workflows for:
 6. Applying machine learning algorithms
 7. Visualization of sensor- and source-space data
 
-MNE includes a comprehensive Python package (provided under the simplified
-BSD license), supplemented by tools compiled from C code for the LINUX and
-Mac OSX operating systems, as well as a MATLAB toolbox.
+MNE includes a comprehensive `Python <https://www.python.org/>`_ package
+supplemented by tools compiled from C code for the LINUX and Mac OSX
+operating systems, as well as a MATLAB toolbox.
 
-**From raw data to source estimates in about 30 lines of code:**
+**From raw data to source estimates in about 30 lines of code** (:ref:`try it yourself! <getting_started>`):
 
 .. code:: python
 
     >>> import mne  # doctest: +SKIP
-    >>> raw = mne.io.Raw('raw.fif', preload=True)  # load data  # doctest: +SKIP
+    >>> raw = mne.io.read_raw_fif('raw.fif', preload=True)  # load data  # doctest: +SKIP
     >>> raw.info['bads'] = ['MEG 2443', 'EEG 053']  # mark bad channels  # doctest: +SKIP
     >>> raw.filter(l_freq=None, h_freq=40.0)  # low-pass filter data  # doctest: +SKIP
     >>> # Extract epochs and save them:
@@ -67,13 +68,20 @@ Mac OSX operating systems, as well as a MATLAB toolbox.
     >>> stc_avg = mne.morph_data('sample', 'fsaverage', stc, 5, smooth=5)  # doctest: +SKIP
     >>> stc_avg.plot()  # doctest: +SKIP
 
-The MNE development is supported by National Institute of Biomedical Imaging and Bioengineering
-grants 5R01EB009048 and P41EB015896 (Center for Functional Neuroimaging Technologies) as well as
-NSF awards 0958669 and 1042134. It has been supported by the
-NCRR *Center for Functional Neuroimaging Technologies* P41RR14075-06, the
-NIH grants 1R01EB009048-01, R01 EB006385-A101, 1R01 HD40712-A1, 1R01
-NS44319-01, and 2R01 NS37462-05, ell as by Department of Energy
-under Award Number DE-FG02-99ER62764 to The MIND Institute.
+MNE development is driven by :ref:`extensive contributions from the community <whats_new>`.
+Direct financial support for the project has been provided by:
+
+- (US) National Institute of Biomedical Imaging and Bioengineering (NIBIB)
+  grants 5R01EB009048 and P41EB015896 (Center for Functional Neuroimaging
+  Technologies)
+- (US) NSF awards 0958669 and 1042134.
+- (US) NCRR *Center for Functional Neuroimaging Technologies* P41RR14075-06
+- (US) NIH grants 1R01EB009048-01, R01 EB006385-A101, 1R01 HD40712-A1, 1R01
+  NS44319-01, and 2R01 NS37462-05
+- (US) Department of Energy Award Number DE-FG02-99ER62764 to The MIND
+  Institute.
+- (FR) IDEX Paris-Saclay, ANR-11-IDEX-0003-02, via the
+  `Center for Data Science <http://www.datascience-paris-saclay.fr/>`_.
 
 .. raw:: html
 
@@ -92,32 +100,34 @@ under Award Number DE-FG02-99ER62764 to The MIND Institute.
    :maxdepth: 1
 
    getting_started
-   whats_new
-   cite
-   references
    tutorials
    auto_examples/index
-   manual/index
-   python_reference
-   generated/commands
    faq
-   advanced_setup
-   mne_cpp
+   contributing
+
+.. toctree::
+   :maxdepth: 1
+
+   python_reference
+   manual/index
+   whats_new
+
+.. toctree::
+   :maxdepth: 1
+
+   cite
+   references
+   cited
 
 .. raw:: html
 
    <h2>Community</h2>
 
-* | Analysis talk: join the
-  | `MNE mailing list <http://mail.nmr.mgh.harvard.edu/mailman/listinfo/mne_analysis>`_
-
-* | Feature requests and bug reports:
-  | `GitHub issues <https://github.com/mne-tools/mne-python/issues/>`_
+* `Analysis talk: join the MNE mailing list <MNE mailing list>`_
 
-* | Chat with developers:
-  | `Gitter <https://gitter.im/mne-tools/mne-python>`_
+* `Feature requests and bug reports on GitHub <https://github.com/mne-tools/mne-python/issues/>`_
 
-* :ref:`Contribute to MNE! <contributing>`
+* `Chat with developers on Gitter <https://gitter.im/mne-tools/mne-python>`_
 
 .. raw:: html
 
diff --git a/doc/getting_started.rst b/doc/install_mne_c.rst
similarity index 64%
copy from doc/getting_started.rst
copy to doc/install_mne_c.rst
index 5fedf75..cb2c35a 100644
--- a/doc/getting_started.rst
+++ b/doc/install_mne_c.rst
@@ -1,40 +1,25 @@
-.. _getting_started:
+.. include:: links.inc
 
-Getting Started
-===============
+.. _install_mne_c:
 
-.. contents:: Contents
-   :local:
-   :depth: 2
-
-.. XXX do a Getting for both C and Python
-
-MNE is an academic software package that aims to provide data analysis
-pipelines encompassing all phases of M/EEG data processing.
-It consists of two subpackages which are fully integrated
-and compatible: the original MNE-C (distributed as compiled C code)
-and MNE-Python. A basic :ref:`ch_matlab` is also available mostly
-to allow reading and write MNE files. For source localization
-the software depends on anatomical MRI processing tools provided
-by the `FreeSurfer`_ software.
-
-.. _FreeSurfer: http://surfer.nmr.mgh.harvard.edu
-
-Downloading and installing the Unix commands
---------------------------------------------
+Install MNE-C
+-------------
 
-.. note::
-
-    If you are working at the Martinos Center see :ref:`setup_martinos`
-    for instructions to work with MNE and to access the Neuromag software.
+Some advanced functionality is provided by the MNE-C command-line tools.
+It is not strictly necessary to have the MNE-C tools installed to use
+MNE-Python, but it can be helpful.
 
 The MNE Unix commands can be downloaded at:
 
-* `Download <http://www.nmr.mgh.harvard.edu/martinos/userInfo/data/MNE_register/index.php>`_ MNE
+* `Download MNE <http://www.nmr.mgh.harvard.edu/martinos/userInfo/data/MNE_register/index.php>`_
 
 :ref:`c_reference` gives an overview of the command line
 tools provided with MNE.
 
+.. contents:: Contents
+   :local:
+   :depth: 1
+
 System requirements
 ###################
 
@@ -78,11 +63,15 @@ Installing from a compressed tar archive
 
 Go to the directory where you want the software to be installed:
 
-``cd`` <*dir*>
+.. code-block:: bash
+
+    $ cd <dir>
 
 Unpack the tar archive:
 
-``tar zxvf`` <*software package*>
+.. code-block:: bash
+
+    $ tar zxvf <software package>
 
 The name of the software directory under <*dir*> will
 be the same as the package file without the .gz extension.
@@ -114,34 +103,47 @@ here referred to by the environment variable MNE_ROOT. There are
 two scripts for setting up user environment so that the software
 can be used conveniently:
 
-``$MNE_ROOT/bin/mne_setup_sh``
+.. code-block:: bash
+
+    $ $MNE_ROOT/bin/mne_setup_sh
 
 and
 
-``$MNE_ROOT/bin/mne_setup``
+.. code-block:: bash
+
+    $ $MNE_ROOT/bin/mne_setup
 
 compatible with the POSIX and csh/tcsh shells, respectively. Since
 the scripts set environment variables they should be 'sourced' to
 the present shell. You can find which type of a shell you are using
 by saying
 
-``echo $SHELL``
+.. code-block:: bash
+
+    $ echo $SHELL
 
 If the output indicates a POSIX shell (bash or sh) you should issue
 the three commands:
 
-``export MNE_ROOT=`` <*MNE*> ``export MATLAB_ROOT=`` <*Matlab*> ``. $MNE_ROOT/bin/mne_setup_sh``
+.. code-block:: bash
+
+    $ export MNE_ROOT=<MNE>
+    $ export MATLAB_ROOT=<Matlab>
+    $ . $MNE_ROOT/bin/mne_setup_sh
 
-with <*MNE*> replaced
-by the directory where you have installed the MNE software and <*Matlab*> is
+with ``<MNE>`` replaced
+by the directory where you have installed the MNE software and ``<Matlab>`` is
 the directory where Matlab is installed. If you do not have Matlab,
 leave MATLAB_ROOT undefined. If Matlab is not available, the utilities
-mne_convert_mne_data , mne_epochs2mat , mne_raw2mat ,
-and mne_simu will not work.
+mne_convert_mne_data, mne_epochs2mat, mne_raw2mat, nd mne_simu will not work.
 
 For csh/tcsh the corresponding commands are:
 
-``setenv MNE_ROOT`` <*MNE*> ``setenv MATLAB_ROOT`` <*Matlab*> ``source $MNE_ROOT/bin/mne_setup``
+.. code-block:: csh
+
+    $ setenv MNE_ROOT <MNE>
+    $ setenv MATLAB_ROOT <Matlab>
+    $ source $MNE_ROOT/bin/mne_setup
 
 For BEM mesh generation using the watershed algorithm or
 on the basis of multi-echo FLASH MRI data (see :ref:`create_bem_model`) and
@@ -162,21 +164,21 @@ listed in :ref:`CIHDGFAA`.
     +-------------------------+--------------------------------------------+
     | Name of the variable    |   Description                              |
     +=========================+============================================+
-    | MNE_ROOT                | Location of the MNE software, see above.   |
+    | ``MNE_ROOT``            | Location of the MNE software, see above.   |
     +-------------------------+--------------------------------------------+
-    | FREESURFER_HOME         | Location of the FreeSurfer software.       |
+    | ``FREESURFER_HOME``     | Location of the FreeSurfer software.       |
     |                         | Needed during FreeSurfer reconstruction    |
     |                         | and if the FreeSurfer MRI viewer is used   |
     |                         | with mne_analyze, see :ref:`CACCHCBF`.     |
     +-------------------------+--------------------------------------------+
-    | SUBJECTS_DIR            | Location of the MRI data.                  |
+    | ``SUBJECTS_DIR``        | Location of the MRI data.                  |
     +-------------------------+--------------------------------------------+
-    | SUBJECT                 | Name of the current subject.               |
+    | ``SUBJECT``             | Name of the current subject.               |
     +-------------------------+--------------------------------------------+
-    | MNE_TRIGGER_CH_NAME     | Name of the trigger channel in raw data,   |
+    | ``MNE_TRIGGER_CH_NAME`` | Name of the trigger channel in raw data,   |
     |                         | see :ref:`mne_process_raw`.                |
     +-------------------------+--------------------------------------------+
-    | MNE_TRIGGER_CH_MASK     | Mask to be applied to the trigger channel  |
+    | ``MNE_TRIGGER_CH_MASK`` | Mask to be applied to the trigger channel  |
     |                         | values, see :ref:`mne_process_raw`.        |
     +-------------------------+--------------------------------------------+
 
@@ -185,8 +187,9 @@ listed in :ref:`CIHDGFAA`.
 Additional software
 ###################
 
-MNE uses the 'Netpbm' package (http://netpbm.sourceforge.net/)
-to create image files in formats other than tif and rgb from mne_analyze and mne_browse_raw .
+MNE uses the `Netpbm package <http://netpbm.sourceforge.net/>`_
+to create image files in formats other than tif and rgb from
+``mne_analyze`` and ``mne_browse_raw``.
 This package is usually present on LINUX systems. On Mac OSX, you
 need to install the netpbm package. The recommended way to do this
 is to use the MacPorts Project tools, see http://www.macports.org/:
@@ -223,7 +226,7 @@ of Nondirect rendering context ,
 software emulation is in effect and you should contact your local
 computer support to enable hardware acceleration for GLX. In some
 cases, this may require acquiring a new graphics display card. Fortunately,
-relatively high-performance OpenGL-capable graphics cards very inexpensive.
+relatively high-performance OpenGL-capable graphics cards are not expensive.
 
 There is also an utility mne_opengl_test to
 assess the graphics performance more quantitatively. This utility
@@ -237,7 +240,9 @@ key (usually control-c) is pressed on the terminal window.
 mne_opengl_test is located
 in the ``bin`` directory and is thus started as:
 
-``$MNE_ROOT/bin/mne_opengl_test``
+.. code-block:: bash
+
+    $ $MNE_ROOT/bin/mne_opengl_test
 
 On the fastest graphics cards, the time per revolution is
 well below 1 second. If this time longer than 10 seconds either
@@ -248,74 +253,6 @@ Obtain FreeSurfer
 #################
 
 The MNE software relies on the FreeSurfer software for cortical
-surface reconstruction and other MRI-related tasks. Please consult
-the FreeSurfer home page site at ``http://surfer.nmr.mgh.harvard.edu/`` .
-
-
-Downloading and installing MNE-Python
--------------------------------------
-
-.. note::
-
-    If you are at the Martinos Center, please see this section :ref:`inside_martinos`.
-
-New to the Python programming language?
-#######################################
-
-This is a very good place to get started: http://scipy-lectures.github.io.
-
-Installing the Python interpreter
-#################################
-
-For a fast and up to date scientific Python environment that resolves all
-dependencies, we recommend the Anaconda Python distribution:
-
-https://store.continuum.io/cshop/anaconda/
-
-Anaconda is free for academic purposes.
-
-To test that everything works properly, open up IPython::
-
-    $ ipython --matplotlib=qt
-
-Now that you have a working Python environment you can install MNE-Python.
-
-mne-python installation
-#######################
-
-Most users should start with the "stable" version of mne-python, which can
-be installed this way:
-
-    $ pip install mne --upgrade
-
-For the newest features (and potentially more bugs), you can instead install
-the development version by:
-
-    $ pip install -e git+https://github.com/mne-tools/mne-python#egg=mne-dev
-
-If you plan to contribute to the project, please follow the git instructions: 
-:ref:`contributing`.
-
-If you would like to use a custom installation of python (or have specific
-questions about integrating special tools like IPython notebooks), please
-see this section :ref:`detailed_notes`.
-
-Checking your installation
-##########################
-
-To check that everything went fine, in ipython, type::
-
-    >>> import mne
-
-If you get a new prompt with no error messages, you should be good to go!
-Consider reading the :ref:`detailed_notes` for more advanced options and
-speed-related enhancements.
-
-Going beyond
-------------
-
-Now you're ready to read our:
+surface reconstruction and other MRI-related tasks. Please consult the
+FreeSurfer_ home page.
 
-  * :ref:`tutorials`
-  * `Examples <auto_examples/index.html>`_
-  * :ref:`manual`
diff --git a/doc/install_mne_python.rst b/doc/install_mne_python.rst
new file mode 100644
index 0000000..a92332a
--- /dev/null
+++ b/doc/install_mne_python.rst
@@ -0,0 +1,277 @@
+.. include:: links.inc
+
+.. _install_python_and_mne_python:
+
+Install Python and MNE-Python
+-----------------------------
+
+To use MNE-Python, you need two things:
+
+1. A working Python interpreter and dependencies
+
+2. The MNE-Python package installed to the Python distribution
+
+Step-by-step instructions to accomplish this are given below.
+
+.. contents:: Steps
+   :local:
+   :depth: 1
+
+.. note:: Users who work at a facility with a site-wide install of
+          MNE-Python (e.g. Martinos center) are encouraged to contact
+          their technical staff about how to access and use MNE-Python,
+          as the instructions might differ.
+
+.. _install_interpreter:
+
+1. Install a Python interpreter and dependencies
+################################################
+
+There are multiple options available for getting a suitable Python interpreter
+running on your system. However, for a fast and up to date scientific Python
+environment that resolves all dependencies, we highly recommend following
+installation instructions for the Anaconda Python distribution. Go here
+to download, and follow the installation instructions:
+
+* https://www.continuum.io/downloads
+* http://docs.continuum.io/anaconda/install
+
+.. note:: Intel's `Math Kernel Library <https://software.intel.com/en-us/intel-mkl>`_
+          speeds up many linear algebra comptutations, some by an order of
+          magnitude. It is included by default in Anaconda, which makes it
+          preferable to some other potentially viable Python interpreter
+          options (e.g., using ``brew`` in OSX or ``sudo apt-get install``
+          on Ubuntu to get a usable Python interpreter). 
+
+If everything is set up correctly, you should be able to check the version
+of ``conda`` that is installed (your version number will probably be newer)
+and which ``python`` will be called when you run ``python``:
+
+.. code-block:: bash
+
+    $ conda --version
+    conda 3.19.1
+    $ which python
+    /home/agramfort/anaconda/bin/python
+
+If your installation doesn't look something like this,
+*something went wrong* and you should try to fix it. We recommend looking
+through the Anaconda documentation a bit, and Googling for Anaconda
+install tips (StackExchange results are often helpful).
+
+Once Anaconda works properly, you can do this to resolve
+the MNE-Python dependencies:
+
+.. code-block:: bash
+
+    $ conda install scipy matplotlib scikit-learn
+
+To test that everything works properly, open up IPython:
+
+.. code-block:: bash
+
+    $ ipython --matplotlib=qt
+
+Now that you have a working Python environment you can install MNE-Python.
+
+If you want to have an environment with a clean MATLAB-like interface,
+consider using Spyder_, which can easily be installed with Anaconda
+as:
+
+.. code-block:: bash
+
+    $ conda install spyder
+
+.. _install_mne_python:
+
+2. Install the MNE-Python package
+#################################
+
+There are a many options for installing MNE-Python, but two of the most
+useful and common are:
+
+1. **Use the stable release version of MNE-Python.** It can be installed as:
+
+   .. code-block:: bash
+
+       $ pip install mne --upgrade
+
+   MNE-Python tends to release about once every six months, and this
+   command can be used to update the install after each release.
+
+.. _installing_master:
+
+2. **Use the development master version of MNE-Python.** If you want to
+   be able to update your MNE-Python version between releases (e.g., for
+   bugfixes or new features), this will set you up for frequent updates:
+
+   .. code-block:: bash
+
+       $ git clone git://github.com/mne-tools/mne-python.git
+       $ cd mne-python
+       $ python setup.py develop
+
+   A cool feature of ``python setup.py develop`` is that any changes made to
+   the files (e.g., by updating to latest ``master``) will be reflected in
+   ``mne`` as soon as you restart your Python interpreter. So to update to
+   the latest version of the ``master`` development branch, you can do:
+
+   .. code-block:: bash
+
+       $ git pull origin master
+
+   and your MNE-Python will be updated to have the latest changes.
+   If you plan to contribute to MNE-Python, you should follow a variant
+   of this approach outlined in the
+   :ref:`contribution instructions <contributing>`. 
+
+3. Check your installation
+##########################
+
+To check that everything went fine, in ipython, type::
+
+    >>> import mne
+
+If you get a new prompt with no error messages, you should be good to go!
+
+A good place to start is on our :ref:`tutorials` page or with our
+:ref:`general_examples`.
+
+Along the way, make frequent use of :ref:`api_reference` and
+:ref:`manual` to understand the capabilities of MNE.
+
+4. Optional advanced setup
+##########################
+
+.. _CUDA:
+
+CUDA
+^^^^
+
+We have developed specialized routines to make use of
+`NVIDIA CUDA GPU processing <http://www.nvidia.com/object/cuda_home_new.html>`_
+to speed up some operations (e.g. FIR filtering) by up to 10x. 
+If you want to use NVIDIA CUDA, you should install:
+
+1. `the NVIDIA toolkit on your system <https://developer.nvidia.com/cuda-downloads>`_
+2. `PyCUDA <http://wiki.tiker.net/PyCuda/Installation/>`_
+3. `skcuda <https://github.com/lebedov/scikits.cuda>`_
+
+For example, on Ubuntu 15.10, a combination of system packages and ``git``
+packages can be used to install the CUDA stack:
+
+.. code-block:: bash
+
+    # install system packages for CUDA
+    $ sudo apt-get install nvidia-cuda-dev nvidia-modprobe
+    # install PyCUDA
+    $ git clone http://git.tiker.net/trees/pycuda.git
+    $ cd pycuda
+    $ ./configure.py --cuda-enable-gl
+    $ git submodule update --init
+    $ make -j 4
+    $ python setup.py install
+    # install skcuda
+    $ cd ..
+    $ git clone https://github.com/lebedov/scikit-cuda.git
+    $ cd scikit-cuda
+    $ python setup.py install
+
+To initialize mne-python cuda support, after installing these dependencies
+and running their associated unit tests (to ensure your installation is correct)
+you can run:
+
+.. code-block:: bash
+
+    $ MNE_USE_CUDA=true MNE_LOGGING_LEVEL=info python -c "import mne; mne.cuda.init_cuda()"
+    Enabling CUDA with 1.55 GB available memory
+
+If you have everything installed correctly, you should see an INFO-level log
+message telling you your CUDA hardware's available memory. To have CUDA
+initialized on startup, you can do::
+
+    >>> mne.utils.set_config('MNE_USE_CUDA', 'true') # doctest: +SKIP
+
+You can test if MNE CUDA support is working by running the associated test:
+
+.. code-block:: bash
+
+    $ nosetests mne/tests/test_filter.py
+
+If ``MNE_USE_CUDA=true`` and all tests pass with none skipped, then
+MNE-Python CUDA support works.
+
+IPython (and notebooks)
+^^^^^^^^^^^^^^^^^^^^^^^
+
+In IPython, we strongly recommend using the Qt matplotlib backend for
+fast and correct rendering:
+
+.. code-block:: bash
+
+    $ ipython --matplotlib=qt
+
+On Linux, for example, QT is the only matplotlib backend for which 3D rendering
+will work correctly. On Mac OS X for other backends certain matplotlib
+functions might not work as expected.
+
+To take full advantage of MNE-Python's visualization capacities in combination
+with IPython notebooks and inline displaying, please explicitly add the
+following magic method invocation to your notebook or configure your notebook
+runtime accordingly::
+
+    In [1]: %matplotlib inline
+
+If you use another Python setup and you encounter some difficulties please
+report them on the MNE mailing list or on github to get assistance.
+
+Mayavi and PySurfer
+^^^^^^^^^^^^^^^^^^^
+
+Mayavi is currently only available for Python2.7.
+The easiest way to install `mayavi` is to do the following with Anaconda:
+
+.. code-block:: bash
+
+    $ conda install mayavi
+
+For other methods of installation, please consult the
+`Mayavi documentation <http://docs.enthought.com/mayavi/mayavi/installation.html>`_.
+
+The PySurfer package, which is used for visualizing cortical source estimates,
+uses Mayavi and can be installed using:
+
+.. code-block:: bash
+
+    $ pip install PySurfer
+
+Some users may need to configure PySurfer before they can make full use of
+our visualization capabilities. Please refer to the
+`PySurfer installation page <https://pysurfer.github.io/install.html>`_
+for up to date information.
+
+Troubleshooting
+###############
+
+If you run into trouble when visualizing source estimates (or anything else
+using mayavi), you can try setting ETS_TOOLKIT environment variable::
+
+    >>> import os
+    >>> os.environ['ETS_TOOLKIT'] = 'qt4'
+    >>> os.environ['QT_API'] = 'pyqt'
+
+This will tell Traits that we will use Qt with PyQt bindings.
+
+If you get an error saying::
+
+    ValueError: API 'QDate' has already been set to version 1
+
+you have run into a conflict with Traits. You can work around this by telling
+the interpreter to use QtGui and QtCore from pyface::
+
+    >>> from pyface.qt import QtGui, QtCore
+
+This line should be added before any imports from mne-python.
+
+For more information, see
+http://docs.enthought.com/mayavi/mayavi/building_applications.html.
diff --git a/doc/manual/appendix/bem_model.rst b/doc/manual/appendix/bem_model.rst
index a24a717..1e13f48 100644
--- a/doc/manual/appendix/bem_model.rst
+++ b/doc/manual/appendix/bem_model.rst
@@ -15,7 +15,7 @@ Creating the BEM meshes
 Using the watershed algorithm
 #############################
 
-The watershed algorithm\egonne *et al.*,
+The watershed algorithm [Segonne *et al.*,
 2004] is part of the FreeSurfer software.
 The name of the program is mri_watershed .
 Its use in the MNE environment is facilitated by the script `mne_watershed_bem`.
@@ -59,7 +59,7 @@ that they are collected at the same time with the MPRAGEs or at
 least with the same scanner. For easy co-registration, the images
 should have FOV, matrix, slice thickness, gap, and slice orientation
 as the MPRAGE data. For information on suitable pulse sequences,
-see reference\. Fischl *et al.* and J. Jovicich *et
+see reference [B. Fischl *et al.* and J. Jovicich *et
 al.*, 2006] in :ref:`CEGEGDEI`. At the Martinos
 Center, use of the 1.5-T Avanto scanner (Bay 2) is recommended for
 best results.
diff --git a/doc/manual/appendix/c_EULA.rst b/doc/manual/appendix/c_EULA.rst
index b4eabd9..d5d6173 100644
--- a/doc/manual/appendix/c_EULA.rst
+++ b/doc/manual/appendix/c_EULA.rst
@@ -33,8 +33,8 @@ or governmental institution or entity which employs or is otherwise
 affiliated with such individual at the time of such download (the "Institution").
 
 - *License Grant.* Subject
-  to all of the terms and conditions of this Agreement,\he General
-  Hospital Corporation, d/b/a Massachusetts General Hospital]\he
+  to all of the terms and conditions of this Agreement, [The General
+  Hospital Corporation, d/b/a Massachusetts General Hospital] [The
   Brigham and Women's Hospital, Inc.] ("Licensor") hereby
   grants you a non-exclusive, non-transferable, non-sublicensable license
   under Licensor's rights in the Software to copy and use the binary
diff --git a/doc/manual/appendix/c_release_notes.rst b/doc/manual/appendix/c_release_notes.rst
index 5dac09f..53865cf 100644
--- a/doc/manual/appendix/c_release_notes.rst
+++ b/doc/manual/appendix/c_release_notes.rst
@@ -237,17 +237,15 @@ The following changes have been made in mne_inverse_operator :
 mne_compute_raw_inverse
 =======================
 
-This utility is now documented in :ref:`CBBCGHAH`. The
-utility mne_make_raw_inverse_operator has
-been removed from the software.
+This utility is now documented in :ref:`computing_inverse`. The
+utility mne_make_raw_inverse_operator has been removed from the software.
 
 Time range settings
 ===================
 
 The tools mne_compute_raw_inverse , mne_convert_mne_data ,
-and mne_compute_mne no longer
-have command-line options to restrict the time range of evoked data
-input.
+and mne_compute_mne no longer have command-line options to restrict
+the time range of evoked data input.
 
 mne_change_baselines
 ====================
diff --git a/doc/manual/c_reference.rst b/doc/manual/c_reference.rst
index f9c8645..ef81b62 100644
--- a/doc/manual/c_reference.rst
+++ b/doc/manual/c_reference.rst
@@ -43,7 +43,7 @@ Software components
     |                            | :ref:`mne_make_movie`.                     |
     +----------------------------+--------------------------------------------+
     | `mne_compute_raw_inverse`_ | Compute the inverse solution from raw data |
-    |                            | see :ref:`CBBCGHAH`.                       |
+    |                            | see :ref:`computing_inverse`.              |
     +----------------------------+--------------------------------------------+
     | `mne_convert_mne_data`_    | Convert MNE data files to other file       |
     |                            | formats.                                   |
@@ -58,10 +58,10 @@ Software components
     |                            | :ref:`CHDDIBAH`.                           |
     +----------------------------+--------------------------------------------+
     | `mne_inverse_operator`_    | Compute the inverse operator decomposition |
-    |                            | see :ref:`CBBDDBGF`.                       |
+    |                            | see :ref:`inverse_operator`.               |
     +----------------------------+--------------------------------------------+
     | `mne_make_movie`_          | Make movies in batch mode, see             |
-    |                            | :ref:`CBBECEDE`.                           |
+    |                            | :ref:`movies_and_snapshots`.               |
     +----------------------------+--------------------------------------------+
     | `mne_make_source_space`_   | Create a *fif* source space description    |
     |                            | file, see :ref:`BEHCGJDD`.                 |
@@ -90,7 +90,8 @@ Software components
     |                            | see :ref:`BABCCEHF`                        |
     +----------------------------+--------------------------------------------+
     | `mne_setup_source_space`_  | A convenience script to create source space|
-    |                            | description file, see :ref:`CIHCHDAE`.     |
+    |                            | description file, see                      |
+    |                            | :ref:`setting_up_source_space`.            |
     +----------------------------+--------------------------------------------+
     | `mne_show_environment`_    | Show information about the production      |
     |                            | environment of a file.                     |
@@ -681,7 +682,7 @@ mne_compute_mne
 
 This program is gradually becoming obsolete. All of its functions will
 be eventually included to :ref:`mne_make_movie`,
-see :ref:`CBBECEDE`. At this time, :ref:`mne_compute_mne` is
+see :ref:`movies_and_snapshots`. At this time, :ref:`mne_compute_mne` is
 still needed to produce time-collapsed w files unless you are willing
 to write a Matlab script of your own for this purpose.
 
@@ -908,7 +909,7 @@ mne_compute_raw_inverse
 
     Specifies a label file to process. For each label file, the values
     of the computed estimates stored in a fif file. For more details,
-    see :ref:`CBBHJDAI`. The label files are produced by tksurfer
+    see :ref:`implementation_details`. The label files are produced by tksurfer
     or mne_analyze and specify regions
     of interests (ROIs). A label file name should end with ``-lh.label`` for
     left-hemisphere ROIs and with ``-rh.label`` for right-hemisphere
@@ -928,7 +929,7 @@ mne_compute_raw_inverse
 ``--align_z``
 
     Instructs the program to try to align the waveform signs within
-    the label. For more information, see :ref:`CBBHJDAI`. This
+    the label. For more information, see :ref:`implementation_details`. This
     flag will not have any effect if the inverse operator has been computed
     with the strict orientation constraint active.
 
@@ -1525,7 +1526,7 @@ mne_do_inverse_operator
 ``--depth``
 
     Employ depth weighting with the standard settings. For details,
-    see :ref:`CBBDFJIE` and :ref:`CBBDDBGF`.
+    see :ref:`depth_weighting` and :ref:`inverse_operator`.
 
 ``--bad <*name*>``
 
@@ -1559,7 +1560,7 @@ mne_do_inverse_operator
 ``--megreg <*value*>``
 
     Regularize the MEG part of the noise-covariance matrix by this amount.
-    Suitable values are in the range 0.05...0.2. For details, see :ref:`CBBHEGAB`.
+    Suitable values are in the range 0.05...0.2. For details, see :ref:`cov_regularization`.
 
 ``--eegreg <*value*>``
 
@@ -1583,7 +1584,7 @@ mne_do_inverse_operator
     the cortical surface. The name of the file given is used as a stem of
     the w files. The actual files should be called <*name*> ``-lh.pri`` and <*name*> ``-rh.pri`` for
     the left and right hemisphere weight files, respectively. The application
-    of the weighting is discussed in :ref:`CBBDIJHI`.
+    of the weighting is discussed in :ref:`mne_fmri_estimates`.
 
 ``--fmrithresh <*value*>``
 
@@ -1810,7 +1811,7 @@ mne_inverse_operator
 
     Use an adaptive loose orientation constraint. This option can be
     only employed if the source spaces included in the forward solution
-    have the patch information computed, see :ref:`CIHCHDAE`.
+    have the patch information computed, see :ref:`setting_up_source_space`.
 
 ``--fwd <name>``
 
@@ -1832,19 +1833,19 @@ mne_inverse_operator
 
     Regularize the planar gradiometer section (channels for which the unit
     of measurement is T/m) of the noise-covariance matrix by the given
-    amount. The value is restricted to the range 0...1. For details, see :ref:`CBBHEGAB`.
+    amount. The value is restricted to the range 0...1. For details, see :ref:`cov_regularization`.
 
 ``--magreg <value>``
 
     Regularize the magnetometer and axial gradiometer section (channels
     for which the unit of measurement is T) of the noise-covariance matrix
     by the given amount. The value is restricted to the range 0...1.
-    For details, see :ref:`CBBHEGAB`.
+    For details, see :ref:`cov_regularization`.
 
 ``--eegreg <value>``
 
     Regularize the EEG section of the noise-covariance matrix by the given
-    amount. The value is restricted to the range 0...1. For details, see :ref:`CBBHEGAB`.
+    amount. The value is restricted to the range 0...1. For details, see :ref:`cov_regularization`.
 
 ``--diagnoise``
 
@@ -1863,17 +1864,17 @@ mne_inverse_operator
 
 ``--depth``
 
-    Employ depth weighting. For details, see :ref:`CBBDFJIE`.
+    Employ depth weighting. For details, see :ref:`depth_weighting`.
 
 ``--weightexp <value>``
 
     This parameter determines the steepness of the depth weighting function
-    (default = 0.8). For details, see :ref:`CBBDFJIE`.
+    (default = 0.8). For details, see :ref:`depth_weighting`.
 
 ``--weightlimit <value>``
 
     Maximum relative strength of the depth weighting (default = 10). For
-    details, see :ref:`CBBDFJIE`.
+    details, see :ref:`depth_weighting`.
 
 ``--fmri <name>``
 
@@ -1888,7 +1889,7 @@ mne_inverse_operator
     the cortical surface. The name of the file given is used as a stem of
     the w files. The actual files should be called <*name*> ``-lh.pri`` and <*name*> ``-rh.pri`` for
     the left and right hemsphere weight files, respectively. The application
-    of the weighting is discussed in :ref:`CBBDIJHI`.
+    of the weighting is discussed in :ref:`mne_fmri_estimates`.
 
 ``--fmrithresh <value>``
 
@@ -1954,8 +1955,8 @@ mne_inverse_operator
     statistics for the source space. Since this computation is time consuming,
     it is recommended that the patch statistics are precomputed and
     the source space file containing the patch information is employed
-    already when the forward solution is computed, see :ref:`CIHCHDAE` and :ref:`BABCHEJD`.
-    For technical details of the patch information, please consult :ref:`CBBDBHDI`. This option is considered experimental at
+    already when the forward solution is computed, see :ref:`setting_up_source_space` and :ref:`BABCHEJD`.
+    For technical details of the patch information, please consult :ref:`patch_stats`. This option is considered experimental at
     the moment.
 
 ``--inv <name>``
@@ -2220,21 +2221,21 @@ Thresholding
 ``--fthresh <*value*>``
 
     Specifies the threshold for the displayed colormaps. At the threshold,
-    the overlayed color will be equal to the background surface color.
+    the overlaid color will be equal to the background surface color.
     For currents, the value will be multiplied by :math:`1^{-10}`.
     The default value is 8.
 
 ``--fmid <*value*>``
 
     Specifies the midpoint for the displayed colormaps. At this value, the
-    overlayed color will be read (positive values) or blue (negative values).
+    overlaid color will be read (positive values) or blue (negative values).
     For currents, the value will be multiplied by :math:`1^{-10}`.
     The default value is 15.
 
 ``--fmax <*value*>``
 
     Specifies the maximum point for the displayed colormaps. At this value,
-    the overlayed color will bright yellow (positive values) or light
+    the overlaid color will bright yellow (positive values) or light
     blue (negative values). For currents, the value will be multiplied
     by :math:`1^{-10}`. The default value is 20.
 
@@ -2782,7 +2783,7 @@ mne_process_raw
 mne_redo_file
 =============
 
-Usage: /home/larsoner/custombuilds/mne/current/bin/mne_redo_file file-to-redo
+Usage: ``mne_redo_file file-to-redo``
 
 
 .. _mne_redo_file_nocwd:
@@ -2790,7 +2791,7 @@ Usage: /home/larsoner/custombuilds/mne/current/bin/mne_redo_file file-to-redo
 mne_redo_file_nocwd
 ===================
 
-Usage: /home/larsoner/custombuilds/mne/current/bin/mne_redo_file_nocwd file-to-redo
+Usage: ``mne_redo_file_nocwd file-to-redo``
 
 
 .. _mne_setup_forward_model:
@@ -2971,7 +2972,7 @@ mne_setup_source_space
 mne_show_environment
 ====================
 
-Usage: /home/larsoner/custombuilds/mne/current/bin/mne_show_environment files
+Usage: ``mne_show_environment files``
 
 
 Utility command-line arguments
diff --git a/doc/manual/channel_interpolation.rst b/doc/manual/channel_interpolation.rst
new file mode 100644
index 0000000..19c942a
--- /dev/null
+++ b/doc/manual/channel_interpolation.rst
@@ -0,0 +1,75 @@
+
+.. contents:: Contents
+    :local:
+    :depth: 2
+
+.. _channel_interpolation:
+
+Repairing bad channels
+######################
+
+Spherical spline interpolation (EEG)
+====================================
+
+In short, data repair using spherical spline interpolation [1]_ consists of the following steps:
+
+* Project the good and bad electrodes onto a unit sphere
+* Compute a mapping matrix that maps :math:`N` good channels to :math:`M` bad channels
+* Use this mapping matrix to compute interpolated data in the bad channels
+
+Spherical splines assume that the potential :math:`V(\boldsymbol{r_i})` at any point :math:`\boldsymbol{r_i}` on the surface of the sphere can be represented by:
+
+.. math:: V(\boldsymbol{r_i}) = c_0 + \sum_{j=1}^{N}c_{i}g_{m}(cos(\boldsymbol{r_i}, \boldsymbol{r_{j}}))
+   :label: model
+
+where the :math:`C = (c_{1}, ..., c_{N})^{T}` are constants which must be estimated. The function :math:`g_{m}(\cdot)` of order :math:`m` is given by:
+
+.. math:: g_{m}(x) = \frac{1}{4 \pi}\sum_{n=1}^{\infty} \frac{2n + 1}{(n(n + 1))^m}P_{n}(x)
+   :label: legendre
+
+where :math:`P_{n}(x)` are `Legendre polynomials`_ of order `n`.
+
+.. _Legendre polynomials: https://en.wikipedia.org/wiki/Legendre_polynomials
+
+To estimate the constants :math:`C`, we must solve the following two equations simultaneously:
+
+.. math:: G_{ss}C + T_{s}c_0 = X
+   :label: matrix_form
+
+.. math:: {T_s}^{T}C = 0
+   :label: constraint
+
+where :math:`G_{ss} \in R^{N \times N}` is a matrix whose entries are :math:`G_{ss}[i, j] = g_{m}(cos(\boldsymbol{r_i}, \boldsymbol{r_j}))` and :math:`X \in R^{N \times 1}` are the potentials :math:`V(\boldsymbol{r_i})` measured at the good channels. :math:`T_{s} = (1, 1, ..., 1)^T` is a column vector of dimension :math:`N`. Equation :eq:`matrix_form` is the matrix formulation of Equation :eq:`model` and equation :eq:`constraint` is like applying an average reference to the data. From eq [...]
+
+.. math:: \begin{bmatrix} c_0 \\ C \end{bmatrix} = {\begin{bmatrix} {T_s}^{T} && 0 \\ T_s && G_{ss} \end{bmatrix}}^{-1} \begin{bmatrix} 0 \\ X \end{bmatrix} = C_{i}X
+   :label: estimate_constant
+
+:math:`C_{i}` is the same as matrix :math:`{\begin{bmatrix} {T_s}^{T} && 0 \\ T_s && G_{ss} \end{bmatrix}}^{-1}` but with its first column deleted, therefore giving a matrix of dimension :math:`(N + 1) \times N`.
+
+Now, to estimate the potentials :math:`\hat{X} \in R^{M \times 1}` at the bad channels, we have to do:
+
+.. math:: \hat{X} = G_{ds}C + T_{d}c_0
+   :label: estimate_data
+
+where :math:`G_{ds} \in R^{M \times N}` computes :math:`g_{m}(\boldsymbol{r_i}, \boldsymbol{r_j})` between the bad and good channels. :math:`T_{d} = (1, 1, ..., 1)^T` is a column vector of dimension :math:`M`. Plugging in equation :eq:`estimate_constant` in :eq:`estimate_data`, we get
+
+.. math:: \hat{X} = \begin{bmatrix} T_d && G_{ds} \end{bmatrix} \begin{bmatrix} c_0 \\ C \end{bmatrix} = \underbrace{\begin{bmatrix} T_d && G_{ds} \end{bmatrix} C_{i}}_\text{mapping matrix}X
+
+
+To interpolate bad channels, one can simply do:
+
+	>>> evoked.interpolate_bads(reset_bads=False)
+
+and the bad channel will be fixed
+
+.. image:: ../../_images/sphx_glr_plot_interpolate_bad_channels_002.png
+   :align: center
+   :height: 300 px
+
+.. topic:: Examples:
+
+	* :ref:`sphx_glr_auto_examples_preprocessing_plot_interpolate_bad_channels.py`
+
+References
+==========
+.. [1] Perrin, F., Pernier, J., Bertrand, O. and Echallier, JF. (1989). Spherical splines for scalp potential and current density mapping. Electroencephalography Clinical Neurophysiology, Feb; 72(2):184-7.
diff --git a/doc/manual/cookbook.rst b/doc/manual/cookbook.rst
index 6ecdb8b..aca9131 100644
--- a/doc/manual/cookbook.rst
+++ b/doc/manual/cookbook.rst
@@ -134,6 +134,20 @@ constructed using::
     >>>                     proj=True, picks=picks, baseline=(None, 0),
     >>>                     preload=True, reject=reject)
 
+.. note:: The rejection thresholds (set with argument ``reject``) are defined
+          in T / m for gradiometers, T for magnetometers and V for EEG and EOG
+          channels.
+
+
+Rejection using annotations
+---------------------------
+
+The reject keyword of :class:`mne.Epochs` is used for rejecting bad epochs
+based on peak-to-peak thresholds. Bad segments of data can also be rejected
+by marking segments of raw data with annotations. See
+:ref:`tut_artifacts_reject` and :class:`mne.Annotations` for more
+information.
+
 Once the :class:`mne.Epochs` are constructed, they can be averaged to obtain
 :class:`mne.Evoked` data as::
 
@@ -162,7 +176,7 @@ The first processing stage is the creation of various surface
 reconstructions with FreeSurfer. The recommended FreeSurfer workflow
 is summarized on the `FreeSurfer wiki pages <https://surfer.nmr.mgh.harvard.edu/fswiki/RecommendedReconstruction>`_.
 
-.. _CIHCHDAE:
+.. _setting_up_source_space:
 
 Setting up the source space
 ###########################
@@ -274,7 +288,7 @@ segmentation.
           e.g. ``conductivities=[0.3]``.
 
 Using this model, the BEM solution can be computed using
-:func:`mne.make_bem_solution`` as::
+:func:`mne.make_bem_solution` as::
 
     >>> bem_sol = make_bem_solution(model)
     >>> write_bem_solution('sample-5120-5120-5120-bem-sol.fif', bem_sol)
diff --git a/doc/manual/datasets_index.rst b/doc/manual/datasets_index.rst
index be4c59f..31484ed 100644
--- a/doc/manual/datasets_index.rst
+++ b/doc/manual/datasets_index.rst
@@ -1,12 +1,12 @@
 .. _datasets:
 
+Datasets
+########
+
 .. contents:: Contents
    :local:
    :depth: 2
 
-Datasets
-########
-
 All the dataset fetchers are available in :mod:`mne.datasets`. To download any of the datasets,
 use the ``data_path`` (fetches full dataset) or the ``load_data`` (fetches dataset partially) functions.
 
@@ -107,13 +107,14 @@ The recordings were made using the BCI2000 system. To load a subject, do::
 
     * :ref:`sphx_glr_auto_examples_decoding_plot_decoding_csp_eeg.py`
 
-Do not hesitate to contact MNE-Python developers on the `MNE mailing list`_ to discuss the possibility to add more publicly available datasets.
+Do not hesitate to contact MNE-Python developers on the
+`MNE mailing list <http://mail.nmr.mgh.harvard.edu/mailman/listinfo/mne_analysis>`_
+to discuss the possibility to add more publicly available datasets.
 
 .. _auditory dataset tutorial: http://neuroimage.usc.edu/brainstorm/DatasetAuditory
 .. _resting state dataset tutorial: http://neuroimage.usc.edu/brainstorm/DatasetResting
 .. _median nerve dataset tutorial: http://neuroimage.usc.edu/brainstorm/DatasetMedianNerveCtf
 .. _SPM faces dataset: http://www.fil.ion.ucl.ac.uk/spm/data/mmfaces/
-.. _MNE mailing list: http://mail.nmr.mgh.harvard.edu/mailman/listinfo/mne_analysis
 
 References
 ==========
diff --git a/doc/manual/decoding.rst b/doc/manual/decoding.rst
index 2e0ab48..ddd584c 100644
--- a/doc/manual/decoding.rst
+++ b/doc/manual/decoding.rst
@@ -1,9 +1,10 @@
-.. _decoding:
 
 .. contents:: Contents
    :local:
    :depth: 3
 
+.. _decoding:
+
 Decoding
 ########
 
diff --git a/doc/manual/gui/analyze.rst b/doc/manual/gui/analyze.rst
index f369258..fe5ede3 100644
--- a/doc/manual/gui/analyze.rst
+++ b/doc/manual/gui/analyze.rst
@@ -1961,7 +1961,7 @@ of the dialog contains the following items:
 
     Regularize the noise covariance before using it in whitening by
     adding a multiple of an identity matrix to the diagonal. This is
-    discussed in more detail in :ref:`CBBHEGAB`. Especially if
+    discussed in more detail in :ref:`cov_regularization`. Especially if
     EEG is included in fitting it is advisable to enter a non-zero value
     (around 0.1) here.
 
diff --git a/doc/manual/gui/browse.rst b/doc/manual/gui/browse.rst
index 692f440..f64a2c7 100644
--- a/doc/manual/gui/browse.rst
+++ b/doc/manual/gui/browse.rst
@@ -2056,7 +2056,7 @@ Since the signal-space projection modifies the signal vectors
 originating in the brain, it is necessary to apply the projection
 to the forward solution in the course of inverse computations. This
 is accomplished by mne_inverse_operator as
-described in :ref:`CBBDDBGF`. For more information on SSP,
+described in :ref:`inverse_operator`. For more information on SSP,
 please consult the references listed in :ref:`CEGIEEBB`.
 
 EEG average electrode reference
@@ -2192,7 +2192,7 @@ are read by mne_inverse_operator and
 applied to the forward solution as well as appropriate. Inclusion
 of the projections into the covariance matrix limits the possibilities
 to use the ``--bad`` and ``--proj`` options in mne_inverse_operator ,
-see :ref:`CBBDDBGF`.
+see :ref:`inverse_operator`.
 
 .. _CACGHEGC:
 
diff --git a/doc/manual/index.rst b/doc/manual/index.rst
index 6498115..dafbe1e 100644
--- a/doc/manual/index.rst
+++ b/doc/manual/index.rst
@@ -1,7 +1,7 @@
 .. _manual:
 
-Manual
-======
+User Manual
+===========
 
 If you are new to MNE, consider first reading the :ref:`cookbook`, as it
 gives some simple steps for starting with analysis. The other sections provide
@@ -13,21 +13,20 @@ and class usage information.
    :local:
    :depth: 1
 
+.. raw:: html
 
-Cookbook
---------
-
-A quick run-through of the basic steps involved in M/EEG source analysis.
+   <h2>Cookbook</h2>
+   A quick run-through of the basic steps involved in M/EEG source analysis.
 
 .. toctree::
    :maxdepth: 2
 
    cookbook
 
-Reading your data
------------------
+.. raw:: html
 
-How to get your raw data loaded in MNE.
+   <h2>Reading your data</h2>
+   How to get your raw data loaded in MNE.
 
 .. toctree::
    :maxdepth: 1
@@ -35,79 +34,91 @@ How to get your raw data loaded in MNE.
    io
    memory
 
-Preprocessing
--------------
+.. raw:: html
 
-Dealing with artifacts and noise sources in data.
+   <h2>Preprocessing</h2>
+   Dealing with artifacts and noise sources in data.
 
 .. toctree::
-   :maxdepth: 2
+   :maxdepth: 1
 
    preprocessing/ica
+   preprocessing/maxwell
    preprocessing/ssp
+   channel_interpolation
 
-Source localization
--------------------
+.. raw:: html
 
-Projecting raw data into source (brain) space.
+   <h2>Source localization</h2>
+   Projecting raw data into source (brain) space.
 
 .. toctree::
    :maxdepth: 1
 
-   source_localization/anatomy
    source_localization/forward
-   source_localization/covariance
    source_localization/inverse
    source_localization/morph
 
-Time frequency analysis
------------------------
+.. raw:: html
 
-Decomposing time-domain signals into time-frequency representations.
+   <h2>Time-frequency analysis</h2>
+   Decomposing time-domain signals into time-frequency representations.
 
 .. toctree::
    :maxdepth: 2
 
    time_frequency
 
-Statistics
-----------
+.. raw:: html
 
-Using parametric and non-parametric tests with M/EEG data.
+   <h2>Statistics</h2>
+   Using parametric and non-parametric tests with M/EEG data.
 
 .. toctree::
    :maxdepth: 2
 
    statistics
 
-Decoding
---------
+.. raw:: html
+
+   <h2>Decoding</h2>
 
 .. toctree::
-   :maxdepth: 3
+   :maxdepth: 2
 
    decoding
 
-Datasets
---------
+.. raw:: html
 
-To enable reproducibility of results, MNE-Python includes several dataset fetchers
+   <h2>Datasets</h2>
+   How to use dataset fetchers for public data
 
 .. toctree::
    :maxdepth: 2
 
    datasets_index
 
-Pitfalls
---------
+.. raw:: html
+
+   <h2>Migrating</h2>
+
+.. toctree::
+   :maxdepth: 1
+
+   migrating
+
+.. raw:: html
+
+   <h2>Pitfalls</h2>
 
 .. toctree::
    :maxdepth: 2
 
    pitfalls
 
-C tools
--------
+.. raw:: html
+
+   <h2>C Tools</h2>
 
 Additional information about various MNE-C tools.
 
@@ -117,28 +128,26 @@ Additional information about various MNE-C tools.
    c_reference
    gui/analyze
    gui/browse
-   c_cookbook
-
 
-MATLAB tools
-------------
+.. raw:: html
 
-Information about the MATLAB toolbox.
+   <h2>MATLAB Tools</h2>
+   Information about the MATLAB toolbox.
 
 .. toctree::
    :maxdepth: 2
 
    matlab
 
-Appendices
-----------
+.. raw:: html
+
+   <h2>Appendices</h2>
 
 More details about our implementations and software.
 
 .. toctree::
    :maxdepth: 1
 
-   appendix/overview
    appendix/bem_model
    appendix/martinos
    appendix/c_misc
diff --git a/doc/manual/io.rst b/doc/manual/io.rst
index 72ac893..16d6141 100644
--- a/doc/manual/io.rst
+++ b/doc/manual/io.rst
@@ -1,10 +1,14 @@
-.. _ch_convert:
 
 .. contents:: Contents
    :local:
    :depth: 2
 
-Here we describe the data reading and conversion utilities included
+.. _ch_convert:
+
+Importing MEG data
+##################
+
+This section describes the data reading and conversion utilities included
 with the MNE software. The cheatsheet below summarizes the different
 file formats supported by MNE software.
 
@@ -16,6 +20,7 @@ MEG                   4-D Neuroimaging / BTI      dir       :func:`mne.io.read_r
 MEG                   CTF                         dir       :func:`mne.io.read_raw_ctf`
 MEG                   KIT                         sqd       :func:`mne.io.read_raw_kit` and :func:`mne.read_epochs_kit`
 EEG                   Brainvision                .vhdr      :func:`mne.io.read_raw_brainvision`
+EEG                   Neuroscan CNT              .cnt       :func:`mne.io.read_raw_cnt`
 EEG                   European data format       .edf       :func:`mne.io.read_raw_edf`
 EEG                   Biosemi data format        .bdf       :func:`mne.io.read_raw_edf`
 EEG                   EGI simple binary          .egi       :func:`mne.io.read_raw_egi`
@@ -30,12 +35,6 @@ Electrode locations   EEGLAB loc, locs, eloc     Misc       :func:`mne.channels.
     supported data formats can be read in MNE-Python directly without first
     saving it to fif.
 
-Importing MEG data
-##################
-
-This section describes reading and converting of various MEG data formats.
-
-
 Elekta NeuroMag (.fif)
 ======================
 
@@ -222,7 +221,7 @@ from available analog trigger channel data by specifying the following parameter
 
 - A list of trigger channels (stim) or default triggers with order: '<' | '>'
   Channel-value correspondence when converting KIT trigger channels to a
-  Neuromag-style stim channel. By default, we assume the first eight miscellanous
+  Neuromag-style stim channel. By default, we assume the first eight miscellaneous
   channels are trigger channels. For '<', the largest values are assigned
   to the first channel (little endian; default). For '>', the largest values are
   assigned to the last channel (big endian). Can also be specified as a list of
@@ -289,6 +288,24 @@ BioSemi. It can also be read in using :func:`mne.io.read_raw_edf`.
 
 .. warning:: The data samples in a BDF file are represented in a 3-byte (24-bit) format. Since 3-byte raw data buffers are not presently supported in the fif format these data will be changed to 4-byte integers in the conversion.
 
+Neuroscan CNT data format (.cnt)
+================================
+
+CNT files can be read in using :func:`mne.io.read_raw_cnt`.
+The channel locations can be read from a montage or the file header. If read
+from the header, the data channels (channels that are not assigned to EOG, ECG,
+EMG or misc) are fit to a sphere and assigned a z-value accordingly. If a
+non-data channel does not fit to the sphere, it is assigned a z-value of 0.
+See :ref:`BJEBIBAI`
+
+.. warning::
+    Reading channel locations from the file header may be dangerous, as the
+    x_coord and y_coord in ELECTLOC section of the header do not necessarily
+    translate to absolute locations. Furthermore, EEG-electrode locations that
+    do not fit to a sphere will distort the layout when computing the z-values.
+    If you are not sure about the channel locations in the header, use of a
+    montage is encouraged.
+
 EGI simple binary (.egi)
 ========================
 
diff --git a/doc/manual/matlab.rst b/doc/manual/matlab.rst
index 0f3d37d..9e2e81a 100644
--- a/doc/manual/matlab.rst
+++ b/doc/manual/matlab.rst
@@ -1,5 +1,4 @@
 
-
 .. _ch_matlab:
 
 ==============
@@ -1171,12 +1170,12 @@ The documented structures are:
     |                     |             | source orientations).                                    |
     +---------------------+-------------+----------------------------------------------------------+
     | sing                | double      | The singular values, *i.e.*, the diagonal values of      |
-    |                     | (nchan)     | :math:`\Lambda`, see :ref:`CHDBEHBC`.                    |
+    |                     | (nchan)     | :math:`\Lambda`, see :ref:`mne_solution`.                |
     +---------------------+-------------+----------------------------------------------------------+
-    | eigen_leads         | double      | The matrix :math:`V`, see :ref:`CHDBEHBC`.               |
+    | eigen_leads         | double      | The matrix :math:`V`, see :ref:`mne_solution`.           |
     |                     | (:,nchan)   |                                                          |
     +---------------------+-------------+----------------------------------------------------------+
-    | eigen_fields        | double      | The matrix :math:`U^T`, see :ref:`CHDBEHBC`.             |
+    | eigen_fields        | double      | The matrix :math:`U^T`, see :ref:`mne_solution`.         |
     |                     | (nchan,     |                                                          |
     |                     | nchan)      |                                                          |
     +---------------------+-------------+----------------------------------------------------------+
@@ -1201,8 +1200,8 @@ The documented structures are:
     |                     |             | factors. Dimension is either nsource (fixed source       |
     |                     |             | orientations) or 3*nsource (all source orientations).    |
     +---------------------+-------------+----------------------------------------------------------+
-    | reginv              | double      | The diagonal matrix :math:`\Gamma`, see :ref:`CHDBEHBC`. |
-    |                     | (nchan)     |                                                          |
+    | reginv              | double      | The diagonal matrix :math:`\Gamma`, see                  |
+    |                     | (nchan)     | :ref:`mne_solution`.                                     |
     +---------------------+-------------+----------------------------------------------------------+
     | noisenorm           | double(:)   | A sparse matrix containing the noise normalization       |
     |                     |             | factors. Dimension is either nsource (fixed source       |
diff --git a/doc/manual/memory.rst b/doc/manual/memory.rst
index 95f1d8c..16f1c03 100644
--- a/doc/manual/memory.rst
+++ b/doc/manual/memory.rst
@@ -1,9 +1,10 @@
-.. _memory:
 
 .. contents:: Contents
    :local:
    :depth: 3
 
+.. _memory:
+
 Memory-efficient IO
 ###################
 
@@ -18,7 +19,7 @@ MNE-Python can read data on-demand using the ``preload`` option provided in :ref
     from mne.datasets import sample
     data_path = sample.data_path()
     raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
 
 .. note:: Filtering does not work with ``preload=False``.
 
@@ -38,8 +39,8 @@ has been loaded with ``preload=True``. Preloading is also supported for :func:`m
 
 .. warning:: This comes with a caveat. When ``preload=False``, data rejection based on peak-to-peak thresholds is executed when the data is loaded from disk, *not* when the ``Epochs`` object is created.
 
-To explicitly reject artifacts with ``preload=False``, use the function :func:`mne.Epochs.drop_bad_epochs`.
+To explicitly reject artifacts with ``preload=False``, use the function :func:`mne.Epochs.drop_bad`.
 
 Loading data explicitly
 =======================
-To load the data if ``preload=False`` was initially selected, use the functions :func:`mne.Raw.load_data` and :func:`mne.Epochs.load_data`.
\ No newline at end of file
+To load the data if ``preload=False`` was initially selected, use the functions :func:`mne.io.Raw.load_data` and :func:`mne.Epochs.load_data`.
diff --git a/doc/manual/migrating.rst b/doc/manual/migrating.rst
new file mode 100644
index 0000000..1e732bc
--- /dev/null
+++ b/doc/manual/migrating.rst
@@ -0,0 +1,54 @@
+.. _migrating:
+
+Migrating from EEGLAB
+=====================
+
+To read in data exported from EEGLAB, MNE offers an EDF reader :func:`mne.io.read_raw_edf` and a ``set`` file reader.
+To read in `set` files containing ``raw`` data, use :func:`mne.io.read_raw_eeglab` and to read in ``set`` files containing
+``epochs`` data, use :func:`mne.read_epochs_eeglab`.
+
+Here is a cheatsheet to help users migrate painlessly from EEGLAB. For the sake of clarity, let us assume
+that the following are already defined or known: the file name ``fname``, time interval of the epochs ``tmin`` and ``tmax``,
+and the conditions ``cond1`` and ``cond2``. The variables ``l_freq`` and ``h_freq`` are the frequencies (in Hz) below which
+and above which to filter out data.
+
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Processing step   | EEGLAB function                                              | MNE                                                                         |
++===================+==============================================================+=============================================================================+
+| Get started       | | addpath(...);                                              | | import mne                                                                |
+|                   | | eeglab;                                                    | | from mne import io,     :class:`Epochs <mne.Epochs>`                      |
+|                   |                                                              | | from mne.preprocessing import     :class:`ICA <mne.preprocessing.ICA>`    |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Import data       | EEG = pop_fileio(fname);                                     | | :func:`raw = io.read_raw_fif(fname) <mne.io.Raw>`                         |
+|                   |                                                              | | :func:`raw = io.read_raw_edf(fname) <mne.io.read_raw_edf>`                |
+|                   |                                                              | | :func:`raw = io.read_raw_eeglab(fname) <mne.io.read_raw_eeglab>`          |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Filter data       | EEG = pop_eegfiltnew(EEG, l_freq, h_freq);                   | :func:`raw.filter(l_freq, h_freq) <mne.io.Raw.filter>`                      |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Run ICA           | EEG = pop_runica(EEG);                                       | :func:`ica.fit(raw) <mne.preprocessing.ICA.fit>`                            |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Epoch data        | | event_id = {'cond1', 'cond2'};                             | | :func:`events = mne.find_events(raw) <mne.find_events>`                   |
+|                   | | Epochs = pop_epochs(EEG, event_id, [tmin, tmax]);          | | :py:class:`event_id = dict(cond1=32, cond2=64) <dict>`                    |
+|                   | |                                                            | | :class:`epochs = Epochs(raw, events, event_id, tmin, tmax) <mne.Epochs>`  |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Selecting epochs  | Epochs = pop_epochs(EEG_epochs, {cond2});                    | :class:`epochs[cond2] <mne.Epochs>`                                         |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| ERP butterfly plot| pop_timtopo(EEG_epochs, ...);                                | :func:`evoked.plot() <mne.Evoked.plot>`                                     |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Contrast ERPs     | pop_compareerps(EEG_epochs1, EEG_epochs2);                   | :func:`(evoked1 - evoked2).plot() <mne.Evoked.__sub__>`                     |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+| Save data         | EEG = pop_saveset(EEG, fname);                               | | :func:`raw.save(fname) <mne.io.Raw.save>`                                 |
+|                   |                                                              | | :func:`epochs.save(fname) <mne.Epochs.save>`                              |
+|                   |                                                              | | :func:`evoked.save(fname) <mne.Evoked.save>`                              |
++-------------------+--------------------------------------------------------------+-----------------------------------------------------------------------------+
+
+Note that MNE has functions to read a variety of file formats, not just :func:`mne.io.Raw`. The interested user is directed to the :ref:`IO documentation <ch_convert>`.
+
+Pitfalls
+--------
+
+* Python methods often operate in-place. This means the object the method is called on is modified in-place (e.g., see the filter example above).
+  This can be confusing to new users migrating from Matlab. However, it is also possible to ask MNE functions not to modify the input.
+  To do this, call the ``copy`` method of the object (.e.g, use  :func:`raw_filtered = raw.copy().filter(l_freq, h_freq) <mne.io.Raw.copy>`).
+* The concept of channel types is critical in MNE because it supports analysis of multimodal data (e.g., EEG, MEG, EOG, Stim channel)
+  whereas most EEGLAB functions assume the same channel type (EEG).
diff --git a/doc/manual/pitfalls.rst b/doc/manual/pitfalls.rst
index 35c06d8..b0e43e6 100644
--- a/doc/manual/pitfalls.rst
+++ b/doc/manual/pitfalls.rst
@@ -1,9 +1,10 @@
-.. _pitfalls:
 
 .. contents:: Contents
    :local:
    :depth: 2
 
+.. _pitfalls:
+
 Pitfalls
 ########
 
@@ -14,14 +15,29 @@ Two evoked objects can be contrasted using::
 
 	>>> evoked = evoked_cond1 - evoked_cond2
 
-Note, however that the number of trials used to obtain the averages for ``evoked_cond1`` and ``evoked_cond2`` are taken into account when computing ``evoked``. That is, what you get is a weighted average, not a simple element-by-element subtraction. To do a uniform (not weighted) average, use the function :func:`mne.combine_evoked`.
+Note, however that the number of trials used to obtain the averages for
+``evoked_cond1`` and ``evoked_cond2`` are taken into account when computing
+``evoked``. That is, what you get is a weighted average, not a simple
+element-by-element subtraction. To do a uniform (not weighted) average, use
+the function :func:`mne.combine_evoked`.
 
 Float64 vs float32
 ==================
 
-MNE-Python performs all computation in memory using the double-precision 64-bit floating point format. This means that the data is typecasted into `float64` format as soon as it is read into memory. The reason for this is that operations such as filtering, preprocessing etc. are more accurate when using the double-precision format. However, for backward compatibility, it writes the `fif` files in a 32-bit format by default. This is advantageous when saving data to disk as it consumes les [...]
+MNE-Python performs all computation in memory using the double-precision
+64-bit floating point format. This means that the data is typecasted into
+`float64` format as soon as it is read into memory. The reason for this is
+that operations such as filtering, preprocessing etc. are more accurate when
+using the double-precision format. However, for backward compatibility, it
+writes the `fif` files in a 32-bit format by default. This is advantageous
+when saving data to disk as it consumes less space.
 
-However, if the users save intermediate results to disk, they should be aware that this may lead to loss in precision. The reason is that writing to disk is 32-bit by default and the typecasting to 64-bit does not recover the lost precision. In case you would like to retain the 64-bit accuracy, there are two possibilities: 
+However, if the users save intermediate results to disk, they should be aware
+that this may lead to loss in precision. The reason is that writing to disk is
+32-bit by default and then typecasting to 64-bit does not recover the lost
+precision. In case you would like to retain the 64-bit accuracy, there are two
+possibilities:
 
 * Chain the operations in memory and not save intermediate results
-* Save intermediate results but change the ``dtype`` used for saving. However, this may render the files unreadable in other software packages
+* Save intermediate results but change the ``dtype`` used for saving. However,
+  this may render the files unreadable in other software packages
diff --git a/doc/manual/preprocessing/maxwell.rst b/doc/manual/preprocessing/maxwell.rst
new file mode 100644
index 0000000..1e23135
--- /dev/null
+++ b/doc/manual/preprocessing/maxwell.rst
@@ -0,0 +1,76 @@
+.. _maxwell:
+
+Maxwell filtering
+#################
+
+.. contents:: Contents
+   :local:
+   :depth: 2
+
+Maxwell filtering in mne-python can be used to suppress sources of external
+intereference and compensate for subject head movements. 
+
+About Maxwell filtering
+-----------------------
+The principles behind Maxwell filtering are covered in relevant references
+[1]_ [2]_ as well as the Elekta MaxFilter manual; see those materials
+for basic principles and algorithm descriptions.
+
+In mne-python Maxwell filtering of raw data can be done using the
+:func:`mne.preprocessing.maxwell_filter` function. 
+
+.. warning:: Automatic bad channel detection is not currently implemented.
+             It is critical to mark bad channels before running Maxwell
+             filtering, so data should be inspected and marked accordingly
+             prior to running this algorithm.
+
+Our Maxwell filtering algorithm currently provides multiple features,
+including:
+
+    * Bad channel reconstruction
+    * Cross-talk cancellation
+    * Fine calibration correction
+    * tSSS
+    * Coordinate frame translation
+    * Regularization of internal components using information theory
+    * Raw movement compensation
+      (using head positions estimated by MaxFilter)
+    * cHPI subtraction (see :func:`mne.chpi.filter_chpi`)
+    * Handling of 3D (in addition to 1D) fine calibration files
+    * Epoch-based movement compensation as described in [1]_ through
+      :func:`mne.epochs.average_movements`
+    * **Experimental** processing of data from (un-compensated)
+      non-Elekta systems
+
+Movement compensation
+---------------------
+When subject head movements are recorded continuously using continuous HPI
+(cHPI) and subjects are expected to move during the recording (e.g., when
+recording data in children), movement compensation can be performed to
+correct for head movements. Movement compensation can be performed two ways:
+
+1. Raw movement compensation: :func:`mne.preprocessing.maxwell_filter` using
+   the ``pos`` argument.
+
+2. Evoked movement compensation: :func:`mne.epochs.average_movements`.
+
+Each of these requires time-varying estimates of head positions, which can
+currently be obtained from MaxFilter using the ``-headpos`` and ``-hp``
+arguments (see the MaxFilter manual for details). The resulting
+MaxFilter-style head position information can be read using
+:func:`mne.chpi.read_head_pos` and passed to mne-python's movement
+compensation algorithms.
+
+References
+----------
+.. [1] Taulu S. and Kajola M. "Presentation of electromagnetic
+       multichannel data: The signal space separation method,"
+       Journal of Applied Physics, vol. 97, pp. 124905 1-10, 2005.
+
+       http://lib.tkk.fi/Diss/2008/isbn9789512295654/article2.pdf
+
+.. [2] Taulu S. and Simola J. "Spatiotemporal signal space separation
+       method for rejecting nearby interference in MEG measurements,"
+       Physics in Medicine and Biology, vol. 51, pp. 1759-1768, 2006.
+
+       http://lib.tkk.fi/Diss/2008/isbn9789512295654/article3.pdf
diff --git a/doc/manual/preprocessing/ssp.rst b/doc/manual/preprocessing/ssp.rst
index e331501..a5011c2 100644
--- a/doc/manual/preprocessing/ssp.rst
+++ b/doc/manual/preprocessing/ssp.rst
@@ -1,12 +1,12 @@
 .. _ssp:
 
+Projections
+###########
+
 .. contents:: Contents
    :local:
    :depth: 3
 
-Projections
-###########
-
 The Signal-Space Projection (SSP) method
 ========================================
 
@@ -89,7 +89,7 @@ Since the signal-space projection modifies the signal vectors
 originating in the brain, it is necessary to apply the projection
 to the forward solution in the course of inverse computations. This
 is accomplished by mne_inverse_operator as
-described in :ref:`CBBDDBGF`. For more information on SSP,
+described in :ref:`inverse_operator`. For more information on SSP,
 please consult the references listed in :ref:`CEGIEEBB`.
 
 .. _CACFGIEC:
@@ -155,6 +155,11 @@ data of empty room recordings or averaged ECG or EOG artifacts.
 
 A second set of highlevel convenience functions is provided to compute projection vector for typical usecases. This includes :func:`mne.preprocessing.compute_proj_ecg` and :func:`mne.preprocessing.compute_proj_eog` for computing the ECG and EOG related artifact components, respectively. For computing the eeg reference signal, the function :func:`mne.preprocessing.ssp.make_eeg_average_ref_proj` can be used. The underlying implementation can be found in :mod:`mne.preprocessing.ssp`.
 
+.. warning:: It is best to compute projectors only on channels that will be
+             used (e.g., excluding bad channels). This ensures that
+             projection vectors will remain ortho-normalized and that they
+             properly capture the activity of interest.
+
 .. _remove_projector:
 
 Adding/removing projectors
@@ -190,7 +195,7 @@ The suggested pipeline is ``proj=True`` in epochs (it's computationally cheaper
 
 .. topic:: Examples:
 
-	* :ref:`example_visualization_plot_evoked_delayed_ssp.py`: Interactive SSP
-	* :ref:`example_visualization_plot_evoked_topomap_delayed_ssp.py`: Interactive SSP
-	* :ref:`example_visualization_plot_ssp_projs_topomaps.py`: SSP sensitivities in sensor space
-	* :ref:`example_visualization_plot_ssp_projs_sensitivity_map.py`: SSP sensitivities in source space
+	* :ref:`sphx_glr_auto_examples_visualization_plot_evoked_delayed_ssp.py`: Interactive SSP
+	* :ref:`sphx_glr_auto_examples_visualization_plot_evoked_topomap_delayed_ssp.py`: Interactive SSP
+	* :ref:`sphx_glr_auto_examples_visualization_plot_ssp_projs_topomaps.py`: SSP sensitivities in sensor space
+	* :ref:`sphx_glr_auto_examples_visualization_plot_ssp_projs_sensitivity_map.py`: SSP sensitivities in source space
diff --git a/doc/manual/sample_dataset.rst b/doc/manual/sample_dataset.rst
index 73f1712..871ceb2 100644
--- a/doc/manual/sample_dataset.rst
+++ b/doc/manual/sample_dataset.rst
@@ -144,7 +144,7 @@ The source space with a 5-mm grid spacing is set up by saying:
 ``mne_setup_source_space --ico -6``
 
 This command sets up the source-space related files in directory ``subjects/sample/bem`` as
-described in :ref:`CIHCHDAE`.
+described in :ref:`setting_up_source_space`.
 
 .. _CHDJDGBD:
 
@@ -409,7 +409,7 @@ Another piece of information derived from the raw data file
 is the estimate for the noise-covariance matrix, which can be computed
 with the command:
 
-``mne_process_raw --raw sample_audvis_raw.fif `` ``--lowpass 40 --projon `` ``--savecovtag -cov --cov audvis.cov``
+``mne_process_raw --raw sample_audvis_raw.fif --lowpass 40 --projon --savecovtag -cov --cov audvis.cov``
 
 Using the definitions in ``audvis.cov`` , this command
 will create the noise-covariance matrix file ``sample_audvis-cov.fif`` .
@@ -623,8 +623,9 @@ The most exciting part of this exercise is to explore the
 data and the current estimates in mne_analyze .
 This section contains some useful steps to get you started. A lot
 of information about the capabilities of mne_analyze is
-given in :ref:`ch_interactive_analysis`. Batch-mode processing with mne_make_movie is discussed
-in :ref:`CBBECEDE`. Cross-subject averaging is covered in :ref:`ch_morph`.
+given in :ref:`ch_interactive_analysis`. Batch-mode processing with
+mne_make_movie is discussed in :ref:`movies_and_snapshots`. Cross-subject
+averaging is covered in :ref:`ch_morph`.
 
 Before launching mne_analyze it
 is advisable to go to the directory ``MEG/sample`` . The
diff --git a/doc/manual/source_localization/covariance.rst b/doc/manual/source_localization/covariance.rst
deleted file mode 100644
index 553416d..0000000
--- a/doc/manual/source_localization/covariance.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-.. _covariance:
-
-==========
-Covariance
-==========
diff --git a/doc/manual/source_localization/forward.rst b/doc/manual/source_localization/forward.rst
index de122b8..0d50743 100644
--- a/doc/manual/source_localization/forward.rst
+++ b/doc/manual/source_localization/forward.rst
@@ -180,7 +180,7 @@ and
 
 .. tabularcolumns:: |p{0.2\linewidth}|p{0.3\linewidth}|p{0.5\linewidth}|
 .. _CHDJDEDJ:
-.. table:: Coordinate transformations in FreeSurfer and MNE software packages. The symbols :math:`T_x` are defined in :ref:`CHDFFJIJ`. Note: mne_make_cor_set /mne_setup_mri prior to release 2.6 did not include transformations :math:`T_3`, :math:`T_4`, :math:`T_-`, and :math:`T_+` in the fif files produced.
+.. table:: Coordinate transformations in FreeSurfer and MNE software packages.
 
     +------------------------------+-------------------------------+--------------------------------------+
     | Transformation               | FreeSurfer                    | MNE                                  |
@@ -215,6 +215,8 @@ and
     |                              |                               | mgz or mgh format.                   |
     +------------------------------+-------------------------------+--------------------------------------+
 
+.. note:: The symbols :math:`T_x` are defined in :ref:`CHDFFJIJ`. mne_make_cor_set /mne_setup_mri prior to release 2.6 did not include transformations :math:`T_3`, :math:`T_4`, :math:`T_-`, and :math:`T_+` in the fif files produced.
+
 .. _BJEBIBAI:
 
 The head and device coordinate systems
@@ -257,7 +259,7 @@ Creating a surface-based source space
 The fif format source space files containing the dipole locations
 and orientations are created with the utility :ref:`mne_make_source_space`.
 This utility is usually invoked by the convenience script :ref:`mne_setup_source_space`,
-see :ref:`CIHCHDAE`.
+see :ref:`setting_up_source_space`.
 
 
 .. _BJEFEHJI:
@@ -483,7 +485,7 @@ in Neuromag software. The coil types fall in two general categories:
 - Axial gradiometers and planar gradiometers
   and
 
-- Planar gradiometers.
+- Planar magnetometers.
 
 For axial sensors, the *z* axis of the
 local coordinate system is parallel to the field component detected, *i.e.*,
@@ -524,7 +526,7 @@ The columns of the tables contain the following data:
 
 .. tabularcolumns:: |p{0.1\linewidth}|p{0.3\linewidth}|p{0.1\linewidth}|p{0.25\linewidth}|p{0.2\linewidth}|
 .. _BGBBHGEC:
-.. table:: Normal coil descriptions. Note: If a plus-minus sign occurs in several coordinates, all possible combinations have to be included.
+.. table:: Normal coil descriptions.
 
     +------+-------------------------+----+----------------------------------+----------------------+
     | Id   | Description             | n  | r/mm                             | w                    |
@@ -581,6 +583,8 @@ The columns of the tables contain the following data:
     |      | diagonal gradients      |    |                                  |                      |
     +------+-------------------------+----+----------------------------------+----------------------+
 
+.. note:: If a plus-minus sign occurs in several coordinates, all possible combinations have to be included.
+
 .. tabularcolumns:: |p{0.1\linewidth}|p{0.3\linewidth}|p{0.05\linewidth}|p{0.25\linewidth}|p{0.15\linewidth}|
 .. _CHDBDFJE:
 .. table:: Accurate coil descriptions
diff --git a/doc/manual/source_localization/inverse.rst b/doc/manual/source_localization/inverse.rst
index a13e55b..6e2d866 100644
--- a/doc/manual/source_localization/inverse.rst
+++ b/doc/manual/source_localization/inverse.rst
@@ -2,9 +2,9 @@
 
 .. _ch_mne:
 
-=====================
-The current estimates
-=====================
+==================================
+The minimum-norm current estimates
+==================================
 
 .. contents:: Contents
    :local:
@@ -13,12 +13,17 @@ The current estimates
 Overview
 ########
 
-This Chapter describes the computation of the minimum-norm
-estimates. This is accomplished with two programs: *mne_inverse_operator* and *mne_make_movie*.
-The chapter starts with a mathematical description of the method,
-followed by description of the two software modules. The interactive
-program for inspecting data and inverse solutions, mne_analyze ,
-is covered in :ref:`ch_interactive_analysis`.
+This page describes the mathematical concepts and the
+computation of the minimum-norm estimates.
+Using the UNIX commands this is accomplished with two programs:
+:ref:`mne_inverse_operator` and :ref:`mne_make_movie` or in Python
+using :func:`mne.minimum_norm.make_inverse_operator`
+and the ``apply`` functions. The use of these functions is
+presented in the tutorial :ref:`tut_inverse_mne_dspm`.
+
+The page starts with a mathematical description of the method.
+The interactive program for inspecting data and inverse solutions,
+:ref:`mne_analyze`, is covered in :ref:`ch_interactive_analysis`.
 
 .. _CBBDJFBJ:
 
@@ -65,7 +70,7 @@ The expected value of the current amplitudes at time *t* is
 then given by :math:`\hat{j}(t) = Mx(t)`, where :math:`x(t)` is
 a vector containing the measured MEG and EEG data values at time *t*.
 
-.. _CBBHAAJJ:
+.. _mne_regularization:
 
 Regularization
 ==============
@@ -124,9 +129,19 @@ such that :math:`\text{trace}(\tilde{G} R \tilde{G}^T) / \text{trace}(I) = 1`. W
 can approximate :math:`\lambda^2 \sim 1/SNR`, where SNR is
 the (power) signal-to-noise ratio of the whitened data.
 
-.. note:: The definition of the signal to noise-ratio/ :math:`\lambda^2` relationship    given above works nicely for the whitened forward solution. In the    un-whitened case scaling with the trace ratio :math:`\text{trace}(GRG^T) / \text{trace}(C)` does not make sense, since the diagonal elements summed have, in general,    different units of measure. For example, the MEG data are expressed    in T or T/m whereas the unit of EEG is Volts.
+.. note::
+    The definition of the signal to noise-ratio/ :math:`\lambda^2` relationship
+    given above works nicely for the whitened forward solution. In the
+    un-whitened case scaling with the trace ratio
+    :math:`\text{trace}(GRG^T) / \text{trace}(C)`
+    does not make sense, since the diagonal elements summed have, in general,
+    different units of measure. For example, the MEG data are expressed
+    in T or T/m whereas the unit of EEG is Volts.
 
-.. _CBBHEGAB:
+See :ref:`tut_compute_covariance` for example of noise covariance
+computation and whitening.
+
+.. _cov_regularization:
 
 Regularization of the noise-covariance matrix
 =============================================
@@ -137,15 +152,14 @@ the smallest eigenvalues of its estimate are usually inaccurate
 and smaller than the true eigenvalues. Depending on the seriousness
 of this problem, the following quantities can be affected:
 
-- The model data predicted by the current
-  estimate,
+- The model data predicted by the current estimate,
 
 - Estimates of signal-to-noise ratios, which lead to estimates
-  of the required regularization, see :ref:`CBBHAAJJ`,
+  of the required regularization, see :ref:`mne_regularization`,
 
 - The estimated current values, and
 
-- The noise-normalized estimates, see :ref:`CBBEAICH`.
+- The noise-normalized estimates, see :ref:`noise_normalization`.
 
 Fortunately, the latter two are least likely to be affected
 due to regularization of the estimates. However, in some cases especially
@@ -153,7 +167,7 @@ the EEG part of the noise-covariance matrix estimate can be deficient, *i.e.*,
 it may possess very small eigenvalues and thus regularization of
 the noise-covariance matrix is advisable.
 
-The MNE software accomplishes the regularization by replacing
+Historically, the MNE software accomplishes the regularization by replacing
 a noise-covariance matrix estimate :math:`C` with
 
 .. math::    C' = C + \sum_k {\varepsilon_k \bar{\sigma_k}^2 I^{(k)}}\ ,
@@ -164,15 +178,17 @@ gradiometers and magnetometers, and EEG), :math:`\varepsilon_k` are
 the corresponding regularization factors, :math:`\bar{\sigma_k}` are
 the average variances across the channel groups, and :math:`I^{(k)}` are
 diagonal matrices containing ones at the positions corresponding
-to the channels contained in each channel group. The values :math:`\varepsilon_k` can
-be adjusted with the regularization options ``--magreg`` , ``--gradreg`` ,
-and ``--eegreg`` specified at the time of the inverse operator
-decomposition, see :ref:`CBBDDBGF`. The convenience script mne_do_inverse_solution has
-the ``--magreg`` and ``--gradreg`` combined to
+to the channels contained in each channel group.
+
+Using the UNIX tools :ref:`mne_inverse_operator`, the values
+:math:`\varepsilon_k` can be adjusted with the regularization options
+``--magreg`` , ``--gradreg`` , and ``--eegreg`` specified at the time of the
+inverse operator decomposition, see :ref:`inverse_operator`. The convenience script
+:ref:`mne_do_inverse_solution` has the ``--magreg`` and ``--gradreg`` combined to
 a single option, ``--megreg`` , see :ref:`CIHCFJEI`.
 Suggested range of values for :math:`\varepsilon_k` is :math:`0.05 \dotso 0.2`.
 
-.. _CHDBEHBC:
+.. _mne_solution:
 
 Computation of the solution
 ===========================
@@ -215,7 +231,7 @@ independent of  :math:`L` and, for fixed :math:`\lambda`,
 we see directly that :math:`j(t)` is independent
 of :math:`L`.
 
-.. _CBBEAICH:
+.. _noise_normalization:
 
 Noise normalization
 ===================
@@ -248,7 +264,7 @@ see directly that
 
 .. math::    \tilde{M} \tilde{M}^T\ = \bar{V} \Gamma^2 \bar{V}^T\ .
 
-Under the conditions expressed at the end of :ref:`CHDBEHBC`, it follows that the *t*-statistic values associated
+Under the conditions expressed at the end of :ref:`mne_solution`, it follows that the *t*-statistic values associated
 with fixed-orientation sources) are thus proportional to :math:`\sqrt{L}` while
 the *F*-statistic employed with free-orientation sources is proportional
 to :math:`L`, correspondingly.
@@ -279,13 +295,13 @@ where the diagonal matrix :math:`\Pi` has
 elements :math:`\pi_k = \lambda_k \gamma_k` The predicted data is
 thus expressed as the weighted sum of the 'recolored eigenfields' in :math:`C^{^1/_2} U`.
 
-.. _CBBDBHDI:
+.. _patch_stats:
 
 Cortical patch statistics
 =========================
 
 If the ``--cps`` option was used in source space
-creation (see :ref:`CIHCHDAE`) or if mne_add_patch_info described
+creation (see :ref:`setting_up_source_space`) or if mne_add_patch_info described
 in :ref:`mne_add_patch_info` was run manually the source space file
 will contain for each vertex of the cortical surface the information
 about the source space point closest to it as well as the distance
@@ -342,7 +358,7 @@ of the surface normal data:
   to fLOC except that the value given with the ``--loosevar`` option
   will be multiplied by :math:`\sigma_d`, defined above.
 
-.. _CBBDFJIE:
+.. _depth_weighting:
 
 Depth weighting
 ===============
@@ -362,7 +378,7 @@ the order of the depth weighting, specified with the ``--weightexp`` option
 to mne_inverse_operator . The
 maximal amount of depth weighting can be adjusted ``--weightlimit`` option.
 
-.. _CBBDIJHI:
+.. _mne_fmri_estimates:
 
 fMRI-guided estimates
 =====================
@@ -433,18 +449,18 @@ we have
 
 .. math::    1 / L_{eff} = \sum_{i = 1}^n {1/{L_i}}
 
-.. _CBBDDBGF:
+.. _inverse_operator:
 
 Inverse-operator decomposition
 ##############################
 
 The program :ref:`mne_inverse_operator` calculates
 the decomposition :math:`A = \tilde{G} R^C = U \Lambda \bar{V^T}`,
-described in :ref:`CHDBEHBC`. It is normally invoked from the convenience
+described in :ref:`mne_solution`. It is normally invoked from the convenience
 script :ref:`mne_do_inverse_operator`. 
 
 
-.. _CBBECEDE:
+.. _movies_and_snapshots:
 
 Producing movies and snapshots
 ##############################
@@ -460,7 +476,7 @@ process, see :ref:`ch_morph`, and read into Matlab using the MNE
 Matlab toolbox, see :ref:`ch_matlab`.
 
 
-.. _CBBCGHAH:
+.. _computing_inverse:
 
 Computing inverse from raw and evoked data
 ##########################################
@@ -474,7 +490,7 @@ or converted to Matlab format using either :ref:`mne_convert_mne_data`,
 :ref:`mne_raw2mat`, or :ref:`mne_epochs2mat`. See
 :ref:`mne_compute_raw_inverse` for command-line options.
 
-.. _CBBHJDAI:
+.. _implementation_details:
 
 Implementation details
 ======================
diff --git a/doc/manual/time_frequency.rst b/doc/manual/time_frequency.rst
index 250c176..fc99e6d 100644
--- a/doc/manual/time_frequency.rst
+++ b/doc/manual/time_frequency.rst
@@ -19,16 +19,16 @@ Many these functions return :func:`mne.SourceEstimate` objects or collections th
 The following functions are based on minimum norm estimates (MNE).
 
 - :func:`mne.minimum_norm.compute_source_psd_epochs` returns single-trial power spectral density (PSD) esitmates using multi-tapers.
-Here, the time axis actually refers to frequencies, even if labled as time.
+  Here, the time axis actually refers to frequencies, even if labeled as time.
 
-- :func:`mne.minimum_norm.compute_source_psd` returns power spectral density (PSD) esitmates from continous data usign FFT.
-Here, the time axis actually refers to frequencies, even if labled as time.
+- :func:`mne.minimum_norm.compute_source_psd` returns power spectral density (PSD) esitmates from continuous data usign FFT.
+  Here, the time axis actually refers to frequencies, even if labeled as time.
 
 - :func:`mne.minimum_norm.source_band_induced_power` returns a collection of time-domain :func:`mne.SourceEstimate` for each
-frequency band, based on Morlet-Wavelets.
+  frequency band, based on Morlet-Wavelets.
 
 - :func:`mne.minimum_norm.source_induced_power` returns power and inter-trial-coherence (ITC) as raw numpy arrays, based on Morlet-Wavelets.
 
 Alternatively, the source power spectral density can also be estimated using the DICS beamformer,
-see ``:func:mne.beamformer.dics_source_power`.
+see :func:`mne.beamformer.dics_source_power`.
  
\ No newline at end of file
diff --git a/doc/martinos.rst b/doc/martinos.rst
new file mode 100644
index 0000000..71d06de
--- /dev/null
+++ b/doc/martinos.rst
@@ -0,0 +1,35 @@
+.. _inside_martinos:
+
+Martinos Center setup
+---------------------
+
+For people within the MGH/MIT/HMS Martinos Center, MNE is available on the network.
+
+In a terminal do:
+
+.. code-block:: bash
+
+    $ setenv PATH /usr/pubsw/packages/python/anaconda/bin:${PATH}
+
+If you use Bash replace the previous instruction with:
+
+.. code-block:: bash
+
+    $ export PATH=/usr/pubsw/packages/python/anaconda/bin:${PATH}
+
+Then start the python interpreter with:
+
+.. code-block:: bash
+
+    $ ipython
+
+Then type::
+
+    >>> import mne
+
+If you get a new prompt with no error messages, you should be good to go. 
+
+We encourage all Martinos center Python users to subscribe to the
+`Martinos Python mailing list`_.
+
+.. _Martinos Python mailing list: https://mail.nmr.mgh.harvard.edu/mailman/listinfo/martinos-python
diff --git a/doc/mne_cpp.rst b/doc/mne_cpp.rst
index 86b00b1..8662c42 100644
--- a/doc/mne_cpp.rst
+++ b/doc/mne_cpp.rst
@@ -4,7 +4,15 @@
 MNE with CPP
 ======================
 
-MNE-CPP is a cross-platform application and algorithm C++ framework for MEG and EEG data analysis and acquisition. It provides a modular structure with many sub-libraries. These libraries can be easily integrated into your project to, e.g. provide full I/O support for the fiff-file format or files generated by the MNE suite. MNE-CPP comes with its own acquisition software, MNE-X, which can be used to acquire and process data from Elekta Neuromag MEG VectorView, BabyMEG, TMSI EEG and eegosports.
+MNE-CPP is a cross-platform application and algorithm C++ framework for
+MEG and EEG data analysis and acquisition. It provides a modular
+structure with many sub-libraries. These libraries can be easily
+integrated into your project to, e.g. provide full I/O support for the
+FIF-file format or files generated by the MNE suite. MNE-CPP comes with
+its own acquisition software, MNE-X, which can be used to acquire and
+process data from Elekta Neuromag MEG VectorView, BabyMEG,
+TMSI EEG and eegosports.
+
 For further information please visit the MNE-CPP project pages:
 
   * `Project Page <http://www.tu-ilmenau.de/bmti/forschung/datenanalyse-modellierung-und-inverse-verfahren/mne-cpp/>`_
diff --git a/doc/python_reference.rst b/doc/python_reference.rst
index 95fc4fe..649d69d 100644
--- a/doc/python_reference.rst
+++ b/doc/python_reference.rst
@@ -1,8 +1,8 @@
 .. _api_reference:
 
-=============
-API Reference
-=============
+====================
+Python API Reference
+====================
 
 This is the classes and functions reference of mne-python. Functions are
 grouped thematically by analysis stage. Functions and classes that are not
@@ -25,6 +25,8 @@ Classes
 
    io.Raw
    io.RawFIF
+   io.RawArray
+   Annotations
    Epochs
    Evoked
    SourceSpaces
@@ -33,25 +35,28 @@ Classes
    MixedSourceEstimate
    Covariance
    Dipole
+   DipoleFixed
    Label
    BiHemiLabel
    Transform
-   io.Info
-   io.Projection
+   Report
+   Info
+   Projection
    preprocessing.ICA
+   preprocessing.Xdawn
    decoding.CSP
-   decoding.Scaler
-   decoding.ConcatenateChannels
+   decoding.EpochsVectorizer
    decoding.FilterEstimator
-   decoding.PSDEstimator
    decoding.GeneralizationAcrossTime
+   decoding.PSDEstimator
+   decoding.Scaler
    decoding.TimeDecoding
    realtime.RtEpochs
    realtime.RtClient
    realtime.MockRtClient
+   realtime.FieldTripClient
    realtime.StimServer
    realtime.StimClient
-   report.Report
 
 Logging and Configuration
 =========================
@@ -67,6 +72,7 @@ Logging and Configuration
    set_log_level
    set_log_file
    set_config
+   sys_info
 
 :py:mod:`mne.cuda`:
 
@@ -104,6 +110,7 @@ Functions:
   :template: function.rst
 
   read_raw_bti
+  read_raw_cnt
   read_raw_ctf
   read_raw_edf
   read_raw_kit
@@ -303,6 +310,7 @@ Functions:
 
    circular_layout
    mne_analyze_colormap
+   plot_bem
    plot_connectivity_circle
    plot_cov
    plot_dipole_amplitudes
@@ -312,7 +320,9 @@ Functions:
    plot_events
    plot_evoked
    plot_evoked_image
+   plot_evoked_topo
    plot_evoked_topomap
+   plot_evoked_joint
    plot_evoked_field
    plot_evoked_white
    plot_ica_sources
@@ -320,17 +330,19 @@ Functions:
    plot_ica_scores
    plot_ica_overlay
    plot_epochs_image
+   plot_layout
    plot_montage
    plot_projs_topomap
    plot_raw
    plot_raw_psd
+   plot_sensors
    plot_snr_estimate
    plot_source_estimates
    plot_sparse_source_estimates
    plot_tfr_topomap
-   plot_topo
    plot_topo_image_epochs
    plot_topomap
+   plot_trans
    compare_fiff
    add_background_image
 
@@ -451,7 +463,29 @@ EEG referencing:
    construct_iir_filter
    high_pass_filter
    low_pass_filter
+   notch_filter
+
+Head position estimation:
+
+.. currentmodule:: mne.chpi
+
+.. autosummary::
+   :toctree: generated/
+   :template: function.rst
+
+   filter_chpi
+   head_pos_to_trans_rot_t
+   read_head_pos
+   write_head_pos
+
+.. currentmodule:: mne.transforms
+
+.. autosummary::
+   :toctree: generated/
+   :template: function.rst
 
+   quat_to_rot
+   rot_to_quat
 
 Events
 ======
@@ -471,6 +505,7 @@ Events
    pick_events
    read_events
    write_events
+   concatenate_epochs
 
 .. currentmodule:: mne.event
 
@@ -489,7 +524,6 @@ Events
    add_channels_epochs
    average_movements
    combine_event_ids
-   concatenate_epochs
    equalize_epoch_counts
 
 Sensor Space Data
@@ -505,13 +539,13 @@ Sensor Space Data
    concatenate_raws
    equalize_channels
    grand_average
-   get_chpi_positions
    pick_channels
    pick_channels_cov
    pick_channels_forward
    pick_channels_regexp
    pick_types
    pick_types_forward
+   pick_info
    read_epochs
    read_reject_parameters
    read_selection
@@ -585,9 +619,9 @@ Functions:
    apply_forward_raw
    average_forward_solutions
    convert_forward_solution
-   do_forward_solution
    make_bem_model
    make_bem_solution
+   make_forward_dipole
    make_forward_solution
    make_field_map
    make_sphere_model
@@ -600,7 +634,7 @@ Functions:
    sensitivity_map
    setup_source_space
    setup_volume_source_space
-   write_bem_surface
+   write_bem_surfaces
    write_trans
 
 .. currentmodule:: mne.bem
@@ -622,13 +656,6 @@ Functions:
    restrict_forward_to_label
    restrict_forward_to_stc
 
-:py:mod:`mne.source_space`:
-
-.. automodule:: mne.source_space
-   :no-members:
-   :no-inherited-members:
-
-
 Inverse Solutions
 =================
 
@@ -774,8 +801,8 @@ Functions that operate on mne-python objects:
    :template: function.rst
 
    compute_epochs_csd
-   compute_epochs_psd
-   compute_raw_psd
+   psd_welch
+   psd_multitaper
    fit_iir_model_raw
    tfr_morlet
    tfr_multitaper
@@ -861,6 +888,8 @@ Statistics
    linear_regression
    linear_regression_raw
    f_mway_rm
+   f_threshold_mway_rm
+   summarize_clusters_stc
 
 Functions to compute connectivity (adjacency) matrices for cluster-level statistics
 
@@ -915,12 +944,13 @@ Classes:
    :toctree: generated/
    :template: class.rst
 
-   Scaler
-   ConcatenateChannels
-   PSDEstimator
-   FilterEstimator
    CSP
+   EpochsVectorizer
+   FilterEstimator
    GeneralizationAcrossTime
+   PSDEstimator
+   Scaler
+   TimeDecoding
 
 Realtime
 ========
@@ -944,6 +974,7 @@ Classes:
    StimServer
    StimClient
 
+
 MNE-Report
 ==========
 
diff --git a/doc/references.rst b/doc/references.rst
index 248972e..f3ed7d5 100644
--- a/doc/references.rst
+++ b/doc/references.rst
@@ -37,8 +37,8 @@ analysis. II: Inflation, flattening, and a surface-based coordinate
 system," Neuroimage, vol. 9, pp. 195-207., 1999.
 
 B. Fischl, M. I. Sereno, R. B. Tootell, and A. M. Dale, "High-resolution intersubject
-averaging and a coordinate system for the cortical surface," Hum
-Brain Mapp, vol. 8, pp. 272-84, 1999.
+averaging and a coordinate system for the cortical surface," Human
+Brain Mapping, vol. 8, pp. 272-84, 1999.
 
 .. _CEGEGDEI:
 
@@ -104,12 +104,12 @@ mapping: combining fMRI and MEG for high-resolution imaging of cortical
 activity," Neuron, vol. 26, pp. 55-67, 2000.
 
 A. K. Liu, A. M. Dale, and J. W. Belliveau, "Monte Carlo
-simulation studies of EEG and MEG localization accuracy," Hum Brain
-Mapp, vol. 16, pp. 47-62, 2002.
+simulation studies of EEG and MEG localization accuracy," Human Brain
+Mapping, vol. 16, pp. 47-62, 2002.
 
 F. H. Lin, J. W. Belliveau, A. M. Dale, and M. S. Hamalainen,
 "Distributed current estimates using cortical orientation constraints,"
-Hum Brain Mapp, vol. 27, pp. 1-13, 2006.
+Human Brain Mapping, vol. 27, pp. 1-13, 2006.
 
 T. F. Oostendorp, J. Delbeke, and D. F. Stegeman, "The conductivity
 of the human skull: results of in vivo and in vitro measurements,"
@@ -123,8 +123,8 @@ pp. 754-67, 2003.
 
 S. Lew, C. H. Wolters, A. Anwander, S. Makeig, and R. S.
 MacLeod, "Improved EEG source analysis using low-resolution conductivity
-estimation in a four-compartment finite element head model," Hum
-Brain Mapp, vol. 30, pp. 2862-78, 2009.
+estimation in a four-compartment finite element head model," Human
+Brain Mapping, vol. 30, pp. 2862-78, 2009.
 
 fMRI-weighted estimates
 #######################
diff --git a/doc/sphinxext/cited_mne.py b/doc/sphinxext/cited_mne.py
new file mode 100644
index 0000000..7de0176
--- /dev/null
+++ b/doc/sphinxext/cited_mne.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+"""Parse google scholar -> rst for MNE citations.
+
+Example usage::
+
+    $ cited_mne --backend selenium --clear
+
+"""
+
+# Author: Mainak Jas <mainak.jas at telecom-paristech.fr>
+# License : BSD 3-clause
+
+# Parts of this code were copied from google_scholar_parser
+# (https://github.com/carlosp420/google_scholar_parser)
+
+import os
+import os.path as op
+import re
+import time
+import random
+import requests
+
+import numpy as np
+from joblib import Memory
+from BeautifulSoup import BeautifulSoup
+
+from mne.externals.tempita import Template
+from mne.commands.utils import get_optparser
+
+# cache to avoid making too many calls to Google Scholar
+cachedir = 'cachedir'
+if not os.path.exists(cachedir):
+    os.mkdir(cachedir)
+mem = Memory(cachedir=cachedir, verbose=2)
+
+UA = ('Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.2.9) '
+      'Gecko/20100913 Firefox/3.6.9')
+
+# ##### Templates for citations #####
+html = (u""".. _cited
+
+Publications from MNE users
+===========================
+
+Papers citing MNE as extracted from Google Scholar (on %s).
+
+""")
+
+cite_template = Template(u"""
+{{for ii, publication in enumerate(publications)}}
+{{ii + 1}}. {{publication}}.
+{{endfor}}
+
+""")
+
+
+def parse_soup_page(soup):
+    """Parse the page using BeautifulSoup.
+
+    Parameters
+    ----------
+    soup : instance of BeautifulSoup
+        The page to be parsed.
+
+    Returns
+    -------
+    titles : list
+        The article titles.
+    authors : list
+        The name of the authors.
+    links : list
+        Hyperlinks to the articles.
+    """
+    titles, authors, links = list(), list(), list()
+    for div in soup.findAll('div'):
+        if div.name == "div" and div.get('class') == "gs_ri":
+            links.append(div.a['href'])
+            div_pub = div.findAll('div')
+            for d in div_pub:
+                if d.name == 'div' and d.get('class') == 'gs_a':
+                    authors.append(d.text)
+            titles.append(div.a.text)
+    return titles, authors, links
+
+
+def get_total_citations(soup):
+    """Get total citations."""
+    results = soup.find('div', attrs={'id': 'gs_ab_md'}).contents[0]
+    matches = re.search("About\s(\d+)\s", results)
+    if matches:
+        hits = matches.groups()[0]
+        return hits
+
+
+def _get_soup(url, backend='selenium'):
+    """Get BeautifulSoup object from url.
+
+    Parameters
+    ----------
+    url : str
+        The url to fetch.
+    backend : 'selenium' | 'requests'
+        Use selenium by default because google can ask for captcha. For
+        'selenium' backend Firefox must be installed.
+
+    Returns
+    -------
+    soup : instance of BeautifulSoup
+        The soup page from the url.
+    """
+    if backend == 'requests':
+        req = requests.get(url, headers={'User-Agent': UA})
+        html_doc = req.text
+        soup = BeautifulSoup(html_doc)
+        if soup.find('div', attrs={'id': 'gs_ab_md'}) is None:
+            print('Falling back on to selenium backend due to captcha.')
+            backend = 'selenium'
+
+    if backend == 'selenium':
+        from selenium import webdriver
+        import selenium.webdriver.support.ui as ui
+
+        driver = webdriver.Firefox()
+        # give enough time to solve captcha.
+        wait = ui.WebDriverWait(driver, 200)
+
+        driver.get(url)
+        wait.until(lambda driver: driver.find_elements_by_id('gs_ab_md'))
+
+        html_doc = driver.page_source
+        soup = BeautifulSoup(html_doc)
+        driver.close()
+
+    return soup
+
+
+ at mem.cache
+def get_citing_articles(cites_url, backend):
+    """Get the citing articles.
+
+    Parameters
+    ----------
+    cites_url: str
+        A citation url from Google Scholar.
+    backend : 'selenium' | 'requests'
+        Use selenium by default because google can ask for captcha. For
+        'selenium' backend Firefox must be installed.
+
+
+    Returns
+    -------
+    titles : list
+        The article titles.
+    authors : list
+        The name of the authors.
+    links : list
+        Hyperlinks to the articles.
+    """
+    n = random.random() * 5
+    time.sleep(n)
+    print("\nSleeping: {0} seconds".format(n))
+
+    # GS seems to allow only 20 hits per page!
+    cites_url += "&num=20"
+    soup = _get_soup(cites_url, backend=backend)
+    hits = get_total_citations(soup)
+    print("Got a total of {0} citations".format(hits))
+
+    hits = int(hits)
+    index = 0
+    titles, authors, links = list(), list(), list()
+    while hits > 1:
+        n = random.random() * 2
+        time.sleep(n)
+        if index > 0:
+            url = cites_url + "&start=" + str(index)
+        else:
+            url = cites_url
+        index += 20
+        hits -= 20
+        print("{0} more citations to process".format(hits))
+        soup = soup = _get_soup(url, backend=backend)
+        title, author, link = parse_soup_page(soup)
+        for this_title, this_author, this_link in zip(title, author, link):
+            titles.append(this_title)
+            authors.append(this_author)
+            links.append(this_link)
+
+    return titles, authors, links
+
+if __name__ == '__main__':
+    parser = get_optparser(__file__)
+    parser.add_option("-c", "--clear", dest="clear", action='store_true',
+                      help="if True, clear the cache.", default=False)
+    parser.add_option("-b", "--backend", dest="backend",
+                      help="backend for parsing (selenium | requests)",
+                      default='requests')
+    options, args = parser.parse_args()
+    backend, clear = options.backend, options.clear
+
+    if clear:
+        mem.clear()
+
+    random.seed()
+    gen_date = time.strftime("%B %d, %Y")
+    html = html % gen_date
+
+    url_tails = ['1521584321377182930', '12188330066413208874']
+    papers = ['MEG and EEG data analysis with MNE-Python',
+              'MNE software for processing MEG and EEG data']
+
+    publications = list()
+    for url_tail, paper in zip(url_tails, papers):
+        titles, authors, links = get_citing_articles(
+            'https://scholar.google.co.in/scholar?cites=%s'
+            % url_tail, backend=backend)
+
+        this_publication = list()
+        for ii in range(len(titles)):
+            pub = '`%s. <%s>`_. %s' % (titles[ii], links[ii], authors[ii])
+            this_publication.append(pub)
+
+        this_publication = [p.encode('utf8') for p in this_publication]
+        publications.append(this_publication)
+
+    # get a union of the citations for the two papers, sorted in
+    # alphabetic order
+    publications = np.union1d(publications[1], publications[0]).tolist()
+
+    # sort by year of publication
+    years = list()
+    for pub in publications:
+        m = re.search('\d{4} -', pub)
+        if m is None:
+            years.append(-1)
+        else:
+            years.append(int(m.group(0)[:-2]))
+    order = np.argsort(years)[::-1]
+    publications = [publications[idx] for idx in order]
+
+    # filter out publications not containing (http://, https://, ftp://)
+    publications = [p for p in publications if
+                    any(sub in p for sub in ('http://', 'https://', 'ftp://'))]
+
+    # create rst & cleanup
+    this_html = cite_template.substitute(publications=publications)
+    this_html = this_html.replace('…', '...')
+    html += this_html
+
+    # output an rst file
+    with open(op.join('..', 'cited.rst'), 'w') as f:
+        f.write(html.encode('utf8'))
diff --git a/doc/sphinxext/flow_diagram.py b/doc/sphinxext/flow_diagram.py
index 06a43bd..abae137 100644
--- a/doc/sphinxext/flow_diagram.py
+++ b/doc/sphinxext/flow_diagram.py
@@ -160,7 +160,7 @@ def generate_flow_diagram(app):
 # This is useful for testing/iterating to see what the result looks like
 if __name__ == '__main__':
     from mne.io.constants import Bunch
-    out_dir = op.abspath(op.join(op.dirname(__file__), '..', 'build', 'html'))
+    out_dir = op.abspath(op.join(op.dirname(__file__), '..', '_build', 'html'))
     app = Bunch(builder=Bunch(outdir=out_dir,
                               config=Bunch(make_flow_diagram=True)))
     g = generate_flow_diagram(app)
diff --git a/doc/sphinxext/commands.py b/doc/sphinxext/gen_commands.py
similarity index 73%
rename from doc/sphinxext/commands.py
rename to doc/sphinxext/gen_commands.py
index 764f995..24d2f9b 100644
--- a/doc/sphinxext/commands.py
+++ b/doc/sphinxext/gen_commands.py
@@ -9,7 +9,6 @@ from mne.utils import run_subprocess
 
 def setup(app):
     app.connect('builder-inited', generate_commands_rst)
-    # app.add_config_value('make_flow_diagram', True, 'html')
 
 
 def setup_module():
@@ -17,12 +16,10 @@ def setup_module():
     pass
 
 
-header = """
+header = """.. _python_commands:
 
-.. _python_commands
-
-Command line tools
-==================
+Command line tools using Python
+===============================
 
 .. contents:: Contents
    :local:
@@ -32,7 +29,7 @@ Command line tools
 
 command_rst = """
 
-.. _%s
+.. _gen_%s:
 
 %s
 ----------------------------------------------------------
@@ -51,15 +48,15 @@ command_rst = """
 
 """
 
+
 def generate_commands_rst(app):
-    out_dir = op.join(app.builder.outdir, 'generated')
+    out_dir = op.abspath(op.join(op.dirname(__file__), '..', 'generated'))
     out_fname = op.join(out_dir, 'commands.rst')
 
     command_path = op.join(os.path.dirname(__file__), '..', '..', 'mne', 'commands')
-    print command_path
+    print(command_path)
     fnames = glob.glob(op.join(command_path, 'mne_*.py'))
 
-
     with open(out_fname, 'w') as f:
         f.write(header)
         for fname in fnames:
@@ -71,10 +68,6 @@ def generate_commands_rst(app):
     print('Done')
 
 
-
 # This is useful for testing/iterating to see what the result looks like
 if __name__ == '__main__':
-    from mne.io.constants import Bunch
-    out_dir = op.abspath(op.join(op.dirname(__file__), '..'))
-    app = Bunch(builder=Bunch(outdir=out_dir))
-    generate_commands_rst(app)
+    generate_commands_rst()
diff --git a/doc/this_project.inc b/doc/this_project.inc
index 95c5c61..7ea0966 100644
--- a/doc/this_project.inc
+++ b/doc/this_project.inc
@@ -1,4 +1,8 @@
 .. mne-python
-.. _mne-python: http://mne-tools.github.io/mne-python-intro
+.. _`mne-python`: http://mne-tools.github.io/mne-python-intro
 .. _`mne-python GitHub`: https://github.com/mne-tools/mne-python
 .. _`mne-python sample dataset`: https://s3.amazonaws.com/mne-python/datasets/MNE-sample-data-processed.tar.gz
+.. _`mne command line utilities`: http://www.nmr.mgh.harvard.edu/martinos/userInfo/data/MNE_register/
+.. _`mne-scripts`: https://github.com/mne-tools/mne-scripts/
+.. _`MNE mailing list`: http://mail.nmr.mgh.harvard.edu/mailman/listinfo/mne_analysis
+.. _`GitHub issues page`: https://github.com/mne-tools/mne-python/issues/
diff --git a/doc/tutorials.rst b/doc/tutorials.rst
index 290e5b3..e9c1e6d 100644
--- a/doc/tutorials.rst
+++ b/doc/tutorials.rst
@@ -3,82 +3,174 @@
 Tutorials
 =========
 
-Getting started
----------------
+Once you have
+:ref:`Python and MNE-Python up and running <install_python_and_mne_python>`,
+you can use these tutorials to get started processing MEG/EEG.
+You can find each step of the processing pipeline, and re-run the
+Python code by copy-paste.
 
-.. toctree::
-   :maxdepth: 1
+These tutorials aim to capture only the most important information.
+For further reading:
 
-   auto_tutorials/plot_introduction.rst
+- For a high-level overview of what you can do with MNE-Python:
+  :ref:`what_can_you_do`
+- For more examples of analyzing M/EEG data, including more sophisticated
+  analysis: :ref:`general_examples`
+- For details about analysis steps: :ref:`manual`
+- For details about specific functions and classes: :ref:`api_reference`
 
+.. note:: The default location for the MNE-sample data is
+          my-path-to/mne-python/examples. If you downloaded data and an
+          example asks you whether to download it again, make sure
+          the data reside in the examples directory
+          and that you run the script from its current directory.
 
-Working with MNE data structures
---------------------------------
-.. toctree::
-   :maxdepth: 1
+          .. code-block:: bash
 
-   auto_tutorials/plot_creating_data_structures.rst
-   auto_tutorials/plot_info.rst
-   auto_tutorials/plot_raw_objects.rst
-   auto_tutorials/plot_epochs_objects.rst
-   auto_tutorials/plot_epochs_to_data_frame.rst
+              $ cd examples/preprocessing
 
+          Then in Python you can do::
 
-Preprocessing
--------------
-.. toctree::
-   :maxdepth: 1
+              In [1]: %run plot_find_ecg_artifacts.py
 
-   auto_tutorials/plot_ica_from_raw.rst
 
-Source localization
--------------------
+          See :ref:`datasets` for a list of all available datasets and some
+          advanced configuration options, e.g. to specify a custom
+          location for storing the datasets.
 
-.. toctree::
-   :maxdepth: 1
+.. container:: span box
 
-   auto_tutorials/plot_source_localization_basics.rst
+  .. raw:: html
 
+    <h2>Introduction to MNE and Python</h2>
+ 
+  .. toctree::
+    :maxdepth: 1
 
-Statistics
-----------
+    auto_tutorials/plot_python_intro.rst
+    tutorials/seven_stories_about_mne.rst
 
-Sensor space
-^^^^^^^^^^^^
+.. container:: span box
 
-.. toctree::
-   :maxdepth: 1
+  .. raw:: html
 
-   auto_tutorials/plot_cluster_methods_tutorial.rst
-   auto_tutorials/plot_spatio_temporal_cluster_stats_sensor.rst
-   auto_tutorials/plot_cluster_1samp_test_time_frequency.rst
-   auto_tutorials/plot_cluster_stats_time_frequency.rst
+    <h2>Preprocessing</h2>
 
+  .. toctree::
+    :maxdepth: 1
 
-Source space
-^^^^^^^^^^^^
+    auto_tutorials/plot_artifacts_detection.rst
+    auto_tutorials/plot_artifacts_correction_filtering.rst
+    auto_tutorials/plot_artifacts_correction_rejection.rst
+    auto_tutorials/plot_artifacts_correction_ssp.rst
+    auto_tutorials/plot_artifacts_correction_ica.rst
+    auto_tutorials/plot_artifacts_correction_maxwell_filtering.rst
 
-.. toctree::
-   :maxdepth: 1
+.. container:: span box
 
-   auto_tutorials/plot_cluster_stats_time_frequency_repeated_measures_anova.rst
-   auto_tutorials/plot_cluster_stats_spatio_temporal_2samp.rst
-   auto_tutorials/plot_cluster_stats_spatio_temporal_repeated_measures_anova.rst
-   auto_tutorials/plot_cluster_stats_spatio_temporal.rst
+  .. raw:: html
 
-Visualization and Reporting
----------------------------
+    <h2>Sensor-level analysis</h2>
 
-.. toctree::
-   :maxdepth: 1
+  .. toctree::
+    :maxdepth: 1
 
-   tutorials/report.rst
+    auto_tutorials/plot_epoching_and_averaging.rst
+    auto_tutorials/plot_eeg_erp.rst
+    auto_tutorials/plot_sensors_time_frequency.rst
+    auto_tutorials/plot_sensors_decoding.rst
 
+.. container:: span box
 
-Command line tools
-------------------
+  .. raw:: html
 
-.. toctree::
-   :maxdepth: 1
+    <h2>Visualization and Reporting</h2>
 
-   tutorials/command_line.rst
+  .. toctree::
+    :maxdepth: 1
+
+    auto_tutorials/plot_visualize_raw.rst
+    auto_tutorials/plot_visualize_epochs.rst
+    auto_tutorials/plot_visualize_evoked.rst
+    tutorials/report.rst
+
+.. container:: span box
+
+  .. raw:: html
+
+    <h2>Manipulating Data Structures and Containers</h2>
+
+  .. toctree::
+    :maxdepth: 1
+
+    auto_tutorials/plot_object_raw.rst
+    auto_tutorials/plot_object_epochs.rst
+    auto_tutorials/plot_object_evoked.rst
+    auto_tutorials/plot_creating_data_structures.rst
+    auto_tutorials/plot_info.rst
+
+.. container:: span box
+
+  .. raw:: html
+
+    <h2>Source-level analysis</h2>
+
+  .. toctree::
+    :maxdepth: 1
+
+    auto_tutorials/plot_forward.rst
+    auto_tutorials/plot_compute_covariance.rst
+    auto_tutorials/plot_mne_dspm_source_localization.rst
+    auto_tutorials/plot_dipole_fit.rst
+    auto_tutorials/plot_brainstorm_auditory.rst
+
+.. container:: span box
+
+  .. raw:: html
+
+    <h2>Sensor-space Univariate Statistics</h2>
+
+  .. toctree::
+    :maxdepth: 1
+
+    auto_tutorials/plot_stats_cluster_methods.rst
+    auto_tutorials/plot_stats_spatio_temporal_cluster_sensors.rst
+    auto_tutorials/plot_stats_cluster_1samp_test_time_frequency.rst
+    auto_tutorials/plot_stats_cluster_time_frequency.rst
+
+.. container:: span box
+
+  .. raw:: html
+
+    <h2>Source-space Univariate Statistics</h2>
+
+  .. toctree::
+    :maxdepth: 1
+
+    auto_tutorials/plot_stats_cluster_time_frequency_repeated_measures_anova.rst
+    auto_tutorials/plot_stats_cluster_spatio_temporal_2samp.rst
+    auto_tutorials/plot_stats_cluster_spatio_temporal_repeated_measures_anova.rst
+    auto_tutorials/plot_stats_cluster_spatio_temporal.rst
+
+.. container:: span box
+
+  .. raw:: html
+
+    <h2>Multivariate Statistics - Decoding</h2>
+
+  .. toctree::
+    :maxdepth: 1
+
+    auto_tutorials/plot_sensors_decoding.rst
+
+.. container:: span box
+
+  .. raw:: html
+
+    <h2>Command line tools</h2>
+
+  .. toctree::
+    :maxdepth: 1
+
+    tutorials/command_line.rst
+    generated/commands.rst
diff --git a/doc/tutorials/command_line.rst b/doc/tutorials/command_line.rst
index b66b2a2..42a252f 100644
--- a/doc/tutorials/command_line.rst
+++ b/doc/tutorials/command_line.rst
@@ -1,11 +1,22 @@
 .. _command_line_tutorial:
 
-=====================================
-Getting started with MNE command line
-=====================================
+Getting started with MNE Unix command line tools
+================================================
+
+This tutorial is a really short step by step presentation
+of an analysis pipeline using the MNE-C command line tools.
+These tools are UNIX commands and therefore only run on
+Mac OS or Linux.
+
+See :ref:`install_mne_c` to setup your system for using the
+MNE-C tools.
 
 The quick start guide shows how to run a standard processing of the
-sample data set provided with MNE. XXX add link to data set download
+sample data set provided with MNE. The sample dataset is further
+described in :ref:`datasets`.
+
+All the following lines are to be run in a terminal and not in a Python
+interpreter.
 
 First define your subject::
 
@@ -21,10 +32,10 @@ Build your source space::
 
 Prepare for forward computation::
 
-    # For homogeneous volume conductor
+    # For homogeneous volume conductor (just inner skull)
     mne_setup_forward_model --homog --surf --ico 4
 
-    # or for XXX
+    # or for a three compartment model (inner and outer skull and skin)
     mne_setup_forward_model --surf --ico 4
 
 List your bad channels in a file. Example sample_bads.bad contains::
@@ -97,4 +108,5 @@ Produce stc files (activation files)::
 
 And, we're done!
 
-You can now get started with the Python :ref:`examples-index`
+See also :ref:`python_commands` for more command line tools
+using MNE-Python.
\ No newline at end of file
diff --git a/doc/tutorials/mne-report.png b/doc/tutorials/mne-report.png
index 0278ae9..e526a10 100644
Binary files a/doc/tutorials/mne-report.png and b/doc/tutorials/mne-report.png differ
diff --git a/doc/tutorials/report.rst b/doc/tutorials/report.rst
index 5ea0a55..866d0ab 100644
--- a/doc/tutorials/report.rst
+++ b/doc/tutorials/report.rst
@@ -1,8 +1,8 @@
 .. _mne_report_tutorial:
 
-=======================================
-Getting started with MNE report command
-=======================================
+===================================
+Getting started with MNE web report
+===================================
 
 This quick start will show you how to run the `mne report` command on the
 sample data set provided with MNE.
@@ -27,54 +27,66 @@ The command line interface
 --------------------------
 
 To generate a barebones report from all the \*.fif files in the sample dataset,
-invoke the following command::
+invoke the following command in a system (e.g., Bash) shell:
 
-    mne report --path MNE-sample-data/ --verbose
+.. code-block:: bash
+
+    $ mne report --path MNE-sample-data/ --verbose
 
 On successful creation of the report, it will open the html in a new tab in the browser.
 To disable this, use the `--no-browser` option.
 
-If the report is generated for a single subject, give the SUBJECT name and the
-SUBJECTS_DIR and this will generate the MRI slices (with BEM contours overlaid on top
-if available)::
+If the report is generated for a single subject, give the ``SUBJECT`` name and the
+``SUBJECTS_DIR`` and this will generate the MRI slices (with BEM contours overlaid on top
+if available):
+
+.. code-block:: bash
+
+    $ mne report --path MNE-sample-data/ --subject sample --subjects-dir MNE-sample-data/subjects --verbose
 
-    mne report --path MNE-sample-data/ --subject sample --subjects-dir MNE-sample-data/subjects --verbose
+To properly render `trans` and `covariance` files, add the measurement information:
 
-To properly render `trans` and `covariance` files, add the measurement information::
+.. code-block:: bash
 
-    mne report --path MNE-sample-data/ --info MNE-sample-data/MEG/sample/sample_audvis-ave.fif \ 
-        --subject sample --subjects-dir MNE-sample-data/subjects --verbose
+    $ mne report --path MNE-sample-data/ --info MNE-sample-data/MEG/sample/sample_audvis-ave.fif \ 
+          --subject sample --subjects-dir MNE-sample-data/subjects --verbose
 
-To render whitened `evoked` files with baseline correction, add the noise covariance file::
+To render whitened `evoked` files with baseline correction, add the noise covariance file:
     
-    mne report --path MNE-sample-data/ --info MNE-sample-data/MEG/sample/sample_audvis-ave.fif \ 
-        --cov MNE-sample-data/MEG/sample/sample_audvis-cov.fif --bmax 0 --subject sample \
-        --subjects-dir MNE-sample-data/subjects --verbose
+.. code-block:: bash
 
-To generate the report in parallel::
+    $ mne report --path MNE-sample-data/ --info MNE-sample-data/MEG/sample/sample_audvis-ave.fif \ 
+          --cov MNE-sample-data/MEG/sample/sample_audvis-cov.fif --bmax 0 --subject sample \
+          --subjects-dir MNE-sample-data/subjects --verbose
 
-    mne report --path MNE-sample-data/ --info MNE-sample-data/MEG/sample/sample_audvis-ave.fif \ 
-        --subject sample --subjects-dir MNE-sample-data/subjects --verbose --jobs 6
+To generate the report in parallel:
+
+.. code-block:: bash
+
+    $ mne report --path MNE-sample-data/ --info MNE-sample-data/MEG/sample/sample_audvis-ave.fif \ 
+          --subject sample --subjects-dir MNE-sample-data/subjects --verbose --jobs 6
 
 The report rendered on sample-data is shown below:
 
     .. image:: mne-report.png
        :align: center
 
-For help on all the available options, do::
+For help on all the available options, do:
+
+.. code-block:: bash
 
-    mne report --help
+    $ mne report --help
 
 The Python interface
 --------------------
 
 The same functionality can also be achieved using the Python interface. Import
-the required functions:
+the required functions::
 
     >>> from mne.report import Report
     >>> from mne.datasets import sample
 
-Generate the report:
+Generate the report::
 
     >>> path = sample.data_path()
     >>> report = Report(verbose=True)
@@ -84,7 +96,7 @@ Generate the report:
     Embedding : jquery-ui.min.css
     Embedding : bootstrap.min.css
 
-Only include \*audvis_raw.fif and \*-eve.fif files in the report:
+Only include \*audvis_raw.fif and \*-eve.fif files in the report::
 
     >>> report.parse_folder(data_path=path, pattern=['*audvis_raw.fif', '*-eve.fif']) # doctest: +SKIP
     Iterating over 6 potential files (this may take some time)
@@ -104,13 +116,13 @@ Only include \*audvis_raw.fif and \*-eve.fif files in the report:
     Rendering : /home/mainak/Desktop/projects/mne-python/examples/MNE-sample-data/MEG/sample/sample_audvis_raw-eve.fif
     Rendering : /home/mainak/Desktop/projects/mne-python/examples/MNE-sample-data/MEG/sample/sample_audvis_ecg-eve.fif
 
-Save the report as an html, but do not open the html in a browser:
+Save the report as an html, but do not open the html in a browser::
 
     >>> report.save('report.html', overwrite=True, open_browser=False) # doctest:+SKIP
     Rendering : Table of Contents...
 
 There is greater flexibility compared to the command line interface. 
-Custom plots can be added to the report. Let us first generate a custom plot:
+Custom plots can be added to the report. Let us first generate a custom plot::
 
     >>> from mne import read_evokeds
     >>> fname = path + '/MEG/sample/sample_audvis-ave.fif'
@@ -129,7 +141,7 @@ Custom plots can be added to the report. Let us first generate a custom plot:
     Applying baseline correction ... (mode: mean)
     >>> fig = evoked.plot() # doctest: +SKIP
 
-To add the custom plot to the report, do:
+To add the custom plot to the report, do::
 
     >>> report.add_figs_to_section(fig, captions='Left Auditory', section='evoked') # doctest: +SKIP
     >>> report.save('report.html', overwrite=True) # doctest: +SKIP
diff --git a/doc/tutorials/seven_stories_about_mne.rst b/doc/tutorials/seven_stories_about_mne.rst
new file mode 100644
index 0000000..be08a74
--- /dev/null
+++ b/doc/tutorials/seven_stories_about_mne.rst
@@ -0,0 +1,171 @@
+Seven stories about MNE
+=======================
+
+
+1. What the FIF does MNE stand for?
+-----------------------------------
+Historically, MNE was a software for computing cortically constrained
+Minimum Norm Estimates from MEG and EEG data. The historical core
+functions of MNE were written by Matti Hämäläinen in Boston and originate
+in part from the Elekta software that is shipped with its MEG systems.
+Ah yes, the FIFF is Elektas Functional Imaging File Format that goes
+along with `.fif` file extensions and is natively used by its MEG systems.
+For these reasons the MNE software is internally relying on the FIFF files.
+Today the situation is a bit different though. MNE is nowadays developed
+mostly in Python by an international team of researchers from diverse
+laboratories and has widened its scope. MNE supports advanced sensor space
+analyses for EEG, temporal ICA, many different file formats and many other
+inverse solvers, for example beamformers. Some of our contributors even
+use it for intracranial data. If you want, MNE can be thought of as MEG'n'EEG.
+
+2. Reading data into the MNE layout
+-----------------------------------
+One of the first things you might be wondering about is how to get your
+data into mne. Assuming that you have unprocessed data, you will probably
+be happy with at least one of these readers:
+
+* :func:`read_raw_fif <mne.io.read_raw_fif>`
+* :func:`read_raw_kit <mne.io.read_raw_kit>`
+* :func:`read_raw_bti <mne.io.read_raw_bti>`
+* :func:`read_raw_ctf <mne.io.read_raw_ctf>`
+* :func:`read_raw_brainvision <mne.io.read_raw_brainvision>`
+* :func:`read_raw_cnt <mne.io.read_raw_cnt>`
+* :func:`read_raw_edf <mne.io.read_raw_edf>`
+* :func:`read_raw_eeglab <mne.io.read_raw_eeglab>`
+* :func:`read_raw_egi <mne.io.read_raw_egi>`
+* :func:`read_raw_nicolet <mne.io.read_raw_nicolet>`
+
+They all have in common to return an :class:`mne.io.Raw` object and the MEG
+readers perform conversions of sensor positions and channel names
+to make the meta data compatible with the conventions of the FIFF
+format. Yes, at this point MNE relies on the historical layout and
+therefore expects MEG data to look like Elekta Neuromag data and to
+conform to Freesurfer data layouts. This is somewhat relaxed for EEG
+data, which have less to do with Neuromag and very often are not
+used for source space analyses. See :ref:`ch_convert`.
+
+3. MNE gives you objects with methods
+-------------------------------------
+We said above that there are MNE objects. This is of course computer
+science jargon. What it actually means is that you get a data structure
+that is more than the channels by time series
+and the information about channel types and locations, meta-data if
+you want. Indeed the structures that MNE is using provide so called
+methods. These are nothing but functions that are configured to take
+the data and the meta-data of the object as parameters. Sounds
+complicated, but it's actually simplifying your life as you will see
+below. Whether you consider Raw objects that describe continuous data,
+Epochs objects describing segmented single trial data, or Evoked objects
+describing averaged data, all have in common that they share certain methods.
+
+- Try :func:`raw.plot <mne.io.Raw.plot>`,
+  :func:`epochs.plot <mne.Epochs.plot>`,
+  :func:`evoked.plot <mne.Evoked.plot>` and any other method that has
+  a name that starts with `plot`. By using the call operators `()`
+  you invoke these methods, e.g.
+  :func:`epochs.plot() <<mne.Epochs.plot>>`.
+  Yes, you don't have to pass arguments but you will get an informative
+  visualization of your data. The method knows what to do with the object.
+  Look up the documentation for configuration options.
+
+- Try :func:`raw.pick_types <mne.io.Raw.pick_types>`,
+  :func:`epochs.pick_types <mne.Epochs.pick_types>`
+  :func:`evoked.pick_types <mne.Evoked.pick_types>`
+  and any other method that has a name that starts with `pick`. These
+  methods will allow you to select channels either by name or by type.
+  Picking is MNE jargon and stands for channel selection.
+
+- Some of these methods can actually change the state of the object,
+  e.g. permanently remove or transform data. To preserve your input
+  data can explicitly use the .copy method to manipulate a copy of
+  your inputs. Example::
+
+    >>> raw.copy().pick_types(meg=False, eeg=True)  # doctest: +SKIP
+
+- This examplifies another important concept, that is chaining. Most
+  methods return the object and hence allow you to write handy pipelines.
+  Guess what this code does::
+
+    >>> (fig = raw.copy()  # doctest: +SKIP
+    >>>           .pick_types(meg=False, eeg=True)  # doctest: +SKIP
+    >>>           .resample(sfreq=100)  # doctest: +SKIP
+    >>>           .filter(1, 30)  # doctest: +SKIP
+    >>>           .plot())  # doctest: +SKIP
+
+  Yes, it creates a figure after filtering a resampled copy of the EEG
+  data. In fact you can also recognize methods by certain linguistic
+  cues. Methods typically use english verbs. So `raw.ch_names` is
+  not a method. It's just an attribute that cannot be invoked like
+  a function.
+
+- Last but not least, many MNE objects returned a `.save` method that
+  allows you to store your data into a FIFF file.
+
+
+4. A key thing for MNE objects is the measurment info
+-----------------------------------------------------
+Besides `.ch_names` another important attribute is .info. It contains
+the channel information and some details about the processing history.
+This is especially relevant if your data cannot be read using the io
+functions listed above. You then need to learn how to create an info.
+See :ref:`tut_info_objects`.
+
+5. MNE is modular
+-----------------
+Beyond methods another concept that is important to get are *modules*.
+Think of them as name spaces, another computer science term.
+Ok, think of street names in different cities. Sending a parcel to the
+Washington street in New York or San Francisco typically
+does not involve a conflict, as these streets are in different cities.
+Now you know what is the idea behind a name space. You can
+read a lot of resources that you will find when googling accordingly.
+What is important here is that our modules are organized by
+processing contexts. Looking for I/O operations for raw data?::
+
+    >>> from mne import io
+
+Wanna do preprocessing?::
+
+    >>> from mne import preprocessing
+
+Wanna do visualization?::
+
+    >>> from mne import viz
+
+Decoding?::
+
+    >>> from mne import decoding
+
+I'm sure you got it, so explore your intuitions when searching for
+a certain function.
+
+6. Inspect and script
+---------------------
+Did you happen to notice that some of the figures returned by `.plot`
+methods allow you to interact with the data? Look at raw.plot and
+epochs.plot for example. They allow you to update channel selections,
+scalings and time ranges. However, they do not replace scripting.
+The MNE philosophy is to facilitate diagnostic plotting but does
+not support doing analysis by clicking your way. MNE is meant to be
+a toolbox, and its your task to combine the tools by writing scripts.
+This should really save you time, first of all by being able to reuse
+code and avoiding to click it again. Second by documenting what you
+did. Reviewers are asking you to update your analysis that you actually
+finished 1 year ago? Luckily you have a script.
+
+7. Eighty percent or Python
+---------------------------
+A related point is that MNE functions are there to make it fun to
+process common tasks and facilitate doing difficult things.
+This means that you will notice certain limits
+here and there, the viz functions do not exactly plot things as
+you want them, even when using the options provided by that function.
+In fact our goal is to guess which are the essential 80 percent that
+you need in order be happy in 80 percent of the time. Where you need
+more Python is there for you. You can easily access the data, e.g.
+`raw[:10, :1000]` or `epochs.get_data()` or `evoked.data` and
+manipulate them using numpy or pass them to high-level machine learning code
+from `scikit-learn <http://scikit-learn.org>`_. Each `.plot` method
+returns a matplotlib figure object. Both packages have great documentations
+and often writing Python code amounts to looking up the right library that
+allows you to tackle the problem in a few lines.
diff --git a/doc/whats_new.rst b/doc/whats_new.rst
index 643a4e6..8a5ba70 100644
--- a/doc/whats_new.rst
+++ b/doc/whats_new.rst
@@ -1,9 +1,218 @@
+.. include:: links.inc
+.. _whats_new:
+
 What's new
 ==========
 ..
     Note, we are now using links to highlight new functions and classes.
     Please be sure to follow the examples below like :func:`mne.stats.f_mway_rm`, so the whats_new page will have a link to the function/class documentation.
 
+.. _changes_0_12:
+
+Version 0.12
+------------
+
+Changelog
+~~~~~~~~~
+
+    - Add ``overlay_times`` parameter to :func:`mne.viz.plot_epochs_image` to be able to display for example reaction times on top of the images, by `Alex Gramfort`_
+
+    - Animation for evoked topomap in :func:`mne.Evoked.animate_topomap` by `Jaakko Leppakangas`_
+
+    - Make :func:`mne.channels.find_layout` more robust for KIT systems in the presence of bad or missing channels by `Jaakko Leppakangas`_
+
+    - Add raw movement compensation to :func:`mne.preprocessing.maxwell_filter` by `Eric Larson`_
+
+    - Add :class:`mne.Annotations` for for annotating segments of raw data by `Jaakko Leppakangas`_
+
+    - Add reading of .fif file montages by `Eric Larson`_
+
+    - Add system config utility :func:`mne.sys_info` by `Eric Larson`_
+
+    - Automatic cross-validation and scoring metrics in :func:`mne.decoding.GeneralizationAcrossTime`, by `Jean-Remi King`_
+
+    - :func:`mne.decoding.GeneralizationAcrossTime` accepts non-deterministic cross-validations, by `Jean-Remi King`_
+
+    - Add plotting RMS of gradiometer pairs in :func:`mne.viz.plot_evoked_topo` by `Jaakko Leppakangas`_
+
+    - Add regularization methods to :func:`mne.compute_raw_covariance` by `Eric Larson`_.
+
+    - Add command ``mne show_info`` to quickly show the measurement info from a .fif file from the terminal by `Alex Gramfort`_.
+
+    - Add creating forward operator for dipole object :func:`mne.make_forward_dipole` by `Chris Bailey`_
+
+    - Add reading and estimation of fixed-position dipole time courses (similar to Elekta ``xfit``) using :func:`mne.read_dipole` and :func:`mne.fit_dipole` by `Eric Larson`_.
+
+    - Accept :class:`mne.decoding.GeneralizationAcrossTime`'s ``scorer`` parameter to be a string that refers to a scikit-learn_ metric scorer by `Asish Panda`_.
+
+    - Add method :func:`mne.Epochs.plot_image` calling :func:`mne.viz.plot_epochs_image` for better usability by `Asish Panda`_.
+
+    - Add :func:`mne.io.read_raw_cnt` for reading Neuroscan CNT files by `Jaakko Leppakangas`_
+
+    - Add ``decim`` parameter to :func:`mne.time_frequency.cwt_morlet`, by `Jean-Remi King`_
+
+    - Add method :func:`mne.Epochs.plot_topo_image` by `Jaakko Leppakangas`_
+
+    - Add the ability to read events when importing raw EEGLAB files, by `Jona Sassenhagen`_.
+
+    - Add function :func:`mne.viz.plot_sensors` and methods :func:`mne.Epochs.plot_sensors`, :func:`mne.io.Raw.plot_sensors` and :func:`mne.Evoked.plot_sensors` for plotting sensor positions and :func:`mne.viz.plot_layout` and :func:`mne.channels.Layout.plot` for plotting layouts by `Jaakko Leppakangas`_
+
+    - Add epoch rejection based on annotated segments by `Jaakko Leppakangas`_
+
+    - Add option to use new-style MEG channel names in :func:`mne.read_selection` by `Eric Larson`_
+
+    - Add option for ``proj`` in :class:`mne.EpochsArray` by `Eric Larson`_
+
+    - Enable the usage of :func:`mne.viz.topomap.plot_topomap` with an :class:`mne.io.Info` instance for location information, by `Jona Sassenhagen`_.
+
+    - Add support for electrocorticography (ECoG) channel type by `Eric Larson`_
+
+    - Add option for ``first_samp`` in :func:`mne.make_fixed_length_events` by `Jon Houck`_
+
+    - Add ability to auto-scale channel types for :func:`mne.viz.plot_raw` and :func:`mne.viz.plot_epochs` and corresponding object plotting methods by `Chris Holdgraf`_
+
+BUG
+~~~
+
+    - :func:`compute_raw_psd`, :func:`compute_epochs_psd`, :func:`psd_multitaper`, and :func:`psd_welch` no longer remove rows/columns of the SSP matrix before applying SSP projectors when picks are provided by `Chris Holdgraf`_.
+
+    - :func:`mne.Epochs.plot_psd` no longer calls a Welch PSD, and instead uses a Multitaper method which is more appropriate for epochs. Flags for this function are passed to :func:`mne.time_frequency.psd_multitaper` by `Chris Holdgraf`_
+
+    - Time-cropping functions (e.g., :func:`mne.Epochs.crop`, :func:`mne.Evoked.crop`, :func:`mne.io.Raw.crop`, :func:`mne.SourceEstimate.crop`) made consistent with behavior of ``tmin`` and ``tmax`` of :class:`mne.Epochs`, where nearest sample is kept. For example, for MGH data acquired with ``sfreq=600.614990234``, constructing ``Epochs(..., tmin=-1, tmax=1)`` has bounds ``+/-1.00064103``, and now ``epochs.crop(-1, 1)`` will also have these bounds (previously they would have been ``+/- [...]
+
+    - Fix EEG spherical spline interpolation code to account for average reference by `Mainak Jas`_ (`#2758 <https://github.com/mne-tools/mne-python/pull/2758>`_)
+
+    - MEG projectors are removed after Maxwell filtering by `Eric Larson`_
+
+    - Fix :func:`mne.decoding.TimeDecoding` to allow specifying ``clf`` by `Jean-Remi King`_
+
+    - Fix bug with units (uV) in 'Brain Vision Data Exchange Header File Version 1.0' by `Federico Raimondo`_
+
+    - Fix bug where :func:`mne.preprocessing.maxwell_filter` ``destination`` parameter did not properly set device-to-head transform by `Eric Larson`_
+
+    - Fix bug in rank calculation of :func:`mne.utils.estimate_rank`, :func:`mne.io.Raw.estimate_rank`, and covariance functions where the tolerance was set to slightly too small a value, new 'auto' mode uses values from ``scipy.linalg.orth`` by `Eric Larson`_.
+
+    - Fix bug when specifying irregular ``train_times['slices']`` in :func:`mne.decoding.GeneralizationAcrossTime`, by `Jean-Remi King`_
+
+    - Fix colorbar range on norm data by `Jaakko Leppakangas`_
+
+    - Fix bug in :func:`mne.preprocessing.run_ica`, which used the ``ecg_criterion`` parameter for the EOG criterion instead of ``eog_criterion`` by `Christian Brodbeck`_
+
+    - Fix normals in CTF data reader by `Eric Larson`_
+
+    - Fix bug in :func:`mne.io.read_raw_ctf`, when omitting samples at the end by `Jaakko Leppakangas`_
+
+    - Fix ``info['lowpass']`` value for downsampled raw data by `Eric Larson`_
+
+    - Remove measurement date from :class:`mne.Info` in :func:`mne.io.Raw.anonymize` by `Eric Larson`_
+
+    - Fix bug that caused synthetic ecg channel creation even if channel was specified for ECG peak detection in :func:`mne.preprocessing.create_ecg_epochs` by `Jaakko Leppakangas`_
+
+    - Fix bug with vmin and vmax when None is passed in :func:`mne.viz.plot_topo_image_epochs` by `Jaakko Leppakangas`_
+
+    - Fix bug with :func:`mne.label_sign_flip` (and :func:`mne.extract_label_time_course`) by `Natalie Klein`_ and `Eric Larson`_
+
+    - Add copy parameter in :func:`mne.Epochs.apply_baseline` and :func:`mne.io.Raw.filter` methods by `Jona Sassenhagen`_ and `Alex Gramfort`_
+
+    - Fix bug in :func:`mne.merge_events` when using ``replace_events=False`` by `Alex Gramfort`_
+
+    - Fix bug in :class:`mne.Evoked` type setting in :func:`mne.stats.linear_regression_raw` by `Eric Larson`_
+
+    - Fix bug in :class: `mne.io.edf.RawEDF` highpass filter setting to take max highpass to match warning message by `Teon Brooks`_
+
+    - Fix bugs with coordinane frame adjustments in :func:`mne.viz.plot_trans` by `Eric Larson`_
+
+    - Fix bug in colormap selection in :func:`mne.Evoked.plot_projs_topomap` by `Jaakko Leppakangas`_
+
+    - Fix bug in source normal adjustment that occurred when 1) patch information is available (e.g., when distances have been calculated) and 2) points are excluded from the source space (by inner skull distance) by `Eric Larson`_
+
+    - Fix bug when merging info that has a field with list of dicts by `Jaakko Leppakangas`_
+    
+    - The BTI/4D reader now considers user defined channel labels instead of the hard-ware names, however only for channels other than MEG. By `Denis Engemann`_ and `Alex Gramfort`_.
+
+    - Fix bug in :func:`mne.compute_raw_covariance` where rejection by non-data channels (e.g. EOG) was not done properly by `Eric Larson`_.
+
+    - Change default scoring method of :func:`mne.decoding.GeneralizationAcrossTime` and :func:`mne.decoding.TimeDecoding` to estimate the scores within the cross-validation as in scikit-learn_ as opposed to across all cross-validated ``y_pred``. The method can be changed with the ``score_mode`` parameter by `Jean-Remi King`_
+
+    - Fix bug in :func:`mne.io.Raw.save` where, in rare cases, automatically split files could end up writing an extra empty file that wouldn't be read properly by `Eric Larson`_
+
+API
+~~~
+
+    - The default `picks=None` in :func:`mne.viz.plot_epochs_image` now only plots the first 5 channels, not all channels, by `Jona Sassenhagen`_
+
+    - The ``mesh_color`` parameter in :func:`mne.viz.plot_dipole_locations` has been removed (use `brain_color` instead), by `Marijn van Vliet`_
+
+    - Deprecated functions :func:`mne.time_frequency.compute_raw_psd` and :func:`mne.time_frequency.compute_epochs_psd`, replaced by :func:`mne.time_frequency.psd_welch` by `Chris Holdgraf`_
+
+    - Deprecated function :func:`mne.time_frequency.multitaper_psd` and replaced by :func:`mne.time_frequency.psd_multitaper` by `Chris Holdgraf`_
+
+    - The ``y_pred`` attribute in :func:`mne.decoding.GeneralizationAcrossTime` and :func:`mne.decoding.TimeDecoding` is now a numpy array, by `Jean-Remi King`_
+
+    - The :func:`mne.bem.fit_sphere_to_headshape` function now default to ``dig_kinds='auto'`` which will use extra digitization points, falling back to extra plus eeg digitization points if there not enough extra points are available.
+
+    - The :func:`mne.bem.fit_sphere_to_headshape` now has a ``units`` argument that should be set explicitly. This will default to ``units='mm'`` in 0.12 for backward compatibility but change to ``units='m'`` in 0.13.
+
+    - Added default parameters in Epochs class namely ``event_id=None``, ``tmin=-0.2`` and ``tmax=0.5``.
+
+    - To unify and extend the behavior of :func:`mne.comupute_raw_covariance` relative to :func:`mne.compute_covariance`, the default parameter ``tstep=0.2`` now discards any epochs at the end of the :class:`mne.io.Raw` instance that are not the full ``tstep`` duration. This will slightly change the computation of :func:`mne.compute_raw_covaraince`, but should only potentially have a big impact if the :class:`mne.io.Raw` instance is short relative to ``tstep`` and the last, too short (no [...]
+
+    - The default ``picks=None`` in :func:`mne.io.Raw.filter` now picks eeg, meg, seeg, and ecog channels, by `Jean-Remi King`_ and `Eric Larson`_
+
+    - EOG, ECG and EMG channels are now plotted by default (if present in data) when using :func:`mne.viz.plot_evoked` by `Marijn van Vliet`_
+
+    - Replace pseudoinverse-based solver with much faster Cholesky solver in :func:`mne.stats.linear_regression_raw`, by `Jona Sassenhagen`_.
+
+    - CTF data reader now reads EEG locations from .pos file as HPI points by `Jaakko Leppakangas`_
+
+    - Subselecting channels can now emit a warning if many channels have been subselected from projection vectors. We recommend only computing projection vertors for and applying projectors to channels that will be used in the final analysis. However, after picking a subset of channels, projection vectors can be renormalized with :func:`mne.Info.normalize_proj` if necessary to avoid warnings about subselection. Changes by `Eric Larson`_ and `Alex Gramfort`_.
+
+    - Rename and deprecate :func:`mne.Epochs.drop_bad_epochs` to :func:`mne.Epochs.drop_bad`, and :func:`mne.Epochs.drop_epochs` to :func:`mne.Epochs.drop` by `Alex Gramfort`_.
+
+    - The C wrapper :func:`mne.do_forward_solution` has been deprecated in favor of the native Python version :func:`mne.make_forward_solution` by `Eric Larson`_
+
+    - The ``events`` parameter of :func:`mne.epochs.EpochsArray` is set by default to chronological time-samples and event values to 1, by `Jean-Remi King`_
+
+Authors
+~~~~~~~
+
+The committer list for this release is the following (preceded by number of commits):
+
+    * 348	Eric Larson
+    * 347	Jaakko Leppakangas
+    * 157	Alexandre Gramfort
+    * 139	Jona Sassenhagen
+    * 67	Jean-Remi King
+    * 32	Chris Holdgraf
+    * 31	Denis A. Engemann
+    * 30	Mainak Jas
+    * 16	Christopher J. Bailey
+    * 13	Marijn van Vliet
+    * 10	Mark Wronkiewicz
+    * 9	Teon Brooks
+    * 9	kaichogami
+    * 8	Clément Moutard
+    * 5	Camilo Lamus
+    * 5	mmagnuski
+    * 4	Christian Brodbeck
+    * 4	Daniel McCloy
+    * 4	Yousra Bekhti
+    * 3	Fede Raimondo
+    * 1	Jussi Nurminen
+    * 1	MartinBaBer
+    * 1	Mikolaj Magnuski
+    * 1	Natalie Klein
+    * 1	Niklas Wilming
+    * 1	Richard Höchenberger
+    * 1	Sagun Pai
+    * 1	Sourav Singh
+    * 1	Tom Dupré la Tour
+    * 1	jona-sassenhagen@
+    * 1	kambysese
+    * 1	pbnsilva
+    * 1	sviter
+    * 1	zuxfoucault
+
 .. _changes_0_11:
 
 Version 0.11
@@ -37,7 +246,7 @@ Changelog
     - Add reader for CTF data in :func:`mne.io.read_raw_ctf` by `Eric Larson`_
 
     - Add support for Brainvision v2 in :func:`mne.io.read_raw_brainvision` by `Teon Brooks`_
-    
+
     - Improve speed of generalization across time :class:`mne.decoding.GeneralizationAcrossTime` decoding up to a factor of seven by `Jean-Remi King`_ and `Federico Raimondo`_ and `Denis Engemann`_.
 
     - Add the explained variance for each principal component, ``explained_var``, key to the :class:`mne.io.Projection` by `Teon Brooks`_
@@ -63,6 +272,8 @@ BUG
 
     - :class:`mne.EpochsArray` no longer has an average EEG reference silently added (but not applied to the data) by default. Use :func:`mne.EpochsArray.add_eeg_ref` to properly add one.
 
+    - Fix :func:`mne.io.read_raw_ctf` to read ``n_samp_tot`` instead of ``n_samp`` by `Jaakko Leppakangas`_
+
 API
 ~~~
 
@@ -77,23 +288,23 @@ Authors
 
 The committer list for this release is the following (preceded by number of commits):
 
-   171  Eric Larson
-   117  Jaakko Leppakangas
-    58  Jona Sassenhagen
-    52  Mainak Jas
-    46  Alexandre Gramfort
-    33  Denis A. Engemann
-    28  Teon Brooks
-    24  Clemens Brunner
-    23  Christian Brodbeck
-    15  Mark Wronkiewicz
-    10  Jean-Remi King
-     5  Marijn van Vliet
-     3  Fede Raimondo
-     2  Alexander Rudiuk
-     2  emilyps14
-     2  lennyvarghese
-     1  Marian Dovgialo
+  * 171  Eric Larson
+  * 117  Jaakko Leppakangas
+  *  58  Jona Sassenhagen
+  *  52  Mainak Jas
+  *  46  Alexandre Gramfort
+  *  33  Denis A. Engemann
+  *  28  Teon Brooks
+  *  24  Clemens Brunner
+  *  23  Christian Brodbeck
+  *  15  Mark Wronkiewicz
+  *  10  Jean-Remi King
+  *   5  Marijn van Vliet
+  *   3  Fede Raimondo
+  *   2  Alexander Rudiuk
+  *   2  emilyps14
+  *   2  lennyvarghese
+  *   1  Marian Dovgialo
 
 .. _changes_0_10:
 
@@ -113,7 +324,7 @@ Changelog
 
     - Add support for scaling and adjusting the number of channels/time per view by `Jaakko Leppakangas`_
 
-    - Add support to toggle the show/hide state of all sections with a single keypress ('t') in :class:`mne.report.Report` by `Mainak Jas`_
+    - Add support to toggle the show/hide state of all sections with a single keypress ('t') in :class:`mne.Report` by `Mainak Jas`_
 
     - Add support for BEM model creation :func:`mne.make_bem_model` by `Eric Larson`_
 
@@ -165,13 +376,13 @@ Changelog
 
     - Add source space morphing in :func:`morph_source_spaces` and :func:`SourceEstimate.to_original_src` by `Eric Larson`_ and `Denis Engemann`_
 
-   - Adapt ``corrmap`` function (Viola et al. 2009) to semi-automatically detect similar ICs across data sets by `Jona Sassenhagen`_ and `Denis Engemann`_ and `Eric Larson`_
+    - Adapt ``corrmap`` function (Viola et al. 2009) to semi-automatically detect similar ICs across data sets by `Jona Sassenhagen`_ and `Denis Engemann`_ and `Eric Larson`_
 
-   - New ``mne flash_bem`` command to compute BEM surfaces from Flash MRI images by `Lorenzo Desantis`_, `Alex Gramfort`_ and `Eric Larson`_. See :func:`mne.bem.utils.make_flash_bem`.
+    - New ``mne flash_bem`` command to compute BEM surfaces from Flash MRI images by `Lorenzo Desantis`_, `Alex Gramfort`_ and `Eric Larson`_. See :func:`mne.bem.utils.make_flash_bem`.
 
-   - New gfp parameter in :func:`mne.Evoked.plot` method to display Global Field Power (GFP) by `Eric Larson`_.
+    - New gfp parameter in :func:`mne.Evoked.plot` method to display Global Field Power (GFP) by `Eric Larson`_.
 
-    - Add :func:`mne.report.Report.add_slider_to_section` methods to :class:`mne.report.Report` by `Teon Brooks`_
+    - Add :func:`mne.Report.add_slider_to_section` methods to :class:`mne.Report` by `Teon Brooks`_
 
 BUG
 ~~~
@@ -214,33 +425,33 @@ Authors
 
 The committer list for this release is the following (preceded by number of commits):
 
-   273  Eric Larson
-   270  Jaakko Leppakangas
-   194  Alexandre Gramfort
-   128  Denis A. Engemann
-   114  Jona Sassenhagen
-   107  Mark Wronkiewicz
-    97  Teon Brooks
-    81  Lorenzo De Santis
-    55  Yousra Bekhti
-    54  Jean-Remi King
-    48  Romain Trachel
-    45  Mainak Jas
-    40  Alexandre Barachant
-    32  Marijn van Vliet
-    26  Jair Montoya
-    22  Chris Holdgraf
-    16  Christopher J. Bailey
-     7  Christian Brodbeck
-     5  Natalie Klein
-     5  Fede Raimondo
-     5  Alan Leggitt
-     5  Roan LaPlante
-     5  Ross Maddox
-     4  Dan G. Wakeman
-     3  Daniel McCloy
-     3  Daniel Strohmeier
-     1  Jussi Nurminen
+   * 273  Eric Larson
+   * 270  Jaakko Leppakangas
+   * 194  Alexandre Gramfort
+   * 128  Denis A. Engemann
+   * 114  Jona Sassenhagen
+   * 107  Mark Wronkiewicz
+   *  97  Teon Brooks
+   *  81  Lorenzo De Santis
+   *  55  Yousra Bekhti
+   *  54  Jean-Remi King
+   *  48  Romain Trachel
+   *  45  Mainak Jas
+   *  40  Alexandre Barachant
+   *  32  Marijn van Vliet
+   *  26  Jair Montoya
+   *  22  Chris Holdgraf
+   *  16  Christopher J. Bailey
+   *   7  Christian Brodbeck
+   *   5  Natalie Klein
+   *   5  Fede Raimondo
+   *   5  Alan Leggitt
+   *   5  Roan LaPlante
+   *   5  Ross Maddox
+   *   4  Dan G. Wakeman
+   *   3  Daniel McCloy
+   *   3  Daniel Strohmeier
+   *   1  Jussi Nurminen
 
 .. _changes_0_9:
 
@@ -333,7 +544,7 @@ Changelog
 
    - Add ``evoked.as_type`` to  allow remapping data in MEG channels to virtual magnetometer or gradiometer channels by `Mainak Jas`_
 
-   - Add :func:`mne.report.Report.add_bem_to_section`, :func:`mne.report.Report.add_htmls_to_section` methods to :class:`mne.report.Report` by `Teon Brooks`_
+   - Add :func:`mne.Report.add_bem_to_section`, :func:`mne.Report.add_htmls_to_section` methods to :class:`mne.Report` by `Teon Brooks`_
 
    - Add support for KIT epochs files with ``read_epochs_kit`` by `Teon Brooks`_
 
@@ -388,7 +599,7 @@ BUG
 
    - Add functionality to determine plot limits automatically or by data percentiles by `Mark Wronkiewicz`_
 
-   - Fix bug in mne.io.edf where the channel offsets were ommitted in the voltage calculations by `Teon Brooks`_
+   - Fix bug in mne.io.edf where the channel offsets were omitted in the voltage calculations by `Teon Brooks`_
 
    - Decouple section ordering in command-line from python interface for mne-report by `Mainak Jas`_
 
@@ -448,40 +659,40 @@ Authors
 
 The committer list for this release is the following (preceded by number of commits):
 
-   515  Eric Larson
-   343  Denis A. Engemann
-   304  Alexandre Gramfort
-   300  Teon Brooks
-   142  Mainak Jas
-   119  Jean-Remi King
-    77  Alan Leggitt
-    75  Marijn van Vliet
-    63  Chris Holdgraf
-    57  Yousra Bekhti
-    49  Mark Wronkiewicz
-    44  Christian Brodbeck
-    30  Jona Sassenhagen
-    29  Hari Bharadwaj
-    27  Clément Moutard
-    24  Ingoo Lee
-    18  Marmaduke Woodman
-    16  Martin Luessi
-    10  Jaakko Leppakangas
-     9  Andrew Dykstra
-     9  Daniel Strohmeier
-     7  kjs
-     6  Dan G. Wakeman
-     5  Federico Raimondo
-     3  Basile Pinsard
-     3  Christoph Dinh
-     3  Hafeza Anevar
-     2  Martin Billinger
-     2  Roan LaPlante
-     1  Manoj Kumar
-     1  Matt Tucker
-     1  Romain Trachel
-     1  mads jensen
-     1  sviter
+   * 515  Eric Larson
+   * 343  Denis A. Engemann
+   * 304  Alexandre Gramfort
+   * 300  Teon Brooks
+   * 142  Mainak Jas
+   * 119  Jean-Remi King
+   *  77  Alan Leggitt
+   *  75  Marijn van Vliet
+   *  63  Chris Holdgraf
+   *  57  Yousra Bekhti
+   *  49  Mark Wronkiewicz
+   *  44  Christian Brodbeck
+   *  30  Jona Sassenhagen
+   *  29  Hari Bharadwaj
+   *  27  Clément Moutard
+   *  24  Ingoo Lee
+   *  18  Marmaduke Woodman
+   *  16  Martin Luessi
+   *  10  Jaakko Leppakangas
+   *   9  Andrew Dykstra
+   *   9  Daniel Strohmeier
+   *   7  kjs
+   *   6  Dan G. Wakeman
+   *   5  Federico Raimondo
+   *   3  Basile Pinsard
+   *   3  Christoph Dinh
+   *   3  Hafeza Anevar
+   *   2  Martin Billinger
+   *   2  Roan LaPlante
+   *   1  Manoj Kumar
+   *   1  Matt Tucker
+   *   1  Romain Trachel
+   *   1  mads jensen
+   *   1  sviter
 
 .. _changes_0_8:
 
@@ -1330,3 +1541,9 @@ of commits):
 .. _Jussi Nurminen: https://scholar.google.fi/citations?user=R6CQz5wAAAAJ&hl=en
 
 .. _Clemens Brunner: https://github.com/cle1109
+
+.. _Asish Panda: https://github.com/kaichogami
+
+.. _Natalie Klein: http://www.stat.cmu.edu/people/students/neklein
+
+.. _Jon Houck: http://www.unm.edu/~jhouck/
diff --git a/examples/README.txt b/examples/README.txt
index bbefdad..364c415 100644
--- a/examples/README.txt
+++ b/examples/README.txt
@@ -1,9 +1,8 @@
+.. _general_examples:
+
 Examples Gallery
 ================
 
 .. contents:: Contents
    :local:
    :depth: 2
-
-Introductory Examples
----------------------
diff --git a/examples/connectivity/plot_cwt_sensor_connectivity.py b/examples/connectivity/plot_cwt_sensor_connectivity.py
index 5529cb5..6a381cd 100644
--- a/examples/connectivity/plot_cwt_sensor_connectivity.py
+++ b/examples/connectivity/plot_cwt_sensor_connectivity.py
@@ -33,7 +33,7 @@ raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 # Add a bad channel
@@ -67,7 +67,7 @@ sfreq = raw.info['sfreq']  # the sampling frequency
 con, freqs, times, _, _ = spectral_connectivity(
     epochs, indices=indices,
     method='wpli2_debiased', mode='cwt_morlet', sfreq=sfreq,
-    cwt_frequencies=cwt_frequencies, cwt_n_cycles=cwt_n_cycles, n_jobs=2)
+    cwt_frequencies=cwt_frequencies, cwt_n_cycles=cwt_n_cycles, n_jobs=1)
 
 # Mark the seed channel with a value of 1.0, so we can see it in the plot
 con[np.where(indices[1] == seed)] = 1.0
diff --git a/examples/connectivity/plot_mne_inverse_coherence_epochs.py b/examples/connectivity/plot_mne_inverse_coherence_epochs.py
index d093572..df4b3e1 100644
--- a/examples/connectivity/plot_mne_inverse_coherence_epochs.py
+++ b/examples/connectivity/plot_mne_inverse_coherence_epochs.py
@@ -16,7 +16,6 @@ import numpy as np
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.minimum_norm import (apply_inverse, apply_inverse_epochs,
                               read_inverse_operator)
 from mne.connectivity import seed_target_indices, spectral_connectivity
@@ -37,7 +36,7 @@ method = "dSPM"  # use dSPM method (could also be MNE or sLORETA)
 # Load data
 inverse_operator = read_inverse_operator(fname_inv)
 label_lh = mne.read_label(fname_label_lh)
-raw = Raw(fname_raw)
+raw = mne.io.read_raw_fif(fname_raw)
 events = mne.read_events(fname_event)
 
 # Add a bad channel
@@ -95,7 +94,7 @@ sfreq = raw.info['sfreq']  # the sampling frequency
 # get 2 frequency bins
 coh, freqs, times, n_epochs, n_tapers = spectral_connectivity(
     stcs, method='coh', mode='fourier', indices=indices,
-    sfreq=sfreq, fmin=fmin, fmax=fmax, faverage=True, n_jobs=2)
+    sfreq=sfreq, fmin=fmin, fmax=fmax, faverage=True, n_jobs=1)
 
 print('Frequencies in Hz over which coherence was averaged for alpha: ')
 print(freqs[0])
diff --git a/examples/connectivity/plot_mne_inverse_connectivity_spectrum.py b/examples/connectivity/plot_mne_inverse_connectivity_spectrum.py
index c0908c9..ded2d53 100644
--- a/examples/connectivity/plot_mne_inverse_connectivity_spectrum.py
+++ b/examples/connectivity/plot_mne_inverse_connectivity_spectrum.py
@@ -14,7 +14,6 @@ import matplotlib.pyplot as plt
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.minimum_norm import apply_inverse_epochs, read_inverse_operator
 from mne.connectivity import spectral_connectivity
 
@@ -28,7 +27,7 @@ fname_event = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 
 # Load data
 inverse_operator = read_inverse_operator(fname_inv)
-raw = Raw(fname_raw)
+raw = mne.io.read_raw_fif(fname_raw)
 events = mne.read_events(fname_event)
 
 # Add a bad channel
@@ -68,7 +67,7 @@ sfreq = raw.info['sfreq']  # the sampling frequency
 
 con, freqs, times, n_epochs, n_tapers = spectral_connectivity(
     label_ts, method='wpli2_debiased', mode='multitaper', sfreq=sfreq,
-    fmin=fmin, fmax=fmax, mt_adaptive=True, n_jobs=2)
+    fmin=fmin, fmax=fmax, mt_adaptive=True, n_jobs=1)
 
 n_rows, n_cols = con.shape[:2]
 fig, axes = plt.subplots(n_rows, n_cols, sharex=True, sharey=True)
diff --git a/examples/connectivity/plot_mne_inverse_label_connectivity.py b/examples/connectivity/plot_mne_inverse_label_connectivity.py
index c60bea9..fe54314 100644
--- a/examples/connectivity/plot_mne_inverse_label_connectivity.py
+++ b/examples/connectivity/plot_mne_inverse_label_connectivity.py
@@ -19,7 +19,6 @@ import matplotlib.pyplot as plt
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.minimum_norm import apply_inverse_epochs, read_inverse_operator
 from mne.connectivity import spectral_connectivity
 from mne.viz import circular_layout, plot_connectivity_circle
@@ -34,7 +33,7 @@ fname_event = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 
 # Load data
 inverse_operator = read_inverse_operator(fname_inv)
-raw = Raw(fname_raw)
+raw = mne.io.read_raw_fif(fname_raw)
 events = mne.read_events(fname_event)
 
 # Add a bad channel
@@ -84,7 +83,7 @@ sfreq = raw.info['sfreq']  # the sampling frequency
 con_methods = ['pli', 'wpli2_debiased']
 con, freqs, times, n_epochs, n_tapers = spectral_connectivity(
     label_ts, method=con_methods, mode='multitaper', sfreq=sfreq, fmin=fmin,
-    fmax=fmax, faverage=True, mt_adaptive=True, n_jobs=2)
+    fmax=fmax, faverage=True, mt_adaptive=True, n_jobs=1)
 
 # con is a 3D array, get the connectivity for the first (and only) freq. band
 # for each method
diff --git a/examples/connectivity/plot_mne_inverse_psi_visual.py b/examples/connectivity/plot_mne_inverse_psi_visual.py
index 34889a6..1c7d69e 100644
--- a/examples/connectivity/plot_mne_inverse_psi_visual.py
+++ b/examples/connectivity/plot_mne_inverse_psi_visual.py
@@ -28,7 +28,6 @@ import numpy as np
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.minimum_norm import read_inverse_operator, apply_inverse_epochs
 from mne.connectivity import seed_target_indices, phase_slope_index
 
@@ -46,7 +45,7 @@ method = "dSPM"  # use dSPM method (could also be MNE or sLORETA)
 
 # Load data
 inverse_operator = read_inverse_operator(fname_inv)
-raw = Raw(fname_raw)
+raw = mne.io.read_raw_fif(fname_raw)
 events = mne.read_events(fname_event)
 
 # pick MEG channels
diff --git a/examples/connectivity/plot_sensor_connectivity.py b/examples/connectivity/plot_sensor_connectivity.py
index ddc4d7d..66852c4 100644
--- a/examples/connectivity/plot_sensor_connectivity.py
+++ b/examples/connectivity/plot_sensor_connectivity.py
@@ -29,7 +29,7 @@ raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 # Add a bad channel
@@ -51,7 +51,7 @@ sfreq = raw.info['sfreq']  # the sampling frequency
 tmin = 0.0  # exclude the baseline period
 con, freqs, times, n_epochs, n_tapers = spectral_connectivity(
     epochs, method='pli', mode='multitaper', sfreq=sfreq, fmin=fmin, fmax=fmax,
-    faverage=True, tmin=tmin, mt_adaptive=False, n_jobs=2)
+    faverage=True, tmin=tmin, mt_adaptive=False, n_jobs=1)
 
 # the epochs contain an EOG channel, which we remove now
 ch_names = epochs.ch_names
diff --git a/examples/datasets/plot_brainstorm_data.py b/examples/datasets/plot_brainstorm_data.py
index 08f0bcd..d923ed3 100644
--- a/examples/datasets/plot_brainstorm_data.py
+++ b/examples/datasets/plot_brainstorm_data.py
@@ -4,15 +4,16 @@ Brainstorm tutorial datasets
 ============================
 
 Here we compute the evoked from raw for the Brainstorm
-tutorial dataset. For comparison, see:
-http://neuroimage.usc.edu/brainstorm/Tutorials/MedianNerveCtf
+tutorial dataset. For comparison, see [1]_ and:
+
+    http://neuroimage.usc.edu/brainstorm/Tutorials/MedianNerveCtf
 
 References
 ----------
 .. [1] Tadel F, Baillet S, Mosher JC, Pantazis D, Leahy RM.
-Brainstorm: A User-Friendly Application for MEG/EEG Analysis.
-Computational Intelligence and Neuroscience, vol. 2011, Article ID 879716,
-13 pages, 2011. doi:10.1155/2011/879716
+       Brainstorm: A User-Friendly Application for MEG/EEG Analysis.
+       Computational Intelligence and Neuroscience, vol. 2011, Article ID
+       879716, 13 pages, 2011. doi:10.1155/2011/879716
 """
 
 # Authors: Mainak Jas <mainak.jas at telecom-paristech.fr>
@@ -23,7 +24,6 @@ import numpy as np
 
 import mne
 from mne.datasets.brainstorm import bst_raw
-from mne.io import Raw
 
 print(__doc__)
 
@@ -34,7 +34,7 @@ data_path = bst_raw.data_path()
 
 raw_fname = data_path + '/MEG/bst_raw/' + \
                         'subj001_somatosensory_20111109_01_AUX-f_raw.fif'
-raw = Raw(raw_fname, preload=True, add_eeg_ref=False)
+raw = mne.io.read_raw_fif(raw_fname, preload=True, add_eeg_ref=False)
 raw.plot()
 
 # set EOG channel
@@ -59,7 +59,7 @@ epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
 evoked = epochs.average()
 
 # remove physiological artifacts (eyeblinks, heartbeats) using SSP on baseline
-evoked.add_proj(mne.compute_proj_evoked(evoked.crop(tmax=0, copy=True)))
+evoked.add_proj(mne.compute_proj_evoked(evoked.copy().crop(tmax=0)))
 evoked.apply_proj()
 
 # fix stim artifact
diff --git a/examples/datasets/plot_megsim_data.py b/examples/datasets/plot_megsim_data.py
index 47b6195..2b746d5 100644
--- a/examples/datasets/plot_megsim_data.py
+++ b/examples/datasets/plot_megsim_data.py
@@ -15,8 +15,8 @@ Gilliam K, Donahue CH, Montano R, Bryant JE, Scott A, Stephen JM
 Realistic Simulated and Empirical Data. Neuroinformatics 10:141-158
 """
 
+import mne
 from mne import find_events, Epochs, pick_types, read_evokeds
-from mne.io import Raw
 from mne.datasets.megsim import load_data
 
 print(__doc__)
@@ -31,7 +31,7 @@ raw_fnames = load_data(condition=condition, data_format='raw',
 evoked_fnames = load_data(condition=condition, data_format='evoked',
                           data_type='simulation', verbose=True)
 
-raw = Raw(raw_fnames[0])
+raw = mne.io.read_raw_fif(raw_fnames[0])
 events = find_events(raw, stim_channel="STI 014", shortest_event=1)
 
 # Visualize raw file
diff --git a/examples/datasets/plot_spm_faces_dataset.py b/examples/datasets/plot_spm_faces_dataset.py
index fe6bef9..6277db2 100644
--- a/examples/datasets/plot_spm_faces_dataset.py
+++ b/examples/datasets/plot_spm_faces_dataset.py
@@ -5,13 +5,14 @@ From raw data to dSPM on SPM Faces dataset
 ==========================================
 
 Runs a full pipeline using MNE-Python:
-- artifact removal
-- averaging Epochs
-- forward model computation
-- source reconstruction using dSPM on the contrast : "faces - scrambled"
 
-Note that this example does quite a bit of processing, so even on a
-fast machine it can take about 10 minutes to complete.
+    - artifact removal
+    - averaging Epochs
+    - forward model computation
+    - source reconstruction using dSPM on the contrast : "faces - scrambled"
+
+.. note:: This example does quite a bit of processing, so even on a
+          fast machine it can take several minutes to complete.
 """
 # Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
 #          Denis Engemann <denis.engemann at gmail.com>
@@ -35,9 +36,12 @@ subjects_dir = data_path + '/subjects'
 ###############################################################################
 # Load and filter data, set up epochs
 
-raw_fname = data_path + '/MEG/spm/SPM_CTF_MEG_example_faces%d_3D_raw.fif'
+raw_fname = data_path + '/MEG/spm/SPM_CTF_MEG_example_faces%d_3D.ds'
 
-raw = io.Raw(raw_fname % 1, preload=True)  # Take first run
+raw = io.read_raw_ctf(raw_fname % 1, preload=True)  # Take first run
+# Here to save memory and time we'll downsample heavily -- this is not
+# advised for real data as it can effectively jitter events!
+raw.resample(120., npad='auto')
 
 picks = mne.pick_types(raw.info, meg=True, exclude='bads')
 raw.filter(1, 30, method='iir')
@@ -57,7 +61,7 @@ epochs = mne.Epochs(raw, events, event_ids, tmin, tmax,  picks=picks,
                     baseline=baseline, preload=True, reject=reject)
 
 # Fit ICA, find and remove major artifacts
-ica = ICA(n_components=0.95).fit(raw, decim=6, reject=reject)
+ica = ICA(n_components=0.95, random_state=0).fit(raw, decim=1, reject=reject)
 
 # compute correlation scores, get bad indices sorted by score
 eog_epochs = create_eog_epochs(raw, ch_name='MRT31-2908', reject=reject)
@@ -66,9 +70,9 @@ ica.plot_scores(eog_scores, eog_inds)  # see scores the selection is based on
 ica.plot_components(eog_inds)  # view topographic sensitivity of components
 ica.exclude += eog_inds[:1]  # we saw the 2nd ECG component looked too dipolar
 ica.plot_overlay(eog_epochs.average())  # inspect artifact removal
-epochs_cln = ica.apply(epochs, copy=True)  # clean data, default in place
+ica.apply(epochs)  # clean data, default in place
 
-evoked = [epochs_cln[k].average() for k in event_ids]
+evoked = [epochs[k].average() for k in event_ids]
 
 contrast = evoked[1] - evoked[0]
 
@@ -80,7 +84,7 @@ for e in evoked:
 plt.show()
 
 # estimate noise covarariance
-noise_cov = mne.compute_covariance(epochs_cln, tmax=0)
+noise_cov = mne.compute_covariance(epochs, tmax=0, method='shrunk')
 
 ###############################################################################
 # Visualize fields on MEG helmet
diff --git a/examples/decoding/plot_decoding_csp_eeg.py b/examples/decoding/plot_decoding_csp_eeg.py
index 9b43901..97509dc 100644
--- a/examples/decoding/plot_decoding_csp_eeg.py
+++ b/examples/decoding/plot_decoding_csp_eeg.py
@@ -68,7 +68,7 @@ picks = pick_types(raw.info, meg=False, eeg=True, stim=False, eog=False,
 # Testing will be done with a running classifier
 epochs = Epochs(raw, events, event_id, tmin, tmax, proj=True, picks=picks,
                 baseline=None, preload=True, add_eeg_ref=False)
-epochs_train = epochs.crop(tmin=1., tmax=2., copy=True)
+epochs_train = epochs.copy().crop(tmin=1., tmax=2.)
 labels = epochs.events[:, -1] - 2
 
 ###############################################################################
diff --git a/examples/decoding/plot_decoding_csp_space.py b/examples/decoding/plot_decoding_csp_space.py
index da1ad28..fb339d0 100644
--- a/examples/decoding/plot_decoding_csp_space.py
+++ b/examples/decoding/plot_decoding_csp_space.py
@@ -19,6 +19,7 @@ See http://en.wikipedia.org/wiki/Common_spatial_pattern and [1]
 # License: BSD (3-clause)
 
 import numpy as np
+import matplotlib.pyplot as plt
 
 import mne
 from mne import io
@@ -36,7 +37,7 @@ tmin, tmax = -0.2, 0.5
 event_id = dict(aud_l=1, vis_l=3)
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(2, None, method='iir')  # replace baselining with high-pass
 events = mne.read_events(event_fname)
 
@@ -101,7 +102,10 @@ print(scores.mean())  # should get better results than above
 
 # plot CSP patterns estimated on full data for visualization
 csp.fit_transform(epochs_data, labels)
-evoked.data = csp.patterns_.T
-evoked.times = np.arange(evoked.data.shape[0])
-evoked.plot_topomap(times=[0, 1, 2, 3], ch_type='grad',
-                    colorbar=False, size=1.5)
+data = csp.patterns_
+fig, axes = plt.subplots(1, 4)
+for idx in range(4):
+    mne.viz.plot_topomap(data[idx], evoked.info, axes=axes[idx], show=False)
+fig.suptitle('CSP patterns')
+fig.tight_layout()
+fig.show()
diff --git a/examples/decoding/plot_decoding_spatio_temporal_source.py b/examples/decoding/plot_decoding_spatio_temporal_source.py
index c801eb3..631fe41 100644
--- a/examples/decoding/plot_decoding_spatio_temporal_source.py
+++ b/examples/decoding/plot_decoding_spatio_temporal_source.py
@@ -43,7 +43,7 @@ tmin, tmax = -0.2, 0.5
 event_id = dict(aud_r=2, vis_r=4)  # load contra-lateral conditions
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(2, None, method='iir')  # replace baselining with high-pass
 events = mne.read_events(event_fname)
 
@@ -55,7 +55,8 @@ picks = mne.pick_types(raw.info, meg=True, eeg=False, stim=True, eog=True,
 # Read epochs
 epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
                     picks=picks, baseline=None, preload=True,
-                    reject=dict(grad=4000e-13, eog=150e-6))
+                    reject=dict(grad=4000e-13, eog=150e-6),
+                    decim=5)  # decimate to save memory and increase speed
 
 epochs.equalize_event_counts(list(event_id.keys()), 'mintime', copy=False)
 epochs_list = [epochs[k] for k in event_id]
diff --git a/examples/decoding/plot_decoding_time_generalization.py b/examples/decoding/plot_decoding_time_generalization.py
deleted file mode 100644
index f04906e..0000000
--- a/examples/decoding/plot_decoding_time_generalization.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""
-==========================================================
-Decoding sensor space data with Generalization Across Time
-==========================================================
-
-This example runs the analysis computed in:
-
-Jean-Remi King, Alexandre Gramfort, Aaron Schurger, Lionel Naccache
-and Stanislas Dehaene, "Two distinct dynamic modes subtend the detection of
-unexpected sounds", PLOS ONE, 2013,
-http://www.ncbi.nlm.nih.gov/pubmed/24475052
-
-The idea is to learn at one time instant and assess if the decoder
-can predict accurately over time.
-"""
-# Authors: Jean-Remi King <jeanremi.king at gmail.com>
-#          Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#          Denis Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne.datasets import spm_face
-from mne.decoding import GeneralizationAcrossTime
-
-print(__doc__)
-
-# Preprocess data
-data_path = spm_face.data_path()
-# Load and filter data, set up epochs
-raw_fname = data_path + '/MEG/spm/SPM_CTF_MEG_example_faces%d_3D_raw.fif'
-
-raw = mne.io.Raw(raw_fname % 1, preload=True)  # Take first run
-
-picks = mne.pick_types(raw.info, meg=True, exclude='bads')
-raw.filter(1, 45, method='iir')
-
-events = mne.find_events(raw, stim_channel='UPPT001')
-event_id = {"faces": 1, "scrambled": 2}
-tmin, tmax = -0.1, 0.5
-
-decim = 4  # decimate to make the example faster to run
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
-                    picks=picks, baseline=None, preload=True,
-                    reject=dict(mag=1.5e-12), decim=decim, verbose=False)
-
-# Define decoder. The decision function is employed to use cross-validation
-gat = GeneralizationAcrossTime(predict_mode='cross-validation', n_jobs=1)
-
-# fit and score
-gat.fit(epochs)
-gat.score(epochs)
-gat.plot(vmin=0.1, vmax=0.9,
-         title="Generalization Across Time (faces vs. scrambled)")
-gat.plot_diagonal()  # plot decoding across time (correspond to GAT diagonal)
diff --git a/examples/decoding/plot_decoding_time_generalization_conditions.py b/examples/decoding/plot_decoding_time_generalization_conditions.py
index 215c8a6..7212b7f 100644
--- a/examples/decoding/plot_decoding_time_generalization_conditions.py
+++ b/examples/decoding/plot_decoding_time_generalization_conditions.py
@@ -37,7 +37,7 @@ data_path = sample.data_path()
 # Load and filter data, set up epochs
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 events_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
-raw = mne.io.Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 picks = mne.pick_types(raw.info, meg=True, exclude='bads')  # Pick MEG channels
 raw.filter(1, 30, method='fft')  # Band pass filtering signals
 events = mne.read_events(events_fname)
diff --git a/examples/decoding/plot_decoding_xdawn_eeg.py b/examples/decoding/plot_decoding_xdawn_eeg.py
index 372be8f..8ac6a6c 100644
--- a/examples/decoding/plot_decoding_xdawn_eeg.py
+++ b/examples/decoding/plot_decoding_xdawn_eeg.py
@@ -50,7 +50,7 @@ tmin, tmax = -0.1, 0.3
 event_id = dict(aud_l=1, aud_r=2, vis_l=3, vis_r=4)
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(1, 20, method='iir')
 events = read_events(event_fname)
 
diff --git a/examples/decoding/plot_ems_filtering.py b/examples/decoding/plot_ems_filtering.py
index b7ed6e5..1022e4b 100644
--- a/examples/decoding/plot_ems_filtering.py
+++ b/examples/decoding/plot_ems_filtering.py
@@ -45,7 +45,7 @@ tmin = -0.2
 tmax = 0.5
 
 # Read data and create epochs
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(1, 45)
 events = mne.read_events(event_fname)
 
diff --git a/examples/decoding/plot_linear_model_patterns.py b/examples/decoding/plot_linear_model_patterns.py
index f30822c..c4467d0 100644
--- a/examples/decoding/plot_linear_model_patterns.py
+++ b/examples/decoding/plot_linear_model_patterns.py
@@ -44,7 +44,7 @@ tmin, tmax = -0.2, 0.5
 event_id = dict(aud_l=1, vis_l=3)
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(2, None, method='iir')  # replace baselining with high-pass
 events = mne.read_events(event_fname)
 
@@ -55,9 +55,9 @@ epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
 labels = epochs.events[:, -1]
 
 # get MEG and EEG data
-meg_epochs = epochs.pick_types(meg=True, eeg=False, copy=True)
+meg_epochs = epochs.copy().pick_types(meg=True, eeg=False)
 meg_data = meg_epochs.get_data().reshape(len(labels), -1)
-eeg_epochs = epochs.pick_types(meg=False, eeg=True, copy=True)
+eeg_epochs = epochs.copy().pick_types(meg=False, eeg=True)
 eeg_data = eeg_epochs.get_data().reshape(len(labels), -1)
 
 ###############################################################################
diff --git a/examples/forward/plot_bem_contour_mri.py b/examples/forward/plot_bem_contour_mri.py
deleted file mode 100644
index 234a08c..0000000
--- a/examples/forward/plot_bem_contour_mri.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""
-=====================
-Plotting BEM Contours
-=====================
-
-This example displays the BEM surfaces (inner skull, outer skull,
-outer skin) as yellow contours on top of the T1 MRI anatomical image
-used for segmentation. This is useful for inspecting the quality of the
-BEM segmentations which are required for computing the forward solution.
-"""
-
-# Author: Mainak Jas <mainak at neuro.hut.fi>
-#         Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-from mne.viz import plot_bem
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-subjects_dir = data_path + '/subjects'
-
-plot_bem(subject='sample', subjects_dir=subjects_dir, orientation='axial')
-plot_bem(subject='sample', subjects_dir=subjects_dir, orientation='sagittal')
-plot_bem(subject='sample', subjects_dir=subjects_dir, orientation='coronal')
diff --git a/examples/forward/plot_coregistration_transform.py b/examples/forward/plot_coregistration_transform.py
deleted file mode 100644
index e6e10b5..0000000
--- a/examples/forward/plot_coregistration_transform.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-=========================================
-Plotting head in helmet from a trans file
-=========================================
-
-In this example, the head is shown in the
-MEG helmet along with the EEG electrodes in MRI
-coordinate system. This allows assessing the
-MEG <-> MRI coregistration quality.
-
-"""
-# Author: Mainak Jas <mainak at neuro.hut.fi>
-#
-# License: BSD (3-clause)
-
-from mne import read_evokeds
-from mne.datasets import sample
-from mne.viz import plot_trans
-
-print(__doc__)
-
-
-data_path = sample.data_path()
-
-data_path = sample.data_path()
-subjects_dir = data_path + '/subjects'
-evoked_fname = data_path + '/MEG/sample/sample_audvis-ave.fif'
-trans_fname = data_path + '/MEG/sample/sample_audvis_raw-trans.fif'
-
-condition = 'Left Auditory'
-evoked = read_evokeds(evoked_fname, condition=condition, baseline=(-0.2, 0.0))
-plot_trans(evoked.info, trans_fname, subject='sample', dig=True,
-           subjects_dir=subjects_dir)
diff --git a/examples/forward/plot_make_forward.py b/examples/forward/plot_forward_sensitivity_maps.py
similarity index 72%
rename from examples/forward/plot_make_forward.py
rename to examples/forward/plot_forward_sensitivity_maps.py
index 473d94a..5a9991a 100644
--- a/examples/forward/plot_make_forward.py
+++ b/examples/forward/plot_forward_sensitivity_maps.py
@@ -1,11 +1,14 @@
 """
-======================================================
-Create a forward operator and display sensitivity maps
-======================================================
+================================================
+Display sensitivity maps for EEG and MEG sensors
+================================================
 
 Sensitivity maps can be produced from forward operators that
 indicate how well different sensor types will be able to detect
 neural currents from different regions of the brain.
+
+To get started with forward modeling see ref:`tut_forward`.
+
 """
 # Author: Eric Larson <larson.eric.d at gmail.com>
 #
@@ -20,22 +23,18 @@ print(__doc__)
 data_path = sample.data_path()
 
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-trans = data_path + '/MEG/sample/sample_audvis_raw-trans.fif'
-src = data_path + '/subjects/sample/bem/sample-oct-6-src.fif'
-bem = data_path + '/subjects/sample/bem/sample-5120-5120-5120-bem-sol.fif'
-subjects_dir = data_path + '/subjects'
+fwd_fname = data_path + '/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif'
 
-# Note that forward solutions can also be read with read_forward_solution
-fwd = mne.make_forward_solution(raw_fname, trans, src, bem,
-                                fname=None, meg=True, eeg=True, mindist=5.0,
-                                n_jobs=2, overwrite=True)
+subjects_dir = data_path + '/subjects'
 
-# convert to surface orientation for better visualization
-fwd = mne.convert_forward_solution(fwd, surf_ori=True)
+# Read the forward solutions with surface orientation
+fwd = mne.read_forward_solution(fwd_fname, surf_ori=True)
 leadfield = fwd['sol']['data']
-
 print("Leadfield size : %d x %d" % leadfield.shape)
 
+###############################################################################
+# Compute sensitivity maps
+
 grad_map = mne.sensitivity_map(fwd, ch_type='grad', mode='fixed')
 mag_map = mne.sensitivity_map(fwd, ch_type='mag', mode='fixed')
 eeg_map = mne.sensitivity_map(fwd, ch_type='eeg', mode='fixed')
diff --git a/examples/forward/plot_read_forward.py b/examples/forward/plot_read_forward.py
deleted file mode 100644
index ddf9157..0000000
--- a/examples/forward/plot_read_forward.py
+++ /dev/null
@@ -1,67 +0,0 @@
-"""
-====================================================
-Read a forward operator and display sensitivity maps
-====================================================
-
-Forward solutions can be read using read_forward_solution in Python.
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#         Denis Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne.datasets import sample
-import matplotlib.pyplot as plt
-
-print(__doc__)
-
-data_path = sample.data_path()
-
-fname = data_path + '/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif'
-subjects_dir = data_path + '/subjects'
-
-fwd = mne.read_forward_solution(fname, surf_ori=True)
-leadfield = fwd['sol']['data']
-
-print("Leadfield size : %d x %d" % leadfield.shape)
-
-###############################################################################
-# Show gain matrix a.k.a. leadfield matrix with sensitivity map
-
-picks_meg = mne.pick_types(fwd['info'], meg=True, eeg=False)
-picks_eeg = mne.pick_types(fwd['info'], meg=False, eeg=True)
-
-fig, axes = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
-fig.suptitle('Lead field matrix (500 dipoles only)', fontsize=14)
-
-for ax, picks, ch_type in zip(axes, [picks_meg, picks_eeg], ['meg', 'eeg']):
-    im = ax.imshow(leadfield[picks, :500], origin='lower', aspect='auto',
-                   cmap='RdBu_r')
-    ax.set_title(ch_type.upper())
-    ax.set_xlabel('sources')
-    ax.set_ylabel('sensors')
-    plt.colorbar(im, ax=ax, cmap='RdBu_r')
-
-###############################################################################
-# Show sensitivity of each sensor type to dipoles in the source space
-
-grad_map = mne.sensitivity_map(fwd, ch_type='grad', mode='fixed')
-mag_map = mne.sensitivity_map(fwd, ch_type='mag', mode='fixed')
-eeg_map = mne.sensitivity_map(fwd, ch_type='eeg', mode='fixed')
-
-plt.figure()
-plt.hist([grad_map.data.ravel(), mag_map.data.ravel(), eeg_map.data.ravel()],
-         bins=20, label=['Gradiometers', 'Magnetometers', 'EEG'],
-         color=['c', 'b', 'k'])
-plt.title('Normal orientation sensitivity')
-plt.xlabel('sensitivity')
-plt.ylabel('count')
-plt.legend()
-
-# Cautious smoothing to see actual dipoles
-grad_map.plot(time_label='Gradiometer sensitivity', subjects_dir=subjects_dir,
-              clim=dict(lims=[0, 50, 100]))
-
-# Note. The source space uses min-dist and therefore discards most
-# superficial dipoles. This is why parts of the gyri are not covered.
diff --git a/examples/inverse/plot_compute_mne_inverse_epochs_in_label.py b/examples/inverse/plot_compute_mne_inverse_epochs_in_label.py
index aa924bf..9a0f3e0 100644
--- a/examples/inverse/plot_compute_mne_inverse_epochs_in_label.py
+++ b/examples/inverse/plot_compute_mne_inverse_epochs_in_label.py
@@ -16,7 +16,6 @@ import matplotlib.pyplot as plt
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.minimum_norm import apply_inverse_epochs, read_inverse_operator
 from mne.minimum_norm import apply_inverse
 
@@ -40,7 +39,7 @@ method = "dSPM"  # use dSPM method (could also be MNE or sLORETA)
 # Load data
 inverse_operator = read_inverse_operator(fname_inv)
 label = mne.read_label(fname_label)
-raw = Raw(fname_raw)
+raw = mne.io.read_raw_fif(fname_raw)
 events = mne.read_events(fname_event)
 
 # Set up pick list
@@ -74,7 +73,7 @@ stc_evoked_label = stc_evoked.in_label(label)
 # Mean across trials but not across vertices in label
 mean_stc = sum(stcs) / len(stcs)
 
-# compute sign flip to avoid signal cancelation when averaging signed values
+# compute sign flip to avoid signal cancellation when averaging signed values
 flip = mne.label_sign_flip(label, inverse_operator['src'])
 
 label_mean = np.mean(mean_stc.data, axis=0)
diff --git a/examples/inverse/plot_compute_mne_inverse_raw_in_label.py b/examples/inverse/plot_compute_mne_inverse_raw_in_label.py
index 5c36f17..f59bb29 100644
--- a/examples/inverse/plot_compute_mne_inverse_raw_in_label.py
+++ b/examples/inverse/plot_compute_mne_inverse_raw_in_label.py
@@ -16,7 +16,6 @@ import matplotlib.pyplot as plt
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.minimum_norm import apply_inverse_raw, read_inverse_operator
 
 print(__doc__)
@@ -32,7 +31,7 @@ lambda2 = 1.0 / snr ** 2
 method = "sLORETA"  # use sLORETA method (could also be MNE or dSPM)
 
 # Load data
-raw = Raw(fname_raw)
+raw = mne.io.read_raw_fif(fname_raw)
 inverse_operator = read_inverse_operator(fname_inv)
 label = mne.read_label(fname_label)
 
diff --git a/examples/inverse/plot_covariance_whitening_dspm.py b/examples/inverse/plot_covariance_whitening_dspm.py
index dea93dc..23a8be3 100644
--- a/examples/inverse/plot_covariance_whitening_dspm.py
+++ b/examples/inverse/plot_covariance_whitening_dspm.py
@@ -7,21 +7,21 @@ Demonstrate impact of whitening on source estimates
 This example demonstrates the relationship between the noise covariance
 estimate and the MNE / dSPM source amplitudes. It computes source estimates for
 the SPM faces data and compares proper regularization with insufficient
-regularization based on the methods described in [1]. The example demonstrates
+regularization based on the methods described in [1]_. The example demonstrates
 that improper regularization can lead to overestimation of source amplitudes.
 This example makes use of the previous, non-optimized code path that was used
-before implementing the suggestions presented in [1]. Please do not copy the
+before implementing the suggestions presented in [1]_. Please do not copy the
 patterns presented here for your own analysis, this is example is purely
 illustrative.
 
-Note that this example does quite a bit of processing, so even on a
-fast machine it can take a couple of minutes to complete.
+.. note:: This example does quite a bit of processing, so even on a
+          fast machine it can take a couple of minutes to complete.
 
 References
 ----------
-[1] Engemann D. and Gramfort A. (2015) Automated model selection in covariance
-    estimation and spatial whitening of MEG and EEG signals, vol. 108,
-    328-342, NeuroImage.
+.. [1] Engemann D. and Gramfort A. (2015) Automated model selection in
+       covariance estimation and spatial whitening of MEG and EEG signals,
+       vol. 108, 328-342, NeuroImage.
 """
 # Author: Denis A. Engemann <denis.engemann at gmail.com>
 #
@@ -48,12 +48,16 @@ print(__doc__)
 data_path = spm_face.data_path()
 subjects_dir = data_path + '/subjects'
 
-raw_fname = data_path + '/MEG/spm/SPM_CTF_MEG_example_faces%d_3D_raw.fif'
+raw_fname = data_path + '/MEG/spm/SPM_CTF_MEG_example_faces%d_3D.ds'
 
-raw = io.Raw(raw_fname % 1, preload=True)  # Take first run
+raw = io.read_raw_ctf(raw_fname % 1)  # Take first run
+# To save time and memory for this demo, we'll just use the first
+# 2.5 minutes (all we need to get 30 total events) and heavily
+# resample 480->60 Hz (usually you wouldn't do either of these!)
+raw = raw.crop(0, 150.).load_data().resample(60, npad='auto')
 
 picks = mne.pick_types(raw.info, meg=True, exclude='bads')
-raw.filter(1, 30, method='iir', n_jobs=1)
+raw.filter(1, None, method='iir', n_jobs=1)
 
 events = mne.find_events(raw, stim_channel='UPPT001')
 
@@ -65,11 +69,12 @@ reject = dict(mag=3e-12)
 # Make source space
 
 trans = data_path + '/MEG/spm/SPM_CTF_MEG_example_faces1_3D_raw-trans.fif'
-src = mne.setup_source_space('spm', spacing='oct6', subjects_dir=subjects_dir,
-                             overwrite=True, add_dist=False)
+src = mne.setup_source_space('spm', fname=None, spacing='oct6',
+                             subjects_dir=subjects_dir, add_dist=False)
 bem = data_path + '/subjects/spm/bem/spm-5120-5120-5120-bem-sol.fif'
 forward = mne.make_forward_solution(raw.info, trans, src, bem)
 forward = mne.convert_forward_solution(forward, surf_ori=True)
+del src
 
 # inverse parameters
 conditions = 'faces', 'scrambled'
@@ -79,24 +84,16 @@ method = 'dSPM'
 clim = dict(kind='value', lims=[0, 2.5, 5])
 
 ###############################################################################
-# Estimate covariance and show resulting source estimates
+# Estimate covariances
 
-method = 'empirical', 'shrunk'
-best_colors = 'steelblue', 'red'
 samples_epochs = 5, 15,
-fig, (axes1, axes2) = plt.subplots(2, 3, figsize=(9.5, 6))
-
-
-def brain_to_mpl(brain):
-    """convert image to be usable with matplotlib"""
-    tmp_path = op.abspath(op.join(op.curdir, 'my_tmp'))
-    brain.save_imageset(tmp_path, views=['ven'])
-    im = imread(tmp_path + '_ven.png')
-    os.remove(tmp_path + '_ven.png')
-    return im
+method = 'empirical', 'shrunk'
+colors = 'steelblue', 'red'
 
-for n_train, (ax_stc_worst, ax_dynamics, ax_stc_best) in zip(samples_epochs,
-                                                             (axes1, axes2)):
+evokeds = list()
+stcs = list()
+methods_ordered = list()
+for n_train in samples_epochs:
     # estimate covs based on a subset of samples
     # make sure we have the same number of conditions.
     events_ = np.concatenate([events[events[:, 2] == id_][:n_train]
@@ -104,33 +101,66 @@ for n_train, (ax_stc_worst, ax_dynamics, ax_stc_best) in zip(samples_epochs,
     epochs_train = mne.Epochs(raw, events_, event_ids, tmin, tmax, picks=picks,
                               baseline=baseline, preload=True, reject=reject)
     epochs_train.equalize_event_counts(event_ids, copy=False)
+    assert len(epochs_train) == 2 * n_train
 
-    noise_covs = compute_covariance(epochs_train, method=method,
-                                    tmin=None, tmax=0,  # baseline only
-                                    return_estimators=True)  # returns list
+    noise_covs = compute_covariance(
+        epochs_train, method=method, tmin=None, tmax=0,  # baseline only
+        return_estimators=True)  # returns list
     # prepare contrast
     evokeds = [epochs_train[k].average() for k in conditions]
-
-    # compute stc based on worst and best
-    for est, ax, kind, color in zip(noise_covs, (ax_stc_worst, ax_stc_best),
-                                    ['best', 'worst'], best_colors):
-        # We skip empirical rank estimation that we introduced in response to
-        # the findings in reference [1] to use the naive code path that
-        # triggered the behavior described in [1]. The expected true rank is
-        # 274 for this dataset. Please do not do this with your data but
-        # rely on the default rank estimator that helps regularizing the
-        # covariance.
-        inverse_operator = make_inverse_operator(epochs_train.info, forward,
-                                                 est, loose=0.2, depth=0.8,
+    del epochs_train, events_
+    # do contrast
+
+    # We skip empirical rank estimation that we introduced in response to
+    # the findings in reference [1] to use the naive code path that
+    # triggered the behavior described in [1]. The expected true rank is
+    # 274 for this dataset. Please do not do this with your data but
+    # rely on the default rank estimator that helps regularizing the
+    # covariance.
+    stcs.append(list())
+    methods_ordered.append(list())
+    for cov in noise_covs:
+        inverse_operator = make_inverse_operator(evokeds[0].info, forward,
+                                                 cov, loose=0.2, depth=0.8,
                                                  rank=274)
         stc_a, stc_b = (apply_inverse(e, inverse_operator, lambda2, "dSPM",
                                       pick_ori=None) for e in evokeds)
         stc = stc_a - stc_b
+        methods_ordered[-1].append(cov['method'])
+        stcs[-1].append(stc)
+    del inverse_operator, evokeds, cov, noise_covs, stc, stc_a, stc_b
+del raw, forward  # save some memory
+
+
+##############################################################################
+# Show the resulting source estimates
+
+fig, (axes1, axes2) = plt.subplots(2, 3, figsize=(9.5, 6))
+
+
+def brain_to_mpl(brain):
+    """convert image to be usable with matplotlib"""
+    tmp_path = op.abspath(op.join(op.curdir, 'my_tmp'))
+    brain.save_imageset(tmp_path, views=['ven'])
+    im = imread(tmp_path + '_ven.png')
+    os.remove(tmp_path + '_ven.png')
+    return im
+
+
+for ni, (n_train, axes) in enumerate(zip(samples_epochs, (axes1, axes2))):
+    # compute stc based on worst and best
+    ax_dynamics = axes[1]
+    for stc, ax, method, kind, color in zip(stcs[ni],
+                                            axes[::2],
+                                            methods_ordered[ni],
+                                            ['best', 'worst'],
+                                            colors):
         brain = stc.plot(subjects_dir=subjects_dir, hemi='both', clim=clim)
         brain.set_time(175)
 
         im = brain_to_mpl(brain)
         brain.close()
+        del brain
         ax.axis('off')
         ax.get_xaxis().set_visible(False)
         ax.get_yaxis().set_visible(False)
@@ -140,7 +170,7 @@ for n_train, (ax_stc_worst, ax_dynamics, ax_stc_best) in zip(samples_epochs,
         # plot spatial mean
         stc_mean = stc.data.mean(0)
         ax_dynamics.plot(stc.times * 1e3, stc_mean,
-                         label='{0} ({1})'.format(est['method'], kind),
+                         label='{0} ({1})'.format(method, kind),
                          color=color)
         # plot spatial std
         stc_var = stc.data.std(0)
diff --git a/examples/inverse/plot_dics_beamformer.py b/examples/inverse/plot_dics_beamformer.py
index c904661..81dc049 100644
--- a/examples/inverse/plot_dics_beamformer.py
+++ b/examples/inverse/plot_dics_beamformer.py
@@ -20,7 +20,6 @@ import mne
 import matplotlib.pyplot as plt
 import numpy as np
 
-from mne.io import Raw
 from mne.datasets import sample
 from mne.time_frequency import compute_epochs_csd
 from mne.beamformer import dics
@@ -37,7 +36,7 @@ subjects_dir = data_path + '/subjects'
 
 ###############################################################################
 # Read raw data
-raw = Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 raw.info['bads'] = ['MEG 2443', 'EEG 053']  # 2 bads channels
 
 # Set picks
diff --git a/examples/inverse/plot_dics_source_power.py b/examples/inverse/plot_dics_source_power.py
index 957af3d..12f4a61 100644
--- a/examples/inverse/plot_dics_source_power.py
+++ b/examples/inverse/plot_dics_source_power.py
@@ -16,7 +16,6 @@ in the human brain. PNAS (2001) vol. 98 (2) pp. 694-699
 # License: BSD (3-clause)
 
 import mne
-from mne.io import Raw
 from mne.datasets import sample
 from mne.time_frequency import compute_epochs_csd
 from mne.beamformer import dics_source_power
@@ -31,7 +30,7 @@ subjects_dir = data_path + '/subjects'
 
 ###############################################################################
 # Read raw data
-raw = Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 raw.info['bads'] = ['MEG 2443']  # 1 bad MEG channel
 
 # Set picks
diff --git a/examples/inverse/plot_dipole_fit.py b/examples/inverse/plot_dipole_fit.py
deleted file mode 100644
index 08eac64..0000000
--- a/examples/inverse/plot_dipole_fit.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-===============
-Do a dipole fit
-===============
-
-This shows how to fit a dipole using mne-python.
-
-For a comparison of fits between MNE-C and mne-python, see:
-
-    https://gist.github.com/Eric89GXL/ca55f791200fe1dc3dd2
-
-"""
-# Author: Eric Larson <larson.eric.d at gmail.com>
-#
-# License: BSD (3-clause)
-
-from os import path as op
-
-import mne
-
-print(__doc__)
-
-data_path = mne.datasets.sample.data_path()
-subjects_dir = op.join(data_path, 'subjects')
-fname_ave = op.join(data_path, 'MEG', 'sample', 'sample_audvis-ave.fif')
-fname_cov = op.join(data_path, 'MEG', 'sample', 'sample_audvis-cov.fif')
-fname_bem = op.join(subjects_dir, 'sample', 'bem', 'sample-5120-bem-sol.fif')
-fname_trans = op.join(data_path, 'MEG', 'sample',
-                      'sample_audvis_raw-trans.fif')
-fname_surf_lh = op.join(subjects_dir, 'sample', 'surf', 'lh.white')
-
-# Let's localize the N100m (using MEG only)
-evoked = mne.read_evokeds(fname_ave, condition='Right Auditory',
-                          baseline=(None, 0))
-evoked.pick_types(meg=True, eeg=False)
-evoked.crop(0.07, 0.08)
-
-# Fit a dipole
-dip = mne.fit_dipole(evoked, fname_cov, fname_bem, fname_trans)[0]
-
-# Plot the result
-dip.plot_locations(fname_trans, 'sample', subjects_dir)
diff --git a/examples/inverse/plot_lcmv_beamformer.py b/examples/inverse/plot_lcmv_beamformer.py
index 811ad1f..5caf21d 100644
--- a/examples/inverse/plot_lcmv_beamformer.py
+++ b/examples/inverse/plot_lcmv_beamformer.py
@@ -16,7 +16,6 @@ import numpy as np
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.beamformer import lcmv
 
 print(__doc__)
@@ -25,16 +24,16 @@ data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_raw-eve.fif'
 fname_fwd = data_path + '/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif'
-fname_cov = data_path + '/MEG/sample/sample_audvis-shrunk-cov.fif'
 label_name = 'Aud-lh'
 fname_label = data_path + '/MEG/sample/labels/%s.label' % label_name
+subjects_dir = data_path + '/subjects'
 
 ###############################################################################
 # Get epochs
 event_id, tmin, tmax = 1, -0.2, 0.5
 
 # Setup for reading the raw data
-raw = Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname, preload=True, proj=True)
 raw.info['bads'] = ['MEG 2443', 'EEG 053']  # 2 bads channels
 events = mne.read_events(event_fname)
 
@@ -43,16 +42,22 @@ left_temporal_channels = mne.read_selection('Left-temporal')
 picks = mne.pick_types(raw.info, meg=True, eeg=False, stim=True, eog=True,
                        exclude='bads', selection=left_temporal_channels)
 
+# Pick the channels of interest
+raw.pick_channels([raw.ch_names[pick] for pick in picks])
+# Re-normalize our empty-room projectors, so they are fine after subselection
+raw.info.normalize_proj()
+
 # Read epochs
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
-                    picks=picks, baseline=(None, 0), preload=True,
+proj = False  # already applied
+epochs = mne.Epochs(raw, events, event_id, tmin, tmax,
+                    baseline=(None, 0), preload=True, proj=proj,
                     reject=dict(grad=4000e-13, mag=4e-12, eog=150e-6))
 evoked = epochs.average()
 
 forward = mne.read_forward_solution(fname_fwd, surf_ori=True)
 
-# Read regularized noise covariance and compute regularized data covariance
-noise_cov = mne.read_cov(fname_cov)
+# Compute regularized noise and data covariances
+noise_cov = mne.compute_covariance(epochs, tmin=tmin, tmax=0, method='shrunk')
 data_cov = mne.compute_covariance(epochs, tmin=0.04, tmax=0.15,
                                   method='shrunk')
 
@@ -80,3 +85,8 @@ plt.ylim(-0.8, 2.2)
 plt.title('LCMV in %s' % label_name)
 plt.legend()
 plt.show()
+
+# Plot last stc in the brain in 3D with PySurfer if available
+brain = stc.plot(hemi='lh', subjects_dir=subjects_dir)
+brain.set_data_time_index(180)
+brain.show_view('lateral')
diff --git a/examples/inverse/plot_lcmv_beamformer_volume.py b/examples/inverse/plot_lcmv_beamformer_volume.py
index ee23493..dc52e67 100644
--- a/examples/inverse/plot_lcmv_beamformer_volume.py
+++ b/examples/inverse/plot_lcmv_beamformer_volume.py
@@ -17,7 +17,6 @@ import matplotlib.pyplot as plt
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.beamformer import lcmv
 
 from nilearn.plotting import plot_stat_map
@@ -29,14 +28,13 @@ data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_raw-eve.fif'
 fname_fwd = data_path + '/MEG/sample/sample_audvis-meg-vol-7-fwd.fif'
-fname_cov = data_path + '/MEG/sample/sample_audvis-shrunk-cov.fif'
 
 ###############################################################################
 # Get epochs
 event_id, tmin, tmax = 1, -0.2, 0.5
 
 # Setup for reading the raw data
-raw = Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname, preload=True, proj=True)
 raw.info['bads'] = ['MEG 2443', 'EEG 053']  # 2 bads channels
 events = mne.read_events(event_fname)
 
@@ -45,16 +43,22 @@ left_temporal_channels = mne.read_selection('Left-temporal')
 picks = mne.pick_types(raw.info, meg=True, eeg=False, stim=True, eog=True,
                        exclude='bads', selection=left_temporal_channels)
 
+# Pick the channels of interest
+raw.pick_channels([raw.ch_names[pick] for pick in picks])
+# Re-normalize our empty-room projectors, so they are fine after subselection
+raw.info.normalize_proj()
+
 # Read epochs
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
-                    picks=picks, baseline=(None, 0), preload=True,
+proj = False  # already applied
+epochs = mne.Epochs(raw, events, event_id, tmin, tmax,
+                    baseline=(None, 0), preload=True, proj=proj,
                     reject=dict(grad=4000e-13, mag=4e-12, eog=150e-6))
 evoked = epochs.average()
 
 forward = mne.read_forward_solution(fname_fwd)
 
 # Read regularized noise covariance and compute regularized data covariance
-noise_cov = mne.read_cov(fname_cov)
+noise_cov = mne.compute_covariance(epochs, tmin=tmin, tmax=0, method='shrunk')
 data_cov = mne.compute_covariance(epochs, tmin=0.04, tmax=0.15,
                                   method='shrunk')
 
diff --git a/examples/inverse/plot_make_inverse_operator.py b/examples/inverse/plot_make_inverse_operator.py
deleted file mode 100644
index 669846d..0000000
--- a/examples/inverse/plot_make_inverse_operator.py
+++ /dev/null
@@ -1,84 +0,0 @@
-"""
-===============================================================
-Assemble inverse operator and compute MNE-dSPM inverse solution
-===============================================================
-
-Assemble M/EEG, MEG, and EEG inverse operators and compute dSPM
-inverse solution on MNE evoked dataset and stores the solution
-in stc files for visualisation.
-
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import matplotlib.pyplot as plt
-
-import mne
-from mne.datasets import sample
-from mne.minimum_norm import (make_inverse_operator, apply_inverse,
-                              write_inverse_operator)
-
-print(__doc__)
-
-data_path = sample.data_path()
-fname_fwd_meeg = data_path + '/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif'
-fname_fwd_eeg = data_path + '/MEG/sample/sample_audvis-eeg-oct-6-fwd.fif'
-fname_cov = data_path + '/MEG/sample/sample_audvis-shrunk-cov.fif'
-fname_evoked = data_path + '/MEG/sample/sample_audvis-ave.fif'
-
-snr = 3.0
-lambda2 = 1.0 / snr ** 2
-
-# Load data
-evoked = mne.read_evokeds(fname_evoked, condition=0, baseline=(None, 0))
-forward_meeg = mne.read_forward_solution(fname_fwd_meeg, surf_ori=True)
-noise_cov = mne.read_cov(fname_cov)
-
-# Restrict forward solution as necessary for MEG
-forward_meg = mne.pick_types_forward(forward_meeg, meg=True, eeg=False)
-# Alternatively, you can just load a forward solution that is restricted
-forward_eeg = mne.read_forward_solution(fname_fwd_eeg, surf_ori=True)
-
-# make an M/EEG, MEG-only, and EEG-only inverse operators
-info = evoked.info
-inverse_operator_meeg = make_inverse_operator(info, forward_meeg, noise_cov,
-                                              loose=0.2, depth=0.8)
-inverse_operator_meg = make_inverse_operator(info, forward_meg, noise_cov,
-                                             loose=0.2, depth=0.8)
-inverse_operator_eeg = make_inverse_operator(info, forward_eeg, noise_cov,
-                                             loose=0.2, depth=0.8)
-
-write_inverse_operator('sample_audvis-meeg-oct-6-inv.fif',
-                       inverse_operator_meeg)
-write_inverse_operator('sample_audvis-meg-oct-6-inv.fif',
-                       inverse_operator_meg)
-write_inverse_operator('sample_audvis-eeg-oct-6-inv.fif',
-                       inverse_operator_eeg)
-
-# Compute inverse solution
-stcs = dict()
-stcs['meeg'] = apply_inverse(evoked, inverse_operator_meeg, lambda2, "dSPM",
-                             pick_ori=None)
-stcs['meg'] = apply_inverse(evoked, inverse_operator_meg, lambda2, "dSPM",
-                            pick_ori=None)
-stcs['eeg'] = apply_inverse(evoked, inverse_operator_eeg, lambda2, "dSPM",
-                            pick_ori=None)
-
-# Save result in stc files
-names = ['meeg', 'meg', 'eeg']
-for name in names:
-    stcs[name].save('mne_dSPM_inverse-%s' % name)
-
-###############################################################################
-# View activation time-series
-plt.close('all')
-plt.figure(figsize=(8, 6))
-for ii in range(len(stcs)):
-    name = names[ii]
-    stc = stcs[name]
-    plt.subplot(len(stcs), 1, ii + 1)
-    plt.plot(1e3 * stc.times, stc.data[::150, :].T)
-    plt.ylabel('%s\ndSPM value' % str.upper(name))
-plt.xlabel('time (ms)')
-plt.show()
diff --git a/examples/inverse/plot_tf_dics.py b/examples/inverse/plot_tf_dics.py
index b41ce36..7d7191b 100644
--- a/examples/inverse/plot_tf_dics.py
+++ b/examples/inverse/plot_tf_dics.py
@@ -15,7 +15,6 @@ dynamics of cortical activity. NeuroImage (2008) vol. 40 (4) pp. 1686-1700
 # License: BSD (3-clause)
 
 import mne
-from mne.io import Raw
 from mne.event import make_fixed_length_events
 from mne.datasets import sample
 from mne.time_frequency import compute_epochs_csd
@@ -35,7 +34,7 @@ fname_label = data_path + '/MEG/sample/labels/%s.label' % label_name
 
 ###############################################################################
 # Read raw data
-raw = Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 raw.info['bads'] = ['MEG 2443']  # 1 bad MEG channel
 
 # Pick a selection of magnetometer channels. A subset of all channels was used
@@ -45,7 +44,11 @@ left_temporal_channels = mne.read_selection('Left-temporal')
 picks = mne.pick_types(raw.info, meg='mag', eeg=False, eog=False,
                        stim=False, exclude='bads',
                        selection=left_temporal_channels)
+raw.pick_channels([raw.ch_names[pick] for pick in picks])
 reject = dict(mag=4e-12)
+# Re-normalize our empty-room projectors, which should be fine after
+# subselection
+raw.info.normalize_proj()
 
 # Setting time windows. Note that tmin and tmax are set so that time-frequency
 # beamforming will be performed for a wider range of time points than will
@@ -57,19 +60,23 @@ tmin_plot, tmax_plot = -0.3, 0.5  # s
 # Read epochs
 event_id = 1
 events = mne.read_events(event_fname)
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, picks=picks,
-                    baseline=None, preload=True, reject=reject)
+epochs = mne.Epochs(raw, events, event_id, tmin, tmax,
+                    baseline=None, preload=True, proj=True, reject=reject)
 
 # Read empty room noise raw data
-raw_noise = Raw(noise_fname)
+raw_noise = mne.io.read_raw_fif(noise_fname, preload=True)
 raw_noise.info['bads'] = ['MEG 2443']  # 1 bad MEG channel
+raw_noise.pick_channels([raw_noise.ch_names[pick] for pick in picks])
+raw_noise.info.normalize_proj()
 
 # Create noise epochs and make sure the number of noise epochs corresponds to
 # the number of data epochs
 events_noise = make_fixed_length_events(raw_noise, event_id)
 epochs_noise = mne.Epochs(raw_noise, events_noise, event_id, tmin_plot,
-                          tmax_plot, proj=True, picks=picks,
-                          baseline=None, preload=True, reject=reject)
+                          tmax_plot, baseline=None, preload=True, proj=True,
+                          reject=reject)
+epochs_noise.info.normalize_proj()
+epochs_noise.apply_proj()
 # then make sure the number of epochs is the same
 epochs_noise = epochs_noise[:len(epochs.events)]
 
diff --git a/examples/inverse/plot_tf_lcmv.py b/examples/inverse/plot_tf_lcmv.py
index 14e10b8..7e8f116 100644
--- a/examples/inverse/plot_tf_lcmv.py
+++ b/examples/inverse/plot_tf_lcmv.py
@@ -16,7 +16,6 @@ dynamics of cortical activity. NeuroImage (2008) vol. 40 (4) pp. 1686-1700
 
 import mne
 from mne import compute_covariance
-from mne.io import Raw
 from mne.datasets import sample
 from mne.event import make_fixed_length_events
 from mne.beamformer import tf_lcmv
@@ -35,17 +34,21 @@ fname_label = data_path + '/MEG/sample/labels/%s.label' % label_name
 
 ###############################################################################
 # Read raw data, preload to allow filtering
-raw = Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 raw.info['bads'] = ['MEG 2443']  # 1 bad MEG channel
 
 # Pick a selection of magnetometer channels. A subset of all channels was used
 # to speed up the example. For a solution based on all MEG channels use
 # meg=True, selection=None and add grad=4000e-13 to the reject dictionary.
+# We could do this with a "picks" argument to Epochs and the LCMV functions,
+# but here we use raw.pick_types() to save memory.
 left_temporal_channels = mne.read_selection('Left-temporal')
-picks = mne.pick_types(raw.info, meg='mag', eeg=False, eog=False,
-                       stim=False, exclude='bads',
-                       selection=left_temporal_channels)
+raw.pick_types(meg='mag', eeg=False, eog=False, stim=False, exclude='bads',
+               selection=left_temporal_channels)
 reject = dict(mag=4e-12)
+# Re-normalize our empty-room projectors, which should be fine after
+# subselection
+raw.info.normalize_proj()
 
 # Setting time limits for reading epochs. Note that tmin and tmax are set so
 # that time-frequency beamforming will be performed for a wider range of time
@@ -61,24 +64,26 @@ tmin_plot, tmax_plot = -0.3, 0.5  # s
 # parameters passed here are used to create epochs from filtered data. However,
 # reading epochs without preloading means that bad epoch rejection is delayed
 # until later. To perform bad epoch rejection based on the reject parameter
-# passed here, run epochs.drop_bad_epochs(). This is done automatically in
+# passed here, run epochs.drop_bad(). This is done automatically in
 # tf_lcmv to reject bad epochs based on unfiltered data.
 event_id = 1
 events = mne.read_events(event_fname)
 epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
-                    picks=picks, baseline=None, preload=False,
-                    reject=reject)
+                    baseline=None, preload=False, reject=reject)
 
-# Read empty room noise, preload to allow filtering
-raw_noise = Raw(noise_fname, preload=True)
+# Read empty room noise, preload to allow filtering, and pick subselection
+raw_noise = mne.io.read_raw_fif(noise_fname, preload=True)
 raw_noise.info['bads'] = ['MEG 2443']  # 1 bad MEG channel
+raw_noise.pick_types(meg='mag', eeg=False, eog=False, stim=False,
+                     exclude='bads', selection=left_temporal_channels)
+raw_noise.info.normalize_proj()
 
 # Create artificial events for empty room noise data
 events_noise = make_fixed_length_events(raw_noise, event_id, duration=1.)
 # Create an epochs object using preload=True to reject bad epochs based on
 # unfiltered data
 epochs_noise = mne.Epochs(raw_noise, events_noise, event_id, tmin, tmax,
-                          proj=True, picks=picks, baseline=None,
+                          proj=True, baseline=None,
                           preload=True, reject=reject)
 
 # Make sure the number of noise epochs is the same as data epochs
@@ -114,10 +119,10 @@ subtract_evoked = False
 noise_covs = []
 for (l_freq, h_freq) in freq_bins:
     raw_band = raw_noise.copy()
-    raw_band.filter(l_freq, h_freq, picks=epochs.picks, method='iir', n_jobs=1)
+    raw_band.filter(l_freq, h_freq, method='iir', n_jobs=1)
     epochs_band = mne.Epochs(raw_band, epochs_noise.events, event_id,
                              tmin=tmin_plot, tmax=tmax_plot, baseline=None,
-                             picks=epochs.picks, proj=True)
+                             proj=True)
 
     noise_cov = compute_covariance(epochs_band, method='shrunk')
     noise_covs.append(noise_cov)
diff --git a/examples/io/README.txt b/examples/io/README.txt
index 45f88d1..4cd1df1 100644
--- a/examples/io/README.txt
+++ b/examples/io/README.txt
@@ -2,4 +2,5 @@
 Input/Ouput
 -----------
 
-Reading and writing files.
+Reading and writing files. See also our :ref:`tutorials` on manipulating
+data structures.
diff --git a/examples/io/plot_objects_from_arrays.py b/examples/io/plot_objects_from_arrays.py
index f8a84b8..1e1f906 100644
--- a/examples/io/plot_objects_from_arrays.py
+++ b/examples/io/plot_objects_from_arrays.py
@@ -24,7 +24,7 @@ print(__doc__)
 sfreq = 1000  # Sampling frequency
 times = np.arange(0, 10, 0.001)  # Use 10000 samples (10s)
 
-sin = np.sin(times * 10)  # Multiplied by 10 for shorter phase
+sin = np.sin(times * 10)  # Multiplied by 10 for shorter cycles
 cos = np.cos(times * 10)
 sinX2 = sin * 2
 cosX2 = cos * 2
@@ -51,6 +51,11 @@ scalings = {'mag': 2, 'grad': 2}
 raw.plot(n_channels=4, scalings=scalings, title='Data from arrays',
          show=True, block=True)
 
+# It is also possible to auto-compute scalings
+scalings = 'auto'  # Could also pass a dictionary with some value == 'auto'
+raw.plot(n_channels=4, scalings=scalings, title='Auto-scaled Data from arrays',
+         show=True, block=True)
+
 
 ###############################################################################
 # EpochsArray
@@ -63,9 +68,9 @@ events = np.array([[200, 0, event_id],
 # Here a data set of 700 ms epochs from 2 channels is
 # created from sin and cos data.
 # Any data in shape (n_epochs, n_channels, n_times) can be used.
-epochs_data = [[sin[:700], cos[:700]],
-               [sin[1000:1700], cos[1000:1700]],
-               [sin[1800:2500], cos[1800:2500]]]
+epochs_data = np.array([[sin[:700], cos[:700]],
+                        [sin[1000:1700], cos[1000:1700]],
+                        [sin[1800:2500], cos[1800:2500]]])
 
 ch_names = ['sin', 'cos']
 ch_types = ['mag', 'mag']
@@ -76,7 +81,7 @@ epochs = mne.EpochsArray(epochs_data, info=info, events=events,
 
 picks = mne.pick_types(info, meg=True, eeg=False, misc=False)
 
-epochs.plot(picks=picks, show=True, block=True)
+epochs.plot(picks=picks, scalings='auto', show=True, block=True)
 
 
 ###############################################################################
diff --git a/examples/io/plot_read_and_write_raw_data.py b/examples/io/plot_read_and_write_raw_data.py
index 84416d6..a1709be 100644
--- a/examples/io/plot_read_and_write_raw_data.py
+++ b/examples/io/plot_read_and_write_raw_data.py
@@ -20,7 +20,7 @@ data_path = sample.data_path()
 
 fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
 
-raw = mne.io.Raw(fname)
+raw = mne.io.read_raw_fif(fname)
 
 # Set up pick list: MEG + STI 014 - bad channels
 want_meg = True
diff --git a/examples/io/plot_read_epochs.py b/examples/io/plot_read_epochs.py
index b6dfd32..d72db92 100644
--- a/examples/io/plot_read_epochs.py
+++ b/examples/io/plot_read_epochs.py
@@ -28,7 +28,7 @@ event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 event_id, tmin, tmax = 1, -0.2, 0.5
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 # Set up pick list: EEG + MEG - bad channels (modify to your needs)
diff --git a/examples/io/plot_read_evoked.py b/examples/io/plot_read_evoked.py
index 4750d94..d8dd050 100644
--- a/examples/io/plot_read_evoked.py
+++ b/examples/io/plot_read_evoked.py
@@ -3,6 +3,7 @@
 Reading and writing an evoked file
 ==================================
 
+This script shows how to read and write evoked datasets.
 """
 # Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
 #
@@ -29,3 +30,7 @@ evoked.plot(exclude=[])
 
 # Show result as a 2D image (x: time, y: channels, color: amplitude)
 evoked.plot_image(exclude=[])
+
+###############################################################################
+# Use :func:`mne.Evoked.save` or :func:`mne.write_evokeds` to write the evoked
+# responses to a file.
diff --git a/examples/plot_compute_mne_inverse.py b/examples/plot_compute_mne_inverse.py
deleted file mode 100644
index 075b560..0000000
--- a/examples/plot_compute_mne_inverse.py
+++ /dev/null
@@ -1,60 +0,0 @@
-"""
-================================================
-Compute MNE-dSPM inverse solution on evoked data
-================================================
-
-Compute dSPM inverse solution on MNE evoked dataset
-and stores the solution in stc files for visualisation.
-
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import matplotlib.pyplot as plt
-from mne.datasets import sample
-from mne import read_evokeds
-from mne.minimum_norm import apply_inverse, read_inverse_operator
-
-print(__doc__)
-
-data_path = sample.data_path()
-fname_inv = data_path + '/MEG/sample/sample_audvis-meg-oct-6-meg-inv.fif'
-fname_evoked = data_path + '/MEG/sample/sample_audvis-ave.fif'
-subjects_dir = data_path + '/subjects'
-
-snr = 3.0
-lambda2 = 1.0 / snr ** 2
-method = "dSPM"  # use dSPM method (could also be MNE or sLORETA)
-
-# Load data
-evoked = read_evokeds(fname_evoked, condition=0, baseline=(None, 0))
-inverse_operator = read_inverse_operator(fname_inv)
-
-# Compute inverse solution
-stc = apply_inverse(evoked, inverse_operator, lambda2, method,
-                    pick_ori=None)
-
-# Save result in stc files
-stc.save('mne_%s_inverse' % method)
-
-###############################################################################
-# View activation time-series
-plt.plot(1e3 * stc.times, stc.data[::100, :].T)
-plt.xlabel('time (ms)')
-plt.ylabel('%s value' % method)
-plt.show()
-
-# Plot brain in 3D with PySurfer if available
-brain = stc.plot(hemi='rh', subjects_dir=subjects_dir)
-brain.show_view('lateral')
-
-# use peak getter to move vizualization to the time point of the peak
-vertno_max, time_idx = stc.get_peak(hemi='rh', time_as_index=True)
-
-brain.set_data_time_index(time_idx)
-
-# draw marker at maximum peaking vertex
-brain.add_foci(vertno_max, coords_as_verts=True, hemi='rh', color='blue',
-               scale_factor=0.6)
-brain.save_image('dSPM_map.png')
diff --git a/examples/plot_extract_events_from_raw.py b/examples/plot_extract_events_from_raw.py
deleted file mode 100644
index 4d52daf..0000000
--- a/examples/plot_extract_events_from_raw.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-=========================
-Find events in a raw file
-=========================
-
-Find events from the stimulation/trigger channel in the raw data.
-The plot them to get an idea of the paradigm.
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne.datasets import sample
-from mne.io import Raw
-
-print(__doc__)
-
-data_path = sample.data_path()
-fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-
-# Reading events
-raw = Raw(fname)
-
-events = mne.find_events(raw, stim_channel='STI 014')
-
-# Writing events
-mne.write_events('sample_audvis_raw-eve.fif', events)
-
-for ind, before, after in events[:5]:
-    print("At sample %d stim channel went from %d to %d"
-          % (ind, before, after))
-
-# Plot the events to get an idea of the paradigm
-# Specify colors and an event_id dictionary for the legend.
-event_id = {'aud_l': 1, 'aud_r': 2, 'vis_l': 3, 'vis_r': 4, 'smiley': 5,
-            'button': 32}
-color = {1: 'green', 2: 'yellow', 3: 'red', 4: 'c', 5: 'black', 32: 'blue'}
-
-mne.viz.plot_events(events, raw.info['sfreq'], raw.first_samp, color=color,
-                    event_id=event_id)
diff --git a/examples/plot_from_raw_to_epochs_to_evoked.py b/examples/plot_from_raw_to_epochs_to_evoked.py
deleted file mode 100644
index c655cc6..0000000
--- a/examples/plot_from_raw_to_epochs_to_evoked.py
+++ /dev/null
@@ -1,77 +0,0 @@
-"""
-========================================================
-Extract epochs, average and save evoked response to disk
-========================================================
-
-This script shows how to read the epochs from a raw file given
-a list of events. The epochs are averaged to produce evoked
-data and then saved to disk.
-
-"""
-# Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#          Denis A. Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne import io
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-
-###############################################################################
-# Set parameters
-raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
-tmin, tmax = -0.2, 0.5
-
-# Select events to extract epochs from.
-event_id = {'Auditory/Left': 1, 'Auditory/Right': 2}
-
-#   Setup for reading the raw data
-raw = io.Raw(raw_fname)
-events = mne.read_events(event_fname)
-
-#   Plot raw data
-raw.plot(events=events, event_color={1: 'cyan', -1: 'lightgray'})
-
-#   Set up pick list: EEG + STI 014 - bad channels (modify to your needs)
-include = []  # or stim channels ['STI 014']
-raw.info['bads'] = ['MEG 2443', 'EEG 053']  # set bads
-
-# pick EEG and MEG channels
-picks = mne.pick_types(raw.info, meg=True, eeg=True, stim=False, eog=True,
-                       include=include, exclude='bads')
-# Read epochs
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0), reject=dict(eeg=80e-6, eog=150e-6),
-                    preload=True)
-
-# Plot epochs.
-epochs.plot(title='Auditory left/right')
-
-# Look at channels that caused dropped events, showing that the subject's
-# blinks were likely to blame for most epochs being dropped
-epochs.drop_bad_epochs()
-epochs.plot_drop_log(subject='sample')
-
-# Average epochs and get evoked data corresponding to the left stimulation
-evoked = epochs['Left'].average()
-
-evoked.save('sample_audvis_eeg-ave.fif')  # save evoked data to disk
-
-###############################################################################
-# View evoked response
-
-evoked.plot(gfp=True)
-
-###############################################################################
-# Save evoked responses for different conditions to disk
-
-# average epochs and get Evoked datasets
-evokeds = [epochs[cond].average() for cond in ['Left', 'Right']]
-
-# save evoked data to disk
-mne.write_evokeds('sample_auditory_and_visual_eeg-ave.fif', evokeds)
diff --git a/examples/preprocessing/plot_corrmap_detection.py b/examples/preprocessing/plot_corrmap_detection.py
deleted file mode 100644
index 96ddf29..0000000
--- a/examples/preprocessing/plot_corrmap_detection.py
+++ /dev/null
@@ -1,76 +0,0 @@
-"""
-==========================================================
-Identify similar ICs across multiple datasets via CORRMAP
-==========================================================
-
-After fitting ICA to multiple data sets, CORRMAP [1]_
-automatically identifies similar ICs in all sets based
-on a manually selected template. These ICs can then be
-removed, or further investigated.
-
-References
-----------
-.. [1] Viola FC, et al. Semi-automatic identification of independent components
-       representing EEG artifact. Clin Neurophysiol 2009, May; 120(5): 868-77.
-"""
-
-# Authors: Jona Sassenhagen <jona.sassenhagen at gmail.com>
-#          Denis Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import mne
-
-from mne.io import Raw
-from mne.preprocessing import ICA
-from mne.preprocessing.ica import corrmap
-from mne.datasets import sample
-
-print(__doc__)
-
-###############################################################################
-# Setup paths and prepare epochs data
-
-data_path = sample.data_path()
-raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-
-raw = Raw(raw_fname, preload=True)
-raw.filter(1, 30, method='iir')
-picks = mne.pick_types(raw.info, meg=False, eeg=True, eog=True, ecg=False,
-                       stim=False, exclude='bads')
-
-events = mne.find_events(raw, stim_channel='STI 014')
-event_id = dict(aud_l=1, aud_r=2, vis_l=3, vis_r=4)
-reject = dict(eog=250e-6)
-tmin, tmax = -0.5, 0.75
-
-
-###############################################################################
-# 1) Fit ICA to all "subjects".
-# In a real-world case, this would instead be multiple subjects/data sets,
-# here we create artificial subsets
-
-all_epochs = mne.Epochs(raw, events, event_id, tmin, tmax,
-                        proj=False, picks=picks, baseline=(None, 0),
-                        preload=True, reject=None, verbose=False)
-
-all_epochs = [all_epochs[start:stop] for start, stop in
-              [(0, 100), (101, 200), (201, 300)]]
-
-icas = [ICA(n_components=20, random_state=1).fit(epochs)
-        for epochs in all_epochs]
-
-# 2) Use corrmap to identify the maps best corresponding
-#    to a pre-specified template across all subsets
-#    (or, in the real world, multiple participant data sets)
-
-template = (0, 0)
-fig_template, fig_detected = corrmap(icas, template=template, label="blinks",
-                                     show=True, threshold=.8)
-
-# 3) Zeroing the identified blink components for all data sets
-#    results in individually cleaned data sets. Specific components
-#    can be accessed using the label_ attribute.
-
-for ica in icas:
-    print(ica.labels_)
diff --git a/examples/preprocessing/plot_define_target_events.py b/examples/preprocessing/plot_define_target_events.py
index 39e6e28..ac3d4c7 100644
--- a/examples/preprocessing/plot_define_target_events.py
+++ b/examples/preprocessing/plot_define_target_events.py
@@ -33,7 +33,7 @@ raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 
 #   Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 #   Set up pick list: EEG + STI 014 - bad channels (modify to your needs)
diff --git a/examples/preprocessing/plot_eog_artifact_histogram.py b/examples/preprocessing/plot_eog_artifact_histogram.py
index 72220c4..e43855a 100644
--- a/examples/preprocessing/plot_eog_artifact_histogram.py
+++ b/examples/preprocessing/plot_eog_artifact_histogram.py
@@ -27,7 +27,7 @@ data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 events = mne.find_events(raw, 'STI 014')
 eog_event_id = 512
 eog_events = mne.preprocessing.find_eog_events(raw, eog_event_id)
diff --git a/examples/preprocessing/plot_estimate_covariance_matrix_baseline.py b/examples/preprocessing/plot_estimate_covariance_matrix_baseline.py
deleted file mode 100644
index d9dfb2e..0000000
--- a/examples/preprocessing/plot_estimate_covariance_matrix_baseline.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""
-===============================================
-Estimate covariance matrix from Epochs baseline
-===============================================
-
-We first define a set of Epochs from events and a raw file.
-Then we estimate the noise covariance of prestimulus data,
-a.k.a. baseline.
-
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne import io
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
-event_id, tmin, tmax = 1, -0.2, 0.5
-
-raw = io.Raw(fname)
-
-###############################################################################
-# Set parameters
-raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-
-#   Setup for reading the raw data
-raw = io.Raw(raw_fname)
-events = mne.read_events(event_fname)
-
-#   Set up pick list: EEG + STI 014 - bad channels (modify to your needs)
-include = []  # or stim channels ['STI 014']
-raw.info['bads'] += ['EEG 053']  # bads + 1 more
-
-# pick EEG channels
-picks = mne.pick_types(raw.info, meg=True, eeg=True, stim=False, eog=True,
-                       include=include, exclude='bads')
-# Read epochs, with proj off by default so we can plot either way later
-reject = dict(grad=4000e-13, mag=4e-12, eeg=80e-6, eog=150e-6)
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0), reject=reject, proj=False)
-
-# Compute the covariance on baseline
-cov = mne.compute_covariance(epochs, tmin=None, tmax=0)
-print(cov)
-
-###############################################################################
-# Show covariance
-mne.viz.plot_cov(cov, raw.info, colorbar=True, proj=True)
-# try setting proj to False to see the effect
diff --git a/examples/preprocessing/plot_estimate_covariance_matrix_raw.py b/examples/preprocessing/plot_estimate_covariance_matrix_raw.py
deleted file mode 100644
index efafce8..0000000
--- a/examples/preprocessing/plot_estimate_covariance_matrix_raw.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-==============================================
-Estimate covariance matrix from a raw FIF file
-==============================================
-
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne import io
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-
-raw = io.Raw(fname)
-
-include = []  # or stim channels ['STI 014']
-raw.info['bads'] += ['EEG 053']  # bads + 1 more
-
-# pick EEG channels
-picks = mne.pick_types(raw.info, meg=True, eeg=True, stim=False, eog=True,
-                       include=include, exclude='bads')
-# setup rejection
-reject = dict(eeg=80e-6, eog=150e-6)
-
-# Compute the covariance from the raw data
-cov = mne.compute_raw_covariance(raw, picks=picks, reject=reject)
-print(cov)
-
-###############################################################################
-# Show covariance
-fig_cov, fig_svd = mne.viz.plot_cov(cov, raw.info, colorbar=True, proj=True)
-# try setting proj to False to see the effect
diff --git a/examples/preprocessing/plot_find_ecg_artifacts.py b/examples/preprocessing/plot_find_ecg_artifacts.py
index 13f84ae..31af97c 100644
--- a/examples/preprocessing/plot_find_ecg_artifacts.py
+++ b/examples/preprocessing/plot_find_ecg_artifacts.py
@@ -27,7 +27,7 @@ data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 
 event_id = 999
 ecg_events, _, _ = mne.preprocessing.find_ecg_events(raw, event_id,
diff --git a/examples/preprocessing/plot_find_eog_artifacts.py b/examples/preprocessing/plot_find_eog_artifacts.py
index 6b884bc..0532fa1 100644
--- a/examples/preprocessing/plot_find_eog_artifacts.py
+++ b/examples/preprocessing/plot_find_eog_artifacts.py
@@ -27,7 +27,7 @@ data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 
 event_id = 998
 eog_events = mne.preprocessing.find_eog_events(raw, event_id)
diff --git a/examples/preprocessing/plot_movement_compensation.py b/examples/preprocessing/plot_movement_compensation.py
new file mode 100644
index 0000000..629f32e
--- /dev/null
+++ b/examples/preprocessing/plot_movement_compensation.py
@@ -0,0 +1,53 @@
+"""
+==============================================
+Maxwell filter data with movement compensation
+==============================================
+
+Demonstrate movement compensation on simulated data. The simulated data
+contains bilateral activation of auditory cortices, repeated over 14
+different head rotations (head center held fixed). See the following for
+details:
+
+    https://github.com/mne-tools/mne-misc-data/blob/master/movement/simulate.py
+
+"""
+# Authors: Eric Larson <larson.eric.d at gmail.com>
+#
+# License: BSD (3-clause)
+
+from os import path as op
+
+import mne
+from mne.preprocessing import maxwell_filter
+
+print(__doc__)
+
+data_path = op.join(mne.datasets.misc.data_path(verbose=True), 'movement')
+
+pos = mne.chpi.read_head_pos(op.join(data_path, 'simulated_quats.pos'))
+raw = mne.io.read_raw_fif(op.join(data_path, 'simulated_movement_raw.fif'))
+raw_stat = mne.io.read_raw_fif(op.join(data_path,
+                                       'simulated_stationary_raw.fif'))
+
+##############################################################################
+# Process our simulated raw data (taking into account head movements)
+
+# extract our resulting events
+events = mne.find_events(raw, stim_channel='STI 014')
+events[:, 2] = 1
+raw.plot(events=events)
+
+topo_kwargs = dict(times=[0, 0.1, 0.2], ch_type='mag', vmin=-500, vmax=500)
+
+# 0. Take average of stationary data (bilateral auditory patterns)
+evoked_stat = mne.Epochs(raw_stat, events, 1, -0.2, 0.8).average()
+evoked_stat.plot_topomap(title='Stationary', **topo_kwargs)
+
+# 1. Take a naive average (smears activity)
+evoked = mne.Epochs(raw, events, 1, -0.2, 0.8).average()
+evoked.plot_topomap(title='Moving: naive average', **topo_kwargs)
+
+# 2. Use raw movement compensation (restores pattern)
+raw_sss = maxwell_filter(raw, head_pos=pos)
+evoked_raw_mc = mne.Epochs(raw_sss, events, 1, -0.2, 0.8).average()
+evoked_raw_mc.plot_topomap(title='Moving: movement compensated', **topo_kwargs)
diff --git a/examples/preprocessing/plot_rereference_eeg.py b/examples/preprocessing/plot_rereference_eeg.py
index dc0c2eb..1d9dd43 100644
--- a/examples/preprocessing/plot_rereference_eeg.py
+++ b/examples/preprocessing/plot_rereference_eeg.py
@@ -23,7 +23,7 @@ event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 event_id, tmin, tmax = 1, -0.2, 0.5
 
 # Read the raw data
-raw = mne.io.Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 events = mne.read_events(event_fname)
 
 # The EEG channels will be plotted to visualize the difference in referencing
diff --git a/examples/preprocessing/plot_resample.py b/examples/preprocessing/plot_resample.py
index 9d50066..75b0af5 100644
--- a/examples/preprocessing/plot_resample.py
+++ b/examples/preprocessing/plot_resample.py
@@ -20,14 +20,13 @@ from __future__ import print_function
 from matplotlib import pyplot as plt
 
 import mne
-from mne.io import Raw
 from mne.datasets import sample
 
 ###############################################################################
-# Setting up data paths and loading raw data
+# Setting up data paths and loading raw data (skip some data for speed)
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-raw = Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname).crop(120, 240).load_data()
 
 ###############################################################################
 # Since downsampling reduces the timing precision of events, we recommend
@@ -37,7 +36,7 @@ epochs = mne.Epochs(raw, events, event_id=2, tmin=-0.1, tmax=0.8, preload=True)
 
 # Downsample to 100 Hz
 print('Original sampling rate:', epochs.info['sfreq'], 'Hz')
-epochs_resampled = epochs.resample(100, copy=True)
+epochs_resampled = epochs.copy().resample(100, npad='auto')
 print('New sampling rate:', epochs_resampled.info['sfreq'], 'Hz')
 
 # Plot a piece of data to see the effects of downsampling
@@ -61,11 +60,11 @@ mne.viz.tight_layout()
 ###############################################################################
 # When resampling epochs is unwanted or impossible, for example when the data
 # doesn't fit into memory or your analysis pipeline doesn't involve epochs at
-# all, the alternative approach is to resample the continous data. This
+# all, the alternative approach is to resample the continuous data. This
 # can also be done on non-preloaded data.
 
 # Resample to 300 Hz
-raw_resampled = raw.resample(300, copy=True)
+raw_resampled = raw.copy().resample(300, npad='auto')
 
 ###############################################################################
 # Because resampling also affects the stim channels, some trigger onsets might
@@ -75,11 +74,12 @@ raw_resampled = raw.resample(300, copy=True)
 print('Number of events before resampling:', len(mne.find_events(raw)))
 
 # Resample to 100 Hz (generates warning)
-raw_resampled = raw.resample(100, copy=True)
+raw_resampled = raw.copy().resample(100, npad='auto')
 print('Number of events after resampling:',
       len(mne.find_events(raw_resampled)))
 
 # To avoid losing events, jointly resample the data and event matrix
 events = mne.find_events(raw)
-raw_resampled, events_resampled = raw.resample(100, events=events, copy=True)
+raw_resampled, events_resampled = raw.copy().resample(
+    100, npad='auto', events=events)
 print('Number of events after resampling:', len(events_resampled))
diff --git a/examples/preprocessing/plot_run_ica.py b/examples/preprocessing/plot_run_ica.py
index ab61561..b9114ca 100644
--- a/examples/preprocessing/plot_run_ica.py
+++ b/examples/preprocessing/plot_run_ica.py
@@ -19,7 +19,6 @@ fast machine it can take about a minute to complete.
 # License: BSD (3-clause)
 
 import mne
-from mne.io import Raw
 from mne.preprocessing import ICA, create_ecg_epochs
 from mne.datasets import sample
 
@@ -31,7 +30,7 @@ print(__doc__)
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 
-raw = Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 raw.filter(1, 30, method='iir')
 raw.pick_types(meg=True, eeg=False, exclude='bads', stim=True)
 
diff --git a/examples/preprocessing/plot_xdawn_denoising.py b/examples/preprocessing/plot_xdawn_denoising.py
index 76b7ce8..03f6f4b 100644
--- a/examples/preprocessing/plot_xdawn_denoising.py
+++ b/examples/preprocessing/plot_xdawn_denoising.py
@@ -49,7 +49,7 @@ tmin, tmax = -0.1, 0.3
 event_id = dict(vis_r=4)
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(1, 20, method='iir')  # replace baselining with high-pass
 events = read_events(event_fname)
 
diff --git a/examples/realtime/ftclient_rt_compute_psd.py b/examples/realtime/ftclient_rt_compute_psd.py
index 22c1095..950ac75 100644
--- a/examples/realtime/ftclient_rt_compute_psd.py
+++ b/examples/realtime/ftclient_rt_compute_psd.py
@@ -20,7 +20,7 @@ import matplotlib.pyplot as plt
 
 import mne
 from mne.realtime import FieldTripClient
-from mne.time_frequency import compute_epochs_psd
+from mne.time_frequency import psd_welch
 
 print(__doc__)
 
@@ -43,7 +43,7 @@ with FieldTripClient(host='localhost', port=1972,
     n_samples = 2048  # time window on which to compute FFT
     for ii in range(20):
         epoch = rt_client.get_data_as_epoch(n_samples=n_samples, picks=picks)
-        psd, freqs = compute_epochs_psd(epoch, fmin=2, fmax=200, n_fft=n_fft)
+        psd, freqs = psd_welch(epoch, fmin=2, fmax=200, n_fft=n_fft)
 
         cmap = 'RdBu_r'
         freq_mask = freqs < 150
diff --git a/examples/realtime/plot_compute_rt_average.py b/examples/realtime/plot_compute_rt_average.py
index 7f6d779..107d169 100644
--- a/examples/realtime/plot_compute_rt_average.py
+++ b/examples/realtime/plot_compute_rt_average.py
@@ -28,7 +28,7 @@ print(__doc__)
 # Fiff file to simulate the realtime client
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-raw = mne.io.Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 
 # select gradiometers
 picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True,
@@ -51,6 +51,7 @@ rt_epochs.start()
 rt_client.send_data(rt_epochs, picks, tmin=0, tmax=150, buffer_size=1000)
 for ii, ev in enumerate(rt_epochs.iter_evoked()):
     print("Just got epoch %d" % (ii + 1))
+    ev.pick_types(meg=True, eog=False)  # leave out the eog channel
     if ii == 0:
         evoked = ev
     else:
diff --git a/examples/realtime/plot_compute_rt_decoder.py b/examples/realtime/plot_compute_rt_decoder.py
index 753728d..9069891 100644
--- a/examples/realtime/plot_compute_rt_decoder.py
+++ b/examples/realtime/plot_compute_rt_decoder.py
@@ -23,7 +23,7 @@ print(__doc__)
 # Fiff file to simulate the realtime client
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-raw = mne.io.Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 
 tmin, tmax = -0.2, 0.5
 event_id = dict(aud_l=1, vis_l=3)
diff --git a/examples/realtime/rt_feedback_server.py b/examples/realtime/rt_feedback_server.py
index 292a32b..c5febc2 100644
--- a/examples/realtime/rt_feedback_server.py
+++ b/examples/realtime/rt_feedback_server.py
@@ -50,7 +50,7 @@ print(__doc__)
 # Load fiff file to simulate data
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-raw = mne.io.Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 
 # Instantiating stimulation server
 
diff --git a/examples/simulation/plot_simulate_evoked_data.py b/examples/simulation/plot_simulate_evoked_data.py
index ef4c5ca..0fab94f 100644
--- a/examples/simulation/plot_simulate_evoked_data.py
+++ b/examples/simulation/plot_simulate_evoked_data.py
@@ -12,9 +12,7 @@ Generate simulated evoked data
 import numpy as np
 import matplotlib.pyplot as plt
 
-from mne import (read_proj, read_forward_solution, read_cov, read_label,
-                 pick_types_forward, pick_types)
-from mne.io import Raw, read_info
+import mne
 from mne.datasets import sample
 from mne.time_frequency import fit_iir_model_raw
 from mne.viz import plot_sparse_source_estimates
@@ -26,8 +24,8 @@ print(__doc__)
 # Load real data as templates
 data_path = sample.data_path()
 
-raw = Raw(data_path + '/MEG/sample/sample_audvis_raw.fif')
-proj = read_proj(data_path + '/MEG/sample/sample_audvis_ecg_proj.fif')
+raw = mne.io.read_raw_fif(data_path + '/MEG/sample/sample_audvis_raw.fif')
+proj = mne.read_proj(data_path + '/MEG/sample/sample_audvis_ecg-proj.fif')
 raw.info['projs'] += proj
 raw.info['bads'] = ['MEG 2443', 'EEG 053']  # mark bad channels
 
@@ -35,13 +33,13 @@ fwd_fname = data_path + '/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif'
 ave_fname = data_path + '/MEG/sample/sample_audvis-no-filter-ave.fif'
 cov_fname = data_path + '/MEG/sample/sample_audvis-cov.fif'
 
-fwd = read_forward_solution(fwd_fname, force_fixed=True, surf_ori=True)
-fwd = pick_types_forward(fwd, meg=True, eeg=True, exclude=raw.info['bads'])
-cov = read_cov(cov_fname)
-info = read_info(ave_fname)
+fwd = mne.read_forward_solution(fwd_fname, force_fixed=True, surf_ori=True)
+fwd = mne.pick_types_forward(fwd, meg=True, eeg=True, exclude=raw.info['bads'])
+cov = mne.read_cov(cov_fname)
+info = mne.io.read_info(ave_fname)
 
 label_names = ['Aud-lh', 'Aud-rh']
-labels = [read_label(data_path + '/MEG/sample/labels/%s.label' % ln)
+labels = [mne.read_label(data_path + '/MEG/sample/labels/%s.label' % ln)
           for ln in label_names]
 
 ###############################################################################
@@ -61,7 +59,7 @@ stc = simulate_sparse_stc(fwd['src'], n_dipoles=2, times=times,
 
 ###############################################################################
 # Generate noisy evoked data
-picks = pick_types(raw.info, meg=True, exclude='bads')
+picks = mne.pick_types(raw.info, meg=True, exclude='bads')
 iir_filter = fit_iir_model_raw(raw, order=5, picks=picks, tmin=60, tmax=180)[1]
 snr = 6.  # dB
 evoked = simulate_evoked(fwd, stc, info, cov, snr, iir_filter=iir_filter)
diff --git a/examples/simulation/plot_simulate_raw_data.py b/examples/simulation/plot_simulate_raw_data.py
index d2d454b..94db8ca 100644
--- a/examples/simulation/plot_simulate_raw_data.py
+++ b/examples/simulation/plot_simulate_raw_data.py
@@ -16,8 +16,8 @@ activation multiple times.
 import numpy as np
 import matplotlib.pyplot as plt
 
+import mne
 from mne import read_source_spaces, find_events, Epochs, compute_covariance
-from mne.io import Raw
 from mne.datasets import sample
 from mne.simulation import simulate_sparse_stc, simulate_raw
 
@@ -31,7 +31,8 @@ bem_fname = (data_path +
              '/subjects/sample/bem/sample-5120-5120-5120-bem-sol.fif')
 
 # Load real data as the template
-raw = Raw(raw_fname).crop(0., 30., copy=False)  # 30 sec is enough
+raw = mne.io.read_raw_fif(raw_fname)
+raw = raw.crop(0., 30.)  # 30 sec is enough
 
 ##############################################################################
 # Generate dipole time series
@@ -67,7 +68,7 @@ fig.show()
 # Simulate raw data
 raw_sim = simulate_raw(raw, stc, trans_fname, src, bem_fname, cov='simple',
                        iir_filter=[0.2, -0.2, 0.04], ecg=True, blink=True,
-                       n_jobs=2, verbose=True)
+                       n_jobs=1, verbose=True)
 raw_sim.plot()
 
 ##############################################################################
diff --git a/examples/stats/plot_cluster_stats_evoked.py b/examples/stats/plot_cluster_stats_evoked.py
index 4f31e88..775c853 100644
--- a/examples/stats/plot_cluster_stats_evoked.py
+++ b/examples/stats/plot_cluster_stats_evoked.py
@@ -31,7 +31,7 @@ tmin = -0.2
 tmax = 0.5
 
 #   Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 channel = 'MEG 1332'  # include only this channel in analysis
@@ -60,7 +60,7 @@ condition2 = condition2[:, 0, :]  # take only one channel to get a 2D array
 threshold = 6.0
 T_obs, clusters, cluster_p_values, H0 = \
     permutation_cluster_test([condition1, condition2], n_permutations=1000,
-                             threshold=threshold, tail=1, n_jobs=2)
+                             threshold=threshold, tail=1, n_jobs=1)
 
 ###############################################################################
 # Plot
diff --git a/examples/stats/plot_fdr_stats_evoked.py b/examples/stats/plot_fdr_stats_evoked.py
index 86fbcf1..8faee10 100644
--- a/examples/stats/plot_fdr_stats_evoked.py
+++ b/examples/stats/plot_fdr_stats_evoked.py
@@ -31,7 +31,7 @@ event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 event_id, tmin, tmax = 1, -0.2, 0.5
 
 #   Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)[:30]
 
 channel = 'MEG 1332'  # include only this channel in analysis
diff --git a/examples/stats/plot_linear_regression_raw.py b/examples/stats/plot_linear_regression_raw.py
index d4393e3..6033c2e 100644
--- a/examples/stats/plot_linear_regression_raw.py
+++ b/examples/stats/plot_linear_regression_raw.py
@@ -3,18 +3,15 @@
 Regression on continuous data (rER[P/F])
 ========================================
 
-This demonstrates how rERPs/regressing the continuous data is a
+This demonstrates how rER[P/F]s - regressing the continuous data - is a
 generalisation of traditional averaging. If all preprocessing steps
-are the same and if no overlap between epochs exists and if all
+are the same, no overlap between epochs exists, and if all
 predictors are binary, regression is virtually identical to traditional
 averaging.
 If overlap exists and/or predictors are continuous, traditional averaging
-is inapplicable, but regression can estimate, including those of
+is inapplicable, but regression can estimate effects, including those of
 continuous predictors.
 
-Note. This example is based on new code which may still not be
-memory-optimized. Be careful when working with a small computer.
-
 rERPs are described in:
 Smith, N. J., & Kutas, M. (2015). Regression-based estimation of ERP
 waveforms: II. Non-linear effects, overlap correction, and practical
@@ -27,41 +24,39 @@ considerations. Psychophysiology, 52(2), 169-189.
 import matplotlib.pyplot as plt
 
 import mne
-from mne.datasets import spm_face
+from mne.datasets import sample
 from mne.stats.regression import linear_regression_raw
 
-# Preprocess data
-data_path = spm_face.data_path()
-# Load and filter data, set up epochs
-raw_fname = data_path + '/MEG/spm/SPM_CTF_MEG_example_faces1_3D_raw.fif'
-
-raw = mne.io.Raw(raw_fname, preload=True)  # Take first run
+# Load and preprocess data
+data_path = sample.data_path()
+raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
+raw = mne.io.read_raw_fif(raw_fname, preload=True).pick_types(
+    meg='grad', stim=True, eeg=False).filter(1, None, method='iir')
 
-picks = mne.pick_types(raw.info, meg=True, exclude='bads')
-raw.filter(1, 45, method='iir')
-
-events = mne.find_events(raw, stim_channel='UPPT001')
-event_id = dict(faces=1, scrambled=2)
+# Set up events
+events = mne.find_events(raw)
+event_id = {'Aud/L': 1, 'Aud/R': 2}
 tmin, tmax = -.1, .5
 
-raw.pick_types(meg=True)
-
 # regular epoching
+picks = mne.pick_types(raw.info, meg=True)
 epochs = mne.Epochs(raw, events, event_id, tmin, tmax, reject=None,
-                    baseline=None, preload=True, verbose=False, decim=4)
+                    baseline=None, preload=True, verbose=False)
 
 # rERF
 evokeds = linear_regression_raw(raw, events=events, event_id=event_id,
-                                reject=None, tmin=tmin, tmax=tmax,
-                                decim=4)
+                                reject=None, tmin=tmin, tmax=tmax)
 # linear_regression_raw returns a dict of evokeds
 # select conditions similarly to mne.Epochs objects
 
-# plot both results
-cond = "faces"
-fig, (ax1, ax2) = plt.subplots(1, 2)
-epochs[cond].average().plot(axes=ax1, show=False)
-evokeds[cond].plot(axes=ax2, show=False)
+# plot both results, and their difference
+cond = "Aud/L"
+fig, (ax1, ax2, ax3) = plt.subplots(3, 1)
+params = dict(spatial_colors=True, show=False, ylim=dict(grad=(-200, 200)))
+epochs[cond].average().plot(axes=ax1, **params)
+evokeds[cond].plot(axes=ax2, **params)
+(evokeds[cond] - epochs[cond].average()).plot(axes=ax3, **params)
 ax1.set_title("Traditional averaging")
 ax2.set_title("rERF")
+ax3.set_title("Difference")
 plt.show()
diff --git a/examples/stats/plot_sensor_permutation_test.py b/examples/stats/plot_sensor_permutation_test.py
index 5aae6bc..c650fa1 100644
--- a/examples/stats/plot_sensor_permutation_test.py
+++ b/examples/stats/plot_sensor_permutation_test.py
@@ -31,7 +31,7 @@ tmin = -0.2
 tmax = 0.5
 
 #   Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 #   Set up pick list: MEG + STI 014 - bad channels (modify to your needs)
@@ -50,7 +50,7 @@ temporal_mask = np.logical_and(0.04 <= times, times <= 0.06)
 data = np.mean(data[:, :, temporal_mask], axis=2)
 
 n_permutations = 50000
-T0, p_values, H0 = permutation_t_test(data, n_permutations, n_jobs=2)
+T0, p_values, H0 = permutation_t_test(data, n_permutations, n_jobs=1)
 
 significant_sensors = picks[p_values <= 0.05]
 significant_sensors_names = [raw.ch_names[k] for k in significant_sensors]
diff --git a/examples/stats/plot_sensor_regression.py b/examples/stats/plot_sensor_regression.py
index a28cd2e..b12c2db 100644
--- a/examples/stats/plot_sensor_regression.py
+++ b/examples/stats/plot_sensor_regression.py
@@ -39,7 +39,7 @@ tmin, tmax = -0.2, 0.5
 event_id = dict(aud_l=1, aud_r=2)
 
 # Setup for reading the raw data
-raw = mne.io.Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 picks = mne.pick_types(raw.info, meg='mag', eeg=False, stim=False,
diff --git a/examples/time_frequency/plot_compute_raw_data_spectrum.py b/examples/time_frequency/plot_compute_raw_data_spectrum.py
index bc9100c..6a158d9 100644
--- a/examples/time_frequency/plot_compute_raw_data_spectrum.py
+++ b/examples/time_frequency/plot_compute_raw_data_spectrum.py
@@ -18,6 +18,7 @@ import matplotlib.pyplot as plt
 import mne
 from mne import io, read_proj, read_selection
 from mne.datasets import sample
+from mne.time_frequency import psd_multitaper
 
 print(__doc__)
 
@@ -25,10 +26,12 @@ print(__doc__)
 # Set parameters
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-proj_fname = data_path + '/MEG/sample/sample_audvis_eog_proj.fif'
+proj_fname = data_path + '/MEG/sample/sample_audvis_eog-proj.fif'
 
-# Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+tmin, tmax = 0, 60  # use the first 60s of data
+
+# Setup for reading the raw data (to save memory, crop before loading)
+raw = io.read_raw_fif(raw_fname).crop(tmin, tmax).load_data()
 raw.info['bads'] += ['MEG 2443', 'EEG 053']  # bads + 2 more
 
 # Add SSP projection vectors to reduce EOG and ECG artifacts
@@ -36,14 +39,11 @@ projs = read_proj(proj_fname)
 raw.add_proj(projs, remove_existing=True)
 
 
-tmin, tmax = 0, 60  # use the first 60s of data
 fmin, fmax = 2, 300  # look at frequencies between 2 and 300Hz
 n_fft = 2048  # the FFT size (n_fft). Ideally a power of 2
 
-plt.ion()
-
 # Let's first check out all channel types
-raw.plot_psd(area_mode='range', tmax=10.0)
+raw.plot_psd(area_mode='range', tmax=10.0, show=False)
 
 # Now let's focus on a smaller subset:
 # Pick MEG magnetometers in the Left-temporal region
@@ -57,16 +57,36 @@ picks = picks[:4]
 plt.figure()
 ax = plt.axes()
 raw.plot_psd(tmin=tmin, tmax=tmax, fmin=fmin, fmax=fmax, n_fft=n_fft,
-             n_jobs=1, proj=False, ax=ax, color=(0, 0, 1),  picks=picks)
+             n_jobs=1, proj=False, ax=ax, color=(0, 0, 1),  picks=picks,
+             show=False)
 
 # And now do the same with SSP applied
 raw.plot_psd(tmin=tmin, tmax=tmax, fmin=fmin, fmax=fmax, n_fft=n_fft,
-             n_jobs=1, proj=True, ax=ax, color=(0, 1, 0), picks=picks)
+             n_jobs=1, proj=True, ax=ax, color=(0, 1, 0), picks=picks,
+             show=False)
 
 # And now do the same with SSP + notch filtering
-raw.notch_filter(np.arange(60, 241, 60), picks=picks, n_jobs=1)
+# Pick all channels for notch since the SSP projection mixes channels together
+raw.notch_filter(np.arange(60, 241, 60), n_jobs=1)
 raw.plot_psd(tmin=tmin, tmax=tmax, fmin=fmin, fmax=fmax, n_fft=n_fft,
-             n_jobs=1, proj=True, ax=ax, color=(1, 0, 0), picks=picks)
+             n_jobs=1, proj=True, ax=ax, color=(1, 0, 0), picks=picks,
+             show=False)
 
 ax.set_title('Four left-temporal magnetometers')
 plt.legend(['Without SSP', 'With SSP', 'SSP + Notch'])
+
+# Alternatively, you may also create PSDs from Raw objects with psd_XXX
+f, ax = plt.subplots()
+psds, freqs = psd_multitaper(raw, low_bias=True, tmin=tmin, tmax=tmax,
+                             fmin=fmin, fmax=fmax, proj=True, picks=picks,
+                             n_jobs=1)
+psds = 10 * np.log10(psds)
+psds_mean = psds.mean(0)
+psds_std = psds.std(0)
+
+ax.plot(freqs, psds_mean, color='k')
+ax.fill_between(freqs, psds_mean - psds_std, psds_mean + psds_std,
+                color='k', alpha=.5)
+ax.set(title='Multitaper PSD', xlabel='Frequency',
+       ylabel='Power Spectral Density (dB)')
+plt.show()
diff --git a/examples/time_frequency/plot_compute_source_psd_epochs.py b/examples/time_frequency/plot_compute_source_psd_epochs.py
index 3196a3a..e3a0ac1 100644
--- a/examples/time_frequency/plot_compute_source_psd_epochs.py
+++ b/examples/time_frequency/plot_compute_source_psd_epochs.py
@@ -17,7 +17,6 @@ import matplotlib.pyplot as plt
 
 import mne
 from mne.datasets import sample
-from mne.io import Raw
 from mne.minimum_norm import read_inverse_operator, compute_source_psd_epochs
 
 print(__doc__)
@@ -37,7 +36,7 @@ method = "dSPM"  # use dSPM method (could also be MNE or sLORETA)
 # Load data
 inverse_operator = read_inverse_operator(fname_inv)
 label = mne.read_label(fname_label)
-raw = Raw(fname_raw)
+raw = mne.io.read_raw_fif(fname_raw)
 events = mne.read_events(fname_event)
 
 # Set up pick list
diff --git a/examples/time_frequency/plot_epochs_spectra.py b/examples/time_frequency/plot_epochs_spectra.py
deleted file mode 100644
index 7c35d9f..0000000
--- a/examples/time_frequency/plot_epochs_spectra.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
-=============================================
-Compute the power spectral density of epochs
-=============================================
-
-This script shows how to compute the power spectral density (PSD)
-of measurements on epochs. It also shows how to plot its spatial
-distribution.
-"""
-# Authors: Denis Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne import io
-from mne.datasets import sample
-
-print(__doc__)
-
-###############################################################################
-# Set parameters
-data_path = sample.data_path()
-raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-event_fname = data_path + '/MEG/sample/sample_audvis_raw-eve.fif'
-
-# Setup for reading the raw data
-raw = io.Raw(raw_fname)
-events = mne.read_events(event_fname)
-
-tmin, tmax, event_id = -1., 1., 1
-raw.info['bads'] += ['MEG 2443']  # bads
-
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax,
-                    proj=True, baseline=(None, 0), preload=True,
-                    reject=dict(grad=4000e-13, eog=150e-6))
-
-# Let's first check out all channel types by averaging across epochs.
-epochs.plot_psd(fmin=2, fmax=200)
-
-# picks MEG gradiometers
-picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=False,
-                       stim=False, exclude='bads')
-
-# Now let's take a look at the spatial distributions of the psd.
-epochs.plot_psd_topomap(ch_type='grad', normalize=True)
diff --git a/examples/time_frequency/plot_source_label_time_frequency.py b/examples/time_frequency/plot_source_label_time_frequency.py
index 5480522..1b47722 100644
--- a/examples/time_frequency/plot_source_label_time_frequency.py
+++ b/examples/time_frequency/plot_source_label_time_frequency.py
@@ -36,7 +36,7 @@ fname_label = data_path + '/MEG/sample/labels/%s.label' % label_name
 tmin, tmax, event_id = -0.2, 0.5, 2
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.find_events(raw, stim_channel='STI 014')
 inverse_operator = read_inverse_operator(fname_inv)
 
diff --git a/examples/time_frequency/plot_source_power_spectrum.py b/examples/time_frequency/plot_source_power_spectrum.py
index 3eeadb5..f502df0 100644
--- a/examples/time_frequency/plot_source_power_spectrum.py
+++ b/examples/time_frequency/plot_source_power_spectrum.py
@@ -27,7 +27,7 @@ fname_inv = data_path + '/MEG/sample/sample_audvis-meg-oct-6-meg-inv.fif'
 fname_label = data_path + '/MEG/sample/labels/Aud-lh.label'
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, verbose=False)
+raw = io.read_raw_fif(raw_fname, verbose=False)
 events = mne.find_events(raw, stim_channel='STI 014')
 inverse_operator = read_inverse_operator(fname_inv)
 raw.info['bads'] = ['MEG 2443', 'EEG 053']
diff --git a/examples/time_frequency/plot_source_space_time_frequency.py b/examples/time_frequency/plot_source_space_time_frequency.py
index 0c7175a..0fa5546 100644
--- a/examples/time_frequency/plot_source_space_time_frequency.py
+++ b/examples/time_frequency/plot_source_space_time_frequency.py
@@ -29,7 +29,7 @@ fname_inv = data_path + '/MEG/sample/sample_audvis-meg-oct-6-meg-inv.fif'
 tmin, tmax, event_id = -0.2, 0.5, 1
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.find_events(raw, stim_channel='STI 014')
 inverse_operator = read_inverse_operator(fname_inv)
 
diff --git a/examples/time_frequency/plot_stockwell.py b/examples/time_frequency/plot_stockwell.py
deleted file mode 100644
index 8916a08..0000000
--- a/examples/time_frequency/plot_stockwell.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-=======================================================
-Time frequency with Stockwell transform in sensor space
-=======================================================
-
-This script shows how to compute induced power and intertrial coherence
-using the Stockwell transform, a.k.a. S-Transform.
-
-"""
-# Authors: Denis A. Engemann <denis.engemann at gmail.com>
-#          Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import mne
-from mne import io
-from mne.time_frequency import tfr_stockwell
-from mne.datasets import somato
-
-print(__doc__)
-
-###############################################################################
-# Set parameters
-data_path = somato.data_path()
-raw_fname = data_path + '/MEG/somato/sef_raw_sss.fif'
-event_id, tmin, tmax = 1, -1., 3.
-
-# Setup for reading the raw data
-raw = io.Raw(raw_fname)
-baseline = (None, 0)
-events = mne.find_events(raw, stim_channel='STI 014')
-
-# picks MEG gradiometers
-picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True, stim=False)
-
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=baseline, reject=dict(grad=4000e-13, eog=350e-6),
-                    preload=True)
-
-###############################################################################
-# Calculate power and intertrial coherence
-
-epochs = epochs.pick_channels([epochs.ch_names[82]])  # reduce computation
-
-power, itc = tfr_stockwell(epochs, fmin=6., fmax=30., decim=4, n_jobs=1,
-                           width=.3, return_itc=True)
-
-power.plot([0], baseline=(-0.5, 0), mode=None, title='S-transform (power)')
-
-itc.plot([0], baseline=None, mode=None, title='S-transform (ITC)')
diff --git a/examples/time_frequency/plot_temporal_whitening.py b/examples/time_frequency/plot_temporal_whitening.py
index d6c7cab..550e76c 100644
--- a/examples/time_frequency/plot_temporal_whitening.py
+++ b/examples/time_frequency/plot_temporal_whitening.py
@@ -24,9 +24,9 @@ print(__doc__)
 data_path = sample.data_path()
 
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
-proj_fname = data_path + '/MEG/sample/sample_audvis_ecg_proj.fif'
+proj_fname = data_path + '/MEG/sample/sample_audvis_ecg-proj.fif'
 
-raw = mne.io.Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 proj = mne.read_proj(proj_fname)
 raw.info['projs'] += proj
 raw.info['bads'] = ['MEG 2443', 'EEG 053']  # mark bad channels
diff --git a/examples/time_frequency/plot_time_frequency_multitaper_sensors.py b/examples/time_frequency/plot_time_frequency_multitaper_sensors.py
deleted file mode 100644
index 6fa4774..0000000
--- a/examples/time_frequency/plot_time_frequency_multitaper_sensors.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""
-===============================================
-Time-frequency analysis using multitaper method
-===============================================
-
-This examples computes induced power and intertrial
-coherence (ITC) using a multitaper method on a somato sensory MEG data.
-The power plot is rendered so that baseline is mean zero.
-"""
-# Authors: Hari Bharadwaj <hari at nmr.mgh.harvard.edu>
-#
-# License: BSD (3-clause)
-
-import numpy as np
-
-import mne
-from mne import io
-from mne.time_frequency import tfr_multitaper
-from mne.datasets import somato
-
-print(__doc__)
-
-###############################################################################
-# Load real somatosensory sample data.
-data_path = somato.data_path()
-raw_fname = data_path + '/MEG/somato/sef_raw_sss.fif'
-event_id, tmin, tmax = 1, -1., 3.
-
-# Setup for reading the raw data
-raw = io.Raw(raw_fname)
-baseline = (None, 0)
-events = mne.find_events(raw, stim_channel='STI 014')
-
-# Pick a good channel for somatosensory responses.
-picks = [raw.info['ch_names'].index('MEG 1142'), ]
-
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=baseline, reject=dict(grad=4000e-13))
-
-###############################################################################
-# Calculate power
-
-freqs = np.arange(5., 50., 2.)  # define frequencies of interest
-n_cycles = freqs / 2.  # 0.5 second time windows for all frequencies
-
-# Choose time x (full) bandwidth product
-time_bandwidth = 4.0  # With 0.5 s time windows, this gives 8 Hz smoothing
-
-power, itc = tfr_multitaper(epochs, freqs=freqs, n_cycles=n_cycles,
-                            use_fft=True, time_bandwidth=time_bandwidth,
-                            return_itc=True, n_jobs=1)
-
-# Plot results (with baseline correction only for power)
-power.plot([0], baseline=(-0.5, 0), mode='mean', title='MEG 1142 - Power')
-itc.plot([0], title='MEG 1142 - Intertrial Coherence')
diff --git a/examples/time_frequency/plot_time_frequency_sensors.py b/examples/time_frequency/plot_time_frequency_sensors.py
deleted file mode 100644
index 3e9cf34..0000000
--- a/examples/time_frequency/plot_time_frequency_sensors.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""
-==============================================================
-Time-frequency representations on topographies for MEG sensors
-==============================================================
-
-Both average power and intertrial coherence are displayed.
-"""
-# Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#          Denis Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-import mne
-from mne import io
-from mne.time_frequency import tfr_morlet
-from mne.datasets import somato
-
-print(__doc__)
-
-###############################################################################
-# Set parameters
-data_path = somato.data_path()
-raw_fname = data_path + '/MEG/somato/sef_raw_sss.fif'
-event_id, tmin, tmax = 1, -1., 3.
-
-# Setup for reading the raw data
-raw = io.Raw(raw_fname)
-baseline = (None, 0)
-events = mne.find_events(raw, stim_channel='STI 014')
-
-# picks MEG gradiometers
-picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True, stim=False)
-
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=baseline, reject=dict(grad=4000e-13, eog=350e-6))
-
-###############################################################################
-# Calculate power and intertrial coherence
-
-freqs = np.arange(6, 30, 3)  # define frequencies of interest
-n_cycles = freqs / 2.  # different number of cycle per frequency
-power, itc = tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles, use_fft=True,
-                        return_itc=True, decim=3, n_jobs=1)
-
-# Baseline correction can be applied to power or done in plots
-# To illustrate the baseline correction in plots the next line is commented
-# power.apply_baseline(baseline=(-0.5, 0), mode='logratio')
-
-# Inspect power
-power.plot_topo(baseline=(-0.5, 0), mode='logratio', title='Average power')
-power.plot([82], baseline=(-0.5, 0), mode='logratio')
-
-fig, axis = plt.subplots(1, 2, figsize=(7, 4))
-power.plot_topomap(ch_type='grad', tmin=0.5, tmax=1.5, fmin=8, fmax=12,
-                   baseline=(-0.5, 0), mode='logratio', axes=axis[0],
-                   title='Alpha', vmax=0.45)
-power.plot_topomap(ch_type='grad', tmin=0.5, tmax=1.5, fmin=13, fmax=25,
-                   baseline=(-0.5, 0), mode='logratio', axes=axis[1],
-                   title='Beta', vmax=0.45)
-mne.viz.tight_layout()
-
-# Inspect ITC
-itc.plot_topo(title='Inter-Trial coherence', vmin=0., vmax=1., cmap='Reds')
diff --git a/examples/visualization/plot_channel_epochs_image.py b/examples/visualization/plot_channel_epochs_image.py
index 62f9764..50077ab 100644
--- a/examples/visualization/plot_channel_epochs_image.py
+++ b/examples/visualization/plot_channel_epochs_image.py
@@ -39,7 +39,7 @@ event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 event_id, tmin, tmax = 1, -0.2, 0.5
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 # Set up pick list: EEG + MEG - bad channels (modify to your needs)
diff --git a/examples/visualization/plot_clickable_image.py b/examples/visualization/plot_clickable_image.py
index 411a60c..6ead345 100644
--- a/examples/visualization/plot_clickable_image.py
+++ b/examples/visualization/plot_clickable_image.py
@@ -24,8 +24,8 @@ print(__doc__)
 
 # Set parameters and paths
 plt.rcParams['image.cmap'] = 'gray'
-im_path = op.join(op.dirname(mne.__file__), 'data', 'image', 'mni_brain.gif')
 
+im_path = op.join(op.dirname(mne.__file__), 'data', 'image', 'mni_brain.gif')
 # We've already clicked and exported
 layout_path = op.join(op.dirname(mne.__file__), 'data', 'image')
 layout_name = 'custom_layout.lout'
diff --git a/examples/visualization/plot_evoked_delayed_ssp.py b/examples/visualization/plot_evoked_delayed_ssp.py
deleted file mode 100644
index c746ba9..0000000
--- a/examples/visualization/plot_evoked_delayed_ssp.py
+++ /dev/null
@@ -1,95 +0,0 @@
-"""
-=========================================
-Create evoked objects in delayed SSP mode
-=========================================
-
-This script shows how to apply SSP projectors delayed, that is,
-at the evoked stage. This is particularly useful to support decisions
-related to the trade-off between denoising and preserving signal.
-We first will extract Epochs and create evoked objects
-with the required settings for delayed SSP application.
-Then we will explore the impact of the particular SSP projectors
-on the evoked data.
-
-"""
-# Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#          Denis Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import matplotlib.pyplot as plt
-import mne
-from mne import io
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-
-###############################################################################
-# Set parameters
-raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
-event_id, tmin, tmax = 1, -0.2, 0.5
-
-# Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
-raw.filter(1, 40, method='iir')
-events = mne.read_events(event_fname)
-
-# pick magnetometer channels
-picks = mne.pick_types(raw.info, meg='mag', stim=False, eog=True,
-                       include=[], exclude='bads')
-
-# If we suspend SSP projection at the epochs stage we might reject
-# more epochs than necessary. To deal with this we set proj to `delayed`
-# while passing reject parameters. Each epoch will then be projected before
-# performing peak-to-peak amplitude rejection. If it survives the rejection
-# procedure the unprojected raw epoch will be employed instead.
-# As a consequence, the point in time at which the projection is applied will
-# not have impact on the final results.
-# We will make use of this function to prepare for interactively selecting
-# projections at the evoked stage.
-
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=None, reject=dict(mag=4e-12),
-                    proj='delayed')
-
-evoked = epochs.average()  # average epochs and get an Evoked dataset.
-
-###############################################################################
-# Interactively select / deselect the SSP projection vectors
-
-# Here we expose the details of how to apply SSPs reversibly
-title = 'Incremental SSP application'
-
-# let's first move the proj list to another location
-projs, evoked.info['projs'] = evoked.info['projs'], []
-fig, axes = plt.subplots(2, 2)  # create 4 subplots for our four vectors
-
-# As the bulk of projectors was extracted from the same source, we can simply
-# iterate over our collection of projs and add them step by step to see how
-# the signals change as a function of the SSPs applied. As this operation
-# can't be undone we will operate on copies of the original evoked object to
-# keep things reversible.
-
-for proj, ax in zip(projs, axes.flatten()):
-    evoked.add_proj(proj)  # add projection vectors loop by loop.
-    evoked.copy().apply_proj().plot(axes=ax)  # apply on a copy of evoked
-    ax.set_title('+ %s' % proj['desc'])  # extract description.
-plt.suptitle(title)
-mne.viz.tight_layout()
-
-# We also could have easily visualized the impact of single projection vectors
-# by deleting the vector directly after visualizing the changes.
-# E.g. had we appended the following line to our loop:
-#   `evoked.del_proj(-1)`
-
-# Often, it is desirable to interactively explore data. To make this more
-# convenient we can make use of the 'interactive' option. This will open a
-# check box that allows us to reversibly select projection vectors. Any
-# modification of the selection will immediately cause the figure to update.
-
-evoked.plot(proj='interactive')
-
-# Hint: the same works with evoked.plot_topomap
diff --git a/examples/visualization/plot_evoked_erf_erp.py b/examples/visualization/plot_evoked_erf_erp.py
deleted file mode 100644
index c2020b0..0000000
--- a/examples/visualization/plot_evoked_erf_erp.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""
-=================================
-Plotting ERF/ERP with evoked data
-=================================
-
-Load evoked data and plot.
-
-"""
-# Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import matplotlib.pyplot as plt
-from mne.datasets import sample
-from mne import read_evokeds
-
-print(__doc__)
-
-path = sample.data_path()
-fname = path + '/MEG/sample/sample_audvis-ave.fif'
-
-# load evoked and subtract baseline
-condition = 'Left Auditory'
-evoked = read_evokeds(fname, condition=condition, baseline=(None, 0))
-
-# Plot the evoked response with spatially color coded lines.
-# Note: You can paint the area with left mouse button to show the topographic
-# map of the N100.
-evoked.plot(spatial_colors=True)
-
-###############################################################################
-# Or plot manually after extracting peak latency
-
-evoked = evoked.pick_types(meg=False, eeg=True)
-times = 1e3 * evoked.times  # time in milliseconds
-
-ch_max_name, latency = evoked.get_peak(mode='neg')
-
-plt.figure()
-plt.plot(times, 1e6 * evoked.data.T, 'k-')
-plt.xlim([times[0], times[-1]])
-plt.xlabel('time (ms)')
-plt.ylabel('Potential (uV)')
-plt.title('EEG evoked potential')
-
-plt.axvline(latency * 1e3, color='red',
-            label=ch_max_name, linewidth=2,
-            linestyle='--')
-plt.legend(loc='best')
-
-plt.show()
diff --git a/examples/visualization/plot_evoked_topomap.py b/examples/visualization/plot_evoked_topomap.py
index dc9ece6..2db4ad0 100644
--- a/examples/visualization/plot_evoked_topomap.py
+++ b/examples/visualization/plot_evoked_topomap.py
@@ -39,6 +39,9 @@ evoked.plot_topomap(times, ch_type='mag', average=0.05)
 # plot gradiometer data (plots the RMS for each pair of gradiometers)
 evoked.plot_topomap(times, ch_type='grad')
 
+# plot magnetometer data as an animation
+evoked.animate_topomap(ch_type='mag', times=times, frame_rate=10)
+
 # plot magnetometer data as topomap at 1 time point : 100 ms
 # and add channel labels and title
 evoked.plot_topomap(0.1, ch_type='mag', show_names=True, colorbar=False,
diff --git a/examples/visualization/plot_evoked_topomap_delayed_ssp.py b/examples/visualization/plot_evoked_topomap_delayed_ssp.py
deleted file mode 100644
index b134294..0000000
--- a/examples/visualization/plot_evoked_topomap_delayed_ssp.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""
-===============================================
-Create topographic ERF maps in delayed SSP mode
-===============================================
-
-This script shows how to apply SSP projectors delayed, that is,
-at the evoked stage. This is particularly useful to support decisions
-related to the trade-off between denoising and preserving signal.
-In this example we demonstrate how to use topographic maps for delayed
-SSP application.
-"""
-# Authors: Denis Engemann <denis.engemann at gmail.com>
-#          Christian Brodbeck <christianbrodbeck at nyu.edu>
-#          Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import numpy as np
-import mne
-from mne import io
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-
-###############################################################################
-# Set parameters
-raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
-ecg_fname = data_path + '/MEG/sample/sample_audvis_ecg_proj.fif'
-event_id, tmin, tmax = 1, -0.2, 0.5
-
-# Setup for reading the raw data
-raw = io.Raw(raw_fname)
-events = mne.read_events(event_fname)
-
-# delete EEG projections (we know it's the last one)
-raw.del_proj(-1)
-# add ECG projs for magnetometers
-[raw.add_proj(p) for p in mne.read_proj(ecg_fname) if 'axial' in p['desc']]
-
-# pick magnetometer channels
-picks = mne.pick_types(raw.info, meg='mag', stim=False, eog=True,
-                       include=[], exclude='bads')
-
-# We will make of the proj `delayed` option to
-# interactively select projections at the evoked stage.
-# more information can be found in the example/plot_evoked_delayed_ssp.py
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0), reject=dict(mag=4e-12), proj='delayed')
-
-evoked = epochs.average()  # average epochs and get an Evoked dataset.
-
-###############################################################################
-# Interactively select / deselect the SSP projection vectors
-
-# set time instants in seconds (from 50 to 150ms in a step of 10ms)
-times = np.arange(0.05, 0.15, 0.01)
-
-evoked.plot_topomap(times, proj='interactive')
-# Hint: the same works for evoked.plot and evoked.plot_topo
diff --git a/examples/visualization/plot_evoked_whitening.py b/examples/visualization/plot_evoked_whitening.py
index 9231432..309c485 100644
--- a/examples/visualization/plot_evoked_whitening.py
+++ b/examples/visualization/plot_evoked_whitening.py
@@ -36,7 +36,7 @@ data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(1, 40, method='iir', n_jobs=1)
 raw.info['bads'] += ['MEG 2443']  # bads + 1 more
 events = mne.read_events(event_fname)
diff --git a/examples/visualization/plot_meg_eeg_fields_3d.py b/examples/visualization/plot_meg_eeg_fields_3d.py
deleted file mode 100644
index 61556b5..0000000
--- a/examples/visualization/plot_meg_eeg_fields_3d.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-======================
-Plot M/EEG field lines
-======================
-
-In this example, M/EEG data are remapped onto the
-MEG helmet (MEG) and subject's head surface (EEG).
-This process can be computationally intensive.
-"""
-
-# Authors: Eric Larson <larson.eric.d at gmail.com>
-#          Denis A. Engemann <denis.engemann at gmail.com>
-#          Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-
-# License: BSD (3-clause)
-
-from mne.datasets import sample
-from mne import make_field_map, read_evokeds
-
-print(__doc__)
-
-data_path = sample.data_path()
-subjects_dir = data_path + '/subjects'
-evoked_fname = data_path + '/MEG/sample/sample_audvis-ave.fif'
-trans_fname = data_path + '/MEG/sample/sample_audvis_raw-trans.fif'
-# If trans_fname is set to None then only MEG estimates can be visualized
-
-condition = 'Left Auditory'
-evoked = read_evokeds(evoked_fname, condition=condition, baseline=(-0.2, 0.0))
-
-# Compute the field maps to project MEG and EEG data to MEG helmet
-# and scalp surface
-maps = make_field_map(evoked, trans_fname, subject='sample',
-                      subjects_dir=subjects_dir, n_jobs=1)
-
-# Plot MEG and EEG fields in the helmet and scalp surface in the same figure.
-evoked.plot_field(maps, time=0.11)
-
-# Compute the MEG fields in the scalp surface
-evoked.pick_types(meg=True, eeg=False)
-maps_head = make_field_map(evoked, trans_fname, subject='sample',
-                           subjects_dir=subjects_dir, n_jobs=1,
-                           meg_surf='head')
-
-# Plot MEG fields both in scalp surface and the helmet in the same figure.
-evoked.plot_field([maps_head[0], maps[1]], time=0.11)
diff --git a/examples/visualization/plot_meg_sensors.py b/examples/visualization/plot_meg_sensors.py
new file mode 100644
index 0000000..6d7d976
--- /dev/null
+++ b/examples/visualization/plot_meg_sensors.py
@@ -0,0 +1,42 @@
+"""
+======================================
+Plotting sensor layouts of MEG systems
+======================================
+
+In this example, sensor layouts of different MEG systems
+are shown.
+"""
+# Author: Eric Larson <larson.eric.d at gmail.com>
+#
+# License: BSD (3-clause)
+
+import os.path as op
+
+from mayavi import mlab
+
+import mne
+from mne.io import read_raw_fif, read_raw_ctf, read_raw_bti, read_raw_kit
+from mne.datasets import sample, spm_face
+from mne.viz import plot_trans
+
+print(__doc__)
+
+bti_path = op.abspath(op.dirname(mne.__file__)) + '/io/bti/tests/data/'
+kit_path = op.abspath(op.dirname(mne.__file__)) + '/io/kit/tests/data/'
+raws = dict(
+    Neuromag=read_raw_fif(sample.data_path() +
+                          '/MEG/sample/sample_audvis_raw.fif'),
+    CTF_275=read_raw_ctf(spm_face.data_path() +
+                         '/MEG/spm/SPM_CTF_MEG_example_faces1_3D.ds'),
+    Magnes_3600wh=read_raw_bti(op.join(bti_path, 'test_pdf_linux'),
+                               op.join(bti_path, 'test_config_linux'),
+                               op.join(bti_path, 'test_hs_linux')),
+    KIT=read_raw_kit(op.join(kit_path, 'test.sqd')),
+)
+
+for system, raw in raws.items():
+    # We don't have coil definitions for KIT refs, so exclude them
+    ref_meg = False if system == 'KIT' else True
+    fig = plot_trans(raw.info, trans=None, dig=False, eeg_sensors=False,
+                     meg_sensors=True, coord_frame='meg', ref_meg=ref_meg)
+    mlab.title(system)
diff --git a/examples/visualization/plot_ssp_projs_sensitivity_map.py b/examples/visualization/plot_ssp_projs_sensitivity_map.py
index 8cea492..1c692e3 100644
--- a/examples/visualization/plot_ssp_projs_sensitivity_map.py
+++ b/examples/visualization/plot_ssp_projs_sensitivity_map.py
@@ -21,11 +21,12 @@ data_path = sample.data_path()
 
 subjects_dir = data_path + '/subjects'
 fname = data_path + '/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif'
-ecg_fname = data_path + '/MEG/sample/sample_audvis_ecg_proj.fif'
+ecg_fname = data_path + '/MEG/sample/sample_audvis_ecg-proj.fif'
 
 fwd = read_forward_solution(fname, surf_ori=True)
 projs = read_proj(ecg_fname)
-projs = projs[3:][::2]  # take only one projection per channel type
+# take only one projection per channel type
+projs = projs[::2]
 
 # Compute sensitivity map
 ssp_ecg_map = sensitivity_map(fwd, ch_type='grad', projs=projs, mode='angle')
diff --git a/examples/visualization/plot_ssp_projs_topomaps.py b/examples/visualization/plot_ssp_projs_topomaps.py
deleted file mode 100644
index 0b7c6b2..0000000
--- a/examples/visualization/plot_ssp_projs_topomaps.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-=================================
-Plot SSP projections topographies
-=================================
-
-This example shows how to display topographies of SSP projection vectors.
-The projections used are the ones correcting for ECG artifacts.
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#         Denis A. Engemann <denis.engemann at gmail.com>
-#         Teon Brooks <teon.brooks at gmail.com>
-
-# License: BSD (3-clause)
-
-from mne import read_proj, read_evokeds
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-
-ecg_fname = data_path + '/MEG/sample/sample_audvis_ecg_proj.fif'
-ave_fname = data_path + '/MEG/sample/sample_audvis-ave.fif'
-
-evoked = read_evokeds(ave_fname, condition='Left Auditory')
-projs = read_proj(ecg_fname)
-evoked.add_proj(projs)
-
-evoked.plot_projs_topomap()
diff --git a/examples/visualization/plot_topo_channel_epochs_image.py b/examples/visualization/plot_topo_channel_epochs_image.py
deleted file mode 100644
index d1d2d97..0000000
--- a/examples/visualization/plot_topo_channel_epochs_image.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""
-============================================================
-Visualize channel over epochs as images in sensor topography
-============================================================
-
-This will produce what is sometimes called event related
-potential / field (ERP/ERF) images.
-
-One sensor topography plot is produced with the evoked field images from
-the selected channels.
-"""
-# Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#          Denis Engemann <denis.engemann at gmail.com>
-#
-# License: BSD (3-clause)
-
-import matplotlib.pyplot as plt
-
-import mne
-from mne import io
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-
-###############################################################################
-# Set parameters
-raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
-event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
-event_id, tmin, tmax = 1, -0.2, 0.5
-
-# Setup for reading the raw data
-raw = io.Raw(raw_fname)
-events = mne.read_events(event_fname)
-
-# Set up pick list: EEG + MEG - bad channels (modify to your needs)
-raw.info['bads'] = ['MEG 2443', 'EEG 053']
-picks = mne.pick_types(raw.info, meg='grad', eeg=False, stim=True, eog=True,
-                       exclude='bads')
-
-# Read epochs
-epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
-                    picks=picks, baseline=(None, 0), preload=True,
-                    reject=dict(grad=4000e-13, eog=150e-6))
-
-###############################################################################
-# Show event related fields images
-
-layout = mne.find_layout(epochs.info, 'meg')  # use full layout
-
-title = 'ERF images - MNE sample data'
-mne.viz.plot_topo_image_epochs(epochs, layout, sigma=0.5, vmin=-200, vmax=200,
-                               colorbar=True, title=title)
-plt.show()
diff --git a/examples/visualization/plot_topo_compare_conditions.py b/examples/visualization/plot_topo_compare_conditions.py
index b163dba..cd94977 100644
--- a/examples/visualization/plot_topo_compare_conditions.py
+++ b/examples/visualization/plot_topo_compare_conditions.py
@@ -20,7 +20,6 @@ evoked responses.
 import matplotlib.pyplot as plt
 import mne
 
-from mne.io import Raw
 from mne.viz import plot_evoked_topo
 from mne.datasets import sample
 
@@ -37,7 +36,7 @@ tmin = -0.2
 tmax = 0.5
 
 #   Setup for reading the raw data
-raw = Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 #   Set up pick list: MEG + STI 014 - bad channels (modify to your needs)
diff --git a/examples/visualization/plot_topo_customized.py b/examples/visualization/plot_topo_customized.py
index 44bda9a..8fdc8f6 100644
--- a/examples/visualization/plot_topo_customized.py
+++ b/examples/visualization/plot_topo_customized.py
@@ -21,7 +21,7 @@ import matplotlib.pyplot as plt
 import mne
 from mne.viz import iter_topography
 from mne import io
-from mne.time_frequency import compute_raw_psd
+from mne.time_frequency import psd_welch
 from mne.datasets import sample
 
 print(__doc__)
@@ -29,15 +29,15 @@ print(__doc__)
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 
-raw = io.Raw(raw_fname, preload=True)
+raw = io.read_raw_fif(raw_fname, preload=True)
 raw.filter(1, 20)
 
 picks = mne.pick_types(raw.info, meg=True, exclude=[])
 tmin, tmax = 0, 120  # use the first 120s of data
 fmin, fmax = 2, 20  # look at frequencies between 2 and 20Hz
 n_fft = 2048  # the FFT size (n_fft). Ideally a power of 2
-psds, freqs = compute_raw_psd(raw, picks=picks, tmin=tmin, tmax=tmax,
-                              fmin=fmin, fmax=fmax)
+psds, freqs = psd_welch(raw, picks=picks, tmin=tmin, tmax=tmax,
+                        fmin=fmin, fmax=fmax)
 psds = 20 * np.log10(psds)  # scale to dB
 
 
diff --git a/examples/visualization/plot_topography.py b/examples/visualization/plot_topography.py
deleted file mode 100644
index 828baee..0000000
--- a/examples/visualization/plot_topography.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""
-=================================
-Plot topographies for MEG sensors
-=================================
-
-"""
-# Author: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#
-# License: BSD (3-clause)
-
-import matplotlib.pyplot as plt
-
-from mne import read_evokeds
-from mne.viz import plot_evoked_topo
-from mne.datasets import sample
-
-print(__doc__)
-
-data_path = sample.data_path()
-
-fname = data_path + '/MEG/sample/sample_audvis-ave.fif'
-
-# Reading
-condition = 'Left Auditory'
-evoked = read_evokeds(fname, condition=condition, baseline=(None, 0))
-
-###############################################################################
-# Show topography
-title = 'MNE sample data (condition : %s)' % evoked.comment
-plot_evoked_topo(evoked, title=title)
-plt.show()
diff --git a/mne/__init__.py b/mne/__init__.py
index 98b02dd..aba4d28 100644
--- a/mne/__init__.py
+++ b/mne/__init__.py
@@ -17,19 +17,20 @@
 # Dev branch marker is: 'X.Y.devN' where N is an integer.
 #
 
-__version__ = '0.11.dev0'
+__version__ = '0.12.0'
 
 # have to import verbose first since it's needed by many things
 from .utils import (set_log_level, set_log_file, verbose, set_config,
                     get_config, get_config_path, set_cache_dir,
-                    set_memmap_min_size, grand_average)
+                    set_memmap_min_size, grand_average, sys_info)
 from .io.pick import (pick_types, pick_channels,
                       pick_channels_regexp, pick_channels_forward,
                       pick_types_forward, pick_channels_cov,
                       pick_channels_evoked, pick_info)
 from .io.base import concatenate_raws
 from .chpi import get_chpi_positions
-from .io.meas_info import create_info
+from .io.meas_info import create_info, Info
+from .io.proj import Projection
 from .io.kit import read_epochs_kit
 from .io.eeglab import read_epochs_eeglab
 from .bem import (make_sphere_model, make_bem_model, make_bem_solution,
@@ -43,7 +44,8 @@ from .event import (read_events, write_events, find_events, merge_events,
 from .forward import (read_forward_solution, apply_forward, apply_forward_raw,
                       do_forward_solution, average_forward_solutions,
                       write_forward_solution, make_forward_solution,
-                      convert_forward_solution, make_field_map)
+                      convert_forward_solution, make_field_map,
+                      make_forward_dipole)
 from .source_estimate import (read_source_estimate, MixedSourceEstimate,
                               SourceEstimate, VolSourceEstimate, morph_data,
                               morph_data_precomputed, compute_morph_matrix,
@@ -63,7 +65,8 @@ from .source_space import (read_source_spaces, vertex_to_mni,
                            setup_volume_source_space, SourceSpaces,
                            add_source_space_distances, morph_source_spaces,
                            get_volume_labels_from_aseg)
-from .epochs import Epochs, EpochsArray, read_epochs
+from .annotations import Annotations
+from .epochs import Epochs, EpochsArray, read_epochs, concatenate_epochs
 from .evoked import Evoked, EvokedArray, read_evokeds, write_evokeds, combine_evoked
 from .label import (read_label, label_sign_flip,
                     write_label, stc_to_label, grow_labels, Label, split_label,
@@ -76,8 +79,9 @@ from .transforms import (read_trans, write_trans,
 from .proj import (read_proj, write_proj, compute_proj_epochs,
                    compute_proj_evoked, compute_proj_raw, sensitivity_map)
 from .selection import read_selection
-from .dipole import read_dipole, Dipole, fit_dipole
+from .dipole import read_dipole, Dipole, DipoleFixed, fit_dipole
 from .channels import equalize_channels, rename_channels, find_layout
+from .report import Report
 
 from . import beamformer
 from . import channels
diff --git a/mne/annotations.py b/mne/annotations.py
new file mode 100644
index 0000000..33df574
--- /dev/null
+++ b/mne/annotations.py
@@ -0,0 +1,113 @@
+# Authors: Jaakko Leppakangas <jaeilepp at student.jyu.fi>
+#
+# License: BSD (3-clause)
+
+from datetime import datetime
+import time
+
+import numpy as np
+
+from .externals.six import string_types
+
+
+class Annotations(object):
+    """Annotation object for annotating segments of raw data.
+
+    Parameters
+    ----------
+    onset : array of float, shape (n_annotations,)
+        Annotation time onsets from the beginning of the recording.
+    duration : array of float, shape (n_annotations,)
+        Durations of the annotations.
+    description : array of str, shape (n_annotations,) | str
+        Array of strings containing description for each annotation. If a
+        string, all the annotations are given the same description.
+    orig_time : float | int | instance of datetime | array of int | None
+        A POSIX Timestamp, datetime or an array containing the timestamp as the
+        first element and microseconds as the second element. Determines the
+        starting time of annotation acquisition. If None (default),
+        starting time is determined from beginning of raw data acquisition.
+        In general, ``raw.info['meas_date']`` (or None) can be used for syncing
+        the annotations with raw data if their acquisiton is started at the
+        same time.
+
+    Notes
+    -----
+    Annotations are synced to sample 0. ``raw.first_samp`` is taken
+    into account in the same way as with events.
+    """
+
+    def __init__(self, onset, duration, description, orig_time=None):
+
+        if orig_time is not None:
+            if isinstance(orig_time, datetime):
+                orig_time = float(time.mktime(orig_time.timetuple()))
+            elif not np.isscalar(orig_time):
+                orig_time = orig_time[0] + orig_time[1] / 1000000.
+            else:  # isscalar
+                orig_time = float(orig_time)  # np.int not serializable
+        self.orig_time = orig_time
+
+        onset = np.array(onset)
+        if onset.ndim != 1:
+            raise ValueError('Onset must be a one dimensional array.')
+        duration = np.array(duration)
+        if isinstance(description, string_types):
+            description = np.repeat(description, len(onset))
+        if duration.ndim != 1:
+            raise ValueError('Duration must be a one dimensional array.')
+        if not (len(onset) == len(duration) == len(description)):
+            raise ValueError('Onset, duration and description must be '
+                             'equal in sizes.')
+        if any([';' in desc for desc in description]):
+            raise ValueError('Semicolons in descriptions not supported.')
+
+        self.onset = onset
+        self.duration = duration
+        self.description = np.array(description)
+
+
+def _combine_annotations(annotations, last_samps, first_samps, sfreq):
+    """Helper for combining a tuple of annotations."""
+    if not any(annotations):
+        return None
+    elif annotations[1] is None:
+        return annotations[0]
+    elif annotations[0] is None:
+        old_onset = list()
+        old_duration = list()
+        old_description = list()
+        old_orig_time = None
+    else:
+        old_onset = annotations[0].onset
+        old_duration = annotations[0].duration
+        old_description = annotations[0].description
+        old_orig_time = annotations[0].orig_time
+
+    if annotations[1].orig_time is None:
+        onset = (annotations[1].onset +
+                 (sum(last_samps[:-1]) - sum(first_samps[:-1])) / sfreq)
+    else:
+        onset = annotations[1].onset
+    onset = np.concatenate([old_onset, onset])
+    duration = np.concatenate([old_duration, annotations[1].duration])
+    description = np.concatenate([old_description, annotations[1].description])
+    return Annotations(onset, duration, description, old_orig_time)
+
+
+def _onset_to_seconds(raw, onset):
+    """Helper function for adjusting annotation onsets in relation to raw data.
+    """
+    meas_date = raw.info['meas_date']
+    if meas_date is None:
+        meas_date = 0
+    elif not np.isscalar(meas_date):
+        meas_date = meas_date[0] + meas_date[1] / 1000000.
+    if raw.annotations.orig_time is None:
+        orig_time = meas_date
+    else:
+        orig_time = raw.annotations.orig_time
+
+    annot_start = (orig_time - meas_date + onset -
+                   raw.first_samp / raw.info['sfreq'])
+    return annot_start
diff --git a/mne/baseline.py b/mne/baseline.py
index 7436587..320b822 100644
--- a/mne/baseline.py
+++ b/mne/baseline.py
@@ -10,8 +10,21 @@ import numpy as np
 from .utils import logger, verbose
 
 
+def _log_rescale(baseline, mode='mean'):
+    """Helper to log the rescaling method"""
+    if baseline is not None:
+        valid_modes = ('logratio', 'ratio', 'zscore', 'mean', 'percent',
+                       'zlogratio')
+        if mode not in valid_modes:
+            raise Exception('mode should be any of : %s' % (valid_modes, ))
+        msg = 'Applying baseline correction (mode: %s)' % mode
+    else:
+        msg = 'No baseline correction applied'
+    logger.info(msg)
+
+
 @verbose
-def rescale(data, times, baseline, mode, verbose=None, copy=True):
+def rescale(data, times, baseline, mode='mean', copy=True, verbose=None):
     """Rescale aka baseline correct data
 
     Parameters
@@ -36,62 +49,54 @@ def rescale(data, times, baseline, mode, verbose=None, copy=True):
         power = [power - mean(power_baseline)] / std(power_baseline)).
         logratio is the same an mean but in log-scale, zlogratio is the
         same as zscore but data is rendered in log-scale first.
+    copy : bool
+        Whether to return a new instance or modify in place.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
-    copy : bool
-        Operate on a copy of the data, or in place.
 
     Returns
     -------
     data_scaled: array
         Array of same shape as data after rescaling.
     """
-    if copy:
-        data = data.copy()
+    data = data.copy() if copy else data
+    _log_rescale(baseline, mode)
+    if baseline is None:
+        return data
 
-    valid_modes = ('logratio', 'ratio', 'zscore', 'mean', 'percent',
-                   'zlogratio')
-    if mode not in valid_modes:
-        raise Exception('mode should be any of : %s' % (valid_modes, ))
-
-    if baseline is not None:
-        logger.info("Applying baseline correction ... (mode: %s)" % mode)
-        bmin, bmax = baseline
-        if bmin is None:
-            imin = 0
-        else:
-            imin = int(np.where(times >= bmin)[0][0])
-        if bmax is None:
-            imax = len(times)
-        else:
-            imax = int(np.where(times <= bmax)[0][-1]) + 1
-
-        # avoid potential "empty slice" warning
-        if data.shape[-1] > 0:
-            mean = np.mean(data[..., imin:imax], axis=-1)[..., None]
-        else:
-            mean = 0  # otherwise we get an ugly nan
-        if mode == 'mean':
-            data -= mean
-        if mode == 'logratio':
-            data /= mean
-            data = np.log10(data)  # a value of 1 means 10 times bigger
-        if mode == 'ratio':
-            data /= mean
-        elif mode == 'zscore':
-            std = np.std(data[..., imin:imax], axis=-1)[..., None]
-            data -= mean
-            data /= std
-        elif mode == 'percent':
-            data -= mean
-            data /= mean
-        elif mode == 'zlogratio':
-            data /= mean
-            data = np.log10(data)
-            std = np.std(data[..., imin:imax], axis=-1)[..., None]
-            data /= std
+    bmin, bmax = baseline
+    if bmin is None:
+        imin = 0
+    else:
+        imin = int(np.where(times >= bmin)[0][0])
+    if bmax is None:
+        imax = len(times)
+    else:
+        imax = int(np.where(times <= bmax)[0][-1]) + 1
 
+    # avoid potential "empty slice" warning
+    if data.shape[-1] > 0:
+        mean = np.mean(data[..., imin:imax], axis=-1)[..., None]
     else:
-        logger.info("No baseline correction applied...")
+        mean = 0  # otherwise we get an ugly nan
+    if mode == 'mean':
+        data -= mean
+    if mode == 'logratio':
+        data /= mean
+        data = np.log10(data)  # a value of 1 means 10 times bigger
+    if mode == 'ratio':
+        data /= mean
+    elif mode == 'zscore':
+        std = np.std(data[..., imin:imax], axis=-1)[..., None]
+        data -= mean
+        data /= std
+    elif mode == 'percent':
+        data -= mean
+        data /= mean
+    elif mode == 'zlogratio':
+        data /= mean
+        data = np.log10(data)
+        std = np.std(data[..., imin:imax], axis=-1)[..., None]
+        data /= std
 
     return data
diff --git a/mne/beamformer/_dics.py b/mne/beamformer/_dics.py
index 3f50e32..a7299d3 100644
--- a/mne/beamformer/_dics.py
+++ b/mne/beamformer/_dics.py
@@ -5,13 +5,12 @@
 #
 # License: BSD (3-clause)
 
-import warnings
 from copy import deepcopy
 
 import numpy as np
 from scipy import linalg
 
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from ..forward import _subject_from_forward
 from ..minimum_norm.inverse import combine_xyz, _check_reference
 from ..source_estimate import _make_stc
@@ -352,9 +351,8 @@ def dics_source_power(info, forward, noise_csds, data_csds, reg=0.01,
         for i in range(len(frequencies) - 1):
             fstep.append(frequencies[i + 1] - frequencies[i])
         if not np.allclose(fstep, np.mean(fstep), 1e-5):
-            warnings.warn('Uneven frequency spacing in CSD object, '
-                          'frequencies in the resulting stc file will be '
-                          'inaccurate.')
+            warn('Uneven frequency spacing in CSD object, frequencies in the '
+                 'resulting stc file will be inaccurate.')
         fstep = fstep[0]
     elif len(frequencies) > 1:
         fstep = frequencies[1] - frequencies[0]
@@ -549,7 +547,7 @@ def tf_dics(epochs, forward, noise_csds, tmin, tmax, tstep, win_lengths,
             # be calculated for an additional time window
             if i_time == n_time_steps - 1 and win_tmax - tstep < tmax and\
                win_tmax >= tmax + (epochs.times[-1] - epochs.times[-2]):
-                warnings.warn('Adding a time window to cover last time points')
+                warn('Adding a time window to cover last time points')
                 win_tmin = tmax - win_length
                 win_tmax = tmax
 
diff --git a/mne/beamformer/_lcmv.py b/mne/beamformer/_lcmv.py
index 4e2b2fe..75a9b02 100644
--- a/mne/beamformer/_lcmv.py
+++ b/mne/beamformer/_lcmv.py
@@ -6,8 +6,6 @@
 #
 # License: BSD (3-clause)
 
-import warnings
-
 import numpy as np
 from scipy import linalg
 
@@ -20,7 +18,7 @@ from ..minimum_norm.inverse import _get_vertno, combine_xyz, _check_reference
 from ..cov import compute_whitener, compute_covariance
 from ..source_estimate import _make_stc, SourceEstimate
 from ..source_space import label_src_vertno_sel
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from .. import Epochs
 from ..externals import six
 
@@ -98,9 +96,8 @@ def _apply_lcmv(data, info, tmin, forward, noise_cov, data_cov, reg,
     stc : SourceEstimate | VolSourceEstimate (or list of thereof)
         Source time courses.
     """
-    is_free_ori, ch_names, proj, vertno, G = (
-        _prepare_beamformer_input(
-            info, forward, label, picks, pick_ori))
+    is_free_ori, ch_names, proj, vertno, G = \
+        _prepare_beamformer_input(info, forward, label, picks, pick_ori)
 
     # Handle whitening + data covariance
     whitener, _ = compute_whitener(noise_cov, info, picks, rank=rank)
@@ -240,8 +237,13 @@ def _prepare_beamformer_input(info, forward, label, picks, pick_ori):
                          'is used.')
 
     # Restrict forward solution to selected channels
-    ch_names = [info['chs'][k]['ch_name'] for k in picks]
-    forward = pick_channels_forward(forward, include=ch_names)
+    info_ch_names = [c['ch_name'] for c in info['chs']]
+    ch_names = [info_ch_names[k] for k in picks]
+    fwd_ch_names = forward['sol']['row_names']
+    # Keep channels in forward present in info:
+    fwd_ch_names = [c for c in fwd_ch_names if c in info_ch_names]
+    forward = pick_channels_forward(forward, fwd_ch_names)
+    picks_forward = [fwd_ch_names.index(c) for c in ch_names]
 
     # Get gain matrix (forward operator)
     if label is not None:
@@ -258,10 +260,15 @@ def _prepare_beamformer_input(info, forward, label, picks, pick_ori):
         G = forward['sol']['data']
 
     # Apply SSPs
-    proj, ncomp, _ = make_projector(info['projs'], ch_names)
+    proj, ncomp, _ = make_projector(info['projs'], fwd_ch_names)
+
     if info['projs']:
         G = np.dot(proj, G)
 
+    # Pick after applying the projections
+    G = G[picks_forward]
+    proj = proj[np.ix_(picks_forward, picks_forward)]
+
     return is_free_ori, ch_names, proj, vertno, G
 
 
@@ -417,7 +424,6 @@ def lcmv_epochs(epochs, forward, noise_cov, data_cov, reg=0.01, label=None,
     picks = _setup_picks(picks, info, forward, noise_cov)
 
     data = epochs.get_data()[:, picks, :]
-
     stcs = _apply_lcmv(
         data=data, info=info, tmin=tmin, forward=forward, noise_cov=noise_cov,
         data_cov=data_cov, reg=reg, label=label, picks=picks, rank=rank,
@@ -733,7 +739,7 @@ def tf_lcmv(epochs, forward, noise_covs, tmin, tmax, tstep, win_lengths,
     raw_picks = [raw.ch_names.index(c) for c in ch_names]
 
     # Make sure epochs.events contains only good events:
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
 
     # Multiplying by 1e3 to avoid numerical issues, e.g. 0.3 // 0.05 == 5
     n_time_steps = int(((tmax - tmin) * 1e3) // (tstep * 1e3))
@@ -767,7 +773,7 @@ def tf_lcmv(epochs, forward, noise_covs, tmin, tmax, tstep, win_lengths,
             # be calculated for an additional time window
             if i_time == n_time_steps - 1 and win_tmax - tstep < tmax and\
                win_tmax >= tmax + (epochs.times[-1] - epochs.times[-2]):
-                warnings.warn('Adding a time window to cover last time points')
+                warn('Adding a time window to cover last time points')
                 win_tmin = tmax - win_length
                 win_tmax = tmax
 
diff --git a/mne/beamformer/tests/test_dics.py b/mne/beamformer/tests/test_dics.py
index b5f48d7..81faec3 100644
--- a/mne/beamformer/tests/test_dics.py
+++ b/mne/beamformer/tests/test_dics.py
@@ -43,7 +43,8 @@ def _get_data(tmin=-0.11, tmax=0.15, read_all_forward=True, compute_csds=True):
     """
     label = mne.read_label(fname_label)
     events = mne.read_events(fname_event)[:10]
-    raw = mne.io.Raw(fname_raw, preload=False)
+    raw = mne.io.read_raw_fif(fname_raw, preload=False)
+    raw.add_proj([], remove_existing=True)  # we'll subselect so remove proj
     forward = mne.read_forward_solution(fname_fwd)
     if read_all_forward:
         forward_surf_ori = read_forward_solution_meg(fname_fwd, surf_ori=True)
@@ -143,7 +144,7 @@ def test_dics():
     assert_array_equal(stcs[0].data, advance_iterator(stcs_).data)
 
     # Test whether correct number of trials was returned
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
     assert_true(len(epochs.events) == len(stcs))
 
     # Average the single trial estimates
diff --git a/mne/beamformer/tests/test_lcmv.py b/mne/beamformer/tests/test_lcmv.py
index d92c60a..ab05444 100644
--- a/mne/beamformer/tests/test_lcmv.py
+++ b/mne/beamformer/tests/test_lcmv.py
@@ -40,7 +40,7 @@ def _get_data(tmin=-0.1, tmax=0.15, all_forward=True, epochs=True,
     """
     label = mne.read_label(fname_label)
     events = mne.read_events(fname_event)
-    raw = mne.io.Raw(fname_raw, preload=True)
+    raw = mne.io.read_raw_fif(fname_raw, preload=True)
     forward = mne.read_forward_solution(fname_fwd)
     if all_forward:
         forward_surf_ori = read_forward_solution_meg(fname_fwd, surf_ori=True)
@@ -55,20 +55,21 @@ def _get_data(tmin=-0.1, tmax=0.15, all_forward=True, epochs=True,
     event_id, tmin, tmax = 1, tmin, tmax
 
     # Setup for reading the raw data
-    raw.info['bads'] = ['MEG 2443', 'EEG 053']  # 2 bads channels
+    raw.info['bads'] = ['MEG 2443', 'EEG 053']  # 2 bad channels
+    # Set up pick list: MEG - bad channels
+    left_temporal_channels = mne.read_selection('Left-temporal')
+    picks = mne.pick_types(raw.info, meg=True, eeg=False, stim=True,
+                           eog=True, ref_meg=False, exclude='bads',
+                           selection=left_temporal_channels)
+    raw.pick_channels([raw.ch_names[ii] for ii in picks])
+    raw.info.normalize_proj()  # avoid projection warnings
 
     if epochs:
-        # Set up pick list: MEG - bad channels
-        left_temporal_channels = mne.read_selection('Left-temporal')
-        picks = mne.pick_types(raw.info, meg=True, eeg=False, stim=True,
-                               eog=True, ref_meg=False, exclude='bads',
-                               selection=left_temporal_channels)
-
         # Read epochs
-        epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
-                            picks=picks, baseline=(None, 0),
-                            preload=epochs_preload,
-                            reject=dict(grad=4000e-13, mag=4e-12, eog=150e-6))
+        epochs = mne.Epochs(
+            raw, events, event_id, tmin, tmax, proj=True,
+            baseline=(None, 0), preload=epochs_preload,
+            reject=dict(grad=4000e-13, mag=4e-12, eog=150e-6))
         if epochs_preload:
             epochs.resample(200, npad=0, n_jobs=2)
         evoked = epochs.average()
@@ -79,11 +80,12 @@ def _get_data(tmin=-0.1, tmax=0.15, all_forward=True, epochs=True,
         info = raw.info
 
     noise_cov = mne.read_cov(fname_cov)
-    noise_cov = mne.cov.regularize(noise_cov, info, mag=0.05, grad=0.05,
-                                   eeg=0.1, proj=True)
+    with warnings.catch_warnings(record=True):  # bad proj
+        noise_cov = mne.cov.regularize(noise_cov, info, mag=0.05, grad=0.05,
+                                       eeg=0.1, proj=True)
     if data_cov:
-        with warnings.catch_warnings(record=True):
-            data_cov = mne.compute_covariance(epochs, tmin=0.04, tmax=0.15)
+        with warnings.catch_warnings(record=True):  # too few samples
+            data_cov = mne.compute_covariance(epochs, tmin=0.04, tmax=0.145)
     else:
         data_cov = None
 
@@ -113,8 +115,8 @@ def test_lcmv():
 
         if fwd is forward:
             # Test picking normal orientation (surface source space only)
-            stc_normal = lcmv(evoked, forward_surf_ori, noise_cov, data_cov,
-                              reg=0.01, pick_ori="normal")
+            stc_normal = lcmv(evoked, forward_surf_ori, noise_cov,
+                              data_cov, reg=0.01, pick_ori="normal")
             stc_normal.crop(0.02, None)
 
             stc_pow = np.sum(np.abs(stc_normal.data), axis=1)
@@ -164,12 +166,13 @@ def test_lcmv():
 
     # Now test single trial using fixed orientation forward solution
     # so we can compare it to the evoked solution
-    stcs = lcmv_epochs(epochs, forward_fixed, noise_cov, data_cov, reg=0.01)
-    stcs_ = lcmv_epochs(epochs, forward_fixed, noise_cov, data_cov, reg=0.01,
-                        return_generator=True)
+    stcs = lcmv_epochs(epochs, forward_fixed, noise_cov, data_cov,
+                       reg=0.01)
+    stcs_ = lcmv_epochs(epochs, forward_fixed, noise_cov, data_cov,
+                        reg=0.01, return_generator=True)
     assert_array_equal(stcs[0].data, advance_iterator(stcs_).data)
 
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
     assert_true(len(epochs.events) == len(stcs))
 
     # average the single trial estimates
@@ -201,14 +204,9 @@ def test_lcmv_raw():
     start, stop = raw.time_as_index([tmin, tmax])
 
     # use only the left-temporal MEG channels for LCMV
-    left_temporal_channels = mne.read_selection('Left-temporal')
-    picks = mne.pick_types(raw.info, meg=True, exclude='bads',
-                           selection=left_temporal_channels)
-
     data_cov = mne.compute_raw_covariance(raw, tmin=tmin, tmax=tmax)
-
-    stc = lcmv_raw(raw, forward, noise_cov, data_cov, reg=0.01, label=label,
-                   start=start, stop=stop, picks=picks)
+    stc = lcmv_raw(raw, forward, noise_cov, data_cov, reg=0.01,
+                   label=label, start=start, stop=stop)
 
     assert_array_almost_equal(np.array([tmin, tmax]),
                               np.array([stc.times[0], stc.times[-1]]),
@@ -238,8 +236,9 @@ def test_lcmv_source_power():
     assert_true(0.4 < max_source_power < 2.4, max_source_power)
 
     # Test picking normal orientation and using a list of CSD matrices
-    stc_normal = _lcmv_source_power(epochs.info, forward_surf_ori, noise_cov,
-                                    data_cov, pick_ori="normal", label=label)
+    stc_normal = _lcmv_source_power(
+        epochs.info, forward_surf_ori, noise_cov, data_cov,
+        pick_ori="normal", label=label)
 
     # The normal orientation results should always be smaller than free
     # orientation results
@@ -268,7 +267,7 @@ def test_tf_lcmv():
     """
     label = mne.read_label(fname_label)
     events = mne.read_events(fname_event)
-    raw = mne.io.Raw(fname_raw, preload=True)
+    raw = mne.io.read_raw_fif(fname_raw, preload=True)
     forward = mne.read_forward_solution(fname_fwd)
 
     event_id, tmin, tmax = 1, -0.2, 0.2
@@ -281,12 +280,15 @@ def test_tf_lcmv():
     picks = mne.pick_types(raw.info, meg=True, eeg=False,
                            stim=True, eog=True, exclude='bads',
                            selection=left_temporal_channels)
+    raw.pick_channels([raw.ch_names[ii] for ii in picks])
+    raw.info.normalize_proj()  # avoid projection warnings
+    del picks
 
     # Read epochs
     epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
-                        picks=picks, baseline=None, preload=False,
+                        baseline=None, preload=False,
                         reject=dict(grad=4000e-13, mag=4e-12, eog=150e-6))
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
 
     freq_bins = [(4, 12), (15, 40)]
     time_windows = [(-0.1, 0.1), (0.0, 0.2)]
@@ -298,15 +300,16 @@ def test_tf_lcmv():
     noise_covs = []
     for (l_freq, h_freq), win_length in zip(freq_bins, win_lengths):
         raw_band = raw.copy()
-        raw_band.filter(l_freq, h_freq, method='iir', n_jobs=1, picks=picks)
-        epochs_band = mne.Epochs(raw_band, epochs.events, epochs.event_id,
-                                 tmin=tmin, tmax=tmax, baseline=None,
-                                 proj=True, picks=picks)
+        raw_band.filter(l_freq, h_freq, method='iir', n_jobs=1)
+        epochs_band = mne.Epochs(
+            raw_band, epochs.events, epochs.event_id, tmin=tmin, tmax=tmax,
+            baseline=None, proj=True)
         with warnings.catch_warnings(record=True):  # not enough samples
             noise_cov = compute_covariance(epochs_band, tmin=tmin, tmax=tmin +
                                            win_length)
-        noise_cov = mne.cov.regularize(noise_cov, epochs_band.info, mag=reg,
-                                       grad=reg, eeg=reg, proj=True)
+        noise_cov = mne.cov.regularize(
+            noise_cov, epochs_band.info, mag=reg, grad=reg, eeg=reg,
+            proj=True)
         noise_covs.append(noise_cov)
         del raw_band  # to save memory
 
@@ -314,13 +317,14 @@ def test_tf_lcmv():
         # time windows to compare to tf_lcmv results and test overlapping
         if (l_freq, h_freq) == freq_bins[0]:
             for time_window in time_windows:
-                with warnings.catch_warnings(record=True):
+                with warnings.catch_warnings(record=True):  # bad samples
                     data_cov = compute_covariance(epochs_band,
                                                   tmin=time_window[0],
                                                   tmax=time_window[1])
-                stc_source_power = _lcmv_source_power(epochs.info, forward,
-                                                      noise_cov, data_cov,
-                                                      reg=reg, label=label)
+                with warnings.catch_warnings(record=True):  # bad proj
+                    stc_source_power = _lcmv_source_power(
+                        epochs.info, forward, noise_cov, data_cov,
+                        reg=reg, label=label)
                 source_power.append(stc_source_power.data)
 
     with warnings.catch_warnings(record=True):
@@ -361,6 +365,7 @@ def test_tf_lcmv():
     # the underlying raw object
     epochs_preloaded = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
                                   baseline=(None, 0), preload=True)
+    epochs_preloaded._raw = None
     with warnings.catch_warnings(record=True):  # not enough samples
         assert_raises(ValueError, tf_lcmv, epochs_preloaded, forward,
                       noise_covs, tmin, tmax, tstep, win_lengths, freq_bins)
diff --git a/mne/bem.py b/mne/bem.py
index c403ed0..5a7a760 100644
--- a/mne/bem.py
+++ b/mne/bem.py
@@ -15,8 +15,9 @@ import numpy as np
 from scipy import linalg
 
 from .fixes import partial
-from .utils import verbose, logger, run_subprocess, get_subjects_dir
+from .utils import verbose, logger, run_subprocess, get_subjects_dir, warn
 from .transforms import _ensure_trans, apply_trans
+from .io import Info
 from .io.constants import FIFF
 from .io.write import (start_file, start_block, write_float, write_int,
                        write_float_matrix, write_int_matrix, end_block,
@@ -499,7 +500,8 @@ def make_bem_model(subject, ico=4, conductivity=(0.3, 0.006, 0.3),
     """Create a BEM model for a subject
 
     .. note:: To get a single layer bem corresponding to the --homog flag in
-              the command line tool set the ``connectivity`` accordingly
+              the command line tool set the ``conductivity`` parameter
+              to a list/tuple with a single value (e.g. [0.3]).
 
     Parameters
     ----------
@@ -705,7 +707,7 @@ def make_sphere_model(r0=(0., 0., 0.04), head_radius=0.09, info=None,
         If float, compute spherical shells for EEG using the given radius.
         If 'auto', estimate an approriate radius from the dig points in Info,
         If None, exclude shells.
-    info : instance of mne.io.meas_info.Info | None
+    info : instance of Info | None
         Measurement info. Only needed if ``r0`` or ``head_radius`` are
         ``'auto'``.
     relative_radii : array-like
@@ -735,16 +737,24 @@ def make_sphere_model(r0=(0., 0., 0.04), head_radius=0.09, info=None,
             if param != 'auto':
                 raise ValueError('%s, if str, must be "auto" not "%s"'
                                  % (name, param))
-
+    relative_radii = np.array(relative_radii, float).ravel()
+    sigmas = np.array(sigmas, float).ravel()
+    if len(relative_radii) != len(sigmas):
+        raise ValueError('relative_radii length (%s) must match that of '
+                         'sigmas (%s)' % (len(relative_radii),
+                                          len(sigmas)))
+    if len(sigmas) == 0 and head_radius is not None:
+            raise ValueError('sigmas must be supplied if head_radius is not '
+                             'None')
     if (isinstance(r0, string_types) and r0 == 'auto') or \
        (isinstance(head_radius, string_types) and head_radius == 'auto'):
         if info is None:
             raise ValueError('Info must not be None for auto mode')
-        head_radius_fit, r0_fit = fit_sphere_to_headshape(info)[:2]
+        head_radius_fit, r0_fit = fit_sphere_to_headshape(info, units='m')[:2]
         if isinstance(r0, string_types):
-            r0 = r0_fit / 1000.
+            r0 = r0_fit
         if isinstance(head_radius, string_types):
-            head_radius = head_radius_fit / 1000.
+            head_radius = head_radius_fit
     sphere = ConductorModel(is_sphere=True, r0=np.array(r0),
                             coord_frame=FIFF.FIFFV_COORD_HEAD)
     sphere['layers'] = list()
@@ -785,49 +795,89 @@ def make_sphere_model(r0=(0., 0., 0.04), head_radius=0.09, info=None,
                            sphere['lambda'][k]))
         logger.info('Set up EEG sphere model with scalp radius %7.1f mm\n'
                     % (1000 * head_radius,))
-    return ConductorModel(sphere)
+    return sphere
 
 
 # #############################################################################
 # Helpers
 
+_dig_kind_dict = {
+    'cardinal': FIFF.FIFFV_POINT_CARDINAL,
+    'hpi': FIFF.FIFFV_POINT_HPI,
+    'eeg': FIFF.FIFFV_POINT_EEG,
+    'extra': FIFF.FIFFV_POINT_EXTRA,
+}
+_dig_kind_rev = dict((val, key) for key, val in _dig_kind_dict.items())
+_dig_kind_ints = tuple(_dig_kind_dict.values())
+
+
 @verbose
-def fit_sphere_to_headshape(info, dig_kinds=(FIFF.FIFFV_POINT_EXTRA,),
-                            verbose=None):
+def fit_sphere_to_headshape(info, dig_kinds='auto', units=None, verbose=None):
     """Fit a sphere to the headshape points to determine head center
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         Measurement info.
-    dig_kinds : tuple of int
-        Kind of digitization points to use in the fitting. These can be
-        any kind defined in io.constants.FIFF::
+    dig_kinds : list of str | str
+        Kind of digitization points to use in the fitting. These can be any
+        combination of ('cardinal', 'hpi', 'eeg', 'extra'). Can also
+        be 'auto' (default), which will use only the 'extra' points if
+        enough are available, and if not, uses 'extra' and 'eeg' points.
+    units : str
+        Can be "m" or "mm". The default in 0.12 is "mm" but will be changed
+        to "m" in 0.13.
 
-            FIFFV_POINT_CARDINAL
-            FIFFV_POINT_HPI
-            FIFFV_POINT_EEG
-            FIFFV_POINT_EXTRA
+        .. versionadded:: 0.12
 
-        Defaults to (FIFFV_POINT_EXTRA,).
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
     Returns
     -------
     radius : float
-        Sphere radius in mm.
+        Sphere radius.
     origin_head: ndarray, shape (3,)
-        Head center in head coordinates (mm).
+        Head center in head coordinates.
     origin_device: ndarray, shape (3,)
-        Head center in device coordinates (mm).
+        Head center in device coordinates.
 
     Notes
     -----
     This function excludes any points that are low and frontal
     (``z < 0 and y > 0``) to improve the fit.
     """
-    # get head digization points of the specified kind
+    if units is None:
+        warn('Please explicitly set the units. In 0.12 units="mm" will '
+             'be used, but this will change to units="m" in 0.13.',
+             DeprecationWarning)
+        units = 'mm'
+    if not isinstance(units, string_types) or units not in ('m', 'mm'):
+        raise ValueError('units must be a "m" or "mm"')
+    if not isinstance(info, Info):
+        raise TypeError('info must be an instance of Info not %s' % type(info))
+    if info['dig'] is None:
+        raise RuntimeError('Cannot fit headshape without digitization '
+                           ', info["dig"] is None')
+    if isinstance(dig_kinds, string_types):
+        if dig_kinds == 'auto':
+            # try "extra" first
+            try:
+                return fit_sphere_to_headshape(info, 'extra', units=units)
+            except ValueError:
+                pass
+            return fit_sphere_to_headshape(info, ('extra', 'eeg'), units=units)
+        else:
+            dig_kinds = (dig_kinds,)
+    # convert string args to ints (first make dig_kinds mutable in case tuple)
+    dig_kinds = list(dig_kinds)
+    for di, d in enumerate(dig_kinds):
+        dig_kinds[di] = _dig_kind_dict.get(d, d)
+        if dig_kinds[di] not in _dig_kind_ints:
+            raise ValueError('dig_kinds[#%d] (%s) must be one of %s'
+                             % (di, d, sorted(list(_dig_kind_dict.keys()))))
+
+    # get head digization points of the specified kind(s)
     hsp = [p['r'] for p in info['dig'] if p['kind'] in dig_kinds]
     if any(p['coord_frame'] != FIFF.FIFFV_COORD_HEAD for p in info['dig']):
         raise RuntimeError('Digitization points not in head coordinates, '
@@ -836,9 +886,15 @@ def fit_sphere_to_headshape(info, dig_kinds=(FIFF.FIFFV_POINT_EXTRA,),
     # exclude some frontal points (nose etc.)
     hsp = [p for p in hsp if not (p[2] < 0 and p[1] > 0)]
 
-    if len(hsp) == 0:
-        raise ValueError('No head digitization points of the specified '
-                         'kinds (%s) found.' % dig_kinds)
+    if len(hsp) <= 10:
+        kinds_str = ', '.join(['"%s"' % _dig_kind_rev[d]
+                               for d in sorted(dig_kinds)])
+        msg = ('Only %s head digitization points of the specified kind%s (%s,)'
+               % (len(hsp), 's' if len(dig_kinds) != 1 else '', kinds_str))
+        if len(hsp) < 4:
+            raise ValueError(msg + ', at least 4 required')
+        else:
+            warn(msg + ', fitting may be inaccurate')
 
     radius, origin_head = _fit_sphere(np.array(hsp), disp=False)
     # compute origin in device coordinates
@@ -853,16 +909,20 @@ def fit_sphere_to_headshape(info, dig_kinds=(FIFF.FIFFV_POINT_EXTRA,),
     # i.e. 108mm "radius", so let's go with 110mm
     # en.wikipedia.org/wiki/Human_head#/media/File:HeadAnthropometry.JPG
     if radius > 110.:
-        logger.warning('Estimated head size (%0.1f mm) exceeded 99th '
-                       'percentile for adult head size' % (radius,))
+        warn('Estimated head size (%0.1f mm) exceeded 99th '
+             'percentile for adult head size' % (radius,))
     # > 2 cm away from head center in X or Y is strange
     if np.sqrt(np.sum(origin_head[:2] ** 2)) > 20:
-        logger.warning('(X, Y) fit (%0.1f, %0.1f) more than 20 mm from '
-                       'head frame origin' % tuple(origin_head[:2]))
+        warn('(X, Y) fit (%0.1f, %0.1f) more than 20 mm from '
+             'head frame origin' % tuple(origin_head[:2]))
     logger.info('Origin head coordinates:'.ljust(30) +
                 '%0.1f %0.1f %0.1f mm' % tuple(origin_head))
     logger.info('Origin device coordinates:'.ljust(30) +
                 '%0.1f %0.1f %0.1f mm' % tuple(origin_device))
+    if units == 'm':
+        radius /= 1e3
+        origin_head /= 1e3
+        origin_device /= 1e3
 
     return radius, origin_head, origin_device
 
@@ -903,10 +963,10 @@ def _check_origin(origin, info, coord_frame='head', disp=False):
             raise ValueError('origin must be a numerical array, or "auto", '
                              'not %s' % (origin,))
         if coord_frame == 'head':
-            R, origin = fit_sphere_to_headshape(info, verbose=False)[:2]
-            origin /= 1000.
+            R, origin = fit_sphere_to_headshape(info, verbose=False,
+                                                units='m')[:2]
             logger.info('    Automatic origin fit: head of radius %0.1f mm'
-                        % R)
+                        % (R * 1000.,))
             del R
         else:
             origin = (0., 0., 0.)
@@ -926,7 +986,7 @@ def _check_origin(origin, info, coord_frame='head', disp=False):
 @verbose
 def make_watershed_bem(subject, subjects_dir=None, overwrite=False,
                        volume='T1', atlas=False, gcaatlas=False, preflood=None,
-                       verbose=None):
+                       show=False, verbose=None):
     """
     Create BEM surfaces using the watershed algorithm included with FreeSurfer
 
@@ -947,26 +1007,31 @@ def make_watershed_bem(subject, subjects_dir=None, overwrite=False,
         Use the subcortical atlas
     preflood : int
         Change the preflood height
+    show : bool
+        Show surfaces to visually inspect all three BEM surfaces (recommended).
+
+        .. versionadded:: 0.12
+
     verbose : bool, str or None
         If not None, override default verbose level
 
+    Notes
+    -----
     .. versionadded:: 0.10
     """
     from .surface import read_surface
+    from .viz.misc import plot_bem
     env, mri_dir = _prepare_env(subject, subjects_dir,
                                 requires_freesurfer=True,
                                 requires_mne=True)[:2]
 
+    subjects_dir = env['SUBJECTS_DIR']
     subject_dir = op.join(subjects_dir, subject)
     mri_dir = op.join(subject_dir, 'mri')
     T1_dir = op.join(mri_dir, volume)
     T1_mgz = op.join(mri_dir, volume + '.mgz')
     bem_dir = op.join(subject_dir, 'bem')
     ws_dir = op.join(subject_dir, 'bem', 'watershed')
-
-    if not op.isdir(subject_dir):
-        raise RuntimeError('Could not find the MRI data directory "%s"'
-                           % subject_dir)
     if not op.isdir(bem_dir):
         os.makedirs(bem_dir)
     if not op.isdir(T1_dir) and not op.isfile(T1_mgz):
@@ -974,13 +1039,13 @@ def make_watershed_bem(subject, subjects_dir=None, overwrite=False,
     if op.isdir(ws_dir):
         if not overwrite:
             raise RuntimeError('%s already exists. Use the --overwrite option'
-                               'to recreate it.' % ws_dir)
+                               ' to recreate it.' % ws_dir)
         else:
             shutil.rmtree(ws_dir)
     # put together the command
     cmd = ['mri_watershed']
     if preflood:
-        cmd += ["-h",  "%s" % int(preflood)]
+        cmd += ["-h", "%s" % int(preflood)]
 
     if gcaatlas:
         cmd += ['-atlas', '-T1', '-brain_atlas', env['FREESURFER_HOME'] +
@@ -1002,20 +1067,42 @@ def make_watershed_bem(subject, subjects_dir=None, overwrite=False,
                 'Results dir = %s\n' % (subjects_dir, subject, ws_dir))
     os.makedirs(op.join(ws_dir, 'ws'))
     run_subprocess(cmd, env=env, stdout=sys.stdout, stderr=sys.stderr)
-    #
-    os.chdir(ws_dir)
+
     if op.isfile(T1_mgz):
         # XXX : do this with python code
-        surfaces = [subject + '_brain_surface', subject +
-                    '_inner_skull_surface', subject + '_outer_skull_surface',
-                    subject + '_outer_skin_surface']
-        for s in surfaces:
-            cmd = ['mne_convert_surface', '--surf', s, '--mghmri', T1_mgz,
-                   '--surfout', s, "--replacegeom"]
+        surfs = ['brain', 'inner_skull', 'outer_skull', 'outer_skin']
+        for s in surfs:
+            surf_ws_out = op.join(ws_dir, '%s_%s_surface' % (subject, s))
+            cmd = ['mne_convert_surface', '--surf', surf_ws_out, '--mghmri',
+                   T1_mgz, '--surfout', s, "--replacegeom"]
             run_subprocess(cmd, env=env, stdout=sys.stdout, stderr=sys.stderr)
-    os.chdir(bem_dir)
-    if op.isfile(subject + '-head.fif'):
-        os.remove(subject + '-head.fif')
+
+            # Create symbolic links
+            surf_out = op.join(bem_dir, '%s.surf' % s)
+            if not overwrite and op.exists(surf_out):
+                skip_symlink = True
+            else:
+                if op.exists(surf_out):
+                    os.remove(surf_out)
+                os.symlink(surf_ws_out, surf_out)
+                skip_symlink = False
+
+        if skip_symlink:
+            logger.info("Unable to create all symbolic links to .surf files "
+                        "in bem folder. Use --overwrite option to recreate "
+                        "them.")
+            dest = op.join(bem_dir, 'watershed')
+        else:
+            logger.info("Symbolic links to .surf files created in bem folder")
+            dest = bem_dir
+
+    logger.info("\nThank you for waiting.\nThe BEM triangulations for this "
+                "subject are now available at:\n%s." % dest)
+
+    # Write a head file for coregistration
+    fname_head = op.join(bem_dir, subject + '-head.fif')
+    if op.isfile(fname_head):
+        os.remove(fname_head)
 
     # run the equivalent of mne_surf2bem
     points, tris = read_surface(op.join(ws_dir,
@@ -1023,9 +1110,14 @@ def make_watershed_bem(subject, subjects_dir=None, overwrite=False,
     points *= 1e-3
     surf = dict(coord_frame=5, id=4, nn=None, np=len(points),
                 ntri=len(tris), rr=points, sigma=1, tris=tris)
-    write_bem_surfaces(subject + '-head.fif', surf)
+    write_bem_surfaces(fname_head, surf)
 
-    logger.info('Created %s/%s-head.fif\n\nComplete.' % (bem_dir, subject))
+    # Show computed BEM surfaces
+    if show:
+        plot_bem(subject=subject, subjects_dir=subjects_dir,
+                 orientation='coronal', slices=None, show=True)
+
+    logger.info('Created %s\n\nComplete.' % (fname_head,))
 
 
 # ############################################################################
@@ -1394,11 +1486,17 @@ def _prepare_env(subject, subjects_dir, requires_freesurfer, requires_mne):
         raise TypeError('The subject argument must be set')
 
     subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)
-
+    if not op.isdir(subjects_dir):
+        raise RuntimeError('Could not find the MRI data directory "%s"'
+                           % subjects_dir)
+    subject_dir = op.join(subjects_dir, subject)
+    if not op.isdir(subject_dir):
+        raise RuntimeError('Could not find the subject data directory "%s"'
+                           % (subject_dir,))
     env['SUBJECT'] = subject
     env['SUBJECTS_DIR'] = subjects_dir
-    mri_dir = op.join(subjects_dir, subject, 'mri')
-    bem_dir = op.join(subjects_dir, subject, 'bem')
+    mri_dir = op.join(subject_dir, 'mri')
+    bem_dir = op.join(subject_dir, 'bem')
     return env, mri_dir, bem_dir
 
 
@@ -1584,6 +1682,7 @@ def make_flash_bem(subject, overwrite=False, show=True, subjects_dir=None,
                                          requires_mne=True)
 
     curdir = os.getcwd()
+    subjects_dir = env['SUBJECTS_DIR']
 
     logger.info('\nProcessing the flash MRI data to produce BEM meshes with '
                 'the following parameters:\n'
diff --git a/mne/channels/channels.py b/mne/channels/channels.py
index 0c70bb7..7970047 100644
--- a/mne/channels/channels.py
+++ b/mne/channels/channels.py
@@ -8,16 +8,15 @@
 
 import os
 import os.path as op
-import warnings
 
 import numpy as np
 from scipy import sparse
 
 from ..externals.six import string_types
 
-from ..utils import verbose, logger
+from ..utils import verbose, logger, warn, _check_copy_dep
 from ..io.pick import (channel_type, pick_info, pick_types,
-                       _check_excludes_includes)
+                       _check_excludes_includes, _PICK_TYPES_KEYS)
 from ..io.constants import FIFF
 
 
@@ -56,7 +55,7 @@ def _contains_ch_type(info, ch_type):
 
     Parameters
     ---------
-    info : instance of mne.io.Info
+    info : instance of Info
         The measurement information.
     ch_type : str
         the channel type to be checked for
@@ -70,10 +69,9 @@ def _contains_ch_type(info, ch_type):
         raise ValueError('`ch_type` is of class {actual_class}. It must be '
                          '`str`'.format(actual_class=type(ch_type)))
 
-    valid_channel_types = ['grad', 'mag', 'planar1', 'planar2', 'eeg', 'stim',
-                           'eog', 'emg', 'ecg', 'ref_meg', 'resp', 'exci',
-                           'ias', 'syst', 'seeg', 'misc']
-
+    meg_extras = ['mag', 'grad', 'planar1', 'planar2']
+    valid_channel_types = sorted([key for key in _PICK_TYPES_KEYS
+                                  if key != 'meg'] + meg_extras)
     if ch_type not in valid_channel_types:
         raise ValueError('ch_type must be one of %s, not "%s"'
                          % (valid_channel_types, ch_type))
@@ -107,8 +105,8 @@ def equalize_channels(candidates, verbose=None):
     ----------
     candidates : list
         list Raw | Epochs | Evoked | AverageTFR
-    verbose : None | bool
-        whether to be verbose or not.
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
 
     Notes
     -----
@@ -154,7 +152,7 @@ class ContainsMixin(object):
             has_ch_type = _contains_ch_type(self.info, ch_type)
         return has_ch_type
 
-
+# XXX Eventually de-duplicate with _kind_dict of mne/io/meas_info.py
 _human2fiff = {'ecg': FIFF.FIFFV_ECG_CH,
                'eeg': FIFF.FIFFV_EEG_CH,
                'emg': FIFF.FIFFV_EMG_CH,
@@ -165,7 +163,9 @@ _human2fiff = {'ecg': FIFF.FIFFV_ECG_CH,
                'resp': FIFF.FIFFV_RESP_CH,
                'seeg': FIFF.FIFFV_SEEG_CH,
                'stim': FIFF.FIFFV_STIM_CH,
-               'syst': FIFF.FIFFV_SYST_CH}
+               'syst': FIFF.FIFFV_SYST_CH,
+               'bio': FIFF.FIFFV_BIO_CH,
+               'ecog': FIFF.FIFFV_ECOG_CH}
 _human2unit = {'ecg': FIFF.FIFF_UNIT_V,
                'eeg': FIFF.FIFF_UNIT_V,
                'emg': FIFF.FIFF_UNIT_V,
@@ -176,8 +176,12 @@ _human2unit = {'ecg': FIFF.FIFF_UNIT_V,
                'resp': FIFF.FIFF_UNIT_NONE,
                'seeg': FIFF.FIFF_UNIT_V,
                'stim': FIFF.FIFF_UNIT_NONE,
-               'syst': FIFF.FIFF_UNIT_NONE}
+               'syst': FIFF.FIFF_UNIT_NONE,
+               'bio': FIFF.FIFF_UNIT_V,
+               'ecog': FIFF.FIFF_UNIT_V}
 _unit2human = {FIFF.FIFF_UNIT_V: 'V',
+               FIFF.FIFF_UNIT_T: 'T',
+               FIFF.FIFF_UNIT_T_M: 'T/m',
                FIFF.FIFF_UNIT_NONE: 'NA'}
 
 
@@ -254,7 +258,7 @@ class SetChannelsMixin(object):
         """Define the sensor type of channels.
 
         Note: The following sensor types are accepted:
-            ecg, eeg, emg, eog, exci, ias, misc, resp, seeg, stim, syst
+            ecg, eeg, emg, eog, exci, ias, misc, resp, seeg, stim, syst, ecog
 
         Parameters
         ----------
@@ -285,13 +289,15 @@ class SetChannelsMixin(object):
             _check_set(self.info['chs'][c_ind], self.info['projs'], ch_type)
             unit_old = self.info['chs'][c_ind]['unit']
             unit_new = _human2unit[ch_type]
+            if unit_old not in _unit2human:
+                raise ValueError("Channel '%s' has unknown unit (%s). Please "
+                                 "fix the measurement info of your data."
+                                 % (ch_name, unit_old))
             if unit_old != _human2unit[ch_type]:
-                warnings.warn("The unit for Channel %s has changed "
-                              "from %s to %s." % (ch_name,
-                                                  _unit2human[unit_old],
-                                                  _unit2human[unit_new]))
+                warn("The unit for channel %s has changed from %s to %s."
+                     % (ch_name, _unit2human[unit_old], _unit2human[unit_new]))
             self.info['chs'][c_ind]['unit'] = _human2unit[ch_type]
-            if ch_type in ['eeg', 'seeg']:
+            if ch_type in ['eeg', 'seeg', 'ecog']:
                 self.info['chs'][c_ind]['coil_type'] = FIFF.FIFFV_COIL_EEG
             else:
                 self.info['chs'][c_ind]['coil_type'] = FIFF.FIFFV_COIL_NONE
@@ -312,12 +318,16 @@ class SetChannelsMixin(object):
         """
         rename_channels(self.info, mapping)
 
-    def set_montage(self, montage):
+    @verbose
+    def set_montage(self, montage, verbose=None):
         """Set EEG sensor configuration
 
         Parameters
         ----------
         montage : instance of Montage or DigMontage
+            The montage to use.
+        verbose : bool, str, int, or None
+            If not None, override default verbose level (see mne.verbose).
 
         Notes
         -----
@@ -328,6 +338,49 @@ class SetChannelsMixin(object):
         from .montage import _set_montage
         _set_montage(self.info, montage)
 
+    def plot_sensors(self, kind='topomap', ch_type=None, title=None,
+                     show_names=False, show=True):
+        """
+        Plot sensors positions.
+
+        Parameters
+        ----------
+        kind : str
+            Whether to plot the sensors as 3d or as topomap. Available options
+            'topomap', '3d'. Defaults to 'topomap'.
+        ch_type : 'mag' | 'grad' | 'eeg' | 'seeg' | 'ecog' | None
+            The channel type to plot. If None, then channels are chosen in the
+            order given above.
+        title : str | None
+            Title for the figure. If None (default), equals to
+            ``'Sensor positions (%s)' % ch_type``.
+        show_names : bool
+            Whether to display all channel names. Defaults to False.
+        show : bool
+            Show figure if True. Defaults to True.
+
+        Returns
+        -------
+        fig : instance of matplotlib figure
+            Figure containing the sensor topography.
+
+        See Also
+        --------
+        mne.viz.plot_layout
+
+        Notes
+        -----
+        This function plots the sensor locations from the info structure using
+        matplotlib. For drawing the sensors using mayavi see
+        :func:`mne.viz.plot_trans`.
+
+        .. versionadded:: 0.12.0
+
+        """
+        from ..viz.utils import plot_sensors
+        return plot_sensors(self.info, kind=kind, ch_type=ch_type, title=title,
+                            show_names=show_names, show=show)
+
 
 class UpdateChannelsMixin(object):
     """Mixin class for Raw, Evoked, Epochs, AverageTFR
@@ -335,8 +388,8 @@ class UpdateChannelsMixin(object):
     def pick_types(self, meg=True, eeg=False, stim=False, eog=False,
                    ecg=False, emg=False, ref_meg='auto', misc=False,
                    resp=False, chpi=False, exci=False, ias=False, syst=False,
-                   seeg=False, include=[], exclude='bads', selection=None,
-                   copy=False):
+                   seeg=False, bio=False, ecog=False, include=[],
+                   exclude='bads', selection=None, copy=None):
         """Pick some channels by type and names
 
         Parameters
@@ -374,6 +427,10 @@ class UpdateChannelsMixin(object):
             System status channel information (on Triux systems only).
         seeg : bool
             Stereotactic EEG channels.
+        bio : bool
+            Bio channels.
+        ecog : bool
+            Electrocorticography channels.
         include : list of string
             List of additional channels to include. If empty do not include
             any.
@@ -383,23 +440,29 @@ class UpdateChannelsMixin(object):
         selection : list of string
             Restrict sensor channels (MEG, EEG) to this list of channel names.
         copy : bool
-            If True, returns new instance. Else, modifies in place. Defaults to
-            False.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
+
+        Returns
+        -------
+        inst : instance of Raw, Epochs, or Evoked
+            The modified instance.
 
         Notes
         -----
         .. versionadded:: 0.9.0
         """
-        inst = self.copy() if copy else self
+        inst = _check_copy_dep(self, copy)
         idx = pick_types(
             self.info, meg=meg, eeg=eeg, stim=stim, eog=eog, ecg=ecg, emg=emg,
             ref_meg=ref_meg, misc=misc, resp=resp, chpi=chpi, exci=exci,
-            ias=ias, syst=syst, seeg=seeg, include=include, exclude=exclude,
-            selection=selection)
+            ias=ias, syst=syst, seeg=seeg, bio=bio, ecog=ecog, include=include,
+            exclude=exclude, selection=selection)
         inst._pick_drop_channels(idx)
         return inst
 
-    def pick_channels(self, ch_names, copy=False):
+    def pick_channels(self, ch_names, copy=None):
         """Pick some channels
 
         Parameters
@@ -407,8 +470,14 @@ class UpdateChannelsMixin(object):
         ch_names : list
             The list of channels to select.
         copy : bool
-            If True, returns new instance. Else, modifies in place. Defaults to
-            False.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
+
+        Returns
+        -------
+        inst : instance of Raw, Epochs, or Evoked
+            The modified instance.
 
         See Also
         --------
@@ -418,15 +487,13 @@ class UpdateChannelsMixin(object):
         -----
         .. versionadded:: 0.9.0
         """
-        inst = self.copy() if copy else self
+        inst = _check_copy_dep(self, copy)
         _check_excludes_includes(ch_names)
-
         idx = [inst.ch_names.index(c) for c in ch_names if c in inst.ch_names]
         inst._pick_drop_channels(idx)
-
         return inst
 
-    def drop_channels(self, ch_names, copy=False):
+    def drop_channels(self, ch_names, copy=None):
         """Drop some channels
 
         Parameters
@@ -434,8 +501,14 @@ class UpdateChannelsMixin(object):
         ch_names : list
             The list of channels to remove.
         copy : bool
-            If True, returns new instance. Else, modifies in place. Defaults to
-            False.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
+
+        Returns
+        -------
+        inst : instance of Raw, Epochs, or Evoked
+            The modified instance.
 
         See Also
         --------
@@ -445,13 +518,11 @@ class UpdateChannelsMixin(object):
         -----
         .. versionadded:: 0.9.0
         """
-        inst = self.copy() if copy else self
-
+        inst = _check_copy_dep(self, copy)
         bad_idx = [inst.ch_names.index(c) for c in ch_names
                    if c in inst.ch_names]
         idx = np.setdiff1d(np.arange(len(inst.ch_names)), bad_idx)
         inst._pick_drop_channels(idx)
-
         return inst
 
     def _pick_drop_channels(self, idx):
@@ -475,7 +546,7 @@ class UpdateChannelsMixin(object):
         if inst_has('_cals'):
             self._cals = self._cals[idx]
 
-        self.info = pick_info(self.info, idx, copy=False)
+        pick_info(self.info, idx, copy=False)
 
         if inst_has('_projector'):
             self._projector = self._projector[idx][:, idx]
@@ -489,7 +560,7 @@ class UpdateChannelsMixin(object):
         elif isinstance(self, Evoked):
             self.data = self.data.take(idx, axis=0)
 
-    def add_channels(self, add_list, copy=False):
+    def add_channels(self, add_list, copy=None, force_update_info=False):
         """Append new channels to the instance.
 
         Parameters
@@ -498,14 +569,22 @@ class UpdateChannelsMixin(object):
             A list of objects to append to self. Must contain all the same
             type as the current object
         copy : bool
-            Whether to return a new instance or modify in place
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
+        force_update_info : bool
+            If True, force the info for objects to be appended to match the
+            values in `self`. This should generally only be used when adding
+            stim channels for which important metadata won't be overwritten.
+
+            .. versionadded:: 0.12
 
         Returns
         -------
-        out : MNE object of type(self)
-            An object with new channels appended (will be the same
-            object if copy==False)
+        inst : instance of Raw, Epochs, or Evoked
+            The modified instance.
         """
+        out = _check_copy_dep(self, copy)
         # avoid circular imports
         from ..io import _BaseRaw, _merge_info
         from ..epochs import _BaseEpochs
@@ -541,13 +620,9 @@ class UpdateChannelsMixin(object):
         # Create final data / info objects
         data = np.concatenate(data, axis=con_axis)
         infos = [self.info] + [inst.info for inst in add_list]
-        new_info = _merge_info(infos)
+        new_info = _merge_info(infos, force_update_to_first=force_update_info)
 
         # Now update the attributes
-        if copy is True:
-            out = self.copy()
-        else:
-            out = self
         setattr(out, data_name, data)
         out.info = new_info
         if isinstance(self, _BaseRaw):
@@ -576,8 +651,8 @@ class InterpolationMixin(object):
 
         Returns
         -------
-        self : mne.io.Raw, mne.Epochs or mne.Evoked
-            The interpolated data.
+        inst : instance of Raw, Epochs, or Evoked
+            The modified instance.
 
         Notes
         -----
@@ -647,9 +722,9 @@ def rename_channels(info, mapping):
 
     # do the reampping in info
     info['bads'] = bads
-    info['ch_names'] = ch_names
     for ch, ch_name in zip(info['chs'], ch_names):
         ch['ch_name'] = ch_name
+    info._update_redundant()
     info._check_consistency()
 
 
diff --git a/mne/channels/data/layouts/Vectorview-grad_norm.lout b/mne/channels/data/layouts/Vectorview-grad_norm.lout
new file mode 100644
index 0000000..d06ce01
--- /dev/null
+++ b/mne/channels/data/layouts/Vectorview-grad_norm.lout
@@ -0,0 +1,103 @@
+-50.000000 50.000000 -50.000000 38.000000
+11	-41.408840	17.090919	6.000000	5.000000	MEG 011X
+12	-33.873951	19.857674	6.000000	5.000000	MEG 012X
+13	-38.464523	9.051075	6.000000	5.000000	MEG 013X
+14	-45.317917	3.279520	6.000000	5.000000	MEG 014X
+21	-32.233719	8.146864	6.000000	5.000000	MEG 021X
+22	-25.690760	8.433022	6.000000	5.000000	MEG 022X
+23	-27.227139	-1.254610	6.000000	5.000000	MEG 023X
+24	-33.698534	-2.642785	6.000000	5.000000	MEG 024X
+31	-23.067547	24.734621	6.000000	5.000000	MEG 031X
+32	-22.098728	16.737410	6.000000	5.000000	MEG 032X
+33	-16.461800	14.609854	6.000000	5.000000	MEG 033X
+34	-28.464256	17.451874	6.000000	5.000000	MEG 034X
+41	-19.362539	7.376735	6.000000	5.000000	MEG 041X
+42	-12.864409	6.474677	6.000000	5.000000	MEG 042X
+43	-13.325964	-1.183000	6.000000	5.000000	MEG 043X
+44	-20.358908	-0.938589	6.000000	5.000000	MEG 044X
+51	-16.560817	29.103437	6.000000	5.000000	MEG 051X
+52	-9.821842	31.383564	6.000000	5.000000	MEG 052X
+53	-9.336051	25.759117	6.000000	5.000000	MEG 053X
+54	-16.222077	22.789145	6.000000	5.000000	MEG 054X
+61	-9.426766	19.671541	6.000000	5.000000	MEG 061X
+62	-2.982150	13.733236	6.000000	5.000000	MEG 062X
+63	-6.324418	6.882314	6.000000	5.000000	MEG 063X
+64	-9.654012	13.389857	6.000000	5.000000	MEG 064X
+71	-6.407364	-0.212448	6.000000	5.000000	MEG 071X
+72	0.444286	-0.277880	6.000000	5.000000	MEG 072X
+73	0.483912	-6.911695	6.000000	5.000000	MEG 073X
+74	-6.503398	-6.874514	6.000000	5.000000	MEG 074X
+81	-2.979496	32.140564	6.000000	5.000000	MEG 081X
+82	-2.981206	26.486458	6.000000	5.000000	MEG 082X
+91	3.820817	31.402866	6.000000	5.000000	MEG 091X
+92	10.618533	29.086569	6.000000	5.000000	MEG 092X
+93	10.229562	22.803463	6.000000	5.000000	MEG 093X
+94	3.361053	25.786205	6.000000	5.000000	MEG 094X
+101	-2.982047	20.501795	6.000000	5.000000	MEG 101X
+102	3.409646	19.674952	6.000000	5.000000	MEG 102X
+103	3.613043	13.399289	6.000000	5.000000	MEG 103X
+104	0.382112	6.933975	6.000000	5.000000	MEG 104X
+111	6.826344	6.452130	6.000000	5.000000	MEG 111X
+112	13.341015	7.352071	6.000000	5.000000	MEG 112X
+113	14.322306	-1.012468	6.000000	5.000000	MEG 113X
+114	7.299809	-1.115800	6.000000	5.000000	MEG 114X
+121	17.159397	24.712067	6.000000	5.000000	MEG 121X
+122	22.594622	17.362583	6.000000	5.000000	MEG 122X
+123	16.098728	16.737411	6.000000	5.000000	MEG 123X
+124	10.418224	14.626265	6.000000	5.000000	MEG 124X
+131	19.690762	8.433019	6.000000	5.000000	MEG 131X
+132	26.213667	8.075083	6.000000	5.000000	MEG 132X
+133	27.774809	-2.728805	6.000000	5.000000	MEG 133X
+134	21.202684	-1.254627	6.000000	5.000000	MEG 134X
+141	27.929657	19.898018	6.000000	5.000000	MEG 141X
+142	35.246883	17.323858	6.000000	5.000000	MEG 142X
+143	39.239410	3.410470	6.000000	5.000000	MEG 143X
+144	32.390839	8.988529	6.000000	5.000000	MEG 144X
+151	-40.253967	-3.703956	6.000000	5.000000	MEG 151X
+152	-38.062698	-14.995193	6.000000	5.000000	MEG 152X
+153	-40.474266	-23.037640	6.000000	5.000000	MEG 153X
+154	-44.949768	-10.637144	6.000000	5.000000	MEG 154X
+161	-32.408976	-12.215726	6.000000	5.000000	MEG 161X
+162	-26.253698	-10.038419	6.000000	5.000000	MEG 162X
+163	-22.034237	-17.815468	6.000000	5.000000	MEG 163X
+164	-28.014048	-20.868780	6.000000	5.000000	MEG 164X
+171	-32.343294	-33.363060	6.000000	5.000000	MEG 171X
+172	-32.557526	-25.167658	6.000000	5.000000	MEG 172X
+173	-24.219797	-32.925196	6.000000	5.000000	MEG 173X
+174	-21.768074	-40.654018	6.000000	5.000000	MEG 174X
+181	-19.800634	-8.646573	6.000000	5.000000	MEG 181X
+182	-13.191874	-8.019776	6.000000	5.000000	MEG 182X
+183	-6.600061	-13.240516	6.000000	5.000000	MEG 183X
+184	-14.718287	-15.782150	6.000000	5.000000	MEG 184X
+191	-15.472808	-23.418205	6.000000	5.000000	MEG 191X
+192	-12.552808	-31.875578	6.000000	5.000000	MEG 192X
+193	-14.142802	-37.886852	6.000000	5.000000	MEG 193X
+194	-21.129593	-27.560652	6.000000	5.000000	MEG 194X
+201	-7.059234	-19.849951	6.000000	5.000000	MEG 201X
+202	1.013249	-19.839857	6.000000	5.000000	MEG 202X
+203	1.170161	-26.385864	6.000000	5.000000	MEG 203X
+204	-7.170043	-26.360546	6.000000	5.000000	MEG 204X
+211	-3.028555	-33.257917	6.000000	5.000000	MEG 211X
+212	-3.000000	-39.515667	6.000000	5.000000	MEG 212X
+213	3.501040	-44.468269	6.000000	5.000000	MEG 213X
+214	-9.538412	-44.461239	6.000000	5.000000	MEG 214X
+221	7.168070	-7.997848	6.000000	5.000000	MEG 221X
+222	13.792637	-8.592716	6.000000	5.000000	MEG 222X
+223	8.728101	-15.836154	6.000000	5.000000	MEG 223X
+224	0.622745	-13.248796	6.000000	5.000000	MEG 224X
+231	9.465158	-23.429756	6.000000	5.000000	MEG 231X
+232	15.043037	-27.577251	6.000000	5.000000	MEG 232X
+233	8.107240	-37.881119	6.000000	5.000000	MEG 233X
+234	6.452683	-31.889233	6.000000	5.000000	MEG 234X
+241	20.260805	-9.959167	6.000000	5.000000	MEG 241X
+242	26.352144	-12.264672	6.000000	5.000000	MEG 242X
+243	21.924099	-20.924681	6.000000	5.000000	MEG 243X
+244	16.034241	-17.815463	6.000000	5.000000	MEG 244X
+251	18.170528	-32.936850	6.000000	5.000000	MEG 251X
+252	26.548311	-25.126150	6.000000	5.000000	MEG 252X
+253	26.293430	-33.390539	6.000000	5.000000	MEG 253X
+254	15.720093	-40.673553	6.000000	5.000000	MEG 254X
+261	34.165833	-3.701116	6.000000	5.000000	MEG 261X
+262	38.903042	-10.588621	6.000000	5.000000	MEG 262X
+263	34.358242	-23.135988	6.000000	5.000000	MEG 263X
+264	32.029198	-14.983262	6.000000	5.000000	MEG 264X
diff --git a/mne/channels/interpolation.py b/mne/channels/interpolation.py
index d9544fd..bffe644 100644
--- a/mne/channels/interpolation.py
+++ b/mne/channels/interpolation.py
@@ -6,7 +6,7 @@ import numpy as np
 from numpy.polynomial.legendre import legval
 from scipy import linalg
 
-from ..utils import logger
+from ..utils import logger, warn
 from ..io.pick import pick_types, pick_channels, pick_info
 from ..surface import _normalize_vectors
 from ..bem import _fit_sphere
@@ -36,27 +36,6 @@ def _calc_g(cosang, stiffness=4, num_lterms=50):
     return legval(cosang, [0] + factors)
 
 
-def _calc_h(cosang, stiffness=4, num_lterms=50):
-    """Calculate spherical spline h function between points on a sphere.
-
-    Parameters
-    ----------
-    cosang : array-like of float, shape(n_channels, n_channels)
-        cosine of angles between pairs of points on a spherical surface. This
-        is equivalent to the dot product of unit vectors.
-    stiffness : float
-        stiffness of the spline. Also referred to as `m`.
-    num_lterms : int
-        number of Legendre terms to evaluate.
-    H : np.ndrarray of float, shape(n_channels, n_channels)
-        The H matrix.
-    """
-    factors = [(2 * n + 1) /
-               (n ** (stiffness - 1) * (n + 1) ** (stiffness - 1) * 4 * np.pi)
-               for n in range(1, num_lterms + 1)]
-    return legval(cosang, [0] + factors)
-
-
 def _make_interpolation_matrix(pos_from, pos_to, alpha=1e-5):
     """Compute interpolation matrix based on spherical splines
 
@@ -95,13 +74,18 @@ def _make_interpolation_matrix(pos_from, pos_to, alpha=1e-5):
     cosang_from = pos_from.dot(pos_from.T)
     cosang_to_from = pos_to.dot(pos_from.T)
     G_from = _calc_g(cosang_from)
-    G_to_from, H_to_from = (f(cosang_to_from) for f in (_calc_g, _calc_h))
+    G_to_from = _calc_g(cosang_to_from)
 
     if alpha is not None:
         G_from.flat[::len(G_from) + 1] += alpha
 
-    C_inv = linalg.pinv(G_from)
-    interpolation = G_to_from.dot(C_inv)
+    n_channels = G_from.shape[0]  # G_from should be square matrix
+    C = np.r_[np.c_[G_from, np.ones((n_channels, 1))],
+              np.c_[np.ones((1, n_channels)), 0]]
+    C_inv = linalg.pinv(C)
+
+    interpolation = np.c_[G_to_from,
+                          np.ones((G_to_from.shape[0], 1))].dot(C_inv[:, :-1])
     return interpolation
 
 
@@ -161,8 +145,8 @@ def _interpolate_bads_eeg(inst):
     distance = np.sqrt(np.sum((pos_good - center) ** 2, 1))
     distance = np.mean(distance / radius)
     if np.abs(1. - distance) > 0.1:
-        logger.warning('Your spherical fit is poor, interpolation results are '
-                       'likely to be inaccurate.')
+        warn('Your spherical fit is poor, interpolation results are '
+             'likely to be inaccurate.')
 
     logger.info('Computing interpolation matrix from {0} sensor '
                 'positions'.format(len(pos_good)))
@@ -201,7 +185,7 @@ def _interpolate_bads_meg(inst, mode='accurate', verbose=None):
     # return without doing anything if there are no meg channels
     if len(picks_meg) == 0 or len(picks_bad) == 0:
         return
-    info_from = pick_info(inst.info, picks_good, copy=True)
-    info_to = pick_info(inst.info, picks_bad, copy=True)
+    info_from = pick_info(inst.info, picks_good)
+    info_to = pick_info(inst.info, picks_bad)
     mapping = _map_meg_channels(info_from, info_to, mode=mode)
     _do_interp_dots(inst, mapping, picks_good, picks_bad)
diff --git a/mne/channels/layout.py b/mne/channels/layout.py
index 318bd95..911add1 100644
--- a/mne/channels/layout.py
+++ b/mne/channels/layout.py
@@ -16,8 +16,10 @@ import os.path as op
 import numpy as np
 
 from ..transforms import _polar_to_cartesian, _cartesian_to_sphere
+from ..bem import fit_sphere_to_headshape
 from ..io.pick import pick_types
 from ..io.constants import FIFF
+from ..io.meas_info import Info
 from ..utils import _clean_names
 from ..externals.six.moves import map
 
@@ -84,6 +86,28 @@ class Layout(object):
         return '<Layout | %s - Channels: %s ...>' % (self.kind,
                                                      ', '.join(self.names[:3]))
 
+    def plot(self, show=True):
+        """Plot the sensor positions.
+
+        Parameters
+        ----------
+        show : bool
+            Show figure if True. Defaults to True.
+
+        Returns
+        -------
+        fig : instance of matplotlib figure
+            Figure containing the sensor topography.
+
+        Notes
+        -----
+
+        .. versionadded:: 0.12.0
+
+        """
+        from ..viz.topomap import plot_layout
+        return plot_layout(self, show=show)
+
 
 def _read_lout(fname):
     """Aux function"""
@@ -156,7 +180,6 @@ def read_layout(kind, path=None, scale=True):
     """
     if path is None:
         path = op.join(op.dirname(__file__), 'data', 'layouts')
-
     if not kind.endswith('.lout') and op.exists(op.join(path, kind + '.lout')):
         kind += '.lout'
     elif not kind.endswith('.lay') and op.exists(op.join(path, kind + '.lay')):
@@ -192,7 +215,7 @@ def make_eeg_layout(info, radius=0.5, width=None, height=None, exclude='bads'):
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         Measurement info (e.g., raw.info).
     radius : float
         Viewport radius as a fraction of main figure height. Defaults to 0.5.
@@ -267,7 +290,7 @@ def make_grid_layout(info, picks=None, n_col=None):
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info | None
+    info : instance of Info | None
         Measurement info (e.g., raw.info). If None, default names will be
         employed.
     picks : array-like of int | None
@@ -306,7 +329,7 @@ def make_grid_layout(info, picks=None, n_col=None):
         if n_col * n_row < size:  # jump to the next full square
             n_row += 1
     else:
-        n_row = np.ceil(size / float(n_col))
+        n_row = int(np.ceil(size / float(n_col)))
 
     # setup position grid
     x, y = np.meshgrid(np.linspace(-0.5, 0.5, n_col),
@@ -342,7 +365,7 @@ def find_layout(info, ch_type=None, exclude='bads'):
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info.
     ch_type : {'mag', 'grad', 'meg', 'eeg'} | None
         The channel type for selecting single channel layouts.
@@ -408,10 +431,13 @@ def find_layout(info, ch_type=None, exclude='bads'):
     elif has_vv_only_mag or (has_vv_meg and ch_type == 'mag'):
         layout_name = 'Vectorview-mag'
     elif has_vv_only_grad or (has_vv_meg and ch_type == 'grad'):
-        layout_name = 'Vectorview-grad'
+        if info['ch_names'][0].endswith('X'):
+            layout_name = 'Vectorview-grad_norm'
+        else:
+            layout_name = 'Vectorview-grad'
     elif ((has_eeg_coils_only and ch_type in [None, 'eeg']) or
           (has_eeg_coils_and_meg and ch_type == 'eeg')):
-        if not isinstance(info, dict):
+        if not isinstance(info, (dict, Info)):
             raise RuntimeError('Cannot make EEG layout, no measurement info '
                                'was passed to `find_layout`')
         return make_eeg_layout(info, exclude=exclude)
@@ -419,9 +445,9 @@ def find_layout(info, ch_type=None, exclude='bads'):
         layout_name = 'magnesWH3600'
     elif has_CTF_grad:
         layout_name = 'CTF-275'
-    elif n_kit_grads == 157:
+    elif n_kit_grads <= 157:
         layout_name = 'KIT-157'
-    elif n_kit_grads == 208:
+    elif n_kit_grads > 157:
         layout_name = 'KIT-AD'
     else:
         return None
@@ -528,7 +554,7 @@ def _find_topomap_coords(info, picks, layout=None):
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         Measurement info.
     picks : list of int
         Channel indices to generate topomap coords for.
@@ -555,7 +581,7 @@ def _find_topomap_coords(info, picks, layout=None):
     return pos
 
 
-def _auto_topomap_coords(info, picks):
+def _auto_topomap_coords(info, picks, ignore_overlap=False):
     """Make a 2 dimensional sensor map from sensor positions in an info dict.
     The default is to use the electrode locations. The fallback option is to
     attempt using digitization points of kind FIFFV_POINT_EEG. This only works
@@ -563,7 +589,7 @@ def _auto_topomap_coords(info, picks):
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info.
     picks : list of int
         The channel indices to generate topomap coords for.
@@ -620,9 +646,7 @@ def _auto_topomap_coords(info, picks):
         dig_kinds = (FIFF.FIFFV_POINT_CARDINAL,
                      FIFF.FIFFV_POINT_EEG,
                      FIFF.FIFFV_POINT_EXTRA)
-        from ..preprocessing.maxfilter import fit_sphere_to_headshape
-        _, origin_head, _ = fit_sphere_to_headshape(info, dig_kinds)
-        origin_head /= 1000.  # to meters
+        _, origin_head, _ = fit_sphere_to_headshape(info, dig_kinds, units='m')
         locs3d -= origin_head
 
         # Match the digitization points with the requested
@@ -632,9 +656,9 @@ def _auto_topomap_coords(info, picks):
 
     # Duplicate points cause all kinds of trouble during visualization
     dist = pdist(locs3d)
-    if np.min(dist) < 1e-10:
+    if np.min(dist) < 1e-10 and not ignore_overlap:
         problematic_electrodes = [
-            info['ch_names'][elec_i]
+            chs[elec_i]['ch_name']
             for elec_i in squareform(dist < 1e-10).any(axis=0).nonzero()[0]
         ]
 
@@ -648,12 +672,45 @@ def _auto_topomap_coords(info, picks):
     return locs2d
 
 
+def _topo_to_sphere(pos, eegs):
+    """Helper function for transforming xy-coordinates to sphere.
+
+    Parameters
+    ----------
+    pos : array-like, shape (n_channels, 2)
+        xy-oordinates to transform.
+    eegs : list of int
+        Indices of eeg channels that are included when calculating the sphere.
+
+    Returns
+    -------
+    coords : array, shape (n_channels, 3)
+        xyz-coordinates.
+    """
+    xs, ys = np.array(pos).T
+
+    sqs = np.max(np.sqrt((xs[eegs] ** 2) + (ys[eegs] ** 2)))
+    xs /= sqs  # Shape to a sphere and normalize
+    ys /= sqs
+
+    xs += 0.5 - np.mean(xs[eegs])  # Center the points
+    ys += 0.5 - np.mean(ys[eegs])
+
+    xs = xs * 2. - 1.  # Values ranging from -1 to 1
+    ys = ys * 2. - 1.
+
+    rs = np.clip(np.sqrt(xs ** 2 + ys ** 2), 0., 1.)
+    alphas = np.arccos(rs)
+    zs = np.sin(alphas)
+    return np.column_stack([xs, ys, zs])
+
+
 def _pair_grad_sensors(info, layout=None, topomap_coords=True, exclude='bads'):
     """Find the picks for pairing grad channels
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         An info dictionary containing channel information.
     layout : Layout | None
         The layout if available. Defaults to None.
diff --git a/mne/channels/montage.py b/mne/channels/montage.py
index 67bed71..849b714 100644
--- a/mne/channels/montage.py
+++ b/mne/channels/montage.py
@@ -11,17 +11,20 @@
 
 import os
 import os.path as op
-import warnings
 
 import numpy as np
 
 from ..viz import plot_montage
 from .channels import _contains_ch_type
 from ..transforms import (_sphere_to_cartesian, apply_trans,
-                          get_ras_to_neuromag_trans, _topo_to_sphere)
-from ..io.meas_info import _make_dig_points, _read_dig_points
+                          get_ras_to_neuromag_trans, _topo_to_sphere,
+                          _str_to_frame, _frame_to_str)
+from ..io.meas_info import _make_dig_points, _read_dig_points, _read_dig_fif
 from ..io.pick import pick_types
+from ..io.open import fiff_open
 from ..io.constants import FIFF
+from ..utils import _check_fname, warn
+
 from ..externals.six import string_types
 from ..externals.six.moves import map
 
@@ -78,11 +81,11 @@ class Montage(object):
 
 
 def read_montage(kind, ch_names=None, path=None, unit='m', transform=False):
-    """Read montage from a file
+    """Read a generic (built-in) montage from a file
 
     This function can be used to read electrode positions from a user specified
     file using the `kind` and `path` parameters. Alternatively, use only the
-    `kind` parameter to load one of the build-in montages:
+    `kind` parameter to load one of the built-in montages:
 
     ===================   =====================================================
     Kind                  description
@@ -152,6 +155,10 @@ def read_montage(kind, ch_names=None, path=None, unit='m', transform=False):
     montage : instance of Montage
         The montage.
 
+    See Also
+    --------
+    read_dig_montage : To read subject-specific digitization information.
+
     Notes
     -----
     Built-in montages are not scaled or transformed by default.
@@ -262,7 +269,8 @@ def read_montage(kind, ch_names=None, path=None, unit='m', transform=False):
         pos = np.vstack((data['x'], data['y'], data['z'])).T
         ch_names_ = data['name'].astype(np.str)
     elif ext in ('.loc', '.locs', '.eloc'):
-        ch_names_ = np.loadtxt(fname, dtype='S4', usecols=[3]).tolist()
+        ch_names_ = np.loadtxt(fname, dtype='S4',
+                               usecols=[3]).astype(np.str).tolist()
         dtype = {'names': ('angle', 'radius'), 'formats': ('f4', 'f4')}
         angle, radius = np.loadtxt(fname, dtype=dtype, usecols=[1, 2],
                                    unpack=True)
@@ -346,13 +354,18 @@ class DigMontage(object):
         The position of the right periauricular fidicual point.
     dev_head_t : array, shape (4, 4)
         A Device-to-Head transformation matrix.
+    dig_ch_pos : dict
+        Dictionary of channel positions.
+
+        .. versionadded:: 0.12
 
     Notes
     -----
     .. versionadded:: 0.9.0
     """
     def __init__(self, hsp, hpi, elp, point_names,
-                 nasion=None, lpa=None, rpa=None, dev_head_t=None):
+                 nasion=None, lpa=None, rpa=None, dev_head_t=None,
+                 dig_ch_pos=None):
         self.hsp = hsp
         self.hpi = hpi
         self.elp = elp
@@ -365,6 +378,7 @@ class DigMontage(object):
             self.dev_head_t = np.identity(4)
         else:
             self.dev_head_t = dev_head_t
+        self.dig_ch_pos = dig_ch_pos
 
     def __repr__(self):
         s = '<DigMontage | %d Dig Points, %d HPI points: %s ...>'
@@ -392,9 +406,23 @@ class DigMontage(object):
                             show_names=show_names)
 
 
+_cardinal_ident_mapping = {
+    FIFF.FIFFV_POINT_NASION: 'nasion',
+    FIFF.FIFFV_POINT_LPA: 'lpa',
+    FIFF.FIFFV_POINT_RPA: 'rpa',
+}
+
+
+def _check_frame(d, frame_str):
+    """Helper to check coordinate frames"""
+    if d['coord_frame'] != _str_to_frame[frame_str]:
+        raise RuntimeError('dig point must be in %s coordinate frame, got %s'
+                           % (frame_str, _frame_to_str[d['coord_frame']]))
+
+
 def read_dig_montage(hsp=None, hpi=None, elp=None, point_names=None,
-                     unit='mm', transform=True, dev_head_t=False):
-    """Read digitization data from a file and generate a DigMontage
+                     unit='mm', fif=None, transform=True, dev_head_t=False):
+    """Read subject-specific digitization montage from a file
 
     Parameters
     ----------
@@ -421,6 +449,12 @@ def read_dig_montage(hsp=None, hpi=None, elp=None, point_names=None,
     unit : 'm' | 'cm' | 'mm'
         Unit of the input file. If not 'm', coordinates will be rescaled
         to 'm'. Default is 'mm'. This is applied only for hsp and elp files.
+    fif : str | None
+        FIF file from which to read digitization locations.
+        If str (filename), all other arguments are ignored.
+
+        .. versionadded:: 0.12
+
     transform : bool
         If True, points will be transformed to Neuromag space.
         The fidicuals, 'nasion', 'lpa', 'rpa' must be specified in
@@ -438,6 +472,10 @@ def read_dig_montage(hsp=None, hpi=None, elp=None, point_names=None,
     montage : instance of DigMontage
         The digitizer montage.
 
+    See Also
+    --------
+    read_montage : Function to read generic EEG templates
+
     Notes
     -----
     All digitized points will be transformed to head-based coordinate system
@@ -445,13 +483,56 @@ def read_dig_montage(hsp=None, hpi=None, elp=None, point_names=None,
 
     .. versionadded:: 0.9.0
     """
+    if not isinstance(unit, string_types) or unit not in('m', 'mm', 'cm'):
+        raise ValueError('unit must be "m", "mm", or "cm"')
+    scale = dict(m=1., mm=1e-3, cm=1e-2)[unit]
+    dig_ch_pos = None
+    fids = None
+    if fif is not None:
+        # Use a different code path
+        if dev_head_t or not transform:
+            raise ValueError('transform must be True and dev_head_t must be '
+                             'False for FIF dig montage')
+        if not all(x is None for x in (hsp, hpi, elp, point_names)):
+            raise ValueError('hsp, hpi, elp, and point_names must all be None '
+                             'if fif is not None')
+        _check_fname(fif, overwrite=True, must_exist=True)
+        # Load the dig data
+        f, tree = fiff_open(fif)[:2]
+        with f as fid:
+            dig = _read_dig_fif(fid, tree)
+        # Split up the dig points by category
+        hsp = list()
+        hpi = list()
+        elp = list()
+        point_names = list()
+        fids = dict()
+        dig_ch_pos = dict()
+        for d in dig:
+            if d['kind'] == FIFF.FIFFV_POINT_CARDINAL:
+                _check_frame(d, 'head')
+                fids[_cardinal_ident_mapping[d['ident']]] = d['r']
+            elif d['kind'] == FIFF.FIFFV_POINT_HPI:
+                _check_frame(d, 'head')
+                hpi.append(d['r'])
+                elp.append(d['r'])
+                point_names.append('HPI%03d' % d['ident'])
+            elif d['kind'] == FIFF.FIFFV_POINT_EXTRA:
+                _check_frame(d, 'head')
+                hsp.append(d['r'])
+            elif d['kind'] == FIFF.FIFFV_POINT_EEG:
+                _check_frame(d, 'head')
+                dig_ch_pos['EEG%03d' % d['ident']] = d['r']
+        fids = np.array([fids[key] for key in ('nasion', 'lpa', 'rpa')])
+        hsp = np.array(hsp)
+        hsp /= scale  # will be multiplied later
+        elp = np.array(elp)
+        elp /= scale  # will be multiplied later
+        transform = False
     if isinstance(hsp, string_types):
         hsp = _read_dig_points(hsp)
     if hsp is not None:
-        if unit == 'mm':
-            hsp *= 1e-3
-        if unit == 'cm':
-            hsp *= 1e-2
+        hsp = hsp * scale
     if isinstance(hpi, string_types):
         ext = op.splitext(hpi)[-1]
         if ext == '.txt':
@@ -467,11 +548,7 @@ def read_dig_montage(hsp=None, hpi=None, elp=None, point_names=None,
         if len(elp) != len(point_names):
             raise ValueError("The elp file contains %i points, but %i names "
                              "were specified." % (len(elp), len(point_names)))
-        if unit == 'mm':
-            elp *= 1e-3
-        elif unit == 'cm':
-            elp *= 1e-2
-
+        elp = elp * scale
     if transform:
         if elp is None:
             raise ValueError("ELP points are not specified. Points are needed "
@@ -503,7 +580,7 @@ def read_dig_montage(hsp=None, hpi=None, elp=None, point_names=None,
         fids = apply_trans(neuromag_trans, fids)
         elp = apply_trans(neuromag_trans, elp)
         hsp = apply_trans(neuromag_trans, hsp)
-    else:
+    elif fids is None:
         fids = [None] * 3
     if dev_head_t:
         from ..coreg import fit_matched_points
@@ -512,7 +589,7 @@ def read_dig_montage(hsp=None, hpi=None, elp=None, point_names=None,
         trans = np.identity(4)
 
     return DigMontage(hsp, hpi, elp, point_names, fids[0], fids[1], fids[2],
-                      trans)
+                      trans, dig_ch_pos)
 
 
 def _set_montage(info, montage, update_ch_names=False):
@@ -542,7 +619,6 @@ def _set_montage(info, montage, update_ch_names=False):
     """
     if isinstance(montage, Montage):
         if update_ch_names:
-            info['ch_names'] = montage.ch_names
             info['chs'] = list()
             for ii, ch_name in enumerate(montage.ch_names):
                 ch_info = {'cal': 1., 'logno': ii + 1, 'scanno': ii + 1,
@@ -551,6 +627,7 @@ def _set_montage(info, montage, update_ch_names=False):
                            'coord_frame': FIFF.FIFFV_COORD_HEAD,
                            'coil_type': FIFF.FIFFV_COIL_EEG}
                 info['chs'].append(ch_info)
+            info._update_redundant()
 
         if not _contains_ch_type(info, 'eeg'):
             raise ValueError('No EEG channels found.')
@@ -561,7 +638,6 @@ def _set_montage(info, montage, update_ch_names=False):
                 continue
 
             ch_idx = info['ch_names'].index(ch_name)
-            info['ch_names'][ch_idx] = ch_name
             info['chs'][ch_idx]['loc'] = np.r_[pos, [0.] * 9]
             sensors_found.append(ch_idx)
 
@@ -575,17 +651,38 @@ def _set_montage(info, montage, update_ch_names=False):
         not_found = np.setdiff1d(eeg_sensors, sensors_found)
         if len(not_found) > 0:
             not_found_names = [info['ch_names'][ch] for ch in not_found]
-            warnings.warn('The following EEG sensors did not have a position '
-                          'specified in the selected montage: ' +
-                          str(not_found_names) + '. Their position has been '
-                          'left untouched.')
+            warn('The following EEG sensors did not have a position '
+                 'specified in the selected montage: ' +
+                 str(not_found_names) + '. Their position has been '
+                 'left untouched.')
 
     elif isinstance(montage, DigMontage):
         dig = _make_dig_points(nasion=montage.nasion, lpa=montage.lpa,
                                rpa=montage.rpa, hpi=montage.hpi,
-                               dig_points=montage.hsp)
+                               dig_points=montage.hsp,
+                               dig_ch_pos=montage.dig_ch_pos)
         info['dig'] = dig
         info['dev_head_t']['trans'] = montage.dev_head_t
+        if montage.dig_ch_pos is not None:  # update channel positions, too
+            eeg_ref_pos = montage.dig_ch_pos.get('EEG000', np.zeros(3))
+            did_set = np.zeros(len(info['ch_names']), bool)
+            is_eeg = np.zeros(len(info['ch_names']), bool)
+            is_eeg[pick_types(info, meg=False, eeg=True, exclude=())] = True
+            for ch_name, ch_pos in montage.dig_ch_pos.items():
+                if ch_name == 'EEG000':
+                    continue
+                if ch_name not in info['ch_names']:
+                    raise RuntimeError('Montage channel %s not found in info'
+                                       % ch_name)
+                idx = info['ch_names'].index(ch_name)
+                did_set[idx] = True
+                this_loc = np.concatenate((ch_pos, eeg_ref_pos))
+                info['chs'][idx]['loc'][:6] = this_loc
+            did_not_set = [info['chs'][ii]['ch_name']
+                           for ii in np.where(is_eeg & ~did_set)[0]]
+            if len(did_not_set) > 0:
+                warn('Did not set %s channel positions:\n%s'
+                     % (len(did_not_set), ', '.join(did_not_set)))
     else:
         raise TypeError("Montage must be a 'Montage' or 'DigMontage' "
                         "instead of '%s'." % type(montage))
diff --git a/mne/channels/tests/test_channels.py b/mne/channels/tests/test_channels.py
index a02ebc9..d7a28f9 100644
--- a/mne/channels/tests/test_channels.py
+++ b/mne/channels/tests/test_channels.py
@@ -6,6 +6,7 @@
 import os.path as op
 
 from copy import deepcopy
+import warnings
 
 import numpy as np
 from numpy.testing import assert_array_equal
@@ -17,11 +18,13 @@ from mne.io import read_info, Raw
 from mne.io.constants import FIFF
 from mne.fixes import partial, savemat
 from mne.utils import _TempDir, run_tests_if_main
-from mne import pick_types
+from mne import pick_types, pick_channels
 
 base_dir = op.join(op.dirname(__file__), '..', '..', 'io', 'tests', 'data')
 raw_fname = op.join(base_dir, 'test_raw.fif')
 
+warnings.simplefilter('always')
+
 
 def test_rename_channels():
     """Test rename channels
@@ -72,25 +75,45 @@ def test_set_channel_types():
     mapping = {'EOG 061': 'xxx'}
     assert_raises(ValueError, raw.set_channel_types, mapping)
     # Test changing type if in proj (avg eeg ref here)
-    mapping = {'EEG 060': 'eog', 'EEG 059': 'ecg', 'EOG 061': 'seeg'}
+    mapping = {'EEG 058': 'ecog', 'EEG 059': 'ecg', 'EEG 060': 'eog',
+               'EOG 061': 'seeg', 'MEG 2441': 'eeg', 'MEG 2443': 'eeg'}
     assert_raises(RuntimeError, raw.set_channel_types, mapping)
     # Test type change
     raw2 = Raw(raw_fname, add_eeg_ref=False)
     raw2.info['bads'] = ['EEG 059', 'EEG 060', 'EOG 061']
-    raw2.set_channel_types(mapping)
+    assert_raises(RuntimeError, raw2.set_channel_types, mapping)  # has proj
+    raw2.add_proj([], remove_existing=True)
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        raw2.set_channel_types(mapping)
+    assert_true(len(w) >= 1, msg=[str(ww.message) for ww in w])
+    assert_true(all('The unit for channel' in str(ww.message) for ww in w))
     info = raw2.info
-    assert_true(info['chs'][374]['ch_name'] == 'EEG 060')
-    assert_true(info['chs'][374]['kind'] == FIFF.FIFFV_EOG_CH)
-    assert_true(info['chs'][374]['unit'] == FIFF.FIFF_UNIT_V)
-    assert_true(info['chs'][374]['coil_type'] == FIFF.FIFFV_COIL_NONE)
+    assert_true(info['chs'][372]['ch_name'] == 'EEG 058')
+    assert_true(info['chs'][372]['kind'] == FIFF.FIFFV_ECOG_CH)
+    assert_true(info['chs'][372]['unit'] == FIFF.FIFF_UNIT_V)
+    assert_true(info['chs'][372]['coil_type'] == FIFF.FIFFV_COIL_EEG)
     assert_true(info['chs'][373]['ch_name'] == 'EEG 059')
     assert_true(info['chs'][373]['kind'] == FIFF.FIFFV_ECG_CH)
     assert_true(info['chs'][373]['unit'] == FIFF.FIFF_UNIT_V)
     assert_true(info['chs'][373]['coil_type'] == FIFF.FIFFV_COIL_NONE)
+    assert_true(info['chs'][374]['ch_name'] == 'EEG 060')
+    assert_true(info['chs'][374]['kind'] == FIFF.FIFFV_EOG_CH)
+    assert_true(info['chs'][374]['unit'] == FIFF.FIFF_UNIT_V)
+    assert_true(info['chs'][374]['coil_type'] == FIFF.FIFFV_COIL_NONE)
     assert_true(info['chs'][375]['ch_name'] == 'EOG 061')
     assert_true(info['chs'][375]['kind'] == FIFF.FIFFV_SEEG_CH)
     assert_true(info['chs'][375]['unit'] == FIFF.FIFF_UNIT_V)
     assert_true(info['chs'][375]['coil_type'] == FIFF.FIFFV_COIL_EEG)
+    for idx in pick_channels(raw.ch_names, ['MEG 2441', 'MEG 2443']):
+        assert_true(info['chs'][idx]['kind'] == FIFF.FIFFV_EEG_CH)
+        assert_true(info['chs'][idx]['unit'] == FIFF.FIFF_UNIT_V)
+        assert_true(info['chs'][idx]['coil_type'] == FIFF.FIFFV_COIL_EEG)
+
+    # Test meaningful error when setting channel type with unknown unit
+    raw.info['chs'][0]['unit'] = 0.
+    ch_types = {raw.ch_names[0]: 'misc'}
+    assert_raises(ValueError, raw.set_channel_types, ch_types)
 
 
 def test_read_ch_connectivity():
diff --git a/mne/channels/tests/test_interpolation.py b/mne/channels/tests/test_interpolation.py
index 2b2a881..6bae870 100644
--- a/mne/channels/tests/test_interpolation.py
+++ b/mne/channels/tests/test_interpolation.py
@@ -1,4 +1,6 @@
 import os.path as op
+import warnings
+
 import numpy as np
 from numpy.testing import (assert_allclose, assert_array_equal)
 from nose.tools import assert_raises, assert_equal, assert_true
@@ -20,7 +22,7 @@ def _load_data():
     """Helper function to load data."""
     # It is more memory efficient to load data in a separate
     # function so it's loaded on-demand
-    raw = io.Raw(raw_fname, add_eeg_ref=False)
+    raw = io.read_raw_fif(raw_fname, add_eeg_ref=False)
     events = read_events(event_name)
     picks_eeg = pick_types(raw.info, meg=False, eeg=True, exclude=[])
     # select every second channel for faster speed but compensate by using
@@ -28,13 +30,15 @@ def _load_data():
     picks_meg = pick_types(raw.info, meg=True, eeg=False, exclude=[])[1::2]
     picks = pick_types(raw.info, meg=True, eeg=True, exclude=[])
 
-    epochs_eeg = Epochs(raw, events, event_id, tmin, tmax, picks=picks_eeg,
-                        preload=True, reject=dict(eeg=80e-6))
-    epochs_meg = Epochs(raw, events, event_id, tmin, tmax, picks=picks_meg,
-                        preload=True, reject=dict(grad=1000e-12, mag=4e-12))
-    epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    preload=True, reject=dict(eeg=80e-6, grad=1000e-12,
-                                              mag=4e-12))
+    with warnings.catch_warnings(record=True):  # proj
+        epochs_eeg = Epochs(raw, events, event_id, tmin, tmax, picks=picks_eeg,
+                            preload=True, reject=dict(eeg=80e-6))
+        epochs_meg = Epochs(raw, events, event_id, tmin, tmax, picks=picks_meg,
+                            preload=True,
+                            reject=dict(grad=1000e-12, mag=4e-12))
+        epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
+                        preload=True, reject=dict(eeg=80e-6, grad=1000e-12,
+                                                  mag=4e-12))
     return raw, epochs, epochs_eeg, epochs_meg
 
 
@@ -100,6 +104,7 @@ def test_interpolation():
     data1 = raw_meg[pick, :][0][0]
     # reset_bads=False here because epochs_meg appears to share the same info
     # dict with raw and we want to test the epochs functionality too
+    raw_meg.info.normalize_proj()
     data2 = raw_meg.interpolate_bads(reset_bads=False)[pick, :][0][0]
     assert_true(np.corrcoef(data1, data2)[0, 1] > thresh)
     # the same number of bads as before
@@ -107,6 +112,7 @@ def test_interpolation():
 
     # MEG -- epochs
     data1 = epochs_meg.get_data()[:, pick, :].ravel()
+    epochs_meg.info.normalize_proj()
     epochs_meg.interpolate_bads()
     data2 = epochs_meg.get_data()[:, pick, :].ravel()
     assert_true(np.corrcoef(data1, data2)[0, 1] > thresh)
@@ -114,6 +120,7 @@ def test_interpolation():
 
     # MEG -- evoked
     data1 = evoked.data[pick]
+    evoked.info.normalize_proj()
     data2 = evoked.interpolate_bads().data[pick]
     assert_true(np.corrcoef(data1, data2)[0, 1] > thresh)
 
diff --git a/mne/channels/tests/test_layout.py b/mne/channels/tests/test_layout.py
index 5166325..816e66e 100644
--- a/mne/channels/tests/test_layout.py
+++ b/mne/channels/tests/test_layout.py
@@ -9,6 +9,8 @@ from __future__ import print_function
 import copy
 import os.path as op
 import warnings
+# Set our plotters to test mode
+import matplotlib
 
 import numpy as np
 from numpy.testing import (assert_array_almost_equal, assert_array_equal,
@@ -22,8 +24,9 @@ from mne.utils import run_tests_if_main
 from mne import pick_types, pick_info
 from mne.io import Raw, read_raw_kit, _empty_info
 from mne.io.constants import FIFF
-from mne.preprocessing.maxfilter import fit_sphere_to_headshape
+from mne.bem import fit_sphere_to_headshape
 from mne.utils import _TempDir
+matplotlib.use('Agg')  # for testing don't use X server
 
 warnings.simplefilter('always')
 
@@ -42,46 +45,25 @@ fname_ctf_raw = op.join(op.dirname(__file__), '..', '..', 'io', 'tests',
 fname_kit_157 = op.join(op.dirname(__file__), '..', '..',  'io', 'kit',
                         'tests', 'data', 'test.sqd')
 
-test_info = _empty_info(1000)
-test_info.update({
-    'ch_names': ['ICA 001', 'ICA 002', 'EOG 061'],
-    'chs': [{'cal': 1,
-             'ch_name': 'ICA 001',
-             'coil_type': 0,
-             'coord_Frame': 0,
-             'kind': 502,
-             'loc': np.array([0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 1.],
-                             dtype=np.float32),
-             'logno': 1,
-             'range': 1.0,
-             'scanno': 1,
-             'unit': -1,
-             'unit_mul': 0},
-            {'cal': 1,
-             'ch_name': 'ICA 002',
-             'coil_type': 0,
-             'coord_Frame': 0,
-             'kind': 502,
-             'loc': np.array([0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 1.],
-                             dtype=np.float32),
-             'logno': 2,
-             'range': 1.0,
-             'scanno': 2,
-             'unit': -1,
-             'unit_mul': 0},
-            {'cal': 0.002142000012099743,
-             'ch_name': 'EOG 061',
-             'coil_type': 1,
-             'coord_frame': 0,
-             'kind': 202,
-             'loc': np.array([0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 1.],
-                             dtype=np.float32),
-             'logno': 61,
-             'range': 1.0,
-             'scanno': 376,
-             'unit': 107,
-             'unit_mul': 0}],
-    'nchan': 3})
+
+def _get_test_info():
+    """Helper to make test info"""
+    test_info = _empty_info(1000)
+    loc = np.array([0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 1.],
+                   dtype=np.float32)
+    test_info['chs'] = [
+        {'cal': 1, 'ch_name': 'ICA 001', 'coil_type': 0, 'coord_Frame': 0,
+         'kind': 502, 'loc': loc.copy(), 'logno': 1, 'range': 1.0, 'scanno': 1,
+         'unit': -1, 'unit_mul': 0},
+        {'cal': 1, 'ch_name': 'ICA 002', 'coil_type': 0, 'coord_Frame': 0,
+         'kind': 502, 'loc': loc.copy(), 'logno': 2, 'range': 1.0, 'scanno': 2,
+         'unit': -1, 'unit_mul': 0},
+        {'cal': 0.002142000012099743, 'ch_name': 'EOG 061', 'coil_type': 1,
+         'coord_frame': 0, 'kind': 202, 'loc': loc.copy(), 'logno': 61,
+         'range': 1.0, 'scanno': 376, 'unit': 107, 'unit_mul': 0}]
+    test_info._update_redundant()
+    test_info._check_consistency()
+    return test_info
 
 
 def test_io_layout_lout():
@@ -122,8 +104,7 @@ def test_auto_topomap_coords():
     dig_kinds = (FIFF.FIFFV_POINT_CARDINAL,
                  FIFF.FIFFV_POINT_EEG,
                  FIFF.FIFFV_POINT_EXTRA)
-    _, origin_head, _ = fit_sphere_to_headshape(info, dig_kinds)
-    origin_head /= 1000.  # to meters
+    _, origin_head, _ = fit_sphere_to_headshape(info, dig_kinds, units='m')
     for ch in info['chs']:
         ch['loc'][:3] -= origin_head
 
@@ -197,7 +178,7 @@ def test_make_grid_layout():
     tmp_name = 'bar'
     lout_name = 'test_ica'
     lout_orig = read_layout(kind=lout_name, path=lout_path)
-    layout = make_grid_layout(test_info)
+    layout = make_grid_layout(_get_test_info())
     layout.save(op.join(tempdir, tmp_name + '.lout'))
     lout_new = read_layout(kind=tmp_name, path=tempdir)
     assert_array_equal(lout_new.kind, tmp_name)
@@ -205,7 +186,7 @@ def test_make_grid_layout():
     assert_array_equal(lout_orig.names, lout_new.names)
 
     # Test creating grid layout with specified number of columns
-    layout = make_grid_layout(test_info, n_col=2)
+    layout = make_grid_layout(_get_test_info(), n_col=2)
     # Vertical positions should be equal
     assert_true(layout.pos[0, 1] == layout.pos[1, 1])
     # Horizontal positions should be unequal
@@ -216,7 +197,8 @@ def test_make_grid_layout():
 
 def test_find_layout():
     """Test finding layout"""
-    assert_raises(ValueError, find_layout, test_info, ch_type='meep')
+    import matplotlib.pyplot as plt
+    assert_raises(ValueError, find_layout, _get_test_info(), ch_type='meep')
 
     sample_info = Raw(fif_fname).info
     grads = pick_types(sample_info, meg='grad')
@@ -229,7 +211,6 @@ def test_find_layout():
     sample_info4 = copy.deepcopy(sample_info)
     for ii, name in enumerate(sample_info4['ch_names']):
         new = name.replace(' ', '')
-        sample_info4['ch_names'][ii] = new
         sample_info4['chs'][ii]['ch_name'] = new
 
     eegs = pick_types(sample_info, meg=False, eeg=True)
@@ -282,6 +263,9 @@ def test_find_layout():
 
     lout = find_layout(read_raw_kit(fname_kit_157).info)
     assert_true(lout.kind == 'KIT-157')
+    # Test plotting
+    lout.plot()
+    plt.close('all')
 
 
 def test_box_size():
diff --git a/mne/channels/tests/test_montage.py b/mne/channels/tests/test_montage.py
index 1b27bfc..138f07c 100644
--- a/mne/channels/tests/test_montage.py
+++ b/mne/channels/tests/test_montage.py
@@ -10,21 +10,29 @@ from nose.tools import assert_equal, assert_true
 import numpy as np
 from numpy.testing import (assert_array_equal, assert_almost_equal,
                            assert_allclose, assert_array_almost_equal)
-
+from mne.tests.common import assert_dig_allclose
 from mne.channels.montage import read_montage, _set_montage, read_dig_montage
-from mne.utils import _TempDir
-from mne import create_info, EvokedArray
+from mne.utils import _TempDir, run_tests_if_main
+from mne import create_info, EvokedArray, read_evokeds
 from mne.coreg import fit_matched_points
 from mne.transforms import apply_trans, get_ras_to_neuromag_trans
 from mne.io.constants import FIFF
 from mne.io.meas_info import _read_dig_points
 from mne.io.kit import read_mrk
+from mne.io import read_raw_brainvision
+
+from mne.datasets import testing
 
+data_path = testing.data_path(download=False)
+fif_dig_montage_fname = op.join(data_path, 'montage', 'eeganes07.fif')
+evoked_fname = op.join(data_path, 'montage', 'level2_raw-ave.fif')
 
-p_dir = op.dirname(__file__)
-elp = op.join(p_dir, '..', '..', 'io', 'kit', 'tests', 'data', 'test_elp.txt')
-hsp = op.join(p_dir, '..', '..', 'io', 'kit', 'tests', 'data', 'test_hsp.txt')
-hpi = op.join(p_dir, '..', '..', 'io', 'kit', 'tests', 'data', 'test_mrk.sqd')
+io_dir = op.join(op.dirname(__file__), '..', '..', 'io')
+kit_dir = op.join(io_dir, 'kit', 'tests', 'data')
+elp = op.join(kit_dir, 'test_elp.txt')
+hsp = op.join(kit_dir, 'test_hsp.txt')
+hpi = op.join(kit_dir, 'test_mrk.sqd')
+bv_fname = op.join(io_dir, 'brainvision', 'tests', 'data', 'test.vhdr')
 
 
 def test_montage():
@@ -176,10 +184,9 @@ def test_read_dig_montage():
 
 def test_set_dig_montage():
     """Test applying DigMontage to inst
-
-    Extensive testing of applying `dig` to info is done in test_meas_info
-    with `test_make_dig_points`.
     """
+    # Extensive testing of applying `dig` to info is done in test_meas_info
+    # with `test_make_dig_points`.
     names = ['nasion', 'lpa', 'rpa', '1', '2', '3', '4', '5']
     hsp_points = _read_dig_points(hsp)
     elp_points = _read_dig_points(elp)
@@ -212,3 +219,39 @@ def test_set_dig_montage():
     assert_array_equal(rpa_dig.ravel(), rpa_point)
     assert_array_equal(hpi_dig, hpi_points)
     assert_array_equal(montage.dev_head_t, info['dev_head_t']['trans'])
+
+
+ at testing.requires_testing_data
+def test_fif_dig_montage():
+    """Test FIF dig montage support"""
+    dig_montage = read_dig_montage(fif=fif_dig_montage_fname)
+
+    # Make a BrainVision file like the one the user would have had
+    raw_bv = read_raw_brainvision(bv_fname, preload=True)
+    raw_bv_2 = raw_bv.copy()
+    mapping = dict()
+    for ii, ch_name in enumerate(raw_bv.ch_names[:-1]):
+        mapping[ch_name] = 'EEG%03d' % (ii + 1,)
+    raw_bv.rename_channels(mapping)
+    for ii, ch_name in enumerate(raw_bv_2.ch_names[:-1]):
+        mapping[ch_name] = 'EEG%03d' % (ii + 33,)
+    raw_bv_2.rename_channels(mapping)
+    raw_bv.drop_channels(['STI 014'])
+    raw_bv.add_channels([raw_bv_2])
+
+    # Set the montage
+    raw_bv.set_montage(dig_montage)
+
+    # Check the result
+    evoked = read_evokeds(evoked_fname)[0]
+
+    assert_equal(len(raw_bv.ch_names), len(evoked.ch_names))
+    for ch_py, ch_c in zip(raw_bv.info['chs'], evoked.info['chs']):
+        assert_equal(ch_py['ch_name'], ch_c['ch_name'].replace('EEG ', 'EEG'))
+        # C actually says it's unknown, but it's not (?):
+        # assert_equal(ch_py['coord_frame'], ch_c['coord_frame'])
+        assert_equal(ch_py['coord_frame'], FIFF.FIFFV_COORD_HEAD)
+        assert_allclose(ch_py['loc'], ch_c['loc'])
+    assert_dig_allclose(raw_bv.info, evoked.info)
+
+run_tests_if_main()
diff --git a/mne/chpi.py b/mne/chpi.py
index 481bc79..c3586b5 100644
--- a/mne/chpi.py
+++ b/mne/chpi.py
@@ -3,24 +3,32 @@
 # License: BSD (3-clause)
 
 import numpy as np
-from os import path as op
-from scipy import linalg
+from scipy import linalg, fftpack
 
 from .io.pick import pick_types, pick_channels
 from .io.base import _BaseRaw
 from .io.constants import FIFF
 from .forward import (_magnetic_dipole_field_vec, _create_meg_coils,
-                      _concatenate_coils)
+                      _concatenate_coils, _read_coil_defs)
 from .cov import make_ad_hoc_cov, _get_whitener_data
-from .transforms import apply_trans, invert_transform
-from .utils import verbose, logger, check_version
+from .transforms import (apply_trans, invert_transform, _angle_between_quats,
+                         quat_to_rot, rot_to_quat)
+from .utils import (verbose, logger, check_version, use_log_level, deprecated,
+                    _check_fname, warn)
 from .fixes import partial
 from .externals.six import string_types
 
+# Eventually we should add:
+#   hpicons
+#   high-passing of data during fits
+
 
 # ############################################################################
 # Reading from text or FIF file
 
+ at deprecated('get_chpi_positions will be removed in v0.13, use '
+            'read_head_pos(fname) or raw[pick_types(meg=False, chpi=True), :] '
+            'instead')
 @verbose
 def get_chpi_positions(raw, t_step=None, return_quat=False, verbose=None):
     """Extract head positions
@@ -31,7 +39,7 @@ def get_chpi_positions(raw, t_step=None, return_quat=False, verbose=None):
     ----------
     raw : instance of Raw | str
         Raw instance to extract the head positions from. Can also be a
-        path to a Maxfilter log file (str).
+        path to a Maxfilter head position estimation log file (str).
     t_step : float | None
         Sampling interval to use when converting data. If None, it will
         be automatically determined. By default, a sampling interval of
@@ -84,24 +92,83 @@ def get_chpi_positions(raw, t_step=None, return_quat=False, verbose=None):
     else:
         if not isinstance(raw, string_types):
             raise TypeError('raw must be an instance of Raw or string')
-        if not op.isfile(raw):
-            raise IOError('File "%s" does not exist' % raw)
         if t_step is not None:
             raise ValueError('t_step must be None if processing a log')
-        data = np.loadtxt(raw, skiprows=1)  # first line is header, skip it
-    out = _quats_to_trans_rot_t(data)
+        data = read_head_pos(raw)
+    out = head_pos_to_trans_rot_t(data)
     if return_quat:
         out = out + (data[:, 1:4],)
     return out
 
 
-def _quats_to_trans_rot_t(quats):
+def read_head_pos(fname):
+    """Read MaxFilter-formatted head position parameters
+
+    Parameters
+    ----------
+    fname : str
+        The filename to read. This can be produced by e.g.,
+        ``maxfilter -headpos <name>.pos``.
+
+    Returns
+    -------
+    pos : array, shape (N, 10)
+        The position and quaternion parameters from cHPI fitting.
+
+    See Also
+    --------
+    write_head_pos
+    head_pos_to_trans_rot_t
+
+    Notes
+    -----
+    .. versionadded:: 0.12
+    """
+    _check_fname(fname, must_exist=True, overwrite=True)
+    data = np.loadtxt(fname, skiprows=1)  # first line is header, skip it
+    data.shape = (-1, 10)  # ensure it's the right size even if empty
+    return data
+
+
+def write_head_pos(fname, pos):
+    """Write MaxFilter-formatted head position parameters
+
+    Parameters
+    ----------
+    fname : str
+        The filename to write.
+    pos : array, shape (N, 10)
+        The position and quaternion parameters from cHPI fitting.
+
+    See Also
+    --------
+    read_head_pos
+    head_pos_to_trans_rot_t
+
+    Notes
+    -----
+    .. versionadded:: 0.12
+    """
+    _check_fname(fname, overwrite=True)
+    pos = np.array(pos, np.float64)
+    if pos.ndim != 2 or pos.shape[1] != 10:
+        raise ValueError('pos must be a 2D array of shape (N, 10)')
+    with open(fname, 'wb') as fid:
+        fid.write(' Time       q1       q2       q3       q4       q5       '
+                  'q6       g-value  error    velocity\n'.encode('ASCII'))
+        for p in pos:
+            fmts = ['% 9.3f'] + ['% 8.5f'] * 9
+            fid.write(((' ' + ' '.join(fmts) + '\n')
+                       % tuple(p)).encode('ASCII'))
+
+
+def head_pos_to_trans_rot_t(quats):
     """Convert Maxfilter-formatted head position quaternions
 
     Parameters
     ----------
     quats : ndarray, shape (N, 10)
-        Maxfilter-formatted quaternions.
+        MaxFilter-formatted position and quaternion parameters.
 
     Returns
     -------
@@ -114,75 +181,15 @@ def _quats_to_trans_rot_t(quats):
 
     See Also
     --------
-    _calculate_chpi_positions, get_chpi_positions
+    read_pos
+    write_pos
     """
-    t = quats[:, 0].copy()
-    rotation = _quat_to_rot(quats[:, 1:4])
-    translation = quats[:, 4:7].copy()
+    t = quats[..., 0].copy()
+    rotation = quat_to_rot(quats[..., 1:4])
+    translation = quats[..., 4:7].copy()
     return translation, rotation, t
 
 
-def _quat_to_rot(q):
-    """Helper to convert quaternions to rotations"""
-    # z = a + bi + cj + dk
-    b, c, d = q[..., 0], q[..., 1], q[..., 2]
-    bb, cc, dd = b * b, c * c, d * d
-    # use max() here to be safe in case roundoff errs put us over
-    aa = np.maximum(1. - bb - cc - dd, 0.)
-    a = np.sqrt(aa)
-    ab_2 = 2 * a * b
-    ac_2 = 2 * a * c
-    ad_2 = 2 * a * d
-    bc_2 = 2 * b * c
-    bd_2 = 2 * b * d
-    cd_2 = 2 * c * d
-    rotation = np.array([(aa + bb - cc - dd, bc_2 - ad_2, bd_2 + ac_2),
-                         (bc_2 + ad_2, aa + cc - bb - dd, cd_2 - ab_2),
-                         (bd_2 - ac_2, cd_2 + ab_2, aa + dd - bb - cc),
-                         ])
-    if q.ndim > 1:
-        rotation = np.rollaxis(np.rollaxis(rotation, 1, q.ndim + 1), 0, q.ndim)
-    return rotation
-
-
-def _one_rot_to_quat(rot):
-    """Convert a rotation matrix to quaternions"""
-    # see e.g. http://www.euclideanspace.com/maths/geometry/rotations/
-    #                 conversions/matrixToQuaternion/
-    t = 1. + rot[0] + rot[4] + rot[8]
-    if t > np.finfo(rot.dtype).eps:
-        s = np.sqrt(t) * 2.
-        qx = (rot[7] - rot[5]) / s
-        qy = (rot[2] - rot[6]) / s
-        qz = (rot[3] - rot[1]) / s
-        # qw = 0.25 * s
-    elif rot[0] > rot[4] and rot[0] > rot[8]:
-        s = np.sqrt(1. + rot[0] - rot[4] - rot[8]) * 2.
-        qx = 0.25 * s
-        qy = (rot[1] + rot[3]) / s
-        qz = (rot[2] + rot[6]) / s
-        # qw = (rot[7] - rot[5]) / s
-    elif rot[4] > rot[8]:
-        s = np.sqrt(1. - rot[0] + rot[4] - rot[8]) * 2
-        qx = (rot[1] + rot[3]) / s
-        qy = 0.25 * s
-        qz = (rot[5] + rot[7]) / s
-        # qw = (rot[2] - rot[6]) / s
-    else:
-        s = np.sqrt(1. - rot[0] - rot[4] + rot[8]) * 2.
-        qx = (rot[2] + rot[6]) / s
-        qy = (rot[5] + rot[7]) / s
-        qz = 0.25 * s
-        # qw = (rot[3] - rot[1]) / s
-    return qx, qy, qz
-
-
-def _rot_to_quat(rot):
-    """Convert a set of rotations to quaternions"""
-    rot = rot.reshape(rot.shape[:-2] + (9,))
-    return np.apply_along_axis(_one_rot_to_quat, -1, rot)
-
-
 # ############################################################################
 # Estimate positions from data
 
@@ -193,70 +200,186 @@ def _get_hpi_info(info):
         raise RuntimeError('Appropriate cHPI information not found in'
                            'raw.info["hpi_meas"], cannot process cHPI')
     hpi_result = info['hpi_results'][-1]
-    hpi_coils = info['hpi_meas'][-1]['hpi_coils']
-    hpi_num = np.array([h['number'] for h in hpi_coils])
-    pos_order = np.searchsorted(hpi_num, hpi_result['order'])
-    hpi_dig = [d for d in info['dig'] if d['kind'] == FIFF.FIFFV_POINT_HPI]
+    hpi_coils = sorted(info['hpi_meas'][-1]['hpi_coils'],
+                       key=lambda x: x['number'])  # ascending (info) order
+    hpi_dig = sorted([d for d in info['dig']
+                      if d['kind'] == FIFF.FIFFV_POINT_HPI],
+                     key=lambda x: x['ident'])  # ascending (dig) order
+    pos_order = hpi_result['order'] - 1  # zero-based indexing, dig->info
+    # hpi_result['dig_points'] are in FIFFV_COORD_UNKNOWN coords...?
+
     # this shouldn't happen, eventually we could add the transforms
     # necessary to put it in head coords
     if not all(d['coord_frame'] == FIFF.FIFFV_COORD_HEAD for d in hpi_dig):
         raise RuntimeError('cHPI coordinate frame incorrect')
+    # Give the user some info
+    logger.info('HPIFIT: %s coils digitized in order %s'
+                % (len(pos_order), ' '.join(str(o + 1) for o in pos_order)))
+    logger.debug('HPIFIT: %s coils accepted: %s'
+                 % (len(hpi_result['used']),
+                    ' '.join(str(h) for h in hpi_result['used'])))
     hpi_rrs = np.array([d['r'] for d in hpi_dig])[pos_order]
+    # errors = 1000 * np.sqrt((hpi_rrs - hpi_rrs_fit) ** 2).sum(axis=1)
+    # logger.debug('HPIFIT errors:  %s'
+    #              % ', '.join('%0.1f' % e for e in errors))
     hpi_freqs = np.array([float(x['coil_freq']) for x in hpi_coils])
     # how cHPI active is indicated in the FIF file
     hpi_sub = info['hpi_subsystem']
-    hpi_pick = pick_channels(info['ch_names'], [hpi_sub['event_channel']])[0]
-    hpi_on = np.sum([coil['event_bits'][0] for coil in hpi_sub['hpi_coils']])
+    if 'event_channel' in hpi_sub:
+        hpi_pick = pick_channels(info['ch_names'],
+                                 [hpi_sub['event_channel']])[0]
+    else:
+        hpi_pick = None  # there is no pick!
+    hpi_on = [coil['event_bits'][0] for coil in hpi_sub['hpi_coils']]
+    # not all HPI coils will actually be used
+    hpi_on = np.array([hpi_on[hc['number'] - 1] for hc in hpi_coils])
+    assert len(hpi_coils) == len(hpi_on)
+    logger.info('Using %s HPI coils: %s Hz'
+                % (len(hpi_freqs), ' '.join(str(int(s)) for s in hpi_freqs)))
     return hpi_freqs, hpi_rrs, hpi_pick, hpi_on, pos_order
 
 
-def _magnetic_dipole_objective(x, B, B2, w, coils):
+def _magnetic_dipole_objective(x, B, B2, coils, scale, method):
     """Project data onto right eigenvectors of whitened forward"""
-    fwd = np.dot(_magnetic_dipole_field_vec(x[np.newaxis, :], coils), w.T)
-    one = np.dot(linalg.svd(fwd, full_matrices=False)[2], B)
-    Bm2 = np.sum(one * one)
+    if method == 'forward':
+        fwd = _magnetic_dipole_field_vec(x[np.newaxis, :], coils)
+    else:
+        from .preprocessing.maxwell import _sss_basis
+        # Eventually we can try incorporating external bases here, which
+        # is why the :3 is on the SVD below
+        fwd = _sss_basis(dict(origin=x, int_order=1, ext_order=0), coils).T
+    fwd = np.dot(fwd, scale.T)
+    one = np.dot(linalg.svd(fwd, full_matrices=False)[2][:3], B)
+    one *= one
+    Bm2 = one.sum()
     return B2 - Bm2
 
 
-def _fit_magnetic_dipole(B_orig, w, coils, x0):
+def _fit_magnetic_dipole(B_orig, x0, coils, scale, method):
     """Fit a single bit of data (x0 = pos)"""
     from scipy.optimize import fmin_cobyla
-    B = np.dot(w, B_orig)
+    B = np.dot(scale, B_orig)
     B2 = np.dot(B, B)
     objective = partial(_magnetic_dipole_objective, B=B, B2=B2,
-                        w=w, coils=coils)
-    x = fmin_cobyla(objective, x0, (), rhobeg=1e-2, rhoend=1e-4, disp=False)
+                        coils=coils, scale=scale, method=method)
+    x = fmin_cobyla(objective, x0, (), rhobeg=1e-2, rhoend=1e-5, disp=False)
     return x, 1. - objective(x) / B2
 
 
-def _chpi_objective(x, est_pos_dev, hpi_head_rrs):
+def _chpi_objective(x, coil_dev_rrs, coil_head_rrs):
     """Helper objective function"""
-    rot = _quat_to_rot(x[:3]).T
-    d = np.dot(est_pos_dev, rot) + x[3:] - hpi_head_rrs
-    return np.sum(d * d)
+    d = np.dot(coil_dev_rrs, quat_to_rot(x[:3]).T)
+    d += x[3:]
+    d -= coil_head_rrs
+    d *= d
+    return d.sum()
 
 
-def _fit_chpi_pos(est_pos_dev, hpi_head_rrs, x0):
+def _unit_quat_constraint(x):
+    """Constrain our 3 quaternion rot params (ignoring w) to have norm <= 1"""
+    return 1 - (x * x).sum()
+
+
+def _fit_chpi_pos(coil_dev_rrs, coil_head_rrs, x0):
     """Fit rotation and translation parameters for cHPI coils"""
     from scipy.optimize import fmin_cobyla
-    denom = np.sum((hpi_head_rrs - np.mean(hpi_head_rrs, axis=0)) ** 2)
-    objective = partial(_chpi_objective, est_pos_dev=est_pos_dev,
-                        hpi_head_rrs=hpi_head_rrs)
-    x = fmin_cobyla(objective, x0, (), rhobeg=1e-2, rhoend=1e-6, disp=False)
+    denom = np.sum((coil_head_rrs - np.mean(coil_head_rrs, axis=0)) ** 2)
+    objective = partial(_chpi_objective, coil_dev_rrs=coil_dev_rrs,
+                        coil_head_rrs=coil_head_rrs)
+    x = fmin_cobyla(objective, x0, _unit_quat_constraint,
+                    rhobeg=1e-2, rhoend=1e-6, disp=False)
     return x, 1. - objective(x) / denom
 
 
-def _angle_between_quats(x, y):
-    """Compute the angle between two quaternions w/3-element representations"""
-    # convert to complete quaternion representation
-    # use max() here to be safe in case roundoff errs put us over
-    x0 = np.sqrt(np.maximum(1. - x[..., 0] ** 2 -
-                            x[..., 1] ** 2 - x[..., 2] ** 2, 0.))
-    y0 = np.sqrt(np.maximum(1. - y[..., 0] ** 2 -
-                            y[..., 1] ** 2 - y[..., 2] ** 2, 0.))
-    # the difference z = x * conj(y), and theta = np.arccos(z0)
-    z0 = np.maximum(np.minimum(y0 * x0 + (x * y).sum(axis=-1), 1.), -1)
-    return 2 * np.arccos(z0)
+ at verbose
+def _setup_chpi_fits(info, t_window, t_step_min, method='forward',
+                     exclude='bads', add_hpi_stim_pick=True,
+                     remove_aliased=False, verbose=None):
+    """Helper to set up cHPI fits"""
+    from scipy.spatial.distance import cdist
+    from .preprocessing.maxwell import _prep_mf_coils
+    if not (check_version('numpy', '1.7') and check_version('scipy', '0.11')):
+        raise RuntimeError('numpy>=1.7 and scipy>=0.11 required')
+    hpi_freqs, coil_head_rrs, hpi_pick, hpi_ons = _get_hpi_info(info)[:4]
+    # What to do e.g. if Raw has been resampled and some of our
+    # HPI freqs would now be aliased
+    highest = info.get('lowpass')
+    highest = info['sfreq'] / 2. if highest is None else highest
+    keepers = np.array([h <= highest for h in hpi_freqs], bool)
+    if remove_aliased:
+        hpi_freqs = hpi_freqs[keepers]
+        coil_head_rrs = coil_head_rrs[keepers]
+        hpi_ons = hpi_ons[keepers]
+    elif not keepers.all():
+        raise RuntimeError('Found HPI frequencies %s above the lowpass '
+                           '(or Nyquist) frequency %0.1f'
+                           % (hpi_freqs[~keepers].tolist(), highest))
+    line_freqs = np.arange(info['line_freq'], info['sfreq'] / 3.,
+                           info['line_freq'])
+    logger.info('Line interference frequencies: %s Hz'
+                % ' '.join(['%d' % l for l in line_freqs]))
+    # initial transforms
+    dev_head_t = info['dev_head_t']['trans']
+    head_dev_t = invert_transform(info['dev_head_t'])['trans']
+    # determine timing
+    n_window = int(round(t_window * info['sfreq']))
+    logger.debug('Coordinate transformation:')
+    for d in (dev_head_t[0, :3], dev_head_t[1, :3], dev_head_t[2, :3],
+              dev_head_t[:3, 3] * 1000.):
+        logger.debug('{0:8.4f} {1:8.4f} {2:8.4f}'.format(*d))
+    slope = np.arange(n_window).astype(np.float64)[:, np.newaxis]
+    slope -= np.mean(slope)
+    rads = slope / info['sfreq']
+    rads *= 2 * np.pi
+    f_t = hpi_freqs[np.newaxis, :] * rads
+    l_t = line_freqs[np.newaxis, :] * rads
+    model = [np.sin(f_t), np.cos(f_t)]  # hpi freqs
+    model += [np.sin(l_t), np.cos(l_t)]  # line freqs
+    model += [slope, np.ones(slope.shape)]
+    model = np.concatenate(model, axis=1)
+    inv_model = linalg.pinv(model)
+    # Set up highpass at half lowest cHPI freq
+    hp_n = 2 ** (int(np.ceil(np.log2(n_window))) + 1)
+    freqs = fftpack.rfftfreq(hp_n, 1. / info['sfreq'])
+    hp_ind = np.where(freqs >= hpi_freqs.min())[0][0] - 2
+    hp_window = np.concatenate(
+        [[0], np.repeat(np.hanning(hp_ind - 1)[:(hp_ind - 1) // 2],
+                        2)])[np.newaxis]
+
+    # Set up magnetic dipole fits
+    picks_meg = pick_types(info, meg=True, eeg=False, exclude=exclude)
+    if add_hpi_stim_pick:
+        if hpi_pick is None:
+            raise RuntimeError('Could not find HPI status channel')
+        picks = np.concatenate([picks_meg, [hpi_pick]])
+    else:
+        picks = picks_meg
+    megchs = [ch for ci, ch in enumerate(info['chs']) if ci in picks_meg]
+    templates = _read_coil_defs(elekta_defs=True, verbose=False)
+    coils = _create_meg_coils(megchs, 'accurate', coilset=templates)
+    if method == 'forward':
+        coils = _concatenate_coils(coils)
+    else:  # == 'multipole'
+        coils = _prep_mf_coils(info)
+    scale = make_ad_hoc_cov(info, verbose=False)
+    scale = _get_whitener_data(info, scale, picks_meg, verbose=False)
+    orig_dev_head_quat = np.concatenate([rot_to_quat(dev_head_t[:3, :3]),
+                                         dev_head_t[:3, 3]])
+    dists = cdist(coil_head_rrs, coil_head_rrs)
+    hpi = dict(dists=dists, scale=scale, picks=picks, model=model,
+               inv_model=inv_model, coil_head_rrs=coil_head_rrs,
+               coils=coils, on=hpi_ons, n_window=n_window, method=method,
+               freqs=hpi_freqs, line_freqs=line_freqs,
+               hp_ind=hp_ind, hp_n=hp_n, hp_window=hp_window)
+    last = dict(quat=orig_dev_head_quat, coil_head_rrs=coil_head_rrs,
+                coil_dev_rrs=apply_trans(head_dev_t, coil_head_rrs),
+                sin_fit=None, fit_time=-t_step_min)
+    return hpi, last
+
+
+def _time_prefix(fit_time):
+    """Helper to format log messages"""
+    return ('    t=%0.3f:' % fit_time).ljust(17)
 
 
 @verbose
@@ -287,12 +410,8 @@ def _calculate_chpi_positions(raw, t_step_min=0.1, t_step_max=10.,
 
     Returns
     -------
-    translation : ndarray, shape (N, 3)
-        Translations at each time point.
-    rotation : ndarray, shape (N, 3, 3)
-        Rotations at each time point.
-    t : ndarray, shape (N,)
-        The time points.
+    quats : ndarray, shape (N, 10)
+        The ``[t, q1, q2, q3, x, y, z, gof, err, v]`` for each fit.
 
     Notes
     -----
@@ -301,158 +420,147 @@ def _calculate_chpi_positions(raw, t_step_min=0.1, t_step_max=10.,
 
     See Also
     --------
-    get_chpi_positions
+    read_head_pos
+    write_head_pos
     """
     from scipy.spatial.distance import cdist
-    if not (check_version('numpy', '1.7') and check_version('scipy', '0.11')):
-        raise RuntimeError('numpy>=1.7 and scipy>=0.11 required')
-    hpi_freqs, orig_head_rrs, hpi_pick, hpi_on, order = _get_hpi_info(raw.info)
-    sfreq, ch_names = raw.info['sfreq'], raw.info['ch_names']
-    # initial transforms
-    dev_head_t = raw.info['dev_head_t']['trans']
-    head_dev_t = invert_transform(raw.info['dev_head_t'])['trans']
-    # determine timing
-    n_window = int(round(t_window * sfreq))
-    fit_starts = np.round(np.arange(0, raw.last_samp / sfreq, t_step_min) *
-                          sfreq).astype(int)
-    fit_starts = fit_starts[fit_starts < raw.n_times - n_window]
-    fit_times = (fit_starts + (n_window + 1) // 2) / sfreq
-    n_freqs = len(hpi_freqs)
-    logger.info('HPIFIT: %s coils digitized in order %s'
-                % (n_freqs, ' '.join(str(o + 1) for o in order)))
-    logger.info('Coordinate transformation:')
-    for d in (dev_head_t[0, :3], dev_head_t[1, :3], dev_head_t[2, :3],
-              dev_head_t[:3, 3] * 1000.):
-        logger.info('{0:8.4f} {1:8.4f} {2:8.4f}'.format(*d))
-    logger.info('Using %s HPI coils: %s Hz'
-                % (n_freqs, ' '.join(str(int(s)) for s in hpi_freqs)))
-    # Set up amplitude fits
-    slope = np.arange(n_window).astype(np.float64)[:, np.newaxis]
-    f_t = 2 * np.pi * hpi_freqs[np.newaxis, :] * (slope / sfreq)
-    model = np.concatenate([np.sin(f_t), np.cos(f_t),
-                            slope, np.ones((n_window, 1))], axis=1)
-    inv_model = linalg.pinv(model)
-    del slope, f_t
-
-    # Set up magnetic dipole fits
-    picks = pick_types(raw.info, meg=True, eeg=False)
-    picks_chpi = np.concatenate([picks, [hpi_pick]])
-    logger.info('Found %s total and %s good MEG channels'
-                % (len(ch_names), len(picks)))
-    megchs = [ch for ci, ch in enumerate(raw.info['chs']) if ci in picks]
-    coils = _concatenate_coils(_create_meg_coils(megchs, 'normal'))
-
-    cov = make_ad_hoc_cov(raw.info, verbose=False)
-    whitener = _get_whitener_data(raw.info, cov, picks, verbose=False)
-    dev_head_quat = np.concatenate([_rot_to_quat(dev_head_t[:3, :3]),
-                                    dev_head_t[:3, 3]])
-    orig_dists = cdist(orig_head_rrs, orig_head_rrs)
-    last_quat = dev_head_quat.copy()
-    last_data_fit = None  # this indicates it's the first run
-    last_time = -t_step_min
-    last_head_rrs = orig_head_rrs.copy()
-    corr_limit = 0.98
+    hpi, last = _setup_chpi_fits(raw.info, t_window, t_step_min)
+    fit_idxs = raw.time_as_index(np.arange(0., raw.times[-1], t_step_min),
+                                 use_rounding=True)
     quats = []
-    est_pos_dev = apply_trans(head_dev_t, orig_head_rrs)
-    for start, t in zip(fit_starts, fit_times):
+    logger.info('Fitting up to %s time points (%0.1f sec duration)'
+                % (len(fit_idxs), raw.times[-1]))
+    pos_0 = None
+    n_freqs = len(hpi['freqs'])
+    for midpt in fit_idxs:
         #
         # 1. Fit amplitudes for each channel from each of the N cHPI sinusoids
         #
-        meg_chpi_data = raw[picks_chpi, start:start + n_window][0]
+        fit_time = midpt / raw.info['sfreq']
+        time_sl = midpt - hpi['n_window'] // 2
+        time_sl = slice(max(time_sl, 0),
+                        min(time_sl + hpi['n_window'], len(raw.times)))
+        with use_log_level(False):
+            meg_chpi_data = raw[hpi['picks'], time_sl][0]
         this_data = meg_chpi_data[:-1]
         chpi_data = meg_chpi_data[-1]
-        if not (chpi_data == hpi_on).all():
-            logger.info('HPI not turned on (t=%7.3f)' % t)
+        ons = (np.round(chpi_data).astype(np.int) &
+               hpi['on'][:, np.newaxis]).astype(bool)
+        n_on = np.sum(ons, axis=0)
+        if not (n_on >= 3).all():
+            logger.info(_time_prefix(fit_time) + '%s < 3 HPI coils turned on, '
+                        'skipping fit' % (n_on.min(),))
             continue
+        # ons = ons.all(axis=1)  # which HPI coils to use
+        this_len = time_sl.stop - time_sl.start
+        if this_len == hpi['n_window']:
+            model, inv_model = hpi['model'], hpi['inv_model']
+        else:  # first or last window
+            model = hpi['model'][:this_len]
+            inv_model = linalg.pinv(model)
         X = np.dot(inv_model, this_data.T)
         data_diff = np.dot(model, X).T - this_data
+        del model, inv_model
         data_diff *= data_diff
         this_data *= this_data
         g_chan = (1 - np.sqrt(data_diff.sum(axis=1) / this_data.sum(axis=1)))
         g_sin = (1 - np.sqrt(data_diff.sum() / this_data.sum()))
         del data_diff, this_data
         X_sin, X_cos = X[:n_freqs], X[n_freqs:2 * n_freqs]
-        s_fit = np.sqrt(X_cos * X_cos + X_sin * X_sin)
-        if last_data_fit is None:  # first iteration
-            corr = 0.
-        else:
-            corr = np.corrcoef(s_fit.ravel(), last_data_fit.ravel())[0, 1]
-
-        # check to see if we need to continue
-        if t - last_time <= t_step_max - 1e-7 and corr > corr_limit and \
-                t != fit_times[-1]:
-            continue  # don't need to re-fit data
-        last_data_fit = s_fit.copy()  # save *before* inplace sign transform
-
-        # figure out principal direction of the vectors and align
-        # for s, c, fit in zip(X_sin, X_cos, s_fit):
-        #     fit *= np.sign(linalg.svd([s, c], full_matrices=False)[2][0])
-        s_fit *= np.sign(np.arctan2(X_sin, X_cos))
+        signs = np.sign(np.arctan2(X_sin, X_cos))
+        X_sin *= X_sin
+        X_cos *= X_cos
+        X_sin += X_cos
+        sin_fit = np.sqrt(X_sin)
+        if last['sin_fit'] is not None:  # first iteration
+            corr = np.corrcoef(sin_fit.ravel(), last['sin_fit'].ravel())[0, 1]
+            # check to see if we need to continue
+            if fit_time - last['fit_time'] <= t_step_max - 1e-7 and \
+                    corr * corr > 0.98:
+                continue  # don't need to re-fit data
+        last['sin_fit'] = sin_fit.copy()  # save *before* inplace sign mult
+        sin_fit *= signs
+        del signs, X_sin, X_cos, X
 
         #
         # 2. Fit magnetic dipole for each coil to obtain coil positions
         #    in device coordinates
         #
-        logger.info('HPI amplitude correlation %s: %s (%s chnls > 0.95)'
-                    % (t, g_sin, (g_chan > 0.95).sum()))
-        outs = [_fit_magnetic_dipole(f, whitener, coils, pos)
-                for f, pos in zip(s_fit, est_pos_dev)]
-        est_pos_dev = np.array([o[0] for o in outs])
+        logger.debug('    HPI amplitude correlation %0.3f: %0.3f '
+                     '(%s chnls > 0.950)' % (fit_time, np.sqrt(g_sin),
+                                             (np.sqrt(g_chan) > 0.95).sum()))
+        outs = [_fit_magnetic_dipole(f, pos, hpi['coils'], hpi['scale'],
+                                     hpi['method'])
+                for f, pos in zip(sin_fit, last['coil_dev_rrs'])]
+        this_coil_dev_rrs = np.array([o[0] for o in outs])
         g_coils = [o[1] for o in outs]
-        these_dists = cdist(est_pos_dev, est_pos_dev)
-        these_dists = np.abs(orig_dists - these_dists)
+        these_dists = cdist(this_coil_dev_rrs, this_coil_dev_rrs)
+        these_dists = np.abs(hpi['dists'] - these_dists)
         # there is probably a better algorithm for finding the bad ones...
         good = False
         use_mask = np.ones(n_freqs, bool)
         while not good:
-            d = (these_dists[use_mask][:, use_mask] <= dist_limit)
-            good = d.all()
+            d = these_dists[use_mask][:, use_mask]
+            d_bad = (d > dist_limit)
+            good = not d_bad.any()
             if not good:
                 if use_mask.sum() == 2:
                     use_mask[:] = False
                     break  # failure
                 # exclude next worst point
-                badness = these_dists[use_mask][:, use_mask].sum(axis=0)
+                badness = (d * d_bad).sum(axis=0)
                 exclude = np.where(use_mask)[0][np.argmax(badness)]
                 use_mask[exclude] = False
         good = use_mask.sum() >= 3
         if not good:
-            logger.warning('    %s/%s acceptable hpi fits found, cannot '
-                           'determine the transformation! (t=%7.3f)'
-                           % (use_mask.sum(), n_freqs, t))
+            warn(_time_prefix(fit_time) + '%s/%s good HPI fits, '
+                 'cannot determine the transformation!'
+                 % (use_mask.sum(), n_freqs))
             continue
 
         #
         # 3. Fit the head translation and rotation params (minimize error
         #    between coil positions and the head coil digitization positions)
         #
-        dev_head_quat, g = _fit_chpi_pos(est_pos_dev[use_mask],
-                                         orig_head_rrs[use_mask],
-                                         dev_head_quat)
+        this_quat, g = _fit_chpi_pos(this_coil_dev_rrs[use_mask],
+                                     hpi['coil_head_rrs'][use_mask],
+                                     last['quat'])
         if g < gof_limit:
-            logger.info('    Bad coil fit for %s! (t=%7.3f)' % t)
+            logger.info(_time_prefix(fit_time) +
+                        'Bad coil fit! (g=%7.3f)' % (g,))
             continue
-        this_dev_head_t = np.concatenate((_quat_to_rot(dev_head_quat[:3]),
-                                          dev_head_quat[3:][:, np.newaxis]),
-                                         axis=1)
+        this_dev_head_t = np.concatenate(
+            (quat_to_rot(this_quat[:3]),
+             this_quat[3:][:, np.newaxis]), axis=1)
         this_dev_head_t = np.concatenate((this_dev_head_t, [[0, 0, 0, 1.]]))
-        this_head_rrs = apply_trans(this_dev_head_t, est_pos_dev)
-        dt = t - last_time
-        vs = tuple(1000. * np.sqrt(np.sum((last_head_rrs -
-                                           this_head_rrs) ** 2, axis=1)) / dt)
-        logger.info('Hpi fit OK, movements [mm/s] = ' +
-                    ' / '.join(['%0.1f'] * n_freqs) % vs)
-        errs = [0] * n_freqs  # XXX eventually calculate this
-        e = 0.  # XXX eventually calculate this
-        d = 100 * np.sqrt(np.sum(last_quat[3:] - dev_head_quat[3:]) ** 2)  # cm
-        r = _angle_between_quats(last_quat[:3], dev_head_quat[:3]) / dt
+        # velocities, in device coords, of HPI coils
+        dt = fit_time - last['fit_time']
+        vs = tuple(1000. * np.sqrt(np.sum((last['coil_dev_rrs'] -
+                                           this_coil_dev_rrs) ** 2,
+                                          axis=1)) / dt)
+        logger.info(_time_prefix(fit_time) +
+                    ('%s/%s good HPI fits, movements [mm/s] = ' +
+                     ' / '.join(['% 6.1f'] * n_freqs))
+                    % ((use_mask.sum(), n_freqs) + vs))
+        # resulting errors in head coil positions
+        est_coil_head_rrs = apply_trans(this_dev_head_t, this_coil_dev_rrs)
+        errs = 1000. * np.sqrt(np.sum((hpi['coil_head_rrs'] -
+                                       est_coil_head_rrs) ** 2,
+                                      axis=1))
+        e = 0.  # XXX eventually calculate this -- cumulative error of fit?
+        d = 100 * np.sqrt(np.sum(last['quat'][3:] - this_quat[3:]) ** 2)  # cm
+        r = _angle_between_quats(last['quat'][:3], this_quat[:3]) / dt
         v = d / dt  # cm/sec
+        if pos_0 is None:
+            pos_0 = this_quat[3:].copy()
+        d = 100 * np.sqrt(np.sum((this_quat[3:] - pos_0) ** 2))  # dis from 1st
+        # MaxFilter averages over a 200 ms window for display, but we don't
         for ii in range(n_freqs):
             if use_mask[ii]:
                 start, end = ' ', '/'
             else:
                 start, end = '(', ')'
-            log_str = (start +
+            log_str = ('    ' + start +
                        '{0:6.1f} {1:6.1f} {2:6.1f} / ' +
                        '{3:6.1f} {4:6.1f} {5:6.1f} / ' +
                        'g = {6:0.3f} err = {7:4.1f} ' +
@@ -461,19 +569,116 @@ def _calculate_chpi_positions(raw, t_step_min=0.1, t_step_max=10.,
                 log_str += '{8:6.3f} {9:6.3f} {10:6.3f}'
             elif ii == 3:
                 log_str += '{8:6.1f} {9:6.1f} {10:6.1f}'
-            vals = np.concatenate((1000 * orig_head_rrs[ii],
-                                   1000 * this_head_rrs[ii],
+            vals = np.concatenate((1000 * hpi['coil_head_rrs'][ii],
+                                   1000 * est_coil_head_rrs[ii],
                                    [g_coils[ii], errs[ii]]))
             if ii <= 2:
                 vals = np.concatenate((vals, this_dev_head_t[ii, :3]))
             elif ii == 3:
                 vals = np.concatenate((vals, this_dev_head_t[:3, 3] * 1000.))
             logger.debug(log_str.format(*vals))
-        logger.info('#t = %0.3f, #e = %0.2f cm, #g = %0.3f, #v = %0.2f cm/s, '
-                    '#r = %0.2f rad/s, #d = %0.2f cm' % (t, e, g, v, r, d))
-        quats.append(np.concatenate(([t], dev_head_quat, [g], [1. - g], [v])))
-        last_time = t
-        last_head_rrs = this_head_rrs.copy()
-    quats = np.array(quats)
+        logger.debug('    #t = %0.3f, #e = %0.2f cm, #g = %0.3f, '
+                     '#v = %0.2f cm/s, #r = %0.2f rad/s, #d = %0.2f cm'
+                     % (fit_time, 100 * e, g, v, r, d))
+        quats.append(np.concatenate(([fit_time], this_quat, [g], [e], [v])))
+        last['fit_time'] = fit_time
+        last['quat'] = this_quat
+        last['coil_dev_rrs'] = this_coil_dev_rrs
+    logger.info('[done]')
+    quats = np.array(quats, np.float64)
     quats = np.zeros((0, 10)) if quats.size == 0 else quats
-    return _quats_to_trans_rot_t(quats)
+    return quats
+
+
+ at verbose
+def filter_chpi(raw, include_line=True, verbose=None):
+    """Remove cHPI and line noise from data
+
+    .. note:: This function will only work properly if cHPI was on
+              during the recording.
+
+    Parameters
+    ----------
+    raw : instance of Raw
+        Raw data with cHPI information. Must be preloaded. Operates in-place.
+    include_line : bool
+        If True, also filter line noise.
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    Returns
+    -------
+    raw : instance of Raw
+        The raw data.
+
+    Notes
+    -----
+    cHPI signals are in general not stationary, because head movements act
+    like amplitude modulators on cHPI signals. Thus it is recommended to
+    to use this procedure, which uses an iterative fitting method, to
+    remove cHPI signals, as opposed to notch filtering.
+
+    .. versionadded:: 0.12
+    """
+    if not raw.preload:
+        raise RuntimeError('raw data must be preloaded')
+    t_window = 0.2
+    t_step = 0.01
+    n_step = int(np.ceil(t_step * raw.info['sfreq']))
+    hpi = _setup_chpi_fits(raw.info, t_window, t_window, exclude=(),
+                           add_hpi_stim_pick=False, remove_aliased=True,
+                           verbose=False)[0]
+    fit_idxs = np.arange(0, len(raw.times) + hpi['n_window'] // 2, n_step)
+    n_freqs = len(hpi['freqs'])
+    n_remove = 2 * n_freqs
+    meg_picks = hpi['picks']
+    n_times = len(raw.times)
+
+    msg = 'Removing %s cHPI' % n_freqs
+    if include_line:
+        n_remove += 2 * len(hpi['line_freqs'])
+        msg += ' and %s line harmonic' % len(hpi['line_freqs'])
+    msg += ' frequencies from %s MEG channels' % len(meg_picks)
+
+    proj = np.dot(hpi['model'][:, :n_remove], hpi['inv_model'][:n_remove]).T
+    logger.info(msg)
+    chunks = list()  # the chunks to subtract
+    last_endpt = 0
+    last_done = 0.
+    next_done = 60.
+    for ii, midpt in enumerate(fit_idxs):
+        if midpt / raw.info['sfreq'] >= next_done or ii == len(fit_idxs) - 1:
+            logger.info('    Filtering % 5.1f - % 5.1f sec'
+                        % (last_done, min(next_done, raw.times[-1])))
+            last_done = next_done
+            next_done += 60.
+        left_edge = midpt - hpi['n_window'] // 2
+        time_sl = slice(max(left_edge, 0),
+                        min(left_edge + hpi['n_window'], len(raw.times)))
+        this_len = time_sl.stop - time_sl.start
+        if this_len == hpi['n_window']:
+            this_proj = proj
+        else:  # first or last window
+            model = hpi['model'][:this_len]
+            inv_model = linalg.pinv(model)
+            this_proj = np.dot(model[:, :n_remove], inv_model[:n_remove]).T
+        this_data = raw._data[meg_picks, time_sl]
+        subt_pt = min(midpt + n_step, n_times)
+        if last_endpt != subt_pt:
+            fit_left_edge = left_edge - time_sl.start + hpi['n_window'] // 2
+            fit_sl = slice(fit_left_edge,
+                           fit_left_edge + (subt_pt - last_endpt))
+            chunks.append((subt_pt, np.dot(this_data, this_proj[:, fit_sl])))
+        last_endpt = subt_pt
+
+        # Consume (trailing) chunks that are now safe to remove because
+        # our windows will no longer touch them
+        if ii < len(fit_idxs) - 1:
+            next_left_edge = fit_idxs[ii + 1] - hpi['n_window'] // 2
+        else:
+            next_left_edge = np.inf
+        while len(chunks) > 0 and chunks[0][0] <= next_left_edge:
+            right_edge, chunk = chunks.pop(0)
+            raw._data[meg_picks,
+                      right_edge - chunk.shape[1]:right_edge] -= chunk
+    return raw
diff --git a/mne/commands/mne_browse_raw.py b/mne/commands/mne_browse_raw.py
index 409aabf..ecf59df 100755
--- a/mne/commands/mne_browse_raw.py
+++ b/mne/commands/mne_browse_raw.py
@@ -4,7 +4,7 @@
 You can do for example:
 
 $ mne browse_raw --raw sample_audvis_raw.fif \
-                 --proj sample_audvis_ecg_proj.fif \
+                 --proj sample_audvis_ecg-proj.fif \
                  --eve sample_audvis_raw-eve.fif
 """
 
@@ -84,7 +84,8 @@ def run():
         parser.print_help()
         sys.exit(1)
 
-    raw = mne.io.Raw(raw_in, preload=preload, allow_maxshield=maxshield)
+    raw = mne.io.read_raw_fif(raw_in, preload=preload,
+                              allow_maxshield=maxshield)
     if len(proj_in) > 0:
         projs = mne.read_proj(proj_in)
         raw.info['projs'] = projs
diff --git a/mne/commands/mne_bti2fiff.py b/mne/commands/mne_bti2fiff.py
index 98ccd05..ec57e55 100755
--- a/mne/commands/mne_bti2fiff.py
+++ b/mne/commands/mne_bti2fiff.py
@@ -1,6 +1,5 @@
 #!/usr/bin/env python
-"""
-Import BTi / 4D MagnesWH3600 data to fif file.
+"""Import BTi / 4D MagnesWH3600 data to fif file.
 
 example usage: mne bti2fiff --pdf C,rfDC -o my_raw.fif
 
diff --git a/mne/commands/mne_clean_eog_ecg.py b/mne/commands/mne_clean_eog_ecg.py
index 3aa9397..3606660 100755
--- a/mne/commands/mne_clean_eog_ecg.py
+++ b/mne/commands/mne_clean_eog_ecg.py
@@ -38,7 +38,7 @@ def clean_ecg_eog(in_fif_fname, out_fif_fname=None, eog=True, ecg=True,
         raise Exception("EOG and ECG cannot be both disabled")
 
     # Reading fif File
-    raw_in = mne.io.Raw(in_fif_fname)
+    raw_in = mne.io.read_raw_fif(in_fif_fname)
 
     if in_fif_fname.endswith('_raw.fif') or in_fif_fname.endswith('-raw.fif'):
         prefix = in_fif_fname[:-8]
diff --git a/mne/commands/mne_compute_proj_ecg.py b/mne/commands/mne_compute_proj_ecg.py
index 735a6db..238f212 100755
--- a/mne/commands/mne_compute_proj_ecg.py
+++ b/mne/commands/mne_compute_proj_ecg.py
@@ -174,20 +174,19 @@ def run():
     else:
         ecg_proj_fname = prefix + '_ecg-proj.fif'
 
-    raw = mne.io.Raw(raw_in, preload=preload)
+    raw = mne.io.read_raw_fif(raw_in, preload=preload)
 
     if raw_event_fname is not None:
-        raw_event = mne.io.Raw(raw_event_fname)
+        raw_event = mne.io.read_raw_fif(raw_event_fname)
     else:
         raw_event = raw
 
     flat = None  # XXX : not exposed to the user
-    cpe = mne.preprocessing.compute_proj_ecg
-    projs, events = cpe(raw, raw_event, tmin, tmax, n_grad, n_mag, n_eeg,
-                        l_freq, h_freq, average, filter_length, n_jobs,
-                        ch_name, reject, flat, bads, avg_ref, no_proj,
-                        event_id, ecg_l_freq, ecg_h_freq, tstart,
-                        qrs_threshold, copy=False)
+    projs, events = mne.preprocessing.compute_proj_ecg(
+        raw, raw_event, tmin, tmax, n_grad, n_mag, n_eeg, l_freq, h_freq,
+        average, filter_length, n_jobs, ch_name, reject, flat, bads, avg_ref,
+        no_proj, event_id, ecg_l_freq, ecg_h_freq, tstart, qrs_threshold,
+        copy=False)
 
     raw.close()
 
diff --git a/mne/commands/mne_compute_proj_eog.py b/mne/commands/mne_compute_proj_eog.py
index e48740b..0513ab4 100755
--- a/mne/commands/mne_compute_proj_eog.py
+++ b/mne/commands/mne_compute_proj_eog.py
@@ -157,10 +157,10 @@ def run():
     else:
         eog_proj_fname = prefix + '_eog-proj.fif'
 
-    raw = mne.io.Raw(raw_in, preload=preload)
+    raw = mne.io.read_raw_fif(raw_in, preload=preload)
 
     if raw_event_fname is not None:
-        raw_event = mne.io.Raw(raw_event_fname)
+        raw_event = mne.io.read_raw_fif(raw_event_fname)
     else:
         raw_event = raw
 
diff --git a/mne/commands/mne_coreg.py b/mne/commands/mne_coreg.py
index 42b58d8..1ead4e9 100644
--- a/mne/commands/mne_coreg.py
+++ b/mne/commands/mne_coreg.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # Authors: Christian Brodbeck  <christianbrodbeck at nyu.edu>
 
-""" Open the coregistration GUI.
+"""Open the coregistration GUI.
 
 example usage:  $ mne coreg
 
diff --git a/mne/commands/mne_freeview_bem_surfaces.py b/mne/commands/mne_freeview_bem_surfaces.py
index 16607e8..78b085b 100644
--- a/mne/commands/mne_freeview_bem_surfaces.py
+++ b/mne/commands/mne_freeview_bem_surfaces.py
@@ -76,7 +76,7 @@ def run():
                       help="Subjects directory", default=subjects_dir)
     parser.add_option("-m", "--method", dest="method",
                       help=("Method used to generate the BEM model. "
-                            "Can be flash or watershed."), metavar="FILE")
+                            "Can be flash or watershed."))
 
     options, args = parser.parse_args()
 
diff --git a/mne/commands/mne_kit2fiff.py b/mne/commands/mne_kit2fiff.py
index c013deb..2fcf086 100755
--- a/mne/commands/mne_kit2fiff.py
+++ b/mne/commands/mne_kit2fiff.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # Authors: Teon Brooks  <teon.brooks at gmail.com>
 
-""" Import KIT / NYU data to fif file.
+"""Import KIT / NYU data to fif file.
 
 example usage:  $ mne kit2fiff --input input.sqd --output output.fif
 Use without arguments to invoke GUI:  $ mne kt2fiff
diff --git a/mne/commands/mne_make_scalp_surfaces.py b/mne/commands/mne_make_scalp_surfaces.py
index af1bae7..9c9318e 100755
--- a/mne/commands/mne_make_scalp_surfaces.py
+++ b/mne/commands/mne_make_scalp_surfaces.py
@@ -6,8 +6,7 @@
 #
 #          simplified bsd-3 license
 
-"""
-Create high-resolution head surfaces for coordinate alignment.
+"""Create high-resolution head surfaces for coordinate alignment.
 
 example usage: mne make_scalp_surfaces --overwrite --subject sample
 """
diff --git a/mne/commands/mne_maxfilter.py b/mne/commands/mne_maxfilter.py
index dd5607c..4c64b9e 100755
--- a/mne/commands/mne_maxfilter.py
+++ b/mne/commands/mne_maxfilter.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-""" Apply MaxFilter
+"""Apply MaxFilter
 
 Example usage:
 
diff --git a/mne/commands/mne_show_info.py b/mne/commands/mne_show_info.py
new file mode 100644
index 0000000..aead8a9
--- /dev/null
+++ b/mne/commands/mne_show_info.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+"""Show measurement info from .fif file.
+
+You can do for example:
+
+$ mne show_info sample_audvis_raw.fif
+"""
+
+# Authors : Alexandre Gramfort, Ph.D.
+
+import sys
+import mne
+
+
+def run():
+    parser = mne.commands.utils.get_optparser(
+        __file__, usage='mne show_info <file>')
+    options, args = parser.parse_args()
+    if len(args) != 1:
+        parser.print_help()
+        sys.exit(1)
+
+    fname = args[0]
+
+    if not fname.endswith('.fif'):
+        raise ValueError('%s does not seem to be a .fif file.' % fname)
+
+    info = mne.io.read_info(fname)
+    print("File : %s" % fname)
+    print(info)
+
+is_main = (__name__ == '__main__')
+if is_main:
+    run()
diff --git a/mne/commands/mne_surf2bem.py b/mne/commands/mne_surf2bem.py
index dd822b0..9b40b8b 100755
--- a/mne/commands/mne_surf2bem.py
+++ b/mne/commands/mne_surf2bem.py
@@ -40,7 +40,7 @@ def run():
     points *= 1e-3
     surf = dict(coord_frame=5, id=int(options.id), nn=None, np=len(points),
                 ntri=len(tris), rr=points, sigma=1, tris=tris)
-    mne.write_bem_surface(options.fif, surf)
+    mne.write_bem_surfaces(options.fif, surf)
 
 
 is_main = (__name__ == '__main__')
diff --git a/mne/commands/tests/test_commands.py b/mne/commands/tests/test_commands.py
index 9b12c2b..55de6ca 100644
--- a/mne/commands/tests/test_commands.py
+++ b/mne/commands/tests/test_commands.py
@@ -11,7 +11,8 @@ from mne.commands import (mne_browse_raw, mne_bti2fiff, mne_clean_eog_ecg,
                           mne_coreg, mne_kit2fiff,
                           mne_make_scalp_surfaces, mne_maxfilter,
                           mne_report, mne_surf2bem, mne_watershed_bem,
-                          mne_compare_fiff, mne_flash_bem, mne_show_fiff)
+                          mne_compare_fiff, mne_flash_bem, mne_show_fiff,
+                          mne_show_info)
 from mne.utils import (run_tests_if_main, _TempDir, requires_mne, requires_PIL,
                        requires_mayavi, requires_tvtk, requires_freesurfer,
                        ArgvSetter, slow_test, ultra_slow_test)
@@ -209,7 +210,7 @@ def test_watershed_bem():
         mne_watershed_bem.run()
 
 
- at slow_test
+ at ultra_slow_test
 @requires_mne
 @requires_freesurfer
 @sample.requires_sample_data
@@ -242,4 +243,11 @@ def test_flash_bem():
     os.chdir(currdir)
 
 
+def test_show_info():
+    """Test mne show_info"""
+    check_usage(mne_show_info)
+    with ArgvSetter((raw_fname,)):
+        mne_show_info.run()
+
+
 run_tests_if_main()
diff --git a/mne/connectivity/spectral.py b/mne/connectivity/spectral.py
index 41beb75..273e8b4 100644
--- a/mne/connectivity/spectral.py
+++ b/mne/connectivity/spectral.py
@@ -3,7 +3,6 @@
 # License: BSD (3-clause)
 
 from ..externals.six import string_types
-from warnings import warn
 from inspect import getmembers
 
 import numpy as np
@@ -13,12 +12,12 @@ from .utils import check_indices
 from ..fixes import tril_indices, partial, _get_args
 from ..parallel import parallel_func
 from ..source_estimate import _BaseSourceEstimate
-from .. import Epochs
+from ..epochs import _BaseEpochs
 from ..time_frequency.multitaper import (dpss_windows, _mt_spectra,
                                          _psd_from_mt, _csd_from_mt,
                                          _psd_from_mt_adaptive)
 from ..time_frequency.tfr import morlet, cwt
-from ..utils import logger, verbose, _time_mask
+from ..utils import logger, verbose, _time_mask, warn
 
 ########################################################################
 # Various connectivity estimators
@@ -755,7 +754,7 @@ def spectral_connectivity(data, method='coh', indices=None, sfreq=2 * np.pi,
     # if none of the comp_con functions needs the PSD, we don't estimate it
     accumulate_psd = any(n == 5 for n in n_comp_args)
 
-    if isinstance(data, Epochs):
+    if isinstance(data, _BaseEpochs):
         times_in = data.times  # input times for Epochs input type
         sfreq = data.info['sfreq']
 
@@ -779,7 +778,7 @@ def spectral_connectivity(data, method='coh', indices=None, sfreq=2 * np.pi,
                                        endpoint=False)
 
             n_times_in = len(times_in)
-            mask = _time_mask(times_in, tmin, tmax)
+            mask = _time_mask(times_in, tmin, tmax, sfreq=sfreq)
             tmin_idx, tmax_idx = np.where(mask)[0][[0, -1]]
             tmax_idx += 1
             tmin_true = times_in[tmin_idx]
diff --git a/mne/cov.py b/mne/cov.py
index b5a71b2..3cbc947 100644
--- a/mne/cov.py
+++ b/mne/cov.py
@@ -5,12 +5,10 @@
 # License: BSD (3-clause)
 
 import copy as cp
-import os
-from math import floor, ceil, log
-import itertools as itt
-import warnings
-from copy import deepcopy
 from distutils.version import LooseVersion
+import itertools as itt
+from math import log
+import os
 
 import numpy as np
 from scipy import linalg
@@ -19,8 +17,8 @@ from .io.write import start_file, end_file
 from .io.proj import (make_projector, _proj_equal, activate_proj,
                       _needs_eeg_average_ref_proj)
 from .io import fiff_open
-from .io.pick import (pick_types, channel_indices_by_type, pick_channels_cov,
-                      pick_channels, pick_info, _picks_by_type)
+from .io.pick import (pick_types, pick_channels_cov, pick_channels, pick_info,
+                      _picks_by_type, _pick_data_channels)
 
 from .io.constants import FIFF
 from .io.meas_info import read_bad_channels
@@ -30,9 +28,12 @@ from .io.tree import dir_tree_find
 from .io.write import (start_block, end_block, write_int, write_name_list,
                        write_double, write_float_matrix, write_string)
 from .defaults import _handle_default
-from .epochs import _is_good
+from .epochs import Epochs
+from .event import make_fixed_length_events
 from .utils import (check_fname, logger, verbose, estimate_rank,
-                    _compute_row_norms, check_version, _time_mask)
+                    _compute_row_norms, check_version, _time_mask, warn,
+                    _check_copy_dep)
+from .fixes import in1d
 
 from .externals.six.moves import zip
 from .externals.six import string_types
@@ -51,8 +52,7 @@ def _check_covs_algebra(cov1, cov2):
 
 def _get_tslice(epochs, tmin, tmax):
     """get the slice."""
-    tstart, tend = None, None
-    mask = _time_mask(epochs.times, tmin, tmax)
+    mask = _time_mask(epochs.times, tmin, tmax, sfreq=epochs.info['sfreq'])
     tstart = np.where(mask)[0][0] if tmin is not None else None
     tend = np.where(mask)[0][-1] + 1 if tmax is not None else None
     tslice = slice(tstart, tend, None)
@@ -161,16 +161,17 @@ class Covariance(dict):
         cov : instance of Covariance
             The copied object.
         """
-        return deepcopy(self)
+        return cp.deepcopy(self)
 
-    def as_diag(self, copy=True):
+    def as_diag(self, copy=None):
         """Set covariance to be processed as being diagonal.
 
         Parameters
         ----------
         copy : bool
-            If True, return a modified copy of the covarince. If False,
-            the covariance is modified in place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
 
         Returns
         -------
@@ -182,12 +183,9 @@ class Covariance(dict):
         This function allows creation of inverse operators
         equivalent to using the old "--diagnoise" mne option.
         """
-        if self['diag'] is True:
-            return self.copy() if copy is True else self
-        if copy is True:
-            cov = cp.deepcopy(self)
-        else:
-            cov = self
+        cov = _check_copy_dep(self, copy, default=True)
+        if cov['diag']:
+            return cov
         cov['diag'] = True
         cov['data'] = np.diag(cov['data'])
         cov['eig'] = None
@@ -303,7 +301,7 @@ def make_ad_hoc_cov(info, verbose=None):
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         Measurement info.
     verbose : bool, str, int, or None (default None)
         If not None, override default verbose level (see mne.verbose).
@@ -342,35 +340,51 @@ def _check_n_samples(n_samples, n_chan):
     if n_samples <= 0:
         raise ValueError('No samples found to compute the covariance matrix')
     if n_samples < n_samples_min:
-        text = ('Too few samples (required : %d got : %d), covariance '
-                'estimate may be unreliable' % (n_samples_min, n_samples))
-        warnings.warn(text)
-        logger.warning(text)
+        warn('Too few samples (required : %d got : %d), covariance '
+             'estimate may be unreliable' % (n_samples_min, n_samples))
 
 
 @verbose
-def compute_raw_covariance(raw, tmin=None, tmax=None, tstep=0.2,
-                           reject=None, flat=None, picks=None,
-                           verbose=None):
+def compute_raw_covariance(raw, tmin=0, tmax=None, tstep=0.2, reject=None,
+                           flat=None, picks=None, method='empirical',
+                           method_params=None, cv=3, scalings=None, n_jobs=1,
+                           return_estimators=False, verbose=None):
     """Estimate noise covariance matrix from a continuous segment of raw data.
 
-    It is typically useful to estimate a noise covariance
-    from empty room data or time intervals before starting
-    the stimulation.
+    It is typically useful to estimate a noise covariance from empty room
+    data or time intervals before starting the stimulation.
+
+    .. note:: This function will:
+
+                  1. Partition the data into evenly spaced, equal-length
+                     epochs.
+                  2. Load them into memory.
+                  3. Subtract the mean across all time points and epochs
+                     for each channel.
+                  4. Process the :class:`Epochs` by
+                     :func:`compute_covariance`.
 
-    Note: To speed up the computation you should consider preloading raw data
-    by setting preload=True when reading the Raw data.
+              This will produce a slightly different result compared to
+              using :func:`make_fixed_length_events`, :class:`Epochs`, and
+              :func:`compute_covariance` directly, since that would (with
+              the recommended baseline correction) subtract the mean across
+              time *for each epoch* (instead of across epochs) for each
+              channel.
 
     Parameters
     ----------
     raw : instance of Raw
         Raw data
-    tmin : float | None (default None)
-        Beginning of time interval in seconds
+    tmin : float
+        Beginning of time interval in seconds. Defaults to 0.
     tmax : float | None (default None)
-        End of time interval in seconds
+        End of time interval in seconds. If None (default), use the end of the
+        recording.
     tstep : float (default 0.2)
         Length of data chunks for artefact rejection in seconds.
+        Can also be None to use a single epoch of (tmax - tmin)
+        duration. This can use a lot of memory for large ``Raw``
+        instances.
     reject : dict | None (default None)
         Rejection parameters based on peak-to-peak amplitude.
         Valid keys are 'grad' | 'mag' | 'eeg' | 'eog' | 'ecg'.
@@ -378,8 +392,8 @@ def compute_raw_covariance(raw, tmin=None, tmax=None, tstep=0.2,
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels)
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels)
                           )
 
     flat : dict | None (default None)
@@ -388,68 +402,112 @@ def compute_raw_covariance(raw, tmin=None, tmax=None, tstep=0.2,
         are floats that set the minimum acceptable peak-to-peak amplitude.
         If flat is None then no rejection is done.
     picks : array-like of int | None (default None)
-        Indices of channels to include (if None, all channels
-        except bad channels are used).
+        Indices of channels to include (if None, data channels are used).
+    method : str | list | None (default 'empirical')
+        The method used for covariance estimation.
+        See :func:`mne.compute_covariance`.
+
+        .. versionadded:: 0.12
+
+    method_params : dict | None (default None)
+        Additional parameters to the estimation procedure.
+        See :func:`mne.compute_covariance`.
+
+        .. versionadded:: 0.12
+
+    cv : int | sklearn cross_validation object (default 3)
+        The cross validation method. Defaults to 3, which will
+        internally trigger a default 3-fold shuffle split.
+
+        .. versionadded:: 0.12
+
+    scalings : dict | None (default None)
+        Defaults to ``dict(mag=1e15, grad=1e13, eeg=1e6)``.
+        These defaults will scale magnetometers and gradiometers
+        at the same unit.
+
+        .. versionadded:: 0.12
+
+    n_jobs : int (default 1)
+        Number of jobs to run in parallel.
+
+        .. versionadded:: 0.12
+
+    return_estimators : bool (default False)
+        Whether to return all estimators or the best. Only considered if
+        method equals 'auto' or is a list of str. Defaults to False
+
+        .. versionadded:: 0.12
+
     verbose : bool | str | int | None (default None)
         If not None, override default verbose level (see mne.verbose).
 
     Returns
     -------
-    cov : instance of Covariance
-        Noise covariance matrix.
+    cov : instance of Covariance | list
+        The computed covariance. If method equals 'auto' or is a list of str
+        and return_estimators equals True, a list of covariance estimators is
+        returned (sorted by log-likelihood, from high to low, i.e. from best
+        to worst).
 
     See Also
     --------
     compute_covariance : Estimate noise covariance matrix from epochs
     """
-    sfreq = raw.info['sfreq']
-
-    # Convert to samples
-    start = 0 if tmin is None else int(floor(tmin * sfreq))
-    if tmax is None:
-        stop = int(raw.last_samp - raw.first_samp)
-    else:
-        stop = int(ceil(tmax * sfreq))
-    step = int(ceil(tstep * raw.info['sfreq']))
+    tmin = 0. if tmin is None else float(tmin)
+    tmax = raw.times[-1] if tmax is None else float(tmax)
+    tstep = tmax - tmin if tstep is None else float(tstep)
+    tstep_m1 = tstep - 1. / raw.info['sfreq']  # inclusive!
+    events = make_fixed_length_events(raw, 1, tmin, tmax, tstep)
+    pl = 's' if len(events) != 1 else ''
+    logger.info('Using up to %s segment%s' % (len(events), pl))
 
     # don't exclude any bad channels, inverses expect all channels present
     if picks is None:
-        picks = pick_types(raw.info, meg=True, eeg=True, eog=False,
-                           ref_meg=False, exclude=[])
-
-    data = 0
-    n_samples = 0
-    mu = 0
-
-    info = pick_info(raw.info, picks)
-    idx_by_type = channel_indices_by_type(info)
-
-    # Read data in chunks
-    for first in range(start, stop, step):
-        last = first + step
-        if last >= stop:
-            last = stop
-        raw_segment, times = raw[picks, first:last]
-        if _is_good(raw_segment, info['ch_names'], idx_by_type, reject, flat,
-                    ignore_chs=info['bads']):
+        # Need to include all channels e.g. if eog rejection is to be used
+        picks = np.arange(raw.info['nchan'])
+        pick_mask = in1d(
+            picks, _pick_data_channels(raw.info, with_ref_meg=False))
+    else:
+        pick_mask = slice(None)
+    epochs = Epochs(raw, events, 1, 0, tstep_m1, baseline=None,
+                    picks=picks, reject=reject, flat=flat, verbose=False,
+                    preload=False, proj=False)
+    if isinstance(method, string_types) and method == 'empirical':
+        # potentially *much* more memory efficient to do it the iterative way
+        picks = picks[pick_mask]
+        data = 0
+        n_samples = 0
+        mu = 0
+        # Read data in chunks
+        for raw_segment in epochs:
+            raw_segment = raw_segment[pick_mask]
             mu += raw_segment.sum(axis=1)
             data += np.dot(raw_segment, raw_segment.T)
             n_samples += raw_segment.shape[1]
-        else:
-            logger.info("Artefact detected in [%d, %d]" % (first, last))
-
-    _check_n_samples(n_samples, len(picks))
-    mu /= n_samples
-    data -= n_samples * mu[:, None] * mu[None, :]
-    data /= (n_samples - 1.0)
-    logger.info("Number of samples used : %d" % n_samples)
-    logger.info('[done]')
-
-    ch_names = [raw.info['ch_names'][k] for k in picks]
-    bads = [b for b in raw.info['bads'] if b in ch_names]
-    projs = cp.deepcopy(raw.info['projs'])
-    # XXX : do not compute eig and eigvec now (think it's better...)
-    return Covariance(data, ch_names, bads, projs, nfree=n_samples)
+        _check_n_samples(n_samples, len(picks))
+        mu /= n_samples
+        data -= n_samples * mu[:, None] * mu[None, :]
+        data /= (n_samples - 1.0)
+        logger.info("Number of samples used : %d" % n_samples)
+        logger.info('[done]')
+        ch_names = [raw.info['ch_names'][k] for k in picks]
+        bads = [b for b in raw.info['bads'] if b in ch_names]
+        projs = cp.deepcopy(raw.info['projs'])
+        return Covariance(data, ch_names, bads, projs, nfree=n_samples)
+    del picks, pick_mask
+
+    # This makes it equivalent to what we used to do (and do above for
+    # empirical mode), treating all epochs as if they were a single long one
+    epochs.load_data()
+    ch_means = epochs._data.mean(axis=0).mean(axis=1)
+    epochs._data -= ch_means[np.newaxis, :, np.newaxis]
+    # fake this value so there are no complaints from compute_covariance
+    epochs.baseline = (None, None)
+    return compute_covariance(epochs, keep_sample_mean=True, method=method,
+                              method_params=method_params, cv=cv,
+                              scalings=scalings, n_jobs=n_jobs,
+                              return_estimators=return_estimators)
 
 
 @verbose
@@ -463,32 +521,34 @@ def compute_covariance(epochs, keep_sample_mean=True, tmin=None, tmax=None,
     when the stim onset is defined from events.
 
     If the covariance is computed for multiple event types (events
-    with different IDs), the following two options can be used and combined.
-    A) either an Epochs object for each event type is created and
-    a list of Epochs is passed to this function.
-    B) an Epochs object is created for multiple events and passed
-    to this function.
-
-    Note: Baseline correction should be used when creating the Epochs.
-          Otherwise the computed covariance matrix will be inaccurate.
-
-    Note: For multiple event types, it is also possible to create a
-          single Epochs object with events obtained using
-          merge_events(). However, the resulting covariance matrix
-          will only be correct if keep_sample_mean is True.
-
-    Note: The covariance can be unstable if the number of samples is not
-          sufficient. In that case it is common to regularize a covariance
-          estimate. The ``method`` parameter of this function allows to
-          regularize the covariance in an automated way. It also allows
-          to select between different alternative estimation algorithms which
-          themselves achieve regularization. Details are described in [1].
+    with different IDs), the following two options can be used and combined:
+
+        1. either an Epochs object for each event type is created and
+           a list of Epochs is passed to this function.
+        2. an Epochs object is created for multiple events and passed
+           to this function.
+
+    .. note:: Baseline correction should be used when creating the Epochs.
+              Otherwise the computed covariance matrix will be inaccurate.
+
+    .. note:: For multiple event types, it is also possible to create a
+              single Epochs object with events obtained using
+              merge_events(). However, the resulting covariance matrix
+              will only be correct if keep_sample_mean is True.
+
+    .. note:: The covariance can be unstable if the number of samples is
+              not sufficient. In that case it is common to regularize a
+              covariance estimate. The ``method`` parameter of this
+              function allows to regularize the covariance in an
+              automated way. It also allows to select between different
+              alternative estimation algorithms which themselves achieve
+              regularization. Details are described in [1]_.
 
     Parameters
     ----------
     epochs : instance of Epochs, or a list of Epochs objects
         The epochs.
-    keep_sample_mean : bool (default true)
+    keep_sample_mean : bool (default True)
         If False, the average response over epochs is computed for
         each event type and subtracted during the covariance
         computation. This is useful if the evoked response from a
@@ -508,22 +568,30 @@ def compute_covariance(epochs, keep_sample_mean=True, tmin=None, tmax=None,
         set of the different methods.
         If 'auto' or a list of methods, the best estimator will be determined
         based on log-likelihood and cross-validation on unseen data as
-        described in ref. [1]. Valid methods are:
-        'empirical', the empirical or sample covariance,
-        'diagonal_fixed', a diagonal regularization as in mne.cov.regularize
-        (see MNE manual), 'ledoit_wolf', the Ledoit-Wolf estimator (see [2]),
-        'shrunk' like 'ledoit_wolf' with cross-validation for optimal alpha
-        (see scikit-learn documentation on covariance estimation), 'pca',
-        probabilistic PCA with low rank
-        (see [3]), and, 'factor_analysis', Factor Analysis with low rank
-        (see [4]). If 'auto', expands to::
+        described in [1]_. Valid methods are:
+
+            * ``'empirical'``: the empirical or sample covariance
+            * ``'diagonal_fixed'``: a diagonal regularization as in
+              mne.cov.regularize (see MNE manual)
+            * ``'ledoit_wolf'``: the Ledoit-Wolf estimator [2]_
+            * ``'shrunk'``: like 'ledoit_wolf' with cross-validation for
+              optimal alpha (see scikit-learn documentation on covariance
+              estimation)
+            * ``'pca'``: probabilistic PCA with low rank [3]_
+            * ``'factor_analysis'``: Factor Analysis with low rank [4]_
+
+        If ``'auto'``, this expands to::
 
              ['shrunk', 'diagonal_fixed', 'empirical', 'factor_analysis']
 
-        Note. 'ledoit_wolf' and 'pca' are similar to 'shrunk' and
-        'factor_analysis', respectively. They are not included to avoid
-        redundancy. In most cases 'shrunk' and 'factor_analysis' represent
-        more appropriate default choices.
+        .. note:: ``'ledoit_wolf'`` and ``'pca'`` are similar to
+           ``'shrunk'`` and ``'factor_analysis'``, respectively. They are not
+           included to avoid redundancy. In most cases ``'shrunk'`` and
+           ``'factor_analysis'`` represent more appropriate default
+           choices.
+
+        The ``'auto'`` mode is not recommended if there are many
+        segments of data, since computation can take a long time.
 
         .. versionadded:: 0.9.0
 
@@ -571,17 +639,17 @@ def compute_covariance(epochs, keep_sample_mean=True, tmin=None, tmax=None,
 
     References
     ----------
-    [1] Engemann D. and Gramfort A. (2015) Automated model selection in
-        covariance estimation and spatial whitening of MEG and EEG signals,
-        vol. 108, 328-342, NeuroImage.
-    [2] Ledoit, O., Wolf, M., (2004). A well-conditioned estimator for
-        large-dimensional covariance matrices. Journal of Multivariate
-        Analysis 88 (2), 365 - 411.
-    [3] Tipping, M. E., Bishop, C. M., (1999). Probabilistic principal
-        component analysis. Journal of the Royal Statistical Society: Series
-        B (Statistical Methodology) 61 (3), 611 - 622.
-    [4] Barber, D., (2012). Bayesian reasoning and machine learning.
-        Cambridge University Press., Algorithm 21.1
+    .. [1] Engemann D. and Gramfort A. (2015) Automated model selection in
+           covariance estimation and spatial whitening of MEG and EEG
+           signals, vol. 108, 328-342, NeuroImage.
+    .. [2] Ledoit, O., Wolf, M., (2004). A well-conditioned estimator for
+           large-dimensional covariance matrices. Journal of Multivariate
+           Analysis 88 (2), 365 - 411.
+    .. [3] Tipping, M. E., Bishop, C. M., (1999). Probabilistic principal
+           component analysis. Journal of the Royal Statistical Society:
+           Series B (Statistical Methodology) 61 (3), 611 - 622.
+    .. [4] Barber, D., (2012). Bayesian reasoning and machine learning.
+           Cambridge University Press., Algorithm 21.1
     """
     accepted_methods = ('auto', 'empirical', 'diagonal_fixed', 'ledoit_wolf',
                         'shrunk', 'pca', 'factor_analysis',)
@@ -631,9 +699,10 @@ def compute_covariance(epochs, keep_sample_mean=True, tmin=None, tmax=None,
 
     # check for baseline correction
     for epochs_t in epochs:
-        if epochs_t.baseline is None and epochs_t.info['highpass'] < 0.5:
-            warnings.warn('Epochs are not baseline corrected, covariance '
-                          'matrix may be inaccurate')
+        if epochs_t.baseline is None and epochs_t.info['highpass'] < 0.5 and \
+                keep_sample_mean:
+            warn('Epochs are not baseline corrected, covariance '
+                 'matrix may be inaccurate')
 
     for epoch in epochs:
         epoch.info._check_consistency()
@@ -684,7 +753,7 @@ def compute_covariance(epochs, keep_sample_mean=True, tmin=None, tmax=None,
                                  ' if `keep_sample_mean` is False')
         # prepare mean covs
         n_epoch_types = len(epochs)
-        data_mean = list(np.zeros(n_epoch_types))
+        data_mean = [0] * n_epoch_types
         n_samples = np.zeros(n_epoch_types, dtype=np.int)
         n_epochs = np.zeros(n_epoch_types, dtype=np.int)
 
@@ -785,7 +854,10 @@ def _compute_covariance_auto(data, method, info, method_params, cv,
                              scalings, n_jobs, stop_early, picks_list,
                              verbose):
     """docstring for _compute_covariance_auto."""
-    from sklearn.grid_search import GridSearchCV
+    try:
+        from sklearn.model_selection import GridSearchCV
+    except Exception:  # XXX support sklearn < 0.18
+        from sklearn.grid_search import GridSearchCV
     from sklearn.covariance import (LedoitWolf, ShrunkCovariance,
                                     EmpiricalCovariance)
 
@@ -893,7 +965,9 @@ def _compute_covariance_auto(data, method, info, method_params, cv,
 def _logdet(A):
     """Compute the log det of a symmetric matrix."""
     vals = linalg.eigh(A)[0]
-    vals = np.abs(vals)  # avoid negative values (numerical errors)
+    # avoid negative (numerical errors) or zero (semi-definite matrix) values
+    tol = vals.max() * vals.size * np.finfo(np.float64).eps
+    vals = np.where(vals > tol, vals, tol)
     return np.sum(np.log(vals))
 
 
@@ -911,7 +985,11 @@ def _gaussian_loglik_scorer(est, X, y=None):
 
 def _cross_val(data, est, cv, n_jobs):
     """Helper to compute cross validation."""
-    from sklearn.cross_validation import cross_val_score
+    try:
+        from sklearn.model_selection import cross_val_score
+    except ImportError:
+        # XXX support sklearn < 0.18
+        from sklearn.cross_validation import cross_val_score
     return np.mean(cross_val_score(est, data, cv=cv, n_jobs=n_jobs,
                                    scoring=_gaussian_loglik_scorer))
 
@@ -939,8 +1017,8 @@ def _auto_low_rank_model(data, mode, n_jobs, method_params, cv,
     # make sure we don't empty the thing if it's a generator
     max_n = max(list(cp.deepcopy(iter_n_components)))
     if max_n > data.shape[1]:
-        warnings.warn('You are trying to estimate %i components on matrix '
-                      'with %i features.' % (max_n, data.shape[1]))
+        warn('You are trying to estimate %i components on matrix '
+             'with %i features.' % (max_n, data.shape[1]))
 
     for ii, n in enumerate(iter_n_components):
         est.n_components = n
@@ -1224,10 +1302,9 @@ def prepare_noise_cov(noise_cov, info, ch_names, rank=None,
         C_eeg_eig, C_eeg_eigvec = _get_ch_whitener(C_eeg, False, 'EEG',
                                                    rank_eeg)
     if _needs_eeg_average_ref_proj(info):
-        warnings.warn('No average EEG reference present in info["projs"], '
-                      'covariance may be adversely affected. Consider '
-                      'recomputing covariance using a raw file with an '
-                      'average eeg reference projector added.')
+        warn('No average EEG reference present in info["projs"], covariance '
+             'may be adversely affected. Consider recomputing covariance using'
+             ' a raw file with an average eeg reference projector added.')
 
     n_chan = len(ch_names)
     eigvec = np.zeros((n_chan, n_chan), dtype=np.float)
@@ -1432,7 +1509,7 @@ def _regularized_covariance(data, reg=None):
                               assume_centered=True)
             else:
                 raise ValueError("regularization parameter should be "
-                                 "'lwf' or 'oas'")
+                                 "'ledoit_wolf' or 'oas'")
         else:
             raise ValueError("regularization parameter should be "
                              "of type str or int (got %s)." % type(reg))
@@ -1443,6 +1520,7 @@ def _regularized_covariance(data, reg=None):
     return cov
 
 
+ at verbose
 def compute_whitener(noise_cov, info, picks=None, rank=None,
                      scalings=None, verbose=None):
     """Compute whitening matrix.
@@ -1806,15 +1884,15 @@ def _check_scaling_inputs(data, picks_list, scalings):
     return scalings_
 
 
-def _estimate_rank_meeg_signals(data, info, scalings, tol=1e-4,
-                                return_singular=False, copy=True):
+def _estimate_rank_meeg_signals(data, info, scalings, tol='auto',
+                                return_singular=False):
     """Estimate rank for M/EEG data.
 
     Parameters
     ----------
     data : np.ndarray of float, shape(n_channels, n_samples)
         The M/EEG signals.
-    info : mne.io.measurement_info.Info
+    info : Info
         The measurment info.
     scalings : dict | 'norm' | np.ndarray | None
         The rescaling method to be applied. If dict, it will override the
@@ -1824,6 +1902,8 @@ def _estimate_rank_meeg_signals(data, info, scalings, tol=1e-4,
 
         If 'norm' data will be scaled by channel-wise norms. If array,
         pre-specified norms will be used. If None, no scaling will be applied.
+    tol : float | str
+        Tolerance. See ``estimate_rank``.
     return_singular : bool
         If True, also return the singular values that were used
         to determine the rank.
@@ -1845,7 +1925,7 @@ def _estimate_rank_meeg_signals(data, info, scalings, tol=1e-4,
         ValueError("You've got fewer samples than channels, your "
                    "rank estimate might be inaccurate.")
     out = estimate_rank(data, tol=tol, norm=False,
-                        return_singular=return_singular, copy=copy)
+                        return_singular=return_singular)
     rank = out[0] if isinstance(out, tuple) else out
     ch_type = ' + '.join(list(zip(*picks_list))[0])
     logger.info('estimated rank (%s): %d' % (ch_type, rank))
@@ -1853,15 +1933,15 @@ def _estimate_rank_meeg_signals(data, info, scalings, tol=1e-4,
     return out
 
 
-def _estimate_rank_meeg_cov(data, info, scalings, tol=1e-4,
-                            return_singular=False, copy=True):
+def _estimate_rank_meeg_cov(data, info, scalings, tol='auto',
+                            return_singular=False):
     """Estimate rank for M/EEG data.
 
     Parameters
     ----------
     data : np.ndarray of float, shape (n_channels, n_channels)
         The M/EEG covariance.
-    info : mne.io.measurement_info.Info
+    info : Info
         The measurment info.
     scalings : dict | 'norm' | np.ndarray | None
         The rescaling method to be applied. If dict, it will override the
@@ -1871,12 +1951,11 @@ def _estimate_rank_meeg_cov(data, info, scalings, tol=1e-4,
 
         If 'norm' data will be scaled by channel-wise norms. If array,
         pre-specified norms will be used. If None, no scaling will be applied.
+    tol : float | str
+        Tolerance. See ``estimate_rank``.
     return_singular : bool
         If True, also return the singular values that were used
         to determine the rank.
-    copy : bool
-        If False, values in data will be modified in-place during
-        rank estimation (saves memory).
 
     Returns
     -------
@@ -1893,7 +1972,7 @@ def _estimate_rank_meeg_cov(data, info, scalings, tol=1e-4,
         ValueError("You've got fewer samples than channels, your "
                    "rank estimate might be inaccurate.")
     out = estimate_rank(data, tol=tol, norm=False,
-                        return_singular=return_singular, copy=copy)
+                        return_singular=return_singular)
     rank = out[0] if isinstance(out, tuple) else out
     ch_type = ' + '.join(list(zip(*picks_list))[0])
     logger.info('estimated rank (%s): %d' % (ch_type, rank))
diff --git a/mne/cuda.py b/mne/cuda.py
index e17b0be..02ae626 100644
--- a/mne/cuda.py
+++ b/mne/cuda.py
@@ -3,9 +3,9 @@
 # License: BSD (3-clause)
 
 import numpy as np
-from scipy.fftpack import fft, ifft
+from scipy.fftpack import fft, ifft, rfft, irfft
 
-from .utils import sizeof_fmt, logger, get_config
+from .utils import sizeof_fmt, logger, get_config, warn
 
 
 # Support CUDA for FFTs; requires scikits.cuda and pycuda
@@ -34,7 +34,7 @@ def get_cuda_memory():
         The amount of available memory as a human-readable string.
     """
     if not _cuda_capable:
-        logger.warning('CUDA not enabled, returning zero for memory')
+        warn('CUDA not enabled, returning zero for memory')
         mem = 0
     else:
         from pycuda.driver import mem_get_info
@@ -67,20 +67,19 @@ def init_cuda(ignore_config=False):
         from pycuda import gpuarray, driver  # noqa
         from pycuda.elementwise import ElementwiseKernel
     except ImportError:
-        logger.warning('module pycuda not found, CUDA not enabled')
+        warn('module pycuda not found, CUDA not enabled')
         return
     try:
         # Initialize CUDA; happens with importing autoinit
         import pycuda.autoinit  # noqa
     except ImportError:
-        logger.warning('pycuda.autoinit could not be imported, likely '
-                       'a hardware error, CUDA not enabled')
+        warn('pycuda.autoinit could not be imported, likely a hardware error, '
+             'CUDA not enabled')
         return
     # Make sure scikit-cuda is installed
     cudafft = _get_cudafft()
     if cudafft is None:
-        logger.warning('module scikit-cuda not found, CUDA not '
-                       'enabled')
+        warn('module scikit-cuda not found, CUDA not enabled')
         return
 
     # let's construct our own CUDA multiply in-place function
@@ -96,8 +95,7 @@ def init_cuda(ignore_config=False):
     try:
         cudafft.Plan(16, np.float64, np.complex128)  # will get auto-GC'ed
     except:
-        logger.warning('Device does not support 64-bit FFTs, '
-                       'CUDA not enabled')
+        warn('Device does not support 64-bit FFTs, CUDA not enabled')
         return
     _cuda_capable = True
     # Figure out limit for CUDA FFT calculations
@@ -171,10 +169,10 @@ def setup_cuda_fft_multiply_repeated(n_jobs, h_fft):
                     x_fft=gpuarray.empty(cuda_fft_len, np.complex128),
                     x=gpuarray.empty(int(n_fft), np.float64))
                 logger.info('Using CUDA for FFT FIR filtering')
-            except Exception:
+            except Exception as exp:
                 logger.info('CUDA not used, could not instantiate memory '
-                            '(arrays may be too large), falling back to '
-                            'n_jobs=1')
+                            '(arrays may be too large: "%s"), falling back to '
+                            'n_jobs=1' % str(exp))
         else:
             logger.info('CUDA not used, CUDA could not be initialized, '
                         'falling back to n_jobs=1')
@@ -297,7 +295,7 @@ def setup_cuda_fft_resample(n_jobs, W, new_len):
     return n_jobs, cuda_dict, W
 
 
-def fft_resample(x, W, new_len, npad, to_remove,
+def fft_resample(x, W, new_len, npads, to_removes,
                  cuda_dict=dict(use_cuda=False)):
     """Do FFT resampling with a filter function (possibly using CUDA)
 
@@ -309,9 +307,10 @@ def fft_resample(x, W, new_len, npad, to_remove,
         The filtering function to apply.
     new_len : int
         The size of the output array (before removing padding).
-    npad : int
-        Amount of padding to apply before resampling.
-    to_remove : int
+    npads : tuple of int
+        Amount of padding to apply to the start and end of the
+        signal before resampling.
+    to_removes : tuple of int
         Number of samples to remove after resampling.
     cuda_dict : dict
         Dictionary constructed using setup_cuda_multiply_repeated().
@@ -324,18 +323,28 @@ def fft_resample(x, W, new_len, npad, to_remove,
     # add some padding at beginning and end to make this work a little cleaner
     if x.dtype != np.float64:
         x = x.astype(np.float64)
-    x = _smart_pad(x, npad)
+    x = _smart_pad(x, npads)
     old_len = len(x)
     shorter = new_len < old_len
     if not cuda_dict['use_cuda']:
         N = int(min(new_len, old_len))
-        sl_1 = slice((N + 1) // 2)
-        y_fft = np.zeros(new_len, np.complex128)
-        x_fft = fft(x).ravel() * W
+        # The below is equivalent to this, but faster
+        # sl_1 = slice((N + 1) // 2)
+        # y_fft = np.zeros(new_len, np.complex128)
+        # x_fft = fft(x).ravel() * W
+        # y_fft[sl_1] = x_fft[sl_1]
+        # sl_2 = slice(-(N - 1) // 2, None)
+        # y_fft[sl_2] = x_fft[sl_2]
+        # y = np.real(ifft(y_fft, overwrite_x=True)).ravel()
+        x_fft = rfft(x).ravel()
+        x_fft *= W[np.arange(1, len(x) + 1) // 2].real
+        y_fft = np.zeros(new_len, np.float64)
+        sl_1 = slice(N)
         y_fft[sl_1] = x_fft[sl_1]
-        sl_2 = slice(-(N - 1) // 2, None)
-        y_fft[sl_2] = x_fft[sl_2]
-        y = np.real(ifft(y_fft, overwrite_x=True)).ravel()
+        if min(new_len, old_len) % 2 == 0:
+            if new_len > old_len:
+                y_fft[N - 1] /= 2.
+        y = irfft(y_fft, overwrite_x=True).ravel()
     else:
         cudafft = _get_cudafft()
         cuda_dict['x'].set(np.concatenate((x, np.zeros(max(new_len - old_len,
@@ -358,10 +367,10 @@ def fft_resample(x, W, new_len, npad, to_remove,
         y = cuda_dict['x'].get()[:new_len if shorter else None]
 
     # now let's trim it back to the correct size (if there was padding)
-    if to_remove > 0:
+    if (to_removes > 0).any():
         keep = np.ones((new_len), dtype='bool')
-        keep[:to_remove] = False
-        keep[-to_remove:] = False
+        keep[:to_removes[0]] = False
+        keep[-to_removes[1]:] = False
         y = np.compress(keep, y)
 
     return y
@@ -374,11 +383,12 @@ def fft_resample(x, W, new_len, npad, to_remove,
 def _smart_pad(x, n_pad):
     """Pad vector x
     """
-    if n_pad == 0:
+    if (n_pad == 0).all():
         return x
-    elif n_pad < 0:
+    elif (n_pad < 0).any():
         raise RuntimeError('n_pad must be non-negative')
     # need to pad with zeros if len(x) <= npad
-    z_pad = np.zeros(max(n_pad - len(x) + 1, 0), dtype=x.dtype)
-    return np.concatenate([z_pad, 2 * x[0] - x[n_pad:0:-1], x,
-                           2 * x[-1] - x[-2:-n_pad - 2:-1], z_pad])
+    l_z_pad = np.zeros(max(n_pad[0] - len(x) + 1, 0), dtype=x.dtype)
+    r_z_pad = np.zeros(max(n_pad[0] - len(x) + 1, 0), dtype=x.dtype)
+    return np.concatenate([l_z_pad, 2 * x[0] - x[n_pad[0]:0:-1], x,
+                           2 * x[-1] - x[-2:-n_pad[1] - 2:-1], r_z_pad])
diff --git a/mne/data/coil_def.dat b/mne/data/coil_def.dat
index dc4eee6..13bd7b4 100644
--- a/mne/data/coil_def.dat
+++ b/mne/data/coil_def.dat
@@ -33,7 +33,7 @@
 #
 #       Produced with:
 #
-#	mne_list_coil_def version 1.12 compiled at Nov 19 2014 04:19:15
+#	mne_list_coil_def version 1.12 compiled at Jan 13 2015 18:20:15
 #
 3   2       0   2  2.789e-02  1.620e-02	"Neuromag-122 planar gradiometer size = 27.89  mm base = 16.20  mm"
  61.7284  8.100e-03  0.000e+00  0.000e+00  0.000  0.000  1.000
@@ -459,3 +459,30 @@
  -1.6667 -2.321e-02 -2.475e-02  0.000e+00  0.000  0.000  1.000
  -1.6667 -5.179e-02  2.475e-02  0.000e+00  0.000  0.000  1.000
  -1.6667 -5.179e-02 -2.475e-02  0.000e+00  0.000  0.000  1.000
+2   9001    0   2  2.000e-02  5.000e-02	"KRISS system gradiometer size = 20.00  mm base = 50.00  mm"
+  1.0000  0.000e+00  0.000e+00  0.000e+00  0.000  0.000  1.000
+ -1.0000  0.000e+00  0.000e+00  5.000e-02  0.000  0.000  1.000
+2   9001    1   8  2.000e-02  5.000e-02	"KRISS system gradiometer size = 20.00  mm base = 50.00  mm"
+  0.2500  5.000e-03  5.000e-03  0.000e+00  0.000  0.000  1.000
+  0.2500 -5.000e-03  5.000e-03  0.000e+00  0.000  0.000  1.000
+  0.2500 -5.000e-03 -5.000e-03  0.000e+00  0.000  0.000  1.000
+  0.2500  5.000e-03 -5.000e-03  0.000e+00  0.000  0.000  1.000
+ -0.2500  5.000e-03  5.000e-03  5.000e-02  0.000  0.000  1.000
+ -0.2500 -5.000e-03  5.000e-03  5.000e-02  0.000  0.000  1.000
+ -0.2500 -5.000e-03 -5.000e-03  5.000e-02  0.000  0.000  1.000
+ -0.2500  5.000e-03 -5.000e-03  5.000e-02  0.000  0.000  1.000
+2   9001    2  14  2.000e-02  5.000e-02	"KRISS system gradiometer size = 20.00  mm base = 50.00  mm"
+  0.2500  0.000e+00  0.000e+00  0.000e+00  0.000  0.000  1.000
+  0.1250  8.165e-03  0.000e+00  0.000e+00  0.000  0.000  1.000
+  0.1250 -8.165e-03  0.000e+00  0.000e+00  0.000  0.000  1.000
+  0.1250  4.082e-03  7.071e-03  0.000e+00  0.000  0.000  1.000
+  0.1250  4.082e-03 -7.071e-03  0.000e+00  0.000  0.000  1.000
+  0.1250 -4.082e-03  7.071e-03  0.000e+00  0.000  0.000  1.000
+  0.1250 -4.082e-03 -7.071e-03  0.000e+00  0.000  0.000  1.000
+ -0.2500  0.000e+00  0.000e+00  5.000e-02  0.000  0.000  1.000
+ -0.1250  8.165e-03  0.000e+00  5.000e-02  0.000  0.000  1.000
+ -0.1250 -8.165e-03  0.000e+00  5.000e-02  0.000  0.000  1.000
+ -0.1250  4.082e-03  7.071e-03  5.000e-02  0.000  0.000  1.000
+ -0.1250  4.082e-03 -7.071e-03  5.000e-02  0.000  0.000  1.000
+ -0.1250 -4.082e-03  7.071e-03  5.000e-02  0.000  0.000  1.000
+ -0.1250 -4.082e-03 -7.071e-03  5.000e-02  0.000  0.000  1.000
diff --git a/mne/datasets/__init__.py b/mne/datasets/__init__.py
index e0530bd..bc86467 100644
--- a/mne/datasets/__init__.py
+++ b/mne/datasets/__init__.py
@@ -1,11 +1,13 @@
 """Demo datasets
 """
 
-from . import sample
-from . import megsim
-from . import spm_face
 from . import brainstorm
 from . import eegbci
+from . import megsim
+from . import misc
+from . import sample
 from . import somato
+from . import spm_face
 from . import testing
 from . import _fake
+from .utils import _download_all_example_data
diff --git a/mne/datasets/brainstorm/bst_auditory.py b/mne/datasets/brainstorm/bst_auditory.py
index 2cbe827..af8bcc9 100644
--- a/mne/datasets/brainstorm/bst_auditory.py
+++ b/mne/datasets/brainstorm/bst_auditory.py
@@ -30,7 +30,7 @@ URL: http://neuroimage.usc.edu/brainstorm/DatasetAuditory
 @verbose
 def data_path(path=None, force_update=False, update_path=True, download=True,
               verbose=None):
-    archive_name = dict(brainstorm='bst_auditory.tar.bz2')
+    archive_name = dict(brainstorm='bst_auditory.tar.gz')
     data_path = _data_path(path=path, force_update=force_update,
                            update_path=update_path, name='brainstorm',
                            download=download, archive_name=archive_name)
diff --git a/mne/datasets/brainstorm/bst_raw.py b/mne/datasets/brainstorm/bst_raw.py
index 1033008..dc3a187 100644
--- a/mne/datasets/brainstorm/bst_raw.py
+++ b/mne/datasets/brainstorm/bst_raw.py
@@ -28,7 +28,7 @@ URL: http://neuroimage.usc.edu/brainstorm/DatasetMedianNerveCtf
 @verbose
 def data_path(path=None, force_update=False, update_path=True, download=True,
               verbose=None):
-    archive_name = dict(brainstorm='bst_raw.tar.bz2')
+    archive_name = dict(brainstorm='bst_raw.tar.gz')
     data_path = _data_path(path=path, force_update=force_update,
                            update_path=update_path, name='brainstorm',
                            download=download, archive_name=archive_name)
diff --git a/mne/datasets/brainstorm/bst_resting.py b/mne/datasets/brainstorm/bst_resting.py
index 3d33652..8e999e0 100644
--- a/mne/datasets/brainstorm/bst_resting.py
+++ b/mne/datasets/brainstorm/bst_resting.py
@@ -21,7 +21,7 @@ URL: http://neuroimage.usc.edu/brainstorm/DatasetResting
 @verbose
 def data_path(path=None, force_update=False, update_path=True, download=True,
               verbose=None):
-    archive_name = dict(brainstorm='bst_resting.tar.bz2')
+    archive_name = dict(brainstorm='bst_resting.tar.gz')
     data_path = _data_path(path=path, force_update=force_update,
                            update_path=update_path, name='brainstorm',
                            download=download, archive_name=archive_name)
diff --git a/mne/datasets/megsim/megsim.py b/mne/datasets/megsim/megsim.py
index 44e77fb..88338cd 100644
--- a/mne/datasets/megsim/megsim.py
+++ b/mne/datasets/megsim/megsim.py
@@ -18,7 +18,7 @@ def data_path(url, path=None, force_update=False, update_path=None,
     """Get path to local copy of MEGSIM dataset URL
 
     This is a low-level function useful for getting a local copy of a
-    remote MEGSIM dataet.
+    remote MEGSIM dataset.
 
     Parameters
     ----------
diff --git a/mne/datasets/megsim/urls.py b/mne/datasets/megsim/urls.py
index 409e60f..c073b78 100644
--- a/mne/datasets/megsim/urls.py
+++ b/mne/datasets/megsim/urls.py
@@ -170,3 +170,10 @@ def url_match(condition, data_format, data_type):
                          'data_format="%s", data_type="%s"'
                          % (condition, data_format, data_type))
     return good_urls
+
+
+def _load_all_data():
+    """Helper for downloading all megsim datasets."""
+    from .megsim import data_path
+    for url in urls:
+        data_path(url_root + url)
diff --git a/mne/datasets/misc/__init__.py b/mne/datasets/misc/__init__.py
new file mode 100644
index 0000000..195a1a9
--- /dev/null
+++ b/mne/datasets/misc/__init__.py
@@ -0,0 +1,4 @@
+"""MNE misc dataset
+"""
+
+from ._misc import data_path
diff --git a/mne/datasets/misc/_misc.py b/mne/datasets/misc/_misc.py
new file mode 100644
index 0000000..22dac64
--- /dev/null
+++ b/mne/datasets/misc/_misc.py
@@ -0,0 +1,18 @@
+# Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
+#          Martin Luessi <mluessi at nmr.mgh.harvard.edu>
+#          Eric Larson <larson.eric.d at gmail.com>
+# License: BSD Style.
+
+from ...utils import verbose
+from ..utils import _data_path, _data_path_doc
+
+
+ at verbose
+def data_path(path=None, force_update=False, update_path=True,
+              download=True, verbose=None):
+    return _data_path(path=path, force_update=force_update,
+                      update_path=update_path, name='misc',
+                      download=download)
+
+data_path.__doc__ = _data_path_doc.format(name='misc',
+                                          conf='MNE_DATASETS_MISC_PATH')
diff --git a/mne/datasets/spm_face/__init__.py b/mne/datasets/spm_face/__init__.py
index 90f01c7..5da98fd 100644
--- a/mne/datasets/spm_face/__init__.py
+++ b/mne/datasets/spm_face/__init__.py
@@ -1,4 +1,4 @@
 """SPM face dataset
 """
 
-from .spm_data import data_path, has_spm_data, get_version
+from .spm_data import data_path, has_spm_data, get_version, requires_spm_data
diff --git a/mne/datasets/spm_face/spm_data.py b/mne/datasets/spm_face/spm_data.py
index 19c6461..8fea978 100644
--- a/mne/datasets/spm_face/spm_data.py
+++ b/mne/datasets/spm_face/spm_data.py
@@ -2,7 +2,9 @@
 #
 # License: BSD Style.
 
-from ...utils import verbose
+import numpy as np
+
+from ...utils import verbose, get_config
 from ...fixes import partial
 from ..utils import (has_dataset, _data_path, _data_path_doc,
                      _get_version, _version_doc)
@@ -26,3 +28,13 @@ def get_version():
     return _get_version('spm')
 
 get_version.__doc__ = _version_doc.format(name='spm')
+
+
+def _skip_spm_data():
+    skip_testing = (get_config('MNE_SKIP_TESTING_DATASET_TESTS', 'false') ==
+                    'true')
+    skip = skip_testing or not has_spm_data()
+    return skip
+
+requires_spm_data = np.testing.dec.skipif(_skip_spm_data,
+                                          'Requires spm dataset')
diff --git a/mne/datasets/testing/__init__.py b/mne/datasets/testing/__init__.py
index 7fa74ee..c12ea77 100644
--- a/mne/datasets/testing/__init__.py
+++ b/mne/datasets/testing/__init__.py
@@ -1,4 +1,4 @@
-"""MNE sample dataset
+"""MNE testing dataset
 """
 
 from ._testing import data_path, requires_testing_data, get_version
diff --git a/mne/datasets/utils.py b/mne/datasets/utils.py
index ab6fa88..41047a3 100644
--- a/mne/datasets/utils.py
+++ b/mne/datasets/utils.py
@@ -8,11 +8,11 @@ import os
 import os.path as op
 import shutil
 import tarfile
-from warnings import warn
 import stat
+import sys
 
 from .. import __version__ as mne_version
-from ..utils import get_config, set_config, _fetch_file, logger
+from ..utils import get_config, set_config, _fetch_file, logger, warn, verbose
 from ..externals.six import string_types
 from ..externals.six.moves import input
 
@@ -95,32 +95,32 @@ def _dataset_version(path, name):
 def _get_path(path, key, name):
     """Helper to get a dataset path"""
     if path is None:
-            # use an intelligent guess if it's not defined
-            def_path = op.realpath(op.join(op.dirname(__file__), '..', '..',
-                                           '..', 'examples'))
-            if get_config(key) is None:
-                key = 'MNE_DATA'
-            path = get_config(key, def_path)
-
-            # use the same for all datasets
-            if not op.exists(path) or not os.access(path, os.W_OK):
+        # use an intelligent guess if it's not defined
+        def_path = op.realpath(op.join(op.dirname(__file__), '..', '..',
+                                       'examples'))
+        if get_config(key) is None:
+            key = 'MNE_DATA'
+        path = get_config(key, def_path)
+
+        # use the same for all datasets
+        if not op.exists(path) or not os.access(path, os.W_OK):
+            try:
+                os.mkdir(path)
+            except OSError:
                 try:
-                    os.mkdir(path)
+                    logger.info('Checking for %s data in '
+                                '"~/mne_data"...' % name)
+                    path = op.join(op.expanduser("~"), "mne_data")
+                    if not op.exists(path):
+                        logger.info("Trying to create "
+                                    "'~/mne_data' in home directory")
+                        os.mkdir(path)
                 except OSError:
-                    try:
-                        logger.info('Checking for %s data in '
-                                    '"~/mne_data"...' % name)
-                        path = op.join(op.expanduser("~"), "mne_data")
-                        if not op.exists(path):
-                            logger.info("Trying to create "
-                                        "'~/mne_data' in home directory")
-                            os.mkdir(path)
-                    except OSError:
-                        raise OSError("User does not have write permissions "
-                                      "at '%s', try giving the path as an "
-                                      "argument to data_path() where user has "
-                                      "write permissions, for ex:data_path"
-                                      "('/home/xyz/me2/')" % (path))
+                    raise OSError("User does not have write permissions "
+                                  "at '%s', try giving the path as an "
+                                  "argument to data_path() where user has "
+                                  "write permissions, for ex:data_path"
+                                  "('/home/xyz/me2/')" % (path))
     if not isinstance(path, string_types):
         raise ValueError('path must be a string or None')
     return path
@@ -132,10 +132,13 @@ def _do_path_update(path, update_path, key, name):
     if update_path is None:
         if get_config(key, '') != path:
             update_path = True
-            msg = ('Do you want to set the path:\n    %s\nas the default '
-                   '%s dataset path in the mne-python config [y]/n? '
-                   % (path, name))
-            answer = input(msg)
+            if '--update-dataset-path' in sys.argv:
+                answer = 'y'
+            else:
+                msg = ('Do you want to set the path:\n    %s\nas the default '
+                       '%s dataset path in the mne-python config [y]/n? '
+                       % (path, name))
+                answer = input(msg)
             if answer.lower() == 'n':
                 update_path = False
         else:
@@ -151,59 +154,68 @@ def _data_path(path=None, force_update=False, update_path=True, download=True,
                archive_name=None):
     """Aux function
     """
-    key = {'sample': 'MNE_DATASETS_SAMPLE_PATH',
-           'spm': 'MNE_DATASETS_SPM_FACE_PATH',
-           'somato': 'MNE_DATASETS_SOMATO_PATH',
-           'brainstorm': 'MNE_DATASETS_BRAINSTORM_PATH',
-           'testing': 'MNE_DATASETS_TESTING_PATH',
-           'fake': 'MNE_DATASETS_FAKE_PATH',
-           }[name]
+    key = {
+        'fake': 'MNE_DATASETS_FAKE_PATH',
+        'misc': 'MNE_DATASETS_MISC_PATH',
+        'sample': 'MNE_DATASETS_SAMPLE_PATH',
+        'spm': 'MNE_DATASETS_SPM_FACE_PATH',
+        'somato': 'MNE_DATASETS_SOMATO_PATH',
+        'brainstorm': 'MNE_DATASETS_BRAINSTORM_PATH',
+        'testing': 'MNE_DATASETS_TESTING_PATH',
+    }[name]
 
     path = _get_path(path, key, name)
-    # To update the testing dataset, push commits, then make a new release
-    # on GitHub. Then update the "testing_release" variable:
-    testing_release = '0.11'
+    # To update the testing or misc dataset, push commits, then make a new
+    # release on GitHub. Then update the "releases" variable:
+    releases = dict(testing='0.19', misc='0.1')
     # And also update the "hashes['testing']" variable below.
 
     # To update any other dataset, update the data archive itself (upload
     # an updated version) and update the hash.
     archive_names = dict(
+        misc='mne-misc-data-%s.tar.gz' % releases['misc'],
         sample='MNE-sample-data-processed.tar.gz',
-        spm='MNE-spm-face.tar.bz2',
         somato='MNE-somato-data.tar.gz',
-        testing='mne-testing-data-%s.tar.gz' % testing_release,
+        spm='MNE-spm-face.tar.gz',
+        testing='mne-testing-data-%s.tar.gz' % releases['testing'],
         fake='foo.tgz',
     )
     if archive_name is not None:
         archive_names.update(archive_name)
     folder_names = dict(
+        brainstorm='MNE-brainstorm-data',
+        fake='foo',
+        misc='MNE-misc-data',
         sample='MNE-sample-data',
-        spm='MNE-spm-face',
         somato='MNE-somato-data',
-        brainstorm='MNE-brainstorm-data',
+        spm='MNE-spm-face',
         testing='MNE-testing-data',
-        fake='foo',
     )
     urls = dict(
-        sample="https://s3.amazonaws.com/mne-python/datasets/%s",
-        spm='https://s3.amazonaws.com/mne-python/datasets/%s',
-        somato='https://s3.amazonaws.com/mne-python/datasets/%s',
-        brainstorm='https://copy.com/ZTHXXFcuIZycvRoA/brainstorm/%s',
-        testing='https://codeload.github.com/mne-tools/mne-testing-data/'
-                'tar.gz/%s' % testing_release,
+        brainstorm='https://mne-tools.s3.amazonaws.com/datasets/'
+                   'MNE-brainstorm-data/%s',
         fake='https://github.com/mne-tools/mne-testing-data/raw/master/'
              'datasets/%s',
+        misc='https://codeload.github.com/mne-tools/mne-misc-data/'
+             'tar.gz/%s' % releases['misc'],
+        sample="https://mne-tools.s3.amazonaws.com/datasets/%s",
+        somato='https://mne-tools.s3.amazonaws.com/datasets/%s',
+        spm='https://mne-tools.s3.amazonaws.com/datasets/%s',
+        testing='https://codeload.github.com/mne-tools/mne-testing-data/'
+                'tar.gz/%s' % releases['testing'],
     )
     hashes = dict(
-        sample='ccf5cbc41a3727ed02821330a07abb13',
-        spm='3e9e83c642136e5b720e2ecc5dcc3244',
-        somato='f3e3a8441477bb5bacae1d0c6e0964fb',
         brainstorm=None,
-        testing='d1753ce154e0e6af12f1b82b21e975ce',
         fake='3194e9f7b46039bb050a74f3e1ae9908',
+        misc='f0708d8914cf2692fee7b6c9f105e71c',
+        sample='1d5da3a809fded1ef5734444ab5bf857',
+        somato='f3e3a8441477bb5bacae1d0c6e0964fb',
+        spm='f61041e3f3f2ba0def8a2ca71592cc41',
+        testing='77b2a435d80adb23cbe7e19144e7bc47'
     )
     folder_origs = dict(  # not listed means None
-        testing='mne-testing-data-%s' % testing_release,
+        misc='mne-misc-data-%s' % releases['misc'],
+        testing='mne-testing-data-%s' % releases['testing'],
     )
     folder_name = folder_names[name]
     archive_name = archive_names[name]
@@ -226,7 +238,10 @@ def _data_path(path=None, force_update=False, update_path=True, download=True,
         return ''
     if not op.exists(folder_path) or force_update:
         if name == 'brainstorm':
-            answer = input('%sAgree (y/[n])? ' % _bst_license_text)
+            if '--accept-brainstorm-license' in sys.argv:
+                answer = 'y'
+            else:
+                answer = input('%sAgree (y/[n])? ' % _bst_license_text)
             if answer.lower() != 'y':
                 raise RuntimeError('You must agree to the license to use this '
                                    'dataset')
@@ -299,7 +314,7 @@ def _data_path(path=None, force_update=False, update_path=True, download=True,
     try:
         from distutils.version import LooseVersion as LV
     except:
-        warn('Could not determine %s dataset version; dataset could\n'
+        warn('Could not determine %s dataset version; dataset could '
              'be out of date. Please install the "distutils" package.'
              % name)
     else:  # 0.7 < 0.7.git shoud be False, therefore strip
@@ -321,16 +336,52 @@ def _get_version(name):
 
 def has_dataset(name):
     """Helper for dataset presence"""
-    endswith = {'sample': 'MNE-sample-data',
-                'spm': 'MNE-spm-face',
-                'somato': 'MNE-somato-data',
-                'testing': 'MNE-testing-data',
-                'fake': 'foo',
-                'brainstorm': 'MNE_brainstorm-data',
-                }[name]
+    endswith = {
+        'brainstorm': 'MNE_brainstorm-data',
+        'fake': 'foo',
+        'misc': 'MNE-misc-data',
+        'sample': 'MNE-sample-data',
+        'somato': 'MNE-somato-data',
+        'spm': 'MNE-spm-face',
+        'testing': 'MNE-testing-data',
+    }[name]
     archive_name = None
     if name == 'brainstorm':
         archive_name = dict(brainstorm='bst_raw')
     dp = _data_path(download=False, name=name, check_version=False,
                     archive_name=archive_name)
     return dp.endswith(endswith)
+
+
+ at verbose
+def _download_all_example_data(verbose=True):
+    """Helper to download all datasets used in examples and tutorials"""
+    # This function is designed primarily to be used by CircleCI. It has
+    # verbose=True by default so we get nice status messages
+    from . import (sample, testing, misc, spm_face, somato, brainstorm, megsim,
+                   eegbci)
+    sample.data_path()
+    testing.data_path()
+    misc.data_path()
+    spm_face.data_path()
+    somato.data_path()
+    sys.argv += ['--accept-brainstorm-license']
+    try:
+        brainstorm.bst_raw.data_path()
+        brainstorm.bst_auditory.data_path()
+    finally:
+        sys.argv.pop(-1)
+    sys.argv += ['--update-dataset-path']
+    try:
+        megsim.load_data(condition='visual', data_format='single-trial',
+                         data_type='simulation')
+        megsim.load_data(condition='visual', data_format='raw',
+                         data_type='experimental')
+        megsim.load_data(condition='visual', data_format='evoked',
+                         data_type='simulation')
+    finally:
+        sys.argv.pop(-1)
+    url_root = 'http://www.physionet.org/physiobank/database/eegmmidb/'
+    eegbci.data_path(url_root + 'S001/S001R06.edf', update_path=True)
+    eegbci.data_path(url_root + 'S001/S001R10.edf', update_path=True)
+    eegbci.data_path(url_root + 'S001/S001R14.edf', update_path=True)
diff --git a/mne/decoding/base.py b/mne/decoding/base.py
index 1c9d4df..8e65dcb 100644
--- a/mne/decoding/base.py
+++ b/mne/decoding/base.py
@@ -352,7 +352,8 @@ class LinearModel(BaseEstimator):
         ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
             The channel type to plot. For 'grad', the gradiometers are
             collected in pairs and the RMS for each pair is plotted.
-            If None, then channels are chosen in the order given above.
+            If None, then first available channel type from order given
+            above is used. Defaults to None.
         layout : None | Layout
             Layout instance specifying sensor positions (does not need to be
             specified for Neuromag data). If possible, the correct layout file
@@ -408,7 +409,7 @@ class LinearModel(BaseEstimator):
             Title. If None (default), no title is displayed.
         mask : ndarray of bool, shape (n_channels, n_times) | None
             The channels to be marked as significant at a given time point.
-            Indicies set to `True` will be considered. Defaults to None.
+            Indices set to `True` will be considered. Defaults to None.
         mask_params : dict | None
             Additional plotting parameters for plotting significant sensors.
             Default (None) equals::
@@ -501,7 +502,8 @@ class LinearModel(BaseEstimator):
         ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
             The channel type to plot. For 'grad', the gradiometers are
             collected in pairs and the RMS for each pair is plotted.
-            If None, then channels are chosen in the order given above.
+            If None, then first available channel type from order given
+            above is used. Defaults to None.
         layout : None | Layout
             Layout instance specifying sensor positions (does not need to be
             specified for Neuromag data). If possible, the correct layout file
@@ -557,7 +559,7 @@ class LinearModel(BaseEstimator):
             Title. If None (default), no title is displayed.
         mask : ndarray of bool, shape (n_channels, n_times) | None
             The channels to be marked as significant at a given time point.
-            Indicies set to `True` will be considered. Defaults to None.
+            Indices set to `True` will be considered. Defaults to None.
         mask_params : dict | None
             Additional plotting parameters for plotting significant sensors.
             Default (None) equals::
diff --git a/mne/decoding/csp.py b/mne/decoding/csp.py
index c34c67a..ade2177 100644
--- a/mne/decoding/csp.py
+++ b/mne/decoding/csp.py
@@ -220,7 +220,8 @@ class CSP(TransformerMixin, EstimatorMixin):
         ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
             The channel type to plot. For 'grad', the gradiometers are
             collected in pairs and the RMS for each pair is plotted.
-            If None, then channels are chosen in the order given above.
+            If None, then first available channel type from order given
+            above is used. Defaults to None.
         layout : None | Layout
             Layout instance specifying sensor positions (does not need to be
             specified for Neuromag data). If possible, the correct layout file
@@ -276,7 +277,7 @@ class CSP(TransformerMixin, EstimatorMixin):
             Title. If None (default), no title is displayed.
         mask : ndarray of bool, shape (n_channels, n_times) | None
             The channels to be marked as significant at a given time point.
-            Indicies set to `True` will be considered. Defaults to None.
+            Indices set to `True` will be considered. Defaults to None.
         mask_params : dict | None
             Additional plotting parameters for plotting significant sensors.
             Default (None) equals::
@@ -364,7 +365,8 @@ class CSP(TransformerMixin, EstimatorMixin):
         ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
             The channel type to plot. For 'grad', the gradiometers are
             collected in pairs and the RMS for each pair is plotted.
-            If None, then channels are chosen in the order given above.
+            If None, then first available channel type from order given
+            above is used. Defaults to None.
         layout : None | Layout
             Layout instance specifying sensor positions (does not need to be
             specified for Neuromag data). If possible, the correct layout file
@@ -420,7 +422,7 @@ class CSP(TransformerMixin, EstimatorMixin):
             Title. If None (default), no title is displayed.
         mask : ndarray of bool, shape (n_channels, n_times) | None
             The channels to be marked as significant at a given time point.
-            Indicies set to `True` will be considered. Defaults to None.
+            Indices set to `True` will be considered. Defaults to None.
         mask_params : dict | None
             Additional plotting parameters for plotting significant sensors.
             Default (None) equals::
diff --git a/mne/decoding/ems.py b/mne/decoding/ems.py
index d41cdbc..6064086 100644
--- a/mne/decoding/ems.py
+++ b/mne/decoding/ems.py
@@ -68,7 +68,7 @@ def compute_ems(epochs, conditions=None, picks=None, n_jobs=1, verbose=None):
     else:
         epochs = epochs[conditions]
 
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
 
     if len(conditions) != 2:
         raise ValueError('Currently this function expects exactly 2 '
@@ -76,7 +76,7 @@ def compute_ems(epochs, conditions=None, picks=None, n_jobs=1, verbose=None):
                          len(conditions))
 
     ev = epochs.events[:, 2]
-    # special care to avoid path dependant mappings and orders
+    # special care to avoid path dependent mappings and orders
     conditions = list(sorted(conditions))
     cond_idx = [np.where(ev == epochs.event_id[k])[0] for k in conditions]
 
@@ -92,11 +92,22 @@ def compute_ems(epochs, conditions=None, picks=None, n_jobs=1, verbose=None):
                 this_picks = pick_types(info, meg=ch_type, eeg=False)
             data[:, this_picks] /= np.std(data[:, this_picks])
 
-    from sklearn.cross_validation import LeaveOneOut
+    try:
+        from sklearn.model_selection import LeaveOneOut
+    except:  # XXX support sklearn < 0.18
+        from sklearn.cross_validation import LeaveOneOut
+
+    def _iter_cv(n):  # XXX support sklearn < 0.18
+        if hasattr(LeaveOneOut, 'split'):
+            cv = LeaveOneOut()
+            return cv.split(np.zeros((n, 1)))
+        else:
+            cv = LeaveOneOut(len(data))
+            return cv
 
     parallel, p_func, _ = parallel_func(_run_ems, n_jobs=n_jobs)
     out = parallel(p_func(_ems_diff, data, cond_idx, train, test)
-                   for train, test in LeaveOneOut(len(data)))
+                   for train, test in _iter_cv(len(data)))
 
     surrogate_trials, spatial_filter = zip(*out)
     surrogate_trials = np.array(surrogate_trials)
diff --git a/mne/decoding/tests/test_csp.py b/mne/decoding/tests/test_csp.py
index cbb84e1..09f80a5 100644
--- a/mne/decoding/tests/test_csp.py
+++ b/mne/decoding/tests/test_csp.py
@@ -27,13 +27,14 @@ start, stop = 0, 8
 def test_csp():
     """Test Common Spatial Patterns algorithm on epochs
     """
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
-    picks = picks[2:9:3]
+    picks = picks[2:9:3]  # subselect channels -> disable proj!
+    raw.add_proj([], remove_existing=True)
     epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0), preload=True)
+                    baseline=(None, 0), preload=True, proj=False)
     epochs_data = epochs.get_data()
     n_channels = epochs_data.shape[1]
 
@@ -59,7 +60,7 @@ def test_csp():
     sources = csp.transform(epochs_data)
     assert_true(sources.shape[1] == n_components)
 
-    epochs.pick_types(meg='mag', copy=False)
+    epochs.pick_types(meg='mag')
 
     # test plot patterns
     components = np.arange(n_components)
@@ -73,8 +74,10 @@ def test_csp():
     # test covariance estimation methods (results should be roughly equal)
     csp_epochs = CSP(cov_est="epoch")
     csp_epochs.fit(epochs_data, y)
-    assert_array_almost_equal(csp.filters_, csp_epochs.filters_, -1)
-    assert_array_almost_equal(csp.patterns_, csp_epochs.patterns_, -1)
+    for attr in ('filters_', 'patterns_'):
+        corr = np.corrcoef(getattr(csp, attr).ravel(),
+                           getattr(csp_epochs, attr).ravel())[0, 1]
+        assert_true(corr >= 0.95, msg='%s < 0.95' % corr)
 
     # make sure error is raised for undefined estimation method
     csp_fail = CSP(cov_est="undefined")
@@ -85,7 +88,7 @@ def test_csp():
 def test_regularized_csp():
     """Test Common Spatial Patterns algorithm using regularized covariance
     """
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
diff --git a/mne/decoding/tests/test_ems.py b/mne/decoding/tests/test_ems.py
index e3abce6..6a05618 100644
--- a/mne/decoding/tests/test_ems.py
+++ b/mne/decoding/tests/test_ems.py
@@ -23,7 +23,7 @@ event_id = dict(aud_l=1, vis_l=3)
 @requires_sklearn
 def test_ems():
     """Test event-matched spatial filters"""
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
 
     # create unequal number of events
     events = read_events(event_name)
@@ -34,7 +34,7 @@ def test_ems():
     epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                     baseline=(None, 0), preload=True)
     assert_raises(ValueError, compute_ems, epochs, ['aud_l', 'vis_l'])
-    epochs.equalize_event_counts(epochs.event_id, copy=False)
+    epochs = epochs.equalize_event_counts(epochs.event_id, copy=False)[0]
 
     assert_raises(KeyError, compute_ems, epochs, ['blah', 'hahah'])
     surrogates, filters, conditions = compute_ems(epochs)
@@ -44,7 +44,7 @@ def test_ems():
     event_id2 = dict(aud_l=1, aud_r=2, vis_l=3)
     epochs = Epochs(raw, events, event_id2, tmin, tmax, picks=picks,
                     baseline=(None, 0), preload=True)
-    epochs.equalize_event_counts(epochs.event_id, copy=False)
+    epochs = epochs.equalize_event_counts(epochs.event_id, copy=False)[0]
 
     n_expected = sum([len(epochs[k]) for k in ['aud_l', 'vis_l']])
 
diff --git a/mne/decoding/tests/test_time_gen.py b/mne/decoding/tests/test_time_gen.py
index 07a6286..fff4059 100644
--- a/mne/decoding/tests/test_time_gen.py
+++ b/mne/decoding/tests/test_time_gen.py
@@ -12,7 +12,7 @@ from numpy.testing import assert_array_equal
 
 from mne import io, Epochs, read_events, pick_types
 from mne.utils import (requires_sklearn, requires_sklearn_0_15, slow_test,
-                       run_tests_if_main)
+                       run_tests_if_main, check_version)
 from mne.decoding import GeneralizationAcrossTime, TimeDecoding
 
 
@@ -24,9 +24,11 @@ tmin, tmax = -0.2, 0.5
 event_id = dict(aud_l=1, vis_l=3)
 event_id_gen = dict(aud_l=2, vis_l=4)
 
+warnings.simplefilter('always')
+
 
 def make_epochs():
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg='mag', stim=False, ecg=False,
                        eog=False, exclude='bads')
@@ -46,13 +48,37 @@ def test_generalization_across_time():
     """Test time generalization decoding
     """
     from sklearn.svm import SVC
-    from sklearn.linear_model import RANSACRegressor, LinearRegression
+    from sklearn.base import is_classifier
+    # KernelRidge is used for testing 1) regression analyses 2) n-dimensional
+    # predictions.
+    from sklearn.kernel_ridge import KernelRidge
     from sklearn.preprocessing import LabelEncoder
-    from sklearn.metrics import mean_squared_error
-    from sklearn.cross_validation import LeaveOneLabelOut
+    from sklearn.metrics import roc_auc_score, mean_squared_error
 
     epochs = make_epochs()
-
+    y_4classes = np.hstack((epochs.events[:7, 2], epochs.events[7:, 2] + 1))
+    if check_version('sklearn', '0.18'):
+        from sklearn.model_selection import (KFold, StratifiedKFold,
+                                             ShuffleSplit, LeaveOneLabelOut)
+        cv_shuffle = ShuffleSplit()
+        cv = LeaveOneLabelOut()
+        # XXX we cannot pass any other parameters than X and y to cv.split
+        # so we have to build it before hand
+        cv_lolo = [(train, test) for train, test in cv.split(
+                   X=y_4classes, y=y_4classes, labels=y_4classes)]
+
+        # With sklearn >= 0.17, `clf` can be identified as a regressor, and
+        # the scoring metrics can therefore be automatically assigned.
+        scorer_regress = None
+    else:
+        from sklearn.cross_validation import (KFold, StratifiedKFold,
+                                              ShuffleSplit, LeaveOneLabelOut)
+        cv_shuffle = ShuffleSplit(len(epochs))
+        cv_lolo = LeaveOneLabelOut(y_4classes)
+
+        # With sklearn < 0.17, `clf` cannot be identified as a regressor, and
+        # therefore the scoring metrics cannot be automatically assigned.
+        scorer_regress = mean_squared_error
     # Test default running
     gat = GeneralizationAcrossTime(picks='foo')
     assert_equal("<GAT | no fit, no prediction, no score>", "%s" % gat)
@@ -70,11 +96,33 @@ def test_generalization_across_time():
     assert_equal("<GAT | fitted, start : -0.200 (s), stop : 0.499 (s), no "
                  "prediction, no score>", '%s' % gat)
     assert_equal(gat.ch_names, epochs.ch_names)
+    # test different predict function:
+    gat = GeneralizationAcrossTime(predict_method='decision_function')
+    gat.fit(epochs)
+    # With classifier, the default cv is StratifiedKFold
+    assert_true(gat.cv_.__class__ == StratifiedKFold)
     gat.predict(epochs)
+    assert_array_equal(np.shape(gat.y_pred_), (15, 15, 14, 1))
+    gat.predict_method = 'predict_proba'
+    gat.predict(epochs)
+    assert_array_equal(np.shape(gat.y_pred_), (15, 15, 14, 2))
+    gat.predict_method = 'foo'
+    assert_raises(NotImplementedError, gat.predict, epochs)
+    gat.predict_method = 'predict'
+    gat.predict(epochs)
+    assert_array_equal(np.shape(gat.y_pred_), (15, 15, 14, 1))
     assert_equal("<GAT | fitted, start : -0.200 (s), stop : 0.499 (s), "
                  "predicted 14 epochs, no score>",
                  "%s" % gat)
     gat.score(epochs)
+    assert_true(gat.scorer_.__name__ == 'accuracy_score')
+    # check clf / predict_method combinations for which the scoring metrics
+    # cannot be inferred.
+    gat.scorer = None
+    gat.predict_method = 'decision_function'
+    assert_raises(ValueError, gat.score, epochs)
+    # Check specifying y manually
+    gat.predict_method = 'predict'
     gat.score(epochs, y=epochs.events[:, 2])
     gat.score(epochs, y=epochs.events[:, 2].tolist())
     assert_equal("<GAT | fitted, start : -0.200 (s), stop : 0.499 (s), "
@@ -123,6 +171,24 @@ def test_generalization_across_time():
     assert_true(len(gat.test_times_['slices'][0]) == 15 ==
                 np.shape(gat.scores_)[1])
 
+    # Test score_mode
+    gat.score_mode = 'foo'
+    assert_raises(ValueError, gat.score, epochs)
+    gat.score_mode = 'fold-wise'
+    scores = gat.score(epochs)
+    assert_array_equal(np.shape(scores), [15, 15, 5])
+    gat.score_mode = 'mean-sample-wise'
+    scores = gat.score(epochs)
+    assert_array_equal(np.shape(scores), [15, 15])
+    gat.score_mode = 'mean-fold-wise'
+    scores = gat.score(epochs)
+    assert_array_equal(np.shape(scores), [15, 15])
+    gat.predict_mode = 'mean-prediction'
+    with warnings.catch_warnings(record=True) as w:
+        gat.score(epochs)
+        assert_true(any("score_mode changed from " in str(ww.message)
+                        for ww in w))
+
     # Test longer time window
     gat = GeneralizationAcrossTime(train_times={'length': .100})
     with warnings.catch_warnings(record=True):
@@ -130,24 +196,23 @@ def test_generalization_across_time():
     assert_true(gat is gat2)  # return self
     assert_true(hasattr(gat2, 'cv_'))
     assert_true(gat2.cv_ != gat.cv)
-    scores = gat.score(epochs)
-    assert_true(isinstance(scores, list))  # type check
+    with warnings.catch_warnings(record=True):  # not vectorizing
+        scores = gat.score(epochs)
+    assert_true(isinstance(scores, np.ndarray))  # type check
     assert_equal(len(scores[0]), len(scores))  # shape check
-
     assert_equal(len(gat.test_times_['slices'][0][0]), 2)
     # Decim training steps
     gat = GeneralizationAcrossTime(train_times={'step': .100})
     with warnings.catch_warnings(record=True):
         gat.fit(epochs)
-
     gat.score(epochs)
     assert_true(len(gat.scores_) == len(gat.estimators_) == 8)  # training time
     assert_equal(len(gat.scores_[0]), 15)  # testing time
 
     # Test start stop training & test cv without n_fold params
     y_4classes = np.hstack((epochs.events[:7, 2], epochs.events[7:, 2] + 1))
-    gat = GeneralizationAcrossTime(cv=LeaveOneLabelOut(y_4classes),
-                                   train_times={'start': 0.090, 'stop': 0.250})
+    train_times = dict(start=0.090, stop=0.250)
+    gat = GeneralizationAcrossTime(cv=cv_lolo, train_times=train_times)
     # predict without fit
     assert_raises(RuntimeError, gat.predict, epochs)
     with warnings.catch_warnings(record=True):
@@ -159,22 +224,26 @@ def test_generalization_across_time():
 
     # Test score without passing epochs & Test diagonal decoding
     gat = GeneralizationAcrossTime(test_times='diagonal')
-    with warnings.catch_warnings(record=True):
+    with warnings.catch_warnings(record=True):  # not vectorizing
         gat.fit(epochs)
     assert_raises(RuntimeError, gat.score)
-    gat.predict(epochs)
+    with warnings.catch_warnings(record=True):  # not vectorizing
+        gat.predict(epochs)
     scores = gat.score()
     assert_true(scores is gat.scores_)
     assert_equal(np.shape(gat.scores_), (15, 1))
     assert_array_equal([tim for ttime in gat.test_times_['times']
                         for tim in ttime], gat.train_times_['times'])
-
+    from mne.utils import set_log_level
+    set_log_level('error')
     # Test generalization across conditions
-    gat = GeneralizationAcrossTime(predict_mode='mean-prediction')
+    gat = GeneralizationAcrossTime(predict_mode='mean-prediction', cv=2)
     with warnings.catch_warnings(record=True):
         gat.fit(epochs[0:6])
-    gat.predict(epochs[7:])
-    gat.score(epochs[7:])
+    with warnings.catch_warnings(record=True):
+        # There are some empty test folds because of n_trials
+        gat.predict(epochs[7:])
+        gat.score(epochs[7:])
 
     # Test training time parameters
     gat_ = copy.deepcopy(gat)
@@ -194,27 +263,50 @@ def test_generalization_across_time():
     # Test testing time parameters
     # --- outside time range
     gat.test_times = dict(start=-999.)
-    assert_raises(ValueError, gat.predict, epochs)
+    with warnings.catch_warnings(record=True):  # no epochs in fold
+        assert_raises(ValueError, gat.predict, epochs)
     gat.test_times = dict(start=999.)
-    assert_raises(ValueError, gat.predict, epochs)
+    with warnings.catch_warnings(record=True):  # no test epochs
+        assert_raises(ValueError, gat.predict, epochs)
     # --- impossible slices
     gat.test_times = dict(step=.000001)
-    assert_raises(ValueError, gat.predict, epochs)
+    with warnings.catch_warnings(record=True):  # no test epochs
+        assert_raises(ValueError, gat.predict, epochs)
     gat_ = copy.deepcopy(gat)
     gat_.train_times_['length'] = .000001
     gat_.test_times = dict(length=.000001)
-    assert_raises(ValueError, gat_.predict, epochs)
+    with warnings.catch_warnings(record=True):  # no test epochs
+        assert_raises(ValueError, gat_.predict, epochs)
     # --- test time region of interest
     gat.test_times = dict(step=.150)
-    gat.predict(epochs)
+    with warnings.catch_warnings(record=True):  # not vectorizing
+        gat.predict(epochs)
     assert_array_equal(np.shape(gat.y_pred_), (15, 5, 14, 1))
     # --- silly value
     gat.test_times = 'foo'
-    assert_raises(ValueError, gat.predict, epochs)
+    with warnings.catch_warnings(record=True):  # no test epochs
+        assert_raises(ValueError, gat.predict, epochs)
     assert_raises(RuntimeError, gat.score)
     # --- unmatched length between training and testing time
     gat.test_times = dict(length=.150)
     assert_raises(ValueError, gat.predict, epochs)
+    # --- irregular length training and testing times
+    # 2 estimators, the first one is trained on two successive time samples
+    # whereas the second one is trained on a single time sample.
+    train_times = dict(slices=[[0, 1], [1]])
+    # The first estimator is tested once, the second estimator is tested on
+    # two successive time samples.
+    test_times = dict(slices=[[[0, 1]], [[0], [1]]])
+    gat = GeneralizationAcrossTime(train_times=train_times,
+                                   test_times=test_times)
+    gat.fit(epochs)
+    with warnings.catch_warnings(record=True):  # not vectorizing
+        gat.score(epochs)
+    assert_array_equal(np.shape(gat.y_pred_[0]), [1, len(epochs), 1])
+    assert_array_equal(np.shape(gat.y_pred_[1]), [2, len(epochs), 1])
+    # check cannot Automatically infer testing times for adhoc training times
+    gat.test_times = None
+    assert_raises(ValueError, gat.predict, epochs)
 
     svc = SVC(C=1, kernel='linear', probability=True)
     gat = GeneralizationAcrossTime(clf=svc, predict_mode='mean-prediction')
@@ -226,28 +318,42 @@ def test_generalization_across_time():
     # and http://bit.ly/1u7t8UT
     assert_raises(ValueError, gat.score, epochs2)
     gat.score(epochs)
-    scores = sum(scores, [])  # flatten
     assert_true(0.0 <= np.min(scores) <= 1.0)
     assert_true(0.0 <= np.max(scores) <= 1.0)
 
     # Test that gets error if train on one dataset, test on another, and don't
     # specify appropriate cv:
-    gat = GeneralizationAcrossTime()
+    gat = GeneralizationAcrossTime(cv=cv_shuffle)
+    gat.fit(epochs)
     with warnings.catch_warnings(record=True):
         gat.fit(epochs)
 
     gat.predict(epochs)
-    assert_raises(IndexError, gat.predict, epochs[:10])
-
-    # TODO JRK: test GAT with non-exhaustive CV (eg. train on 80%, test on 10%)
+    assert_raises(ValueError, gat.predict, epochs[:10])
+
+    # Make CV with some empty train and test folds:
+    # --- empty test fold(s) should warn when gat.predict()
+    gat._cv_splits[0] = [gat._cv_splits[0][0], np.empty(0)]
+    with warnings.catch_warnings(record=True) as w:
+        gat.predict(epochs)
+        assert_true(len(w) > 0)
+        assert_true(any('do not have any test epochs' in str(ww.message)
+                        for ww in w))
+    # --- empty train fold(s) should raise when gat.fit()
+    gat = GeneralizationAcrossTime(cv=[([0], [1]), ([], [0])])
+    assert_raises(ValueError, gat.fit, epochs[:2])
 
     # Check that still works with classifier that output y_pred with
     # shape = (n_trials, 1) instead of (n_trials,)
-    gat = GeneralizationAcrossTime(clf=RANSACRegressor(LinearRegression()),
-                                   cv=2)
-    epochs.crop(None, epochs.times[2])
-    gat.fit(epochs)
-    gat.predict(epochs)
+    if check_version('sklearn', '0.17'):  # no is_regressor before v0.17
+        gat = GeneralizationAcrossTime(clf=KernelRidge(), cv=2)
+        epochs.crop(None, epochs.times[2])
+        gat.fit(epochs)
+        # With regression the default cv is KFold and not StratifiedKFold
+        assert_true(gat.cv_.__class__ == KFold)
+        gat.score(epochs)
+        # with regression the default scoring metrics is mean squared error
+        assert_true(gat.scorer_.__name__ == 'mean_squared_error')
 
     # Test combinations of complex scenarios
     # 2 or more distinct classes
@@ -258,32 +364,52 @@ def test_generalization_across_time():
     y[len(y) // 2:] += 2
     ys = (y, y + 1000)
     # Univariate and multivariate prediction
-    svc = SVC(C=1, kernel='linear')
+    svc = SVC(C=1, kernel='linear', probability=True)
+    reg = KernelRidge()
 
-    class SVC_proba(SVC):
-        def predict(self, x):
-            probas = super(SVC_proba, self).predict_proba(x)
-            return probas[:, 0]
+    def scorer_proba(y_true, y_pred):
+        return roc_auc_score(y_true, y_pred[:, 0])
 
-    svcp = SVC_proba(C=1, kernel='linear', probability=True)
-    clfs = [svc, svcp]
-    scorers = [None, mean_squared_error]
+    # We re testing 3 scenario: default, classifier + predict_proba, regressor
+    scorers = [None, scorer_proba, scorer_regress]
+    predict_methods = [None, 'predict_proba', None]
+    clfs = [svc, svc, reg]
     # Test all combinations
-    for clf, scorer in zip(clfs, scorers):
+    for clf, predict_method, scorer in zip(clfs, predict_methods, scorers):
         for y in ys:
             for n_class in n_classes:
-                y_ = y % n_class
-                with warnings.catch_warnings(record=True):
-                    gat = GeneralizationAcrossTime(cv=2, clf=clf,
-                                                   scorer=scorer)
-                    gat.fit(epochs, y=y_)
-                    gat.score(epochs, y=y_)
+                for predict_mode in ['cross-validation', 'mean-prediction']:
+                    # Cannot use AUC for n_class > 2
+                    if (predict_method == 'predict_proba' and n_class != 2):
+                        continue
+
+                    y_ = y % n_class
+
+                    with warnings.catch_warnings(record=True):
+                        gat = GeneralizationAcrossTime(
+                            cv=2, clf=clf, scorer=scorer,
+                            predict_mode=predict_mode)
+                        gat.fit(epochs, y=y_)
+                        gat.score(epochs, y=y_)
+
+                    # Check that scorer is correctly defined manually and
+                    # automatically.
+                    scorer_name = gat.scorer_.__name__
+                    if scorer is None:
+                        if is_classifier(clf):
+                            assert_equal(scorer_name, 'accuracy_score')
+                        else:
+                            assert_equal(scorer_name, 'mean_squared_error')
+                    else:
+                        assert_equal(scorer_name, scorer.__name__)
 
 
 @requires_sklearn
 def test_decoding_time():
     """Test TimeDecoding
     """
+    from sklearn.svm import SVR
+    from sklearn.cross_validation import KFold
     epochs = make_epochs()
     tg = TimeDecoding()
     assert_equal("<TimeDecoding | no fit, no prediction, no score>", '%s' % tg)
@@ -296,16 +422,39 @@ def test_decoding_time():
     assert_true(not hasattr(tg, 'train_times_'))
     assert_true(not hasattr(tg, 'test_times_'))
     assert_raises(RuntimeError, tg.score, epochs=None)
-    tg.predict(epochs)
+    with warnings.catch_warnings(record=True):  # not vectorizing
+        tg.predict(epochs)
     assert_equal("<TimeDecoding | fitted, start : -0.200 (s), stop : 0.499 "
                  "(s), predicted 14 epochs, no score>",
                  '%s' % tg)
     assert_array_equal(np.shape(tg.y_pred_), [15, 14, 1])
-    tg.score(epochs)
+    with warnings.catch_warnings(record=True):  # not vectorizing
+        tg.score(epochs)
     tg.score()
     assert_array_equal(np.shape(tg.scores_), [15])
     assert_equal("<TimeDecoding | fitted, start : -0.200 (s), stop : 0.499 "
                  "(s), predicted 14 epochs,\n scored (accuracy_score)>",
                  '%s' % tg)
+    # Test with regressor
+    clf = SVR()
+    cv = KFold(len(epochs))
+    y = np.random.rand(len(epochs))
+    tg = TimeDecoding(clf=clf, cv=cv)
+    tg.fit(epochs, y=y)
+
+    # Test scorer parameter to accept string
+    epochs.crop(epochs.times[0], epochs.times[2])
+    td_1 = TimeDecoding(scorer='accuracy')
+    td_1.fit(epochs)
+    score_1 = td_1.score(epochs)
+
+    td_2 = TimeDecoding()
+    td_2.fit(epochs)
+    score_2 = td_2.score(epochs)
+    assert_array_equal(score_1, score_2)
+
+    td_1.scorer = 'accuracies'
+    assert_raises(KeyError, td_1.score, epochs)
+
 
 run_tests_if_main()
diff --git a/mne/decoding/tests/test_transformer.py b/mne/decoding/tests/test_transformer.py
index 87b862c..791810e 100644
--- a/mne/decoding/tests/test_transformer.py
+++ b/mne/decoding/tests/test_transformer.py
@@ -28,7 +28,7 @@ event_name = op.join(data_dir, 'test-eve.fif')
 def test_scaler():
     """Test methods of Scaler
     """
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
@@ -61,7 +61,7 @@ def test_scaler():
 def test_filterestimator():
     """Test methods of FilterEstimator
     """
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
@@ -100,7 +100,7 @@ def test_filterestimator():
 def test_psdestimator():
     """Test methods of PSDEstimator
     """
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
@@ -123,7 +123,7 @@ def test_psdestimator():
 def test_epochs_vectorizer():
     """Test methods of EpochsVectorizer
     """
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
diff --git a/mne/decoding/time_gen.py b/mne/decoding/time_gen.py
index 6f8cbca..af073df 100644
--- a/mne/decoding/time_gen.py
+++ b/mne/decoding/time_gen.py
@@ -8,10 +8,10 @@
 import numpy as np
 import copy
 
-from ..io.pick import pick_types
+from ..io.pick import _pick_data_channels
 from ..viz.decoding import plot_gat_matrix, plot_gat_times
 from ..parallel import parallel_func, check_n_jobs
-from ..utils import logger
+from ..utils import warn, check_version
 
 
 class _DecodingTime(dict):
@@ -61,11 +61,13 @@ class _DecodingTime(dict):
 
 
 class _GeneralizationAcrossTime(object):
-    """ see GeneralizationAcrossTime
+    """Generic object to train and test a series of classifiers at and across
+    different time samples.
     """  # noqa
     def __init__(self, picks=None, cv=5, clf=None, train_times=None,
-                 test_times=None, predict_mode='cross-validation', scorer=None,
-                 n_jobs=1):
+                 test_times=None, predict_method='predict',
+                 predict_mode='cross-validation', scorer=None,
+                 score_mode='mean-fold-wise', n_jobs=1):
 
         from sklearn.preprocessing import StandardScaler
         from sklearn.linear_model import LogisticRegression
@@ -92,14 +94,17 @@ class _GeneralizationAcrossTime(object):
         self.clf = clf
         self.predict_mode = predict_mode
         self.scorer = scorer
+        self.score_mode = score_mode
         self.picks = picks
+        self.predict_method = predict_method
         self.n_jobs = n_jobs
 
     def fit(self, epochs, y=None):
-        """ Train a classifier on each specified time slice.
+        """Train a classifier on each specified time slice.
 
-        Note. This function sets the ``picks_``, ``ch_names``, ``cv_``,
-        ``y_train``, ``train_times_`` and ``estimators_`` attributes.
+        .. note::
+            This function sets the ``picks_``, ``ch_names``, ``cv_``,
+            ``y_train``, ``train_times_`` and ``estimators_`` attributes.
 
         Parameters
         ----------
@@ -114,7 +119,7 @@ class _GeneralizationAcrossTime(object):
             Returns fitted GeneralizationAcrossTime object.
 
         Notes
-        ------
+        -----
         If X and y are not C-ordered and contiguous arrays of np.float64 and
         X is not a scipy.sparse.csr_matrix, X and/or y may be copied.
 
@@ -122,9 +127,8 @@ class _GeneralizationAcrossTime(object):
         matrices as input.
         """
         from sklearn.base import clone
-        from sklearn.cross_validation import check_cv, StratifiedKFold
 
-        # clean attributes
+        # Clean attributes
         for att in ['picks_', 'ch_names', 'y_train_', 'cv_', 'train_times_',
                     'estimators_', 'test_times_', 'y_pred_', 'y_true_',
                     'scores_', 'scorer_']:
@@ -136,44 +140,35 @@ class _GeneralizationAcrossTime(object):
         X, y, self.picks_ = _check_epochs_input(epochs, y, self.picks)
         self.ch_names = [epochs.ch_names[p] for p in self.picks_]
 
-        cv = self.cv
-        if isinstance(cv, (int, np.int)):
-            cv = StratifiedKFold(y, cv)
-        cv = check_cv(cv, X, y, classifier=True)
-        self.cv_ = cv  # update CV
+        # Prepare cross-validation
+        self.cv_, self._cv_splits = _set_cv(self.cv, clf=self.clf, X=X, y=y)
 
         self.y_train_ = y
 
-        # Cross validation scheme
-        # XXX Cross validation should later be transformed into a make_cv, and
-        # defined in __init__
-        self.train_times_ = copy.deepcopy(self.train_times)
-        if 'slices' not in self.train_times_:
-            self.train_times_ = _sliding_window(epochs.times, self.train_times,
-                                                epochs.info['sfreq'])
+        # Get train slices of times
+        self.train_times_ = _sliding_window(epochs.times, self.train_times,
+                                            epochs.info['sfreq'])
 
         # Parallel across training time
         # TODO: JRK: Chunking times points needs to be simplified
-        parallel, p_time_gen, n_jobs = parallel_func(_fit_slices, n_jobs)
+        parallel, p_func, n_jobs = parallel_func(_fit_slices, n_jobs)
         n_chunks = min(len(self.train_times_['slices']), n_jobs)
-        splits = np.array_split(self.train_times_['slices'], n_chunks)
+        time_chunks = np.array_split(self.train_times_['slices'], n_chunks)
 
-        def f(x):
-            return np.unique(np.concatenate(x))
+        out = parallel(p_func(clone(self.clf),
+                              X[..., np.unique(np.concatenate(time_chunk))],
+                              y, time_chunk, self._cv_splits)
+                       for time_chunk in time_chunks)
 
-        out = parallel(p_time_gen(clone(self.clf),
-                                  X[..., f(train_slices_chunk)],
-                                  y, train_slices_chunk, cv)
-                       for train_slices_chunk in splits)
         # Unpack estimators into time slices X folds list of lists.
         self.estimators_ = sum(out, list())
         return self
 
     def predict(self, epochs):
-        """ Test each classifier on each specified testing time slice.
+        """Classifiers' predictions on each specified testing time slice.
 
-        .. note:: This function sets the ``y_pred_`` and ``test_times_``
-                  attributes.
+        .. note::
+            This function sets the ``y_pred_`` and ``test_times_`` attributes.
 
         Parameters
         ----------
@@ -190,19 +185,43 @@ class _GeneralizationAcrossTime(object):
             ``np.shape(y_pred_) = (n_train_time, n_test_time, n_epochs)``.
         """  # noqa
 
+        # Check that classifier has predict_method (e.g. predict_proba is not
+        # always available):
+        if not hasattr(self.clf, self.predict_method):
+            raise NotImplementedError('%s does not have "%s"' % (
+                self.clf, self.predict_method))
+
         # Check that at least one classifier has been trained
         if not hasattr(self, 'estimators_'):
             raise RuntimeError('Please fit models before trying to predict')
 
-        # clean attributes
+        # Check predict mode
+        if self.predict_mode not in ['cross-validation', 'mean-prediction']:
+            raise ValueError('predict_mode must be a str, "mean-prediction" '
+                             'or "cross-validation"')
+
+        # Check that training cv and predicting cv match
+        if self.predict_mode == 'cross-validation':
+            n_est_cv = [len(estimator) for estimator in self.estimators_]
+            heterogeneous_cv = len(set(n_est_cv)) != 1
+            mismatch_cv = n_est_cv[0] != len(self._cv_splits)
+            mismatch_y = len(self.y_train_) != len(epochs)
+            if heterogeneous_cv or mismatch_cv or mismatch_y:
+                raise ValueError(
+                    'When predict_mode = "cross-validation", the training '
+                    'and predicting cv schemes must be identical.')
+
+        # Clean attributes
         for att in ['y_pred_', 'test_times_', 'scores_', 'scorer_', 'y_true_']:
             if hasattr(self, att):
                 delattr(self, att)
-
-        n_jobs = self.n_jobs
+        _warn_once.clear()  # reset self-baked warning tracker
 
         X, y, _ = _check_epochs_input(epochs, None, self.picks_)
 
+        if not np.all([len(test) for train, test in self._cv_splits]):
+            warn('Some folds do not have any test epochs.')
+
         # Define testing sliding window
         if self.test_times == 'diagonal':
             test_times = _DecodingTime()
@@ -211,61 +230,81 @@ class _GeneralizationAcrossTime(object):
         elif isinstance(self.test_times, dict):
             test_times = copy.deepcopy(self.test_times)
         else:
-            raise ValueError('`test_times` must be a dict or "diagonal"')
+            raise ValueError('test_times must be a dict or "diagonal"')
 
         if 'slices' not in test_times:
+            if 'length' not in self.train_times_.keys():
+                ValueError('Need test_times["slices"] with adhoc train_times.')
             # Check that same number of time sample in testing than in training
             # (otherwise it won 't be the same number of features')
-            if 'length' not in test_times:
-                test_times['length'] = self.train_times_['length']
-            if test_times['length'] != self.train_times_['length']:
-                raise ValueError('`train_times` and `test_times` must have '
-                                 'identical `length` keys')
+            test_times['length'] = test_times.get('length',
+                                                  self.train_times_['length'])
             # Make a sliding window for each training time.
             slices_list = list()
-            times_list = list()
-            for t in range(0, len(self.train_times_['slices'])):
+            for _ in range(len(self.train_times_['slices'])):
                 test_times_ = _sliding_window(epochs.times, test_times,
                                               epochs.info['sfreq'])
-                times_list += [test_times_['times']]
                 slices_list += [test_times_['slices']]
             test_times = test_times_
             test_times['slices'] = slices_list
-            test_times['times'] = times_list
+        test_times['times'] = [_set_window_time(test, epochs.times)
+                               for test in test_times['slices']]
+
+        for train, tests in zip(self.train_times_['slices'],
+                                test_times['slices']):
+            # The user may define irregular timing. We thus need to ensure
+            # that the dimensionality of each estimator (i.e. training
+            # time) corresponds to the dimensionality of each testing time)
+            if not np.all([len(test) == len(train) for test in tests]):
+                raise ValueError('train_times and test_times must '
+                                 'have identical lengths')
 
         # Store all testing times parameters
         self.test_times_ = test_times
 
-        # Prepare parallel predictions across time points
+        n_orig_epochs, _, n_times = X.shape
+
+        # Subselects the to-be-predicted epochs so as to manipulate a
+        # contiguous array X by using slices rather than indices.
+        test_epochs = []
+        if self.predict_mode == 'cross-validation':
+            test_idxs = [ii for train, test in self._cv_splits for ii in test]
+            start = 0
+            for _, test in self._cv_splits:
+                n_test_epochs = len(test)
+                stop = start + n_test_epochs
+                test_epochs.append(slice(start, stop, 1))
+                start += n_test_epochs
+            X = X[test_idxs]
+
+        # Prepare parallel predictions across testing time points
         # FIXME Note that this means that TimeDecoding.predict isn't parallel
-        parallel, p_time_gen, n_jobs = parallel_func(_predict_slices, n_jobs)
-        n_test_slice = max(len(sl) for sl in self.train_times_['slices'])
+        parallel, p_func, n_jobs = parallel_func(_predict_slices, self.n_jobs)
+        n_test_slice = max(len(sl) for sl in self.test_times_['slices'])
         # Loop across estimators (i.e. training times)
         n_chunks = min(n_test_slice, n_jobs)
-        splits = [np.array_split(slices, n_chunks)
+        chunks = [np.array_split(slices, n_chunks)
                   for slices in self.test_times_['slices']]
-        splits = map(list, zip(*splits))
-
-        def chunk_X(X, slices):
-            """Smart chunking to avoid memory overload"""
-            # from object array to list
-            slices = [sl for sl in slices if len(sl)]
-            start = np.min(slices)
-            stop = np.max(slices) + 1
-            slices_ = np.array(slices) - start
-            X_ = X[:, :, start:stop]
-            return (X_, self.estimators_, self.cv_, slices_.tolist(),
-                    self.predict_mode)
-
-        y_pred = parallel(p_time_gen(*chunk_X(X, slices))
-                          for slices in splits)
-
-        # concatenate chunks across test time dimension. Don't use
-        # np.concatenate as this would need new memory allocations
-        self.y_pred_ = [[test for chunk in train for test in chunk]
-                        for train in map(list, zip(*y_pred))]
-
-        _warn_once.clear()  # reset self-baked warning tracker
+        chunks = map(list, zip(*chunks))
+
+        # To minimize memory during parallelization, we apply some chunking
+        y_pred = parallel(p_func(
+            estimators=self.estimators_, cv_splits=self._cv_splits,
+            predict_mode=self.predict_mode, predict_method=self.predict_method,
+            n_orig_epochs=n_orig_epochs, test_epochs=test_epochs,
+            **dict(zip(['X', 'train_times'], _chunk_data(X, chunk))))
+            for chunk in chunks)
+
+        # Concatenate chunks across test time dimension.
+        n_tests = [len(sl) for sl in self.test_times_['slices']]
+        if len(set(n_tests)) == 1:  # does GAT deal with a regular array/matrix
+            self.y_pred_ = np.concatenate(y_pred, axis=1)
+        else:
+            # Non regular testing times, y_pred is an array of arrays with
+            # different lengths.
+            # FIXME: should do this with numpy operators only
+            self.y_pred_ = [[test for chunk in train for test in chunk]
+                            for train in map(list, zip(*y_pred))]
         return self.y_pred_
 
     def score(self, epochs=None, y=None):
@@ -276,8 +315,13 @@ class _GeneralizationAcrossTime(object):
 
         Calls ``predict()`` if it has not been already.
 
-        Note. The function updates the ``scorer_``, ``scores_``, and
-        ``y_true_`` attributes.
+        .. note::
+            The function updates the ``scorer_``, ``scores_``, and
+            ``y_true_`` attributes.
+
+        .. note::
+            If ``predict_mode`` is 'mean-prediction', ``score_mode`` is
+            automatically set to 'mean-sample-wise'.
 
         Parameters
         ----------
@@ -296,9 +340,18 @@ class _GeneralizationAcrossTime(object):
             The scores estimated by ``scorer_`` at each training time and each
             testing time (e.g. mean accuracy of ``predict(X)``). Note that the
             number of testing times per training time need not be regular;
-            else, np.shape(scores) = (n_train_time, n_test_time).
+            else, np.shape(scores) = (n_train_time, n_test_time). If
+            ``score_mode`` is 'fold-wise', np.shape(scores) = (n_train_time,
+            n_test_time, n_folds).
         """
-        from sklearn.metrics import accuracy_score
+        import sklearn.metrics
+        from sklearn.base import is_classifier
+        from sklearn.metrics import accuracy_score, mean_squared_error
+        if check_version('sklearn', '0.17'):
+            from sklearn.base import is_regressor
+        else:
+            def is_regressor(clf):
+                return False
 
         # Run predictions if not already done
         if epochs is not None:
@@ -308,14 +361,38 @@ class _GeneralizationAcrossTime(object):
                 raise RuntimeError('Please predict() epochs first or pass '
                                    'epochs to score()')
 
-        # clean gat.score() attributes
-        for att in ['scores_', 'scorer_', 'y_true_']:
-            if hasattr(self, att):
-                delattr(self, att)
-
         # Check scorer
-        # XXX Need API to identify proper scorer from the clf
-        self.scorer_ = accuracy_score if self.scorer is None else self.scorer
+        if self.score_mode not in ('fold-wise', 'mean-fold-wise',
+                                   'mean-sample-wise'):
+            raise ValueError("score_mode must be 'fold-wise', "
+                             "'mean-fold-wise' or 'mean-sample-wise'. "
+                             "Got %s instead'" % self.score_mode)
+        score_mode = self.score_mode
+        if (self.predict_mode == 'mean-prediction' and
+                self.score_mode != 'mean-sample-wise'):
+            warn("score_mode changed from %s set to 'mean-sample-wise' because"
+                 " predict_mode is 'mean-prediction'." % self.score_mode)
+            score_mode = 'mean-sample-wise'
+        self.scorer_ = self.scorer
+        if self.scorer_ is None:
+            # Try to guess which scoring metrics should be used
+            if self.predict_method == "predict":
+                if is_classifier(self.clf):
+                    self.scorer_ = accuracy_score
+                elif is_regressor(self.clf):
+                    self.scorer_ = mean_squared_error
+
+        elif isinstance(self.scorer_, str):
+            if hasattr(sklearn.metrics, '%s_score' % self.scorer_):
+                self.scorer_ = getattr(sklearn.metrics, '%s_score' %
+                                       self.scorer_)
+            else:
+                raise KeyError("{0} scorer Doesn't appear to be valid a "
+                               "scikit-learn scorer.".format(self.scorer_))
+        if not self.scorer_:
+            raise ValueError('Could not find a scoring metric for clf=%s '
+                             ' and predict_method=%s. Manually define scorer'
+                             '.' % (self.clf, self.predict_method))
 
         # If no regressor is passed, use default epochs events
         if y is None:
@@ -335,40 +412,36 @@ class _GeneralizationAcrossTime(object):
                                  'for scoring.')
         elif isinstance(y, list):
             y = np.array(y)
+
+        # Clean attributes
+        for att in ['scores_', 'y_true_']:
+            if hasattr(self, att):
+                delattr(self, att)
+
         self.y_true_ = y  # to be compared with y_pred for scoring
 
-        # Preprocessing for parallelization
+        # Preprocessing for parallelization across training times; to avoid
+        # overheads, we divide them in large chunks.
         n_jobs = min(len(self.y_pred_[0][0]), check_n_jobs(self.n_jobs))
-        parallel, p_time_gen, n_jobs = parallel_func(_score_slices, n_jobs)
+        parallel, p_func, n_jobs = parallel_func(_score_slices, n_jobs)
         n_estimators = len(self.train_times_['slices'])
         n_chunks = min(n_estimators, n_jobs)
-        splits = np.array_split(range(len(self.train_times_['slices'])),
+        chunks = np.array_split(range(len(self.train_times_['slices'])),
                                 n_chunks)
-        scores = parallel(
-            p_time_gen(self.y_true_,
-                       [self.y_pred_[train] for train in split],
-                       self.scorer_)
-            for split in splits)
-
-        self.scores_ = [score for chunk in scores for score in chunk]
+        scores = parallel(p_func(
+            self.y_true_, [self.y_pred_[train] for train in chunk],
+            self.scorer_, score_mode, self._cv_splits)
+            for chunk in chunks)
+        # TODO: np.array scores from initialization JRK
+        self.scores_ = np.array([score for chunk in scores for score in chunk])
         return self.scores_
 
 
-def _predict_slices(X, estimators, cv, slices, predict_mode):
-    """Aux function of GeneralizationAcrossTime that loops across chunks of
-    testing slices.
-    """
-    out = list()
-    for this_estimator, this_slice in zip(estimators, slices):
-        out.append(_predict_time_loop(X, this_estimator, cv, this_slice,
-                                      predict_mode))
-    return out
-
-
 _warn_once = dict()
 
 
-def _predict_time_loop(X, estimators, cv, slices, predict_mode):
+def _predict_slices(X, train_times, estimators, cv_splits, predict_mode,
+                    predict_method, n_orig_epochs, test_epochs):
     """Aux function of GeneralizationAcrossTime
 
     Run classifiers predictions loop across time samples.
@@ -377,11 +450,15 @@ def _predict_time_loop(X, estimators, cv, slices, predict_mode):
     ----------
     X : ndarray, shape (n_epochs, n_features, n_times)
         To-be-fitted data.
-    estimators : array-like, shape (n_times, n_folds)
-        Array of scikit-learn classifiers fitted in cross-validation.
-    slices : list
-        List of slices selecting data from X from which is prediction is
-        generated.
+    estimators : list of array-like, shape (n_times, n_folds)
+        List of array of scikit-learn classifiers fitted in cross-validation.
+    cv_splits : list of tuples
+        List of tuples of train and test array generated from cv.
+    train_times : list
+        List of list of slices selecting data from X from which is prediction
+        is generated.
+    predict_method : str
+        Specifies prediction method for the estimator.
     predict_mode : {'cross-validation', 'mean-prediction'}
         Indicates how predictions are achieved with regards to the cross-
         validation procedure:
@@ -392,87 +469,131 @@ def _predict_time_loop(X, estimators, cv, slices, predict_mode):
                 each of the k-fold cross-validation classifiers, and average
                 these predictions into a single estimate per sample.
         Default: 'cross-validation'
+    n_orig_epochs : int
+        Original number of predicted epochs before slice definition. Note
+        that the number of epochs may have been cropped if the cross validation
+        is not deterministic (e.g. with ShuffleSplit, we may only predict a
+        subset of epochs).
+    test_epochs : list of slices
+        List of slices to select the tested epoched in the cv.
     """
 
     # Check inputs
-    n_orig_epochs, _, n_times = X.shape
-    if predict_mode == 'cross-validation':
-        # Subselect to-be-predicted epochs so as to manipulate a contiguous
-        # array X by using slices rather than indices.
-        all_test = np.concatenate(list(zip(*cv))[-1])
-        test_epochs_slices = []
-        start = 0
-        for _, test in cv:
-            n_test_epochs = len(test)
-            stop = start + n_test_epochs
-            test_epochs_slices.append(slice(start, stop, 1))
-            start += n_test_epochs
-        X = X[all_test]  # XXX JRK: Still 12 % of cpu time.
-
-        # Check that training cv and predicting cv match
-        if (len(estimators) != len(cv)) or (cv.n != len(X)):
-            raise ValueError(
-                'When `predict_mode = "cross-validation"`, the training '
-                'and predicting cv schemes must be identical.')
-    elif predict_mode != 'mean-prediction':
-        raise ValueError('`predict_mode` must be a str, "mean-prediction" or'
-                         '"cross-validation"')
     n_epochs, _, n_times = X.shape
-
-    # Checks whether the GAT is based on contiguous window of lengths = 1
-    # time-sample, ranging across the entire times. In this case, we will
-    # be able to vectorize the testing times samples.
-    expected_start = np.arange(n_times)
-    is_single_time_sample = np.array_equal([ii for sl in slices for ii in sl],
-                                           expected_start)
-    if is_single_time_sample:
-        # In simple mode, we avoid iterating over time slices.
-        slices = [slice(expected_start[0], expected_start[-1] + 1, 1)]
-    elif _warn_once.get('vectorization', True):
-        logger.warning('not vectorizing predictions across testing times, '
-                       'using a time window with length > 1')
-        _warn_once['vectorization'] = False
-    # Iterate over testing times. If is_single_time_sample, then 1 iteration.
-    y_pred = list()
-    for t, indices in enumerate(slices):
-        # Vectoring chan_times features in case of multiple time samples given
-        # to the estimators.
-        if not is_single_time_sample:
-            X_pred = X[:, :, indices].reshape(n_epochs, -1)
-        else:
+    n_train = len(estimators)
+    n_test = [len(test_t_idxs) for test_t_idxs in train_times]
+
+    # Loop across training times (i.e. estimators)
+    y_pred = None
+    for train_t_idx, (estimator_cv, test_t_idxs) in enumerate(
+            zip(estimators, train_times)):
+        # Checks whether predict is based on contiguous windows of lengths = 1
+        # time-sample, ranging across the entire times. In this case, we will
+        # be able to vectorize the testing times samples.
+        # Set expected start time if window length == 1
+        start = np.arange(n_times)
+        contiguous_start = np.array_equal([sl[0] for sl in test_t_idxs], start)
+        window_lengths = np.unique([len(sl) for sl in test_t_idxs])
+        vectorize_times = (window_lengths == 1) and contiguous_start
+        if vectorize_times:
+            # In vectorize mode, we avoid iterating over time test time indices
+            test_t_idxs = [slice(start[0], start[-1] + 1, 1)]
+        elif _warn_once.get('vectorization', True):
+            # Only warn if multiple testing time
+            if len(test_t_idxs) > 1:
+                warn('Due to a time window with length > 1, unable to '
+                     ' vectorize across testing times. This leads to slower '
+                     'predictions compared to the length == 1 case.')
+                _warn_once['vectorization'] = False
+
+        # Iterate over testing times. If vectorize_times: 1 iteration.
+        for ii, test_t_idx in enumerate(test_t_idxs):
+            # Vectoring chan_times features in case of multiple time samples
+            # given to the estimators.
             X_pred = X
+            if not vectorize_times:
+                X_pred = X[:, :, test_t_idx].reshape(n_epochs, -1)
+
+            if predict_mode == 'mean-prediction':
+                # Bagging: predict with each fold's estimator and combine
+                # predictions.
+                y_pred_ = _predict(X_pred, estimator_cv,
+                                   vectorize_times=vectorize_times,
+                                   predict_method=predict_method)
+                # Initialize y_pred now we know its dimensionality
+                if y_pred is None:
+                    n_dim = y_pred_.shape[-1]
+                    y_pred = _init_ypred(n_train, n_test, n_orig_epochs, n_dim)
+                if vectorize_times:
+                    # When vectorizing, we predict multiple time points at once
+                    # to gain speed. The utput predictions thus correspond to
+                    # different test time indices.
+                    y_pred[train_t_idx][test_t_idx] = y_pred_
+                else:
+                    # Output predictions in a single test time column
+                    y_pred[train_t_idx][ii] = y_pred_
+            elif predict_mode == 'cross-validation':
+                # Predict using the estimator corresponding to each fold
+                for (_, test), test_epoch, estimator in zip(
+                        cv_splits, test_epochs, estimator_cv):
+                    if test.size == 0:  # see issue #2788
+                        continue
+                    y_pred_ = _predict(X_pred[test_epoch], [estimator],
+                                       vectorize_times=vectorize_times,
+                                       predict_method=predict_method)
+                    # Initialize y_pred now we know its dimensionality
+                    if y_pred is None:
+                        n_dim = y_pred_.shape[-1]
+                        y_pred = _init_ypred(n_train, n_test, n_orig_epochs,
+                                             n_dim)
+                    if vectorize_times:
+                        # When vectorizing, we predict multiple time points at
+                        # once to gain speed. The output predictions thus
+                        # correspond to different test_t_idx columns.
+                        y_pred[train_t_idx][test_t_idx, test, ...] = y_pred_
+                    else:
+                        # Output predictions in a single test_t_idx column
+                        y_pred[train_t_idx][ii, test, ...] = y_pred_
+
+    return y_pred
 
-        if predict_mode == 'mean-prediction':
-            # Predict with each fold's estimator and average predictions.
-            y_pred.append(_predict(X_pred, estimators,
-                          is_single_time_sample=is_single_time_sample))
-        elif predict_mode == 'cross-validation':
-            # Predict with the estimator trained on the separate training set.
-            for k, (train, test) in enumerate(cv):
-                # Single trial predictions
-                X_pred_t = X_pred[test_epochs_slices[k]]
-                # If is_single_time_sample, we are predicting each time sample
-                # as if it was a different epoch (vectoring)
-                y_pred_ = _predict(X_pred_t, estimators[k:k + 1],
-                                   is_single_time_sample=is_single_time_sample)
-                # XXX I didn't manage to initialize correctly this array, as
-                # its size depends on the the type of predictor and the
-                # number of class.
-                if k == 0:
-                    # /!\ The CV may not be exhaustive. Thus, we need to fill
-                    # the prediction to an array whose length is similar to X
-                    # before it was rendered contiguous.
-                    this_ypred = np.empty((n_orig_epochs,) + y_pred_.shape[1:])
-                    y_pred.append(this_ypred)
-                y_pred[-1][test, ...] = y_pred_
-
-    if is_single_time_sample:
-        y_pred = [yp for yp in y_pred[0].transpose([1, 0, 2])]
 
+def _init_ypred(n_train, n_test, n_orig_epochs, n_dim):
+    """Initialize the predictions for each train/test time points.
+
+    Parameters
+    ----------
+    n_train : int
+        Number of training time point (i.e. estimators)
+    n_test : list of int
+        List of number of testing time points for each estimator.
+    n_orig_epochs : int
+        Number of epochs passed to gat.predict()
+    n_dim : int
+        Number of dimensionality of y_pred. See np.shape(clf.predict(X))
+
+    Returns
+    -------
+    y_pred : np.array, shape(n_train, n_test, n_orig_epochs, n_dim)
+        Empty array.
+
+    Notes
+    -----
+    The ``y_pred`` variable can only be initialized after the first
+    prediction, because we can't know whether it is a a categorical output or a
+    set of probabilistic estimates. If all train time points have the same
+    number of testing time points, then y_pred is a matrix. Else it is an array
+    of arrays.
+    """
+    if len(set(n_test)) == 1:
+        y_pred = np.empty((n_train, n_test[0], n_orig_epochs, n_dim))
+    else:
+        y_pred = np.array([np.empty((this_n, n_orig_epochs, n_dim))
+                           for this_n in n_test])
     return y_pred
 
 
-def _score_slices(y_true, list_y_pred, scorer):
+def _score_slices(y_true, list_y_pred, scorer, score_mode, cv):
     """Aux function of GeneralizationAcrossTime that loops across chunks of
     testing slices.
     """
@@ -480,8 +601,19 @@ def _score_slices(y_true, list_y_pred, scorer):
     for y_pred in list_y_pred:
         scores = list()
         for t, this_y_pred in enumerate(y_pred):
-            # Scores across trials
-            scores.append(scorer(y_true, np.array(this_y_pred)))
+            if score_mode in ['mean-fold-wise', 'fold-wise']:
+                # Estimate score within each fold
+                scores_ = list()
+                for train, test in cv:
+                    scores_.append(scorer(y_true[test], this_y_pred[test]))
+                scores_ = np.array(scores_)
+                # Summarize score as average across folds
+                if score_mode == 'mean-fold-wise':
+                    scores_ = np.mean(scores_, axis=0)
+            elif score_mode == 'mean-sample-wise':
+                # Estimate score across all y_pred without cross-validation.
+                scores_ = scorer(y_true, this_y_pred)
+            scores.append(scores_)
         scores_list.append(scores)
     return scores_list
 
@@ -489,7 +621,7 @@ def _score_slices(y_true, list_y_pred, scorer):
 def _check_epochs_input(epochs, y, picks=None):
     """Aux function of GeneralizationAcrossTime
 
-    Format MNE data into scikit-learn X and y
+    Format MNE data into scikit-learn X and y.
 
     Parameters
     ----------
@@ -521,9 +653,7 @@ def _check_epochs_input(epochs, y, picks=None):
 
     # Pick channels
     if picks is None:  # just use good data channels
-        picks = pick_types(epochs.info, meg=True, eeg=True, seeg=True,
-                           eog=False, ecg=False, misc=False, stim=False,
-                           ref_meg=False, exclude='bads')
+        picks = _pick_data_channels(epochs.info, with_ref_meg=False)
     if isinstance(picks, (list, np.ndarray)):
         picks = np.array(picks, dtype=np.int)
     else:
@@ -535,7 +665,7 @@ def _check_epochs_input(epochs, y, picks=None):
     return X, y, picks
 
 
-def _fit_slices(clf, x_chunk, y, slices, cv):
+def _fit_slices(clf, x_chunk, y, slices, cv_splits):
     """Aux function of GeneralizationAcrossTime
 
     Fit each classifier.
@@ -550,8 +680,8 @@ def _fit_slices(clf, x_chunk, y, slices, cv):
         To-be-fitted model.
     slices : list | array, shape (n_training_slice,)
         List of training slices, indicating time sample relative to X
-    cv : scikit-learn cross-validation generator
-        A cross-validation generator to use.
+    cv_splits : list of tuples
+        List of (train, test) tuples generated from cv.split()
 
     Returns
     -------
@@ -564,21 +694,18 @@ def _fit_slices(clf, x_chunk, y, slices, cv):
     n_epochs = len(x_chunk)
     estimators = list()
     # Identify the time samples of X_chunck corresponding to X
-    values = np.unique(np.concatenate(slices))
-    indices = range(len(values))
+    values = np.unique([val for sl in slices for val in sl])
     # Loop across time slices
     for t_slice in slices:
         # Translate absolute time samples into time sample relative to x_chunk
-        for ii in indices:
-            t_slice[t_slice == values[ii]] = indices[ii]
+        t_slice = np.array([np.where(ii == values)[0][0] for ii in t_slice])
         # Select slice
         X = x_chunk[..., t_slice]
-        # Reshape data matrix to flatten features in case of multiple time
-        # samples.
+        # Reshape data matrix to flatten features if multiple time samples.
         X = X.reshape(n_epochs, np.prod(X.shape[1:]))
         # Loop across folds
         estimators_ = list()
-        for fold, (train, test) in enumerate(cv):
+        for fold, (train, test) in enumerate(cv_splits):
             # Fit classifier
             clf_ = clone(clf)
             clf_.fit(X[train, :], y[train])
@@ -588,84 +715,87 @@ def _fit_slices(clf, x_chunk, y, slices, cv):
     return estimators
 
 
-def _sliding_window(times, window_params, sfreq):
+def _sliding_window(times, window, sfreq):
     """Aux function of GeneralizationAcrossTime
 
-    Define the slices on which to train each classifier.
+    Define the slices on which to train each classifier. The user either define
+    the time slices manually in window['slices'] or s/he passes optional params
+    to set them from window['start'], window['stop'], window['step'] and
+    window['length'].
 
     Parameters
     ----------
     times : ndarray, shape (n_times,)
         Array of times from MNE epochs.
-    window_params : dict keys: ('start', 'stop', 'step', 'length')
-        Either train or test times. See GAT documentation.
+    window : dict keys: ('start', 'stop', 'step', 'length')
+        Either train or test times.
 
     Returns
     -------
-    time_pick : list
-        List of training slices, indicating for each classifier the time
-        sample (in indices of times) to be fitted on.
+    window : dict
+        Dictionary to set training and testing times.
+
+    See Also
+    --------
+    GeneralizationAcrossTime
+
     """
+    import copy
 
-    window_params = _DecodingTime(window_params)
+    window = _DecodingTime(copy.deepcopy(window))
 
     # Default values
-    if ('slices' in window_params and
-            all(k in window_params for k in
-                ('start', 'stop', 'step', 'length'))):
-        time_pick = window_params['slices']
-    else:
-        if 'start' not in window_params:
-            window_params['start'] = times[0]
-        if 'stop' not in window_params:
-            window_params['stop'] = times[-1]
-        if 'step' not in window_params:
-            window_params['step'] = 1. / sfreq
-        if 'length' not in window_params:
-            window_params['length'] = 1. / sfreq
-
-        if (window_params['start'] < times[0] or
-                window_params['start'] > times[-1]):
+    time_slices = window.get('slices', None)
+    # If the user hasn't manually defined the time slices, we'll define them
+    # with ``start``, ``stop``, ``step`` and ``length`` parameters.
+    if time_slices is None:
+        window['start'] = window.get('start', times[0])
+        window['stop'] = window.get('stop', times[-1])
+        window['step'] = window.get('step', 1. / sfreq)
+        window['length'] = window.get('length', 1. / sfreq)
+
+        if not (times[0] <= window['start'] <= times[-1]):
             raise ValueError(
-                '`start` (%.2f s) outside time range [%.2f, %.2f].' % (
-                    window_params['start'], times[0], times[-1]))
-        if (window_params['stop'] < times[0] or
-                window_params['stop'] > times[-1]):
+                'start (%.2f s) outside time range [%.2f, %.2f].' % (
+                    window['start'], times[0], times[-1]))
+        if not (times[0] <= window['stop'] <= times[-1]):
             raise ValueError(
-                '`stop` (%.2f s) outside time range [%.2f, %.2f].' % (
-                    window_params['stop'], times[0], times[-1]))
-        if window_params['step'] < 1. / sfreq:
-            raise ValueError('`step` must be >= 1 / sampling_frequency')
-        if window_params['length'] < 1. / sfreq:
-            raise ValueError('`length` must be >= 1 / sampling_frequency')
-        if window_params['length'] > np.ptp(times):
-            raise ValueError('`length` must be <= time range')
+                'stop (%.2f s) outside time range [%.2f, %.2f].' % (
+                    window['stop'], times[0], times[-1]))
+        if window['step'] < 1. / sfreq:
+            raise ValueError('step must be >= 1 / sampling_frequency')
+        if window['length'] < 1. / sfreq:
+            raise ValueError('length must be >= 1 / sampling_frequency')
+        if window['length'] > np.ptp(times):
+            raise ValueError('length must be <= time range')
 
         # Convert seconds to index
 
-        def find_time_idx(t):  # find closest time point
+        def find_t_idx(t):  # find closest time point
             return np.argmin(np.abs(np.asarray(times) - t))
 
-        start = find_time_idx(window_params['start'])
-        stop = find_time_idx(window_params['stop'])
-        step = int(round(window_params['step'] * sfreq))
-        length = int(round(window_params['length'] * sfreq))
+        start = find_t_idx(window['start'])
+        stop = find_t_idx(window['stop'])
+        step = int(round(window['step'] * sfreq))
+        length = int(round(window['length'] * sfreq))
 
         # For each training slice, give time samples to be included
-        time_pick = [range(start, start + length)]
-        while (time_pick[-1][0] + step) <= (stop - length + 1):
-            start = time_pick[-1][0] + step
-            time_pick.append(range(start, start + length))
-        window_params['slices'] = time_pick
+        time_slices = [range(start, start + length)]
+        while (time_slices[-1][0] + step) <= (stop - length + 1):
+            start = time_slices[-1][0] + step
+            time_slices.append(range(start, start + length))
+        window['slices'] = time_slices
+    window['times'] = _set_window_time(window['slices'], times)
+    return window
 
-    # Keep last training times in milliseconds
-    t_inds_ = [t[-1] for t in window_params['slices']]
-    window_params['times'] = times[t_inds_]
 
-    return window_params
+def _set_window_time(slices, times):
+    """Aux function to define time as the last training time point"""
+    t_idx_ = [t[-1] for t in slices]
+    return times[t_idx_]
 
 
-def _predict(X, estimators, is_single_time_sample):
+def _predict(X, estimators, vectorize_times, predict_method):
     """Aux function of GeneralizationAcrossTime
 
     Predict each classifier. If multiple classifiers are passed, average
@@ -678,6 +808,14 @@ def _predict(X, estimators, is_single_time_sample):
         Array of scikit-learn classifiers to predict data.
     X : ndarray, shape (n_epochs, n_features, n_times)
         To-be-predicted data
+    vectorize_times : bool
+        If True, X can be vectorized to predict all times points at once
+    predict_method : str
+        Name of the method used to make predictions from the estimator. For
+        example, both `predict_proba` and `predict` are supported for
+        sklearn.linear_model.LogisticRegression. Note that the scorer must be
+        adapted to the prediction outputs of the method. Defaults to 'predict'.
+
     Returns
     -------
     y_pred : ndarray, shape (n_epochs, m_prediction_dimensions)
@@ -695,14 +833,14 @@ def _predict(X, estimators, is_single_time_sample):
 
     # in simple case, we are predicting each time sample as if it
     # was a different epoch
-    if is_single_time_sample:  # treat times as trials for optimization
+    if vectorize_times:  # treat times as trials for optimization
         X = np.hstack(X).T  # XXX JRK: still 17% of cpu time
     n_epochs_tmp = len(X)
 
     # Compute prediction for each sub-estimator (i.e. per fold)
     # if independent, estimators = all folds
     for fold, clf in enumerate(estimators):
-        _y_pred = clf.predict(X)
+        _y_pred = getattr(clf, predict_method)(X)
         # See inconsistency in dimensionality: scikit-learn/scikit-learn#5058
         if _y_pred.ndim == 1:
             _y_pred = _y_pred[:, None]
@@ -712,20 +850,18 @@ def _predict(X, estimators, is_single_time_sample):
             y_pred = np.ones((n_epochs_tmp, predict_size, n_clf))
         y_pred[:, :, fold] = _y_pred
 
-    # Collapse y_pred across folds if necessary (i.e. if independent)
+    # Bagging: Collapse y_pred across folds if necessary (i.e. if independent)
+    # XXX need API to identify how multiple predictions can be combined?
     if fold > 0:
-        # XXX need API to identify how multiple predictions can be combined?
-        if is_classifier(clf):
+        if is_classifier(clf) and (predict_method == 'predict'):
             y_pred, _ = stats.mode(y_pred, axis=2)
         else:
-            y_pred = np.mean(y_pred, axis=2)
-
+            y_pred = np.mean(y_pred, axis=2, keepdims=True)
+    y_pred = y_pred[:, :, 0]
     # Format shape
-    y_pred = y_pred.reshape((n_epochs_tmp, predict_size))
-    if is_single_time_sample:
-        y_pred = np.reshape(y_pred,
-                            [n_epochs, n_times, y_pred.shape[-1]])
-
+    if vectorize_times:
+        shape = [n_epochs, n_times, y_pred.shape[-1]]
+        y_pred = y_pred.reshape(shape).transpose([1, 0, 2])
     return y_pred
 
 
@@ -744,8 +880,9 @@ class GeneralizationAcrossTime(_GeneralizationAcrossTime):
     cv : int | object
         If an integer is passed, it is the number of folds.
         Specific cross-validation objects can be passed, see
-        scikit-learn.cross_validation module for the list of possible objects.
-        Defaults to 5.
+        scikit-learn.model_selection module for the list of possible objects.
+        If clf is a classifier, defaults to StratifiedKFold(n_folds=5), else
+        defaults to KFold(n_folds=5).
     clf : object | None
         An estimator compliant with the scikit-learn API (fit & predict).
         If None the classifier will be a standard pipeline including
@@ -753,19 +890,19 @@ class GeneralizationAcrossTime(_GeneralizationAcrossTime):
     train_times : dict | None
         A dictionary to configure the training times:
 
-            ``slices`` : ndarray, shape (n_clfs,)
+            * ``slices`` : ndarray, shape (n_clfs,)
                 Array of time slices (in indices) used for each classifier.
                 If not given, computed from 'start', 'stop', 'length', 'step'.
-            ``start`` : float
+            * ``start`` : float
                 Time at which to start decoding (in seconds).
                 Defaults to min(epochs.times).
-            ``stop`` : float
+            * ``stop`` : float
                 Maximal time at which to stop decoding (in seconds).
                 Defaults to max(times).
-            ``step`` : float
+            * ``step`` : float
                 Duration separating the start of subsequent classifiers (in
                 seconds). Defaults to one time sample.
-            ``length`` : float
+            * ``length`` : float
                 Duration of each classifier (in seconds).
                 Defaults to one time sample.
 
@@ -776,27 +913,47 @@ class GeneralizationAcrossTime(_GeneralizationAcrossTime):
         each classifier is trained.
         If set to None, predictions are made at all time points.
         If set to dict, the dict should contain ``slices`` or be contructed in
-        a similar way to train_times::
+        a similar way to train_times:
 
             ``slices`` : ndarray, shape (n_clfs,)
                 Array of time slices (in indices) used for each classifier.
                 If not given, computed from 'start', 'stop', 'length', 'step'.
 
         If None, empty dict.
+    predict_method : str
+        Name of the method used to make predictions from the estimator. For
+        example, both `predict_proba` and `predict` are supported for
+        sklearn.linear_model.LogisticRegression. Note that the scorer must be
+        adapted to the prediction outputs of the method. Defaults to 'predict'.
     predict_mode : {'cross-validation', 'mean-prediction'}
         Indicates how predictions are achieved with regards to the cross-
         validation procedure:
 
-            ``cross-validation`` : estimates a single prediction per sample
+            * ``cross-validation`` : estimates a single prediction per sample
                 based on the unique independent classifier fitted in the
                 cross-validation.
-            ``mean-prediction`` : estimates k predictions per sample, based on
-                each of the k-fold cross-validation classifiers, and average
+
+            * ``mean-prediction`` : estimates k predictions per sample, based
+                on each of the k-fold cross-validation classifiers, and average
                 these predictions into a single estimate per sample.
 
-        Default: 'cross-validation'
-    scorer : object | None
-        scikit-learn Scorer instance. If None, set to accuracy_score.
+        Defaults to 'cross-validation'.
+    scorer : object | None | str
+        scikit-learn Scorer instance or str type indicating the name of the
+        scorer such as ``accuracy``, ``roc_auc``. If None, set to ``accuracy``.
+    score_mode : {'fold-wise', 'mean-fold-wise', 'mean-sample-wise'}
+        Determines how the scorer is estimated:
+
+            * ``fold-wise`` : returns the score obtained in each fold.
+
+            * ``mean-fold-wise`` : returns the average of the fold-wise scores.
+
+            * ``mean-sample-wise`` : returns score estimated across across all
+                y_pred independently of the cross-validation. This method is
+                faster than ``mean-fold-wise`` but less conventional, use at
+                your own risk.
+
+        Defaults to 'mean-fold-wise'.
     n_jobs : int
         Number of jobs to run in parallel. Defaults to 1.
 
@@ -811,10 +968,11 @@ class GeneralizationAcrossTime(_GeneralizationAcrossTime):
     train_times_ : dict
         A dictionary that configures the training times:
 
-            ``slices`` : ndarray, shape (n_clfs,)
+            * ``slices`` : ndarray, shape (n_clfs,)
                 Array of time slices (in indices) used for each classifier.
                 If not given, computed from 'start', 'stop', 'length', 'step'.
-            ``times`` : ndarray, shape (n_clfs,)
+
+            * ``times`` : ndarray, shape (n_clfs,)
                 The training times (in seconds).
 
     test_times_ : dict
@@ -860,18 +1018,21 @@ class GeneralizationAcrossTime(_GeneralizationAcrossTime):
     .. versionadded:: 0.9.0
     """  # noqa
     def __init__(self, picks=None, cv=5, clf=None, train_times=None,
-                 test_times=None, predict_mode='cross-validation', scorer=None,
-                 n_jobs=1):
+                 test_times=None, predict_method='predict',
+                 predict_mode='cross-validation', scorer=None,
+                 score_mode='mean-fold-wise', n_jobs=1):
         super(GeneralizationAcrossTime, self).__init__(
             picks=picks, cv=cv, clf=clf, train_times=train_times,
-            test_times=test_times, predict_mode=predict_mode, scorer=scorer,
+            test_times=test_times, predict_method=predict_method,
+            predict_mode=predict_mode, scorer=scorer, score_mode=score_mode,
             n_jobs=n_jobs)
 
     def __repr__(self):
         s = ''
         if hasattr(self, "estimators_"):
             s += "fitted, start : %0.3f (s), stop : %0.3f (s)" % (
-                self.train_times_['start'], self.train_times_['stop'])
+                self.train_times_.get('start', np.nan),
+                self.train_times_.get('stop', np.nan))
         else:
             s += 'no fit'
         if hasattr(self, 'y_pred_'):
@@ -1025,9 +1186,7 @@ class GeneralizationAcrossTime(_GeneralizationAcrossTime):
         fig : instance of matplotlib.figure.Figure
             The figure.
         """
-        if (not isinstance(train_time, float) and
-            not (isinstance(train_time, (list, np.ndarray)) and
-                 np.all([isinstance(time, float) for time in train_time]))):
+        if not np.array(train_time).dtype is np.dtype('float'):
             raise ValueError('train_time must be float | list or array of '
                              'floats. Got %s.' % type(train_time))
 
@@ -1050,8 +1209,9 @@ class TimeDecoding(_GeneralizationAcrossTime):
     cv : int | object
         If an integer is passed, it is the number of folds.
         Specific cross-validation objects can be passed, see
-        scikit-learn.cross_validation module for the list of possible objects.
-        Defaults to 5.
+        scikit-learn.model_selection module for the list of possible objects.
+        If clf is a classifier, defaults to StratifiedKFold(n_folds=5), else
+        defaults to KFold(n_folds=5).
     clf : object | None
         An estimator compliant with the scikit-learn API (fit & predict).
         If None the classifier will be a standard pipeline including
@@ -1062,34 +1222,58 @@ class TimeDecoding(_GeneralizationAcrossTime):
             ``slices`` : ndarray, shape (n_clfs,)
                 Array of time slices (in indices) used for each classifier.
                 If not given, computed from 'start', 'stop', 'length', 'step'.
+
             ``start`` : float
                 Time at which to start decoding (in seconds). By default,
                 min(epochs.times).
+
             ``stop`` : float
                 Maximal time at which to stop decoding (in seconds). By
                 default, max(times).
+
             ``step`` : float
                 Duration separating the start of subsequent classifiers (in
                 seconds). By default, equals one time sample.
+
             ``length`` : float
                 Duration of each classifier (in seconds). By default, equals
                 one time sample.
 
         If None, empty dict.
+    predict_method : str
+        Name of the method used to make predictions from the estimator. For
+        example, both `predict_proba` and `predict` are supported for
+        sklearn.linear_model.LogisticRegression. Note that the scorer must be
+        adapted to the prediction outputs of the method. Defaults to 'predict'.
     predict_mode : {'cross-validation', 'mean-prediction'}
         Indicates how predictions are achieved with regards to the cross-
         validation procedure:
 
-            ``cross-validation`` : estimates a single prediction per sample
+            * ``cross-validation`` : estimates a single prediction per sample
                 based on the unique independent classifier fitted in the
                 cross-validation.
-            ``mean-prediction`` : estimates k predictions per sample, based on
-                each of the k-fold cross-validation classifiers, and average
+
+            * ``mean-prediction`` : estimates k predictions per sample, based
+                on each of the k-fold cross-validation classifiers, and average
                 these predictions into a single estimate per sample.
 
-        Default: 'cross-validation'
-    scorer : object | None
-        scikit-learn Scorer instance. If None, set to accuracy_score.
+        Defaults to 'cross-validation'.
+    scorer : object | None | str
+        scikit-learn Scorer instance or str type indicating the name of the
+        scorer such as ``accuracy``, ``roc_auc``. If None, set to ``accuracy``.
+    score_mode : {'fold-wise', 'mean-fold-wise', 'mean-sample-wise'}
+        Determines how the scorer is estimated:
+
+            * ``fold-wise`` : returns the score obtained in each fold.
+
+            * ``mean-fold-wise`` : returns the average of the fold-wise scores.
+
+            * ``mean-sample-wise`` : returns score estimated across across all
+                y_pred independently of the cross-validation. This method is
+                faster than ``mean-fold-wise`` but less conventional, use at
+                your own risk.
+
+        Defaults to 'mean-fold-wise'.
     n_jobs : int
         Number of jobs to run in parallel. Defaults to 1.
 
@@ -1104,10 +1288,11 @@ class TimeDecoding(_GeneralizationAcrossTime):
     times_ : dict
         A dictionary that configures the training times:
 
-            ``slices`` : ndarray, shape (n_clfs,)
+            * ``slices`` : ndarray, shape (n_clfs,)
                 Array of time slices (in indices) used for each classifier.
                 If not given, computed from 'start', 'stop', 'length', 'step'.
-            ``times`` : ndarray, shape (n_clfs,)
+
+            * ``times`` : ndarray, shape (n_clfs,)
                 The training times (in seconds).
 
     cv_ : CrossValidation object
@@ -1135,19 +1320,24 @@ class TimeDecoding(_GeneralizationAcrossTime):
     """
 
     def __init__(self, picks=None, cv=5, clf=None, times=None,
-                 predict_mode='cross-validation', scorer=None, n_jobs=1):
-        super(TimeDecoding, self).__init__(picks=picks, cv=cv, clf=None,
+                 predict_method='predict', predict_mode='cross-validation',
+                 scorer=None, score_mode='mean-fold-wise', n_jobs=1):
+        super(TimeDecoding, self).__init__(picks=picks, cv=cv, clf=clf,
                                            train_times=times,
                                            test_times='diagonal',
+                                           predict_method=predict_method,
                                            predict_mode=predict_mode,
-                                           scorer=scorer, n_jobs=n_jobs)
+                                           scorer=scorer,
+                                           score_mode=score_mode,
+                                           n_jobs=n_jobs)
         self._clean_times()
 
     def __repr__(self):
         s = ''
         if hasattr(self, "estimators_"):
             s += "fitted, start : %0.3f (s), stop : %0.3f (s)" % (
-                self.times_['start'], self.times_['stop'])
+                self.times_.get('start', np.nan),
+                self.times_.get('stop', np.nan))
         else:
             s += 'no fit'
         if hasattr(self, 'y_pred_'):
@@ -1168,10 +1358,11 @@ class TimeDecoding(_GeneralizationAcrossTime):
         return "<TimeDecoding | %s>" % s
 
     def fit(self, epochs, y=None):
-        """ Train a classifier on each specified time slice.
+        """Train a classifier on each specified time slice.
 
-        Note. This function sets the ``picks_``, ``ch_names``, ``cv_``,
-        ``y_train``, ``train_times_`` and ``estimators_`` attributes.
+        .. note::
+            This function sets the ``picks_``, ``ch_names``, ``cv_``,
+            ``y_train``, ``train_times_`` and ``estimators_`` attributes.
 
         Parameters
         ----------
@@ -1186,7 +1377,7 @@ class TimeDecoding(_GeneralizationAcrossTime):
             Returns fitted TimeDecoding object.
 
         Notes
-        ------
+        -----
         If X and y are not C-ordered and contiguous arrays of np.float64 and
         X is not a scipy.sparse.csr_matrix, X and/or y may be copied.
 
@@ -1199,10 +1390,10 @@ class TimeDecoding(_GeneralizationAcrossTime):
         return self
 
     def predict(self, epochs):
-        """ Test each classifier on each specified testing time slice.
+        """Test each classifier on each specified testing time slice.
 
-        .. note:: This function sets the ``y_pred_`` and ``test_times_``
-                  attributes.
+        .. note::
+            This function sets the ``y_pred_`` and ``test_times_`` attributes.
 
         Parameters
         ----------
@@ -1228,8 +1419,13 @@ class TimeDecoding(_GeneralizationAcrossTime):
 
         Calls ``predict()`` if it has not been already.
 
-        Note. The function updates the ``scorer_``, ``scores_``, and
-        ``y_true_`` attributes.
+        .. note::
+            The function updates the ``scorer_``, ``scores_``, and
+            ``y_true_`` attributes.
+
+        .. note::
+            If ``predict_mode`` is 'mean-prediction', ``score_mode`` is
+            automatically set to 'mean-sample-wise'.
 
         Parameters
         ----------
@@ -1314,7 +1510,7 @@ class TimeDecoding(_GeneralizationAcrossTime):
         return fig
 
     def _prep_times(self):
-        """Auxiliary function to allow compability with GAT"""
+        """Auxiliary function to allow compatibility with GAT"""
         self.test_times = 'diagonal'
         if hasattr(self, 'times'):
             self.train_times = self.times
@@ -1331,7 +1527,7 @@ class TimeDecoding(_GeneralizationAcrossTime):
             self.y_pred_ = [[y_pred] for y_pred in self.y_pred_]
 
     def _clean_times(self):
-        """Auxiliary function to allow compability with GAT"""
+        """Auxiliary function to allow compatibility with GAT"""
         if hasattr(self, 'train_times'):
             self.times = self.train_times
         if hasattr(self, 'train_times_'):
@@ -1344,3 +1540,57 @@ class TimeDecoding(_GeneralizationAcrossTime):
             self.y_pred_ = [y_pred[0] for y_pred in self.y_pred_]
         if hasattr(self, 'scores_'):
             self.scores_ = [score[0] for score in self.scores_]
+
+
+def _chunk_data(X, slices):
+    """Smart chunking to avoid memory overload.
+
+    The parallelization is performed across time samples. To avoid overheads,
+    the X data is splitted into large chunks of different time sizes. To
+    avoid duplicating the memory load to each job, we only pass the time
+    samples that are required by each job. The indices of the training times
+    must be adjusted accordingly.
+    """
+
+    # from object array to list
+    slices = [sl for sl in slices if len(sl)]
+    selected_times = np.hstack([np.ravel(sl) for sl in slices])
+    start = np.min(selected_times)
+    stop = np.max(selected_times) + 1
+    slices_chunk = [sl - start for sl in slices]
+    X_chunk = X[:, :, start:stop]
+    return X_chunk, slices_chunk
+
+
+def _set_cv(cv, clf=None, X=None, y=None):
+    from sklearn.base import is_classifier
+
+    # Set the default cross-validation depending on whether clf is classifier
+    # or regressor.
+    if check_version('sklearn', '0.18'):
+        from sklearn.model_selection import (check_cv, StratifiedKFold, KFold)
+        if isinstance(cv, (int, np.int)):
+            XFold = StratifiedKFold if is_classifier(clf) else KFold
+            cv = XFold(n_folds=cv)
+        cv = check_cv(cv=cv, y=y, classifier=is_classifier(clf))
+    else:
+        from sklearn.cross_validation import (check_cv, StratifiedKFold, KFold)
+        if isinstance(cv, (int, np.int)):
+            if is_classifier(clf):
+                cv = StratifiedKFold(y=y, n_folds=cv)
+            else:
+                cv = KFold(n=len(y), n_folds=cv)
+        cv = check_cv(cv=cv, X=X, y=y, classifier=is_classifier(clf))
+
+    # Extract train and test set to retrieve them at predict time
+    if hasattr(cv, 'split'):
+        cv_splits = [(train, test) for train, test in
+                     cv.split(X=np.zeros_like(y), y=y)]
+    else:
+        # XXX support sklearn.cross_validation cv
+        cv_splits = [(train, test) for train, test in cv]
+
+    if not np.all([len(train) for train, _ in cv_splits]):
+        raise ValueError('Some folds do not have any train epochs.')
+
+    return cv, cv_splits
diff --git a/mne/decoding/transformer.py b/mne/decoding/transformer.py
index 55a28f8..6f1d772 100644
--- a/mne/decoding/transformer.py
+++ b/mne/decoding/transformer.py
@@ -11,7 +11,7 @@ from .mixin import TransformerMixin
 from .. import pick_types
 from ..filter import (low_pass_filter, high_pass_filter, band_pass_filter,
                       band_stop_filter)
-from ..time_frequency import multitaper_psd
+from ..time_frequency.psd import _psd_multitaper
 from ..externals import six
 from ..utils import _check_type_picks
 
@@ -71,8 +71,8 @@ class Scaler(TransformerMixin):
                                        exclude='bads')
         picks_list['grad'] = pick_types(self.info, meg='grad', ref_meg=False,
                                         exclude='bads')
-        picks_list['eeg'] = pick_types(self.info, eeg='grad', ref_meg=False,
-                                       exclude='bads')
+        picks_list['eeg'] = pick_types(self.info, eeg=True, ref_meg=False,
+                                       meg=False, exclude='bads')
 
         self.picks_list_ = picks_list
 
@@ -181,7 +181,7 @@ class EpochsVectorizer(TransformerMixin):
 
         Returns
         -------
-        self : instance of ConcatenateChannels
+        self : instance of EpochsVectorizer
             returns the modified instance
         """
         if not isinstance(epochs_data, np.ndarray):
@@ -272,6 +272,10 @@ class PSDEstimator(TransformerMixin):
         the signal (as in nitime).
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
+
+    See Also
+    --------
+    psd_multitaper
     """
     def __init__(self, sfreq=2 * np.pi, fmin=0, fmax=np.inf, bandwidth=None,
                  adaptive=False, low_bias=True, n_jobs=1,
@@ -300,7 +304,6 @@ class PSDEstimator(TransformerMixin):
         -------
         self : instance of PSDEstimator
             returns the modified instance
-
         """
         if not isinstance(epochs_data, np.ndarray):
             raise ValueError("epochs_data should be of type ndarray (got %s)."
@@ -328,22 +331,11 @@ class PSDEstimator(TransformerMixin):
         if not isinstance(epochs_data, np.ndarray):
             raise ValueError("epochs_data should be of type ndarray (got %s)."
                              % type(epochs_data))
-
-        epochs_data = np.atleast_3d(epochs_data)
-
-        n_epochs, n_channels, n_times = epochs_data.shape
-        X = epochs_data.reshape(n_epochs * n_channels, n_times)
-
-        psd, _ = multitaper_psd(x=X, sfreq=self.sfreq, fmin=self.fmin,
-                                fmax=self.fmax, bandwidth=self.bandwidth,
-                                adaptive=self.adaptive, low_bias=self.low_bias,
-                                n_jobs=self.n_jobs,
-                                normalization=self.normalization,
-                                verbose=self.verbose)
-
-        _, n_freqs = psd.shape
-        psd = psd.reshape(n_epochs, n_channels, n_freqs)
-
+        psd, _ = _psd_multitaper(
+            epochs_data, sfreq=self.sfreq, fmin=self.fmin, fmax=self.fmax,
+            bandwidth=self.bandwidth, adaptive=self.adaptive,
+            low_bias=self.low_bias, normalization=self.normalization,
+            n_jobs=self.n_jobs)
         return psd
 
 
diff --git a/mne/defaults.py b/mne/defaults.py
index 6a58b47..4f8faa2 100644
--- a/mne/defaults.py
+++ b/mne/defaults.py
@@ -10,23 +10,26 @@ DEFAULTS = dict(
     color=dict(mag='darkblue', grad='b', eeg='k', eog='k', ecg='m',
                emg='k', ref_meg='steelblue', misc='k', stim='k',
                resp='k', chpi='k', exci='k', ias='k', syst='k',
-               seeg='k'),
+               seeg='k', dipole='k', gof='k', bio='k', ecog='k'),
     config_opts=dict(),
     units=dict(eeg='uV', grad='fT/cm', mag='fT', eog='uV', misc='AU',
-               seeg='uV'),
-    scalings=dict(mag=1e15, grad=1e13, eeg=1e6, eog=1e6,
-                  misc=1.0, seeg=1e4),
+               seeg='uV', dipole='nAm', gof='GOF', emg='uV', ecg='uV',
+               bio='uV', ecog='uV'),
+    scalings=dict(mag=1e15, grad=1e13, eeg=1e6, eog=1e6, emg=1e6, ecg=1e6,
+                  misc=1.0, seeg=1e4, dipole=1e9, gof=1.0, bio=1e6, ecog=1e6),
     scalings_plot_raw=dict(mag=1e-12, grad=4e-11, eeg=20e-6,
                            eog=150e-6, ecg=5e-4, emg=1e-3,
                            ref_meg=1e-12, misc=1e-3,
                            stim=1, resp=1, chpi=1e-4, exci=1,
-                           ias=1, syst=1, seeg=1e-5),
+                           ias=1, syst=1, seeg=1e-5, bio=1e-6, ecog=1e-4),
     scalings_cov_rank=dict(mag=1e12, grad=1e11, eeg=1e5),
-    ylim=dict(mag=(-600., 600.), grad=(-200., 200.),
-              eeg=(-200., 200.), misc=(-5., 5.),
-              seeg=(-200., 200.)),
-    titles=dict(eeg='EEG', grad='Gradiometers',
-                mag='Magnetometers', misc='misc', seeg='sEEG'),
+    ylim=dict(mag=(-600., 600.), grad=(-200., 200.), eeg=(-200., 200.),
+              misc=(-5., 5.), seeg=(-200., 200.), dipole=(-100., 100.),
+              gof=(0., 1.), bio=(-500., 500.), ecog=(-200., 200.)),
+    titles=dict(eeg='EEG', grad='Gradiometers', mag='Magnetometers',
+                misc='misc', seeg='sEEG', dipole='Dipole', eog='EOG',
+                gof='Goodness of fit', ecg='ECG', emg='EMG', bio='BIO',
+                ecog='ECoG'),
     mask_params=dict(marker='o',
                      markerfacecolor='w',
                      markeredgecolor='k',
diff --git a/mne/dipole.py b/mne/dipole.py
index d1c71e6..b5d8fde 100644
--- a/mne/dipole.py
+++ b/mne/dipole.py
@@ -3,17 +3,21 @@
 #
 # License: Simplified BSD
 
-import numpy as np
-from scipy import linalg
 from copy import deepcopy
 import re
 
+import numpy as np
+from scipy import linalg
+
 from .cov import read_cov, _get_whitener_data
+from .io.constants import FIFF
 from .io.pick import pick_types, channel_type
 from .io.proj import make_projector, _needs_eeg_average_ref_proj
 from .bem import _fit_sphere
+from .evoked import _read_evoked, _aspect_rev, _write_evokeds
 from .transforms import (_print_coord_trans, _coord_frame_name,
                          apply_trans, invert_transform, Transform)
+from .viz.evoked import _plot_evoked
 
 from .forward._make_forward import (_get_trans, _setup_bem,
                                     _prep_meg_channels, _prep_eeg_channels)
@@ -28,22 +32,26 @@ from .source_space import (_make_volume_source_space, SourceSpaces,
                            _points_outside_surface)
 from .parallel import parallel_func
 from .fixes import partial
-from .utils import logger, verbose, _time_mask
+from .utils import logger, verbose, _time_mask, warn, _check_fname, check_fname
 
 
 class Dipole(object):
-    """Dipole class
+    """Dipole class for sequential dipole fits
+
+    .. note:: This class should usually not be instantiated directly,
+              instead :func:`mne.read_dipole` should be used.
 
     Used to store positions, orientations, amplitudes, times, goodness of fit
     of dipoles, typically obtained with Neuromag/xfit, mne_dipole_fit
-    or certain inverse solvers.
+    or certain inverse solvers. Note that dipole position vectors are given in
+    the head coordinate frame.
 
     Parameters
     ----------
     times : array, shape (n_dipoles,)
         The time instants at which each dipole was fitted (sec).
     pos : array, shape (n_dipoles, 3)
-        The dipoles positions (m).
+        The dipoles positions (m) in head coordinates.
     amplitude : array, shape (n_dipoles,)
         The amplitude of the dipoles (nAm).
     ori : array, shape (n_dipoles, 3)
@@ -52,13 +60,24 @@ class Dipole(object):
         The goodness of fit.
     name : str | None
         Name of the dipole.
+
+    See Also
+    --------
+    read_dipole
+    DipoleFixed
+
+    Notes
+    -----
+    This class is for sequential dipole fits, where the position
+    changes as a function of time. For fixed dipole fits, where the
+    position is fixed as a function of time, use :class:`mne.DipoleFixed`.
     """
     def __init__(self, times, pos, amplitude, ori, gof, name=None):
-        self.times = times
-        self.pos = pos
-        self.amplitude = amplitude
-        self.ori = ori
-        self.gof = gof
+        self.times = np.array(times)
+        self.pos = np.array(pos)
+        self.amplitude = np.array(amplitude)
+        self.ori = np.array(ori)
+        self.gof = np.array(gof)
         self.name = name
 
     def __repr__(self):
@@ -76,6 +95,7 @@ class Dipole(object):
             The name of the .dip file.
         """
         fmt = "  %7.1f %7.1f %8.2f %8.2f %8.2f %8.3f %8.3f %8.3f %8.3f %6.1f"
+        # NB CoordinateSystem is hard-coded as Head here
         with open(fname, 'wb') as fid:
             fid.write('# CoordinateSystem "Head"\n'.encode('utf-8'))
             fid.write('#   begin     end   X (mm)   Y (mm)   Z (mm)'
@@ -101,7 +121,10 @@ class Dipole(object):
         tmax : float | None
             End time of selection in seconds.
         """
-        mask = _time_mask(self.times, tmin, tmax)
+        sfreq = None
+        if len(self.times) > 1:
+            sfreq = 1. / np.median(np.diff(self.times))
+        mask = _time_mask(self.times, tmin, tmax, sfreq=sfreq)
         for attr in ('times', 'pos', 'gof', 'amplitude', 'ori'):
             setattr(self, attr, getattr(self, attr)[mask])
 
@@ -118,8 +141,8 @@ class Dipole(object):
     @verbose
     def plot_locations(self, trans, subject, subjects_dir=None,
                        bgcolor=(1, 1, 1), opacity=0.3,
-                       brain_color=(0.7, 0.7, 0.7), mesh_color=(1, 1, 0),
-                       fig_name=None, fig_size=(600, 600), mode='cone',
+                       brain_color=(1, 1, 0), fig_name=None,
+                       fig_size=(600, 600), mode='cone',
                        scale_factor=0.1e-1, colors=None, verbose=None):
         """Plot dipole locations as arrows
 
@@ -140,8 +163,6 @@ class Dipole(object):
             Opacity of brain mesh.
         brain_color : tuple of length 3
             Brain color.
-        mesh_color : tuple of length 3
-            Mesh color.
         fig_name : tuple of length 2
             Mayavi figure name.
         fig_size : tuple of length 2
@@ -168,7 +189,7 @@ class Dipole(object):
             dipoles[-1].crop(t, t)
         return plot_dipole_locations(
             dipoles, trans, subject, subjects_dir, bgcolor, opacity,
-            brain_color, mesh_color, fig_name, fig_size, mode, scale_factor,
+            brain_color, fig_name, fig_size, mode, scale_factor,
             colors)
 
     def plot_amplitudes(self, color='k', show=True):
@@ -211,9 +232,113 @@ class Dipole(object):
         return self.pos.shape[0]
 
 
+def _read_dipole_fixed(fname):
+    """Helper to read a fixed dipole FIF file"""
+    logger.info('Reading %s ...' % fname)
+    _check_fname(fname, overwrite=True, must_exist=True)
+    info, nave, aspect_kind, first, last, comment, times, data = \
+        _read_evoked(fname)
+    return DipoleFixed(info, data, times, nave, aspect_kind, first, last,
+                       comment)
+
+
+class DipoleFixed(object):
+    """Dipole class for fixed-position dipole fits
+
+    .. note:: This class should usually not be instantiated directly,
+              instead :func:`mne.read_dipole` should be used.
+
+    Parameters
+    ----------
+    info : instance of Info
+        The measurement info.
+    data : array, shape (n_channels, n_times)
+        The dipole data.
+    times : array, shape (n_times,)
+        The time points.
+    nave : int
+        Number of averages.
+    aspect_kind : int
+        The kind of data.
+    first : int
+        First sample.
+    last : int
+        Last sample.
+    comment : str
+        The dipole comment.
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    See Also
+    --------
+    read_dipole
+    Dipole
+
+    Notes
+    -----
+    This class is for fixed-position dipole fits, where the position
+    (and maybe orientation) is static over time. For sequential dipole fits,
+    where the position can change a function of time, use :class:`mne.Dipole`.
+
+    .. versionadded:: 0.12
+    """
+    @verbose
+    def __init__(self, info, data, times, nave, aspect_kind, first, last,
+                 comment, verbose=None):
+        self.info = info
+        self.nave = nave
+        self._aspect_kind = aspect_kind
+        self.kind = _aspect_rev.get(str(aspect_kind), 'Unknown')
+        self.first = first
+        self.last = last
+        self.comment = comment
+        self.times = times
+        self.data = data
+        self.verbose = verbose
+
+    @property
+    def ch_names(self):
+        return self.info['ch_names']
+
+    @verbose
+    def save(self, fname, verbose=None):
+        """Save dipole in a .fif file
+
+        Parameters
+        ----------
+        fname : str
+            The name of the .fif file. Must end with ``'.fif'`` or
+            ``'.fif.gz'`` to make it explicit that the file contains
+            dipole information in FIF format.
+        verbose : bool, str, int, or None
+            If not None, override default verbose level (see mne.verbose).
+        """
+        check_fname(fname, 'DipoleFixed', ('-dip.fif', '-dip.fif.gz'),
+                    ('.fif', '.fif.gz'))
+        _write_evokeds(fname, self, check=False)
+
+    def plot(self, show=True):
+        """Plot dipole data
+
+        Parameters
+        ----------
+        show : bool
+            Call pyplot.show() at the end or not.
+
+        Returns
+        -------
+        fig : instance of matplotlib.figure.Figure
+            The figure containing the time courses.
+        """
+        return _plot_evoked(self, picks=None, exclude=(), unit=True, show=show,
+                            ylim=None, xlim='tight', proj=False, hline=None,
+                            units=None, scalings=None, titles=None, axes=None,
+                            gfp=False, window_title=None, spatial_colors=False,
+                            plot_type="butterfly", selectable=False)
+
+
 # #############################################################################
 # IO
-
 @verbose
 def read_dipole(fname, verbose=None):
     """Read .dip file from Neuromag/xfit or MNE
@@ -221,15 +346,23 @@ def read_dipole(fname, verbose=None):
     Parameters
     ----------
     fname : str
-        The name of the .dip file.
+        The name of the .dip or .fif file.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
     Returns
     -------
-    dipole : instance of Dipole
+    dipole : instance of Dipole or DipoleFixed
         The dipole.
+
+    See Also
+    --------
+    mne.Dipole
+    mne.DipoleFixed
     """
+    _check_fname(fname, overwrite=True, must_exist=True)
+    if fname.endswith('.fif') or fname.endswith('.fif.gz'):
+        return _read_dipole_fixed(fname)
     try:
         data = np.loadtxt(fname, comments='%')
     except:
@@ -323,31 +456,56 @@ def _dipole_gof(uu, sing, vv, B, B2):
     return gof, one
 
 
-def _fit_Q(fwd_data, whitener, proj_op, B, B2, B_orig, rd):
+def _fit_Q(fwd_data, whitener, proj_op, B, B2, B_orig, rd, ori=None):
     """Fit the dipole moment once the location is known"""
-    fwd, fwd_orig, scales = _dipole_forwards(fwd_data, whitener,
-                                             rd[np.newaxis, :])
-    uu, sing, vv = linalg.svd(fwd, full_matrices=False)
-    gof, one = _dipole_gof(uu, sing, vv, B, B2)
-    ncomp = len(one)
-    # Counteract the effect of column normalization
-    Q = scales[0] * np.sum(uu.T[:ncomp] * (one / sing[:ncomp])[:, np.newaxis],
-                           axis=0)
-    # apply the projector to both elements
-    B_residual = np.dot(proj_op, B_orig) - np.dot(np.dot(Q, fwd_orig),
-                                                  proj_op.T)
+    if 'fwd' in fwd_data:
+        # should be a single precomputed "guess" (i.e., fixed position)
+        assert rd is None
+        fwd = fwd_data['fwd']
+        assert fwd.shape[0] == 3
+        fwd_orig = fwd_data['fwd_orig']
+        assert fwd_orig.shape[0] == 3
+        scales = fwd_data['scales']
+        assert scales.shape == (3,)
+        fwd_svd = fwd_data['fwd_svd'][0]
+    else:
+        fwd, fwd_orig, scales = _dipole_forwards(fwd_data, whitener,
+                                                 rd[np.newaxis, :])
+        fwd_svd = None
+    if ori is None:
+        if fwd_svd is None:
+            fwd_svd = linalg.svd(fwd, full_matrices=False)
+        uu, sing, vv = fwd_svd
+        gof, one = _dipole_gof(uu, sing, vv, B, B2)
+        ncomp = len(one)
+        # Counteract the effect of column normalization
+        Q = scales[0] * np.sum(uu.T[:ncomp] *
+                               (one / sing[:ncomp])[:, np.newaxis], axis=0)
+    else:
+        fwd = np.dot(ori[np.newaxis], fwd)
+        sing = np.linalg.norm(fwd)
+        one = np.dot(fwd / sing, B)
+        gof = (one * one)[0] / B2
+        Q = ori * (scales[0] * np.sum(one / sing))
+    B_residual = _compute_residual(proj_op, B_orig, fwd_orig, Q)
     return Q, gof, B_residual
 
 
-def _fit_dipoles(min_dist_to_inner_skull, data, times, guess_rrs,
-                 guess_fwd_svd, fwd_data, whitener, proj_op, n_jobs):
+def _compute_residual(proj_op, B_orig, fwd_orig, Q):
+    """Compute the residual"""
+    # apply the projector to both elements
+    return np.dot(proj_op, B_orig) - np.dot(np.dot(Q, fwd_orig), proj_op.T)
+
+
+def _fit_dipoles(fun, min_dist_to_inner_skull, data, times, guess_rrs,
+                 guess_data, fwd_data, whitener, proj_op, ori, n_jobs):
     """Fit a single dipole to the given whitened, projected data"""
     from scipy.optimize import fmin_cobyla
-    parallel, p_fun, _ = parallel_func(_fit_dipole, n_jobs)
+    parallel, p_fun, _ = parallel_func(fun, n_jobs)
     # parallel over time points
     res = parallel(p_fun(min_dist_to_inner_skull, B, t, guess_rrs,
-                         guess_fwd_svd, fwd_data, whitener, proj_op,
-                         fmin_cobyla)
+                         guess_data, fwd_data, whitener, proj_op,
+                         fmin_cobyla, ori)
                    for B, t in zip(data.T, times))
     pos = np.array([r[0] for r in res])
     amp = np.array([r[1] for r in res])
@@ -451,47 +609,51 @@ def _simplex_minimize(p, ftol, stol, fun, max_eval=1000):
 '''
 
 
+def _surface_constraint(rd, surf, min_dist_to_inner_skull):
+    """Surface fitting constraint"""
+    dist = _compute_nearest(surf['rr'], rd[np.newaxis, :],
+                            return_dists=True)[1][0]
+    if _points_outside_surface(rd[np.newaxis, :], surf, 1)[0]:
+        dist *= -1.
+    # Once we know the dipole is below the inner skull,
+    # let's check if its distance to the inner skull is at least
+    # min_dist_to_inner_skull. This can be enforced by adding a
+    # constrain proportional to its distance.
+    dist -= min_dist_to_inner_skull
+    return dist
+
+
+def _sphere_constraint(rd, r0, R_adj):
+    """Sphere fitting constraint"""
+    return R_adj - np.sqrt(np.sum((rd - r0) ** 2))
+
+
 def _fit_dipole(min_dist_to_inner_skull, B_orig, t, guess_rrs,
-                guess_fwd_svd, fwd_data, whitener, proj_op,
-                fmin_cobyla):
+                guess_data, fwd_data, whitener, proj_op,
+                fmin_cobyla, ori):
     """Fit a single bit of data"""
     B = np.dot(whitener, B_orig)
 
     # make constraint function to keep the solver within the inner skull
     if isinstance(fwd_data['inner_skull'], dict):  # bem
         surf = fwd_data['inner_skull']
-
-        def constraint(rd):
-
-            dist = _compute_nearest(surf['rr'], rd[np.newaxis, :],
-                                    return_dists=True)[1][0]
-
-            if _points_outside_surface(rd[np.newaxis, :], surf, 1)[0]:
-                dist *= -1.
-
-            # Once we know the dipole is below the inner skull,
-            # let's check if its distance to the inner skull is at least
-            # min_dist_to_inner_skull. This can be enforced by adding a
-            # constrain proportional to its distance.
-            dist -= min_dist_to_inner_skull
-            return dist
-
+        constraint = partial(_surface_constraint, surf=surf,
+                             min_dist_to_inner_skull=min_dist_to_inner_skull)
     else:  # sphere
         surf = None
         R, r0 = fwd_data['inner_skull']
-        R_adj = R - min_dist_to_inner_skull
-
-        def constraint(rd):
-            return R_adj - np.sqrt(np.sum((rd - r0) ** 2))
+        constraint = partial(_sphere_constraint, r0=r0,
+                             R_adj=R - min_dist_to_inner_skull)
+        del R, r0
 
     # Find a good starting point (find_best_guess in C)
     B2 = np.dot(B, B)
     if B2 == 0:
-        logger.warning('Zero field found for time %s' % t)
+        warn('Zero field found for time %s' % t)
         return np.zeros(3), 0, np.zeros(3), 0
 
     idx = np.argmin([_fit_eval(guess_rrs[[fi], :], B, B2, fwd_svd)
-                     for fi, fwd_svd in enumerate(guess_fwd_svd)])
+                     for fi, fwd_svd in enumerate(guess_data['fwd_svd'])])
     x0 = guess_rrs[idx]
     fun = partial(_fit_eval, B=B, B2=B2, fwd_data=fwd_data, whitener=whitener)
 
@@ -509,16 +671,15 @@ def _fit_dipole(min_dist_to_inner_skull, B_orig, t, guess_rrs,
 
     # Compute the dipole moment at the final point
     Q, gof, residual = _fit_Q(fwd_data, whitener, proj_op, B, B2, B_orig,
-                              rd_final)
+                              rd_final, ori=ori)
     amp = np.sqrt(np.dot(Q, Q))
     norm = 1. if amp == 0. else amp
     ori = Q / norm
 
     msg = '---- Fitted : %7.1f ms' % (1000. * t)
     if surf is not None:
-        dist_to_inner_skull = _compute_nearest(surf['rr'],
-                                               rd_final[np.newaxis, :],
-                                               return_dists=True)[1][0]
+        dist_to_inner_skull = _compute_nearest(
+            surf['rr'], rd_final[np.newaxis, :], return_dists=True)[1][0]
         msg += (", distance to inner skull : %2.4f mm"
                 % (dist_to_inner_skull * 1000.))
 
@@ -526,9 +687,31 @@ def _fit_dipole(min_dist_to_inner_skull, B_orig, t, guess_rrs,
     return rd_final, amp, ori, gof, residual
 
 
+def _fit_dipole_fixed(min_dist_to_inner_skull, B_orig, t, guess_rrs,
+                      guess_data, fwd_data, whitener, proj_op,
+                      fmin_cobyla, ori):
+    """Fit a data using a fixed position"""
+    B = np.dot(whitener, B_orig)
+    B2 = np.dot(B, B)
+    if B2 == 0:
+        warn('Zero field found for time %s' % t)
+        return np.zeros(3), 0, np.zeros(3), 0
+    # Compute the dipole moment
+    Q, gof, residual = _fit_Q(guess_data, whitener, proj_op, B, B2, B_orig,
+                              rd=None, ori=ori)
+    if ori is None:
+        amp = np.sqrt(np.dot(Q, Q))
+        norm = 1. if amp == 0. else amp
+        ori = Q / norm
+    else:
+        amp = np.dot(Q, ori)
+    # No corresponding 'logger' message here because it should go *very* fast
+    return guess_rrs[0], amp, ori, gof, residual
+
+
 @verbose
 def fit_dipole(evoked, cov, bem, trans=None, min_dist=5., n_jobs=1,
-               verbose=None):
+               pos=None, ori=None, verbose=None):
     """Fit a dipole
 
     Parameters
@@ -550,13 +733,32 @@ def fit_dipole(evoked, cov, bem, trans=None, min_dist=5., n_jobs=1,
     n_jobs : int
         Number of jobs to run in parallel (used in field computation
         and fitting).
+    pos : ndarray, shape (3,) | None
+        Position of the dipole to use. If None (default), sequential
+        fitting (different position and orientation for each time instance)
+        is performed. If a position (in head coords) is given as an array,
+        the position is fixed during fitting.
+
+        .. versionadded:: 0.12
+
+    ori : ndarray, shape (3,) | None
+        Orientation of the dipole to use. If None (default), the
+        orientation is free to change as a function of time. If an
+        orientation (in head coordinates) is given as an array, ``pos``
+        must also be provided, and the routine computes the amplitude and
+        goodness of fit of the dipole at the given position and orientation
+        for each time instant.
+
+        .. versionadded:: 0.12
+
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
     Returns
     -------
-    dip : instance of Dipole
-        The dipole fits.
+    dip : instance of Dipole or DipoleFixed
+        The dipole fits. A :class:`mne.DipoleFixed` is returned if
+        ``pos`` and ``ori`` are both not None.
     residual : ndarray, shape (n_meeg_channels, n_times)
         The good M-EEG data channels with the fitted dipolar activity
         removed.
@@ -578,9 +780,10 @@ def fit_dipole(evoked, cov, bem, trans=None, min_dist=5., n_jobs=1,
     if _needs_eeg_average_ref_proj(evoked.info):
         raise ValueError('EEG average reference is mandatory for dipole '
                          'fitting.')
-
     if min_dist < 0:
         raise ValueError('min_dist should be positive. Got %s' % min_dist)
+    if ori is not None and pos is None:
+        raise ValueError('pos must be provided if ori is not None')
 
     data = evoked.data
     info = evoked.info
@@ -594,13 +797,13 @@ def fit_dipole(evoked, cov, bem, trans=None, min_dist=5., n_jobs=1,
     # Figure out our inputs
     neeg = len(pick_types(info, meg=False, eeg=True, exclude=[]))
     if isinstance(bem, string_types):
-        logger.info('BEM              : %s' % bem)
+        logger.info('BEM               : %s' % bem)
     if trans is not None:
-        logger.info('MRI transform    : %s' % trans)
+        logger.info('MRI transform     : %s' % trans)
         mri_head_t, trans = _get_trans(trans)
     else:
         mri_head_t = Transform('head', 'mri', np.eye(4))
-    bem = _setup_bem(bem, bem, neeg, mri_head_t)
+    bem = _setup_bem(bem, bem, neeg, mri_head_t, verbose=False)
     if not bem['is_sphere']:
         if trans is None:
             raise ValueError('mri must not be None if BEM is provided')
@@ -608,38 +811,78 @@ def fit_dipole(evoked, cov, bem, trans=None, min_dist=5., n_jobs=1,
         inner_skull = _bem_find_surface(bem, 'inner_skull')
         inner_skull = inner_skull.copy()
         R, r0 = _fit_sphere(inner_skull['rr'], disp=False)
+        # r0 back to head frame for logging
         r0 = apply_trans(mri_head_t['trans'], r0[np.newaxis, :])[0]
-        logger.info('Grid origin      : '
+        logger.info('Head origin       : '
                     '%6.1f %6.1f %6.1f mm rad = %6.1f mm.'
                     % (1000 * r0[0], 1000 * r0[1], 1000 * r0[2], 1000 * R))
     else:
         r0 = bem['r0']
-        logger.info('Sphere model     : origin at (% 7.2f % 7.2f % 7.2f) mm'
-                    % (1000 * r0[0], 1000 * r0[1], 1000 * r0[2]))
-        if 'layers' in bem:
+        if len(bem.get('layers', [])) > 0:
             R = bem['layers'][0]['rad']
-        else:
-            R = np.inf
-        inner_skull = [R, r0]
+            kind = 'rad'
+        else:  # MEG-only
+            # Use the minimum distance to the MEG sensors as the radius then
+            R = np.dot(linalg.inv(info['dev_head_t']['trans']),
+                       np.hstack([r0, [1.]]))[:3]  # r0 -> device
+            R = R - [info['chs'][pick]['loc'][:3]
+                     for pick in pick_types(info, meg=True, exclude=[])]
+            if len(R) == 0:
+                raise RuntimeError('No MEG channels found, but MEG-only '
+                                   'sphere model used')
+            R = np.min(np.sqrt(np.sum(R * R, axis=1)))  # use dist to sensors
+            kind = 'max_rad'
+        logger.info('Sphere model      : origin at (% 7.2f % 7.2f % 7.2f) mm, '
+                    '%s = %6.1f mm'
+                    % (1000 * r0[0], 1000 * r0[1], 1000 * r0[2], kind, R))
+        inner_skull = [R, r0]  # NB sphere model defined in head frame
     r0_mri = apply_trans(invert_transform(mri_head_t)['trans'],
                          r0[np.newaxis, :])[0]
-
-    # Eventually these could be parameters, but they are just used for
-    # the initial grid anyway
-    guess_grid = 0.02  # MNE-C uses 0.01, but this is faster w/similar perf
-    guess_mindist = max(0.005, min_dist_to_inner_skull)
-    guess_exclude = 0.02
-    accurate = False  # can be made an option later (shouldn't make big diff)
-
-    logger.info('Guess grid       : %6.1f mm' % (1000 * guess_grid,))
-    if guess_mindist > 0.0:
-        logger.info('Guess mindist    : %6.1f mm' % (1000 * guess_mindist,))
-    if guess_exclude > 0:
-        logger.info('Guess exclude    : %6.1f mm' % (1000 * guess_exclude,))
-    logger.info('Using %s MEG coil definitions.'
-                % ("accurate" if accurate else "standard"))
+    accurate = False  # can be an option later (shouldn't make big diff)
+
+    # Deal with DipoleFixed cases here
+    if pos is not None:
+        fixed_position = True
+        pos = np.array(pos, float)
+        if pos.shape != (3,):
+            raise ValueError('pos must be None or a 3-element array-like,'
+                             ' got %s' % (pos,))
+        logger.info('Fixed position    : %6.1f %6.1f %6.1f mm'
+                    % tuple(1000 * pos))
+        if ori is not None:
+            ori = np.array(ori, float)
+            if ori.shape != (3,):
+                raise ValueError('oris must be None or a 3-element array-like,'
+                                 ' got %s' % (ori,))
+            norm = np.sqrt(np.sum(ori * ori))
+            if not np.isclose(norm, 1):
+                raise ValueError('ori must be a unit vector, got length %s'
+                                 % (norm,))
+            logger.info('Fixed orientation  : %6.4f %6.4f %6.4f mm'
+                        % tuple(ori))
+        else:
+            logger.info('Free orientation   : <time-varying>')
+        fit_n_jobs = 1  # only use 1 job to do the guess fitting
+    else:
+        fixed_position = False
+        # Eventually these could be parameters, but they are just used for
+        # the initial grid anyway
+        guess_grid = 0.02  # MNE-C uses 0.01, but this is faster w/similar perf
+        guess_mindist = max(0.005, min_dist_to_inner_skull)
+        guess_exclude = 0.02
+
+        logger.info('Guess grid        : %6.1f mm' % (1000 * guess_grid,))
+        if guess_mindist > 0.0:
+            logger.info('Guess mindist     : %6.1f mm'
+                        % (1000 * guess_mindist,))
+        if guess_exclude > 0:
+            logger.info('Guess exclude     : %6.1f mm'
+                        % (1000 * guess_exclude,))
+        logger.info('Using %s MEG coil definitions.'
+                    % ("accurate" if accurate else "standard"))
+        fit_n_jobs = n_jobs
     if isinstance(cov, string_types):
-        logger.info('Noise covariance : %s' % (cov,))
+        logger.info('Noise covariance  : %s' % (cov,))
         cov = read_cov(cov, verbose=False)
     logger.info('')
 
@@ -682,39 +925,87 @@ def fit_dipole(evoked, cov, bem, trans=None, min_dist=5., n_jobs=1,
     whitener = _get_whitener_data(info, cov, picks, verbose=False)
 
     # Proceed to computing the fits (make_guess_data)
-    logger.info('\n---- Computing the forward solution for the guesses...')
-    guess_src = _make_guesses(inner_skull, r0_mri,
-                              guess_grid, guess_exclude, guess_mindist,
-                              n_jobs=n_jobs)[0]
+    if fixed_position:
+        guess_src = dict(nuse=1, rr=pos[np.newaxis], inuse=np.array([True]))
+        logger.info('Compute forward for dipole location...')
+    else:
+        logger.info('\n---- Computing the forward solution for the guesses...')
+        guess_src = _make_guesses(inner_skull, r0_mri,
+                                  guess_grid, guess_exclude, guess_mindist,
+                                  n_jobs=n_jobs)[0]
+        # grid coordinates go from mri to head frame
+        transform_surface_to(guess_src, 'head', mri_head_t)
+        logger.info('Go through all guess source locations...')
+
+    # inner_skull goes from mri to head frame
     if isinstance(inner_skull, dict):
         transform_surface_to(inner_skull, 'head', mri_head_t)
-    transform_surface_to(guess_src, 'head', mri_head_t)
+    if fixed_position:
+        if isinstance(inner_skull, dict):
+            check = _surface_constraint(pos, inner_skull,
+                                        min_dist_to_inner_skull)
+        else:
+            check = _sphere_constraint(pos, r0,
+                                       R_adj=R - min_dist_to_inner_skull)
+        if check <= 0:
+            raise ValueError('fixed position is %0.1fmm outside the inner '
+                             'skull boundary' % (-1000 * check,))
 
-    # C code computes guesses using a sphere model for speed, don't bother here
-    logger.info('Go through all guess source locations...')
+    # C code computes guesses w/sphere model for speed, don't bother here
     fwd_data = dict(coils_list=[megcoils, eegels], infos=[meg_info, None],
                     ccoils_list=[compcoils, None], coil_types=['meg', 'eeg'],
                     inner_skull=inner_skull)
+    # fwd_data['inner_skull'] in head frame, bem in mri, confusing...
     _prep_field_computation(guess_src['rr'], bem, fwd_data, n_jobs,
                             verbose=False)
-    guess_fwd = _dipole_forwards(fwd_data, whitener, guess_src['rr'],
-                                 n_jobs=n_jobs)[0]
+    guess_fwd, guess_fwd_orig, guess_fwd_scales = _dipole_forwards(
+        fwd_data, whitener, guess_src['rr'], n_jobs=fit_n_jobs)
     # decompose ahead of time
-    guess_fwd_svd = [linalg.svd(fwd, overwrite_a=True, full_matrices=False)
+    guess_fwd_svd = [linalg.svd(fwd, overwrite_a=False, full_matrices=False)
                      for fwd in np.array_split(guess_fwd,
                                                len(guess_src['rr']))]
-    del guess_fwd  # destroyed
-    logger.info('[done %d sources]' % guess_src['nuse'])
+    guess_data = dict(fwd=guess_fwd, fwd_svd=guess_fwd_svd,
+                      fwd_orig=guess_fwd_orig, scales=guess_fwd_scales)
+    del guess_fwd, guess_fwd_svd, guess_fwd_orig, guess_fwd_scales  # destroyed
+    pl = '' if guess_src['nuse'] == 1 else 's'
+    logger.info('[done %d source%s]' % (guess_src['nuse'], pl))
 
     # Do actual fits
     data = data[picks]
     ch_names = [info['ch_names'][p] for p in picks]
     proj_op = make_projector(info['projs'], ch_names, info['bads'])[0]
-    out = _fit_dipoles(min_dist_to_inner_skull, data, times, guess_src['rr'],
-                       guess_fwd_svd, fwd_data,
-                       whitener, proj_op, n_jobs)
-    dipoles = Dipole(times, out[0], out[1], out[2], out[3], comment)
+    fun = _fit_dipole_fixed if fixed_position else _fit_dipole
+    out = _fit_dipoles(
+        fun, min_dist_to_inner_skull, data, times, guess_src['rr'],
+        guess_data, fwd_data, whitener, proj_op, ori, n_jobs)
+    if fixed_position and ori is not None:
+        # DipoleFixed
+        data = np.array([out[1], out[3]])
+        out_info = deepcopy(info)
+        loc = np.concatenate([pos, ori, np.zeros(6)])
+        out_info['chs'] = [
+            dict(ch_name='dip 01', loc=loc, kind=FIFF.FIFFV_DIPOLE_WAVE,
+                 coord_frame=FIFF.FIFFV_COORD_UNKNOWN, unit=FIFF.FIFF_UNIT_AM,
+                 coil_type=FIFF.FIFFV_COIL_DIPOLE,
+                 unit_mul=0, range=1, cal=1., scanno=1, logno=1),
+            dict(ch_name='goodness', loc=np.zeros(12),
+                 kind=FIFF.FIFFV_GOODNESS_FIT, unit=FIFF.FIFF_UNIT_AM,
+                 coord_frame=FIFF.FIFFV_COORD_UNKNOWN,
+                 coil_type=FIFF.FIFFV_COIL_NONE,
+                 unit_mul=0, range=1., cal=1., scanno=2, logno=100)]
+        for key in ['hpi_meas', 'hpi_results', 'projs']:
+            out_info[key] = list()
+        for key in ['acq_pars', 'acq_stim', 'description', 'dig',
+                    'experimenter', 'hpi_subsystem', 'proj_id', 'proj_name',
+                    'subject_info']:
+            out_info[key] = None
+        out_info._update_redundant()
+        out_info._check_consistency()
+        dipoles = DipoleFixed(out_info, data, times, evoked.nave,
+                              evoked._aspect_kind, evoked.first, evoked.last,
+                              comment)
+    else:
+        dipoles = Dipole(times, out[0], out[1], out[2], out[3], comment)
     residual = out[4]
-
-    logger.info('%d dipoles fitted' % len(dipoles.times))
+    logger.info('%d time points fitted' % len(dipoles.times))
     return dipoles, residual
diff --git a/mne/epochs.py b/mne/epochs.py
index 1633734..8dbc202 100644
--- a/mne/epochs.py
+++ b/mne/epochs.py
@@ -11,7 +11,6 @@
 # License: BSD (3-clause)
 
 from copy import deepcopy
-import warnings
 import json
 import os.path as op
 from distutils.version import LooseVersion
@@ -28,20 +27,24 @@ from .io.tree import dir_tree_find
 from .io.tag import read_tag, read_tag_info
 from .io.constants import FIFF
 from .io.pick import (pick_types, channel_indices_by_type, channel_type,
-                      pick_channels, pick_info, _pick_data_channels)
+                      pick_channels, pick_info, _pick_data_channels,
+                      _pick_aux_channels, _DATA_CH_TYPES_SPLIT)
 from .io.proj import setup_proj, ProjMixin, _proj_equal
-from .io.base import _BaseRaw, ToDataFrameMixin
+from .io.base import _BaseRaw, ToDataFrameMixin, TimeMixin
 from .bem import _check_origin
-from .evoked import EvokedArray, _aspect_rev
-from .baseline import rescale
+from .evoked import EvokedArray
+from .baseline import rescale, _log_rescale
 from .channels.channels import (ContainsMixin, UpdateChannelsMixin,
                                 SetChannelsMixin, InterpolationMixin)
 from .filter import resample, detrend, FilterMixin
 from .event import _read_events_fif
 from .fixes import in1d, _get_args
-from .viz import plot_epochs, plot_epochs_psd, plot_epochs_psd_topomap
+from .viz import (plot_epochs, plot_epochs_psd, plot_epochs_psd_topomap,
+                  plot_epochs_image, plot_topo_image_epochs)
 from .utils import (check_fname, logger, verbose, _check_type_picks,
-                    _time_mask, check_random_state, object_hash)
+                    _time_mask, check_random_state, object_hash, warn,
+                    _check_copy_dep)
+from .utils import deprecated
 from .externals.six import iteritems, string_types
 from .externals.six.moves import zip
 
@@ -139,13 +142,13 @@ def _save_split(epochs, fname, part_idx, n_parts):
 
 class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                   SetChannelsMixin, InterpolationMixin, FilterMixin,
-                  ToDataFrameMixin):
+                  ToDataFrameMixin, TimeMixin):
     """Abstract base class for Epochs-type classes
 
     This class provides basic functionality and should never be instantiated
     directly. See Epochs below for an explanation of the parameters.
     """
-    def __init__(self, info, data, events, event_id, tmin, tmax,
+    def __init__(self, info, data, events, event_id=None, tmin=-0.2, tmax=0.5,
                  baseline=(None, 0), raw=None,
                  picks=None, name='Unknown', reject=None, flat=None,
                  decim=1, reject_tmin=None, reject_tmax=None, detrend=None,
@@ -193,8 +196,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                     if on_missing == 'error':
                         raise ValueError(msg)
                     elif on_missing == 'warning':
-                        logger.warning(msg)
-                        warnings.warn(msg)
+                        warn(msg)
                     else:  # on_missing == 'ignore':
                         pass
 
@@ -213,9 +215,8 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             n_events = len(events)
             if n_events > 1:
                 if np.diff(events.astype(np.int64)[:, 0]).min() <= 0:
-                    warnings.warn('The events passed to the Epochs '
-                                  'constructor are not chronologically '
-                                  'ordered.', RuntimeWarning)
+                    warn('The events passed to the Epochs constructor are not '
+                         'chronologically ordered.', RuntimeWarning)
 
             if n_events > 0:
                 logger.info('%d matching events found' % n_events)
@@ -255,7 +256,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                     raise ValueError(err)
         if tmin > tmax:
             raise ValueError('tmin has to be less than or equal to tmax')
-
+        _log_rescale(baseline)
         self.baseline = baseline
         self.reject_tmin = reject_tmin
         self.reject_tmax = reject_tmax
@@ -349,7 +350,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         assert self._data.shape[-1] == len(self.times)
         return self
 
-    def decimate(self, decim, copy=False):
+    def decimate(self, decim, copy=None, offset=0):
         """Decimate the epochs
 
         Parameters
@@ -357,7 +358,15 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         decim : int
             The amount to decimate data.
         copy : bool
-            If True, operate on and return a copy of the Epochs object.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
+        offset : int
+            Apply an offset to where the decimation starts relative to the
+            sample corresponding to t=0. The offset is in samples at the
+            current sampling rate.
+
+            .. versionadded:: 0.12
 
         Returns
         -------
@@ -375,29 +384,31 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         if decim < 1 or decim != int(decim):
             raise ValueError('decim must be an integer > 0')
         decim = int(decim)
-        epochs = self.copy() if copy else self
+        epochs = _check_copy_dep(self, copy)
         del self
 
         new_sfreq = epochs.info['sfreq'] / float(decim)
         lowpass = epochs.info['lowpass']
         if decim > 1 and lowpass is None:
-            warnings.warn('The measurement information indicates data is not '
-                          'low-pass filtered. The decim=%i parameter will '
-                          'result in a sampling frequency of %g Hz, which can '
-                          'cause aliasing artifacts.'
-                          % (decim, new_sfreq))
+            warn('The measurement information indicates data is not low-pass '
+                 'filtered. The decim=%i parameter will result in a sampling '
+                 'frequency of %g Hz, which can cause aliasing artifacts.'
+                 % (decim, new_sfreq))
         elif decim > 1 and new_sfreq < 2.5 * lowpass:
-            warnings.warn('The measurement information indicates a low-pass '
-                          'frequency of %g Hz. The decim=%i parameter will '
-                          'result in a sampling frequency of %g Hz, which can '
-                          'cause aliasing artifacts.'
-                          % (lowpass, decim, new_sfreq))  # > 50% nyquist limit
-
+            warn('The measurement information indicates a low-pass frequency '
+                 'of %g Hz. The decim=%i parameter will result in a sampling '
+                 'frequency of %g Hz, which can cause aliasing artifacts.'
+                 % (lowpass, decim, new_sfreq))  # > 50% nyquist lim
+        offset = int(offset)
+        if not 0 <= offset < decim:
+            raise ValueError('decim must be at least 0 and less than %s, got '
+                             '%s' % (decim, offset))
         epochs._decim *= decim
         start_idx = int(round(epochs._raw_times[0] * (epochs.info['sfreq'] *
                                                       epochs._decim)))
         i_start = start_idx % epochs._decim
-        decim_slice = slice(i_start, len(epochs._raw_times), epochs._decim)
+        decim_slice = slice(i_start + offset, len(epochs._raw_times),
+                            epochs._decim)
         epochs.info['sfreq'] = new_sfreq
         if epochs.preload:
             epochs._data = epochs._data[:, :, decim_slice].copy()
@@ -411,7 +422,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         return epochs
 
     @verbose
-    def apply_baseline(self, baseline, verbose=None):
+    def apply_baseline(self, baseline, copy=None, verbose=None):
         """Baseline correct epochs
 
         Parameters
@@ -422,6 +433,10 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             of the data is used and if b is None then b is set to the end of
             the interval. If baseline is equal to (None, None) all the time
             interval is used.
+        copy : bool
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
         verbose : bool, str, int, or None
             If not None, override default verbose level (see mne.verbose).
 
@@ -440,13 +455,17 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             raise ValueError('`baseline=%s` is an invalid argument.'
                              % str(baseline))
 
-        data = self._data
-        picks = pick_types(self.info, meg=True, eeg=True, stim=False,
-                           ref_meg=True, eog=True, ecg=True, seeg=True,
-                           emg=True, exclude=[])
+        epochs = _check_copy_dep(self, copy)
+        picks = _pick_data_channels(epochs.info, exclude=[], with_ref_meg=True)
+        picks_aux = _pick_aux_channels(epochs.info, exclude=[])
+        picks = np.sort(np.concatenate((picks, picks_aux)))
+
+        data = epochs._data
         data[:, picks, :] = rescale(data[:, picks, :], self.times, baseline,
-                                    'mean', copy=False)
-        self.baseline = baseline
+                                    copy=False)
+        epochs.baseline = baseline
+
+        return epochs
 
     def _reject_setup(self, reject, flat):
         """Sets self._reject_time and self._channel_type_idx"""
@@ -463,7 +482,9 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                                % (kind, bads))
 
         for key in idx.keys():
-            if len(idx[key]) == 0 and (key in reject or key in flat):
+            # don't throw an error if rejection/flat would do nothing
+            if len(idx[key]) == 0 and (np.isfinite(reject.get(key, np.inf)) or
+                                       flat.get(key, -1) >= 0):
                 # This is where we could eventually add e.g.
                 # self.allow_missing_reject_keys check to allow users to
                 # provide keys that don't exist in data
@@ -521,6 +542,8 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
     @verbose
     def _is_good_epoch(self, data, verbose=None):
         """Determine if epoch is good"""
+        if isinstance(data, string_types):
+            return False, [data]
         if data is None:
             return False, ['NO_DATA']
         n_times = len(self.times)
@@ -543,8 +566,8 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
         Note: operates inplace
         """
-        if epoch is None:
-            return None
+        if (epoch is None) or isinstance(epoch, string_types):
+            return epoch
 
         # Detrend
         if self.detrend is not None:
@@ -554,9 +577,9 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         # Baseline correct
         picks = pick_types(self.info, meg=True, eeg=True, stim=False,
                            ref_meg=True, eog=True, ecg=True, seeg=True,
-                           emg=True, exclude=[])
+                           emg=True, bio=True, ecog=True, exclude=[])
         epoch[picks] = rescale(epoch[picks], self._raw_times, self.baseline,
-                               'mean', copy=False, verbose=verbose)
+                               copy=False, verbose=False)
 
         # handle offset
         if self._offset is not None:
@@ -567,7 +590,10 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         return epoch
 
     def iter_evoked(self):
-        """Iterate over Evoked objects with nave=1
+        """Iterate over epochs as a sequence of Evoked objects
+
+        The Evoked objects yielded will each contain a single epoch (i.e., no
+        averaging is performed).
         """
         self._current = 0
 
@@ -605,9 +631,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         """
         logger.info('Subtracting Evoked from Epochs')
         if evoked is None:
-            picks = pick_types(self.info, meg=True, eeg=True,
-                               stim=False, eog=False, ecg=False, seeg=True,
-                               emg=False, exclude=[])
+            picks = _pick_data_channels(self.info, exclude=[])
             evoked = self.average(picks)
 
         # find the indices of the channels to use
@@ -620,7 +644,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             diff_idx = [self.ch_names.index(ch) for ch in diff_ch]
             diff_types = [channel_type(self.info, idx) for idx in diff_idx]
             bad_idx = [diff_types.index(t) for t in diff_types if t in
-                       ['grad', 'mag', 'eeg', 'seeg']]
+                       _DATA_CH_TYPES_SPLIT]
             if len(bad_idx) > 0:
                 bad_str = ', '.join([diff_ch[ii] for ii in bad_idx])
                 raise ValueError('The following data channels are missing '
@@ -636,7 +660,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
         # handle SSPs
         if not self.proj and evoked.proj:
-            warnings.warn('Evoked has SSP applied while Epochs has not.')
+            warn('Evoked has SSP applied while Epochs has not.')
         if self.proj and not evoked.proj:
             evoked = evoked.copy().apply_proj()
 
@@ -670,13 +694,19 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         Parameters
         ----------
         picks : array-like of int | None
-            If None only MEG, EEG and SEEG channels are kept
+            If None only MEG, EEG, SEEG, and ECoG channels are kept
             otherwise the channels indices in picks are kept.
 
         Returns
         -------
         evoked : instance of Evoked
             The averaged epochs.
+
+        Notes
+        -----
+        Computes an average of all epochs in the instance, even if
+        they correspond to different conditions. To average by condition,
+        do ``epochs[condition].average()`` for each condition separately.
         """
         return self._compute_mean_or_stderr(picks, 'ave')
 
@@ -686,7 +716,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         Parameters
         ----------
         picks : array-like of int | None
-            If None only MEG, EEG and SEEG channels are kept
+            If None only MEG, EEG, SEEG, and ECoG channels are kept
             otherwise the channels indices in picks are kept.
 
         Returns
@@ -731,18 +761,16 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                 data = np.sqrt(data / n_events)
 
         if not _do_std:
-            _aspect_kind = FIFF.FIFFV_ASPECT_AVERAGE
+            kind = 'average'
         else:
-            _aspect_kind = FIFF.FIFFV_ASPECT_STD_ERR
+            kind = 'standard_error'
             data /= np.sqrt(n_events)
         return self._evoked_from_epoch_data(data, self.info, picks, n_events,
-                                            _aspect_kind)
+                                            kind)
 
-    def _evoked_from_epoch_data(self, data, info, picks, n_events,
-                                aspect_kind):
+    def _evoked_from_epoch_data(self, data, info, picks, n_events, kind):
         """Helper to create an evoked object from epoch data"""
         info = deepcopy(info)
-        kind = _aspect_rev.get(str(aspect_kind), 'Unknown')
         evoked = EvokedArray(data, info, tmin=self.times[0],
                              comment=self.name, nave=n_events, kind=kind,
                              verbose=self.verbose)
@@ -760,8 +788,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             raise ValueError('No data channel found when averaging.')
 
         if evoked.nave < 1:
-            warnings.warn('evoked object is empty (based on less '
-                          'than 1 epoch)', RuntimeWarning)
+            warn('evoked object is empty (based on less than 1 epoch)')
 
         return evoked
 
@@ -787,9 +814,17 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Channels to be included. If None only good data channels are used.
             Defaults to None
         scalings : dict | None
-            Scale factors for the traces. If None, defaults to
-            ``dict(mag=1e-12, grad=4e-11, eeg=20e-6, eog=150e-6, ecg=5e-4,
-            emg=1e-3, ref_meg=1e-12, misc=1e-3, stim=1, resp=1, chpi=1e-4)``.
+            Scaling factors for the traces. If any fields in scalings are
+            'auto', the scaling factor is set to match the 99.5th percentile of
+            a subset of the corresponding data. If scalings == 'auto', all
+            scalings fields are set to 'auto'. If any fields are 'auto' and
+            data is not preloaded, a subset of epochs up to 100mb will be
+            loaded. If None, defaults to::
+
+                dict(mag=1e-12, grad=4e-11, eeg=20e-6, eog=150e-6, ecg=5e-4,
+                     emg=1e-3, ref_meg=1e-12, misc=1e-3, stim=1, resp=1,
+                     chpi=1e-4)
+
         show : bool
             Whether to show the figure or not.
         block : bool
@@ -828,10 +863,10 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                            n_epochs=n_epochs, n_channels=n_channels,
                            title=title, show=show, block=block)
 
-    def plot_psd(self, fmin=0, fmax=np.inf, proj=False, n_fft=256,
+    def plot_psd(self, fmin=0, fmax=np.inf, proj=False, bandwidth=None,
+                 adaptive=False, low_bias=True, normalization='length',
                  picks=None, ax=None, color='black', area_mode='std',
-                 area_alpha=0.33, n_overlap=0, dB=True,
-                 n_jobs=1, verbose=None, show=True):
+                 area_alpha=0.33, dB=True, n_jobs=1, verbose=None, show=True):
         """Plot the power spectral density across epochs
 
         Parameters
@@ -842,8 +877,19 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             End frequency to consider.
         proj : bool
             Apply projection.
-        n_fft : int
-            Number of points to use in Welch FFT calculations.
+        bandwidth : float
+            The bandwidth of the multi taper windowing function in Hz.
+            The default value is a window half-bandwidth of 4.
+        adaptive : bool
+            Use adaptive weights to combine the tapered spectra into PSD
+            (slow, use n_jobs >> 1 to speed up computation).
+        low_bias : bool
+            Only use tapers with more than 90% spectral concentration within
+            bandwidth.
+        normalization : str
+            Either "full" or "length" (default). If "full", the PSD will
+            be normalized by the sampling rate as well as the length of
+            the signal (as in nitime).
         picks : array-like of int | None
             List of channels to use.
         ax : instance of matplotlib Axes | None
@@ -857,8 +903,6 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             these calculations. If None, no area will be plotted.
         area_alpha : float
             Alpha for the area.
-        n_overlap : int
-            The number of points of overlap between blocks.
         dB : bool
             If True, transform data to decibels.
         n_jobs : int
@@ -874,18 +918,18 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Figure distributing one image per channel across sensor topography.
         """
         return plot_epochs_psd(self, fmin=fmin, fmax=fmax, proj=proj,
-                               n_fft=n_fft, picks=picks, ax=ax,
-                               color=color, area_mode=area_mode,
-                               area_alpha=area_alpha,
-                               n_overlap=n_overlap, dB=dB, n_jobs=n_jobs,
-                               verbose=None, show=show)
+                               bandwidth=bandwidth, adaptive=adaptive,
+                               low_bias=low_bias, normalization=normalization,
+                               picks=picks, ax=ax, color=color,
+                               area_mode=area_mode, area_alpha=area_alpha,
+                               dB=dB, n_jobs=n_jobs, verbose=None, show=show)
 
     def plot_psd_topomap(self, bands=None, vmin=None, vmax=None, proj=False,
-                         n_fft=256, ch_type=None,
-                         n_overlap=0, layout=None, cmap='RdBu_r',
-                         agg_fun=None, dB=True, n_jobs=1, normalize=False,
-                         cbar_fmt='%0.3f', outlines='head', show=True,
-                         verbose=None):
+                         bandwidth=None, adaptive=False, low_bias=True,
+                         normalization='length', ch_type=None,
+                         layout=None, cmap='RdBu_r', agg_fun=None, dB=True,
+                         n_jobs=1, normalize=False, cbar_fmt='%0.3f',
+                         outlines='head', show=True, verbose=None):
         """Plot the topomap of the power spectral density across epochs
 
         Parameters
@@ -907,16 +951,25 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             output equals vmax(data). Defaults to None.
         proj : bool
             Apply projection.
-        n_fft : int
-            Number of points to use in Welch FFT calculations.
+        bandwidth : float
+            The bandwidth of the multi taper windowing function in Hz.
+            The default value is a window half-bandwidth of 4 Hz.
+        adaptive : bool
+            Use adaptive weights to combine the tapered spectra into PSD
+            (slow, use n_jobs >> 1 to speed up computation).
+        low_bias : bool
+            Only use tapers with more than 90% spectral concentration within
+            bandwidth.
+        normalization : str
+            Either "full" or "length" (default). If "full", the PSD will
+            be normalized by the sampling rate as well as the length of
+            the signal (as in nitime).
         ch_type : {None, 'mag', 'grad', 'planar1', 'planar2', 'eeg'}
             The channel type to plot. For 'grad', the gradiometers are
             collected in
             pairs and the RMS for each pair is plotted. If None, defaults to
             'mag' if MEG data are present and to 'eeg' if only EEG data are
             present.
-        n_overlap : int
-            The number of points of overlap between blocks.
         layout : None | Layout
             Layout instance specifying sensor positions (does not need to
             be specified for Neuromag data). If possible, the correct layout
@@ -936,7 +989,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         n_jobs : int
             Number of jobs to run in parallel.
         normalize : bool
-            If True, each band will be devided by the total power. Defaults to
+            If True, each band will be divided by the total power. Defaults to
             False.
         cbar_fmt : str
             The colorbar format. Defaults to '%0.3f'.
@@ -962,19 +1015,87 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Figure distributing one image per channel across sensor topography.
         """
         return plot_epochs_psd_topomap(
-            self, bands=bands, vmin=vmin, vmax=vmax, proj=proj, n_fft=n_fft,
-            ch_type=ch_type, n_overlap=n_overlap, layout=layout, cmap=cmap,
+            self, bands=bands, vmin=vmin, vmax=vmax, proj=proj,
+            bandwidth=bandwidth, adaptive=adaptive,
+            low_bias=low_bias, normalization=normalization,
+            ch_type=ch_type, layout=layout, cmap=cmap,
             agg_fun=agg_fun, dB=dB, n_jobs=n_jobs, normalize=normalize,
             cbar_fmt=cbar_fmt, outlines=outlines, show=show, verbose=None)
 
+    def plot_topo_image(self, layout=None, sigma=0., vmin=None, vmax=None,
+                        colorbar=True, order=None, cmap='RdBu_r',
+                        layout_scale=.95, title=None, scalings=None,
+                        border='none', fig_facecolor='k', font_color='w',
+                        show=True):
+        """Plot Event Related Potential / Fields image on topographies
+
+        Parameters
+        ----------
+        layout: instance of Layout
+            System specific sensor positions.
+        sigma : float
+            The standard deviation of the Gaussian smoothing to apply along the
+            epoch axis to apply in the image. If 0., no smoothing is applied.
+        vmin : float
+            The min value in the image. The unit is uV for EEG channels,
+            fT for magnetometers and fT/cm for gradiometers.
+        vmax : float
+            The max value in the image. The unit is uV for EEG channels,
+            fT for magnetometers and fT/cm for gradiometers.
+        colorbar : bool
+            Display or not a colorbar.
+        order : None | array of int | callable
+            If not None, order is used to reorder the epochs on the y-axis
+            of the image. If it's an array of int it should be of length
+            the number of good epochs. If it's a callable the arguments
+            passed are the times vector and the data as 2d array
+            (data.shape[1] == len(times)).
+        cmap : instance of matplotlib.pyplot.colormap
+            Colors to be mapped to the values.
+        layout_scale: float
+            scaling factor for adjusting the relative size of the layout
+            on the canvas.
+        title : str
+            Title of the figure.
+        scalings : dict | None
+            The scalings of the channel types to be applied for plotting. If
+            None, defaults to `dict(eeg=1e6, grad=1e13, mag=1e15)`.
+        border : str
+            matplotlib borders style to be used for each sensor plot.
+        fig_facecolor : str | obj
+            The figure face color. Defaults to black.
+        font_color : str | obj
+            The color of tick labels in the colorbar. Defaults to white.
+        show : bool
+            Show figure if True.
+
+        Returns
+        -------
+        fig : instance of matplotlib figure
+            Figure distributing one image per channel across sensor topography.
+        """
+        return plot_topo_image_epochs(
+            self, layout=layout, sigma=sigma, vmin=vmin, vmax=vmax,
+            colorbar=colorbar, order=order, cmap=cmap,
+            layout_scale=layout_scale, title=title, scalings=scalings,
+            border=border, fig_facecolor=fig_facecolor, font_color=font_color,
+            show=show)
+
+    @deprecated('drop_bad_epochs method has been renamed drop_bad. '
+                'drop_bad_epochs method will be removed in 0.13')
     def drop_bad_epochs(self, reject='existing', flat='existing'):
+        """Drop bad epochs without retaining the epochs data"""
+        return self.drop_bad(reject, flat)
+
+    @verbose
+    def drop_bad(self, reject='existing', flat='existing', verbose=None):
         """Drop bad epochs without retaining the epochs data.
 
         Should be used before slicing operations.
 
-        .. Warning:: Operation is slow since all epochs have to be read from
-            disk. To avoid reading epochs from disk multiple times, initialize
-            Epochs object with preload=True.
+        .. warning:: This operation is slow since all epochs have to be read
+                     from disk. To avoid reading epochs from disk multiple
+                     times, use :func:`mne.Epochs.load_data()`.
 
         Parameters
         ----------
@@ -989,6 +1110,14 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             are floats that set the minimum acceptable peak-to-peak amplitude.
             If flat is None then no rejection is done. If 'existing',
             then the flat parameters set at instantiation are used.
+        verbose : bool, str, int, or None
+            If not None, override default verbose level (see mne.verbose).
+            Defaults to self.verbose.
+
+        Returns
+        -------
+        epochs : instance of Epochs
+            The epochs with bad epochs dropped. Operates in-place.
 
         Notes
         -----
@@ -1008,6 +1137,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             raise ValueError('reject and flat, if strings, must be "existing"')
         self._reject_setup(reject, flat)
         self._get_data(out=False)
+        return self
 
     def drop_log_stats(self, ignore=('IGNORED',)):
         """Compute the channel stats based on a drop_log from Epochs.
@@ -1061,23 +1191,93 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         if not self._bad_dropped:
             raise ValueError("You cannot use plot_drop_log since bad "
                              "epochs have not yet been dropped. "
-                             "Use epochs.drop_bad_epochs().")
+                             "Use epochs.drop_bad().")
 
         from .viz import plot_drop_log
         return plot_drop_log(self.drop_log, threshold, n_max_plot, subject,
                              color=color, width=width, ignore=ignore,
                              show=show)
 
-    @verbose
+    def plot_image(self, picks=None, sigma=0., vmin=None,
+                   vmax=None, colorbar=True, order=None, show=True,
+                   units=None, scalings=None, cmap='RdBu_r',
+                   fig=None, overlay_times=None):
+        """Plot Event Related Potential / Fields image
+
+        Parameters
+        ----------
+        picks : int | array-like of int | None
+            The indices of the channels to consider. If None, the first
+            five good channels are plotted.
+        sigma : float
+            The standard deviation of the Gaussian smoothing to apply along
+            the epoch axis to apply in the image. If 0., no smoothing is
+            applied.
+        vmin : float
+            The min value in the image. The unit is uV for EEG channels,
+            fT for magnetometers and fT/cm for gradiometers.
+        vmax : float
+            The max value in the image. The unit is uV for EEG channels,
+            fT for magnetometers and fT/cm for gradiometers.
+        colorbar : bool
+            Display or not a colorbar.
+        order : None | array of int | callable
+            If not None, order is used to reorder the epochs on the y-axis
+            of the image. If it's an array of int it should be of length
+            the number of good epochs. If it's a callable the arguments
+            passed are the times vector and the data as 2d array
+            (data.shape[1] == len(times).
+        show : bool
+            Show figure if True.
+        units : dict | None
+            The units of the channel types used for axes lables. If None,
+            defaults to `units=dict(eeg='uV', grad='fT/cm', mag='fT')`.
+        scalings : dict | None
+            The scalings of the channel types to be applied for plotting.
+            If None, defaults to `scalings=dict(eeg=1e6, grad=1e13, mag=1e15,
+            eog=1e6)`.
+        cmap : matplotlib colormap
+            Colormap.
+        fig : matplotlib figure | None
+            Figure instance to draw the image to. Figure must contain two
+            axes for drawing the single trials and evoked responses. If
+            None a new figure is created. Defaults to None.
+        overlay_times : array-like, shape (n_epochs,) | None
+            If not None the parameter is interpreted as time instants in
+            seconds and is added to the image. It is typically useful to
+            display reaction times. Note that it is defined with respect
+            to the order of epochs such that overlay_times[0] corresponds
+            to epochs[0].
+
+        Returns
+        -------
+        figs : list of matplotlib figures
+            One figure per channel displayed.
+        """
+        return plot_epochs_image(self, picks=picks, sigma=sigma, vmin=vmin,
+                                 vmax=vmax, colorbar=colorbar, order=order,
+                                 show=show, units=units, scalings=scalings,
+                                 cmap=cmap, fig=fig,
+                                 overlay_times=overlay_times)
+
+    @deprecated('drop_epochs method has been renamed drop. '
+                'drop_epochs method will be removed in 0.13')
     def drop_epochs(self, indices, reason='USER', verbose=None):
+        """Drop epochs based on indices or boolean mask"""
+        return self.drop(indices, reason, verbose)
+
+    @verbose
+    def drop(self, indices, reason='USER', verbose=None):
         """Drop epochs based on indices or boolean mask
 
-        Note that the indices refer to the current set of undropped epochs
-        rather than the complete set of dropped and undropped epochs.
-        They are therefore not necessarily consistent with any external indices
-        (e.g., behavioral logs). To drop epochs based on external criteria,
-        do not use the preload=True flag when constructing an Epochs object,
-        and call this method before calling the drop_bad_epochs method.
+        .. note:: The indices refer to the current set of undropped epochs
+                  rather than the complete set of dropped and undropped epochs.
+                  They are therefore not necessarily consistent with any
+                  external indices (e.g., behavioral logs). To drop epochs
+                  based on external criteria, do not use the ``preload=True``
+                  flag when constructing an Epochs object, and call this
+                  method before calling the :func:`mne.Epochs.drop_bad` or
+                  :func:`mne.Epochs.load_data` methods.
 
         Parameters
         ----------
@@ -1090,7 +1290,12 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Default: 'USER'.
         verbose : bool, str, int, or None
             If not None, override default verbose level (see mne.verbose).
-            Defaults to raw.verbose.
+            Defaults to self.verbose.
+
+        Returns
+        -------
+        epochs : instance of Epochs
+            The epochs with indices dropped. Operates in-place.
         """
         indices = np.atleast_1d(indices)
 
@@ -1115,6 +1320,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
         count = len(indices)
         logger.info('Dropped %d epoch%s' % (count, '' if count == 1 else 's'))
+        return self
 
     def _get_epoch_from_raw(self, idx, verbose=None):
         """Method to get a given epoch from disk"""
@@ -1123,8 +1329,9 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
     def _project_epoch(self, epoch):
         """Helper to process a raw epoch based on the delayed param"""
         # whenever requested, the first epoch is being projected.
-        if epoch is None:  # can happen if t < 0
-            return None
+        if (epoch is None) or isinstance(epoch, string_types):
+            # can happen if t < 0 or reject based on annotations
+            return epoch
         proj = self._do_delayed_proj or self.proj
         if self._projector is not None and proj is True:
             epoch = np.dot(self._projector, epoch)
@@ -1151,6 +1358,8 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         else:
             # we start out with an empty array, allocate only if necessary
             data = np.empty((0, len(self.info['ch_names']), len(self.times)))
+            logger.info('Loading data for %s events and %s original time '
+                        'points ...' % (n_events, len(self._raw_times)))
         if self._bad_dropped:
             if not out:
                 return
@@ -1172,7 +1381,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                 data[idx] = epoch_out
         else:
             # bads need to be dropped, this might occur after a preload
-            # e.g., when calling drop_bad_epochs w/new params
+            # e.g., when calling drop_bad w/new params
             good_idx = []
             n_out = 0
             assert n_events == len(self.selection)
@@ -1188,10 +1397,11 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                     epoch_noproj = self._get_epoch_from_raw(idx)
                     epoch_noproj = self._detrend_offset_decim(epoch_noproj)
                     epoch = self._project_epoch(epoch_noproj)
+
                 epoch_out = epoch_noproj if self._do_delayed_proj else epoch
-                is_good, offenders = self._is_good_epoch(epoch)
+                is_good, offending_reason = self._is_good_epoch(epoch)
                 if not is_good:
-                    self.drop_log[sel] += offenders
+                    self.drop_log[sel] += offending_reason
                     continue
                 good_idx.append(idx)
 
@@ -1239,7 +1449,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             raise RuntimeError('Since bad epochs have not been dropped, the '
                                'length of the Epochs is not known. Load the '
                                'Epochs with preload=True, or call '
-                               'Epochs.drop_bad_epochs(). To find the number '
+                               'Epochs.drop_bad(). To find the number '
                                'of events in the Epochs, use '
                                'len(Epochs.events).')
         return len(self.events)
@@ -1255,17 +1465,17 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             yield x
 
     def next(self, return_event_id=False):
-        """To make iteration over epochs easy.
+        """Iterate over epoch data.
 
         Parameters
         ----------
         return_event_id : bool
-            If True, return both an epoch and and event_id.
+            If True, return both the epoch data and an event_id.
 
         Returns
         -------
-        epoch : instance of Epochs
-            The epoch.
+        epoch : array of shape (n_channels, n_times)
+            The epoch data.
         event_id : int
             The event id. Only returned if ``return_event_id`` is ``True``.
         """
@@ -1370,7 +1580,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                                if v in epochs.events[:, 2])
         return epochs
 
-    def crop(self, tmin=None, tmax=None, copy=False):
+    def crop(self, tmin=None, tmax=None, copy=None):
         """Crops a time interval from epochs object.
 
         Parameters
@@ -1380,11 +1590,13 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         tmax : float | None
             End time of selection in seconds.
         copy : bool
-            If False epochs is cropped in place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
 
         Returns
         -------
-        epochs : Epochs instance
+        epochs : instance of Epochs
             The cropped epochs.
 
         Notes
@@ -1401,42 +1613,45 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         if tmin is None:
             tmin = self.tmin
         elif tmin < self.tmin:
-            warnings.warn("tmin is not in epochs' time interval."
-                          "tmin is set to epochs.tmin")
+            warn('tmin is not in epochs time interval. tmin is set to '
+                 'epochs.tmin')
             tmin = self.tmin
 
         if tmax is None:
             tmax = self.tmax
         elif tmax > self.tmax:
-            warnings.warn("tmax is not in epochs' time interval."
-                          "tmax is set to epochs.tmax")
+            warn('tmax is not in epochs time interval. tmax is set to '
+                 'epochs.tmax')
             tmax = self.tmax
 
-        tmask = _time_mask(self.times, tmin, tmax)
-        this_epochs = self if not copy else self.copy()
+        tmask = _time_mask(self.times, tmin, tmax, sfreq=self.info['sfreq'])
+        this_epochs = _check_copy_dep(self, copy)
         this_epochs.times = this_epochs.times[tmask]
         this_epochs._raw_times = this_epochs._raw_times[tmask]
         this_epochs._data = this_epochs._data[:, :, tmask]
         return this_epochs
 
     @verbose
-    def resample(self, sfreq, npad=100, window='boxcar', n_jobs=1,
-                 copy=False, verbose=None):
+    def resample(self, sfreq, npad=None, window='boxcar', n_jobs=1,
+                 copy=None, verbose=None):
         """Resample preloaded data
 
         Parameters
         ----------
         sfreq : float
             New sample rate to use
-        npad : int
+        npad : int | str
             Amount to pad the start and end of the data.
+            Can also be "auto" to use a padding that will result in
+            a power-of-two size (can be much faster).
         window : string or tuple
             Window to use in resampling. See scipy.signal.resample.
         n_jobs : int
             Number of jobs to run in parallel.
         copy : bool
-            Whether to operate on a copy of the data (True) or modify data
-            in-place (False). Defaults to False.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
         verbose : bool, str, int, or None
             If not None, override default verbose level (see mne.verbose).
             Defaults to self.verbose.
@@ -1446,6 +1661,11 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         epochs : instance of Epochs
             The resampled epochs object.
 
+        See Also
+        --------
+        mne.Epochs.savgol_filter
+        mne.io.Raw.resample
+
         Notes
         -----
         For some data, it may be more accurate to use npad=0 to reduce
@@ -1454,14 +1674,17 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         # XXX this could operate on non-preloaded data, too
         if not self.preload:
             raise RuntimeError('Can only resample preloaded data')
-
-        inst = self.copy() if copy else self
-
+        if npad is None:
+            npad = 100
+            warn('npad is currently taken to be 100, but will be changed to '
+                 '"auto" in 0.13. Please set the value explicitly.',
+                 DeprecationWarning)
+        inst = _check_copy_dep(self, copy)
         o_sfreq = inst.info['sfreq']
-        inst._data = resample(inst._data, sfreq, o_sfreq, npad,
+        inst._data = resample(inst._data, sfreq, o_sfreq, npad, window=window,
                               n_jobs=n_jobs)
         # adjust indirectly affected variables
-        inst.info['sfreq'] = sfreq
+        inst.info['sfreq'] = float(sfreq)
         inst.times = (np.arange(inst._data.shape[2], dtype=np.float) /
                       sfreq + inst.times[0])
         return inst
@@ -1501,7 +1724,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
         # to know the length accurately. The get_data() call would drop
         # bad epochs anyway
-        self.drop_bad_epochs()
+        self.drop_bad()
         total_size = self[0].get_data().nbytes * len(self)
         n_parts = int(np.ceil(total_size / float(split_size)))
         epoch_idxs = np.array_split(np.arange(len(self)), n_parts)
@@ -1512,7 +1735,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             this_epochs.event_id = self.event_id
             _save_split(this_epochs, fname, part_idx, n_parts)
 
-    def equalize_event_counts(self, event_ids, method='mintime', copy=True):
+    def equalize_event_counts(self, event_ids, method='mintime', copy=None):
         """Equalize the number of trials in each condition
 
         It tries to make the remaining epochs occurring as close as possible in
@@ -1543,8 +1766,9 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             list. If 'mintime', timing differences between each event list
             will be minimized.
         copy : bool
-            If True, a copy of epochs will be returned. Otherwise, the
-            function will operate in-place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
 
         Returns
         -------
@@ -1568,19 +1792,17 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         conditions will contribute evenly. E.g., it is possible to end up
         with 70 'Nonspatial' trials, 69 'Left' and 1 'Right'.
         """
-        if copy is True:
-            epochs = self.copy()
-        else:
-            epochs = self
+        epochs = _check_copy_dep(self, copy, default=True)
         if len(event_ids) == 0:
             raise ValueError('event_ids must have at least one element')
         if not epochs._bad_dropped:
-            epochs.drop_bad_epochs()
+            epochs.drop_bad()
         # figure out how to equalize
         eq_inds = list()
 
         # deal with hierarchical tags
         ids = epochs.event_id
+        orig_ids = list(event_ids)
         tagging = False
         if "/" in "".join(ids):
             # make string inputs a list of length 1
@@ -1598,8 +1820,11 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                          if all(id__ not in ids for id__ in id_)
                          else id_  # straight pass for non-tag inputs
                          for id_ in event_ids]
-            for id_ in event_ids:
-                if len(set([sub_id in ids for sub_id in id_])) != 1:
+            for ii, id_ in enumerate(event_ids):
+                if len(id_) == 0:
+                    raise KeyError(orig_ids[ii] + "not found in the "
+                                   "epoch object's event_id.")
+                elif len(set([sub_id in ids for sub_id in id_])) != 1:
                     err = ("Don't mix hierarchical and regular event_ids"
                            " like in \'%s\'." % ", ".join(id_))
                     raise ValueError(err)
@@ -1625,7 +1850,7 @@ class _BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         indices = _get_drop_indices(event_times, method)
         # need to re-index indices
         indices = np.concatenate([e[idx] for e, idx in zip(eq_inds, indices)])
-        epochs.drop_epochs(indices, reason='EQUALIZED_COUNT')
+        epochs.drop(indices, reason='EQUALIZED_COUNT')
         # actually remove the indices
         return epochs, indices
 
@@ -1671,9 +1896,9 @@ class Epochs(_BaseEpochs):
         and a dict is created with string integer names corresponding
         to the event id integers.
     tmin : float
-        Start time before event.
+        Start time before event. If nothing is provided, defaults to -0.2
     tmax : float
-        End time after event.
+        End time after event. If nothing is provided, defaults to 0.5
     baseline : None or tuple of length 2 (default (None, 0))
         The time interval to apply baseline correction.
         If None do not apply it. If baseline is (a, b)
@@ -1699,8 +1924,8 @@ class Epochs(_BaseEpochs):
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels)
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels)
                           )
 
     flat : dict | None
@@ -1747,6 +1972,10 @@ class Epochs(_BaseEpochs):
         warn, if 'ignore' it will proceed silently. Note.
         If none of the event ids are found in the data, an error will be
         automatically generated irrespective of this parameter.
+    reject_by_annotation : bool
+        Whether to reject based on annotations. If True (default), epochs
+        overlapping with segments whose description begins with ``'bad'`` are
+        rejected. If False, no rejection based on annotations is performed.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
         Defaults to raw.verbose.
@@ -1775,7 +2004,7 @@ class Epochs(_BaseEpochs):
         'NO_DATA' or 'TOO_SHORT' if epoch didn't contain enough data;
         names of channels that exceeded the amplitude threshold;
         'EQUALIZED_COUNTS' (see equalize_event_counts);
-        or 'USER' for user-defined reasons (see drop_epochs).
+        or 'USER' for user-defined reasons (see drop method).
     verbose : bool, str, int, or None
         See above.
 
@@ -1817,11 +2046,11 @@ class Epochs(_BaseEpochs):
     mne.Epochs.equalize_event_counts
     """
     @verbose
-    def __init__(self, raw, events, event_id, tmin, tmax, baseline=(None, 0),
-                 picks=None, name='Unknown', preload=False, reject=None,
-                 flat=None, proj=True, decim=1, reject_tmin=None,
+    def __init__(self, raw, events, event_id=None, tmin=-0.2, tmax=0.5,
+                 baseline=(None, 0), picks=None, name='Unknown', preload=False,
+                 reject=None, flat=None, proj=True, decim=1, reject_tmin=None,
                  reject_tmax=None, detrend=None, add_eeg_ref=True,
-                 on_missing='error', verbose=None):
+                 on_missing='error', reject_by_annotation=True, verbose=None):
         if not isinstance(raw, _BaseRaw):
             raise ValueError('The first argument to `Epochs` must be an '
                              'instance of `mne.io.Raw`')
@@ -1830,6 +2059,7 @@ class Epochs(_BaseEpochs):
         # proj is on when applied in Raw
         proj = proj or raw.proj
 
+        self.reject_by_annotation = reject_by_annotation
         # call _BaseEpochs constructor
         super(Epochs, self).__init__(
             info, None, events, event_id, tmin, tmax, baseline=baseline,
@@ -1840,7 +2070,14 @@ class Epochs(_BaseEpochs):
 
     @verbose
     def _get_epoch_from_raw(self, idx, verbose=None):
-        """Load one epoch from disk"""
+        """Load one epoch from disk
+
+        Returns
+        -------
+        data : array | str | None
+            If string it's details on rejection reason.
+            If None it means no data.
+        """
         if self._raw is None:
             # This should never happen, as raw=None only if preload=True
             raise ValueError('An error has occurred, no valid raw file found.'
@@ -1852,7 +2089,9 @@ class Epochs(_BaseEpochs):
         first_samp = self._raw.first_samp
         start = int(round(event_samp + self.tmin * sfreq)) - first_samp
         stop = start + len(self._raw_times)
-        return None if start < 0 else self._raw[self.picks, start:stop][0]
+        data = self._raw._check_bad_segment(start, stop, self.picks,
+                                            self.reject_by_annotation)
+        return data
 
 
 class EpochsArray(_BaseEpochs):
@@ -1865,12 +2104,14 @@ class EpochsArray(_BaseEpochs):
     info : instance of Info
         Info dictionary. Consider using ``create_info`` to populate
         this structure.
-    events : array of int, shape (n_events, 3)
+    events : None | array of int, shape (n_events, 3)
         The events typically returned by the read_events function.
         If some events don't match the events of interest as specified
         by event_id, they will be marked as 'IGNORED' in the drop log.
+        If None (default), all event values are set to 1 and event time-samples
+        are set to range(n_epochs).
     tmin : float
-        Start time before event.
+        Start time before event. If nothing provided, defaults to -0.2.
     event_id : int | list of int | dict | None
         The id of the event to consider. If dict,
         the keys can later be used to access associated events. Example:
@@ -1886,8 +2127,8 @@ class EpochsArray(_BaseEpochs):
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels)
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels)
                           )
 
     flat : dict | None
@@ -1909,6 +2150,8 @@ class EpochsArray(_BaseEpochs):
         and if b is None then b is set to the end of the interval.
         If baseline is equal to (None, None) all the time
         interval is used.
+    proj : bool | 'delayed'
+        Apply SSP projection vectors. See :class:`mne.Epochs` for details.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
         Defaults to raw.verbose.
@@ -1919,9 +2162,9 @@ class EpochsArray(_BaseEpochs):
     """
 
     @verbose
-    def __init__(self, data, info, events, tmin=0, event_id=None,
+    def __init__(self, data, info, events=None, tmin=0, event_id=None,
                  reject=None, flat=None, reject_tmin=None,
-                 reject_tmax=None, baseline=None, verbose=None):
+                 reject_tmax=None, baseline=None, proj=True, verbose=None):
         dtype = np.complex128 if np.any(np.iscomplex(data)) else np.float64
         data = np.asanyarray(data, dtype=dtype)
         if data.ndim != 3:
@@ -1931,9 +2174,14 @@ class EpochsArray(_BaseEpochs):
         if len(info['ch_names']) != data.shape[1]:
             raise ValueError('Info and data must have same number of '
                              'channels.')
+        if events is None:
+            n_epochs = len(data)
+            events = np.c_[np.arange(n_epochs), np.zeros(n_epochs, int),
+                           np.ones(n_epochs, int)]
         if data.shape[0] != len(events):
             raise ValueError('The number of epochs and the number of events'
                              'must match')
+        info = deepcopy(info)  # do not modify original info
         tmax = (data.shape[2] - 1) / info['sfreq'] + tmin
         if event_id is None:  # convert to int to make typing-checks happy
             event_id = dict((str(e), int(e)) for e in np.unique(events[:, 2]))
@@ -1941,7 +2189,7 @@ class EpochsArray(_BaseEpochs):
                                           tmax, baseline, reject=reject,
                                           flat=flat, reject_tmin=reject_tmin,
                                           reject_tmax=reject_tmax, decim=1,
-                                          add_eeg_ref=False)
+                                          add_eeg_ref=False, proj=proj)
         if len(events) != in1d(self.events[:, 2],
                                list(self.event_id.values())).sum():
             raise ValueError('The events must only contain event numbers from '
@@ -1949,7 +2197,7 @@ class EpochsArray(_BaseEpochs):
         for ii, e in enumerate(self._data):
             # This is safe without assignment b/c there is no decim
             self._detrend_offset_decim(e)
-        self.drop_bad_epochs()
+        self.drop_bad()
 
 
 def combine_event_ids(epochs, old_event_ids, new_event_id, copy=True):
@@ -1966,8 +2214,7 @@ def combine_event_ids(epochs, old_event_ids, new_event_id, copy=True):
         condition. Note that for safety, this cannot be any
         existing id (in epochs.event_id.values()).
     copy : bool
-        If True, a copy of epochs will be returned. Otherwise, the
-        function will operate in-place.
+        Whether to return a new instance or modify in place.
 
     Notes
     -----
@@ -1978,8 +2225,7 @@ def combine_event_ids(epochs, old_event_ids, new_event_id, copy=True):
     would create a 'Directional' entry in epochs.event_id replacing
     'Left' and 'Right' (combining their trials).
     """
-    if copy:
-        epochs = epochs.copy()
+    epochs = epochs.copy() if copy else epochs
     old_event_ids = np.asanyarray(old_event_ids)
     if isinstance(new_event_id, int):
         new_event_id = {str(new_event_id): new_event_id}
@@ -2043,11 +2289,11 @@ def equalize_epoch_counts(epochs_list, method='mintime'):
     # make sure bad epochs are dropped
     for e in epochs_list:
         if not e._bad_dropped:
-            e.drop_bad_epochs()
+            e.drop_bad()
     event_times = [e.events[:, 0] for e in epochs_list]
     indices = _get_drop_indices(event_times, method)
     for e, inds in zip(epochs_list, indices):
-        e.drop_epochs(inds, reason='EQUALIZED_COUNT')
+        e.drop(inds, reason='EQUALIZED_COUNT')
 
 
 def _get_drop_indices(event_times, method):
@@ -2080,6 +2326,9 @@ def _minimize_time_diff(t_shorter, t_longer):
     """Find a boolean mask to minimize timing differences"""
     from scipy.interpolate import interp1d
     keep = np.ones((len(t_longer)), dtype=bool)
+    if len(t_shorter) == 0:
+        keep.fill(False)
+        return keep
     scores = np.ones((len(t_longer)))
     x1 = np.arange(len(t_shorter))
     # The first set of keep masks to test
@@ -2152,7 +2401,6 @@ def _is_good(e, ch_names, channel_type_idx, reject, flat, full_report=False,
             return False, bad_list
 
 
- at verbose
 def _read_one_epoch_file(f, tree, fname, preload):
     """Helper to read a single FIF file"""
 
@@ -2406,7 +2654,7 @@ class EpochsFIF(_BaseEpochs):
             name=name, proj=proj, add_eeg_ref=add_eeg_ref,
             preload_at_end=False, on_missing='ignore', selection=selection,
             drop_log=drop_log, verbose=verbose)
-        # use the private property instead of drop_bad_epochs so that epochs
+        # use the private property instead of drop_bad so that epochs
         # are not all read from disk for preload=False
         self._bad_dropped = True
 
@@ -2502,7 +2750,7 @@ def add_channels_epochs(epochs_list, name='Unknown', add_eeg_ref=True,
 
     Returns
     -------
-    epochs : Epochs
+    epochs : instance of Epochs
         Concatenated epochs.
     """
     if not all(e.preload for e in epochs_list):
@@ -2606,7 +2854,7 @@ def _finish_concat(info, data, events, event_id, tmin, tmax, baseline,
                       baseline=baseline, add_eeg_ref=False,
                       selection=selection, drop_log=drop_log,
                       proj=False, on_missing='ignore', verbose=verbose)
-    out.drop_bad_epochs()
+    out.drop_bad()
     return out
 
 
@@ -2631,25 +2879,27 @@ def concatenate_epochs(epochs_list):
 
 
 @verbose
-def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
-                      weight_all=True, int_order=8, ext_order=3,
-                      ignore_ref=False, return_mapping=False, verbose=None):
+def average_movements(epochs, head_pos=None, orig_sfreq=None, picks=None,
+                      origin='auto', weight_all=True, int_order=8, ext_order=3,
+                      destination=None, ignore_ref=False, return_mapping=False,
+                      pos=None, verbose=None):
     """Average data using Maxwell filtering, transforming using head positions
 
     Parameters
     ----------
     epochs : instance of Epochs
         The epochs to operate on.
-    pos : tuple
-        Tuple of position information as ``(trans, rot, t)`` like that
-        returned by `get_chpi_positions`. The positions will be matched
-        based on the last given position before the onset of the epoch.
+    head_pos : array | tuple | None
+        The array should be of shape ``(N, 10)``, holding the position
+        parameters as returned by e.g. `read_head_pos`. For backward
+        compatibility, this can also be a tuple of ``(trans, rot t)``
+        as returned by `head_pos_to_trans_rot_t`.
     orig_sfreq : float | None
         The original sample frequency of the data (that matches the
         event sample numbers in ``epochs.events``). Can be ``None``
         if data have not been decimated or resampled.
     picks : array-like of int | None
-        If None only MEG, EEG and SEEG channels are kept
+        If None only MEG, EEG, SEEG, and ECoG channels are kept
         otherwise the channels indices in picks are kept.
     origin : array-like, shape (3,) | str
         Origin of internal and external multipolar moment space in head
@@ -2667,6 +2917,17 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
         Basis regularization type, must be "in" or None.
         See :func:`mne.preprocessing.maxwell_filter` for details.
         Regularization is chosen based only on the destination position.
+    destination : str | array-like, shape (3,) | None
+        The destination location for the head. Can be ``None``, which
+        will not change the head position, or a string path to a FIF file
+        containing a MEG device<->head transformation, or a 3-element array
+        giving the coordinates to translate to (with no rotations).
+        For example, ``destination=(0, 0, 0.04)`` would translate the bases
+        as ``--trans default`` would in MaxFilter™ (i.e., to the default
+        head location).
+
+        .. versionadded:: 0.12
+
     ignore_ref : bool
         If True, do not include reference channels in compensation. This
         option should be True for KIT files, since Maxwell filtering
@@ -2684,6 +2945,7 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
     See Also
     --------
     mne.preprocessing.maxwell_filter
+    mne.chpi.read_head_pos
 
     Notes
     -----
@@ -2708,21 +2970,35 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
            of children in MEG: Quantification, effects on source
            estimation, and compensation. NeuroImage 40:541–550, 2008.
     """
-    from .preprocessing.maxwell import (_info_sss_basis, _reset_meg_bads,
+    from .preprocessing.maxwell import (_trans_sss_basis, _reset_meg_bads,
                                         _check_usable, _col_norm_pinv,
-                                        _get_n_moments, _get_mf_picks)
+                                        _get_n_moments, _get_mf_picks,
+                                        _prep_mf_coils, _check_destination,
+                                        _remove_meg_projs)
+    if pos is not None:
+        head_pos = pos
+        warn('pos has been replaced by head_pos and will be removed in 0.13',
+             DeprecationWarning)
+    if head_pos is None:
+        raise TypeError('head_pos must be provided and cannot be None')
+    from .chpi import head_pos_to_trans_rot_t
     if not isinstance(epochs, _BaseEpochs):
         raise TypeError('epochs must be an instance of Epochs, not %s'
                         % (type(epochs),))
     orig_sfreq = epochs.info['sfreq'] if orig_sfreq is None else orig_sfreq
     orig_sfreq = float(orig_sfreq)
-    trn, rot, t = pos
-    del pos
+    if isinstance(head_pos, np.ndarray):
+        head_pos = head_pos_to_trans_rot_t(head_pos)
+    trn, rot, t = head_pos
+    del head_pos
     _check_usable(epochs)
     origin = _check_origin(origin, epochs.info, 'head')
+    recon_trans = _check_destination(destination, epochs.info, True)
 
     logger.info('Aligning and averaging up to %s epochs'
                 % (len(epochs.events)))
+    if not np.array_equal(epochs.events[:, 0], np.unique(epochs.events[:, 0])):
+        raise RuntimeError('Epochs must have monotonically increasing events')
     meg_picks, _, _, good_picks, coil_scale, _ = \
         _get_mf_picks(epochs.info, int_order, ext_order, ignore_ref)
     n_channels, n_times = len(epochs.ch_names), len(epochs.times)
@@ -2731,6 +3007,8 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
     count = 0
     # keep only MEG w/bad channels marked in "info_from"
     info_from = pick_info(epochs.info, good_picks, copy=True)
+    all_coils_recon = _prep_mf_coils(epochs.info, ignore_ref=ignore_ref)
+    all_coils = _prep_mf_coils(info_from, ignore_ref=ignore_ref)
     # remove MEG bads in "to" info
     info_to = deepcopy(epochs.info)
     _reset_meg_bads(info_to)
@@ -2740,16 +3018,17 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
     S_decomp = 0.  # this will end up being a weighted average
     last_trans = None
     decomp_coil_scale = coil_scale[good_picks]
+    exp = dict(int_order=int_order, ext_order=ext_order, head_frame=True,
+               origin=origin)
     for ei, epoch in enumerate(epochs):
         event_time = epochs.events[epochs._current - 1, 0] / orig_sfreq
         use_idx = np.where(t <= event_time)[0]
         if len(use_idx) == 0:
-            raise RuntimeError('Event time %0.3f occurs before first '
-                               'position time %0.3f' % (event_time, t[0]))
-        use_idx = use_idx[-1]
-        trans = np.row_stack([np.column_stack([rot[use_idx],
-                                               trn[[use_idx]].T]),
-                              [[0., 0., 0., 1.]]])
+            trans = epochs.info['dev_head_t']['trans']
+        else:
+            use_idx = use_idx[-1]
+            trans = np.vstack([np.hstack([rot[use_idx], trn[[use_idx]].T]),
+                               [[0., 0., 0., 1.]]])
         loc_str = ', '.join('%0.1f' % tr for tr in (trans[:3, 3] * 1000))
         if last_trans is None or not np.allclose(last_trans, trans):
             logger.info('    Processing epoch %s (device location: %s mm)'
@@ -2762,9 +3041,8 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
             reuse = True
         epoch = epoch.copy()  # because we operate inplace
         if not reuse:
-            S = _info_sss_basis(info_from, trans, origin,
-                                int_order, ext_order, True,
-                                coil_scale=decomp_coil_scale)
+            S = _trans_sss_basis(exp, all_coils, trans,
+                                 coil_scale=decomp_coil_scale)
             # Get the weight from the un-regularized version
             weight = np.sqrt(np.sum(S * S))  # frobenius norm (eq. 44)
             # XXX Eventually we could do cross-talk and fine-cal here
@@ -2785,8 +3063,9 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
         S_decomp /= w_sum
         # Get recon matrix
         # (We would need to include external here for regularization to work)
-        S_recon = _info_sss_basis(epochs.info, None, origin,
-                                  int_order, 0, True)
+        exp['ext_order'] = 0
+        S_recon = _trans_sss_basis(exp, all_coils_recon, recon_trans)
+        exp['ext_order'] = ext_order
         # We could determine regularization on basis of destination basis
         # matrix, restricted to good channels, as regularizing individual
         # matrices within the loop above does not seem to work. But in
@@ -2800,7 +3079,9 @@ def average_movements(epochs, pos, orig_sfreq=None, picks=None, origin='auto',
         mapping = np.dot(S_recon, pS_ave)
         # Apply mapping
         data[meg_picks] = np.dot(mapping, data[good_picks])
-    evoked = epochs._evoked_from_epoch_data(
-        data, info_to, picks, count, 'average')
+    info_to['dev_head_t'] = recon_trans  # set the reconstruction transform
+    evoked = epochs._evoked_from_epoch_data(data, info_to, picks,
+                                            n_events=count, kind='average')
+    _remove_meg_projs(evoked)  # remove MEG projectors, they won't apply now
     logger.info('Created Evoked dataset from %s epochs' % (count,))
     return (evoked, mapping) if return_mapping else evoked
diff --git a/mne/event.py b/mne/event.py
index fd15e63..6683e15 100644
--- a/mne/event.py
+++ b/mne/event.py
@@ -11,7 +11,7 @@
 import numpy as np
 from os.path import splitext
 
-from .utils import check_fname, logger, verbose, _get_stim_channel
+from .utils import check_fname, logger, verbose, _get_stim_channel, warn
 from .io.constants import FIFF
 from .io.tree import dir_tree_find
 from .io.tag import read_tag
@@ -207,9 +207,9 @@ def read_events(filename, include=None, exclude=None, mask=0):
         A event id to exclude or a list of them.
         If None no event is excluded. If include is not None
         the exclude parameter is ignored.
-    mask : int
+    mask : int | None
         The value of the digital mask to apply to the stim channel values.
-        The default value is 0.
+        The default value is 0. ``None`` skips masking.
 
     Returns
     -------
@@ -257,12 +257,19 @@ def read_events(filename, include=None, exclude=None, mask=0):
             raise ValueError('Unknown number of columns in event text file')
 
         event_list = lines[:, goods]
-        if event_list.shape[0] > 0 and event_list[0, 2] == 0:
+        if (mask is not None and event_list.shape[0] > 0 and
+                event_list[0, 2] == 0):
             event_list = event_list[1:]
+            warn('first row of event file discarded (zero-valued)')
 
     event_list = pick_events(event_list, include, exclude)
-    event_list = _mask_trigs(event_list, mask)
-
+    unmasked_len = event_list.shape[0]
+    if mask is not None:
+        event_list = _mask_trigs(event_list, mask)
+        masked_len = event_list.shape[0]
+        if masked_len < unmasked_len:
+            warn('{0} of {1} events masked'.format(unmasked_len - masked_len,
+                                                   unmasked_len))
     return event_list
 
 
@@ -398,8 +405,7 @@ def find_stim_steps(raw, pad_start=None, pad_stop=None, merge=0,
         raise ValueError('No stim channel found to extract event triggers.')
     data, _ = raw[picks, :]
     if np.any(data < 0):
-        logger.warning('Trigger channel contains negative values. '
-                       'Taking absolute value.')
+        warn('Trigger channel contains negative values, using absolute value.')
         data = np.abs(data)  # make sure trig channel is positive
     data = data.astype(np.int)
 
@@ -407,9 +413,9 @@ def find_stim_steps(raw, pad_start=None, pad_stop=None, merge=0,
                             pad_stop=pad_stop, merge=merge)
 
 
- at verbose
 def _find_events(data, first_samp, verbose=None, output='onset',
-                 consecutive='increasing', min_samples=0, mask=0):
+                 consecutive='increasing', min_samples=0, mask=0,
+                 uint_cast=False):
     """Helper function for find events"""
     if min_samples > 0:
         merge = int(min_samples // 1)
@@ -418,11 +424,15 @@ def _find_events(data, first_samp, verbose=None, output='onset',
     else:
         merge = 0
 
-    if np.any(data < 0):
-        logger.warning('Trigger channel contains negative values. '
-                       'Taking absolute value.')
-        data = np.abs(data)  # make sure trig channel is positive
     data = data.astype(np.int)
+    if uint_cast:
+        data = data.astype(np.uint16).astype(np.int)
+    if data.min() < 0:
+        warn('Trigger channel contains negative values, using absolute '
+             'value. If data were acquired on a Neuromag system with '
+             'STI016 active, consider using uint_cast=True to work around '
+             'an acquisition bug')
+        data = np.abs(data)  # make sure trig channel is positive
 
     events = _find_stim_steps(data, first_samp, pad_stop=0, merge=merge)
     events = _mask_trigs(events, mask)
@@ -475,9 +485,9 @@ def _find_events(data, first_samp, verbose=None, output='onset',
 
 
 @verbose
-def find_events(raw, stim_channel=None, verbose=None, output='onset',
+def find_events(raw, stim_channel=None, output='onset',
                 consecutive='increasing', min_duration=0,
-                shortest_event=2, mask=0):
+                shortest_event=2, mask=0, uint_cast=False, verbose=None):
     """Find events from raw file
 
     Parameters
@@ -491,8 +501,6 @@ def find_events(raw, stim_channel=None, verbose=None, output='onset',
         etc. are read. If these are not found, it will fall back to
         'STI 014' if present, then fall back to the first channel of type
         'stim', if present.
-    verbose : bool, str, int, or None
-        If not None, override default verbose level (see mne.verbose).
     output : 'onset' | 'offset' | 'step'
         Whether to report when events start, when events end, or both.
     consecutive : bool | 'increasing'
@@ -511,6 +519,17 @@ def find_events(raw, stim_channel=None, verbose=None, output='onset',
     mask : int
         The value of the digital mask to apply to the stim channel values.
         The default value is 0.
+    uint_cast : bool
+        If True (default False), do a cast to ``uint16`` on the channel
+        data. This can be used to fix a bug with STI101 and STI014 in
+        Neuromag acquisition setups that use channel STI016 (channel 16
+        turns data into e.g. -32768), similar to ``mne_fix_stim14 --32``
+        in MNE-C.
+
+        .. versionadded:: 0.12
+
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
 
     Returns
     -------
@@ -518,7 +537,7 @@ def find_events(raw, stim_channel=None, verbose=None, output='onset',
         All events that were found. The first column contains the event time
         in samples and the third column contains the event id. For output =
         'onset' or 'step', the second column contains the value of the stim
-        channel immediately before the the event/step. For output = 'offset',
+        channel immediately before the event/step. For output = 'offset',
         the second column contains the value of the stim channel after the
         event offset.
 
@@ -599,7 +618,7 @@ def find_events(raw, stim_channel=None, verbose=None, output='onset',
 
     events = _find_events(data, raw.first_samp, verbose=verbose, output=output,
                           consecutive=consecutive, min_samples=min_samples,
-                          mask=mask)
+                          mask=mask, uint_cast=uint_cast)
 
     # add safety check for spurious events (for ex. from neuromag syst.) by
     # checking the number of low sample events
@@ -635,7 +654,7 @@ def merge_events(events, ids, new_id, replace_events=True):
 
     Parameters
     ----------
-    events : array
+    events : array, shape (n_events_in, 3)
         Events.
     ids : array of int
         The ids of events to merge.
@@ -647,19 +666,47 @@ def merge_events(events, ids, new_id, replace_events=True):
 
     Returns
     -------
-    new_events: array
+    new_events: array, shape (n_events_out, 3)
         The new events
+
+    Examples
+    --------
+    Here is quick example of the behavior::
+
+        >>> events = [[134, 0, 1], [341, 0, 2], [502, 0, 3]]
+        >>> merge_events(events, [1, 2], 12, replace_events=True)
+        array([[134,   0,  12],
+               [341,   0,  12],
+               [502,   0,   3]])
+        >>> merge_events(events, [1, 2], 12, replace_events=False)
+        array([[134,   0,   1],
+               [134,   0,  12],
+               [341,   0,   2],
+               [341,   0,  12],
+               [502,   0,   3]])
+
+    Notes
+    -----
+    Rather than merging events you can use hierarchical event_id
+    in Epochs. For example, here::
+
+        >>> event_id = {'auditory/left': 1, 'auditory/right': 2}
+
+    And the condition 'auditory' would correspond to either 1 or 2.
     """
+    events = np.asarray(events)
     events_out = events.copy()
-    where = np.empty(events.shape[0], dtype=bool)
+    idx_touched = []  # to keep track of the original events we can keep
     for col in [1, 2]:
-        where.fill(False)
         for i in ids:
-            where = (events[:, col] == i)
-            events_out[where, col] = new_id
+            mask = events[:, col] == i
+            events_out[mask, col] = new_id
+            idx_touched.append(np.where(mask)[0])
     if not replace_events:
-        events_out = np.concatenate((events_out, events), axis=0)
-        events_out = events_out[np.argsort(events_out[:, 0])]
+        idx_touched = np.unique(np.concatenate(idx_touched))
+        events_out = np.concatenate((events_out, events[idx_touched]), axis=0)
+        # Now sort in lexical order
+        events_out = events_out[np.lexsort(events_out.T[::-1])]
     return events_out
 
 
@@ -689,7 +736,8 @@ def shift_time_events(events, ids, tshift, sfreq):
     return events
 
 
-def make_fixed_length_events(raw, id, start=0, stop=None, duration=1.):
+def make_fixed_length_events(raw, id, start=0, stop=None, duration=1.,
+                             first_samp=True):
     """Make a set of events separated by a fixed duration
 
     Parameters
@@ -705,25 +753,38 @@ def make_fixed_length_events(raw, id, start=0, stop=None, duration=1.):
         of the recording.
     duration: float
         The duration to separate events by.
+    first_samp: bool
+        If True (default), times will have raw.first_samp added to them, as
+        in :func:`mne.find_events`. This behavior is not desirable if the
+        returned events will be combined with event times that already
+        have ``raw.first_samp`` added to them, e.g. event times that come
+        from :func:`mne.find_events`.
 
     Returns
     -------
     new_events : array
         The new events.
     """
-    start = raw.time_as_index(start)
-    start = start[0] + raw.first_samp
+    start = raw.time_as_index(start)[0]
     if stop is not None:
-        stop = raw.time_as_index(stop)
-        stop = min([stop[0] + raw.first_samp, raw.last_samp + 1])
+        stop = raw.time_as_index(stop)[0]
     else:
         stop = raw.last_samp + 1
+    if first_samp:
+        start = start + raw.first_samp
+        stop = min([stop + raw.first_samp, raw.last_samp + 1])
+    else:
+        stop = min([stop, len(raw.times)])
     if not isinstance(id, int):
         raise ValueError('id must be an integer')
     # Make sure we don't go out the end of the file:
     stop -= int(np.ceil(raw.info['sfreq'] * duration))
-    ts = np.arange(start, stop, raw.info['sfreq'] * duration).astype(int)
+    # This should be inclusive due to how we generally use start and stop...
+    ts = np.arange(start, stop + 1, raw.info['sfreq'] * duration).astype(int)
     n_events = len(ts)
+    if n_events == 0:
+        raise ValueError('No events produced, check the values of start, '
+                         'stop, and duration')
     events = np.c_[ts, np.zeros(n_events, dtype=int),
                    id * np.ones(n_events, dtype=int)]
     return events
diff --git a/mne/evoked.py b/mne/evoked.py
index f54ce66..001c2b4 100644
--- a/mne/evoked.py
+++ b/mne/evoked.py
@@ -8,7 +8,6 @@
 
 from copy import deepcopy
 import numpy as np
-import warnings
 
 from .baseline import rescale
 from .channels.channels import (ContainsMixin, UpdateChannelsMixin,
@@ -16,23 +15,26 @@ from .channels.channels import (ContainsMixin, UpdateChannelsMixin,
                                 equalize_channels)
 from .filter import resample, detrend, FilterMixin
 from .fixes import in1d
-from .utils import check_fname, logger, verbose, object_hash, _time_mask
+from .utils import (check_fname, logger, verbose, object_hash, _time_mask,
+                    warn, _check_copy_dep)
 from .viz import (plot_evoked, plot_evoked_topomap, plot_evoked_field,
                   plot_evoked_image, plot_evoked_topo)
-from .viz.evoked import _plot_evoked_white
+from .viz.evoked import (_plot_evoked_white, plot_evoked_joint,
+                         _animate_evoked_topomap)
+
 from .externals.six import string_types
 
 from .io.constants import FIFF
 from .io.open import fiff_open
 from .io.tag import read_tag
 from .io.tree import dir_tree_find
-from .io.pick import channel_type, pick_types
+from .io.pick import channel_type, pick_types, _pick_data_channels
 from .io.meas_info import read_meas_info, write_meas_info
 from .io.proj import ProjMixin
 from .io.write import (start_file, start_block, end_file, end_block,
                        write_int, write_string, write_float_matrix,
                        write_id)
-from .io.base import ToDataFrameMixin
+from .io.base import ToDataFrameMixin, TimeMixin
 
 _aspect_dict = {'average': FIFF.FIFFV_ASPECT_AVERAGE,
                 'standard_error': FIFF.FIFFV_ASPECT_STD_ERR}
@@ -42,7 +44,7 @@ _aspect_rev = {str(FIFF.FIFFV_ASPECT_AVERAGE): 'average',
 
 class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
              SetChannelsMixin, InterpolationMixin, FilterMixin,
-             ToDataFrameMixin):
+             ToDataFrameMixin, TimeMixin):
     """Evoked data
 
     Parameters
@@ -91,194 +93,27 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         Evoked response.
     verbose : bool, str, int, or None.
         See above.
+
+    Notes
+    -----
+    Evoked objects contain a single condition only.
+
     """
     @verbose
     def __init__(self, fname, condition=None, baseline=None, proj=True,
                  kind='average', verbose=None):
-
-        if fname is None:
-            raise ValueError('No evoked filename specified')
-
-        self.verbose = verbose
-        logger.info('Reading %s ...' % fname)
-        f, tree, _ = fiff_open(fname)
-        with f as fid:
-            if not isinstance(proj, bool):
-                raise ValueError(r"'proj' must be 'True' or 'False'")
-
-            #   Read the measurement info
-            info, meas = read_meas_info(fid, tree, clean_bads=True)
-            info['filename'] = fname
-
-            #   Locate the data of interest
-            processed = dir_tree_find(meas, FIFF.FIFFB_PROCESSED_DATA)
-            if len(processed) == 0:
-                raise ValueError('Could not find processed data')
-
-            evoked_node = dir_tree_find(meas, FIFF.FIFFB_EVOKED)
-            if len(evoked_node) == 0:
-                raise ValueError('Could not find evoked data')
-
-            # find string-based entry
-            if isinstance(condition, string_types):
-                if kind not in _aspect_dict.keys():
-                    raise ValueError('kind must be "average" or '
-                                     '"standard_error"')
-
-                comments, aspect_kinds, t = _get_entries(fid, evoked_node)
-                goods = np.logical_and(in1d(comments, [condition]),
-                                       in1d(aspect_kinds,
-                                            [_aspect_dict[kind]]))
-                found_cond = np.where(goods)[0]
-                if len(found_cond) != 1:
-                    raise ValueError('condition "%s" (%s) not found, out of '
-                                     'found datasets:\n  %s'
-                                     % (condition, kind, t))
-                condition = found_cond[0]
-            elif condition is None:
-                if len(evoked_node) > 1:
-                    _, _, conditions = _get_entries(fid, evoked_node)
-                    raise TypeError("Evoked file has more than one "
-                                    "conditions, the condition parameters "
-                                    "must be specified from:\n%s" % conditions)
-                else:
-                    condition = 0
-
-            if condition >= len(evoked_node) or condition < 0:
-                raise ValueError('Data set selector out of range')
-
-            my_evoked = evoked_node[condition]
-
-            # Identify the aspects
-            aspects = dir_tree_find(my_evoked, FIFF.FIFFB_ASPECT)
-            if len(aspects) > 1:
-                logger.info('Multiple aspects found. Taking first one.')
-            my_aspect = aspects[0]
-
-            # Now find the data in the evoked block
-            nchan = 0
-            sfreq = -1
-            chs = []
-            comment = None
-            for k in range(my_evoked['nent']):
-                my_kind = my_evoked['directory'][k].kind
-                pos = my_evoked['directory'][k].pos
-                if my_kind == FIFF.FIFF_COMMENT:
-                    tag = read_tag(fid, pos)
-                    comment = tag.data
-                elif my_kind == FIFF.FIFF_FIRST_SAMPLE:
-                    tag = read_tag(fid, pos)
-                    first = int(tag.data)
-                elif my_kind == FIFF.FIFF_LAST_SAMPLE:
-                    tag = read_tag(fid, pos)
-                    last = int(tag.data)
-                elif my_kind == FIFF.FIFF_NCHAN:
-                    tag = read_tag(fid, pos)
-                    nchan = int(tag.data)
-                elif my_kind == FIFF.FIFF_SFREQ:
-                    tag = read_tag(fid, pos)
-                    sfreq = float(tag.data)
-                elif my_kind == FIFF.FIFF_CH_INFO:
-                    tag = read_tag(fid, pos)
-                    chs.append(tag.data)
-
-            if comment is None:
-                comment = 'No comment'
-
-            #   Local channel information?
-            if nchan > 0:
-                if chs is None:
-                    raise ValueError('Local channel information was not found '
-                                     'when it was expected.')
-
-                if len(chs) != nchan:
-                    raise ValueError('Number of channels and number of '
-                                     'channel definitions are different')
-
-                info['chs'] = chs
-                info['nchan'] = nchan
-                logger.info('    Found channel information in evoked data. '
-                            'nchan = %d' % nchan)
-                if sfreq > 0:
-                    info['sfreq'] = sfreq
-
-            nsamp = last - first + 1
-            logger.info('    Found the data of interest:')
-            logger.info('        t = %10.2f ... %10.2f ms (%s)'
-                        % (1000 * first / info['sfreq'],
-                           1000 * last / info['sfreq'], comment))
-            if info['comps'] is not None:
-                logger.info('        %d CTF compensation matrices available'
-                            % len(info['comps']))
-
-            # Read the data in the aspect block
-            nave = 1
-            epoch = []
-            for k in range(my_aspect['nent']):
-                kind = my_aspect['directory'][k].kind
-                pos = my_aspect['directory'][k].pos
-                if kind == FIFF.FIFF_COMMENT:
-                    tag = read_tag(fid, pos)
-                    comment = tag.data
-                elif kind == FIFF.FIFF_ASPECT_KIND:
-                    tag = read_tag(fid, pos)
-                    aspect_kind = int(tag.data)
-                elif kind == FIFF.FIFF_NAVE:
-                    tag = read_tag(fid, pos)
-                    nave = int(tag.data)
-                elif kind == FIFF.FIFF_EPOCH:
-                    tag = read_tag(fid, pos)
-                    epoch.append(tag)
-
-            logger.info('        nave = %d - aspect type = %d'
-                        % (nave, aspect_kind))
-
-            nepoch = len(epoch)
-            if nepoch != 1 and nepoch != info['nchan']:
-                raise ValueError('Number of epoch tags is unreasonable '
-                                 '(nepoch = %d nchan = %d)'
-                                 % (nepoch, info['nchan']))
-
-            if nepoch == 1:
-                # Only one epoch
-                all_data = epoch[0].data.astype(np.float)
-                # May need a transpose if the number of channels is one
-                if all_data.shape[1] == 1 and info['nchan'] == 1:
-                    all_data = all_data.T.astype(np.float)
-            else:
-                # Put the old style epochs together
-                all_data = np.concatenate([e.data[None, :] for e in epoch],
-                                          axis=0).astype(np.float)
-
-            if all_data.shape[1] != nsamp:
-                raise ValueError('Incorrect number of samples (%d instead of '
-                                 ' %d)' % (all_data.shape[1], nsamp))
-
-        # Calibrate
-        cals = np.array([info['chs'][k]['cal'] *
-                         info['chs'][k].get('scale', 1.0)
-                         for k in range(info['nchan'])])
-        all_data *= cals[:, np.newaxis]
-
-        times = np.arange(first, last + 1, dtype=np.float) / info['sfreq']
-        self.info = info
-
-        # Put the rest together all together
-        self.nave = nave
-        self._aspect_kind = aspect_kind
+        if not isinstance(proj, bool):
+            raise ValueError(r"'proj' must be 'True' or 'False'")
+        # Read the requested data
+        self.info, self.nave, self._aspect_kind, self.first, self.last, \
+            self.comment, self.times, self.data = _read_evoked(
+                fname, condition, kind)
         self.kind = _aspect_rev.get(str(self._aspect_kind), 'Unknown')
-        self.first = first
-        self.last = last
-        self.comment = comment
-        self.times = times
-        self.data = all_data
-
-        # bind info, proj, data to self so apply_proj can be used
-        self.data = all_data
+        self.verbose = verbose
+        # project and baseline correct
         if proj:
             self.apply_proj()
-        # Run baseline correction
-        self.data = rescale(self.data, times, baseline, 'mean', copy=False)
+        self.data = rescale(self.data, self.times, baseline, copy=False)
 
     def save(self, fname):
         """Save dataset to file.
@@ -287,6 +122,11 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         ----------
         fname : string
             Name of the file where to save the data.
+
+        Notes
+        -----
+        To write multiple conditions into a single file, use
+        :func:`mne.write_evokeds`.
         """
         write_evokeds(fname, self)
 
@@ -303,7 +143,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         """Channel names"""
         return self.info['ch_names']
 
-    def crop(self, tmin=None, tmax=None, copy=False):
+    def crop(self, tmin=None, tmax=None, copy=None):
         """Crop data to a given time interval
 
         Parameters
@@ -313,10 +153,12 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         tmax : float | None
             End time of selection in seconds.
         copy : bool
-            If False epochs is cropped in place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
         """
-        inst = self if not copy else self.copy()
-        mask = _time_mask(inst.times, tmin, tmax)
+        inst = _check_copy_dep(self, copy)
+        mask = _time_mask(inst.times, tmin, tmax, sfreq=self.info['sfreq'])
         inst.times = inst.times[mask]
         inst.first = int(inst.times[0] * inst.info['sfreq'])
         inst.last = len(inst.times) + inst.first - 1
@@ -355,7 +197,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
              xlim='tight', proj=False, hline=None, units=None, scalings=None,
              titles=None, axes=None, gfp=False, window_title=None,
              spatial_colors=False):
-        """Plot evoked data as butterfly plots
+        """Plot evoked data using butterfly plots
 
         Left click to a line shows the channel name. Selecting an area by
         clicking and holding left mouse button plots a topographic map of the
@@ -374,9 +216,12 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Scale plot with channel (SI) unit.
         show : bool
             Call pyplot.show() at the end or not.
-        ylim : dict
-            ylim for plots. e.g. ylim = dict(eeg=[-200e-6, 200e-6])
-            Valid keys are eeg, mag, grad
+        ylim : dict | None
+            ylim for plots (after scaling has been applied). The value
+            determines the upper and lower subplot limits. e.g.
+            ylim = dict(eeg=[-20, 20]). Valid keys are eeg, mag, grad. If None,
+            the ylim parameter for each channel is determined by the maximum
+            absolute peak.
         xlim : 'tight' | tuple | None
             xlim for plots.
         proj : bool | 'interactive'
@@ -408,6 +253,11 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             coordinates into color values. Spatially similar channels will have
             similar colors. Bad channels will be dotted. If False, the good
             channels are plotted black and bad channels red. Defaults to False.
+
+        Returns
+        -------
+        fig : instance of matplotlib.figure.Figure
+            Figure containing the butterfly plots.
         """
         return plot_evoked(self, picks=picks, exclude=exclude, unit=unit,
                            show=show, ylim=ylim, proj=proj, xlim=xlim,
@@ -456,6 +306,11 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Axes, there must be only one channel type plotted.
         cmap : matplotlib colormap
             Colormap.
+
+        Returns
+        -------
+        fig : instance of matplotlib.figure.Figure
+            Figure containing the images.
         """
         return plot_evoked_image(self, picks=picks, exclude=exclude, unit=unit,
                                  show=show, clim=clim, proj=proj, xlim=xlim,
@@ -466,7 +321,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                   border='none', ylim=None, scalings=None, title=None,
                   proj=False, vline=[0.0], fig_facecolor='k',
                   fig_background=None, axis_facecolor='k', font_color='w',
-                  show=True):
+                  merge_grads=False, show=True):
         """Plot 2D topography of evoked responses.
 
         Clicking on the plot of an individual sensor opens a new figure showing
@@ -489,7 +344,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             matplotlib borders style to be used for each sensor plot.
         ylim : dict | None
             ylim for plots. The value determines the upper and lower subplot
-            limits. e.g. ylim = dict(eeg=[-200e-6, 200e6]). Valid keys are eeg,
+            limits. e.g. ylim = dict(eeg=[-20, 20]). Valid keys are eeg,
             mag, grad, misc. If None, the ylim parameter for each channel is
             determined by the maximum absolute peak.
         scalings : dict | None
@@ -512,6 +367,9 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             The face color to be used for each sensor plot. Defaults to black.
         font_color : str | obj
             The color of text in the colorbar and title. Defaults to white.
+        merge_grads : bool
+            Whether to use RMS value of gradiometer pairs. Only works for
+            Neuromag data. Defaults to False.
         show : bool
             Show figure if True.
 
@@ -520,6 +378,8 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         fig : Instance of matplotlib.figure.Figure
             Images of evoked responses at sensor locations
 
+        Notes
+        -----
         .. versionadded:: 0.10.0
         """
         return plot_evoked_topo(self, layout=layout, layout_scale=layout_scale,
@@ -528,10 +388,11 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                                 vline=vline, fig_facecolor=fig_facecolor,
                                 fig_background=fig_background,
                                 axis_facecolor=axis_facecolor,
-                                font_color=font_color, show=show)
+                                font_color=font_color, merge_grads=merge_grads,
+                                show=show)
 
     def plot_topomap(self, times="auto", ch_type=None, layout=None, vmin=None,
-                     vmax=None, cmap='RdBu_r', sensors=True, colorbar=True,
+                     vmax=None, cmap=None, sensors=True, colorbar=True,
                      scale=None, scale_time=1e3, unit=None, res=64, size=1,
                      cbar_fmt="%3.1f", time_format='%01d ms', proj=False,
                      show=True, show_names=False, title=None, mask=None,
@@ -551,7 +412,8 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
             The channel type to plot. For 'grad', the gradiometers are collec-
             ted in pairs and the RMS for each pair is plotted.
-            If None, then channels are chosen in the order given above.
+            If None, then first available channel type from order given
+            above is used. Defaults to None.
         layout : None | Layout
             Layout instance specifying sensor positions (does not need to
             be specified for Neuromag data). If possible, the correct
@@ -567,8 +429,9 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             If None, the maximum absolute value is used. If vmin is None,
             but vmax is not, defaults to np.max(data).
             If callable, the output equals vmax(data).
-        cmap : matplotlib colormap
-            Colormap. Defaults to 'RdBu_r'.
+        cmap : matplotlib colormap | None
+            Colormap to use. If None, 'Reds' is used for all positive data,
+            otherwise defaults to 'RdBu_r'.
         sensors : bool | str
             Add markers for sensor locations to the plot. Accepts matplotlib
             plot format string (e.g., 'r+' for red plusses). If True, a circle
@@ -609,7 +472,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Title. If None (default), no title is displayed.
         mask : ndarray of bool, shape (n_channels, n_times) | None
             The channels to be marked as significant at a given time point.
-            Indicies set to `True` will be considered. Defaults to None.
+            Indices set to `True` will be considered. Defaults to None.
         mask_params : dict | None
             Additional plotting parameters for plotting significant sensors.
             Default (None) equals:
@@ -647,21 +510,24 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             the same length as ``times`` (unless ``times`` is None). If
             instance of Axes, ``times`` must be a float or a list of one float.
             Defaults to None.
+
+        Returns
+        -------
+        fig : instance of matplotlib.figure.Figure
+            Images of evoked responses at sensor locations
         """
         return plot_evoked_topomap(self, times=times, ch_type=ch_type,
-                                   layout=layout, vmin=vmin,
-                                   vmax=vmax, cmap=cmap, sensors=sensors,
+                                   layout=layout, vmin=vmin, vmax=vmax,
+                                   cmap=cmap, sensors=sensors,
                                    colorbar=colorbar, scale=scale,
-                                   scale_time=scale_time,
-                                   unit=unit, res=res, proj=proj, size=size,
-                                   cbar_fmt=cbar_fmt, time_format=time_format,
-                                   show=show, show_names=show_names,
-                                   title=title, mask=mask,
-                                   mask_params=mask_params,
+                                   scale_time=scale_time, unit=unit, res=res,
+                                   proj=proj, size=size, cbar_fmt=cbar_fmt,
+                                   time_format=time_format, show=show,
+                                   show_names=show_names, title=title,
+                                   mask=mask, mask_params=mask_params,
                                    outlines=outlines, contours=contours,
-                                   image_interp=image_interp,
-                                   average=average, head_pos=head_pos,
-                                   axes=axes)
+                                   image_interp=image_interp, average=average,
+                                   head_pos=head_pos, axes=axes)
 
     def plot_field(self, surf_maps, time=None, time_label='t = %0.0f ms',
                    n_jobs=1):
@@ -727,6 +593,102 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         return _plot_evoked_white(self, noise_cov=noise_cov, scalings=None,
                                   rank=None, show=show)
 
+    def plot_joint(self, times="peaks", title='', picks=None,
+                   exclude='bads', show=True, ts_args=None,
+                   topomap_args=None):
+        """Plot evoked data as butterfly plots and add topomaps for selected
+        time points.
+
+        Parameters
+        ----------
+        times : float | array of floats | "auto" | "peaks"
+            The time point(s) to plot. If "auto", 5 evenly spaced topographies
+            between the first and last time instant will be shown. If "peaks",
+            finds time points automatically by checking for 3 local
+            maxima in Global Field Power. Defaults to "peaks".
+        title : str
+            The title. If ``None``, suppress printing channel type. Defaults to
+            an empty string.
+        picks : array-like of int | None
+            The indices of channels to plot. If ``None``, show all. Defaults
+            to None.
+        exclude : list of str | 'bads'
+            Channels names to exclude from being shown. If 'bads', the
+            bad channels are excluded. Defaults to 'bads'.
+        show : bool
+            Show figure if True. Defaults to True.
+        ts_args : None | dict
+            A dict of `kwargs` that are forwarded to `evoked.plot` to
+            style the butterfly plot. `axes` and `show` are ignored.
+            If `spatial_colors` is not in this dict, `spatial_colors=True`
+            will be passed. Beyond that, if `None`, no customizable arguments
+            will be passed.
+        topomap_args : None | dict
+            A dict of `kwargs` that are forwarded to `evoked.plot_topomap`
+            to style the topomaps. `axes` and `show` are ignored. If `times`
+            is not in this dict, automatic peak detection is used. Beyond
+            that, if `None`, no customizable arguments will be passed.
+
+        Returns
+        -------
+        fig : instance of matplotlib.figure.Figure | list
+            The figure object containing the plot. If `evoked` has multiple
+            channel types, a list of figures, one for each channel type, is
+            returned.
+
+        Notes
+        -----
+        .. versionadded:: 0.12.0
+        """
+        return plot_evoked_joint(self, times=times, title=title, picks=picks,
+                                 exclude=exclude, show=show, ts_args=ts_args,
+                                 topomap_args=topomap_args)
+
+    def animate_topomap(self, ch_type='mag', times=None, frame_rate=None,
+                        butterfly=False, blit=True, show=True):
+        """Make animation of evoked data as topomap timeseries. Animation can
+        be paused/resumed with left mouse button. Left and right arrow keys can
+        be used to move backward or forward in time
+
+        Parameters
+        ----------
+        ch_type : str | None
+            Channel type to plot. Accepted data types: 'mag', 'grad', 'eeg'.
+            If None, first available channel type from ('mag', 'grad', 'eeg')
+            is used. Defaults to None.
+        times : array of floats | None
+            The time points to plot. If None, 10 evenly spaced samples are
+            calculated over the evoked time series. Defaults to None.
+        frame_rate : int | None
+            Frame rate for the animation in Hz. If None,
+            frame rate = sfreq / 10. Defaults to None.
+        butterfly : bool
+            Whether to plot the data as butterfly plot under the topomap.
+            Defaults to False.
+        blit : bool
+            Whether to use blit to optimize drawing. In general, it is
+            recommended to use blit in combination with ``show=True``. If you
+            intend to save the animation it is better to disable blit.
+            Defaults to True.
+        show : bool
+            Whether to show the animation. Defaults to True.
+
+        Returns
+        -------
+        fig : instance of matplotlib figure
+            The figure.
+        anim : instance of matplotlib FuncAnimation
+            Animation of the topomap.
+
+        Notes
+        -----
+        .. versionadded:: 0.12.0
+        """
+        return _animate_evoked_topomap(self, ch_type=ch_type, times=times,
+                                       frame_rate=frame_rate,
+                                       butterfly=butterfly, blit=blit,
+                                       show=show)
+
     def as_type(self, ch_type='grad', mode='fast'):
         """Compute virtual evoked using interpolated fields in mag/grad
         channels.
@@ -757,7 +719,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         from .forward import _as_meg_type_evoked
         return _as_meg_type_evoked(self, ch_type=ch_type, mode=mode)
 
-    def resample(self, sfreq, npad=100, window='boxcar'):
+    def resample(self, sfreq, npad=None, window='boxcar'):
         """Resample data
 
         This function operates in-place.
@@ -766,11 +728,19 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         ----------
         sfreq : float
             New sample rate to use
-        npad : int
+        npad : int | str
             Amount to pad the start and end of the data.
+            Can also be "auto" to use a padding that will result in
+            a power-of-two size (can be much faster).
         window : string or tuple
             Window to use in resampling. See scipy.signal.resample.
         """
+        if npad is None:
+            npad = 100
+            warn('npad is currently taken to be 100, but will be changed to '
+                 '"auto" in 0.13. Please set the value explicitly.',
+                 DeprecationWarning)
+        sfreq = float(sfreq)
         o_sfreq = self.info['sfreq']
         self.data = resample(self.data, sfreq, o_sfreq, npad, -1, window)
         # adjust indirectly affected variables
@@ -791,13 +761,17 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             Either 0 or 1, the order of the detrending. 0 is a constant
             (DC) detrend, 1 is a linear detrend.
         picks : array-like of int | None
-            If None only MEG, EEG and SEEG channels are detrended.
+            If None only MEG, EEG, SEEG, and ECoG channels are detrended.
+
+        Returns
+        -------
+        evoked : instance of Evoked
+            The evoked instance.
         """
         if picks is None:
-            picks = pick_types(self.info, meg=True, eeg=True, ref_meg=False,
-                               stim=False, eog=False, ecg=False, emg=False,
-                               seeg=True, exclude='bads')
+            picks = _pick_data_channels(self.info)
         self.data[picks] = detrend(self.data[picks], order, axis=-1)
+        return self
 
     def copy(self):
         """Copy the instance of evoked
@@ -810,18 +784,34 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         return evoked
 
     def __add__(self, evoked):
-        """Add evoked taking into account number of epochs"""
+        """Add evoked taking into account number of epochs
+
+        The addition will be performed by weighting each Evoked
+        instance by the number of averages.
+
+        See Also
+        --------
+        mne.combine_evoked
+        """
         out = combine_evoked([self, evoked])
         out.comment = self.comment + " + " + evoked.comment
         return out
 
     def __sub__(self, evoked):
-        """Add evoked taking into account number of epochs"""
+        """Subtract evoked taking into account number of epochs
+
+        The subtraction will be performed by weighting each Evoked
+        instance by the number of averages.
+
+        See Also
+        --------
+        mne.combine_evoked
+        """
         this_evoked = deepcopy(evoked)
         this_evoked.data *= -1.
         out = combine_evoked([self, this_evoked])
         if self.comment is None or this_evoked.comment is None:
-            warnings.warn('evoked.comment expects a string but is None')
+            warn('evoked.comment expects a string but is None')
             out.comment = 'unknown'
         else:
             out.comment = self.comment + " - " + this_evoked.comment
@@ -836,7 +826,7 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
         Parameters
         ----------
-        ch_type : {'mag', 'grad', 'eeg', 'seeg', 'misc', None}
+        ch_type : {'mag', 'grad', 'eeg', 'seeg', 'ecog', 'misc', None}
             The channel type to use. Defaults to None. If more than one sensor
             Type is present in the data the channel type has to be explicitly
             set.
@@ -862,10 +852,8 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             The time point of the maximum response, either latency in seconds
             or index.
         """
-        supported = ('mag', 'grad', 'eeg', 'seeg', 'misc', 'None')
-
-        data_picks = pick_types(self.info, meg=True, eeg=True, seeg=True,
-                                ref_meg=False)
+        supported = ('mag', 'grad', 'eeg', 'seeg', 'ecog', 'misc', 'None')
+        data_picks = _pick_data_channels(self.info, with_ref_meg=False)
         types_used = set([channel_type(self.info, idx) for idx in data_picks])
 
         if str(ch_type) not in supported:
@@ -883,8 +871,8 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                                'must not be `None`, pass a sensor type '
                                'value instead')
 
-        meg, eeg, misc, seeg, picks = False, False, False, False, None
-
+        meg = eeg = misc = seeg = ecog = False
+        picks = None
         if ch_type == 'mag':
             meg = ch_type
         elif ch_type == 'grad':
@@ -895,10 +883,12 @@ class Evoked(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             misc = True
         elif ch_type == 'seeg':
             seeg = True
+        elif ch_type == 'ecog':
+            ecog = True
 
         if ch_type is not None:
             picks = pick_types(self.info, meg=meg, eeg=eeg, misc=misc,
-                               seeg=seeg, ref_meg=False)
+                               seeg=seeg, ecog=ecog, ref_meg=False)
 
         data = self.data
         ch_names = self.ch_names
@@ -969,10 +959,12 @@ class EvokedArray(Evoked):
         self.picks = None
         self.verbose = verbose
         self._projector = None
-        if self.kind == 'average':
-            self._aspect_kind = _aspect_dict['average']
-        else:
-            self._aspect_kind = _aspect_dict['standard_error']
+        if not isinstance(self.kind, string_types):
+            raise TypeError('kind must be a string, not "%s"' % (type(kind),))
+        if self.kind not in _aspect_dict:
+            raise ValueError('unknown kind "%s", should be "average" or '
+                             '"standard_error"' % (self.kind,))
+        self._aspect_kind = _aspect_dict[self.kind]
 
 
 def _get_entries(fid, evoked_node):
@@ -1009,7 +1001,7 @@ def _get_evoked_node(fname):
     """Helper to get info in evoked file"""
     f, tree, _ = fiff_open(fname)
     with f as fid:
-        _, meas = read_meas_info(fid, tree)
+        _, meas = read_meas_info(fid, tree, verbose=False)
         evoked_node = dir_tree_find(meas, FIFF.FIFFB_EVOKED)
     return evoked_node
 
@@ -1160,7 +1152,7 @@ def read_evokeds(fname, condition=None, baseline=None, kind='average',
     write_evokeds
     """
     check_fname(fname, 'evoked', ('-ave.fif', '-ave.fif.gz'))
-
+    logger.info('Reading %s ...' % fname)
     return_list = True
     if condition is None:
         evoked_node = _get_evoked_node(fname)
@@ -1175,6 +1167,182 @@ def read_evokeds(fname, condition=None, baseline=None, kind='average',
     return out if return_list else out[0]
 
 
+def _read_evoked(fname, condition=None, kind='average'):
+    """Read evoked data from a FIF file"""
+    if fname is None:
+        raise ValueError('No evoked filename specified')
+
+    f, tree, _ = fiff_open(fname)
+    with f as fid:
+        #   Read the measurement info
+        info, meas = read_meas_info(fid, tree, clean_bads=True)
+        info['filename'] = fname
+
+        #   Locate the data of interest
+        processed = dir_tree_find(meas, FIFF.FIFFB_PROCESSED_DATA)
+        if len(processed) == 0:
+            raise ValueError('Could not find processed data')
+
+        evoked_node = dir_tree_find(meas, FIFF.FIFFB_EVOKED)
+        if len(evoked_node) == 0:
+            raise ValueError('Could not find evoked data')
+
+        # find string-based entry
+        if isinstance(condition, string_types):
+            if kind not in _aspect_dict.keys():
+                raise ValueError('kind must be "average" or '
+                                 '"standard_error"')
+
+            comments, aspect_kinds, t = _get_entries(fid, evoked_node)
+            goods = np.logical_and(in1d(comments, [condition]),
+                                   in1d(aspect_kinds,
+                                        [_aspect_dict[kind]]))
+            found_cond = np.where(goods)[0]
+            if len(found_cond) != 1:
+                raise ValueError('condition "%s" (%s) not found, out of '
+                                 'found datasets:\n  %s'
+                                 % (condition, kind, t))
+            condition = found_cond[0]
+        elif condition is None:
+            if len(evoked_node) > 1:
+                _, _, conditions = _get_entries(fid, evoked_node)
+                raise TypeError("Evoked file has more than one "
+                                "conditions, the condition parameters "
+                                "must be specified from:\n%s" % conditions)
+            else:
+                condition = 0
+
+        if condition >= len(evoked_node) or condition < 0:
+            raise ValueError('Data set selector out of range')
+
+        my_evoked = evoked_node[condition]
+
+        # Identify the aspects
+        aspects = dir_tree_find(my_evoked, FIFF.FIFFB_ASPECT)
+        if len(aspects) > 1:
+            logger.info('Multiple aspects found. Taking first one.')
+        my_aspect = aspects[0]
+
+        # Now find the data in the evoked block
+        nchan = 0
+        sfreq = -1
+        chs = []
+        comment = last = first = first_time = nsamp = None
+        for k in range(my_evoked['nent']):
+            my_kind = my_evoked['directory'][k].kind
+            pos = my_evoked['directory'][k].pos
+            if my_kind == FIFF.FIFF_COMMENT:
+                tag = read_tag(fid, pos)
+                comment = tag.data
+            elif my_kind == FIFF.FIFF_FIRST_SAMPLE:
+                tag = read_tag(fid, pos)
+                first = int(tag.data)
+            elif my_kind == FIFF.FIFF_LAST_SAMPLE:
+                tag = read_tag(fid, pos)
+                last = int(tag.data)
+            elif my_kind == FIFF.FIFF_NCHAN:
+                tag = read_tag(fid, pos)
+                nchan = int(tag.data)
+            elif my_kind == FIFF.FIFF_SFREQ:
+                tag = read_tag(fid, pos)
+                sfreq = float(tag.data)
+            elif my_kind == FIFF.FIFF_CH_INFO:
+                tag = read_tag(fid, pos)
+                chs.append(tag.data)
+            elif my_kind == FIFF.FIFF_FIRST_TIME:
+                tag = read_tag(fid, pos)
+                first_time = float(tag.data)
+            elif my_kind == FIFF.FIFF_NO_SAMPLES:
+                tag = read_tag(fid, pos)
+                nsamp = int(tag.data)
+
+        if comment is None:
+            comment = 'No comment'
+
+        #   Local channel information?
+        if nchan > 0:
+            if chs is None:
+                raise ValueError('Local channel information was not found '
+                                 'when it was expected.')
+
+            if len(chs) != nchan:
+                raise ValueError('Number of channels and number of '
+                                 'channel definitions are different')
+
+            info['chs'] = chs
+            logger.info('    Found channel information in evoked data. '
+                        'nchan = %d' % nchan)
+            if sfreq > 0:
+                info['sfreq'] = sfreq
+
+        # Read the data in the aspect block
+        nave = 1
+        epoch = []
+        for k in range(my_aspect['nent']):
+            kind = my_aspect['directory'][k].kind
+            pos = my_aspect['directory'][k].pos
+            if kind == FIFF.FIFF_COMMENT:
+                tag = read_tag(fid, pos)
+                comment = tag.data
+            elif kind == FIFF.FIFF_ASPECT_KIND:
+                tag = read_tag(fid, pos)
+                aspect_kind = int(tag.data)
+            elif kind == FIFF.FIFF_NAVE:
+                tag = read_tag(fid, pos)
+                nave = int(tag.data)
+            elif kind == FIFF.FIFF_EPOCH:
+                tag = read_tag(fid, pos)
+                epoch.append(tag)
+
+        nepoch = len(epoch)
+        if nepoch != 1 and nepoch != info['nchan']:
+            raise ValueError('Number of epoch tags is unreasonable '
+                             '(nepoch = %d nchan = %d)'
+                             % (nepoch, info['nchan']))
+
+        if nepoch == 1:
+            # Only one epoch
+            data = epoch[0].data
+            # May need a transpose if the number of channels is one
+            if data.shape[1] == 1 and info['nchan'] == 1:
+                data = data.T
+        else:
+            # Put the old style epochs together
+            data = np.concatenate([e.data[None, :] for e in epoch], axis=0)
+        data = data.astype(np.float)
+
+        if first is not None:
+            nsamp = last - first + 1
+        elif first_time is not None:
+            first = int(round(first_time * info['sfreq']))
+            last = first + nsamp
+        else:
+            raise RuntimeError('Could not read time parameters')
+        if nsamp is not None and data.shape[1] != nsamp:
+            raise ValueError('Incorrect number of samples (%d instead of '
+                             ' %d)' % (data.shape[1], nsamp))
+        nsamp = data.shape[1]
+        last = first + nsamp - 1
+        logger.info('    Found the data of interest:')
+        logger.info('        t = %10.2f ... %10.2f ms (%s)'
+                    % (1000 * first / info['sfreq'],
+                       1000 * last / info['sfreq'], comment))
+        if info['comps'] is not None:
+            logger.info('        %d CTF compensation matrices available'
+                        % len(info['comps']))
+        logger.info('        nave = %d - aspect type = %d'
+                    % (nave, aspect_kind))
+
+    # Calibrate
+    cals = np.array([info['chs'][k]['cal'] *
+                     info['chs'][k].get('scale', 1.0)
+                     for k in range(info['nchan'])])
+    data *= cals[:, np.newaxis]
+
+    times = np.arange(first, last + 1, dtype=np.float) / info['sfreq']
+    return info, nave, aspect_kind, first, last, comment, times, data
+
+
 def write_evokeds(fname, evoked):
     """Write an evoked dataset to a file
 
@@ -1191,7 +1359,13 @@ def write_evokeds(fname, evoked):
     --------
     read_evokeds
     """
-    check_fname(fname, 'evoked', ('-ave.fif', '-ave.fif.gz'))
+    _write_evokeds(fname, evoked)
+
+
+def _write_evokeds(fname, evoked, check=True):
+    """Helper to write evoked data"""
+    if check:
+        check_fname(fname, 'evoked', ('-ave.fif', '-ave.fif.gz'))
 
     if not isinstance(evoked, list):
         evoked = [evoked]
diff --git a/mne/filter.py b/mne/filter.py
index 8e881d8..2401f85 100644
--- a/mne/filter.py
+++ b/mne/filter.py
@@ -1,17 +1,17 @@
 """IIR and FIR filtering functions"""
 
-from .externals.six import string_types, integer_types
-import warnings
+from copy import deepcopy
+
 import numpy as np
 from scipy.fftpack import fft, ifftshift, fftfreq
-from copy import deepcopy
 
-from .fixes import get_firwin2, get_filtfilt
-from .time_frequency.multitaper import dpss_windows, _mt_spectra
-from .parallel import parallel_func, check_n_jobs
 from .cuda import (setup_cuda_fft_multiply_repeated, fft_multiply_repeated,
                    setup_cuda_fft_resample, fft_resample, _smart_pad)
-from .utils import logger, verbose, sum_squared, check_version
+from .externals.six import string_types, integer_types
+from .fixes import get_firwin2, get_filtfilt
+from .parallel import parallel_func, check_n_jobs
+from .time_frequency.multitaper import dpss_windows, _mt_spectra
+from .utils import logger, verbose, sum_squared, check_version, warn
 
 
 def is_power2(num):
@@ -117,7 +117,7 @@ def _overlap_add_filter(x, h, n_fft=None, zero_phase=True, picks=None,
                          "len(h) if zero_phase == False")
 
     if not is_power2(n_fft):
-        warnings.warn("FFT length is not a power of 2. Can be slower.")
+        warn("FFT length is not a power of 2. Can be slower.")
 
     # Filter in frequency domain
     h_fft = fft(np.concatenate([h, np.zeros(n_fft - n_h, dtype=h.dtype)]))
@@ -159,7 +159,7 @@ def _1d_overlap_filter(x, h_fft, n_h, n_edge, zero_phase, cuda_dict):
         n_fft = cuda_dict['x'].size  # account for CUDA's modification of h_fft
     else:
         n_fft = len(h_fft)
-    x_ext = _smart_pad(x, n_edge)
+    x_ext = _smart_pad(x, np.array([n_edge, n_edge]))
     n_x = len(x_ext)
     x_filtered = np.zeros_like(x_ext)
 
@@ -329,8 +329,8 @@ def _filter(x, Fs, freq, gain, filter_length='10s', picks=None, n_jobs=1,
         att_db, att_freq = _filter_attenuation(h, freq, gain)
         if att_db < min_att_db:
             att_freq *= Fs / 2
-            warnings.warn('Attenuation at stop frequency %0.1fHz is only '
-                          '%0.1fdB.' % (att_freq, att_db))
+            warn('Attenuation at stop frequency %0.1fHz is only %0.1fdB.'
+                 % (att_freq, att_db))
 
         # Make zero-phase filter function
         B = np.abs(fft(h)).ravel()
@@ -363,9 +363,9 @@ def _filter(x, Fs, freq, gain, filter_length='10s', picks=None, n_jobs=1,
         att_db += 6  # the filter is applied twice (zero phase)
         if att_db < min_att_db:
             att_freq *= Fs / 2
-            warnings.warn('Attenuation at stop frequency %0.1fHz is only '
-                          '%0.1fdB. Increase filter_length for higher '
-                          'attenuation.' % (att_freq, att_db))
+            warn('Attenuation at stop frequency %0.1fHz is only %0.1fdB. '
+                 'Increase filter_length for higher attenuation.'
+                 % (att_freq, att_db))
 
         # reconstruct filter, this time with appropriate gain for fwd-bkwd
         gain = np.sqrt(gain)
@@ -1265,8 +1265,9 @@ def resample(x, up, down, npad=100, axis=-1, window='boxcar', n_jobs=1,
         Factor to upsample by.
     down : float
         Factor to downsample by.
-    npad : integer
+    npad : int | str
         Number of samples to use at the beginning and end for padding.
+        Can be "auto" to pad to the next highest power of 2.
     axis : int
         Axis along which to resample (default is the last axis).
     window : string or tuple
@@ -1315,15 +1316,33 @@ def resample(x, up, down, npad=100, axis=-1, window='boxcar', n_jobs=1,
     orig_shape = x.shape
     x_len = orig_shape[-1]
     if x_len == 0:
-        warnings.warn('x has zero length along last axis, returning a copy of '
-                      'x')
+        warn('x has zero length along last axis, returning a copy of x')
         return x.copy()
+    bad_msg = 'npad must be "auto" or an integer'
+    if isinstance(npad, string_types):
+        if npad != 'auto':
+            raise ValueError(bad_msg)
+        # Figure out reasonable pad that gets us to a power of 2
+        min_add = min(x_len // 8, 100) * 2
+        npad = 2 ** int(np.ceil(np.log2(x_len + min_add))) - x_len
+        npad, extra = divmod(npad, 2)
+        npads = np.array([npad, npad + extra], int)
+    else:
+        if npad != int(npad):
+            raise ValueError(bad_msg)
+        npads = np.array([npad, npad], int)
+    del npad
 
     # prep for resampling now
     x_flat = x.reshape((-1, x_len))
-    orig_len = x_len + 2 * npad  # length after padding
+    orig_len = x_len + npads.sum()  # length after padding
     new_len = int(round(ratio * orig_len))  # length after resampling
-    to_remove = np.round(ratio * npad).astype(int)
+    final_len = int(round(ratio * x_len))
+    to_removes = [int(round(ratio * npads[0]))]
+    to_removes.append(new_len - final_len - to_removes[0])
+    to_removes = np.array(to_removes)
+    # This should hold:
+    # assert np.abs(to_removes[1] - to_removes[0]) <= int(np.ceil(ratio))
 
     # figure out windowing function
     if window is not None:
@@ -1345,13 +1364,13 @@ def resample(x, up, down, npad=100, axis=-1, window='boxcar', n_jobs=1,
     # do the resampling using an adaptation of scipy's FFT-based resample()
     # use of the 'flat' window is recommended for minimal ringing
     if n_jobs == 1:
-        y = np.zeros((len(x_flat), new_len - 2 * to_remove), dtype=x.dtype)
+        y = np.zeros((len(x_flat), new_len - to_removes.sum()), dtype=x.dtype)
         for xi, x_ in enumerate(x_flat):
-            y[xi] = fft_resample(x_, W, new_len, npad, to_remove,
+            y[xi] = fft_resample(x_, W, new_len, npads, to_removes,
                                  cuda_dict)
     else:
         parallel, p_fun, _ = parallel_func(fft_resample, n_jobs)
-        y = parallel(p_fun(x_, W, new_len, npad, to_remove, cuda_dict)
+        y = parallel(p_fun(x_, W, new_len, npads, to_removes, cuda_dict)
                      for x_ in x_flat)
         y = np.array(y)
 
@@ -1491,9 +1510,9 @@ def _get_filter_length(filter_length, sfreq, min_length=128, len_x=np.inf):
         # only need to check min_length if the filter is shorter than len_x
         elif filter_length < min_length:
             filter_length = min_length
-            warnings.warn('filter_length was too short, using filter of '
-                          'length %d samples ("%0.1fs")'
-                          % (filter_length, filter_length / float(sfreq)))
+            warn('filter_length was too short, using filter of length %d '
+                 'samples ("%0.1fs")'
+                 % (filter_length, filter_length / float(sfreq)))
 
     if filter_length is not None:
         if not isinstance(filter_length, integer_types):
@@ -1504,7 +1523,7 @@ def _get_filter_length(filter_length, sfreq, min_length=128, len_x=np.inf):
 class FilterMixin(object):
     """Object for Epoch/Evoked filtering"""
 
-    def savgol_filter(self, h_freq):
+    def savgol_filter(self, h_freq, copy=False):
         """Filter the data using Savitzky-Golay polynomial method
 
         Parameters
@@ -1515,6 +1534,14 @@ class FilterMixin(object):
             done using polynomial fits instead of FIR/IIR filtering.
             This parameter is thus used to determine the length of the
             window over which a 5th-order polynomial smoothing is used.
+        copy : bool
+            If True, a copy of the object, filtered, is returned.
+            If False (default), it operates on the object in place.
+
+        Returns
+        -------
+        inst : instance of Epochs or Evoked
+            The object with the filtering applied.
 
         See Also
         --------
@@ -1522,8 +1549,6 @@ class FilterMixin(object):
 
         Notes
         -----
-        Data are modified in-place.
-
         For Savitzky-Golay low-pass approximation, see:
 
             https://gist.github.com/Eric89GXL/bbac101d50176611136b
@@ -1548,24 +1573,26 @@ class FilterMixin(object):
         """  # noqa
         from .evoked import Evoked
         from .epochs import _BaseEpochs
-        if isinstance(self, Evoked):
-            data = self.data
+        inst = self.copy() if copy else self
+        if isinstance(inst, Evoked):
+            data = inst.data
             axis = 1
-        elif isinstance(self, _BaseEpochs):
-            if not self.preload:
+        elif isinstance(inst, _BaseEpochs):
+            if not inst.preload:
                 raise RuntimeError('data must be preloaded to filter')
-            data = self._data
+            data = inst._data
             axis = 2
 
         h_freq = float(h_freq)
-        if h_freq >= self.info['sfreq'] / 2.:
+        if h_freq >= inst.info['sfreq'] / 2.:
             raise ValueError('h_freq must be less than half the sample rate')
 
         # savitzky-golay filtering
         if not check_version('scipy', '0.14'):
             raise RuntimeError('scipy >= 0.14 must be installed for savgol')
         from scipy.signal import savgol_filter
-        window_length = (int(np.round(self.info['sfreq'] /
+        window_length = (int(np.round(inst.info['sfreq'] /
                                       h_freq)) // 2) * 2 + 1
         data[...] = savgol_filter(data, axis=axis, polyorder=5,
                                   window_length=window_length)
+        return inst
diff --git a/mne/fixes.py b/mne/fixes.py
index f7d47f2..399715e 100644
--- a/mne/fixes.py
+++ b/mne/fixes.py
@@ -3,7 +3,7 @@
 If you add content to this file, please give the version of the package
 at which the fixe is no longer needed.
 
-# XXX : copied from scikit-learn
+# XXX : originally copied from scikit-learn
 
 """
 # Authors: Emmanuelle Gouillart <emmanuelle.gouillart at normalesup.org>
@@ -14,20 +14,22 @@ at which the fixe is no longer needed.
 
 from __future__ import division
 import collections
-from operator import itemgetter
+from distutils.version import LooseVersion
+from functools import partial
+from gzip import GzipFile
 import inspect
-
+from math import ceil, log
+from operator import itemgetter
+import re
 import warnings
+
 import numpy as np
+from numpy.fft import irfft
 import scipy
 from scipy import linalg, sparse
-from math import ceil, log
-from numpy.fft import irfft
-from distutils.version import LooseVersion
-from functools import partial
+
 from .externals import six
 from .externals.six.moves import copyreg, xrange
-from gzip import GzipFile
 
 
 ###############################################################################
@@ -773,6 +775,83 @@ def assert_is_not(expr1, expr2, msg=None):
     assert_true(expr1 is not expr2, msg)
 
 
+assert_raises_regex_impl = None
+
+
+# from numpy 1.9.1
+def assert_raises_regex(exception_class, expected_regexp,
+                        callable_obj=None, *args, **kwargs):
+    """
+    Fail unless an exception of class exception_class and with message that
+    matches expected_regexp is thrown by callable when invoked with arguments
+    args and keyword arguments kwargs.
+    Name of this function adheres to Python 3.2+ reference, but should work in
+    all versions down to 2.6.
+    """
+    __tracebackhide__ = True  # Hide traceback for py.test
+    import nose
+
+    global assert_raises_regex_impl
+    if assert_raises_regex_impl is None:
+        try:
+            # Python 3.2+
+            assert_raises_regex_impl = nose.tools.assert_raises_regex
+        except AttributeError:
+            try:
+                # 2.7+
+                assert_raises_regex_impl = nose.tools.assert_raises_regexp
+            except AttributeError:
+                # 2.6
+
+                # This class is copied from Python2.7 stdlib almost verbatim
+                class _AssertRaisesContext(object):
+
+                    def __init__(self, expected, expected_regexp=None):
+                        self.expected = expected
+                        self.expected_regexp = expected_regexp
+
+                    def failureException(self, msg):
+                        return AssertionError(msg)
+
+                    def __enter__(self):
+                        return self
+
+                    def __exit__(self, exc_type, exc_value, tb):
+                        if exc_type is None:
+                            try:
+                                exc_name = self.expected.__name__
+                            except AttributeError:
+                                exc_name = str(self.expected)
+                            raise self.failureException(
+                                "{0} not raised".format(exc_name))
+                        if not issubclass(exc_type, self.expected):
+                            # let unexpected exceptions pass through
+                            return False
+                        self.exception = exc_value  # store for later retrieval
+                        if self.expected_regexp is None:
+                            return True
+
+                        expected_regexp = self.expected_regexp
+                        if isinstance(expected_regexp, basestring):
+                            expected_regexp = re.compile(expected_regexp)
+                        if not expected_regexp.search(str(exc_value)):
+                            raise self.failureException(
+                                '"%s" does not match "%s"' %
+                                (expected_regexp.pattern, str(exc_value)))
+                        return True
+
+                def impl(cls, regex, callable_obj, *a, **kw):
+                    mgr = _AssertRaisesContext(cls, regex)
+                    if callable_obj is None:
+                        return mgr
+                    with mgr:
+                        callable_obj(*a, **kw)
+                assert_raises_regex_impl = impl
+
+    return assert_raises_regex_impl(exception_class, expected_regexp,
+                                    callable_obj, *args, **kwargs)
+
+
 def _sparse_block_diag(mats, format=None, dtype=None):
     """An implementation of scipy.sparse.block_diag since old versions of
     scipy don't have it. Forms a sparse matrix by stacking matrices in block
diff --git a/mne/forward/__init__.py b/mne/forward/__init__.py
index b2da0c0..1f8b21c 100644
--- a/mne/forward/__init__.py
+++ b/mne/forward/__init__.py
@@ -8,10 +8,13 @@ from .forward import (Forward, read_forward_solution, write_forward_solution,
                       _restrict_gain_matrix, _stc_src_sel,
                       _fill_measurement_info, _apply_forward,
                       _subject_from_forward, convert_forward_solution,
-                      _to_fixed_ori, prepare_bem_model, _merge_meg_eeg_fwds)
+                      _to_fixed_ori, prepare_bem_model, _merge_meg_eeg_fwds,
+                      _do_forward_solution)
 from ._make_forward import (make_forward_solution, _prepare_for_forward,
                             _prep_meg_channels, _prep_eeg_channels,
-                            _to_forward_dict, _create_meg_coils)
+                            _to_forward_dict, _create_meg_coils,
+                            _read_coil_defs, _transform_orig_meg_coils,
+                            make_forward_dipole)
 from ._compute_forward import (_magnetic_dipole_field_vec, _compute_forwards,
                                _concatenate_coils)
 from ._field_interpolation import (_make_surface_mapping, make_field_map,
diff --git a/mne/forward/_compute_forward.py b/mne/forward/_compute_forward.py
index 16b5271..f6bdab9 100644
--- a/mne/forward/_compute_forward.py
+++ b/mne/forward/_compute_forward.py
@@ -37,10 +37,10 @@ def _dup_coil_set(coils, coord_frame, t):
     if t is not None:
         coord_frame = t['to']
         for coil in coils:
+            for key in ('ex', 'ey', 'ez'):
+                if key in coil:
+                    coil[key] = apply_trans(t['trans'], coil[key], False)
             coil['r0'] = apply_trans(t['trans'], coil['r0'])
-            coil['ex'] = apply_trans(t['trans'], coil['ex'], False)
-            coil['ey'] = apply_trans(t['trans'], coil['ey'], False)
-            coil['ez'] = apply_trans(t['trans'], coil['ez'], False)
             coil['rmag'] = apply_trans(t['trans'], coil['rmag'])
             coil['cosmag'] = apply_trans(t['trans'], coil['cosmag'], False)
             coil['coord_frame'] = t['to']
@@ -200,15 +200,17 @@ def _bem_specify_coils(bem, coils, coord_frame, mults, n_jobs):
     # Process each of the surfaces
     rmags, cosmags, ws, bins = _concatenate_coils(coils)
     lens = np.cumsum(np.r_[0, [len(s['rr']) for s in bem['surfs']]])
-    coeff = np.empty((bins[-1] + 1, lens[-1]))  # shape(n_coils, n_BEM_verts)
+    sol = np.zeros((bins[-1] + 1, bem['solution'].shape[1]))
 
+    lims = np.concatenate([np.arange(0, sol.shape[0], 100), [sol.shape[0]]])
     # Compute coeffs for each surface, one at a time
     for o1, o2, surf, mult in zip(lens[:-1], lens[1:],
                                   bem['surfs'], bem['field_mult']):
-        coeff[:, o1:o2] = _lin_field_coeff(surf, mult, rmags, cosmags, ws,
-                                           bins, n_jobs)
-    # put through the bem
-    sol = np.dot(coeff, bem['solution'])
+        coeff = _lin_field_coeff(surf, mult, rmags, cosmags, ws, bins, n_jobs)
+        # put through the bem (in chunks to save memory)
+        for start, stop in zip(lims[:-1], lims[1:]):
+            sol[start:stop] += np.dot(coeff[start:stop],
+                                      bem['solution'][o1:o2])
     sol *= mults
     return sol
 
@@ -409,7 +411,7 @@ def _bem_pot_or_field(rr, mri_rr, mri_Q, coils, solution, bem_rr, n_jobs,
     Returns
     -------
     B : ndarray, shape (n_dipoles * 3, n_sensors)
-        Foward solution for a set of sensors
+        Forward solution for a set of sensors
     """
     # Both MEG and EEG have the inifinite-medium potentials
     # This could be just vectorized, but eats too much memory, so instead we
@@ -477,7 +479,7 @@ def _do_inf_pots(mri_rr, bem_rr, mri_Q, sol):
     Returns
     -------
     B : ndarray, (n_dipoles * 3, n_sensors)
-        Foward solution for sensors due to volume currents
+        Forward solution for sensors due to volume currents
     """
 
     # Doing work of 'fwd_bem_pot_calc' in MNE-C
@@ -487,7 +489,7 @@ def _do_inf_pots(mri_rr, bem_rr, mri_Q, sol):
     # B = np.dot(v0s, sol)
 
     # We chunk the source mri_rr's in order to save memory
-    bounds = np.r_[np.arange(0, len(mri_rr), 1000), len(mri_rr)]
+    bounds = np.concatenate([np.arange(0, len(mri_rr), 200), [len(mri_rr)]])
     B = np.empty((len(mri_rr) * 3, sol.shape[1]))
     for bi in range(len(bounds) - 1):
         # v0 in Hamalainen et al., 1989 == v_inf in Mosher, et al., 1999
@@ -679,7 +681,7 @@ def _prep_field_computation(rr, bem, fwd_data, n_jobs, verbose=None):
         Boundary Element Model information
     fwd_data : dict
         Dict containing sensor information. Gets updated here with BEM and
-        sensor information for later foward calculations
+        sensor information for later forward calculations
     n_jobs : int
         Number of jobs to run in parallel
     verbose : bool, str, int, or None
@@ -799,7 +801,7 @@ def _compute_forwards_meeg(rr, fd, n_jobs, verbose=None):
                     '(free orientations)...'
                     % (coil_type.upper(), len(rr),
                        '' if len(rr) == 1 else 's'))
-        # Calculate foward solution using spherical or BEM model
+        # Calculate forward solution using spherical or BEM model
         B = fun(rr, mri_rr, mri_Q, coils, solution, bem_rr, n_jobs,
                 coil_type)
 
diff --git a/mne/forward/_field_interpolation.py b/mne/forward/_field_interpolation.py
index e46f2cf..21fff15 100644
--- a/mne/forward/_field_interpolation.py
+++ b/mne/forward/_field_interpolation.py
@@ -111,9 +111,9 @@ def _map_meg_channels(info_from, info_to, mode='fast', origin=(0., 0., 0.04)):
 
     Parameters
     ----------
-    info_from : mne.io.MeasInfo
+    info_from : instance of Info
         The measurement data to interpolate from.
-    info_to : mne.io.MeasInfo
+    info_to : instance of Info
         The measurement info to interpolate to.
     mode : str
         Either `'accurate'` or `'fast'`, determines the quality of the
@@ -196,9 +196,9 @@ def _as_meg_type_evoked(evoked, ch_type='grad', mode='fast'):
                          ' locations of the destination channels will be used'
                          ' for interpolation.')
 
-    info_from = pick_info(evoked.info, pick_from, copy=True)
-    info_to = pick_info(evoked.info, pick_to, copy=True)
-    mapping = _map_meg_channels(info_from, info_to, mode='fast')
+    info_from = pick_info(evoked.info, pick_from)
+    info_to = pick_info(evoked.info, pick_to)
+    mapping = _map_meg_channels(info_from, info_to, mode=mode)
 
     # compute evoked data by multiplying by the 'gain matrix' from
     # original sensors to virtual sensors
@@ -211,8 +211,8 @@ def _as_meg_type_evoked(evoked, ch_type='grad', mode='fast'):
     # change channel names to emphasize they contain interpolated data
     for ch in evoked.info['chs']:
         ch['ch_name'] += '_virtual'
-    evoked.info['ch_names'] = [ch['ch_name'] for ch in evoked.info['chs']]
-
+    evoked.info._update_redundant()
+    evoked.info._check_consistency()
     return evoked
 
 
@@ -223,7 +223,7 @@ def _make_surface_mapping(info, surf, ch_type='meg', trans=None, mode='fast',
 
     Parameters
     ----------
-    info : instance of io.meas_info.Info
+    info : instance of Info
         Measurement info.
     surf : dict
         The surface to map the data to. The required fields are `'rr'`,
@@ -261,6 +261,7 @@ def _make_surface_mapping(info, surf, ch_type='meg', trans=None, mode='fast',
         raise ValueError('mode must be "accurate" or "fast", not "%s"' % mode)
 
     # deal with coordinate frames here -- always go to "head" (easiest)
+    orig_surf = surf
     surf = transform_surface_to(deepcopy(surf), 'head', trans)
     n_jobs = check_n_jobs(n_jobs)
     origin = _check_origin(origin, info)
@@ -279,7 +280,7 @@ def _make_surface_mapping(info, surf, ch_type='meg', trans=None, mode='fast',
         logger.info('Prepare EEG mapping...')
     if len(picks) == 0:
         raise RuntimeError('cannot map, no channels found')
-    chs = pick_info(info, picks, copy=True)['chs']
+    chs = pick_info(info, picks)['chs']
 
     # create coil defs in head coordinates
     if ch_type == 'meg':
@@ -316,8 +317,10 @@ def _make_surface_mapping(info, surf, ch_type='meg', trans=None, mode='fast',
     logger.info('Field mapping data ready')
 
     fmd['data'] = _compute_mapping_matrix(fmd, info)
+    # bring the original back, whatever coord frame it was in
+    fmd['surf'] = orig_surf
 
-    # Remove some unecessary fields
+    # Remove some unnecessary fields
     del fmd['self_dots']
     del fmd['surface_dots']
     del fmd['int_rad']
@@ -417,8 +420,8 @@ def make_field_map(evoked, trans='auto', subject=None, subjects_dir=None,
 
     for this_type, this_surf in zip(types, surfs):
         this_map = _make_surface_mapping(evoked.info, this_surf, this_type,
-                                         trans, n_jobs=n_jobs, origin=origin)
-        this_map['surf'] = this_surf  # XXX : a bit weird...
+                                         trans, n_jobs=n_jobs, origin=origin,
+                                         mode=mode)
         surf_maps.append(this_map)
 
     return surf_maps
diff --git a/mne/forward/_lead_dots.py b/mne/forward/_lead_dots.py
index 25be329..05fcba8 100644
--- a/mne/forward/_lead_dots.py
+++ b/mne/forward/_lead_dots.py
@@ -106,30 +106,39 @@ def _get_legen_table(ch_type, volume_integral=False, n_coeff=100,
     return lut, n_fact
 
 
-def _get_legen_lut_fast(x, lut):
+def _get_legen_lut_fast(x, lut, block=None):
     """Return Legendre coefficients for given x values in -1<=x<=1"""
     # map into table vals (works for both vals and deriv tables)
     n_interp = (lut.shape[0] - 1.0)
     # equiv to "(x + 1.0) / 2.0) * n_interp" but faster
-    mm = x * (n_interp / 2.0) + 0.5 * n_interp
+    mm = x * (n_interp / 2.0)
+    mm += 0.5 * n_interp
     # nearest-neighbor version (could be decent enough...)
     idx = np.round(mm).astype(int)
-    vals = lut[idx]
+    if block is None:
+        vals = lut[idx]
+    else:  # read only one block at a time to minimize memory consumption
+        vals = lut[idx, :, block]
     return vals
 
 
-def _get_legen_lut_accurate(x, lut):
+def _get_legen_lut_accurate(x, lut, block=None):
     """Return Legendre coefficients for given x values in -1<=x<=1"""
     # map into table vals (works for both vals and deriv tables)
     n_interp = (lut.shape[0] - 1.0)
     # equiv to "(x + 1.0) / 2.0) * n_interp" but faster
-    mm = x * (n_interp / 2.0) + 0.5 * n_interp
+    mm = x * (n_interp / 2.0)
+    mm += 0.5 * n_interp
     # slower, more accurate interpolation version
     mm = np.minimum(mm, n_interp - 0.0000000001)
     idx = np.floor(mm).astype(int)
     w2 = mm - idx
-    w2.shape += tuple([1] * (lut.ndim - w2.ndim))  # expand to correct size
-    vals = (1 - w2) * lut[idx] + w2 * lut[idx + 1]
+    if block is None:
+        w2.shape += tuple([1] * (lut.ndim - w2.ndim))  # expand to correct size
+        vals = (1 - w2) * lut[idx] + w2 * lut[idx + 1]
+    else:  # read only one block at a time to minimize memory consumption
+        w2.shape += tuple([1] * (lut[:, :, block].ndim - w2.ndim))
+        vals = (1 - w2) * lut[idx, :, block] + w2 * lut[idx + 1, :, block]
     return vals
 
 
@@ -138,11 +147,15 @@ def _comp_sum_eeg(beta, ctheta, lut_fun, n_fact):
     # Compute the sum occurring in the evaluation.
     # The result is
     #   sums[:]    (2n+1)^2/n beta^n P_n
-    coeffs = lut_fun(ctheta)
-    betans = np.cumprod(np.tile(beta[:, np.newaxis], (1, n_fact.shape[0])),
-                        axis=1)
-    coeffs *= betans
-    s0 = np.dot(coeffs, n_fact)  # == weighted sum across cols
+    n_chunk = 50000000 // (8 * max(n_fact.shape) * 2)
+    lims = np.concatenate([np.arange(0, beta.size, n_chunk), [beta.size]])
+    s0 = np.empty(beta.shape)
+    for start, stop in zip(lims[:-1], lims[1:]):
+        coeffs = lut_fun(ctheta[start:stop])
+        betans = np.tile(beta[start:stop][:, np.newaxis], (1, n_fact.shape[0]))
+        np.cumprod(betans, axis=1, out=betans)  # run inplace
+        coeffs *= betans
+        s0[start:stop] = np.dot(coeffs, n_fact)  # == weighted sum across cols
     return s0
 
 
@@ -174,14 +187,23 @@ def _comp_sums_meg(beta, ctheta, lut_fun, n_fact, volume_integral):
     #  * sums[:, 1]    n/(2n+1) beta^(n+1) P_n'
     #  * sums[:, 2]    n/((2n+1)(n+1)) beta^(n+1) P_n'
     #  * sums[:, 3]    n/((2n+1)(n+1)) beta^(n+1) P_n''
-    coeffs = lut_fun(ctheta)
-    bbeta = np.cumprod(np.tile(beta[np.newaxis], (n_fact.shape[0], 1)),
-                       axis=0)
-    bbeta *= beta
+
     # This is equivalent, but slower:
     # sums = np.sum(bbeta[:, :, np.newaxis].T * n_fact * coeffs, axis=1)
     # sums = np.rollaxis(sums, 2)
-    sums = np.einsum('ji,jk,ijk->ki', bbeta, n_fact, coeffs)
+    # or
+    # sums = np.einsum('ji,jk,ijk->ki', bbeta, n_fact, lut_fun(ctheta)))
+    sums = np.empty((n_fact.shape[1], len(beta)))
+    # beta can be e.g. 3 million elements, which ends up using lots of memory
+    # so we split up the computations into ~50 MB blocks
+    n_chunk = 50000000 // (8 * max(n_fact.shape) * 2)
+    lims = np.concatenate([np.arange(0, beta.size, n_chunk), [beta.size]])
+    for start, stop in zip(lims[:-1], lims[1:]):
+        bbeta = np.tile(beta[start:stop][np.newaxis], (n_fact.shape[0], 1))
+        bbeta[0] *= beta[start:stop]
+        np.cumprod(bbeta, axis=0, out=bbeta)  # run inplace
+        np.einsum('ji,jk,ijk->ki', bbeta, n_fact, lut_fun(ctheta[start:stop]),
+                  out=sums[:, start:stop])
     return sums
 
 
@@ -233,8 +255,10 @@ def _fast_sphere_dot_r0(r, rr1_orig, rr2s, lr1, lr2s, cosmags1, cosmags2s,
     """
     if w1 is None:  # operating on surface, treat independently
         out_shape = (len(rr2s), len(rr1_orig))
+        sum_axis = 1  # operate along second axis only at the end
     else:
         out_shape = (len(rr2s),)
+        sum_axis = None  # operate on flattened array at the end
     out = np.empty(out_shape)
     rr2 = np.concatenate(rr2s)
     lr2 = np.concatenate(lr2s)
@@ -291,7 +315,7 @@ def _fast_sphere_dot_r0(r, rr1_orig, rr2s, lr1, lr2s, cosmags1, cosmags2s,
     if w1 is not None:
         result *= w1[:, np.newaxis]
     for ii, w2 in enumerate(w2s):
-        out[ii] = np.sum(result[:, offset:offset + len(w2)])
+        out[ii] = np.sum(result[:, offset:offset + len(w2)], axis=sum_axis)
         offset += len(w2)
     return out
 
diff --git a/mne/forward/_make_forward.py b/mne/forward/_make_forward.py
index a790bc2..145d06a 100644
--- a/mne/forward/_make_forward.py
+++ b/mne/forward/_make_forward.py
@@ -15,14 +15,18 @@ from ..io.constants import FIFF
 from ..transforms import (_ensure_trans, transform_surface_to, apply_trans,
                           _get_trans, _print_coord_trans, _coord_frame_name,
                           Transform)
-from ..utils import logger, verbose
-from ..source_space import _ensure_src, _filter_source_spaces
+from ..utils import logger, verbose, warn
+from ..source_space import (_ensure_src, _filter_source_spaces,
+                            _make_discrete_source_space, SourceSpaces)
+from ..source_estimate import VolSourceEstimate
 from ..surface import _normalize_vectors
 from ..bem import read_bem_solution, _bem_find_surface, ConductorModel
 from ..externals.six import string_types
 
-from .forward import Forward, write_forward_solution, _merge_meg_eeg_fwds
+from .forward import (Forward, write_forward_solution, _merge_meg_eeg_fwds,
+                      convert_forward_solution)
 from ._compute_forward import _compute_forwards
+from ..fixes import in1d
 
 
 _accuracy_dict = dict(normal=FIFF.FWD_COIL_ACCURACY_NORMAL,
@@ -111,12 +115,9 @@ def _read_coil_def_file(fname):
     return coils
 
 
-def _create_meg_coil(coilset, ch, acc, t):
+def _create_meg_coil(coilset, ch, acc, do_es):
     """Create a coil definition using templates, transform if necessary"""
     # Also change the coordinate frame if so desired
-    if t is None:
-        t = Transform('meg', 'meg', np.eye(4))  # identity, no change
-
     if ch['kind'] not in [FIFF.FIFFV_MEG_CH, FIFF.FIFFV_REF_MEG_CH]:
         raise RuntimeError('%s is not a MEG channel' % ch['ch_name'])
 
@@ -130,18 +131,22 @@ def _create_meg_coil(coilset, ch, acc, t):
                            '(type = %d acc = %d)' % (ch['coil_type'], acc))
 
     # Apply a coordinate transformation if so desired
-    coil_trans = np.dot(t['trans'], _loc_to_coil_trans(ch['loc']))
+    coil_trans = _loc_to_coil_trans(ch['loc'])
 
     # Create the result
     res = dict(chname=ch['ch_name'], coil_class=coil['coil_class'],
                accuracy=coil['accuracy'], base=coil['base'], size=coil['size'],
                type=ch['coil_type'], w=coil['w'], desc=coil['desc'],
-               coord_frame=t['to'], rmag=apply_trans(coil_trans, coil['rmag']),
+               coord_frame=FIFF.FIFFV_COORD_DEVICE, rmag_orig=coil['rmag'],
+               cosmag_orig=coil['cosmag'], coil_trans_orig=coil_trans,
+               r0=coil_trans[:3, 3],
+               rmag=apply_trans(coil_trans, coil['rmag']),
                cosmag=apply_trans(coil_trans, coil['cosmag'], False))
-    r0_exey = (np.dot(coil['rmag'][:, :2], coil_trans[:3, :2].T) +
-               coil_trans[:3, 3])
-    res.update(ex=coil_trans[:3, 0], ey=coil_trans[:3, 1],
-               ez=coil_trans[:3, 2], r0=coil_trans[:3, 3], r0_exey=r0_exey)
+    if do_es:
+        r0_exey = (np.dot(coil['rmag'][:, :2], coil_trans[:3, :2].T) +
+                   coil_trans[:3, 3])
+        res.update(ex=coil_trans[:3, 0], ey=coil_trans[:3, 1],
+                   ez=coil_trans[:3, 2], r0_exey=r0_exey)
     return res
 
 
@@ -173,14 +178,32 @@ def _create_eeg_el(ch, t=None):
     return res
 
 
-def _create_meg_coils(chs, acc=None, t=None, coilset=None):
+def _create_meg_coils(chs, acc, t=None, coilset=None, do_es=False):
     """Create a set of MEG coils in the head coordinate frame"""
     acc = _accuracy_dict[acc] if isinstance(acc, string_types) else acc
     coilset = _read_coil_defs(verbose=False) if coilset is None else coilset
-    coils = [_create_meg_coil(coilset, ch, acc, t) for ch in chs]
+    coils = [_create_meg_coil(coilset, ch, acc, do_es) for ch in chs]
+    _transform_orig_meg_coils(coils, t, do_es=do_es)
     return coils
 
 
+def _transform_orig_meg_coils(coils, t, do_es=True):
+    """Helper to transform original (device) MEG coil positions"""
+    if t is None:
+        return
+    for coil in coils:
+        coil_trans = np.dot(t['trans'], coil['coil_trans_orig'])
+        coil.update(
+            coord_frame=t['to'], r0=coil_trans[:3, 3],
+            rmag=apply_trans(coil_trans, coil['rmag_orig']),
+            cosmag=apply_trans(coil_trans, coil['cosmag_orig'], False))
+        if do_es:
+            r0_exey = (np.dot(coil['rmag_orig'][:, :2],
+                              coil_trans[:3, :2].T) + coil_trans[:3, 3])
+            coil.update(ex=coil_trans[:3, 0], ey=coil_trans[:3, 1],
+                        ez=coil_trans[:3, 2], r0_exey=r0_exey)
+
+
 def _create_eeg_els(chs):
     """Create a set of EEG electrodes in the head coordinate frame"""
     return [_create_eeg_el(ch) for ch in chs]
@@ -197,8 +220,9 @@ def _setup_bem(bem, bem_extra, neeg, mri_head_t, verbose=None):
         raise TypeError('bem must be a string or ConductorModel')
     if bem['is_sphere']:
         logger.info('Using the sphere model.\n')
-        if len(bem['layers']) == 0:
-            raise RuntimeError('Spherical model has zero layers')
+        if len(bem['layers']) == 0 and neeg > 0:
+            raise RuntimeError('Spherical model has zero shells, cannot use '
+                               'with EEG data')
         if bem['coord_frame'] != FIFF.FIFFV_COORD_HEAD:
             raise RuntimeError('Spherical model is not in head coordinates')
     else:
@@ -216,7 +240,8 @@ def _setup_bem(bem, bem_extra, neeg, mri_head_t, verbose=None):
 
 @verbose
 def _prep_meg_channels(info, accurate=True, exclude=(), ignore_ref=False,
-                       elekta_defs=False, head_frame=True, verbose=None):
+                       elekta_defs=False, head_frame=True, do_es=False,
+                       verbose=None):
     """Prepare MEG coil definitions for forward calculation
 
     Parameters
@@ -236,6 +261,8 @@ def _prep_meg_channels(info, accurate=True, exclude=(), ignore_ref=False,
         point geometry. False by default.
     head_frame : bool
         If True (default), use head frame coords. Otherwise, use device frame.
+    do_es : bool
+        If True, compute and store ex, ey, ez, and r0_exey.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
         Defaults to raw.verbose.
@@ -248,7 +275,7 @@ def _prep_meg_channels(info, accurate=True, exclude=(), ignore_ref=False,
         Information for each prepped MEG coil
     megnames : list of str
         Name of each prepped MEG coil
-    meginfo : Info
+    meginfo : instance of Info
         Information subselected for just the set of MEG coils
     """
 
@@ -304,12 +331,14 @@ def _prep_meg_channels(info, accurate=True, exclude=(), ignore_ref=False,
     else:
         transform = None
 
-    megcoils = _create_meg_coils(megchs, accuracy, transform, templates)
+    megcoils = _create_meg_coils(megchs, accuracy, transform, templates,
+                                 do_es=do_es)
 
     if ncomp > 0:
         logger.info('%d compensation data sets in %s' % (ncomp_data,
                                                          info_extra))
-        compcoils = _create_meg_coils(compchs, 'normal', transform, templates)
+        compcoils = _create_meg_coils(compchs, 'normal', transform, templates,
+                                      do_es=do_es)
 
     # Check that coordinate frame is correct and log it
     if head_frame:
@@ -395,15 +424,16 @@ def _prepare_for_forward(src, mri_head_t, info, bem, mindist, n_jobs,
     _print_coord_trans(mri_head_t)
 
     # make a new dict with the relevant information
-    arg_list = [info_extra, trans, src, bem_extra, fname,  meg, eeg,
+    arg_list = [info_extra, trans, src, bem_extra, fname, meg, eeg,
                 mindist, overwrite, n_jobs, verbose]
     cmd = 'make_forward_solution(%s)' % (', '.join([str(a) for a in arg_list]))
     mri_id = dict(machid=np.zeros(2, np.int32), version=0, secs=0, usecs=0)
-    info = Info(nchan=info['nchan'], chs=info['chs'], comps=info['comps'],
-                ch_names=info['ch_names'], dev_head_t=info['dev_head_t'],
-                mri_file=trans, mri_id=mri_id, meas_file=info_extra,
-                meas_id=None, working_dir=os.getcwd(),
+    info = Info(chs=info['chs'], comps=info['comps'],
+                dev_head_t=info['dev_head_t'], mri_file=trans, mri_id=mri_id,
+                meas_file=info_extra, meas_id=None, working_dir=os.getcwd(),
                 command_line=cmd, bads=info['bads'], mri_head_t=mri_head_t)
+    info._update_redundant()
+    info._check_consistency()
     logger.info('')
 
     megcoils, compcoils, megnames, meg_info = [], [], [], []
@@ -441,6 +471,9 @@ def _prepare_for_forward(src, mri_head_t, info, bem, mindist, n_jobs,
         logger.info('')
 
     rr = np.concatenate([s['rr'][s['vertno']] for s in src])
+    if len(rr) < 1:
+        raise RuntimeError('No points left in source space after excluding '
+                           'points close to inner skull.')
 
     # deal with free orientations:
     source_nn = np.tile(np.eye(3), (len(rr), 1))
@@ -459,7 +492,7 @@ def make_forward_solution(info, trans, src, bem, fname=None, meg=True,
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info | str
+    info : instance of mne.Info | str
         If str, then it should be a filename to a Raw, Epochs, or Evoked
         file with measurement information. If dict, should be an info
         dict (such as one from Raw, Epochs, or Evoked).
@@ -502,16 +535,12 @@ def make_forward_solution(info, trans, src, bem, fname=None, meg=True,
     fwd : instance of Forward
         The forward solution.
 
-    See Also
-    --------
-    do_forward_solution
-
     Notes
     -----
     Some of the forward solution calculation options from the C code
     (e.g., `--grad`, `--fixed`) are not implemented here. For those,
-    consider using the C command line tools or the Python wrapper
-    `do_forward_solution`.
+    consider using the C command line tools, or request that they
+    be added to the MNE-Python.
     """
     # Currently not (sup)ported:
     # 1. --grad option (gradients of the field, not used much)
@@ -525,13 +554,13 @@ def make_forward_solution(info, trans, src, bem, fname=None, meg=True,
     if fname is not None and op.isfile(fname) and not overwrite:
         raise IOError('file "%s" exists, consider using overwrite=True'
                       % fname)
-    if not isinstance(info, (dict, string_types)):
-        raise TypeError('info should be a dict or string')
+    if not isinstance(info, (Info, string_types)):
+        raise TypeError('info should be an instance of Info or string')
     if isinstance(info, string_types):
         info_extra = op.split(info)[1]
         info = read_info(info, verbose=False)
     else:
-        info_extra = 'info dict'
+        info_extra = 'instance of Info'
 
     # Report the setup
     logger.info('Source space                 : %s' % src)
@@ -582,6 +611,130 @@ def make_forward_solution(info, trans, src, bem, fname=None, meg=True,
     return fwd
 
 
+def make_forward_dipole(dipole, bem, info, trans=None, n_jobs=1, verbose=None):
+    """Convert dipole object to source estimate and calculate forward operator
+
+    The instance of Dipole is converted to a discrete source space,
+    which is then combined with a BEM or a sphere model and
+    the sensor information in info to form a forward operator.
+
+    The source estimate object (with the forward operator) can be projected to
+    sensor-space using :func:`mne.simulation.evoked.simulate_evoked`.
+
+    Note that if the (unique) time points of the dipole object are unevenly
+    spaced, the first output will be a list of single-timepoint source
+    estimates.
+
+    Parameters
+    ----------
+    dipole : instance of Dipole
+        Dipole object containing position, orientation and amplitude of
+        one or more dipoles. Multiple simultaneous dipoles may be defined by
+        assigning them identical times.
+    bem : str | dict
+        The BEM filename (str) or a loaded sphere model (dict).
+    info : instance of Info
+        The measurement information dictionary. It is sensor-information etc.,
+        e.g., from a real data file.
+    trans : str | None
+        The head<->MRI transform filename. Must be provided unless BEM
+        is a sphere model.
+    n_jobs : int
+        Number of jobs to run in parallel (used in making forward solution).
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    Returns
+    -------
+    fwd : instance of Forward
+        The forward solution corresponding to the source estimate(s).
+    stc : instance of VolSourceEstimate | list of VolSourceEstimate
+        The dipoles converted to a discrete set of points and associated
+        time courses. If the time points of the dipole are unevenly spaced,
+        a list of single-timepoint source estimates are returned.
+
+    See Also
+    --------
+    mne.simulation.simulate_evoked
+
+    Notes
+    -----
+    .. versionadded:: 0.12.0
+    """
+    # Make copies to avoid mangling original dipole
+    times = dipole.times.copy()
+    pos = dipole.pos.copy()
+    amplitude = dipole.amplitude.copy()
+    ori = dipole.ori.copy()
+
+    # Convert positions to discrete source space (allows duplicate rr & nn)
+    # NB information about dipole orientation enters here, then no more
+    sources = dict(rr=pos, nn=ori)
+    # Dipole objects must be in the head frame
+    sp = _make_discrete_source_space(sources, coord_frame='head')
+    src = SourceSpaces([sp])  # dict with working_dir, command_line not nec
+
+    # Forward operator created for channels in info (use pick_info to restrict)
+    # Use defaults for most params, including min_dist
+    fwd = make_forward_solution(info, trans, src, bem, fname=None,
+                                n_jobs=n_jobs, verbose=verbose)
+    # Convert from free orientations to fixed (in-place)
+    convert_forward_solution(fwd, surf_ori=False, force_fixed=True,
+                             copy=False, verbose=None)
+
+    # Check for omissions due to proximity to inner skull in
+    # make_forward_solution, which will result in an exception
+    if fwd['src'][0]['nuse'] != len(pos):
+        inuse = fwd['src'][0]['inuse'].astype(np.bool)
+        head = ('The following dipoles are outside the inner skull boundary')
+        msg = len(head) * '#' + '\n' + head + '\n'
+        for (t, pos) in zip(times[np.logical_not(inuse)],
+                            pos[np.logical_not(inuse)]):
+            msg += '    t={:.0f} ms, pos=({:.0f}, {:.0f}, {:.0f}) mm\n'.\
+                format(t * 1000., pos[0] * 1000.,
+                       pos[1] * 1000., pos[2] * 1000.)
+        msg += len(head) * '#'
+        logger.error(msg)
+        raise ValueError('One or more dipoles outside the inner skull.')
+
+    # multiple dipoles (rr and nn) per time instant allowed
+    # uneven sampling in time returns list
+    timepoints = np.unique(times)
+    if len(timepoints) > 1:
+        tdiff = np.diff(timepoints)
+        if not np.allclose(tdiff, tdiff[0]):
+            warn('Unique time points of dipoles unevenly spaced: returned '
+                 'stc will be a list, one for each time point.')
+            tstep = -1.0
+        else:
+            tstep = tdiff[0]
+    elif len(timepoints) == 1:
+        tstep = 0.001
+
+    # Build the data matrix, essentially a block-diagonal with
+    # n_rows: number of dipoles in total (dipole.amplitudes)
+    # n_cols: number of unique time points in dipole.times
+    # amplitude with identical value of times go together in one col (others=0)
+    data = np.zeros((len(amplitude), len(timepoints)))  # (n_d, n_t)
+    row = 0
+    for tpind, tp in enumerate(timepoints):
+        amp = amplitude[in1d(times, tp)]
+        data[row:row + len(amp), tpind] = amp
+        row += len(amp)
+
+    if tstep > 0:
+        stc = VolSourceEstimate(data, vertices=fwd['src'][0]['vertno'],
+                                tmin=timepoints[0],
+                                tstep=tstep, subject=None)
+    else:  # Must return a list of stc, one for each time point
+        stc = []
+        for col, tp in enumerate(timepoints):
+            stc += [VolSourceEstimate(data[:, col][:, np.newaxis],
+                                      vertices=fwd['src'][0]['vertno'],
+                                      tmin=tp, tstep=0.001, subject=None)]
+    return fwd, stc
+
+
 def _to_forward_dict(fwd, names, fwd_grad=None,
                      coord_frame=FIFF.FIFFV_COORD_HEAD,
                      source_ori=FIFF.FIFFV_MNE_FREE_ORI):
diff --git a/mne/forward/forward.py b/mne/forward/forward.py
index 773026d..c2f2fd1 100644
--- a/mne/forward/forward.py
+++ b/mne/forward/forward.py
@@ -4,9 +4,7 @@
 #
 # License: BSD (3-clause)
 
-from ..externals.six import string_types
 from time import time
-import warnings
 from copy import deepcopy
 import re
 
@@ -18,6 +16,7 @@ import os
 from os import path as op
 import tempfile
 
+from ..externals.six import string_types
 from ..fixes import sparse_block_diag
 from ..io import RawArray, Info
 from ..io.constants import FIFF
@@ -26,28 +25,33 @@ from ..io.tree import dir_tree_find
 from ..io.tag import find_tag, read_tag
 from ..io.matrix import (_read_named_matrix, _transpose_named_matrix,
                          write_named_matrix)
-from ..io.meas_info import read_bad_channels
+from ..io.meas_info import read_bad_channels, write_info
 from ..io.pick import (pick_channels_forward, pick_info, pick_channels,
                        pick_types)
 from ..io.write import (write_int, start_block, end_block,
                         write_coord_trans, write_ch_info, write_name_list,
                         write_string, start_file, end_file, write_id)
 from ..io.base import _BaseRaw
-from ..evoked import Evoked, write_evokeds, EvokedArray
-from ..epochs import Epochs, _BaseEpochs
+from ..evoked import Evoked, EvokedArray
+from ..epochs import _BaseEpochs
 from ..source_space import (_read_source_spaces_from_tree,
                             find_source_space_hemi,
                             _write_source_spaces_to_fid)
 from ..source_estimate import VolSourceEstimate
 from ..transforms import (transform_surface_to, invert_transform,
                           write_trans)
-from ..utils import (_check_fname, get_subjects_dir, has_mne_c,
-                     run_subprocess, check_fname, logger, verbose)
+from ..utils import (_check_fname, get_subjects_dir, has_mne_c, warn,
+                     run_subprocess, check_fname, logger, verbose,
+                     deprecated)
+from ..label import Label
 
 
 class Forward(dict):
     """Forward class to represent info from forward solution
     """
+    def copy(self):
+        """Copy the Forward instance"""
+        return Forward(deepcopy(self))
 
     def __repr__(self):
         """Summarize forward info instead of printing all"""
@@ -238,8 +242,8 @@ def _read_one(fid, node):
                                 FIFF.FIFF_NCHAN)
     try:
         one['sol'] = _read_named_matrix(fid, node,
-                                        FIFF.FIFF_MNE_FORWARD_SOLUTION)
-        one['sol'] = _transpose_named_matrix(one['sol'], copy=False)
+                                        FIFF.FIFF_MNE_FORWARD_SOLUTION,
+                                        transpose=True)
         one['_orig_sol'] = one['sol']['data'].copy()
     except Exception:
         logger.error('Forward solution data not found')
@@ -247,8 +251,8 @@ def _read_one(fid, node):
 
     try:
         fwd_type = FIFF.FIFF_MNE_FORWARD_SOLUTION_GRAD
-        one['sol_grad'] = _read_named_matrix(fid, node, fwd_type)
-        one['sol_grad'] = _transpose_named_matrix(one['sol_grad'], copy=False)
+        one['sol_grad'] = _read_named_matrix(fid, node, fwd_type,
+                                             transpose=True)
         one['_orig_sol_grad'] = one['sol_grad']['data'].copy()
     except Exception:
         one['sol_grad'] = None
@@ -280,7 +284,7 @@ def _read_forward_meas_info(tree, fid):
 
     Returns
     -------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info.
     """
     # This function assumes fid is being used as a context manager
@@ -317,9 +321,7 @@ def _read_forward_meas_info(tree, fid):
             tag = read_tag(fid, pos)
             chs.append(tag.data)
     info['chs'] = chs
-
-    info['ch_names'] = [c['ch_name'] for c in chs]
-    info['nchan'] = len(chs)
+    info._update_redundant()
 
     #   Get the MRI <-> head coordinate transformation
     tag = find_tag(fid, parent_mri, FIFF.FIFF_COORD_TRANS)
@@ -581,8 +583,8 @@ def convert_forward_solution(fwd, surf_ori=False, force_fixed=False,
         implies surf_ori=True.
     force_fixed : bool, optional (default False)
         Force fixed source orientation mode?
-    copy : bool, optional (default True)
-        If False, operation will be done in-place (modifying the input).
+    copy : bool
+        Whether to return a new instance or modify in place.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -591,8 +593,7 @@ def convert_forward_solution(fwd, surf_ori=False, force_fixed=False,
     fwd : dict
         The modified forward solution.
     """
-    if copy is True:
-        fwd = deepcopy(fwd)
+    fwd = fwd.copy() if copy else fwd
 
     # We need to change these entries (only):
     # 1. source_nn
@@ -789,7 +790,7 @@ def write_forward_solution(fname, fwd, overwrite=False, verbose=None):
     if n_meg > 0:
         meg_solution = dict(data=sol[picks_meg], nrow=n_meg, ncol=n_col,
                             row_names=row_names_meg, col_names=[])
-        meg_solution = _transpose_named_matrix(meg_solution, copy=False)
+        _transpose_named_matrix(meg_solution)
         start_block(fid, FIFF.FIFFB_MNE_FORWARD_SOLUTION)
         write_int(fid, FIFF.FIFF_MNE_INCLUDED_METHODS, FIFF.FIFFV_MNE_MEG)
         write_int(fid, FIFF.FIFF_MNE_COORD_FRAME, fwd['coord_frame'])
@@ -801,8 +802,7 @@ def write_forward_solution(fname, fwd, overwrite=False, verbose=None):
             meg_solution_grad = dict(data=sol_grad[picks_meg],
                                      nrow=n_meg, ncol=n_col * 3,
                                      row_names=row_names_meg, col_names=[])
-            meg_solution_grad = _transpose_named_matrix(meg_solution_grad,
-                                                        copy=False)
+            _transpose_named_matrix(meg_solution_grad)
             write_named_matrix(fid, FIFF.FIFF_MNE_FORWARD_SOLUTION_GRAD,
                                meg_solution_grad)
         end_block(fid, FIFF.FIFFB_MNE_FORWARD_SOLUTION)
@@ -813,7 +813,7 @@ def write_forward_solution(fname, fwd, overwrite=False, verbose=None):
     if n_eeg > 0:
         eeg_solution = dict(data=sol[picks_eeg], nrow=n_eeg, ncol=n_col,
                             row_names=row_names_eeg, col_names=[])
-        eeg_solution = _transpose_named_matrix(eeg_solution, copy=False)
+        _transpose_named_matrix(eeg_solution)
         start_block(fid, FIFF.FIFFB_MNE_FORWARD_SOLUTION)
         write_int(fid, FIFF.FIFF_MNE_INCLUDED_METHODS, FIFF.FIFFV_MNE_EEG)
         write_int(fid, FIFF.FIFF_MNE_COORD_FRAME, fwd['coord_frame'])
@@ -825,8 +825,7 @@ def write_forward_solution(fname, fwd, overwrite=False, verbose=None):
             eeg_solution_grad = dict(data=sol_grad[picks_eeg],
                                      nrow=n_eeg, ncol=n_col * 3,
                                      row_names=row_names_eeg, col_names=[])
-            eeg_solution_grad = _transpose_named_matrix(eeg_solution_grad,
-                                                        copy=False)
+            _transpose_named_matrix(eeg_solution_grad)
             write_named_matrix(fid, FIFF.FIFF_MNE_FORWARD_SOLUTION_GRAD,
                                eeg_solution_grad)
         end_block(fid, FIFF.FIFFB_MNE_FORWARD_SOLUTION)
@@ -865,7 +864,7 @@ def write_forward_meas_info(fid, info):
     ----------
     fid : file id
         The file id
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info.
     """
     info._check_consistency()
@@ -932,8 +931,8 @@ def compute_orient_prior(forward, loose=0.2, verbose=None):
                              'not %s.' % loose)
 
         if is_fixed_ori:
-            warnings.warn('Ignoring loose parameter with forward operator '
-                          'with fixed orientation.')
+            warn('Ignoring loose parameter with forward operator '
+                 'with fixed orientation.')
 
     orient_prior = np.ones(n_sources, dtype=np.float)
     if (not is_fixed_ori) and (loose is not None) and (loose < 1):
@@ -966,7 +965,7 @@ def _restrict_gain_matrix(G, info):
                 G = G[sel]
                 logger.info('    %d EEG channels' % len(sel))
             else:
-                logger.warning('Could not find MEG or EEG channels')
+                warn('Could not find MEG or EEG channels')
     return G
 
 
@@ -1081,16 +1080,16 @@ def _apply_forward(fwd, stc, start=None, stop=None, verbose=None):
                          'supported.')
 
     if np.all(stc.data > 0):
-        warnings.warn('Source estimate only contains currents with positive '
-                      'values. Use pick_ori="normal" when computing the '
-                      'inverse to compute currents not current magnitudes.')
+        warn('Source estimate only contains currents with positive values. '
+             'Use pick_ori="normal" when computing the inverse to compute '
+             'currents not current magnitudes.')
 
     max_cur = np.max(np.abs(stc.data))
     if max_cur > 1e-7:  # 100 nAm threshold for warning
-        warnings.warn('The maximum current magnitude is %0.1f nAm, which is '
-                      'very large. Are you trying to apply the forward model '
-                      'to dSPM values? The result will only be correct if '
-                      'currents are used.' % (1e9 * max_cur))
+        warn('The maximum current magnitude is %0.1f nAm, which is very large.'
+             ' Are you trying to apply the forward model to dSPM values? The '
+             'result will only be correct if currents are used.'
+             % (1e9 * max_cur))
 
     src_sel = _stc_src_sel(fwd['src'], stc)
     if isinstance(stc, VolSourceEstimate):
@@ -1134,7 +1133,7 @@ def apply_forward(fwd, stc, info, start=None, stop=None,
         Forward operator to use. Has to be fixed-orientation.
     stc : SourceEstimate
         The source estimate from which the sensor space data is computed.
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         Measurement info to generate the evoked.
     start : int, optional
         Index of first time sample (index not time is seconds).
@@ -1194,7 +1193,7 @@ def apply_forward_raw(fwd, stc, info, start=None, stop=None,
         Forward operator to use. Has to be fixed-orientation.
     stc : SourceEstimate
         The source estimate from which the sensor space data is computed.
-    info : Instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info.
     start : int, optional
         Index of first time sample (index not time is seconds).
@@ -1301,16 +1300,28 @@ def restrict_forward_to_label(fwd, labels):
     --------
     restrict_forward_to_stc
     """
+    message = 'labels must be instance of Label or a list of Label.'
+    vertices = [np.array([], int), np.array([], int)]
 
     if not isinstance(labels, list):
         labels = [labels]
 
+    # Get vertices separately of each hemisphere from all label
+    for label in labels:
+        if not isinstance(label, Label):
+            raise TypeError(message + ' Instead received %s' % type(label))
+        i = 0 if label.hemi == 'lh' else 1
+        vertices[i] = np.append(vertices[i], label.vertices)
+    # Remove duplicates and sort
+    vertices = [np.unique(vert_hemi) for vert_hemi in vertices]
+
     fwd_out = deepcopy(fwd)
     fwd_out['source_rr'] = np.zeros((0, 3))
     fwd_out['nsource'] = 0
     fwd_out['source_nn'] = np.zeros((0, 3))
     fwd_out['sol']['data'] = np.zeros((fwd['sol']['data'].shape[0], 0))
     fwd_out['sol']['ncol'] = 0
+    nuse_lh = fwd['src'][0]['nuse']
 
     for i in range(2):
         fwd_out['src'][i]['vertno'] = np.array([], int)
@@ -1320,26 +1331,22 @@ def restrict_forward_to_label(fwd, labels):
         fwd_out['src'][i]['use_tris'] = np.array([], int)
         fwd_out['src'][i]['nuse_tri'] = np.array([0])
 
-    for label in labels:
-        if label.hemi == 'lh':
-            i = 0
-            src_sel = np.intersect1d(fwd['src'][0]['vertno'], label.vertices)
-            src_sel = np.searchsorted(fwd['src'][0]['vertno'], src_sel)
-        else:
-            i = 1
-            src_sel = np.intersect1d(fwd['src'][1]['vertno'], label.vertices)
-            src_sel = (np.searchsorted(fwd['src'][1]['vertno'], src_sel) +
-                       len(fwd['src'][0]['vertno']))
+        # src_sel is idx to cols in fwd that are in any label per hemi
+        src_sel = np.intersect1d(fwd['src'][i]['vertno'], vertices[i])
+        src_sel = np.searchsorted(fwd['src'][i]['vertno'], src_sel)
+
+        # Reconstruct each src
+        vertno = fwd['src'][i]['vertno'][src_sel]
+        fwd_out['src'][i]['inuse'][vertno] = 1
+        fwd_out['src'][i]['nuse'] += len(vertno)
+        fwd_out['src'][i]['vertno'] = np.where(fwd_out['src'][i]['inuse'])[0]
 
+        # Reconstruct part of fwd that is not sol data
+        src_sel += i * nuse_lh  # Add column shift to right hemi
         fwd_out['source_rr'] = np.vstack([fwd_out['source_rr'],
                                           fwd['source_rr'][src_sel]])
         fwd_out['nsource'] += len(src_sel)
 
-        fwd_out['src'][i]['vertno'] = np.r_[fwd_out['src'][i]['vertno'],
-                                            src_sel]
-        fwd_out['src'][i]['nuse'] += len(src_sel)
-        fwd_out['src'][i]['inuse'][src_sel] = 1
-
         if is_fixed_orient(fwd):
             idx = src_sel
         else:
@@ -1354,6 +1361,8 @@ def restrict_forward_to_label(fwd, labels):
     return fwd_out
 
 
+ at deprecated('do_forward_solution is deprecated and will be removed in 0.13, '
+            'use make_forward_solution instead')
 @verbose
 def do_forward_solution(subject, meas, fname=None, src=None, spacing=None,
                         mindist=None, bem=None, mri=None, trans=None,
@@ -1426,6 +1435,85 @@ def do_forward_solution(subject, meas, fname=None, src=None, spacing=None,
     fwd : dict
         The generated forward solution.
     """
+    return _do_forward_solution(subject, meas, fname, src, spacing,
+                                mindist, bem, mri, trans, eeg, meg, fixed,
+                                grad, mricoord, overwrite, subjects_dir,
+                                verbose)
+
+
+def _do_forward_solution(subject, meas, fname=None, src=None, spacing=None,
+                         mindist=None, bem=None, mri=None, trans=None,
+                         eeg=True, meg=True, fixed=False, grad=False,
+                         mricoord=False, overwrite=False, subjects_dir=None,
+                         verbose=None):
+    """Calculate a forward solution for a subject using MNE-C routines
+
+    This is kept around for testing purposes.
+
+    This function wraps to mne_do_forward_solution, so the mne
+    command-line tools must be installed and accessible from Python.
+
+    Parameters
+    ----------
+    subject : str
+        Name of the subject.
+    meas : Raw | Epochs | Evoked | str
+        If Raw or Epochs, a temporary evoked file will be created and
+        saved to a temporary directory. If str, then it should be a
+        filename to a file with measurement information the mne
+        command-line tools can understand (i.e., raw or evoked).
+    fname : str | None
+        Destination forward solution filename. If None, the solution
+        will be created in a temporary directory, loaded, and deleted.
+    src : str | None
+        Source space name. If None, the MNE default is used.
+    spacing : str
+        The spacing to use. Can be ``'#'`` for spacing in mm, ``'ico#'`` for a
+        recursively subdivided icosahedron, or ``'oct#'`` for a recursively
+        subdivided octahedron (e.g., ``spacing='ico4'``). Default is 7 mm.
+    mindist : float | str | None
+        Minimum distance of sources from inner skull surface (in mm).
+        If None, the MNE default value is used. If string, 'all'
+        indicates to include all points.
+    bem : str | None
+        Name of the BEM to use (e.g., "sample-5120-5120-5120"). If None
+        (Default), the MNE default will be used.
+    mri : str | None
+        The name of the trans file in FIF format.
+        If None, trans must not be None.
+    trans : dict | str | None
+        File name of the trans file in text format.
+        If None, mri must not be None.
+    eeg : bool
+        If True (Default), include EEG computations.
+    meg : bool
+        If True (Default), include MEG computations.
+    fixed : bool
+        If True, make a fixed-orientation forward solution (Default:
+        False). Note that fixed-orientation inverses can still be
+        created from free-orientation forward solutions.
+    grad : bool
+        If True, compute the gradient of the field with respect to the
+        dipole coordinates as well (Default: False).
+    mricoord : bool
+        If True, calculate in MRI coordinates (Default: False).
+    overwrite : bool
+        If True, the destination file (if it exists) will be overwritten.
+        If False (default), an error will be raised if the file exists.
+    subjects_dir : None | str
+        Override the SUBJECTS_DIR environment variable.
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    See Also
+    --------
+    forward.make_forward_solution
+
+    Returns
+    -------
+    fwd : dict
+        The generated forward solution.
+    """
     if not has_mne_c():
         raise RuntimeError('mne command line tools could not be found')
 
@@ -1439,25 +1527,16 @@ def do_forward_solution(subject, meas, fname=None, src=None, spacing=None,
         raise ValueError('subject must be a string')
 
     # check for meas to exist as string, or try to make evoked
-    meas_data = None
     if isinstance(meas, string_types):
         if not op.isfile(meas):
             raise IOError('measurement file "%s" could not be found' % meas)
-    elif isinstance(meas, _BaseRaw):
-        events = np.array([[0, 0, 1]], dtype=np.int)
-        end = 1. / meas.info['sfreq']
-        meas_data = Epochs(meas, events, 1, 0, end, proj=False).average()
-    elif isinstance(meas, _BaseEpochs):
-        meas_data = meas.average()
-    elif isinstance(meas, Evoked):
-        meas_data = meas
+    elif isinstance(meas, (_BaseRaw, _BaseEpochs, Evoked)):
+        meas_file = op.join(temp_dir, 'info.fif')
+        write_info(meas_file, meas.info)
+        meas = meas_file
     else:
         raise ValueError('meas must be string, Raw, Epochs, or Evoked')
 
-    if meas_data is not None:
-        meas = op.join(temp_dir, 'evoked.fif')
-        write_evokeds(meas, meas_data)
-
     # deal with trans/mri
     if mri is not None and trans is not None:
         raise ValueError('trans and mri cannot both be specified')
diff --git a/mne/forward/tests/test_field_interpolation.py b/mne/forward/tests/test_field_interpolation.py
index 724041c..a8160cd 100644
--- a/mne/forward/tests/test_field_interpolation.py
+++ b/mne/forward/tests/test_field_interpolation.py
@@ -1,8 +1,9 @@
-import numpy as np
 from os import path as op
+
+import numpy as np
 from numpy.polynomial import legendre
-from numpy.testing.utils import (assert_allclose, assert_array_equal,
-                                 assert_array_almost_equal)
+from numpy.testing import (assert_allclose, assert_array_equal, assert_equal,
+                           assert_array_almost_equal)
 from nose.tools import assert_raises, assert_true
 
 from mne.forward import _make_surface_mapping, make_field_map
@@ -15,7 +16,7 @@ from mne.forward._make_forward import _create_meg_coils
 from mne.forward._field_interpolation import _setup_dots
 from mne.surface import get_meg_helmet_surf, get_head_surf
 from mne.datasets import testing
-from mne import read_evokeds
+from mne import read_evokeds, pick_types
 from mne.fixes import partial
 from mne.externals.six.moves import zip
 from mne.utils import run_tests_if_main, slow_test
@@ -136,7 +137,7 @@ def test_make_field_map_meg():
     assert_raises(ValueError, _make_surface_mapping, info, surf, 'meg',
                   mode='foo')
     # no picks
-    evoked_eeg = evoked.pick_types(meg=False, eeg=True, copy=True)
+    evoked_eeg = evoked.copy().pick_types(meg=False, eeg=True)
     assert_raises(RuntimeError, _make_surface_mapping, evoked_eeg.info,
                   surf, 'meg')
     # bad surface def
@@ -151,6 +152,7 @@ def test_make_field_map_meg():
 
     # now do it with make_field_map
     evoked.pick_types(meg=True, eeg=False)
+    evoked.info.normalize_proj()  # avoid projection warnings
     fmd = make_field_map(evoked, None,
                          subject='sample', subjects_dir=subjects_dir)
     assert_true(len(fmd) == 1)
@@ -161,6 +163,7 @@ def test_make_field_map_meg():
 
     # now test the make_field_map on head surf for MEG
     evoked.pick_types(meg=True, eeg=False)
+    evoked.info.normalize_proj()
     fmd = make_field_map(evoked, trans_fname, meg_surf='head',
                          subject='sample', subjects_dir=subjects_dir)
     assert_true(len(fmd) == 1)
@@ -171,6 +174,29 @@ def test_make_field_map_meg():
                   subjects_dir=subjects_dir, trans=trans_fname)
 
 
+ at testing.requires_testing_data
+def test_make_field_map_meeg():
+    """Test making a M/EEG field map onto helmet & head"""
+    evoked = read_evokeds(evoked_fname, baseline=(-0.2, 0.0))[0]
+    picks = pick_types(evoked.info, meg=True, eeg=True)
+    picks = picks[::10]
+    evoked.pick_channels([evoked.ch_names[p] for p in picks])
+    evoked.info.normalize_proj()
+    maps = make_field_map(evoked, trans_fname, subject='sample',
+                          subjects_dir=subjects_dir, n_jobs=1)
+    assert_equal(maps[0]['data'].shape, (642, 6))  # EEG->Head
+    assert_equal(maps[1]['data'].shape, (304, 31))  # MEG->Helmet
+    # reasonable ranges
+    for map_ in maps:
+        assert_true(0.5 < map_['data'].max() < 2)
+        assert_true(-2 < map_['data'].min() < -0.5)
+    # calculated from correct looking mapping on 2015/12/26
+    assert_allclose(np.sqrt(np.sum(maps[0]['data'] ** 2)), 16.6088,
+                    atol=1e-3, rtol=1e-3)
+    assert_allclose(np.sqrt(np.sum(maps[1]['data'] ** 2)), 20.1245,
+                    atol=1e-3, rtol=1e-3)
+
+
 def _setup_args(info):
     """Helper to test_as_meg_type_evoked."""
     coils = _create_meg_coils(info['chs'], 'normal', info['dev_head_t'])
@@ -193,13 +219,14 @@ def test_as_meg_type_evoked():
 
     # channel names
     ch_names = evoked.info['ch_names']
-    virt_evoked = evoked.pick_channels(ch_names=ch_names[:10:1],
-                                       copy=True).as_type('mag')
+    virt_evoked = evoked.copy().pick_channels(ch_names=ch_names[:10:1])
+    virt_evoked.info.normalize_proj()
+    virt_evoked = virt_evoked.as_type('mag')
     assert_true(all('_virtual' in ch for ch in virt_evoked.info['ch_names']))
 
     # pick from and to channels
-    evoked_from = evoked.pick_channels(ch_names=ch_names[2:10:3], copy=True)
-    evoked_to = evoked.pick_channels(ch_names=ch_names[0:10:3], copy=True)
+    evoked_from = evoked.copy().pick_channels(ch_names=ch_names[2:10:3])
+    evoked_to = evoked.copy().pick_channels(ch_names=ch_names[0:10:3])
 
     info_from, info_to = evoked_from.info, evoked_to.info
 
diff --git a/mne/forward/tests/test_forward.py b/mne/forward/tests/test_forward.py
index eee1cdb..2e4b770 100644
--- a/mne/forward/tests/test_forward.py
+++ b/mne/forward/tests/test_forward.py
@@ -11,8 +11,9 @@ from numpy.testing import (assert_array_almost_equal, assert_equal,
 from mne.datasets import testing
 from mne import (read_forward_solution, apply_forward, apply_forward_raw,
                  average_forward_solutions, write_forward_solution,
-                 convert_forward_solution)
-from mne import SourceEstimate, pick_types_forward, read_evokeds
+                 convert_forward_solution, SourceEstimate, pick_types_forward,
+                 read_evokeds)
+from mne.tests.common import assert_naming
 from mne.label import read_label
 from mne.utils import (requires_mne, run_subprocess, _TempDir,
                        run_tests_if_main, slow_test)
@@ -133,7 +134,7 @@ def test_io_forward():
         fwd_badname = op.join(temp_dir, 'test-bad-name.fif.gz')
         write_forward_solution(fwd_badname, fwd)
         read_forward_solution(fwd_badname)
-    assert_true(len(w) == 2)
+    assert_naming(w, 'test_forward.py', 2)
 
     fwd = read_forward_solution(fname_meeg)
     write_forward_solution(fname_temp, fwd, overwrite=True)
@@ -248,16 +249,19 @@ def test_restrict_forward_to_label():
 
     src_sel_lh = np.intersect1d(fwd['src'][0]['vertno'], label_lh.vertices)
     src_sel_lh = np.searchsorted(fwd['src'][0]['vertno'], src_sel_lh)
+    vertno_lh = fwd['src'][0]['vertno'][src_sel_lh]
 
+    nuse_lh = fwd['src'][0]['nuse']
     src_sel_rh = np.intersect1d(fwd['src'][1]['vertno'], label_rh.vertices)
-    src_sel_rh = (np.searchsorted(fwd['src'][1]['vertno'], src_sel_rh) +
-                  len(fwd['src'][0]['vertno']))
+    src_sel_rh = np.searchsorted(fwd['src'][1]['vertno'], src_sel_rh)
+    vertno_rh = fwd['src'][1]['vertno'][src_sel_rh]
+    src_sel_rh += nuse_lh
 
     assert_equal(fwd_out['sol']['ncol'], len(src_sel_lh) + len(src_sel_rh))
     assert_equal(fwd_out['src'][0]['nuse'], len(src_sel_lh))
     assert_equal(fwd_out['src'][1]['nuse'], len(src_sel_rh))
-    assert_equal(fwd_out['src'][0]['vertno'], src_sel_lh)
-    assert_equal(fwd_out['src'][1]['vertno'], src_sel_rh)
+    assert_equal(fwd_out['src'][0]['vertno'], vertno_lh)
+    assert_equal(fwd_out['src'][1]['vertno'], vertno_rh)
 
     fwd = read_forward_solution(fname_meeg, force_fixed=False)
     fwd = pick_types_forward(fwd, meg=True)
@@ -271,17 +275,20 @@ def test_restrict_forward_to_label():
 
     src_sel_lh = np.intersect1d(fwd['src'][0]['vertno'], label_lh.vertices)
     src_sel_lh = np.searchsorted(fwd['src'][0]['vertno'], src_sel_lh)
+    vertno_lh = fwd['src'][0]['vertno'][src_sel_lh]
 
+    nuse_lh = fwd['src'][0]['nuse']
     src_sel_rh = np.intersect1d(fwd['src'][1]['vertno'], label_rh.vertices)
-    src_sel_rh = (np.searchsorted(fwd['src'][1]['vertno'], src_sel_rh) +
-                  len(fwd['src'][0]['vertno']))
+    src_sel_rh = np.searchsorted(fwd['src'][1]['vertno'], src_sel_rh)
+    vertno_rh = fwd['src'][1]['vertno'][src_sel_rh]
+    src_sel_rh += nuse_lh
 
     assert_equal(fwd_out['sol']['ncol'],
                  3 * (len(src_sel_lh) + len(src_sel_rh)))
     assert_equal(fwd_out['src'][0]['nuse'], len(src_sel_lh))
     assert_equal(fwd_out['src'][1]['nuse'], len(src_sel_rh))
-    assert_equal(fwd_out['src'][0]['vertno'], src_sel_lh)
-    assert_equal(fwd_out['src'][1]['vertno'], src_sel_rh)
+    assert_equal(fwd_out['src'][0]['vertno'], vertno_lh)
+    assert_equal(fwd_out['src'][1]['vertno'], vertno_rh)
 
 
 @testing.requires_testing_data
diff --git a/mne/forward/tests/test_make_forward.py b/mne/forward/tests/test_make_forward.py
index 4c1ca1d..1389db1 100644
--- a/mne/forward/tests/test_make_forward.py
+++ b/mne/forward/tests/test_make_forward.py
@@ -1,8 +1,8 @@
 from __future__ import print_function
 
+from itertools import product
 import os
 import os.path as op
-from subprocess import CalledProcessError
 import warnings
 
 from nose.tools import assert_raises, assert_true
@@ -13,15 +13,18 @@ from mne.datasets import testing
 from mne.io import Raw, read_raw_kit, read_raw_bti, read_info
 from mne.io.constants import FIFF
 from mne import (read_forward_solution, make_forward_solution,
-                 do_forward_solution, read_trans,
                  convert_forward_solution, setup_volume_source_space,
                  read_source_spaces, make_sphere_model,
-                 pick_types_forward, pick_info, pick_types, Transform)
+                 pick_types_forward, pick_info, pick_types, Transform,
+                 read_evokeds, read_cov, read_dipole)
 from mne.utils import (requires_mne, requires_nibabel, _TempDir,
                        run_tests_if_main, slow_test, run_subprocess)
-from mne.forward._make_forward import _create_meg_coils
+from mne.forward._make_forward import _create_meg_coils, make_forward_dipole
 from mne.forward._compute_forward import _magnetic_dipole_field_vec
-from mne.forward import Forward
+from mne.forward import Forward, _do_forward_solution
+from mne.dipole import Dipole, fit_dipole
+from mne.simulation import simulate_evoked
+from mne.source_estimate import VolSourceEstimate
 from mne.source_space import (get_volume_labels_from_aseg,
                               _compare_source_spaces, setup_source_space)
 
@@ -30,8 +33,9 @@ fname_meeg = op.join(data_path, 'MEG', 'sample',
                      'sample_audvis_trunc-meg-eeg-oct-4-fwd.fif')
 fname_raw = op.join(op.dirname(__file__), '..', '..', 'io', 'tests', 'data',
                     'test_raw.fif')
-fname_evoked = op.join(op.dirname(__file__), '..', '..', 'io', 'tests',
-                       'data', 'test-ave.fif')
+fname_evo = op.join(data_path, 'MEG', 'sample', 'sample_audvis_trunc-ave.fif')
+fname_cov = op.join(data_path, 'MEG', 'sample', 'sample_audvis_trunc-cov.fif')
+fname_dip = op.join(data_path, 'MEG', 'sample', 'sample_audvis_trunc_set1.dip')
 fname_trans = op.join(data_path, 'MEG', 'sample',
                       'sample_audvis_trunc-trans.fif')
 subjects_dir = os.path.join(data_path, 'subjects')
@@ -50,32 +54,41 @@ def _compare_forwards(fwd, fwd_py, n_sensors, n_src,
     # check source spaces
     assert_equal(len(fwd['src']), len(fwd_py['src']))
     _compare_source_spaces(fwd['src'], fwd_py['src'], mode='approx')
-    for surf_ori in [False, True]:
-        if surf_ori:
-            # use copy here to leave our originals unmodified
-            fwd = convert_forward_solution(fwd, surf_ori, copy=True)
-            fwd_py = convert_forward_solution(fwd, surf_ori, copy=True)
-
-        for key in ['nchan', 'source_nn', 'source_rr', 'source_ori',
-                    'surf_ori', 'coord_frame', 'nsource']:
-            print(key)
-            assert_allclose(fwd_py[key], fwd[key], rtol=1e-4, atol=1e-7)
+    for surf_ori, force_fixed in product([False, True], [False, True]):
+        # use copy here to leave our originals unmodified
+        fwd = convert_forward_solution(fwd, surf_ori, force_fixed,
+                                       copy=True)
+        fwd_py = convert_forward_solution(fwd_py, surf_ori, force_fixed,
+                                          copy=True)
+        check_src = n_src // 3 if force_fixed else n_src
+
+        for key in ('nchan', 'source_rr', 'source_ori',
+                    'surf_ori', 'coord_frame', 'nsource'):
+            assert_allclose(fwd_py[key], fwd[key], rtol=1e-4, atol=1e-7,
+                            err_msg=key)
+        # In surf_ori=True only Z matters for source_nn
+        if surf_ori and not force_fixed:
+            ori_sl = slice(2, None, 3)
+        else:
+            ori_sl = slice(None)
+        assert_allclose(fwd_py['source_nn'][ori_sl], fwd['source_nn'][ori_sl],
+                        rtol=1e-4, atol=1e-6)
         assert_allclose(fwd_py['mri_head_t']['trans'],
                         fwd['mri_head_t']['trans'], rtol=1e-5, atol=1e-8)
 
-        assert_equal(fwd_py['sol']['data'].shape, (n_sensors, n_src))
+        assert_equal(fwd_py['sol']['data'].shape, (n_sensors, check_src))
         assert_equal(len(fwd['sol']['row_names']), n_sensors)
         assert_equal(len(fwd_py['sol']['row_names']), n_sensors)
 
         # check MEG
-        assert_allclose(fwd['sol']['data'][:306],
-                        fwd_py['sol']['data'][:306],
+        assert_allclose(fwd['sol']['data'][:306, ori_sl],
+                        fwd_py['sol']['data'][:306, ori_sl],
                         rtol=meg_rtol, atol=meg_atol,
                         err_msg='MEG mismatch')
         # check EEG
         if fwd['sol']['data'].shape[0] > 306:
-            assert_allclose(fwd['sol']['data'][306:],
-                            fwd_py['sol']['data'][306:],
+            assert_allclose(fwd['sol']['data'][306:, ori_sl],
+                            fwd_py['sol']['data'][306:, ori_sl],
                             rtol=eeg_rtol, atol=eeg_atol,
                             err_msg='EEG mismatch')
 
@@ -130,9 +143,9 @@ def test_make_forward_solution_kit():
     n_src = 108  # this is the resulting # of verts in fwd
 
     # first use mne-C: convert file, make forward solution
-    fwd = do_forward_solution('sample', fname_kit_raw, src=fname_src_small,
-                              bem=fname_bem_meg, mri=trans_path,
-                              eeg=False, meg=True, subjects_dir=subjects_dir)
+    fwd = _do_forward_solution('sample', fname_kit_raw, src=fname_src_small,
+                               bem=fname_bem_meg, mri=trans_path,
+                               eeg=False, meg=True, subjects_dir=subjects_dir)
     assert_true(isinstance(fwd, Forward))
 
     # now let's use python with the same raw file
@@ -158,10 +171,11 @@ def test_make_forward_solution_kit():
                       meg_rtol=1e-3, meg_atol=1e-7)
 
     # BTI python end-to-end versus C
-    fwd = do_forward_solution('sample', fname_bti_raw, src=fname_src_small,
-                              bem=fname_bem_meg, mri=trans_path,
-                              eeg=False, meg=True, subjects_dir=subjects_dir)
-    raw_py = read_raw_bti(bti_pdf, bti_config, bti_hs, preload=False)
+    fwd = _do_forward_solution('sample', fname_bti_raw, src=fname_src_small,
+                               bem=fname_bem_meg, mri=trans_path,
+                               eeg=False, meg=True, subjects_dir=subjects_dir)
+    with warnings.catch_warnings(record=True):  # weight tables
+        raw_py = read_raw_bti(bti_pdf, bti_config, bti_hs, preload=False)
     fwd_py = make_forward_solution(raw_py.info, src=src, eeg=False, meg=True,
                                    bem=fname_bem_meg, trans=trans_path)
     _compare_forwards(fwd, fwd_py, 248, n_src)
@@ -170,9 +184,9 @@ def test_make_forward_solution_kit():
     fwd_py = make_forward_solution(fname_ctf_raw, fname_trans, src,
                                    fname_bem_meg, eeg=False, meg=True)
 
-    fwd = do_forward_solution('sample', fname_ctf_raw, mri=fname_trans,
-                              src=fname_src_small, bem=fname_bem_meg,
-                              eeg=False, meg=True, subjects_dir=subjects_dir)
+    fwd = _do_forward_solution('sample', fname_ctf_raw, mri=fname_trans,
+                               src=fname_src_small, bem=fname_bem_meg,
+                               eeg=False, meg=True, subjects_dir=subjects_dir)
     _compare_forwards(fwd, fwd_py, 274, n_src)
 
     # CTF with compensation changed in python
@@ -181,10 +195,10 @@ def test_make_forward_solution_kit():
     fwd_py = make_forward_solution(ctf_raw.info, fname_trans, src,
                                    fname_bem_meg, eeg=False, meg=True)
     with warnings.catch_warnings(record=True):
-        fwd = do_forward_solution('sample', ctf_raw, mri=fname_trans,
-                                  src=fname_src_small, bem=fname_bem_meg,
-                                  eeg=False, meg=True,
-                                  subjects_dir=subjects_dir)
+        fwd = _do_forward_solution('sample', ctf_raw, mri=fname_trans,
+                                   src=fname_src_small, bem=fname_bem_meg,
+                                   eeg=False, meg=True,
+                                   subjects_dir=subjects_dir)
     _compare_forwards(fwd, fwd_py, 274, n_src)
 
 
@@ -229,78 +243,6 @@ def test_make_forward_solution_sphere():
                         1.0, rtol=1e-3)
 
 
- at testing.requires_testing_data
- at requires_mne
-def test_do_forward_solution():
-    """Test wrapping forward solution from python
-    """
-    temp_dir = _TempDir()
-    existing_file = op.join(temp_dir, 'test.fif')
-    with open(existing_file, 'w') as fid:
-        fid.write('aoeu')
-
-    mri = read_trans(fname_trans)
-    fname_fake = op.join(temp_dir, 'no_have.fif')
-
-    # ## Error checks
-    # bad subject
-    assert_raises(ValueError, do_forward_solution, 1, fname_raw,
-                  subjects_dir=subjects_dir)
-    # bad meas
-    assert_raises(ValueError, do_forward_solution, 'sample', 1,
-                  subjects_dir=subjects_dir)
-    # meas doesn't exist
-    assert_raises(IOError, do_forward_solution, 'sample', fname_fake,
-                  subjects_dir=subjects_dir)
-    # don't specify trans and meas
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  subjects_dir=subjects_dir)
-    # specify both trans and meas
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  trans='me', mri='you', subjects_dir=subjects_dir)
-    # specify non-existent trans
-    assert_raises(IOError, do_forward_solution, 'sample', fname_raw,
-                  trans=fname_fake, subjects_dir=subjects_dir)
-    # specify non-existent mri
-    assert_raises(IOError, do_forward_solution, 'sample', fname_raw,
-                  mri=fname_fake, subjects_dir=subjects_dir)
-    # specify non-string mri
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  mri=1, subjects_dir=subjects_dir)
-    # specify non-string trans
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  trans=1, subjects_dir=subjects_dir)
-    # test specifying an actual trans in python space -- this should work but
-    # the transform I/O reduces our accuracy -- so we'll just hack a test here
-    # by making it bomb with eeg=False and meg=False
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  mri=mri, eeg=False, meg=False, subjects_dir=subjects_dir)
-    # mindist as non-integer
-    assert_raises(TypeError, do_forward_solution, 'sample', fname_raw,
-                  mri=fname_trans, mindist=dict(), subjects_dir=subjects_dir)
-    # mindist as string but not 'all'
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  mri=fname_trans, eeg=False, mindist='yall',
-                  subjects_dir=subjects_dir)
-    # src, spacing, and bem as non-str
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  mri=fname_trans, src=1, subjects_dir=subjects_dir)
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  mri=fname_trans, spacing=1, subjects_dir=subjects_dir)
-    assert_raises(ValueError, do_forward_solution, 'sample', fname_raw,
-                  mri=fname_trans, bem=1, subjects_dir=subjects_dir)
-    # no overwrite flag
-    assert_raises(IOError, do_forward_solution, 'sample', fname_raw,
-                  existing_file, mri=fname_trans, subjects_dir=subjects_dir)
-    # let's catch an MNE error, this time about trans being wrong
-    assert_raises(CalledProcessError, do_forward_solution, 'sample',
-                  fname_raw, existing_file, trans=fname_trans, overwrite=True,
-                  spacing='oct6', subjects_dir=subjects_dir)
-
-    # No need to actually calculate and check here, since it's effectively
-    # done in previous tests.
-
-
 @slow_test
 @testing.requires_testing_data
 @requires_nibabel(False)
@@ -353,4 +295,106 @@ def test_forward_mixed_source_space():
                   mri_resolution=True, trans=vox_mri_t)
 
 
+ at slow_test
+ at testing.requires_testing_data
+def test_make_forward_dipole():
+    """Test forward-projecting dipoles"""
+    rng = np.random.RandomState(0)
+
+    evoked = read_evokeds(fname_evo)[0]
+    cov = read_cov(fname_cov)
+    dip_c = read_dipole(fname_dip)
+
+    # Only use magnetometers for speed!
+    picks = pick_types(evoked.info, meg='mag', eeg=False)
+    evoked.pick_channels([evoked.ch_names[p] for p in picks])
+    info = evoked.info
+
+    # Make new Dipole object with n_test_dipoles picked from the dipoles
+    # in the test dataset.
+    n_test_dipoles = 3  # minimum 3 needed to get uneven sampling in time
+    dipsel = np.sort(rng.permutation(np.arange(len(dip_c)))[:n_test_dipoles])
+    dip_test = Dipole(times=dip_c.times[dipsel],
+                      pos=dip_c.pos[dipsel],
+                      amplitude=dip_c.amplitude[dipsel],
+                      ori=dip_c.ori[dipsel],
+                      gof=dip_c.gof[dipsel])
+
+    sphere = make_sphere_model(head_radius=0.1)
+
+    # Warning emitted due to uneven sampling in time
+    with warnings.catch_warnings(record=True) as w:
+        fwd, stc = make_forward_dipole(dip_test, sphere, info,
+                                       trans=fname_trans)
+        assert_true(issubclass(w[-1].category, RuntimeWarning))
+
+    # stc is list of VolSourceEstimate's
+    assert_true(isinstance(stc, list))
+    for nd in range(n_test_dipoles):
+        assert_true(isinstance(stc[nd], VolSourceEstimate))
+
+    # Now simulate evoked responses for each of the test dipoles,
+    # and fit dipoles to them (sphere model, MEG and EEG)
+    times, pos, amplitude, ori, gof = [], [], [], [], []
+    snr = 20.  # add a tiny amount of noise to the simulated evokeds
+    for s in stc:
+        evo_test = simulate_evoked(fwd, s, info, cov,
+                                   snr=snr, random_state=rng)
+        # evo_test.add_proj(make_eeg_average_ref_proj(evo_test.info))
+        dfit, resid = fit_dipole(evo_test, cov, sphere, None)
+        times += dfit.times.tolist()
+        pos += dfit.pos.tolist()
+        amplitude += dfit.amplitude.tolist()
+        ori += dfit.ori.tolist()
+        gof += dfit.gof.tolist()
+
+    # Create a new Dipole object with the dipole fits
+    dip_fit = Dipole(times, pos, amplitude, ori, gof)
+
+    # check that true (test) dipoles and fits are "close"
+    # cf. mne/tests/test_dipole.py
+    diff = dip_test.pos - dip_fit.pos
+    corr = np.corrcoef(dip_test.pos.ravel(), dip_fit.pos.ravel())[0, 1]
+    dist = np.sqrt(np.mean(np.sum(diff * diff, axis=1)))
+    gc_dist = 180 / np.pi * \
+        np.mean(np.arccos(np.sum(dip_test.ori * dip_fit.ori, axis=1)))
+    amp_err = np.sqrt(np.mean((dip_test.amplitude - dip_fit.amplitude) ** 2))
+
+    # Make sure each coordinate is close to reference
+    # NB tolerance should be set relative to snr of simulated evoked!
+    assert_allclose(dip_fit.pos, dip_test.pos, rtol=0, atol=1e-2,
+                    err_msg='position mismatch')
+    assert_true(dist < 1e-2, 'dist: %s' % dist)  # within 1 cm
+    assert_true(corr > 1 - 1e-2, 'corr: %s' % corr)
+    assert_true(gc_dist < 20, 'gc_dist: %s' % gc_dist)  # less than 20 degrees
+    assert_true(amp_err < 10e-9, 'amp_err: %s' % amp_err)  # within 10 nAm
+
+    # Make sure rejection works with BEM: one dipole at z=1m
+    # NB _make_forward.py:_prepare_for_forward will raise a RuntimeError
+    # if no points are left after min_dist exclusions, hence 2 dips here!
+    dip_outside = Dipole(times=[0., 0.001],
+                         pos=[[0., 0., 1.0], [0., 0., 0.040]],
+                         amplitude=[100e-9, 100e-9],
+                         ori=[[1., 0., 0.], [1., 0., 0.]], gof=1)
+    assert_raises(ValueError, make_forward_dipole, dip_outside, fname_bem,
+                  info, fname_trans)
+    # if we get this far, can safely assume the code works with BEMs too
+    # -> use sphere again below for speed
+
+    # Now make an evenly sampled set of dipoles, some simultaneous,
+    # should return a VolSourceEstimate regardless
+    times = [0., 0., 0., 0.001, 0.001, 0.002]
+    pos = np.random.rand(6, 3) * 0.020 + \
+        np.array([0., 0., 0.040])[np.newaxis, :]
+    amplitude = np.random.rand(6) * 100e-9
+    ori = np.eye(6, 3) + np.eye(6, 3, -3)
+    gof = np.arange(len(times)) / len(times)  # arbitrary
+
+    dip_even_samp = Dipole(times, pos, amplitude, ori, gof)
+
+    fwd, stc = make_forward_dipole(dip_even_samp, sphere, info,
+                                   trans=fname_trans)
+    assert_true(isinstance, VolSourceEstimate)
+    assert_allclose(stc.times, np.arange(0., 0.003, 0.001))
+
 run_tests_if_main()
diff --git a/mne/gui/__init__.py b/mne/gui/__init__.py
index 0286d02..acdd7b8 100644
--- a/mne/gui/__init__.py
+++ b/mne/gui/__init__.py
@@ -15,6 +15,8 @@ def combine_kit_markers():
     The functionality in this GUI is also part of :func:`kit2fiff`.
     """
     _check_mayavi_version()
+    from ._backend import _check_backend
+    _check_backend()
     from ._marker_gui import CombineMarkersFrame
     gui = CombineMarkersFrame()
     gui.configure_traits()
@@ -25,6 +27,11 @@ def coregistration(tabbed=False, split=True, scene_width=0o1, inst=None,
                    subject=None, subjects_dir=None):
     """Coregister an MRI with a subject's head shape
 
+    The recommended way to use the GUI is through bash with::
+
+        $ mne coreg
+
+
     Parameters
     ----------
     tabbed : bool
@@ -46,7 +53,6 @@ def coregistration(tabbed=False, split=True, scene_width=0o1, inst=None,
 
     Notes
     -----
-    All parameters are optional, since they can be set through the GUI.
     Step by step instructions for the coregistrations can be accessed as
     slides, `for subjects with structural MRI
     <http://www.slideshare.net/mne-python/mnepython-coregistration>`_ and `for
@@ -54,6 +60,8 @@ def coregistration(tabbed=False, split=True, scene_width=0o1, inst=None,
     <http://www.slideshare.net/mne-python/mnepython-scale-mri>`_.
     """
     _check_mayavi_version()
+    from ._backend import _check_backend
+    _check_backend()
     from ._coreg_gui import CoregFrame, _make_view
     view = _make_view(tabbed, split, scene_width)
     gui = CoregFrame(inst, subject, subjects_dir)
@@ -80,6 +88,8 @@ def fiducials(subject=None, fid_file=None, subjects_dir=None):
     The functionality in this GUI is also part of :func:`coregistration`.
     """
     _check_mayavi_version()
+    from ._backend import _check_backend
+    _check_backend()
     from ._fiducials_gui import FiducialsFrame
     gui = FiducialsFrame(subject, subjects_dir, fid_file=fid_file)
     gui.configure_traits()
@@ -88,8 +98,15 @@ def fiducials(subject=None, fid_file=None, subjects_dir=None):
 
 def kit2fiff():
     """Convert KIT files to the fiff format
+
+    The recommended way to use the GUI is through bash with::
+
+        $ mne kit2fiff
+
     """
     _check_mayavi_version()
+    from ._backend import _check_backend
+    _check_backend()
     from ._kit2fiff_gui import Kit2FiffFrame
     gui = Kit2FiffFrame()
     gui.configure_traits()
diff --git a/mne/gui/_backend.py b/mne/gui/_backend.py
new file mode 100644
index 0000000..9bfc0b3
--- /dev/null
+++ b/mne/gui/_backend.py
@@ -0,0 +1,27 @@
+"""Deal with pyface backend issues"""
+# Author: Christian Brodbeck <christianbrodbeck at nyu.edu>
+#
+# License: BSD (3-clause)
+
+
+def _check_backend():
+    from ..utils import _check_pyface_backend
+    try:
+        from pyface.api import warning
+    except ImportError:
+        warning = None
+
+    backend, status = _check_pyface_backend()
+    if status == 0:
+        return
+    elif status == 1:
+        msg = ("The currently selected Pyface backend %s has not been "
+               "extensively tested. We recommend using qt4 which can be "
+               "enabled by installing the pyside package. If you proceed with "
+               "the current backend pease let the developers know your "
+               "experience." % backend)
+    elif status == 2:
+        msg = ("The currently selected Pyface backend %s has known issues. We "
+               "recommend using qt4 which can be enabled by installing the "
+               "pyside package." % backend)
+    warning(None, msg, "Pyface Backend Warning")
diff --git a/mne/inverse_sparse/_gamma_map.py b/mne/inverse_sparse/_gamma_map.py
index d615b50..8566ff3 100644
--- a/mne/inverse_sparse/_gamma_map.py
+++ b/mne/inverse_sparse/_gamma_map.py
@@ -9,7 +9,7 @@ from scipy import linalg
 from ..forward import is_fixed_orient, _to_fixed_ori
 
 from ..minimum_norm.inverse import _check_reference
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from ..externals.six.moves import xrange as range
 from .mxne_inverse import (_make_sparse_stc, _prepare_gain,
                            _reapply_source_weighting, _compute_residual)
@@ -107,7 +107,6 @@ def _gamma_map_opt(M, G, alpha, maxit=10000, tol=1e-6, update_mode=1,
         S = S[np.newaxis, :]
         CM = np.dot(U * S, U.T)
         CMinv = np.dot(U / (S + eps), U.T)
-
         CMinvG = np.dot(CMinv, G)
         A = np.dot(CMinvG.T, M)  # mult. w. Diag(gamma) in gamma update
 
@@ -156,9 +155,9 @@ def _gamma_map_opt(M, G, alpha, maxit=10000, tol=1e-6, update_mode=1,
             break
 
     if itno < maxit - 1:
-        print('\nConvergence reached !\n')
+        logger.info('\nConvergence reached !\n')
     else:
-        print('\nConvergence NOT reached !\n')
+        warn('\nConvergence NOT reached !\n')
 
     # undo normalization and compute final posterior mean
     n_const = np.sqrt(M_normalize_constant) / G_normalize_constant
diff --git a/mne/inverse_sparse/mxne_optim.py b/mne/inverse_sparse/mxne_optim.py
index c3f929e..ce54817 100644
--- a/mne/inverse_sparse/mxne_optim.py
+++ b/mne/inverse_sparse/mxne_optim.py
@@ -5,13 +5,13 @@ from __future__ import print_function
 # License: Simplified BSD
 
 from copy import deepcopy
-import warnings
 from math import sqrt, ceil
+
 import numpy as np
 from scipy import linalg
 
 from .mxne_debiasing import compute_bias
-from ..utils import logger, verbose, sum_squared
+from ..utils import logger, verbose, sum_squared, warn
 from ..time_frequency.stft import stft_norm2, stft, istft
 from ..externals.six.moves import xrange as range
 
@@ -392,14 +392,13 @@ def mixed_norm_solver(M, G, alpha, maxit=3000, tol=1e-8, verbose=None,
 
     if solver == 'cd':
         if n_orient == 1 and not has_sklearn:
-            warnings.warn("Scikit-learn >= 0.12 cannot be found. "
-                          "Using block coordinate descent instead of "
-                          "coordinate descent.")
+            warn('Scikit-learn >= 0.12 cannot be found. Using block coordinate'
+                 ' descent instead of coordinate descent.')
             solver = 'bcd'
         if n_orient > 1:
-            warnings.warn("Coordinate descent is only available for fixed "
-                          "orientation. Using block coordinate descent "
-                          "instead of coordinate descent")
+            warn('Coordinate descent is only available for fixed orientation. '
+                 'Using block coordinate descent instead of coordinate '
+                 'descent')
             solver = 'bcd'
 
     if solver == 'cd':
@@ -474,7 +473,7 @@ def mixed_norm_solver(M, G, alpha, maxit=3000, tol=1e-8, verbose=None,
                 idx = np.searchsorted(idx_active_set, idx_old_active_set)
                 X_init[idx] = X
         else:
-            logger.warning('Did NOT converge ! (gap: %s > %s)' % (gap, tol))
+            warn('Did NOT converge ! (gap: %s > %s)' % (gap, tol))
     else:
         X, active_set, E = l21_solver(M, G, alpha, lc, maxit=maxit,
                                       tol=tol, n_orient=n_orient, init=None)
diff --git a/mne/inverse_sparse/tests/test_gamma_map.py b/mne/inverse_sparse/tests/test_gamma_map.py
index 2a36d87..98f20d9 100644
--- a/mne/inverse_sparse/tests/test_gamma_map.py
+++ b/mne/inverse_sparse/tests/test_gamma_map.py
@@ -3,9 +3,10 @@
 # License: Simplified BSD
 
 import os.path as op
-import numpy as np
+
 from nose.tools import assert_true
-from numpy.testing import assert_array_almost_equal
+import numpy as np
+from numpy.testing import assert_array_almost_equal, assert_equal
 
 from mne.datasets import testing
 from mne import read_cov, read_forward_solution, read_evokeds
@@ -23,42 +24,46 @@ fname_fwd = op.join(data_path, 'MEG', 'sample',
 subjects_dir = op.join(data_path, 'subjects')
 
 
+def _check_stc(stc, evoked, idx, ratio=50.):
+    """Helper to check correctness"""
+    assert_array_almost_equal(stc.times, evoked.times, 5)
+    amps = np.sum(stc.data ** 2, axis=1)
+    order = np.argsort(amps)[::-1]
+    amps = amps[order]
+    verts = np.concatenate(stc.vertices)[order]
+    assert_equal(idx, verts[0], err_msg=str(list(verts)))
+    assert_true(amps[0] > ratio * amps[1], msg=str(amps[0] / amps[1]))
+
+
 @slow_test
 @testing.requires_testing_data
 def test_gamma_map():
     """Test Gamma MAP inverse"""
-
     forward = read_forward_solution(fname_fwd, force_fixed=False,
                                     surf_ori=True)
     forward = pick_types_forward(forward, meg=False, eeg=True)
-    evoked = read_evokeds(fname_evoked, condition=0, baseline=(None, 0))
-    evoked.resample(50)
-    evoked.crop(tmin=0, tmax=0.3)
+    evoked = read_evokeds(fname_evoked, condition=0, baseline=(None, 0),
+                          proj=False)
+    evoked.resample(50, npad=100)
+    evoked.crop(tmin=0.1, tmax=0.16)  # crop to nice window near samp border
 
     cov = read_cov(fname_cov)
     cov = regularize(cov, evoked.info)
 
-    alpha = 0.2
-    stc = gamma_map(evoked, forward, cov, alpha, tol=1e-5,
-                    xyz_same_gamma=True, update_mode=1, verbose=False)
-    assert_array_almost_equal(stc.times, evoked.times, 5)
-    idx = np.argmax(np.sum(stc.data ** 2, axis=1))
-    assert_true(np.concatenate(stc.vertices)[idx] == 96397)
+    alpha = 0.5
+    stc = gamma_map(evoked, forward, cov, alpha, tol=1e-4,
+                    xyz_same_gamma=True, update_mode=1)
+    _check_stc(stc, evoked, 68477)
 
-    stc = gamma_map(evoked, forward, cov, alpha, tol=1e-5,
-                    xyz_same_gamma=False, update_mode=1, verbose=False)
-    assert_array_almost_equal(stc.times, evoked.times, 5)
-    idx = np.argmax(np.sum(stc.data ** 2, axis=1))
-    assert_true(np.concatenate(stc.vertices)[idx] == 82010)
+    stc = gamma_map(evoked, forward, cov, alpha, tol=1e-4,
+                    xyz_same_gamma=False, update_mode=1)
+    _check_stc(stc, evoked, 82010)
 
     # force fixed orientation
-    stc, res = gamma_map(evoked, forward, cov, alpha, tol=1e-5,
-                         xyz_same_gamma=False, update_mode=2,
-                         loose=None, return_residual=True, verbose=False)
-    assert_array_almost_equal(stc.times, evoked.times, 5)
-    idx = np.argmax(np.sum(stc.data ** 2, axis=1))
-    # assert_true(np.concatenate(stc.vertices)[idx] == 83398)  # XXX FIX
-    assert_array_almost_equal(evoked.times, res.times)
+    stc = gamma_map(evoked, forward, cov, alpha, tol=1e-4,
+                    xyz_same_gamma=False, update_mode=2,
+                    loose=None, return_residual=False)
+    _check_stc(stc, evoked, 85739, 20)
 
 
 run_tests_if_main()
diff --git a/mne/inverse_sparse/tests/test_mxne_inverse.py b/mne/inverse_sparse/tests/test_mxne_inverse.py
index 9b0c134..29951d9 100644
--- a/mne/inverse_sparse/tests/test_mxne_inverse.py
+++ b/mne/inverse_sparse/tests/test_mxne_inverse.py
@@ -41,7 +41,7 @@ def test_mxne_inverse():
     evoked.crop(tmin=-0.05, tmax=0.2)
 
     evoked_l21 = evoked.copy()
-    evoked_l21.crop(tmin=0.08, tmax=0.1)
+    evoked_l21.crop(tmin=0.081, tmax=0.1)
     label = read_label(fname_label)
 
     forward = read_forward_solution(fname_fwd, force_fixed=False,
diff --git a/mne/io/__init__.py b/mne/io/__init__.py
index 565569b..734869e 100644
--- a/mne/io/__init__.py
+++ b/mne/io/__init__.py
@@ -7,7 +7,7 @@
 
 from .open import fiff_open, show_fiff, _fiff_get_fid
 from .meas_info import (read_fiducials, write_fiducials, read_info, write_info,
-                        _empty_info, _merge_info, Info)
+                        _empty_info, _merge_info, _force_update_info, Info)
 
 from .proj import make_eeg_average_ref_proj, Projection
 from .tag import _loc_to_coil_trans, _coil_trans_to_loc, _loc_to_eeg_loc
@@ -17,6 +17,7 @@ from . import array
 from . import base
 from . import brainvision
 from . import bti
+from . import cnt
 from . import ctf
 from . import constants
 from . import edf
@@ -30,6 +31,7 @@ from . import pick
 from .array import RawArray
 from .brainvision import read_raw_brainvision
 from .bti import read_raw_bti
+from .cnt import read_raw_cnt
 from .ctf import read_raw_ctf
 from .edf import read_raw_edf
 from .egi import read_raw_egi
@@ -39,8 +41,8 @@ from .nicolet import read_raw_nicolet
 from .eeglab import read_raw_eeglab, read_epochs_eeglab
 
 # for backward compatibility
-from .fiff import RawFIF
-from .fiff import RawFIF as Raw
+from .fiff import Raw
+from .fiff import Raw as RawFIF
 from .base import concatenate_raws
 from .reference import (set_eeg_reference, set_bipolar_reference,
                         add_reference_channels)
diff --git a/mne/io/array/array.py b/mne/io/array/array.py
index bc992ec..25834fb 100644
--- a/mne/io/array/array.py
+++ b/mne/io/array/array.py
@@ -7,6 +7,7 @@
 import numpy as np
 
 from ..base import _BaseRaw
+from ..meas_info import Info
 from ...utils import verbose, logger
 
 
@@ -20,6 +21,11 @@ class RawArray(_BaseRaw):
     info : instance of Info
         Info dictionary. Consider using `create_info` to populate
         this structure. This may be modified in place by the class.
+    first_samp : int
+        First sample offset used during recording (default 0).
+
+        .. versionadded:: 0.12
+
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -28,7 +34,10 @@ class RawArray(_BaseRaw):
     EpochsArray, EvokedArray, create_info
     """
     @verbose
-    def __init__(self, data, info, verbose=None):
+    def __init__(self, data, info, first_samp=0, verbose=None):
+        if not isinstance(info, Info):
+            raise TypeError('info must be an instance of Info, got %s'
+                            % type(info))
         dtype = np.complex128 if np.any(np.iscomplex(data)) else np.float64
         data = np.asanyarray(data, dtype=dtype)
 
@@ -44,7 +53,9 @@ class RawArray(_BaseRaw):
         assert len(info['ch_names']) == info['nchan']
         if info.get('buffer_size_sec', None) is None:
             info['buffer_size_sec'] = 1.  # reasonable default
-        super(RawArray, self).__init__(info, data, verbose=verbose)
+        super(RawArray, self).__init__(info, data,
+                                       first_samps=(int(first_samp),),
+                                       verbose=verbose)
         logger.info('    Range : %d ... %d =  %9.3f ... %9.3f secs' % (
                     self.first_samp, self.last_samp,
                     float(self.first_samp) / info['sfreq'],
diff --git a/mne/io/array/tests/test_array.py b/mne/io/array/tests/test_array.py
index d47e517..f49bf0d 100644
--- a/mne/io/array/tests/test_array.py
+++ b/mne/io/array/tests/test_array.py
@@ -15,7 +15,7 @@ from mne.io import Raw
 from mne.io.array import RawArray
 from mne.io.tests.test_raw import _test_raw_reader
 from mne.io.meas_info import create_info, _kind_dict
-from mne.utils import slow_test, requires_version
+from mne.utils import slow_test, requires_version, run_tests_if_main
 
 matplotlib.use('Agg')  # for testing don't use X server
 
@@ -55,11 +55,12 @@ def test_array_raw():
     # use real types
     info = create_info(ch_names, sfreq, types)
     raw2 = _test_raw_reader(RawArray, test_preloading=False,
-                            data=data, info=info)
+                            data=data, info=info, first_samp=2 * data.shape[1])
     data2, times2 = raw2[:, :]
     assert_allclose(data, data2)
     assert_allclose(times, times2)
     assert_true('RawArray' in repr(raw2))
+    assert_raises(TypeError, RawArray, info, data)
 
     # filtering
     picks = pick_types(raw2.info, misc=True, exclude='bads')[:4]
@@ -96,9 +97,10 @@ def test_array_raw():
     assert_true(len(events) > 2)
     epochs = Epochs(raw2, events, 1, -0.2, 0.4, preload=True)
     epochs.plot_drop_log()
-    with warnings.catch_warnings(record=True):  # deprecation
-        warnings.simplefilter('always')
-        epochs.plot()
+    epochs.plot()
     evoked = epochs.average()
     evoked.plot()
+    assert_equal(evoked.nave, len(events) - 1)
     plt.close('all')
+
+run_tests_if_main()
diff --git a/mne/io/base.py b/mne/io/base.py
index fa498ca..82e01c3 100644
--- a/mne/io/base.py
+++ b/mne/io/base.py
@@ -9,7 +9,6 @@
 
 import copy
 from copy import deepcopy
-import warnings
 import os
 import os.path as op
 
@@ -18,6 +17,7 @@ from scipy import linalg
 
 from .constants import FIFF
 from .pick import pick_types, channel_type, pick_channels, pick_info
+from .pick import _pick_data_channels, _pick_data_or_ica
 from .meas_info import write_meas_info
 from .proj import setup_proj, activate_proj, _proj_equal, ProjMixin
 from ..channels.channels import (ContainsMixin, UpdateChannelsMixin,
@@ -27,7 +27,7 @@ from .compensator import set_current_comp
 from .write import (start_file, end_file, start_block, end_block,
                     write_dau_pack16, write_float, write_double,
                     write_complex64, write_complex128, write_int,
-                    write_id, write_string, _get_split_size)
+                    write_id, write_string, write_name_list, _get_split_size)
 
 from ..filter import (low_pass_filter, high_pass_filter, band_pass_filter,
                       notch_filter, band_stop_filter, resample,
@@ -35,17 +35,18 @@ from ..filter import (low_pass_filter, high_pass_filter, band_pass_filter,
 from ..fixes import in1d
 from ..parallel import parallel_func
 from ..utils import (_check_fname, _check_pandas_installed,
-                     _check_pandas_index_arguments,
+                     _check_pandas_index_arguments, _check_copy_dep,
                      check_fname, _get_stim_channel, object_hash,
-                     logger, verbose, _time_mask)
+                     logger, verbose, _time_mask, warn, deprecated)
 from ..viz import plot_raw, plot_raw_psd, plot_raw_psd_topo
 from ..defaults import _handle_default
 from ..externals.six import string_types
 from ..event import find_events, concatenate_events
+from ..annotations import _combine_annotations, _onset_to_seconds
 
 
 class ToDataFrameMixin(object):
-    '''Class to add to_data_frame capabilities to certain classes.'''
+    """Class to add to_data_frame capabilities to certain classes."""
     def _get_check_picks(self, picks, picks_check):
         if picks is None:
             picks = list(range(self.info['nchan']))
@@ -201,6 +202,36 @@ class ToDataFrameMixin(object):
         return df
 
 
+class TimeMixin(object):
+    """Class to add sfreq and time_as_index capabilities to certain classes."""
+
+    def time_as_index(self, times, use_rounding=False):
+        """Convert time to indices
+
+        Parameters
+        ----------
+        times : list-like | float | int
+            List of numbers or a number representing points in time.
+        use_rounding : boolean
+            If True, use rounding (instead of truncation) when converting
+            times to indices. This can help avoid non-unique indices.
+
+        Returns
+        -------
+        index : ndarray
+            Indices corresponding to the times supplied.
+        """
+        from ..source_estimate import _BaseSourceEstimate
+        if isinstance(self, _BaseSourceEstimate):
+            sfreq = 1. / self.tstep
+        else:
+            sfreq = self.info['sfreq']
+        index = (np.atleast_1d(times) - self.times[0]) * sfreq
+        if use_rounding:
+            index = np.round(index)
+        return index.astype(int)
+
+
 def _check_fun(fun, d, *args, **kwargs):
     want_shape = d.shape
     d = fun(d, *args, **kwargs)
@@ -213,7 +244,8 @@ def _check_fun(fun, d, *args, **kwargs):
 
 
 class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
-               SetChannelsMixin, InterpolationMixin, ToDataFrameMixin):
+               SetChannelsMixin, InterpolationMixin, ToDataFrameMixin,
+               TimeMixin):
     """Base class for Raw data
 
     Subclasses must provide the following methods:
@@ -229,9 +261,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
     def __init__(self, info, preload=False,
                  first_samps=(0,), last_samps=None,
                  filenames=(None,), raw_extras=(None,),
-                 comp=None, orig_comp_grade=None,
-                 orig_format='double', dtype=np.float64,
-                 verbose=None):
+                 comp=None, orig_comp_grade=None, orig_format='double',
+                 dtype=np.float64, verbose=None):
         # wait until the end to preload data, but triage here
         if isinstance(preload, np.ndarray):
             # some functions (e.g., filtering) only work w/64-bit data
@@ -242,7 +273,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                 raise ValueError('preload and dtype must match')
             self._data = preload
             self.preload = True
-            last_samps = [self._data.shape[1] - 1]
+            assert len(first_samps) == 1
+            last_samps = [first_samps[0] + self._data.shape[1] - 1]
             load_from_disk = False
         else:
             if last_samps is None:
@@ -274,10 +306,11 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         self._projectors = list()
         self._projector = None
         self._dtype_ = dtype
+        self.annotations = None
         # If we have True or a string, actually do the preloading
+        self._update_times()
         if load_from_disk:
             self._preload_data(preload)
-        self._update_times()
 
     @property
     def _dtype(self):
@@ -312,8 +345,6 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         -------
         data : array, [channels x samples]
            the data matrix (channels x samples).
-        times : array, [samples]
-            returns the time values corresponding to the samples.
         """
         #  Initial checks
         start = int(start)
@@ -322,10 +353,6 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         if start >= stop:
             raise ValueError('No data in this range')
 
-        logger.info('Reading %d ... %d  =  %9.3f ... %9.3f secs...' %
-                    (start, stop - 1, start / float(self.info['sfreq']),
-                     (stop - 1) / float(self.info['sfreq'])))
-
         #  Initialize the data and calibration vector
         n_sel_channels = self.info['nchan'] if sel is None else len(sel)
         # convert sel to a slice if possible for efficiency
@@ -336,7 +363,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         dtype = self._dtype
         if isinstance(data_buffer, np.ndarray):
             if data_buffer.shape != data_shape:
-                raise ValueError('data_buffer has incorrect shape')
+                raise ValueError('data_buffer has incorrect shape: %s != %s'
+                                 % (data_buffer.shape, data_shape))
             data = data_buffer
         elif isinstance(data_buffer, string_types):
             # use a memmap
@@ -384,8 +412,6 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                                     int(start_file), int(stop_file),
                                     cals, mult)
             offset += n_read
-
-        logger.info('[done]')
         return data
 
     def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
@@ -413,6 +439,46 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         """
         raise NotImplementedError
 
+    def _check_bad_segment(self, start, stop, picks,
+                           reject_by_annotation=False):
+        """Function for checking if data segment is bad.
+
+        If the slice is good, returns the data in desired range.
+        If rejected based on annotation, returns description of the
+        bad segment as a string.
+
+        Parameters
+        ----------
+        start : int
+            First sample of the slice.
+        stop : int
+            End of the slice.
+        picks : array of int
+            Channel picks.
+        reject_by_annotation : bool
+            Whether to perform rejection based on annotations.
+            False by default.
+
+        Returns
+        -------
+        data : array | str
+            Data in the desired range (good segment) or description of the bad
+            segment.
+        """
+        if start < 0:
+            return None
+        if reject_by_annotation and self.annotations is not None:
+            annot = self.annotations
+            sfreq = self.info['sfreq']
+            onset = _onset_to_seconds(self, annot.onset)
+            overlaps = np.where(onset < stop / sfreq)
+            overlaps = np.where(onset[overlaps] + annot.duration[overlaps] >
+                                start / sfreq)
+            for descr in annot.description[overlaps]:
+                if descr.lower().startswith('bad'):
+                    return descr
+        return self[picks, start:stop][0]
+
     @verbose
     def load_data(self, verbose=None):
         """Load raw data
@@ -438,9 +504,13 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             self._preload_data(True)
         return self
 
-    def _preload_data(self, preload):
+    @verbose
+    def _preload_data(self, preload, verbose=None):
         """This function actually preloads the data"""
-        data_buffer = preload if isinstance(preload, string_types) else None
+        data_buffer = preload if isinstance(preload, (string_types,
+                                                      np.ndarray)) else None
+        logger.info('Reading %d ... %d  =  %9.3f ... %9.3f secs...' %
+                    (0, len(self.times) - 1, 0., self.times[-1]))
         self._data = self._read_segment(data_buffer=data_buffer)
         assert len(self._data) == self.info['nchan']
         self.preload = True
@@ -504,6 +574,10 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         if isinstance(item[0], slice):
             start = item[0].start if item[0].start is not None else 0
             nchan = self.info['nchan']
+            if start < 0:
+                start += nchan
+                if start < 0:
+                    raise ValueError('start must be >= -%s' % nchan)
             stop = item[0].stop if item[0].stop is not None else nchan
             step = item[0].step if item[0].step is not None else 1
             sel = list(range(start, stop, step))
@@ -551,10 +625,7 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
     def __setitem__(self, item, value):
         """setting raw data content with python slicing"""
-        if not self.preload:
-            raise RuntimeError('Modifying data of Raw is only supported '
-                               'when preloading is used. Use preload=True '
-                               '(or string) in the constructor.')
+        _check_preload(self, 'Modifying data of Raw')
         sel, start, stop = self._parse_get_set_params(item)
         # set the data
         self._data[sel, start:stop] = value
@@ -562,7 +633,7 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
     def anonymize(self):
         """Anonymize data
 
-        This function will remove info['subject_info'] if it exists.
+        This function will remove ``raw.info['subject_info']`` if it exists.
 
         Returns
         -------
@@ -582,14 +653,16 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         the dtype parameter, which causes the data type used for representing
         the raw data to change.
 
-        The Raw object has to be constructed using preload=True (or string).
+        The Raw object has to have the data loaded e.g. with ``preload=True``
+        or ``self.load_data()``.
 
-        Note: If n_jobs > 1, more memory is required as "len(picks) * n_times"
-              additional time points need to be temporaily stored in memory.
+        .. note:: If n_jobs > 1, more memory is required as
+                  ``len(picks) * n_times`` additional time points need to
+                  be temporaily stored in memory.
 
-        Note: If the data type changes (dtype != None), more memory is required
-              since the original and the converted data needs to be stored in
-              memory.
+        .. note:: If the data type changes (dtype != None), more memory is
+                  required since the original and the converted data needs
+                  to be stored in memory.
 
         Parameters
         ----------
@@ -613,11 +686,10 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             as a member of ``kwargs``, it will be consumed and will override
             the default mne-python verbose level (see mne.verbose).
         """
-        if not self.preload:
-            raise RuntimeError('Raw data needs to be preloaded. Use '
-                               'preload=True (or string) in the constructor.')
+        _check_preload(self, 'raw.apply_function')
         if picks is None:
-            picks = pick_types(self.info, meg=True, eeg=True, exclude=[])
+            picks = _pick_data_channels(self.info, exclude=[],
+                                        with_ref_meg=False)
 
         if not callable(fun):
             raise ValueError('fun needs to be a function')
@@ -652,18 +724,19 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         channels defined in "picks" is computed, resulting in the envelope
         signal.
 
-        Note: DO NOT use envelope=True if you intend to compute an inverse
-              solution from the raw data. If you want to compute the
-              envelope in source space, use envelope=False and compute the
-              envelope after the inverse solution has been obtained.
+        .. warning: Do not use ``envelope=True`` if you intend to compute
+                    an inverse solution from the raw data. If you want to
+                    compute the envelope in source space, use
+                    ``envelope=False`` and compute the envelope after the
+                    inverse solution has been obtained.
 
-        Note: If envelope=False, more memory is required since the original
-              raw data as well as the analytic signal have temporarily to
-              be stored in memory.
+        .. note:: If envelope=False, more memory is required since the
+                  original raw data as well as the analytic signal have
+                  temporarily to be stored in memory.
 
-        Note: If n_jobs > 1 and envelope=True, more memory is required as
-              "len(picks) * n_times" additional time points need to be
-              temporaily stored in memory.
+        .. note:: If n_jobs > 1, more memory is required as
+                  ``len(picks) * n_times`` additional time points need to
+                  be temporaily stored in memory.
 
         Parameters
         ----------
@@ -717,24 +790,26 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         """Filter a subset of channels.
 
         Applies a zero-phase low-pass, high-pass, band-pass, or band-stop
-        filter to the channels selected by "picks". The data of the Raw
-        object is modified inplace.
+        filter to the channels selected by ``picks``. By default the data
+        of the Raw object is modified inplace.
 
-        The Raw object has to be constructed using preload=True (or string).
+        The Raw object has to have the data loaded e.g. with ``preload=True``
+        or ``self.load_data()``.
 
-        l_freq and h_freq are the frequencies below which and above which,
-        respectively, to filter out of the data. Thus the uses are:
+        ``l_freq`` and ``h_freq`` are the frequencies below which and above
+        which, respectively, to filter out of the data. Thus the uses are:
 
             * ``l_freq < h_freq``: band-pass filter
             * ``l_freq > h_freq``: band-stop filter
             * ``l_freq is not None and h_freq is None``: high-pass filter
             * ``l_freq is None and h_freq is not None``: low-pass filter
 
-        If n_jobs > 1, more memory is required as "len(picks) * n_times"
-        additional time points need to be temporarily stored in memory.
+        ``self.info['lowpass']`` and ``self.info['highpass']`` are only
+        updated with picks=None.
 
-        self.info['lowpass'] and self.info['highpass'] are only updated
-        with picks=None.
+        .. note:: If n_jobs > 1, more memory is required as
+                  ``len(picks) * n_times`` additional time points need to
+                  be temporaily stored in memory.
 
         Parameters
         ----------
@@ -776,12 +851,17 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             If not None, override default verbose level (see mne.verbose).
             Defaults to self.verbose.
 
+        Returns
+        -------
+        raw : instance of Raw
+            The raw instance with filtered data.
+
         See Also
         --------
         mne.Epochs.savgol_filter
+        mne.io.Raw.notch_filter
+        mne.io.Raw.resample
         """
-        if verbose is None:
-            verbose = self.verbose
         fs = float(self.info['sfreq'])
         if l_freq == 0:
             l_freq = None
@@ -791,16 +871,9 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             l_freq = float(l_freq)
         if h_freq is not None and not isinstance(h_freq, float):
             h_freq = float(h_freq)
-
-        if not self.preload:
-            raise RuntimeError('Raw data needs to be preloaded to filter. Use '
-                               'preload=True (or string) in the constructor.')
+        _check_preload(self, 'raw.filter')
         if picks is None:
-            if 'ICA ' in ','.join(self.ch_names):
-                pick_parameters = dict(misc=True, ref_meg=False)
-            else:
-                pick_parameters = dict(meg=True, eeg=True, ref_meg=False)
-            picks = pick_types(self.info, exclude=[], **pick_parameters)
+            picks = _pick_data_or_ica(self.info)
             # let's be safe.
             if len(picks) < 1:
                 raise RuntimeError('Could not find any valid channels for '
@@ -819,6 +892,12 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                    (self.info["highpass"] is None or
                    l_freq > self.info['highpass']):
                         self.info['highpass'] = l_freq
+        else:
+            if h_freq is not None or l_freq is not None:
+                logger.info('Filtering a subset of channels. The highpass and '
+                            'lowpass values in the measurement info will not '
+                            'be updated.')
+
         if l_freq is None and h_freq is not None:
             logger.info('Low-pass filtering at %0.2g Hz' % h_freq)
             low_pass_filter(self._data, fs, h_freq,
@@ -854,21 +933,24 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                     h_trans_bandwidth=l_trans_bandwidth, method=method,
                     iir_params=iir_params, picks=picks, n_jobs=n_jobs,
                     copy=False)
+        return self
 
     @verbose
     def notch_filter(self, freqs, picks=None, filter_length='10s',
                      notch_widths=None, trans_bandwidth=1.0, n_jobs=1,
-                     method='fft', iir_params=None,
-                     mt_bandwidth=None, p_value=0.05, verbose=None):
+                     method='fft', iir_params=None, mt_bandwidth=None,
+                     p_value=0.05, verbose=None):
         """Notch filter a subset of channels.
 
         Applies a zero-phase notch filter to the channels selected by
-        "picks". The data of the Raw object is modified inplace.
+        "picks". By default the data of the Raw object is modified inplace.
 
-        The Raw object has to be constructed using preload=True (or string).
+        The Raw object has to have the data loaded e.g. with ``preload=True``
+        or ``self.load_data()``.
 
-        Note: If n_jobs > 1, more memory is required as "len(picks) * n_times"
-              additional time points need to be temporaily stored in memory.
+        .. note:: If n_jobs > 1, more memory is required as
+                  ``len(picks) * n_times`` additional time points need to
+                  be temporaily stored in memory.
 
         Parameters
         ----------
@@ -916,44 +998,42 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             If not None, override default verbose level (see mne.verbose).
             Defaults to self.verbose.
 
+        Returns
+        -------
+        raw : instance of Raw
+            The raw instance with filtered data.
+
+        See Also
+        --------
+        mne.io.Raw.filter
+
         Notes
         -----
-        For details, see mne.filter.notch_filter.
+        For details, see :func:`mne.filter.notch_filter`.
         """
-        if verbose is None:
-            verbose = self.verbose
         fs = float(self.info['sfreq'])
         if picks is None:
-            if 'ICA ' in ','.join(self.ch_names):
-                pick_parameters = dict(misc=True)
-            else:
-                pick_parameters = dict(meg=True, eeg=True)
-            picks = pick_types(self.info, exclude=[], **pick_parameters)
+            picks = _pick_data_or_ica(self.info)
             # let's be safe.
             if len(picks) < 1:
                 raise RuntimeError('Could not find any valid channels for '
                                    'your Raw object. Please contact the '
                                    'MNE-Python developers.')
-        if not self.preload:
-            raise RuntimeError('Raw data needs to be preloaded to filter. Use '
-                               'preload=True (or string) in the constructor.')
-
-        self._data = notch_filter(self._data, fs, freqs,
-                                  filter_length=filter_length,
-                                  notch_widths=notch_widths,
-                                  trans_bandwidth=trans_bandwidth,
-                                  method=method, iir_params=iir_params,
-                                  mt_bandwidth=mt_bandwidth, p_value=p_value,
-                                  picks=picks, n_jobs=n_jobs, copy=False)
+        _check_preload(self, 'raw.notch_filter')
+        self._data = notch_filter(
+            self._data, fs, freqs, filter_length=filter_length,
+            notch_widths=notch_widths, trans_bandwidth=trans_bandwidth,
+            method=method, iir_params=iir_params, mt_bandwidth=mt_bandwidth,
+            p_value=p_value, picks=picks, n_jobs=n_jobs, copy=False)
+        return self
 
     @verbose
-    def resample(self, sfreq, npad=100, window='boxcar', stim_picks=None,
-                 n_jobs=1, events=None, copy=False, verbose=None):
-        """Resample data channels.
-
-        Resamples all channels.
+    def resample(self, sfreq, npad=None, window='boxcar', stim_picks=None,
+                 n_jobs=1, events=None, copy=None, verbose=None):
+        """Resample all channels.
 
-        The Raw object has to be constructed using preload=True (or string).
+        The Raw object has to have the data loaded e.g. with ``preload=True``
+        or ``self.load_data()``.
 
         .. warning:: The intended purpose of this function is primarily to
                      speed up computations (e.g., projection calculation) when
@@ -962,9 +1042,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                      generally recommended not to epoch downsampled data,
                      but instead epoch and then downsample, as epoching
                      downsampled data jitters triggers.
-                     See here for an example:
-
-                         https://gist.github.com/Eric89GXL/01642cb3789992fbca59
+                     For more, see
+                     `this illustrative gist <https://gist.github.com/Eric89GXL/01642cb3789992fbca59>`_.
 
                      If resampling the continuous data is desired, it is
                      recommended to construct events using the original data.
@@ -975,16 +1054,19 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         ----------
         sfreq : float
             New sample rate to use.
-        npad : int
+        npad : int | str
             Amount to pad the start and end of the data.
+            Can also be "auto" to use a padding that will result in
+            a power-of-two size (can be much faster).
         window : string or tuple
-            Window to use in resampling. See scipy.signal.resample.
+            Frequency-domain window to use in resampling.
+            See :func:`scipy.signal.resample`.
         stim_picks : array of int | None
             Stim channels. These channels are simply subsampled or
             supersampled (without applying any filtering). This reduces
             resampling artifacts in stim channels, but may lead to missing
             triggers. If None, stim channels are automatically chosen using
-            mne.pick_types(raw.info, meg=False, stim=True, exclude=[]).
+            :func:`mne.pick_types`.
         n_jobs : int | str
             Number of jobs to run in parallel. Can be 'cuda' if scikits.cuda
             is installed properly and CUDA is initialized.
@@ -1003,15 +1085,23 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         raw : instance of Raw
             The resampled version of the raw object.
 
+        See Also
+        --------
+        mne.io.Raw.filter
+        mne.Epochs.resample
+
         Notes
         -----
-        For some data, it may be more accurate to use npad=0 to reduce
+        For some data, it may be more accurate to use ``npad=0`` to reduce
         artifacts. This is dataset dependent -- check your data!
-        """
-        if not self.preload:
-            raise RuntimeError('Can only resample preloaded data')
-
-        inst = self.copy() if copy else self
+        """  # noqa
+        if npad is None:
+            npad = 100
+            warn('npad is currently taken to be 100, but will be changed to '
+                 '"auto" in 0.13. Please set the value explicitly.',
+                 DeprecationWarning)
+        _check_preload(self, 'raw.resample')
+        inst = _check_copy_dep(self, copy)
 
         # When no event object is supplied, some basic detection of dropped
         # events is performed to generate a warning. Finding events can fail
@@ -1041,7 +1131,7 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         for ri in range(len(inst._raw_lengths)):
             data_chunk = inst._data[:, offsets[ri]:offsets[ri + 1]]
             new_data.append(resample(data_chunk, sfreq, o_sfreq, npad,
-                                     n_jobs=n_jobs))
+                                     window=window, n_jobs=n_jobs))
             new_ntimes = new_data[ri].shape[1]
 
             # In empirical testing, it was faster to resample all channels
@@ -1060,6 +1150,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
         inst._data = np.concatenate(new_data, axis=1)
         inst.info['sfreq'] = sfreq
+        if inst.info.get('lowpass') is not None:
+            inst.info['lowpass'] = min(inst.info['lowpass'], sfreq / 2.)
         inst._update_times()
 
         # See the comment above why we ignore all errors here.
@@ -1068,12 +1160,10 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                 # Did we loose events?
                 resampled_events = find_events(inst)
                 if len(resampled_events) != len(original_events):
-                    warnings.warn(
-                        'Resampling of the stim channels caused event '
-                        'information to become unreliable. Consider finding '
-                        'events on the original data and passing the event '
-                        'matrix as a parameter.'
-                    )
+                    warn('Resampling of the stim channels caused event '
+                         'information to become unreliable. Consider finding '
+                         'events on the original data and passing the event '
+                         'matrix as a parameter.')
             except:
                 pass
 
@@ -1088,14 +1178,13 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             )
             return inst, events
 
-    def crop(self, tmin=0.0, tmax=None, copy=True):
+    def crop(self, tmin=0.0, tmax=None, copy=None):
         """Crop raw data file.
 
         Limit the data from the raw file to go between specific times. Note
         that the new tmin is assumed to be t=0 for all subsequently called
         functions (e.g., time_as_index, or Epochs). New first_samp and
-        last_samp are set accordingly. And data are modified in-place when
-        called with copy=False.
+        last_samp are set accordingly.
 
         Parameters
         ----------
@@ -1104,14 +1193,16 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         tmax : float | None
             New end time in seconds of the data (cannot exceed data duration).
         copy : bool
-            If False Raw is cropped in place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
 
         Returns
         -------
         raw : instance of Raw
             The cropped raw object.
         """
-        raw = self.copy() if copy is True else self
+        raw = _check_copy_dep(self, copy, default=True)
         max_time = (raw.n_times - 1) / raw.info['sfreq']
         if tmax is None:
             tmax = max_time
@@ -1124,7 +1215,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             raise ValueError('tmax must be less than or equal to the max raw '
                              'time (%0.4f sec)' % max_time)
 
-        smin, smax = np.where(_time_mask(self.times, tmin, tmax))[0][[0, -1]]
+        smin, smax = np.where(_time_mask(self.times, tmin, tmax,
+                                         sfreq=self.info['sfreq']))[0][[0, -1]]
         cumul_lens = np.concatenate(([0], np.array(raw._raw_lengths,
                                                    dtype='int')))
         cumul_lens = np.cumsum(cumul_lens)
@@ -1175,8 +1267,10 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             that only accepts raw files with buffers of the same size.
         proj : bool
             If True the data is saved with the projections applied (active).
-            Note: If apply_proj() was used to apply the projections,
-            the projectons will be active even if proj is False.
+
+            .. note:: If ``apply_proj()`` was used to apply the projections,
+                      the projectons will be active even if ``proj`` is False.
+
         fmt : str
             Format to use to save raw data. Valid options are 'double',
             'single', 'int', and 'short' for 64- or 32-bit float, or 32- or
@@ -1195,7 +1289,10 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             parameter specifies the maximum size of each piece. If the
             parameter is an integer, it specifies the size in Bytes. It is
             also possible to pass a human-readable string, e.g., 100MB.
-            Note: Due to FIFF file limitations, the maximum split size is 2GB.
+
+            .. note:: Due to FIFF file limitations, the maximum split
+                      size is 2GB.
+
         verbose : bool, str, int, or None
             If not None, override default verbose level (see mne.verbose).
             Defaults to self.verbose.
@@ -1222,8 +1319,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
         if self.preload:
             if np.iscomplexobj(self._data):
-                warnings.warn('Saving raw file with complex data. Loading '
-                              'with command-line MNE tools will not work.')
+                warn('Saving raw file with complex data. Loading with '
+                     'command-line MNE tools will not work.')
 
         type_dict = dict(short=FIFF.FIFFT_DAU_PACK16,
                          int=FIFF.FIFFT_INT,
@@ -1265,10 +1362,12 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         #   Convert to samples
         start = int(np.floor(tmin * self.info['sfreq']))
 
+        # "stop" is the first sample *not* to save, so we need +1's here
         if tmax is None:
-            stop = self.last_samp + 1 - self.first_samp
+            stop = np.inf
         else:
-            stop = int(np.floor(tmax * self.info['sfreq']))
+            stop = self.time_as_index(float(tmax), use_rounding=True)[0] + 1
+        stop = min(stop, self.last_samp - self.first_samp + 1)
         buffer_size = self._get_buffer_size(buffer_size_sec)
 
         # write the raw file
@@ -1307,7 +1406,12 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         event_color : color object
             Color to use for events.
         scalings : dict | None
-            Scale factors for the traces. If None, defaults to::
+            Scaling factors for the traces. If any fields in scalings are
+            'auto', the scaling factor is set to match the 99.5th percentile of
+            a subset of the corresponding data. If scalings == 'auto', all
+            scalings fields are set to 'auto'. If any fields are 'auto' and
+            data is not preloaded, a subset of times up to 100mb will be
+            loaded. If None, defaults to::
 
                 dict(mag=1e-12, grad=4e-11, eeg=20e-6, eog=150e-6, ecg=5e-4,
                      emg=1e-3, ref_meg=1e-12, misc=1e-3, stim=1,
@@ -1487,7 +1591,7 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                                  axis_facecolor=axis_facecolor, dB=dB,
                                  show=show, n_jobs=n_jobs, verbose=verbose)
 
-    def time_as_index(self, times, use_first_samp=False, use_rounding=False):
+    def time_as_index(self, times, use_first_samp=None, use_rounding=False):
         """Convert time to indices
 
         Parameters
@@ -1495,20 +1599,33 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         times : list-like | float | int
             List of numbers or a number representing points in time.
         use_first_samp : boolean
+            This is deprecated and will be removed in 0.13.
             If True, time is treated as relative to the session onset, else
-            as relative to the recording onset.
+            as relative to the recording onset. Default is False.
         use_rounding : boolean
             If True, use rounding (instead of truncation) when converting
-            times to indicies. This can help avoid non-unique indices.
+            times to indices. This can help avoid non-unique indices.
 
         Returns
         -------
         index : ndarray
             Indices corresponding to the times supplied.
         """
-        return _time_as_index(times, self.info['sfreq'], self.first_samp,
-                              use_first_samp, use_rounding=use_rounding)
-
+        # Note: this entire class can be removed in 0.13 (proper method
+        # will be inherited from TimeMixin)
+        if use_first_samp is None:
+            use_first_samp = False
+        else:
+            warn('use_first_samp is deprecated, add raw.first_samp manually '
+                 'if first sample offset is required', DeprecationWarning)
+        index = super(_BaseRaw, self).time_as_index(times, use_rounding)
+        if use_first_samp:
+            index -= self.first_samp
+        return index
+
+    @deprecated('index_as_time is deprecated and will be removed in 0.13, '
+                'use raw.times[idx] (or raw.times[idx + raw.first_samp] '
+                'instead')
     def index_as_time(self, index, use_first_samp=False):
         """Convert indices to time
 
@@ -1594,8 +1711,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             stop = min(self.n_times - 1, self.time_as_index(tstop)[0])
         tslice = slice(start, stop + 1)
         if picks is None:
-            picks = pick_types(self.info, meg=True, eeg=True, ref_meg=False,
-                               exclude='bads')
+            picks = _pick_data_channels(self.info, exclude='bads',
+                                        with_ref_meg=False)
         # ensure we don't get a view of data
         if len(picks) == 1:
             return 1.0, 1.0
@@ -1603,9 +1720,7 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         data = self[picks, tslice][0]
         out = _estimate_rank_meeg_signals(
             data, pick_info(self.info, picks),
-            scalings=scalings, tol=tol, return_singular=return_singular,
-            copy=False)
-
+            scalings=scalings, tol=tol, return_singular=return_singular)
         return out
 
     @property
@@ -1628,8 +1743,10 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
 
     def load_bad_channels(self, bad_file=None, force=False):
         """
-        Mark channels as bad from a text file, in the style
-        (mostly) of the C function mne_mark_bad_channels
+        Mark channels as bad from a text file
+
+        This function operates mostly in the style of the C function
+        ``mne_mark_bad_channels``.
 
         Parameters
         ----------
@@ -1637,7 +1754,6 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             File name of the text file containing bad channels
             If bad_file = None, bad channels are cleared, but this
             is more easily done directly as raw.info['bads'] = [].
-
         force : boolean
             Whether or not to force bad channel marking (of those
             that exist) if channels are not found, instead of
@@ -1658,9 +1774,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
                                      'in:\n%s' % (bad_file,
                                                   self._filenames[0]))
                 else:
-                    warnings.warn('%d bad channels from:\n%s\nnot found '
-                                  'in:\n%s' % (count_diff, bad_file,
-                                               self._filenames[0]))
+                    warn('%d bad channels from:\n%s\nnot found in:\n%s'
+                         % (count_diff, bad_file, self._filenames[0]))
             self.info['bads'] = names_there
         else:
             self.info['bads'] = []
@@ -1682,10 +1797,6 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             None, preload=True or False is inferred using the preload status
             of the raw files passed in.
         """
-        from .fiff.raw import RawFIF
-        from .kit.kit import RawKIT
-        from .edf.edf import RawEDF
-
         if not isinstance(raws, list):
             raws = [raws]
 
@@ -1702,9 +1813,6 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             else:
                 preload = False
 
-        if not preload and not isinstance(self, (RawFIF, RawKIT, RawEDF)):
-            raise RuntimeError('preload must be True to concatenate '
-                               'files unless they are FIF, KIT, or EDF')
         if preload is False:
             if self.preload:
                 self._data = None
@@ -1745,6 +1853,12 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
             self._last_samps = np.r_[self._last_samps, r._last_samps]
             self._raw_extras += r._raw_extras
             self._filenames += r._filenames
+            self.annotations = _combine_annotations((self.annotations,
+                                                     r.annotations),
+                                                    self._last_samps,
+                                                    self._first_samps,
+                                                    self.info['sfreq'])
+
         self._update_times()
 
         if not (len(self._first_samps) == len(self._last_samps) ==
@@ -1767,10 +1881,8 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
     def __repr__(self):
         name = self._filenames[0]
         name = 'None' if name is None else op.basename(name)
-        s = ', '.join(('%r' % name, "n_channels x n_times : %s x %s"
-                       % (len(self.ch_names), self.n_times)))
-        s = "n_channels x n_times : %s x %s" % (len(self.info['ch_names']),
-                                                self.n_times)
+        s = ('%s, n_channels x n_times : %s x %s (%0.1f sec)'
+             % (name, len(self.ch_names), self.n_times, self.times[-1]))
         return "<%s  |  %s>" % (self.__class__.__name__, s)
 
     def add_events(self, events, stim_channel=None):
@@ -1821,6 +1933,14 @@ class _BaseRaw(ProjMixin, ContainsMixin, UpdateChannelsMixin,
         return int(np.ceil(buffer_size_sec * self.info['sfreq']))
 
 
+def _check_preload(raw, msg):
+    """Helper to ensure data are preloaded"""
+    if not raw.preload:
+        raise RuntimeError(msg + ' requires raw data to be loaded. Use '
+                           'preload=True (or string) in the constructor or '
+                           'raw.load_data().')
+
+
 def _allocate_data(data, data_buffer, data_shape, dtype):
     """Helper to data in memory or in memmap for preloading"""
     if data is None:
@@ -1834,45 +1954,6 @@ def _allocate_data(data, data_buffer, data_shape, dtype):
     return data
 
 
-def _time_as_index(times, sfreq, first_samp=0, use_first_samp=False,
-                   use_rounding=False):
-    """Convert time to indices
-
-    Parameters
-    ----------
-    times : list-like | float | int
-        List of numbers or a number representing points in time.
-    sfreq : float | int
-        Sample frequency.
-    first_samp : int
-       Index to use as first time point.
-    use_first_samp : boolean
-        If True, time is treated as relative to the session onset, else
-        as relative to the recording onset.
-    use_rounding : boolean
-        If True, use rounding (instead of truncation) when converting times to
-        indicies. This can help avoid non-unique indices.
-
-    Returns
-    -------
-    index : ndarray
-        Indices corresponding to the times supplied.
-
-    Notes
-    -----
-    np.round will return the nearest even number for values exactly between
-        two integers.
-    """
-    index = np.atleast_1d(times) * sfreq
-    index -= (first_samp if use_first_samp else 0)
-
-    # Round or truncate time indices
-    if use_rounding:
-        return np.round(index).astype(int)
-    else:
-        return index.astype(int)
-
-
 def _index_as_time(index, sfreq, first_samp=0, use_first_samp=False):
     """Convert indices to time
 
@@ -1915,19 +1996,23 @@ def _write_raw(fname, raw, info, picks, fmt, data_type, reset_range, start,
                split_size, part_idx, prev_fname):
     """Write raw file with splitting
     """
+    # we've done something wrong if we hit this
+    n_times_max = len(raw.times)
+    if start >= stop or stop > n_times_max:
+        raise RuntimeError('Cannot write raw file with no data: %s -> %s '
+                           '(max: %s) requested' % (start, stop, n_times_max))
 
     if part_idx > 0:
         # insert index in filename
-        path, base = op.split(fname)
-        idx = base.find('.')
-        use_fname = op.join(path, '%s-%d.%s' % (base[:idx], part_idx,
-                                                base[idx + 1:]))
+        base, ext = op.splitext(fname)
+        use_fname = '%s-%d%s' % (base, part_idx, ext)
     else:
         use_fname = fname
     logger.info('Writing %s' % use_fname)
 
     fid, cals = _start_writing_raw(use_fname, info, picks, data_type,
-                                   reset_range)
+                                   reset_range, raw.annotations)
+    use_picks = slice(None) if picks is None else picks
 
     first_samp = raw.first_samp + start
     if first_samp != 0:
@@ -1943,16 +2028,18 @@ def _write_raw(fname, raw, info, picks, fmt, data_type, reset_range, start,
         write_int(fid, FIFF.FIFF_REF_FILE_NUM, part_idx - 1)
         end_block(fid, FIFF.FIFFB_REF)
 
-    pos_prev = None
+    pos_prev = fid.tell()
+    if pos_prev > split_size:
+        raise ValueError('file is larger than "split_size" after writing '
+                         'measurement information, you must use a larger '
+                         'value for split size: %s plus enough bytes for '
+                         'the chosen buffer_size' % pos_prev)
+    next_file_buffer = 2 ** 20  # extra cushion for last few post-data tags
     for first in range(start, stop, buffer_size):
-        last = first + buffer_size
-        if last >= stop:
-            last = stop + 1
-
-        if picks is None:
-            data, times = raw[:, first:last]
-        else:
-            data, times = raw[picks, first:last]
+        # Write blocks <= buffer_size in size
+        last = min(first + buffer_size, stop)
+        data, times = raw[use_picks, first:last]
+        assert len(times) == last - first
 
         if projector is not None:
             data = np.dot(projector, data)
@@ -1963,23 +2050,26 @@ def _write_raw(fname, raw, info, picks, fmt, data_type, reset_range, start,
                         '[done]')
             break
         logger.info('Writing ...')
-
-        if pos_prev is None:
-            pos_prev = fid.tell()
-
         _write_raw_buffer(fid, data, cals, fmt, inv_comp)
 
         pos = fid.tell()
         this_buff_size_bytes = pos - pos_prev
-        if this_buff_size_bytes > split_size / 2:
-            raise ValueError('buffer size is too large for the given split'
-                             'size: decrease "buffer_size_sec" or increase'
-                             '"split_size".')
-        if pos > split_size:
-            raise logger.warning('file is larger than "split_size"')
+        overage = pos - split_size + next_file_buffer
+        if overage > 0:
+            # This should occur on the first buffer write of the file, so
+            # we should mention the space required for the meas info
+            raise ValueError(
+                'buffer size (%s) is too large for the given split size (%s) '
+                'by %s bytes after writing info (%s) and leaving enough space '
+                'for end tags (%s): decrease "buffer_size_sec" or increase '
+                '"split_size".' % (this_buff_size_bytes, split_size, overage,
+                                   pos_prev, next_file_buffer))
 
         # Split files if necessary, leave some space for next file info
-        if pos >= split_size - this_buff_size_bytes - 2 ** 20:
+        # make sure we check to make sure we actually *need* another buffer
+        # with the "and" check
+        if pos >= split_size - this_buff_size_bytes - next_file_buffer and \
+                first + buffer_size < stop:
             next_fname, next_idx = _write_raw(
                 fname, raw, info, picks, fmt,
                 data_type, reset_range, first + buffer_size, stop, buffer_size,
@@ -2008,7 +2098,7 @@ def _write_raw(fname, raw, info, picks, fmt, data_type, reset_range, start,
 
 
 def _start_writing_raw(name, info, sel=None, data_type=FIFF.FIFFT_FLOAT,
-                       reset_range=True):
+                       reset_range=True, annotations=None):
     """Start write raw data in file
 
     Data will be written in float
@@ -2026,6 +2116,8 @@ def _start_writing_raw(name, info, sel=None, data_type=FIFF.FIFFT_FLOAT,
         5 (FIFFT_DOUBLE), 16 (FIFFT_DAU_PACK16), or 3 (FIFFT_INT) for raw data.
     reset_range : bool
         If True, the info['chs'][k]['range'] parameter will be set to unity.
+    annotations : instance of Annotations or None
+        The annotations to write.
 
     Returns
     -------
@@ -2035,12 +2127,12 @@ def _start_writing_raw(name, info, sel=None, data_type=FIFF.FIFFT_FLOAT,
         calibration factors.
     """
     #
-    #    Measurement info
+    # Measurement info
     #
-    info = pick_info(info, sel, copy=True)
+    info = pick_info(info, sel)
 
     #
-    #  Create the file and save the essentials
+    # Create the file and save the essentials
     #
     fid = start_file(name)
     start_block(fid, FIFF.FIFFB_MEAS)
@@ -2061,6 +2153,21 @@ def _start_writing_raw(name, info, sel=None, data_type=FIFF.FIFFT_FLOAT,
     write_meas_info(fid, info, data_type=data_type, reset_range=reset_range)
 
     #
+    # Annotations
+    #
+    if annotations is not None:
+        start_block(fid, FIFF.FIFFB_MNE_ANNOTATIONS)
+        write_float(fid, FIFF.FIFF_MNE_BASELINE_MIN, annotations.onset)
+        write_float(fid, FIFF.FIFF_MNE_BASELINE_MAX,
+                    annotations.duration + annotations.onset)
+        # To allow : in description, they need to be replaced for serialization
+        write_name_list(fid, FIFF.FIFF_COMMENT, [d.replace(':', ';') for d in
+                                                 annotations.description])
+        if annotations.orig_time is not None:
+            write_double(fid, FIFF.FIFF_MEAS_DATE, annotations.orig_time)
+        end_block(fid, FIFF.FIFFB_MNE_ANNOTATIONS)
+
+    #
     # Start the raw data
     #
     if info.get('maxshield', False):
@@ -2172,9 +2279,8 @@ def _check_raw_compatibility(raw):
                    zip(raw[0].info['projs'], raw[ri].info['projs'])):
             raise ValueError('SSP projectors in raw files must be the same')
     if not all(r.orig_format == raw[0].orig_format for r in raw):
-        warnings.warn('raw files do not all have the same data format, '
-                      'could result in precision mismatch. Setting '
-                      'raw.orig_format="unknown"')
+        warn('raw files do not all have the same data format, could result in '
+             'precision mismatch. Setting raw.orig_format="unknown"')
         raw[0].orig_format = 'unknown'
 
 
@@ -2236,8 +2342,8 @@ def _check_update_montage(info, montage, path=None, update_ch_names=False):
 
             # raise error if positions are missing
             if missing_positions:
-                err = ("The following positions are missing from the montage "
-                       "definitions: %s. If those channels lack positions "
-                       "because they are EOG channels use the eog parameter."
-                       % str(missing_positions))
-                raise KeyError(err)
+                raise KeyError(
+                    "The following positions are missing from the montage "
+                    "definitions: %s. If those channels lack positions "
+                    "because they are EOG channels use the eog parameter."
+                    % str(missing_positions))
diff --git a/mne/io/brainvision/brainvision.py b/mne/io/brainvision/brainvision.py
index d13052a..943686f 100644
--- a/mne/io/brainvision/brainvision.py
+++ b/mne/io/brainvision/brainvision.py
@@ -8,17 +8,16 @@
 # License: BSD (3-clause)
 
 import os
-import time
 import re
-import warnings
+import time
 
 import numpy as np
 
-from ...utils import verbose, logger
+from ...utils import verbose, logger, warn, deprecated
 from ..constants import FIFF
 from ..meas_info import _empty_info
 from ..base import _BaseRaw, _check_update_montage
-from ..utils import _mult_cal_one
+from ..utils import _read_segments_file, _synthesize_stim_channel
 
 from ...externals.six import StringIO
 from ...externals.six.moves import configparser
@@ -56,11 +55,12 @@ class RawBrainVision(_BaseRaw):
         triggers will be ignored. Default is 0 for backwards compatibility, but
         typically another value or None will be necessary.
     event_id : dict | None
-        The id of the event to consider. If None (default),
-        only stimulus events are added to the stimulus channel. If dict,
-        the keys will be mapped to trigger values on the stimulus channel
-        in addition to the stimulus events. Keys are case-sensitive.
-        Example: {'SyncStatus': 1; 'Pulse Artifact': 3}.
+        The id of special events to consider in addition to those that
+        follow the normal Brainvision trigger format ('SXXX').
+        If dict, the keys will be mapped to trigger values on the stimulus
+        channel. Example: {'SyncStatus': 1; 'Pulse Artifact': 3}. If None
+        or an empty dict (default), only stimulus events are added to the
+        stimulus channel. Keys are case sensitive.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -94,24 +94,15 @@ class RawBrainVision(_BaseRaw):
     def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
         """Read a chunk of raw data"""
         # read data
+        dtype = _fmt_dtype_dict[self.orig_format]
         n_data_ch = len(self.ch_names) - 1
-        n_times = stop - start
-        pointer = start * n_data_ch * _fmt_byte_dict[self.orig_format]
-        with open(self._filenames[fi], 'rb') as f:
-            f.seek(pointer)
-            # extract data
-            data_buffer = np.fromfile(
-                f, dtype=_fmt_dtype_dict[self.orig_format],
-                count=n_times * n_data_ch)
-        data_buffer = data_buffer.reshape((n_data_ch, n_times),
-                                          order=self._order)
-
-        data_ = np.empty((n_data_ch + 1, n_times), dtype=np.float64)
-        data_[:-1] = data_buffer  # cast to float64
-        del data_buffer
-        data_[-1] = self._event_ch[start:stop]
-        _mult_cal_one(data, data_, idx, cals, mult)
+        _read_segments_file(self, data, idx, fi, start, stop, cals, mult,
+                            dtype=dtype, n_channels=n_data_ch,
+                            trigger_ch=self._event_ch)
 
+    @deprecated('get_brainvision_events is deprecated and will be removed '
+                'in 0.13, use mne.find_events(raw, "STI014") to get properly '
+                'formatted events instead')
     def get_brainvision_events(self):
         """Retrieve the events associated with the Brain Vision Raw object
 
@@ -121,8 +112,21 @@ class RawBrainVision(_BaseRaw):
             Events, each row consisting of an (onset, duration, trigger)
             sequence.
         """
+        return self._get_brainvision_events()
+
+    def _get_brainvision_events(self):
+        """Retrieve the events associated with the Brain Vision Raw object
+
+        Returns
+        -------
+        events : array, shape (n_events, 3)
+            Events, each row consisting of an (onset, duration, trigger)
+            sequence.
+        """
         return self._events.copy()
 
+    @deprecated('set_brainvision_events is deprecated and will be removed '
+                'in 0.13')
     def set_brainvision_events(self, events):
         """Set the events and update the synthesized stim channel
 
@@ -132,6 +136,17 @@ class RawBrainVision(_BaseRaw):
             Events, each row consisting of an (onset, duration, trigger)
             sequence.
         """
+        return self._set_brainvision_events(events)
+
+    def _set_brainvision_events(self, events):
+        """Set the events and update the synthesized stim channel
+
+        Parameters
+        ----------
+        events : array, shape (n_events, 3)
+            Events, each row consisting of an (onset, duration, trigger)
+            sequence.
+        """
         self._create_event_ch(events)
 
     def _create_event_ch(self, events, n_samp=None):
@@ -156,10 +171,12 @@ def _read_vmrk_events(fname, event_id=None, response_trig_shift=0):
     fname : str
         vmrk file to be read.
     event_id : dict | None
-        The id of the event to consider. If dict, the keys will be mapped to
-        trigger values on the stimulus channel. Example:
-        {'SyncStatus': 1; 'Pulse Artifact': 3}. If empty dict (default),
-        only stimulus events are added to the stimulus channel.
+        The id of special events to consider in addition to those that
+        follow the normal Brainvision trigger format ('SXXX').
+        If dict, the keys will be mapped to trigger values on the stimulus
+        channel. Example: {'SyncStatus': 1; 'Pulse Artifact': 3}. If None
+        or an empty dict (default), only stimulus events are added to the
+        stimulus channel. Keys are case sensitive.
     response_trig_shift : int | None
         Integer to shift response triggers by. None ignores response triggers.
 
@@ -192,55 +209,43 @@ def _read_vmrk_events(fname, event_id=None, response_trig_shift=0):
 
     # extract event information
     items = re.findall("^Mk\d+=(.*)", mk_txt, re.MULTILINE)
-    events = []
+    events, dropped = list(), list()
     for info in items:
         mtype, mdesc, onset, duration = info.split(',')[:4]
         onset = int(onset)
         duration = (int(duration) if duration.isdigit() else 1)
-        try:
-            trigger = int(re.findall('[A-Za-z]*\s*?(\d+)', mdesc)[0])
-        except IndexError:
-            trigger = None
-
-        if mtype.lower().startswith('response'):
-            if response_trig_shift is not None:
-                trigger += response_trig_shift
-            else:
-                trigger = None
         if mdesc in event_id:
             trigger = event_id[mdesc]
+        else:
+            try:
+                trigger = int(re.findall('[A-Za-z]*\s*?(\d+)', mdesc)[0])
+            except IndexError:
+                trigger = None
+            if mtype.lower().startswith('response'):
+                if response_trig_shift is not None:
+                    trigger += response_trig_shift
+                else:
+                    trigger = None
         if trigger:
             events.append((onset, duration, trigger))
+        else:
+            if len(mdesc) > 0:
+                dropped.append(mdesc)
+
+    if len(dropped) > 0:
+        dropped = list(set(dropped))
+        examples = ", ".join(dropped[:5])
+        if len(dropped) > 5:
+            examples += ", ..."
+        warn("Currently, {0} trigger(s) will be dropped, such as [{1}]. "
+             "Consider using ``event_id`` to parse triggers that "
+             "do not follow the 'SXXX' pattern.".format(
+                 len(dropped), examples))
 
     events = np.array(events).reshape(-1, 3)
     return events
 
 
-def _synthesize_stim_channel(events, n_samp):
-    """Synthesize a stim channel from events read from a vmrk file
-
-    Parameters
-    ----------
-    events : array, shape (n_events, 3)
-        Each row representing an event as (onset, duration, trigger) sequence
-        (the format returned by _read_vmrk_events).
-    n_samp : int
-        The number of samples.
-
-    Returns
-    -------
-    stim_channel : array, shape (n_samples,)
-        An array containing the whole recording's event marking
-    """
-    # select events overlapping buffer
-    onset = events[:, 0]
-    # create output buffer
-    stim_channel = np.zeros(n_samp, int)
-    for onset, duration, trigger in events:
-        stim_channel[onset:onset + duration] = trigger
-    return stim_channel
-
-
 def _check_hdr_version(header):
     tags = ['Brain Vision Data Exchange Header File Version 1.0',
             'Brain Vision Data Exchange Header File Version 2.0']
@@ -263,7 +268,7 @@ _orientation_dict = dict(MULTIPLEXED='F', VECTORIZED='C')
 _fmt_dict = dict(INT_16='short', INT_32='int', IEEE_FLOAT_32='single')
 _fmt_byte_dict = dict(short=2, int=4, single=4)
 _fmt_dtype_dict = dict(short='<i2', int='<i4', single='<f4')
-_unit_dict = {'V': 1., u'µV': 1e-6}
+_unit_dict = {'V': 1., u'µV': 1e-6, 'uV': 1e-6}
 
 
 def _get_vhdr_info(vhdr_fname, eog, misc, scale, montage):
@@ -340,10 +345,10 @@ def _get_vhdr_info(vhdr_fname, eog, misc, scale, montage):
     fmt = _fmt_dict[fmt]
 
     # load channel labels
-    info['nchan'] = cfg.getint('Common Infos', 'NumberOfChannels') + 1
-    ch_names = [''] * info['nchan']
-    cals = np.empty(info['nchan'])
-    ranges = np.empty(info['nchan'])
+    nchan = cfg.getint('Common Infos', 'NumberOfChannels') + 1
+    ch_names = [''] * nchan
+    cals = np.empty(nchan)
+    ranges = np.empty(nchan)
     cals.fill(np.nan)
     ch_dict = dict()
     for chan, props in cfg.items('Channel Infos'):
@@ -417,9 +422,8 @@ def _get_vhdr_info(vhdr_fname, eog, misc, scale, montage):
                 info['highpass'] = float(highpass[0])
         else:
             info['highpass'] = np.min(np.array(highpass, dtype=np.float))
-            warnings.warn('%s' % ('Channels contain different highpass '
-                                  'filters. Highest filter setting will '
-                                  'be stored.'))
+            warn('Channels contain different highpass filters. Highest filter '
+                 'setting will be stored.')
         if len(lowpass) == 0:
             pass
         elif all(lowpass):
@@ -429,8 +433,8 @@ def _get_vhdr_info(vhdr_fname, eog, misc, scale, montage):
                 info['lowpass'] = float(lowpass[0])
         else:
             info['lowpass'] = np.min(np.array(lowpass, dtype=np.float))
-            warnings.warn('%s' % ('Channels contain different lowpass filters.'
-                                  ' Lowest filter setting will be stored.'))
+            warn('Channels contain different lowpass filters. Lowest filter '
+                 'setting will be stored.')
 
         # Post process highpass and lowpass to take into account units
         header = settings[idx].split('  ')
@@ -449,13 +453,12 @@ def _get_vhdr_info(vhdr_fname, eog, misc, scale, montage):
     # Creates a list of dicts of eeg channels for raw.info
     logger.info('Setting channel info structure...')
     info['chs'] = []
-    info['ch_names'] = ch_names
     for idx, ch_name in enumerate(ch_names):
-        if ch_name in eog or idx in eog or idx - info['nchan'] in eog:
+        if ch_name in eog or idx in eog or idx - nchan in eog:
             kind = FIFF.FIFFV_EOG_CH
             coil_type = FIFF.FIFFV_COIL_NONE
             unit = FIFF.FIFF_UNIT_V
-        elif ch_name in misc or idx in misc or idx - info['nchan'] in misc:
+        elif ch_name in misc or idx in misc or idx - nchan in misc:
             kind = FIFF.FIFFV_MISC_CH
             coil_type = FIFF.FIFFV_COIL_NONE
             unit = FIFF.FIFF_UNIT_V
@@ -475,6 +478,7 @@ def _get_vhdr_info(vhdr_fname, eog, misc, scale, montage):
 
     # for stim channel
     mrk_fname = os.path.join(path, cfg.get('Common Infos', 'MarkerFile'))
+    info._update_redundant()
     info._check_consistency()
     return info, fmt, order, mrk_fname, montage
 
@@ -514,11 +518,12 @@ def read_raw_brainvision(vhdr_fname, montage=None,
         triggers will be ignored. Default is 0 for backwards compatibility, but
         typically another value or None will be necessary.
     event_id : dict | None
-        The id of the event to consider. If None (default),
-        only stimulus events are added to the stimulus channel. If dict,
-        the keys will be mapped to trigger values on the stimulus channel
-        in addition to the stimulus events. Keys are case-sensitive.
-        Example: {'SyncStatus': 1; 'Pulse Artifact': 3}.
+        The id of special events to consider in addition to those that
+        follow the normal Brainvision trigger format ('SXXX').
+        If dict, the keys will be mapped to trigger values on the stimulus
+        channel. Example: {'SyncStatus': 1; 'Pulse Artifact': 3}. If None
+        or an empty dict (default), only stimulus events are added to the
+        stimulus channel. Keys are case sensitive.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
diff --git a/mne/io/brainvision/tests/test_brainvision.py b/mne/io/brainvision/tests/test_brainvision.py
index 621a1f8..8882991 100644
--- a/mne/io/brainvision/tests/test_brainvision.py
+++ b/mne/io/brainvision/tests/test_brainvision.py
@@ -7,6 +7,7 @@ from __future__ import print_function
 
 import os.path as op
 import inspect
+import warnings
 
 from nose.tools import assert_equal, assert_raises, assert_true
 import numpy as np
@@ -30,13 +31,17 @@ montage = op.join(data_dir, 'test.hpts')
 eeg_bin = op.join(data_dir, 'test_bin_raw.fif')
 eog = ['HL', 'HR', 'Vb']
 
+warnings.simplefilter('always')
+
 
 def test_brainvision_data_filters():
     """Test reading raw Brain Vision files
     """
-    raw = _test_raw_reader(read_raw_brainvision,
-                           vhdr_fname=vhdr_highpass_path, montage=montage,
-                           eog=eog)
+    with warnings.catch_warnings(record=True) as w:  # event parsing
+        raw = _test_raw_reader(
+            read_raw_brainvision, vhdr_fname=vhdr_highpass_path,
+            montage=montage, eog=eog)
+    assert_true(all('parse triggers that' in str(ww.message) for ww in w))
 
     assert_equal(raw.info['highpass'], 0.1)
     assert_equal(raw.info['lowpass'], 250.)
@@ -48,9 +53,11 @@ def test_brainvision_data():
     assert_raises(IOError, read_raw_brainvision, vmrk_path)
     assert_raises(ValueError, read_raw_brainvision, vhdr_path, montage,
                   preload=True, scale="foo")
-    raw_py = _test_raw_reader(read_raw_brainvision,
-                              vhdr_fname=vhdr_path, montage=montage, eog=eog)
-
+    with warnings.catch_warnings(record=True) as w:  # event parsing
+        raw_py = _test_raw_reader(
+            read_raw_brainvision, vhdr_fname=vhdr_path, montage=montage,
+            eog=eog)
+    assert_true(all('parse triggers that' in str(ww.message) for ww in w))
     assert_true('RawBrainVision' in repr(raw_py))
 
     assert_equal(raw_py.info['highpass'], 0.)
@@ -88,82 +95,103 @@ def test_events():
     tempdir = _TempDir()
 
     # check that events are read and stim channel is synthesized correcly
-    raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True)
-    events = raw.get_brainvision_events()
-    assert_array_equal(events, [[487, 1, 253],
-                                [497, 1, 255],
-                                [1770, 1, 254],
-                                [1780, 1, 255],
-                                [3253, 1, 254],
-                                [3263, 1, 255],
-                                [4936, 1, 253],
-                                [4946, 1, 255],
-                                [6000, 1, 255],
-                                [6620, 1, 254],
-                                [6630, 1, 255]])
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True)
+        events = raw._get_brainvision_events()
+        assert_array_equal(events, [[487, 1, 253],
+                                    [497, 1, 255],
+                                    [1770, 1, 254],
+                                    [1780, 1, 255],
+                                    [3253, 1, 254],
+                                    [3263, 1, 255],
+                                    [4936, 1, 253],
+                                    [4946, 1, 255],
+                                    [6000, 1, 255],
+                                    [6620, 1, 254],
+                                    [6630, 1, 255]])
+        assert_equal(len(w), 1)  # for dropping Sync & R255 events
 
     # check that events are read and stim channel is synthesized correcly and
     # response triggers are shifted like they're supposed to be.
-    raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True,
-                               response_trig_shift=1000)
-    events = raw.get_brainvision_events()
-    assert_array_equal(events, [[487, 1, 253],
-                                [497, 1, 255],
-                                [1770, 1, 254],
-                                [1780, 1, 255],
-                                [3253, 1, 254],
-                                [3263, 1, 255],
-                                [4936, 1, 253],
-                                [4946, 1, 255],
-                                [6000, 1, 1255],
-                                [6620, 1, 254],
-                                [6630, 1, 255]])
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True,
+                                   response_trig_shift=1000)
+        events = raw._get_brainvision_events()
+        assert_array_equal(events, [[487, 1, 253],
+                                    [497, 1, 255],
+                                    [1770, 1, 254],
+                                    [1780, 1, 255],
+                                    [3253, 1, 254],
+                                    [3263, 1, 255],
+                                    [4936, 1, 253],
+                                    [4946, 1, 255],
+                                    [6000, 1, 1255],
+                                    [6620, 1, 254],
+                                    [6630, 1, 255]])
+        assert_equal(len(w), 1)  # for dropping Sync & R255 events
 
     # check that events are read and stim channel is synthesized correcly and
     # response triggers are ignored.
-    raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True,
-                               response_trig_shift=None)
-    events = raw.get_brainvision_events()
-    assert_array_equal(events, [[487, 1, 253],
-                                [497, 1, 255],
-                                [1770, 1, 254],
-                                [1780, 1, 255],
-                                [3253, 1, 254],
-                                [3263, 1, 255],
-                                [4936, 1, 253],
-                                [4946, 1, 255],
-                                [6620, 1, 254],
-                                [6630, 1, 255]])
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True,
+                                   response_trig_shift=None)
+        events = raw._get_brainvision_events()
+        assert_array_equal(events, [[487, 1, 253],
+                                    [497, 1, 255],
+                                    [1770, 1, 254],
+                                    [1780, 1, 255],
+                                    [3253, 1, 254],
+                                    [3263, 1, 255],
+                                    [4936, 1, 253],
+                                    [4946, 1, 255],
+                                    [6620, 1, 254],
+                                    [6630, 1, 255]])
+        assert_equal(len(w), 1)  # for dropping Sync & R255 events
+
     # check that events are read properly when event_id is specified for
     # auxiliary events
-    raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True,
-                               response_trig_shift=None,
-                               event_id={'Sync On': 5})
-    events = raw.get_brainvision_events()
-    assert_array_equal(events, [[487, 1, 253],
-                                [497, 1, 255],
-                                [1770, 1, 254],
-                                [1780, 1, 255],
-                                [3253, 1, 254],
-                                [3263, 1, 255],
-                                [4936, 1, 253],
-                                [4946, 1, 255],
-                                [6620, 1, 254],
-                                [6630, 1, 255],
-                                [7630, 1, 5]])
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        raw = read_raw_brainvision(vhdr_path, eog=eog, preload=True,
+                                   response_trig_shift=None,
+                                   event_id={'Sync On': 5})
+        events = raw._get_brainvision_events()
+        assert_array_equal(events, [[487, 1, 253],
+                                    [497, 1, 255],
+                                    [1770, 1, 254],
+                                    [1780, 1, 255],
+                                    [3253, 1, 254],
+                                    [3263, 1, 255],
+                                    [4936, 1, 253],
+                                    [4946, 1, 255],
+                                    [6620, 1, 254],
+                                    [6630, 1, 255],
+                                    [7630, 1, 5]])
+        assert_equal(len(w), 1)  # parsing Sync event, missing R255
 
     assert_raises(TypeError, read_raw_brainvision, vhdr_path, eog=eog,
                   preload=True, response_trig_shift=0.1)
     assert_raises(TypeError, read_raw_brainvision, vhdr_path, eog=eog,
                   preload=True, response_trig_shift=np.nan)
 
-    mne_events = find_events(raw, stim_channel='STI 014')
-    assert_array_equal(events[:, [0, 2]], mne_events[:, [0, 2]])
+    # Test that both response_trig_shit and event_id can be set
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        read_raw_brainvision(vhdr_path, eog=eog, preload=False,
+                             response_trig_shift=100,
+                             event_id={'Sync On': 5})
+
+        mne_events = find_events(raw, stim_channel='STI 014')
+        assert_array_equal(events[:, [0, 2]], mne_events[:, [0, 2]])
+        assert_equal(len(w), 0)  # parsing the Sync event
 
     # modify events and check that stim channel is updated
     index = events[:, 2] == 255
     events = events[index]
-    raw.set_brainvision_events(events)
+    raw._set_brainvision_events(events)
     mne_events = find_events(raw, stim_channel='STI 014')
     assert_array_equal(events[:, [0, 2]], mne_events[:, [0, 2]])
 
@@ -171,7 +199,7 @@ def test_events():
     nchan = raw.info['nchan']
     ch_name = raw.info['chs'][-2]['ch_name']
     events = np.empty((0, 3))
-    raw.set_brainvision_events(events)
+    raw._set_brainvision_events(events)
     assert_equal(raw.info['nchan'], nchan)
     assert_equal(len(raw._data), nchan)
     assert_equal(raw.info['chs'][-2]['ch_name'], ch_name)
@@ -182,7 +210,7 @@ def test_events():
 
     # add events back in
     events = [[10, 1, 2]]
-    raw.set_brainvision_events(events)
+    raw._set_brainvision_events(events)
     assert_equal(raw.info['nchan'], nchan)
     assert_equal(len(raw._data), nchan)
     assert_equal(raw.info['chs'][-1]['ch_name'], 'STI 014')
diff --git a/mne/io/bti/bti.py b/mne/io/bti/bti.py
index 96c4477..2c25bf9 100644
--- a/mne/io/bti/bti.py
+++ b/mne/io/bti/bti.py
@@ -9,18 +9,17 @@
 
 import os.path as op
 from itertools import count
-import warnings
 
 import numpy as np
 
-from ...utils import logger, verbose, sum_squared
+from ...utils import logger, verbose, sum_squared, warn
 from ...transforms import (combine_transforms, invert_transform, apply_trans,
                            Transform)
 from ..constants import FIFF
 from .. import _BaseRaw, _coil_trans_to_loc, _loc_to_coil_trans, _empty_info
-from ..utils import _mult_cal_one
+from ..utils import _mult_cal_one, read_str
 from .constants import BTI
-from .read import (read_int32, read_int16, read_str, read_float, read_double,
+from .read import (read_int32, read_int16, read_float, read_double,
                    read_transform, read_char, read_int64, read_uint16,
                    read_uint32, read_double_matrix, read_float_matrix,
                    read_int16_matrix)
@@ -797,7 +796,7 @@ def _read_ch_config(fid):
         chan['transform'] = read_transform(fid)
         chan['reserved'] = read_char(fid, BTI.FILE_CONF_CH_RESERVED)
 
-    elif ch_type in [BTI.CHTYPE_TRIGGER,  BTI.CHTYPE_EXTERNAL,
+    elif ch_type in [BTI.CHTYPE_TRIGGER, BTI.CHTYPE_EXTERNAL,
                      BTI.CHTYPE_UTILITY, BTI.CHTYPE_DERIVED]:
         chan['user_space_size'] = read_int32(fid)
         if ch_type == BTI.CHTYPE_TRIGGER:
@@ -895,7 +894,8 @@ def _read_bti_header_pdf(pdf_fname):
 def _read_bti_header(pdf_fname, config_fname, sort_by_ch_name=True):
     """ Read bti PDF header
     """
-    info = _read_bti_header_pdf(pdf_fname)
+
+    info = _read_bti_header_pdf(pdf_fname) if pdf_fname is not None else dict()
     cfg = _read_config(config_fname)
     info['bti_transform'] = cfg['transforms']
 
@@ -927,10 +927,13 @@ def _read_bti_header(pdf_fname, config_fname, sort_by_ch_name=True):
             ch['loc'] = _coil_trans_to_loc(ch_cfg['dev']['transform'])
         else:
             ch['loc'] = None
-        if info['data_format'] <= 2:  # see DTYPES, implies integer
-            ch['cal'] = ch['scale'] * ch['upb'] / float(ch['gain'])
-        else:  # float
-            ch['cal'] = ch['scale'] * ch['gain']
+        if pdf_fname is not None:
+            if info['data_format'] <= 2:  # see DTYPES, implies integer
+                ch['cal'] = ch['scale'] * ch['upb'] / float(ch['gain'])
+            else:  # float
+                ch['cal'] = ch['scale'] * ch['gain']
+        else:  # if we are in this mode we don't read data, only channel info.
+            ch['cal'] = ch['scale'] = 1.0  # so we put a trivial default value
 
     if sort_by_ch_name:
         by_index = [(i, d['index']) for i, d in enumerate(chans)]
@@ -950,7 +953,7 @@ def _read_bti_header(pdf_fname, config_fname, sort_by_ch_name=True):
         info['order'] = sort_by_name_idx
     else:
         info['chs'] = chans
-        info['order'] = Ellipsis
+        info['order'] = np.arange(len(chans))
 
     # finally add some important fields from the config
     info['e_table'] = cfg['user_blocks'][BTI.UB_B_E_TABLE_USED]
@@ -1016,12 +1019,7 @@ class RawBTi(_BaseRaw):
                  translation=(0.0, 0.02, 0.11), convert=True,
                  rename_channels=True, sort_by_ch_name=True,
                  ecg_ch='E31', eog_ch=('E63', 'E64'),
-                 preload=None, verbose=None):
-        if preload is None:
-            warnings.warn('preload is True by default but will be changed to '
-                          'False in v0.12. Please explicitly set preload.',
-                          DeprecationWarning)
-            preload = True
+                 preload=False, verbose=None):
         info, bti_info = _get_bti_info(
             pdf_fname=pdf_fname, config_fname=config_fname,
             head_shape_fname=head_shape_fname, rotation_x=rotation_x,
@@ -1040,33 +1038,61 @@ class RawBTi(_BaseRaw):
         """Read a segment of data from a file"""
         bti_info = self._raw_extras[fi]
         fname = bti_info['pdf_fname']
+        dtype = bti_info['dtype']
+        n_channels = self.info['nchan']
+        n_bytes = np.dtype(dtype).itemsize
+        data_left = (stop - start) * n_channels
         read_cals = np.empty((bti_info['total_chans'],))
         for ch in bti_info['chs']:
             read_cals[ch['index']] = ch['cal']
+
+        block_size = ((int(100e6) // n_bytes) // n_channels) * n_channels
+        block_size = min(data_left, block_size)
+        # extract data in chunks
         with _bti_open(fname, 'rb') as fid:
             fid.seek(bti_info['bytes_per_slice'] * start, 0)
-            shape = (stop - start, bti_info['total_chans'])
-            count = np.prod(shape)
-            dtype = bti_info['dtype']
-            if isinstance(fid, six.BytesIO):
-                one_orig = np.fromstring(fid.getvalue(), dtype, count)
-            else:
-                one_orig = np.fromfile(fid, dtype, count)
-            one_orig.shape = shape
-            one = np.empty(shape[::-1])
-            for ii, b_i_o in enumerate(bti_info['order']):
-                one[ii] = one_orig[:, b_i_o] * read_cals[b_i_o]
-        _mult_cal_one(data, one, idx, cals, mult)
+            for sample_start in np.arange(0, data_left,
+                                          block_size) // n_channels:
+                count = min(block_size, data_left - sample_start * n_channels)
+                if isinstance(fid, six.BytesIO):
+                    block = np.fromstring(fid.getvalue(), dtype, count)
+                else:
+                    block = np.fromfile(fid, dtype, count)
+                sample_stop = sample_start + count // n_channels
+                shape = (sample_stop - sample_start, bti_info['total_chans'])
+                block.shape = shape
+                data_view = data[:, sample_start:sample_stop]
+                one = np.empty(block.shape[::-1])
+
+                for ii, b_i_o in enumerate(bti_info['order']):
+                    one[ii] = block[:, b_i_o] * read_cals[b_i_o]
+                _mult_cal_one(data_view, one, idx, cals, mult)
 
 
 def _get_bti_info(pdf_fname, config_fname, head_shape_fname, rotation_x,
                   translation, convert, ecg_ch, eog_ch, rename_channels=True,
                   sort_by_ch_name=True):
-    """Helper to read BTI info"""
+    """Helper to read BTI info
+
+    Note. This helper supports partial construction of infos when `pdf_fname`
+    is None. Some datasets, such as the HCP, are shipped as a large collection
+    of zipped files where it can be more efficient to only read the needed
+    information. In such a situation, some information can neither be accessed
+    directly nor guessed based on the `config`.
+
+    These fields will thus be set to None:
+        - 'lowpass'
+        - 'highpass'
+        - 'sfreq'
+        - 'meas_date'
+
+    """
     if pdf_fname is None:
-        raise ValueError('pdf_fname must be a path, not None')
-    if not isinstance(pdf_fname, six.BytesIO):
-        pdf_fname = op.abspath(pdf_fname)
+        logger.info('No pdf_fname passed, trying to construct partial info '
+                    'from config')
+    if pdf_fname is not None and not isinstance(pdf_fname, six.BytesIO):
+        if not op.isabs(pdf_fname):
+            pdf_fname = op.abspath(pdf_fname)
 
     if not isinstance(config_fname, six.BytesIO):
         if not op.isabs(config_fname):
@@ -1111,13 +1137,23 @@ def _get_bti_info(pdf_fname, config_fname, head_shape_fname, rotation_x,
         sfreq = 1. / bti_info['sample_period']
     else:
         sfreq = None
-    info = _empty_info(sfreq)
-    info['buffer_size_sec'] = 1.  # reasonable default for writing
-    date = bti_info['processes'][0]['timestamp']
-    info['meas_date'] = [date, 0]
-    info['nchan'] = len(bti_info['chs'])
 
+    if pdf_fname is not None:
+        info = _empty_info(sfreq)
+        date = bti_info['processes'][0]['timestamp']
+        info['meas_date'] = [date, 0]
+    else:  # these cannot be guessed from config, see docstring
+        info = _empty_info(1.0)
+        info['sfreq'] = None
+        info['lowpass'] = None
+        info['highpass'] = None
+        info['meas_date'] = None
+        bti_info['processes'] = list()
+
+    info['buffer_size_sec'] = 1.  # reasonable default for writing
     # browse processing info for filter specs.
+    hp, lp = ((0.0, info['sfreq'] * 0.4) if pdf_fname is not None else
+              (None, None))
     hp, lp = info['highpass'], info['lowpass']
     for proc in bti_info['processes']:
         if 'filt' in proc['process_type']:
@@ -1133,7 +1169,17 @@ def _get_bti_info(pdf_fname, config_fname, head_shape_fname, rotation_x,
     info['lowpass'] = lp
     chs = []
 
-    bti_ch_names = [ch['name'] for ch in bti_info['chs']]
+    # Note that 'name' and 'chan_label' are not the same.
+    # We want the configured label if out IO parsed it
+    # except for the MEG channels for which we keep the config name
+    bti_ch_names = list()
+    for ch in bti_info['chs']:
+        # we have always relied on 'A' as indicator of MEG data channels.
+        ch_name = ch['name']
+        if not ch_name.startswith('A'):
+            ch_name = ch.get('chan_label', ch_name)
+        bti_ch_names.append(ch_name)
+
     neuromag_ch_names = _rename_channels(
         bti_ch_names, ecg_ch=ecg_ch, eog_ch=eog_ch)
     ch_mapping = zip(bti_ch_names, neuromag_ch_names)
@@ -1206,7 +1252,6 @@ def _get_bti_info(pdf_fname, config_fname, head_shape_fname, rotation_x,
         chs.append(chan_info)
 
     info['chs'] = chs
-    info['ch_names'] = neuromag_ch_names if rename_channels else bti_ch_names
 
     if head_shape_fname:
         logger.info('... Reading digitization points from %s' %
@@ -1258,13 +1303,13 @@ def _get_bti_info(pdf_fname, config_fname, head_shape_fname, rotation_x,
                        colcals=np.ones(mat.shape[1], dtype='>f4'),
                        save_calibrated=0)]
     else:
-        logger.warning('Warning. Currently direct inclusion of 4D weight t'
-                       'ables is not supported. For critical use cases '
-                       '\nplease take into account the MNE command '
-                       '\'mne_create_comp_data\' to include weights as '
-                       'printed out \nby the 4D \'print_table\' routine.')
+        warn('Currently direct inclusion of 4D weight tables is not supported.'
+             ' For critical use cases please take into account the MNE command'
+             ' "mne_create_comp_data" to include weights as printed out by '
+             'the 4D "print_table" routine.')
 
     # check that the info is complete
+    info._update_redundant()
     info._check_consistency()
     return info, bti_info
 
@@ -1274,7 +1319,7 @@ def read_raw_bti(pdf_fname, config_fname='config',
                  head_shape_fname='hs_file', rotation_x=0.,
                  translation=(0.0, 0.02, 0.11), convert=True,
                  rename_channels=True, sort_by_ch_name=True,
-                 ecg_ch='E31', eog_ch=('E63', 'E64'), preload=None,
+                 ecg_ch='E31', eog_ch=('E63', 'E64'), preload=False,
                  verbose=None):
     """ Raw object from 4D Neuroimaging MagnesWH3600 data
 
diff --git a/mne/io/bti/read.py b/mne/io/bti/read.py
index ebc78ce..30ae81b 100644
--- a/mne/io/bti/read.py
+++ b/mne/io/bti/read.py
@@ -2,7 +2,6 @@
 #          simplified BSD-3 license
 
 import numpy as np
-from ...externals.six import b
 
 
 def _unpack_matrix(fid, rows, cols, dtype, out_dtype):
@@ -26,17 +25,6 @@ def _unpack_simple(fid, dtype, out_dtype):
     return out
 
 
-def read_str(fid, count=1):
-    """ Read string """
-    dtype = np.dtype('>S%i' % count)
-    string = fid.read(dtype.itemsize)
-    data = np.fromstring(string, dtype=dtype)[0]
-    bytestr = b('').join([data[0:data.index(b('\x00')) if
-                          b('\x00') in data else count]])
-
-    return str(bytestr.decode('ascii'))  # Return native str type for Py2/3
-
-
 def read_char(fid, count=1):
     " Read character from bti file """
     return _unpack_simple(fid, '>S%s' % count, 'S')
diff --git a/mne/io/bti/tests/test_bti.py b/mne/io/bti/tests/test_bti.py
index 5c4f8f4..50a9f6d 100644
--- a/mne/io/bti/tests/test_bti.py
+++ b/mne/io/bti/tests/test_bti.py
@@ -62,7 +62,7 @@ def test_crop_append():
     y, t = raw[:]
     t0, t1 = 0.25 * t[-1], 0.75 * t[-1]
     mask = (t0 <= t) * (t <= t1)
-    raw_ = raw.crop(t0, t1)
+    raw_ = raw.copy().crop(t0, t1, copy=False)
     y_, _ = raw_[:]
     assert_true(y_.shape[1] == mask.sum())
     assert_true(y_.shape[0] == y.shape[0])
@@ -73,7 +73,8 @@ def test_transforms():
     bti_trans = (0.0, 0.02, 0.11)
     bti_dev_t = Transform('ctf_meg', 'meg', _get_bti_dev_t(0.0, bti_trans))
     for pdf, config, hs, in zip(pdf_fnames, config_fnames, hs_fnames):
-        raw = read_raw_bti(pdf, config, hs, preload=False)
+        with warnings.catch_warnings(record=True):  # weight tables
+            raw = read_raw_bti(pdf, config, hs, preload=False)
         dev_ctf_t = raw.info['dev_ctf_t']
         dev_head_t_old = raw.info['dev_head_t']
         ctf_head_t = raw.info['ctf_head_t']
@@ -101,12 +102,14 @@ def test_raw():
         if op.exists(tmp_raw_fname):
             os.remove(tmp_raw_fname)
         ex = Raw(exported, preload=True)
-        ra = read_raw_bti(pdf, config, hs, preload=False)
+        with warnings.catch_warnings(record=True):  # weight tables
+            ra = read_raw_bti(pdf, config, hs, preload=False)
         assert_true('RawBTi' in repr(ra))
         assert_equal(ex.ch_names[:NCH], ra.ch_names[:NCH])
         assert_array_almost_equal(ex.info['dev_head_t']['trans'],
                                   ra.info['dev_head_t']['trans'], 7)
-        assert_dig_allclose(ex.info, ra.info)
+        with warnings.catch_warnings(record=True):  # headshape
+            assert_dig_allclose(ex.info, ra.info)
         coil1, coil2 = [np.concatenate([d['loc'].flatten()
                         for d in r_.info['chs'][:NCH]])
                         for r_ in (ra, ex)]
@@ -146,14 +149,21 @@ def test_raw():
         os.remove(tmp_raw_fname)
 
 
-def test_info_no_rename_no_reorder():
-    """ Test private renaming and reordering option """
+def test_info_no_rename_no_reorder_no_pdf():
+    """ Test private renaming, reordering and partial construction option """
     for pdf, config, hs in zip(pdf_fnames, config_fnames, hs_fnames):
-        info, bti_info = _get_bti_info(
-            pdf_fname=pdf, config_fname=config, head_shape_fname=hs,
-            rotation_x=0.0, translation=(0.0, 0.02, 0.11), convert=False,
-            ecg_ch='E31', eog_ch=('E63', 'E64'),
-            rename_channels=False, sort_by_ch_name=False)
+        with warnings.catch_warnings(record=True):  # weight tables
+            info, bti_info = _get_bti_info(
+                pdf_fname=pdf, config_fname=config, head_shape_fname=hs,
+                rotation_x=0.0, translation=(0.0, 0.02, 0.11), convert=False,
+                ecg_ch='E31', eog_ch=('E63', 'E64'),
+                rename_channels=False, sort_by_ch_name=False)
+            info2, bti_info = _get_bti_info(
+                pdf_fname=None, config_fname=config, head_shape_fname=hs,
+                rotation_x=0.0, translation=(0.0, 0.02, 0.11), convert=False,
+                ecg_ch='E31', eog_ch=('E63', 'E64'),
+                rename_channels=False, sort_by_ch_name=False)
+
         assert_equal(info['ch_names'],
                      [ch['ch_name'] for ch in info['chs']])
         assert_equal([n for n in info['ch_names'] if n.startswith('A')][:5],
@@ -161,6 +171,46 @@ def test_info_no_rename_no_reorder():
         assert_equal([n for n in info['ch_names'] if n.startswith('A')][-5:],
                      ['A133', 'A158', 'A44', 'A134', 'A216'])
 
+        info = pick_info(info, pick_types(info, meg=True, stim=True,
+                                          resp=True))
+        info2 = pick_info(info2, pick_types(info2, meg=True, stim=True,
+                                            resp=True))
+
+        assert_true(info['sfreq'] is not None)
+        assert_true(info['lowpass'] is not None)
+        assert_true(info['highpass'] is not None)
+        assert_true(info['meas_date'] is not None)
+
+        assert_equal(info2['sfreq'], None)
+        assert_equal(info2['lowpass'], None)
+        assert_equal(info2['highpass'], None)
+        assert_equal(info2['meas_date'], None)
+
+        assert_equal(info['ch_names'], info2['ch_names'])
+        assert_equal(info['ch_names'], info2['ch_names'])
+        for key in ['dev_ctf_t', 'dev_head_t', 'ctf_head_t']:
+            assert_array_equal(info[key]['trans'], info2[key]['trans'])
+
+        assert_array_equal(
+            np.array([ch['loc'] for ch in info['chs']]),
+            np.array([ch['loc'] for ch in info2['chs']]))
+
+    # just check reading data | corner case
+    with warnings.catch_warnings(record=True):  # weight tables
+        raw1 = read_raw_bti(
+            pdf_fname=pdf, config_fname=config, head_shape_fname=None,
+            sort_by_ch_name=False, preload=True)
+        # just check reading data | corner case
+        raw2 = read_raw_bti(
+            pdf_fname=pdf, config_fname=config, head_shape_fname=None,
+            rename_channels=False,
+            sort_by_ch_name=True, preload=True)
+
+    sort_idx = [raw1.bti_ch_labels.index(ch) for ch in raw2.bti_ch_labels]
+    raw1._data = raw1._data[sort_idx]
+    assert_array_equal(raw1._data, raw2._data)
+    assert_array_equal(raw2.bti_ch_labels, raw2.ch_names)
+
 
 def test_no_conversion():
     """ Test bti no-conversion option """
@@ -172,10 +222,12 @@ def test_no_conversion():
         rename_channels=False, sort_by_ch_name=False)
 
     for pdf, config, hs in zip(pdf_fnames, config_fnames, hs_fnames):
-        raw_info, _ = get_info(pdf, config, hs, convert=False)
-        raw_info_con = read_raw_bti(
-            pdf_fname=pdf, config_fname=config, head_shape_fname=hs,
-            convert=True, preload=False).info
+        with warnings.catch_warnings(record=True):  # weight tables
+            raw_info, _ = get_info(pdf, config, hs, convert=False)
+        with warnings.catch_warnings(record=True):  # weight tables
+            raw_info_con = read_raw_bti(
+                pdf_fname=pdf, config_fname=config, head_shape_fname=hs,
+                convert=True, preload=False).info
 
         pick_info(raw_info_con,
                   pick_types(raw_info_con, meg=True, ref_meg=True),
@@ -223,7 +275,8 @@ def test_no_conversion():
 def test_bytes_io():
     """ Test bti bytes-io API """
     for pdf, config, hs in zip(pdf_fnames, config_fnames, hs_fnames):
-        raw = read_raw_bti(pdf, config, hs, convert=True, preload=False)
+        with warnings.catch_warnings(record=True):  # weight tables
+            raw = read_raw_bti(pdf, config, hs, convert=True, preload=False)
 
         with open(pdf, 'rb') as fid:
             pdf = six.BytesIO(fid.read())
@@ -231,7 +284,8 @@ def test_bytes_io():
             config = six.BytesIO(fid.read())
         with open(hs, 'rb') as fid:
             hs = six.BytesIO(fid.read())
-        raw2 = read_raw_bti(pdf, config, hs, convert=True, preload=False)
+        with warnings.catch_warnings(record=True):  # weight tables
+            raw2 = read_raw_bti(pdf, config, hs, convert=True, preload=False)
         repr(raw2)
         assert_array_equal(raw[:][0], raw2[:][0])
 
diff --git a/mne/io/cnt/__init__.py b/mne/io/cnt/__init__.py
new file mode 100644
index 0000000..7f9ac44
--- /dev/null
+++ b/mne/io/cnt/__init__.py
@@ -0,0 +1 @@
+from .cnt import read_raw_cnt
diff --git a/mne/io/cnt/cnt.py b/mne/io/cnt/cnt.py
new file mode 100644
index 0000000..db472f9
--- /dev/null
+++ b/mne/io/cnt/cnt.py
@@ -0,0 +1,371 @@
+"""Conversion tool from Neuroscan CNT to FIF
+"""
+
+# Author: Jaakko Leppakangas <jaeilepp at student.jyu.fi>
+#
+# License: BSD (3-clause)
+from os import path
+import datetime
+import calendar
+
+import numpy as np
+
+from ...utils import warn, verbose
+from ...channels.layout import _topo_to_sphere
+from ..constants import FIFF
+from ..utils import _mult_cal_one, _find_channels, _create_chs
+from ..meas_info import _empty_info
+from ..base import _BaseRaw, _check_update_montage
+from ..utils import read_str
+
+
+def read_raw_cnt(input_fname, montage, eog=(), misc=(), ecg=(), emg=(),
+                 preload=False, verbose=None):
+    """Read CNT data as raw object.
+
+    .. Note::
+        If montage is not provided, the x and y coordinates are read from the
+        file header. Channels that are not assigned with keywords ``eog``,
+        ``ecg``, ``emg`` and ``misc`` are assigned as eeg channels. All the eeg
+        channel locations are fit to a sphere when computing the z-coordinates
+        for the channels. If channels assigned as eeg channels have locations
+        far away from the head (i.e. x and y coordinates don't fit to a
+        sphere), all the channel locations will be distorted. If you are not
+        sure that the channel locations in the header are correct, it is
+        probably safer to use a (standard) montage. See
+        :func:`mne.channels.read_montage`
+
+    Parameters
+    ----------
+    input_fname : str
+        Path to the data file.
+    montage : str | None | instance of montage
+        Path or instance of montage containing electrode positions. If None,
+        xy sensor locations are read from the header (``x_coord`` and
+        ``y_coord`` in ``ELECTLOC``) and fit to a sphere. See the documentation
+        of :func:`mne.channels.read_montage` for more information.
+    eog : list | tuple | 'auto' | 'header'
+        Names of channels or list of indices that should be designated
+        EOG channels. If 'header', VEOG and HEOG channels assigned in the file
+        header are used. If 'auto', channel names containing 'EOG' are used.
+        Defaults to empty tuple.
+    misc : list or tuple
+        Names of channels or list of indices that should be designated
+        MISC channels. Defaults to empty tuple.
+    ecg : list or tuple | 'auto'
+        Names of channels or list of indices that should be designated
+        ECG channels. If 'auto', the channel names containing 'ECG' are used.
+        Defaults to empty tuple.
+    emg : list or tuple
+        Names of channels or list of indices that should be designated
+        EMG channels. If 'auto', the channel names containing 'EMG' are used.
+        Defaults to empty tuple.
+    preload : bool or str (default False)
+        Preload data into memory for data manipulation and faster indexing.
+        If True, the data will be preloaded into memory (fast, requires
+        large amount of memory). If preload is a string, preload is the
+        file name of a memory-mapped file which is used to store the data
+        on the hard drive (slower, requires less memory).
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    Returns
+    -------
+    raw : instance of RawCNT.
+        The raw data.
+
+    See Also
+    --------
+    mne.io.Raw : Documentation of attribute and methods.
+
+    Notes
+    -----
+    .. versionadded:: 0.12
+    """
+    return RawCNT(input_fname, montage=montage, eog=eog, misc=misc, ecg=ecg,
+                  emg=emg, preload=preload, verbose=verbose)
+
+
+def _get_cnt_info(input_fname, eog, ecg, emg, misc):
+    """Helper for reading the cnt header."""
+    data_offset = 900  # Size of the 'SETUP' header.
+    cnt_info = dict()
+    # Reading only the fields of interest. Structure of the whole header at
+    # http://paulbourke.net/dataformats/eeg/
+    with open(input_fname, 'rb', buffering=0) as fid:
+        fid.seek(21)
+        patient_id = read_str(fid, 20)
+        patient_id = int(patient_id) if patient_id.isdigit() else 0
+        fid.seek(121)
+        patient_name = read_str(fid, 20).split()
+        last_name = patient_name[0] if len(patient_name) > 0 else ''
+        first_name = patient_name[-1] if len(patient_name) > 0 else ''
+        fid.seek(2, 1)
+        sex = read_str(fid, 1)
+        if sex == 'M':
+            sex = FIFF.FIFFV_SUBJ_SEX_MALE
+        elif sex == 'F':
+            sex = FIFF.FIFFV_SUBJ_SEX_FEMALE
+        else:  # can be 'U'
+            sex = FIFF.FIFFV_SUBJ_SEX_UNKNOWN
+        hand = read_str(fid, 1)
+        if hand == 'R':
+            hand = FIFF.FIFFV_SUBJ_HAND_RIGHT
+        elif hand == 'L':
+            hand = FIFF.FIFFV_SUBJ_HAND_LEFT
+        else:  # can be 'M' for mixed or 'U'
+            hand = None
+        fid.seek(205)
+        session_label = read_str(fid, 20)
+        session_date = read_str(fid, 10)
+        time = read_str(fid, 12)
+        date = session_date.split('/')
+        if len(date) != 3:
+            meas_date = -1
+        else:
+            if date[2].startswith('9'):
+                date[2] = '19' + date[2]
+            elif len(date[2]) == 2:
+                date[2] = '20' + date[2]
+            time = time.split(':')
+            if len(time) == 3:
+                # Assuming mm/dd/yy
+                date = datetime.datetime(int(date[2]), int(date[0]),
+                                         int(date[1]), int(time[0]),
+                                         int(time[1]), int(time[2]))
+                meas_date = calendar.timegm(date.utctimetuple())
+            else:
+                meas_date = -1
+        if meas_date < 0:
+            warn('  Could not parse meas date from the header. Setting to '
+                 '[0, 0]...')
+            meas_date = 0
+        fid.seek(370)
+        n_channels = np.fromfile(fid, dtype='<u2', count=1)[0]
+        fid.seek(376)
+        sfreq = np.fromfile(fid, dtype='<u2', count=1)[0]
+        if eog == 'header':
+            fid.seek(402)
+            eog = [idx for idx in np.fromfile(fid, dtype='i2', count=2) if
+                   idx >= 0]
+        fid.seek(438)
+        lowpass_toggle = np.fromfile(fid, 'i1', count=1)[0]
+        highpass_toggle = np.fromfile(fid, 'i1', count=1)[0]
+        fid.seek(869)
+        lowcutoff = np.fromfile(fid, dtype='f4', count=1)[0]
+        fid.seek(2, 1)
+        highcutoff = np.fromfile(fid, dtype='f4', count=1)[0]
+
+        fid.seek(886)
+        event_offset = np.fromfile(fid, dtype='<i4', count=1)[0]
+        cnt_info['continuous_seconds'] = np.fromfile(fid, dtype='<f4',
+                                                     count=1)[0]
+        # Channel offset refers to the size of blocks per channel in the file.
+        cnt_info['channel_offset'] = np.fromfile(fid, dtype='<i4', count=1)[0]
+        if cnt_info['channel_offset'] > 1:
+            cnt_info['channel_offset'] //= 2  # Data read as 2 byte ints.
+        else:
+            cnt_info['channel_offset'] = 1
+        n_samples = (event_offset - (900 + 75 * n_channels)) // (2 *
+                                                                 n_channels)
+        ch_names, cals, baselines, chs, pos = (list(), list(), list(), list(),
+                                               list())
+        bads = list()
+        for ch_idx in range(n_channels):  # ELECTLOC fields
+            fid.seek(data_offset + 75 * ch_idx)
+            ch_name = read_str(fid, 10)
+            ch_names.append(ch_name)
+            fid.seek(data_offset + 75 * ch_idx + 4)
+            if np.fromfile(fid, dtype='u1', count=1)[0]:
+                bads.append(ch_name)
+            fid.seek(data_offset + 75 * ch_idx + 19)
+            xy = np.fromfile(fid, dtype='f4', count=2)
+            xy[1] *= -1  # invert y-axis
+            pos.append(xy)
+            fid.seek(data_offset + 75 * ch_idx + 47)
+            # Baselines are subtracted before scaling the data.
+            baselines.append(np.fromfile(fid, dtype='i2', count=1)[0])
+            fid.seek(data_offset + 75 * ch_idx + 59)
+            sensitivity = np.fromfile(fid, dtype='f4', count=1)[0]
+            fid.seek(data_offset + 75 * ch_idx + 71)
+            cal = np.fromfile(fid, dtype='f4', count=1)
+            cals.append(cal * sensitivity * 1e-6 / 204.8)
+
+        fid.seek(event_offset)
+        event_type = np.fromfile(fid, dtype='<i1', count=1)[0]
+        event_size = np.fromfile(fid, dtype='<i4', count=1)[0]
+        if event_type == 1:
+            event_bytes = 8
+        elif event_type == 2:
+            event_bytes = 19
+        else:
+            raise IOError('Unexpected event size.')
+
+        n_events = event_size // event_bytes
+        stim_channel = np.zeros(n_samples)  # Construct stim channel
+        for i in range(n_events):
+            fid.seek(event_offset + 9 + i * event_bytes)
+            event_id = np.fromfile(fid, dtype='u2', count=1)[0]
+            fid.seek(event_offset + 9 + i * event_bytes + 4)
+            offset = np.fromfile(fid, dtype='<i4', count=1)[0]
+            event_time = (offset - 900 - 75 * n_channels) // (n_channels * 2)
+            stim_channel[event_time - 1] = event_id
+
+    info = _empty_info(sfreq)
+    if lowpass_toggle == 1:
+        info['lowpass'] = highcutoff
+    if highpass_toggle == 1:
+        info['highpass'] = lowcutoff
+    subject_info = {'hand': hand, 'id': patient_id, 'sex': sex,
+                    'first_name': first_name, 'last_name': last_name}
+
+    if eog == 'auto':
+        eog = _find_channels(ch_names, 'EOG')
+    if ecg == 'auto':
+        ecg = _find_channels(ch_names, 'ECG')
+    if emg == 'auto':
+        emg = _find_channels(ch_names, 'EMG')
+
+    chs = _create_chs(ch_names, cals, FIFF.FIFFV_COIL_EEG,
+                      FIFF.FIFFV_EEG_CH, eog, ecg, emg, misc)
+    eegs = [idx for idx, ch in enumerate(chs) if
+            ch['coil_type'] == FIFF.FIFFV_COIL_EEG]
+    coords = _topo_to_sphere(pos, eegs)
+    locs = np.zeros((len(chs), 12), dtype=float)
+    locs[:, :3] = coords
+    for ch, loc in zip(chs, locs):
+        ch.update(loc=loc)
+
+    # Add the stim channel.
+    chan_info = {'cal': 1.0, 'logno': len(chs) + 1, 'scanno': len(chs) + 1,
+                 'range': 1.0, 'unit_mul': 0., 'ch_name': 'STI 014',
+                 'unit': FIFF.FIFF_UNIT_NONE,
+                 'coord_frame': FIFF.FIFFV_COORD_UNKNOWN, 'loc': np.zeros(12),
+                 'coil_type': FIFF.FIFFV_COIL_NONE, 'kind': FIFF.FIFFV_STIM_CH}
+    chs.append(chan_info)
+    baselines.append(0)  # For stim channel
+    cnt_info.update(baselines=np.array(baselines), n_samples=n_samples,
+                    stim_channel=stim_channel)
+    info.update(filename=input_fname, meas_date=np.array([meas_date, 0]),
+                description=str(session_label), buffer_size_sec=10., bads=bads,
+                subject_info=subject_info, chs=chs)
+    info._update_redundant()
+    return info, cnt_info
+
+
+class RawCNT(_BaseRaw):
+    """Raw object from Neuroscan CNT file.
+
+    .. Note::
+        If montage is not provided, the x and y coordinates are read from the
+        file header. Channels that are not assigned with keywords ``eog``,
+        ``ecg``, ``emg`` and ``misc`` are assigned as eeg channels. All the eeg
+        channel locations are fit to a sphere when computing the z-coordinates
+        for the channels. If channels assigned as eeg channels have locations
+        far away from the head (i.e. x and y coordinates don't fit to a
+        sphere), all the channel locations will be distorted. If you are not
+        sure that the channel locations in the header are correct, it is
+        probably safer to use a (standard) montage. See
+        :func:`mne.channels.read_montage`
+
+    Parameters
+    ----------
+    input_fname : str
+        Path to the CNT file.
+    montage : str | None | instance of montage
+        Path or instance of montage containing electrode positions. If None,
+        xy sensor locations are read from the header (``x_coord`` and
+        ``y_coord`` in ``ELECTLOC``) and fit to a sphere. See the documentation
+        of :func:`mne.channels.read_montage` for more information.
+    eog : list | tuple
+        Names of channels or list of indices that should be designated
+        EOG channels. If 'auto', the channel names beginning with
+        ``EOG`` are used. Defaults to empty tuple.
+    misc : list or tuple
+        Names of channels or list of indices that should be designated
+        MISC channels. Defaults to empty tuple.
+    ecg : list or tuple
+        Names of channels or list of indices that should be designated
+        ECG channels. If 'auto', the channel names beginning with
+        ``ECG`` are used. Defaults to empty tuple.
+    emg : list or tuple
+        Names of channels or list of indices that should be designated
+        EMG channels. If 'auto', the channel names beginning with
+        ``EMG`` are used. Defaults to empty tuple.
+    preload : bool or str (default False)
+        Preload data into memory for data manipulation and faster indexing.
+        If True, the data will be preloaded into memory (fast, requires
+        large amount of memory). If preload is a string, preload is the
+        file name of a memory-mapped file which is used to store the data
+        on the hard drive (slower, requires less memory).
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    See Also
+    --------
+    mne.io.Raw : Documentation of attribute and methods.
+    """
+    def __init__(self, input_fname, montage, eog=(), misc=(), ecg=(), emg=(),
+                 preload=False, verbose=None):
+        input_fname = path.abspath(input_fname)
+        info, cnt_info = _get_cnt_info(input_fname, eog, ecg, emg, misc)
+        last_samps = [cnt_info['n_samples'] - 1]
+        _check_update_montage(info, montage)
+        super(RawCNT, self).__init__(
+            info, preload, filenames=[input_fname], raw_extras=[cnt_info],
+            last_samps=last_samps, orig_format='int',
+            verbose=verbose)
+
+    @verbose
+    def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
+        """Take a chunk of raw data, multiply by mult or cals, and store"""
+        n_channels = self.info['nchan'] - 1  # Stim channel already read.
+        channel_offset = self._raw_extras[0]['channel_offset']
+        baselines = self._raw_extras[0]['baselines']
+        stim_ch = self._raw_extras[0]['stim_channel']
+        n_bytes = 2
+        sel = np.arange(n_channels + 1)[idx]
+        chunk_size = channel_offset * n_channels  # Size of chunks in file.
+        # The data is divided into blocks of samples / channel.
+        # channel_offset determines the amount of successive samples.
+        # Here we use sample offset to align the data because start can be in
+        # the middle of these blocks.
+        data_left = (stop - start) * n_channels
+        # Read up to 100 MB of data at a time, block_size is in data samples
+        block_size = ((int(100e6) // n_bytes) // chunk_size) * chunk_size
+        block_size = min(data_left, block_size)
+        s_offset = start % channel_offset
+        with open(self._filenames[fi], 'rb', buffering=0) as fid:
+            fid.seek(900 + n_channels * (75 + (start - s_offset) * n_bytes))
+            for sample_start in np.arange(0, data_left,
+                                          block_size) // n_channels:
+                sample_stop = sample_start + min((block_size // n_channels,
+                                                  data_left // n_channels -
+                                                  sample_start))
+                n_samps = sample_stop - sample_start
+                data_ = np.empty((n_channels + 1, n_samps))
+                # In case channel offset and start time do not align perfectly,
+                # extra sample sets are read here to cover the desired time
+                # window. The whole (up to 100 MB) block is read at once and
+                # then reshaped to (n_channels, n_samples).
+                extra_samps = chunk_size if (s_offset != 0 or n_samps %
+                                             channel_offset != 0) else 0
+                if s_offset >= (channel_offset / 2):  # Extend at the end.
+                    extra_samps += chunk_size
+                count = n_samps // channel_offset * chunk_size + extra_samps
+                n_chunks = count // chunk_size
+                samps = np.fromfile(fid, dtype='<i2', count=count)
+                samps = samps.reshape((n_chunks, n_channels, channel_offset),
+                                      order='C')
+                # Intermediate shaping to chunk sizes.
+                block = np.zeros((n_channels + 1, channel_offset * n_chunks))
+                for set_idx, row in enumerate(samps):  # Final shape.
+                    block_slice = slice(set_idx * channel_offset,
+                                        (set_idx + 1) * channel_offset)
+                    block[:-1, block_slice] = row
+                block = block[sel, s_offset:n_samps + s_offset]
+                data_[sel] = block
+                data_[-1] = stim_ch[start + sample_start:start + sample_stop]
+                data_[sel] -= baselines[sel][:, None]
+                _mult_cal_one(data[:, sample_start:sample_stop], data_, idx,
+                              cals, mult=None)
diff --git a/mne/io/cnt/tests/__init__.py b/mne/io/cnt/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mne/io/cnt/tests/test_cnt.py b/mne/io/cnt/tests/test_cnt.py
new file mode 100644
index 0000000..7175a11
--- /dev/null
+++ b/mne/io/cnt/tests/test_cnt.py
@@ -0,0 +1,34 @@
+
+# Author: Jaakko Leppakangas <jaeilepp at student.jyu.fi>
+#
+# License: BSD (3-clause)
+
+import os.path as op
+import warnings
+
+from nose.tools import assert_equal, assert_true
+
+import mne
+from mne.utils import run_tests_if_main
+from mne.datasets import testing
+from mne.io.tests.test_raw import _test_raw_reader
+from mne.io.cnt import read_raw_cnt
+
+warnings.simplefilter('always')
+
+data_path = testing.data_path(download=False)
+fname = op.join(data_path, 'CNT', 'scan41_short.cnt')
+
+
+ at testing.requires_testing_data
+def test_data():
+    """Test reading raw cnt files."""
+    with warnings.catch_warnings(record=True) as w:
+        raw = _test_raw_reader(read_raw_cnt, montage=None, input_fname=fname,
+                               eog='auto', misc=['NA1', 'LEFT_EAR'])
+    assert_true(all('meas date' in str(ww.message) for ww in w))
+    eog_chs = mne.pick_types(raw.info, eog=True, exclude=[])
+    assert_equal(len(eog_chs), 2)  # test eog='auto'
+    assert_equal(raw.info['bads'], ['LEFT_EAR', 'VEOGR'])  # test bads
+
+run_tests_if_main()
diff --git a/mne/io/compensator.py b/mne/io/compensator.py
index 91b38cc..8746c6e 100644
--- a/mne/io/compensator.py
+++ b/mne/io/compensator.py
@@ -48,7 +48,7 @@ def _make_compensator(info, kind):
                     raise ValueError('Ambiguous channel %s' % col_name)
                 presel[col, ind[0]] = 1.0
 
-            #   Create the postselector
+            #   Create the postselector (zero entries for channels not found)
             postsel = np.zeros((info['nchan'], this_data['nrow']))
             for c, ch_name in enumerate(info['ch_names']):
                 ind = [k for k, ch in enumerate(this_data['row_names'])
@@ -57,6 +57,7 @@ def _make_compensator(info, kind):
                     raise ValueError('Ambiguous channel %s' % ch_name)
                 elif len(ind) == 1:
                     postsel[c, ind[0]] = 1.0
+                # else, don't use it at all (postsel[c, ?] = 0.0) by allocation
             this_comp = np.dot(postsel, np.dot(this_data['data'], presel))
             return this_comp
 
diff --git a/mne/io/constants.py b/mne/io/constants.py
index f31a5ab..64d1813 100644
--- a/mne/io/constants.py
+++ b/mne/io/constants.py
@@ -164,20 +164,24 @@ FIFF.FIFFV_NEXT_NONE   = -1
 #
 # Channel types
 #
-FIFF.FIFFV_MEG_CH     =   1
-FIFF.FIFFV_REF_MEG_CH = 301
-FIFF.FIFFV_EEG_CH     =   2
-FIFF.FIFFV_MCG_CH     = 201
-FIFF.FIFFV_STIM_CH    =   3
-FIFF.FIFFV_EOG_CH     = 202
-FIFF.FIFFV_EMG_CH     = 302
-FIFF.FIFFV_ECG_CH     = 402
-FIFF.FIFFV_MISC_CH    = 502
-FIFF.FIFFV_RESP_CH    = 602  # Respiration monitoring
-FIFF.FIFFV_SEEG_CH    = 802  # stereotactic EEG
-FIFF.FIFFV_SYST_CH    = 900  # some system status information (on Triux systems only)
-FIFF.FIFFV_IAS_CH     = 910  # Internal Active Shielding data (maybe on Triux only)
-FIFF.FIFFV_EXCI_CH    = 920  # flux excitation channel used to be a stimulus channel
+FIFF.FIFFV_BIO_CH       = 102
+FIFF.FIFFV_MEG_CH       =   1
+FIFF.FIFFV_REF_MEG_CH   = 301
+FIFF.FIFFV_EEG_CH       =   2
+FIFF.FIFFV_MCG_CH       = 201
+FIFF.FIFFV_STIM_CH      =   3
+FIFF.FIFFV_EOG_CH       = 202
+FIFF.FIFFV_EMG_CH       = 302
+FIFF.FIFFV_ECG_CH       = 402
+FIFF.FIFFV_MISC_CH      = 502
+FIFF.FIFFV_RESP_CH      = 602  # Respiration monitoring
+FIFF.FIFFV_SEEG_CH      = 802  # stereotactic EEG
+FIFF.FIFFV_SYST_CH      = 900  # some system status information (on Triux systems only)
+FIFF.FIFFV_ECOG_CH      = 902
+FIFF.FIFFV_IAS_CH       = 910  # Internal Active Shielding data (maybe on Triux only)
+FIFF.FIFFV_EXCI_CH      = 920  # flux excitation channel used to be a stimulus channel
+FIFF.FIFFV_DIPOLE_WAVE  = 1000  # Dipole time curve (xplotter/xfit)
+FIFF.FIFFV_GOODNESS_FIT = 1001  # Goodness of fit (xplotter/xfit)
 
 #
 # Quaternion channels for head position monitoring
@@ -228,6 +232,13 @@ FIFF.FIFF_SUBJ_HEIGHT       = 408  # Height of the subject
 FIFF.FIFF_SUBJ_COMMENT      = 409  # Comment about the subject
 FIFF.FIFF_SUBJ_HIS_ID       = 410  # ID used in the Hospital Information System
 
+FIFF.FIFFV_SUBJ_HAND_RIGHT  = 1    # Righthanded
+FIFF.FIFFV_SUBJ_HAND_LEFT   = 2    # Lefthanded
+
+FIFF.FIFFV_SUBJ_SEX_UNKNOWN = 0    # Unknown gender
+FIFF.FIFFV_SUBJ_SEX_MALE    = 1    # Male
+FIFF.FIFFV_SUBJ_SEX_FEMALE  = 2    # Female
+
 FIFF.FIFF_PROJ_ID           = 500
 FIFF.FIFF_PROJ_NAME         = 501
 FIFF.FIFF_PROJ_AIM          = 502
@@ -325,6 +336,8 @@ FIFF.FIFF_PROJ_ITEM_NVEC         = 3414
 FIFF.FIFF_PROJ_ITEM_VECTORS      = 3415
 FIFF.FIFF_PROJ_ITEM_DEFINITION   = 3416
 FIFF.FIFF_PROJ_ITEM_CH_NAME_LIST = 3417
+#   XPlotter
+FIFF.FIFF_XPLOTTER_LAYOUT        = 3501  # string - "Xplotter layout tag"
 #
 #   MRIs
 #
@@ -809,3 +822,6 @@ FIFF.FIFF_MNE_RT_CLIENT_ID         = 3701  # realtime client
 # MNE epochs bookkeeping
 FIFF.FIFFB_MNE_EPOCHS_SELECTION    = 3800  # the epochs selection
 FIFF.FIFFB_MNE_EPOCHS_DROP_LOG     = 3801  # the drop log
+
+# MNE annotations
+FIFF.FIFFB_MNE_ANNOTATIONS          = 3810  # annotations
diff --git a/mne/io/ctf/ctf.py b/mne/io/ctf/ctf.py
index cc32a2b..fd7b2fd 100644
--- a/mne/io/ctf/ctf.py
+++ b/mne/io/ctf/ctf.py
@@ -18,7 +18,7 @@ from ..utils import _mult_cal_one, _blk_read_lims
 
 from .res4 import _read_res4, _make_ctf_name
 from .hc import _read_hc
-from .eeg import _read_eeg
+from .eeg import _read_eeg, _read_pos
 from .trans import _make_ctf_coord_trans_set
 from .info import _compose_meas_info
 from .constants import CTF
@@ -105,10 +105,16 @@ class RawCTF(_BaseRaw):
         res4 = _read_res4(directory)  # Read the magical res4 file
         coils = _read_hc(directory)  # Read the coil locations
         eeg = _read_eeg(directory)  # Read the EEG electrode loc info
+
         # Investigate the coil location data to get the coordinate trans
         coord_trans = _make_ctf_coord_trans_set(res4, coils)
+
+        digs = _read_pos(directory, coord_trans)
+
         # Compose a structure which makes fiff writing a piece of cake
         info = _compose_meas_info(res4, coils, coord_trans, eeg)
+        info['dig'] += digs
+
         # Determine how our data is distributed across files
         fnames = list()
         last_samps = list()
@@ -143,13 +149,13 @@ class RawCTF(_BaseRaw):
         with open(self._filenames[fi], 'rb') as fid:
             for bi in range(len(r_lims)):
                 samp_offset = (bi + trial_start_idx) * si['res4_nsamp']
-                n_read = min(si['n_samp'] - samp_offset, si['block_size'])
+                n_read = min(si['n_samp_tot'] - samp_offset, si['block_size'])
                 # read the chunk of data
                 pos = CTF.HEADER_SIZE
                 pos += samp_offset * si['n_chan'] * 4
                 fid.seek(pos, 0)
-                this_data = np.fromstring(
-                    fid.read(si['n_chan'] * n_read * 4), '>i4')
+                this_data = np.fromfile(fid, '>i4',
+                                        count=si['n_chan'] * n_read)
                 this_data.shape = (si['n_chan'], n_read)
                 this_data = this_data[:, r_lims[bi, 0]:r_lims[bi, 1]]
                 data_view = data[:, d_lims[bi, 0]:d_lims[bi, 1]]
diff --git a/mne/io/ctf/eeg.py b/mne/io/ctf/eeg.py
index edfde44..e090fdb 100644
--- a/mne/io/ctf/eeg.py
+++ b/mne/io/ctf/eeg.py
@@ -6,10 +6,13 @@
 # License: BSD (3-clause)
 
 import numpy as np
+from os.path import join
+from os import listdir
 
-from ...utils import logger
+from ...utils import logger, warn
 from ..constants import FIFF
 from .res4 import _make_ctf_name
+from ...transforms import apply_trans
 
 
 _cardinal_dict = dict(nasion=FIFF.FIFFV_POINT_NASION,
@@ -49,3 +52,46 @@ def _read_eeg(directory):
                     eeg['np'] += 1
     logger.info('    Separate EEG position data file read.')
     return eeg
+
+
+def _read_pos(directory, transformations):
+    """Read the .pos file and return eeg positions as digitizer extra points.
+    """
+    fname = [join(directory, f) for f in listdir(directory) if
+             f.endswith('.pos')]
+    if len(fname) < 1:
+        return list()
+    elif len(fname) > 1:
+        warn('    Found multiple pos files. Extra digitizer points not added.')
+        return list()
+    logger.info('    Reading digitizer points from %s...' % fname)
+    if transformations['t_ctf_head_head'] is None:
+        warn('    No transformation found. Extra digitizer points not added.')
+        return list()
+    fname = fname[0]
+    digs = list()
+    i = 2000
+    with open(fname, 'r') as fid:
+        for line in fid:
+            line = line.strip()
+            if len(line) > 0:
+                parts = line.split()
+                # The lines can have 4 or 5 parts. First part is for the id,
+                # which can be an int or a string. The last three are for xyz
+                # coordinates. The extra part is for additional info
+                # (e.g. 'Pz', 'Cz') which is ignored.
+                if len(parts) not in [4, 5]:
+                    continue
+                try:
+                    ident = int(parts[0]) + 1000
+                except ValueError:  # if id is not an int
+                    ident = i
+                    i += 1
+                dig = dict(kind=FIFF.FIFFV_POINT_EXTRA, ident=ident, r=list(),
+                           coord_frame=FIFF.FIFFV_MNE_COORD_CTF_HEAD)
+                r = np.array([float(p) for p in parts[-3:]]) / 100.  # cm to m
+                if (r * r).sum() > 1e-4:
+                    r = apply_trans(transformations['t_ctf_head_head'], r)
+                    dig['r'] = r
+                    digs.append(dig)
+    return digs
diff --git a/mne/io/ctf/info.py b/mne/io/ctf/info.py
index 2a58d9c..36def60 100644
--- a/mne/io/ctf/info.py
+++ b/mne/io/ctf/info.py
@@ -10,7 +10,7 @@ from calendar import timegm
 
 import numpy as np
 
-from ...utils import logger
+from ...utils import logger, warn
 from ...transforms import (apply_trans, _coord_frame_name, invert_transform,
                            combine_transforms)
 
@@ -68,7 +68,12 @@ def _convert_time(date_str, time_str):
         else:
             break
     else:
-        raise RuntimeError("Illegal date: %s" % date)
+        raise RuntimeError(
+            'Illegal date: %s.\nIf the language of the date does not '
+            'correspond to your local machine\'s language try to set the '
+            'locale to the language of the date string:\n'
+            'locale.setlocale(locale.LC_ALL, "en_US")' % date_str)
+
     for fmt in ('%H:%M:%S', '%H:%M'):
         try:
             time = strptime(time_str, fmt)
@@ -77,7 +82,7 @@ def _convert_time(date_str, time_str):
         else:
             break
     else:
-        raise RuntimeError('Illegal time: %s' % time)
+        raise RuntimeError('Illegal time: %s' % time_str)
     # MNE-C uses mktime which uses local time, but here we instead decouple
     # conversion location from the process, and instead assume that the
     # acquisiton was in GMT. This will be wrong for most sites, but at least
@@ -132,6 +137,19 @@ def _convert_channel_info(res4, t, use_eeg_pos):
         if cch['sensor_type_index'] in (CTF.CTFV_REF_MAG_CH,
                                         CTF.CTFV_REF_GRAD_CH,
                                         CTF.CTFV_MEG_CH):
+            # Extra check for a valid MEG channel
+            if np.sum(cch['coil']['pos'][0] ** 2) < 1e-6 or \
+                    np.sum(cch['coil']['norm'][0] ** 2) < 1e-6:
+                nmisc += 1
+                ch.update(logno=nmisc, coord_frame=FIFF.FIFFV_COORD_UNKNOWN,
+                          kind=FIFF.FIFFV_MISC_CH, unit=FIFF.FIFF_UNIT_V)
+                text = 'MEG'
+                if cch['sensor_type_index'] != CTF.CTFV_MEG_CH:
+                    text += ' ref'
+                warn('%s channel %s did not have position assigned, so '
+                     'it was changed to a MISC channel'
+                     % (text, ch['ch_name']))
+                continue
             ch['unit'] = FIFF.FIFF_UNIT_T
             # Set up the local coordinate frame
             pos['r0'][:] = cch['coil']['pos'][0]
@@ -142,24 +160,28 @@ def _convert_channel_info(res4, t, use_eeg_pos):
                 pos['ez'] *= -1
             # Check how the other vectors should be defined
             off_diag = False
+            # Default: ex and ey are arbitrary in the plane normal to ez
             if cch['sensor_type_index'] == CTF.CTFV_REF_GRAD_CH:
+                # The off-diagonal gradiometers are an exception:
+                #
                 # We use the same convention for ex as for Neuromag planar
-                # gradiometers: pointing in the positive gradient direction
+                # gradiometers: ex pointing in the positive gradient direction
                 diff = cch['coil']['pos'][0] - cch['coil']['pos'][1]
                 size = np.sqrt(np.sum(diff * diff))
                 if size > 0.:
                     diff /= size
+                # Is ez normal to the line joining the coils?
                 if np.abs(np.dot(diff, pos['ez'])) < 1e-3:
                     off_diag = True
-                if off_diag:
-                    # The off-diagonal gradiometers are an exception
+                    # Handle the off-diagonal gradiometer coordinate system
                     pos['r0'] -= size * diff / 2.0
                     pos['ex'][:] = diff
                     pos['ey'][:] = np.cross(pos['ez'], pos['ex'])
+                else:
+                    pos['ex'][:], pos['ey'][:] = _get_plane_vectors(pos['ez'])
             else:
-                # ex and ey are arbitrary in the plane normal to ex
                 pos['ex'][:], pos['ey'][:] = _get_plane_vectors(pos['ez'])
-            # Transform into a Neuromag-like coordinate system
+            # Transform into a Neuromag-like device coordinate system
             pos['r0'][:] = apply_trans(t['t_ctf_dev_dev'], pos['r0'])
             for key in ('ex', 'ey', 'ez'):
                 pos[key][:] = apply_trans(t['t_ctf_dev_dev'], pos[key],
@@ -194,31 +216,24 @@ def _convert_channel_info(res4, t, use_eeg_pos):
                 pos['r0'][:] = cch['coil']['pos'][0]
                 if not _at_origin(pos['r0']):
                     if t['t_ctf_head_head'] is None:
-                        logger.warning('EEG electrode (%s) location omitted '
-                                       'because of missing HPI information'
-                                       % (ch['ch_name']))
+                        warn('EEG electrode (%s) location omitted because of '
+                             'missing HPI information' % ch['ch_name'])
                         pos['r0'][:] = np.zeros(3)
                         coord_frame = FIFF.FIFFV_COORD_CTF_HEAD
                     else:
                         pos['r0'][:] = apply_trans(t['t_ctf_head_head'],
                                                    pos['r0'])
             neeg += 1
-            ch['logno'] = neeg
-            ch['kind'] = FIFF.FIFFV_EEG_CH
-            ch['unit'] = FIFF.FIFF_UNIT_V
-            ch['coord_frame'] = coord_frame
+            ch.update(logno=neeg, kind=FIFF.FIFFV_EEG_CH,
+                      unit=FIFF.FIFF_UNIT_V, coord_frame=coord_frame)
         elif cch['sensor_type_index'] == CTF.CTFV_STIM_CH:
             nstim += 1
-            ch['logno'] = nstim
-            ch['kind'] = FIFF.FIFFV_STIM_CH
-            ch['unit'] = FIFF.FIFF_UNIT_V
-            ch['coord_frame'] = FIFF.FIFFV_COORD_UNKNOWN
+            ch.update(logno=nstim, coord_frame=FIFF.FIFFV_COORD_UNKNOWN,
+                      kind=FIFF.FIFFV_STIM_CH, unit=FIFF.FIFF_UNIT_V)
         else:
             nmisc += 1
-            ch['logno'] = nmisc
-            ch['kind'] = FIFF.FIFFV_MISC_CH
-            ch['unit'] = FIFF.FIFF_UNIT_V
-            ch['coord_frame'] = FIFF.FIFFV_COORD_UNKNOWN
+            ch.update(logno=nmisc, coord_frame=FIFF.FIFFV_COORD_UNKNOWN,
+                      kind=FIFF.FIFFV_MISC_CH, unit=FIFF.FIFF_UNIT_V)
     return chs
 
 
@@ -389,13 +404,11 @@ def _compose_meas_info(res4, coils, trans, eeg):
         if trans['t_ctf_head_head'] is not None:
             info['ctf_head_t'] = trans['t_ctf_head_head']
     info['chs'] = _convert_channel_info(res4, trans, eeg is None)
-    info['nchan'] = len(info['chs'])
     info['comps'] = _convert_comp_data(res4)
     if eeg is None:
         # Pick EEG locations from chan info if not read from a separate file
         eeg = _pick_eeg_pos(info)
     _add_eeg_pos(eeg, trans, info)
-    info['ch_names'] = [ch['ch_name'] for ch in info['chs']]
     logger.info('    Measurement info composed.')
-    info._check_consistency()
+    info._update_redundant()
     return info
diff --git a/mne/io/ctf/tests/test_ctf.py b/mne/io/ctf/tests/test_ctf.py
index dca48b1..6edd6f3 100644
--- a/mne/io/ctf/tests/test_ctf.py
+++ b/mne/io/ctf/tests/test_ctf.py
@@ -5,9 +5,10 @@
 import os
 from os import path as op
 import shutil
+import warnings
 
 import numpy as np
-from nose.tools import assert_raises, assert_true
+from nose.tools import assert_raises, assert_true, assert_false
 from numpy.testing import assert_allclose, assert_array_equal, assert_equal
 
 from mne import pick_types
@@ -16,7 +17,7 @@ from mne.transforms import apply_trans
 from mne.io import Raw, read_raw_ctf
 from mne.io.tests.test_raw import _test_raw_reader
 from mne.utils import _TempDir, run_tests_if_main, slow_test
-from mne.datasets import testing
+from mne.datasets import testing, spm_face
 
 ctf_dir = op.join(testing.data_path(download=False), 'CTF')
 ctf_fname_continuous = 'testdata_ctf.ds'
@@ -53,7 +54,9 @@ def test_read_ctf():
     os.mkdir(op.join(temp_dir, 'randpos'))
     ctf_eeg_fname = op.join(temp_dir, 'randpos', ctf_fname_catch)
     shutil.copytree(op.join(ctf_dir, ctf_fname_catch), ctf_eeg_fname)
-    raw = _test_raw_reader(read_raw_ctf, directory=ctf_eeg_fname)
+    with warnings.catch_warnings(record=True) as w:  # reclassified ch
+        raw = _test_raw_reader(read_raw_ctf, directory=ctf_eeg_fname)
+    assert_true(all('MISC channel' in str(ww.message) for ww in w))
     picks = pick_types(raw.info, meg=False, eeg=True)
     pos = np.random.RandomState(42).randn(len(picks), 3)
     fake_eeg_fname = op.join(ctf_eeg_fname, 'catch-alp-good-f.eeg')
@@ -68,7 +71,9 @@ def test_read_ctf():
                 '%0.5f' % x for x in 100 * pos[ii])  # convert to cm
             fid.write(('\t'.join(args) + '\n').encode('ascii'))
     pos_read_old = np.array([raw.info['chs'][p]['loc'][:3] for p in picks])
-    raw = read_raw_ctf(ctf_eeg_fname)  # read modified data
+    with warnings.catch_warnings(record=True) as w:  # reclassified channel
+        raw = read_raw_ctf(ctf_eeg_fname)  # read modified data
+    assert_true(all('MISC channel' in str(ww.message) for ww in w))
     pos_read = np.array([raw.info['chs'][p]['loc'][:3] for p in picks])
     assert_allclose(apply_trans(raw.info['ctf_head_t'], pos), pos_read,
                     rtol=1e-5, atol=1e-5)
@@ -82,7 +87,8 @@ def test_read_ctf():
     shutil.copytree(ctf_eeg_fname, ctf_no_hc_fname)
     remove_base = op.join(ctf_no_hc_fname, op.basename(ctf_fname_catch[:-3]))
     os.remove(remove_base + '.hc')
-    assert_raises(RuntimeError, read_raw_ctf, ctf_no_hc_fname)  # no coord tr
+    with warnings.catch_warnings(record=True):  # no coord tr
+        assert_raises(RuntimeError, read_raw_ctf, ctf_no_hc_fname)
     os.remove(remove_base + '.eeg')
     shutil.copy(op.join(ctf_dir, 'catch-alp-good-f.ds_nohc_raw.fif'),
                 op.join(temp_dir, 'no_hc', 'catch-alp-good-f.ds_raw.fif'))
@@ -91,7 +97,9 @@ def test_read_ctf():
     use_fnames = [op.join(ctf_dir, c) for c in ctf_fnames]
     for fname in use_fnames:
         raw_c = Raw(fname + '_raw.fif', add_eeg_ref=False, preload=True)
-        raw = read_raw_ctf(fname)
+        with warnings.catch_warnings(record=True) as w:  # reclassified ch
+            raw = read_raw_ctf(fname)
+        assert_true(all('MISC channel' in str(ww.message) for ww in w))
 
         # check info match
         assert_array_equal(raw.ch_names, raw_c.ch_names)
@@ -132,10 +140,29 @@ def test_read_ctf():
         for ii, (c1, c2) in enumerate(zip(raw.info['chs'], raw_c.info['chs'])):
             for key in ('kind', 'scanno', 'unit', 'ch_name', 'unit_mul',
                         'range', 'coord_frame', 'coil_type', 'logno'):
-                assert_equal(c1[key], c2[key])
-            for key in ('loc', 'cal'):
+                if c1['ch_name'] == 'RMSP' and \
+                        'catch-alp-good-f' in fname and \
+                        key in ('kind', 'unit', 'coord_frame', 'coil_type',
+                                'logno'):
+                    continue  # XXX see below...
+                assert_equal(c1[key], c2[key], err_msg=key)
+            for key in ('cal',):
                 assert_allclose(c1[key], c2[key], atol=1e-6, rtol=1e-4,
                                 err_msg='raw.info["chs"][%d][%s]' % (ii, key))
+            # XXX 2016/02/24: fixed bug with normal computation that used
+            # to exist, once mne-C tools are updated we should update our FIF
+            # conversion files, then the slices can go away (and the check
+            # can be combined with that for "cal")
+            for key in ('loc',):
+                if c1['ch_name'] == 'RMSP' and 'catch-alp-good-f' in fname:
+                    continue
+                assert_allclose(c1[key][:3], c2[key][:3], atol=1e-6, rtol=1e-4,
+                                err_msg='raw.info["chs"][%d][%s]' % (ii, key))
+                assert_allclose(c1[key][9:12], c2[key][9:12], atol=1e-6,
+                                rtol=1e-4,
+                                err_msg='raw.info["chs"][%d][%s]' % (ii, key))
+        if fname.endswith('catch-alp-good-f.ds'):  # omit points from .pos file
+            raw.info['dig'] = raw.info['dig'][:-10]
         assert_dig_allclose(raw.info, raw_c.info)
 
         # check data match
@@ -159,8 +186,11 @@ def test_read_ctf():
             assert_allclose(raw_read[pick_ch, sl_time][0],
                             raw_c[pick_ch, sl_time][0])
         # all data / preload
-        raw = read_raw_ctf(fname, preload=True)
+        with warnings.catch_warnings(record=True) as w:  # reclassified ch
+            raw = read_raw_ctf(fname, preload=True)
+        assert_true(all('MISC channel' in str(ww.message) for ww in w))
         assert_allclose(raw[:][0], raw_c[:][0])
+    raw.plot(show=False)  # Test plotting with ref_meg channels.
     assert_raises(TypeError, read_raw_ctf, 1)
     assert_raises(ValueError, read_raw_ctf, ctf_fname_continuous + 'foo.ds')
     # test ignoring of system clock
@@ -168,4 +198,16 @@ def test_read_ctf():
     assert_raises(ValueError, read_raw_ctf,
                   op.join(ctf_dir, ctf_fname_continuous), 'foo')
 
+
+ at spm_face.requires_spm_data
+def test_read_spm_ctf():
+    """Test CTF reader with omitted samples."""
+    data_path = spm_face.data_path()
+    raw_fname = op.join(data_path, 'MEG', 'spm',
+                        'SPM_CTF_MEG_example_faces1_3D.ds')
+    raw = read_raw_ctf(raw_fname)
+    extras = raw._raw_extras[0]
+    assert_equal(extras['n_samp'], raw.n_times)
+    assert_false(extras['n_samp'] == extras['n_samp_tot'])
+
 run_tests_if_main()
diff --git a/mne/io/edf/edf.py b/mne/io/edf/edf.py
index bcf6eb2..d1109cd 100644
--- a/mne/io/edf/edf.py
+++ b/mne/io/edf/edf.py
@@ -7,15 +7,14 @@
 #
 # License: BSD (3-clause)
 
-import os
 import calendar
 import datetime
+import os
 import re
-import warnings
 
 import numpy as np
 
-from ...utils import verbose, logger
+from ...utils import verbose, logger, warn
 from ..utils import _blk_read_lims
 from ..base import _BaseRaw, _check_update_montage
 from ..meas_info import _empty_info
@@ -80,8 +79,8 @@ class RawEDF(_BaseRaw):
         _check_update_montage(info, montage)
 
         if bool(annot) != bool(annotmap):
-            warnings.warn(("Stimulus Channel will not be annotated. "
-                           "Both 'annot' and 'annotmap' must be specified."))
+            warn("Stimulus Channel will not be annotated. Both 'annot' and "
+                 "'annotmap' must be specified.")
 
         # Raw attributes
         last_samps = [edf_info['nsamples'] - 1]
@@ -161,14 +160,12 @@ class RawEDF(_BaseRaw):
                             ch_data = np.hstack([ch_data, [0] * n_missing])
                             ch_data = ch_data[r_sidx:r_eidx]
                         elif ci == stim_channel:
-                            if annot and annotmap or \
-                                    tal_channel is not None:
+                            if annot and annotmap or tal_channel is not None:
                                 # don't bother with resampling the stim ch
                                 # because it gets overwritten later on.
                                 ch_data = np.zeros(n_buf_samp)
                             else:
-                                warnings.warn('Interpolating stim channel.'
-                                              ' Events may jitter.')
+                                # Stim channel will be interpolated
                                 oldrange = np.linspace(0, 1, n_samp + 1, True)
                                 newrange = np.linspace(0, 1, buf_len, False)
                                 newrange = newrange[r_sidx:r_eidx]
@@ -310,8 +307,8 @@ def _get_edf_info(fname, stim_channel, annot, annotmap, eog, misc, preload):
         record_length = float(fid.read(8).decode())
         if record_length == 0:
             edf_info['record_length'] = record_length = 1.
-            warnings.warn('Header information is incorrect for record length. '
-                          'Default record length set to 1.')
+            warn('Header information is incorrect for record length. Default '
+                 'record length set to 1.')
         else:
             edf_info['record_length'] = record_length
         nchan = int(fid.read(4).decode())
@@ -430,9 +427,7 @@ def _get_edf_info(fname, stim_channel, annot, annotmap, eog, misc, preload):
     info = _empty_info(sfreq)
     info['filename'] = fname
     info['meas_date'] = calendar.timegm(date.utctimetuple())
-    info['nchan'] = nchan
     info['chs'] = chs
-    info['ch_names'] = ch_names
 
     if highpass.size == 0:
         pass
@@ -444,9 +439,9 @@ def _get_edf_info(fname, stim_channel, annot, annotmap, eog, misc, preload):
         else:
             info['highpass'] = float(highpass[0])
     else:
-        info['highpass'] = float(np.min(highpass))
-        warnings.warn('Channels contain different highpass filters. '
-                      'Highest filter setting will be stored.')
+        info['highpass'] = float(np.max(highpass))
+        warn('Channels contain different highpass filters. Highest filter '
+             'setting will be stored.')
 
     if lowpass.size == 0:
         pass
@@ -457,14 +452,19 @@ def _get_edf_info(fname, stim_channel, annot, annotmap, eog, misc, preload):
             info['lowpass'] = float(lowpass[0])
     else:
         info['lowpass'] = float(np.min(lowpass))
-        warnings.warn('%s' % ('Channels contain different lowpass filters.'
-                              ' Lowest filter setting will be stored.'))
+        warn('Channels contain different lowpass filters. Lowest filter '
+             'setting will be stored.')
 
     # Some keys to be consistent with FIF measurement info
     info['description'] = None
     info['buffer_size_sec'] = 10.
     edf_info['nsamples'] = int(n_records * max_samp)
 
+    # These are the conditions under which a stim channel will be interpolated
+    if stim_channel is not None and not (annot and annotmap) and \
+            tal_channel is None and n_samps[stim_channel] != int(max_samp):
+        warn('Interpolating stim channel. Events may jitter.')
+    info._update_redundant()
     return info, edf_info
 
 
diff --git a/mne/io/edf/tests/test_edf.py b/mne/io/edf/tests/test_edf.py
index 42c7abc..5029476 100644
--- a/mne/io/edf/tests/test_edf.py
+++ b/mne/io/edf/tests/test_edf.py
@@ -19,6 +19,7 @@ from scipy import io
 import numpy as np
 
 from mne import pick_types
+from mne.datasets import testing
 from mne.externals.six import iterbytes
 from mne.utils import _TempDir, run_tests_if_main, requires_pandas
 from mne.io import read_raw_edf, Raw
@@ -40,6 +41,9 @@ edf_uneven_eeglab_path = op.join(data_dir, 'test_uneven_samp.mat')
 edf_stim_channel_path = op.join(data_dir, 'test_edf_stim_channel.edf')
 edf_txt_stim_channel_path = op.join(data_dir, 'test_edf_stim_channel.txt')
 
+data_path = testing.data_path(download=False)
+edf_stim_resamp_path = op.join(data_path, 'EDF', 'test_edf_stim_resamp.edf')
+
 
 eog = ['REOG', 'LEOG', 'IEOG']
 misc = ['EXG1', 'EXG5', 'EXG8', 'M1', 'M2']
@@ -101,6 +105,7 @@ def test_edf_data():
     assert_array_equal(edf_events, events)
 
 
+ at testing.requires_testing_data
 def test_stim_channel():
     """Test reading raw edf files with stim channel"""
     raw_py = read_raw_edf(edf_path, misc=range(-4, 0), stim_channel=139,
@@ -135,6 +140,16 @@ def test_stim_channel():
 
     assert_raises(RuntimeError, read_raw_edf, edf_path, preload=False)
 
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        raw = read_raw_edf(edf_stim_resamp_path, verbose=True)
+    assert_equal(len(w), 1)
+    assert_true('Events may jitter' in str(w[0].message))
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        raw[:]
+    assert_equal(len(w), 0)
+
 
 def test_parse_annotation():
     """Test parsing the tal channel"""
diff --git a/mne/io/eeglab/eeglab.py b/mne/io/eeglab/eeglab.py
index 72f2906..f53e6ae 100644
--- a/mne/io/eeglab/eeglab.py
+++ b/mne/io/eeglab/eeglab.py
@@ -1,16 +1,18 @@
-# Author: Mainak Jas <mainak.jas at telecom-paristech.fr>
+# Authors: Mainak Jas <mainak.jas at telecom-paristech.fr>
+#          Jona Sassenhagen <jona.sassenhagen at gmail.com>
 #
 # License: BSD (3-clause)
 
 import os.path as op
+
 import numpy as np
-import warnings
 
-from ..utils import _read_segments_file, _find_channels
+from ..utils import (_read_segments_file, _find_channels,
+                     _synthesize_stim_channel)
 from ..constants import FIFF
 from ..meas_info import _empty_info, create_info
 from ..base import _BaseRaw, _check_update_montage
-from ...utils import logger, verbose, check_version
+from ...utils import logger, verbose, check_version, warn
 from ...channels.montage import Montage
 from ...epochs import _BaseEpochs
 from ...event import read_events
@@ -21,8 +23,7 @@ CAL = 1e-6
 
 
 def _check_fname(fname):
-    """Check if the file extension is valid.
-    """
+    """Check if the file extension is valid."""
     fmt = str(op.splitext(fname)[-1])
     if fmt == '.dat':
         raise NotImplementedError(
@@ -33,8 +34,7 @@ def _check_fname(fname):
 
 
 def _check_mat_struct(fname):
-    """Check if the mat struct contains 'EEG'.
-    """
+    """Check if the mat struct contains 'EEG'."""
     if not check_version('scipy', '0.12'):
         raise RuntimeError('scipy >= 0.12 must be installed for reading EEGLAB'
                            ' files.')
@@ -51,8 +51,7 @@ def _check_mat_struct(fname):
 
 
 def _to_loc(ll):
-    """Check if location exists.
-    """
+    """Check if location exists."""
     if isinstance(ll, (int, float)) or len(ll) > 0:
         return ll
     else:
@@ -60,10 +59,8 @@ def _to_loc(ll):
 
 
 def _get_info(eeg, montage, eog=()):
-    """Get measurement info.
-    """
+    """Get measurement info."""
     info = _empty_info(sfreq=eeg.srate)
-    info['nchan'] = eeg.nbchan
 
     # add the ch_names and info['chs'][idx]['loc']
     path = None
@@ -85,6 +82,8 @@ def _get_info(eeg, montage, eog=()):
             montage = Montage(np.array(pos), ch_names, kind, selection)
     elif isinstance(montage, string_types):
         path = op.dirname(montage)
+    else:  # if eeg.chanlocs is empty, we still need default chan names
+        ch_names = ["EEG %03d" % ii for ii in range(eeg.nbchan)]
 
     if montage is None:
         info = create_info(ch_names, eeg.srate, ch_types='eeg')
@@ -103,11 +102,11 @@ def _get_info(eeg, montage, eog=()):
         if ch['ch_name'] in eog or idx in eog:
             ch['coil_type'] = FIFF.FIFFV_COIL_NONE
             ch['kind'] = FIFF.FIFFV_EOG_CH
-
     return info
 
 
-def read_raw_eeglab(input_fname, montage=None, preload=False, eog=(),
+def read_raw_eeglab(input_fname, montage=None, eog=(), event_id=None,
+                    event_id_func='strip_to_integer', preload=False,
                     verbose=None):
     """Read an EEGLAB .set file
 
@@ -120,6 +119,28 @@ def read_raw_eeglab(input_fname, montage=None, preload=False, eog=(),
         Path or instance of montage containing electrode positions.
         If None, sensor locations are (0,0,0). See the documentation of
         :func:`mne.channels.read_montage` for more information.
+    eog : list | tuple | 'auto'
+        Names or indices of channels that should be designated EOG channels.
+        If 'auto', the channel names containing ``EOG`` or ``EYE`` are used.
+        Defaults to empty tuple.
+    event_id : dict | None
+        The ids of the events to consider. If None (default), an empty dict is
+        used and ``event_id_func`` (see below) is called on every event value.
+        If dict, the keys will be mapped to trigger values on the stimulus
+        channel and only keys not in ``event_id`` will be handled by
+        ``event_id_func``. Keys are case-sensitive.
+        Example::
+
+            {'SyncStatus': 1; 'Pulse Artifact': 3}
+
+    event_id_func : None | str | callable
+        What to do for events not found in ``event_id``. Must take one ``str``
+        argument and return an ``int``. If string, must be 'strip-to-integer',
+        in which case it defaults to stripping event codes such as "D128" or
+        "S  1" of their non-integer parts and returns the integer.
+        If the event is not in the ``event_id`` and calling ``event_id_func``
+        on it results in a ``TypeError`` (e.g. if ``event_id_func`` is
+        ``None``) or a ``ValueError``, the event is dropped.
     preload : bool or str (default False)
         Preload data into memory for data manipulation and faster indexing.
         If True, the data will be preloaded into memory (fast, requires
@@ -128,10 +149,6 @@ def read_raw_eeglab(input_fname, montage=None, preload=False, eog=(),
         on the hard drive (slower, requires less memory). Note that
         preload=False will be effective only if the data is stored in a
         separate binary file.
-    eog : list | tuple | 'auto'
-        Names or indices of channels that should be designated
-        EOG channels. If 'auto', the channel names containing
-        ``EOG`` or ``EYE`` are used. Defaults to empty tuple.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -149,7 +166,8 @@ def read_raw_eeglab(input_fname, montage=None, preload=False, eog=(),
     mne.io.Raw : Documentation of attribute and methods.
     """
     return RawEEGLAB(input_fname=input_fname, montage=montage, preload=preload,
-                     eog=eog, verbose=verbose)
+                     eog=eog, event_id=event_id, event_id_func=event_id_func,
+                     verbose=verbose)
 
 
 def read_epochs_eeglab(input_fname, events=None, event_id=None, montage=None,
@@ -168,9 +186,13 @@ def read_epochs_eeglab(input_fname, events=None, event_id=None, montage=None,
         in the drop log. If None, it is constructed from the EEGLAB (.set) file
         with each unique event encoded with a different integer.
     event_id : int | list of int | dict | None
-        The id of the event to consider. If dict,
-        the keys can later be used to acces associated events. Example:
-        dict(auditory=1, visual=3). If int, a dict will be created with
+        The id of the event to consider. If dict, the keys can later be used
+        to access associated events.
+        Example::
+
+            {"auditory":1, "visual":3}
+
+        If int, a dict will be created with
         the id as string. If a list, all events with the IDs specified
         in the list are used. If None, the event_id is constructed from the
         EEGLAB (.set) file with each descriptions copied from `eventtype`.
@@ -179,9 +201,9 @@ def read_epochs_eeglab(input_fname, events=None, event_id=None, montage=None,
         If None, sensor locations are (0,0,0). See the documentation of
         :func:`mne.channels.read_montage` for more information.
     eog : list | tuple | 'auto'
-        Names or indices of channels that should be designated
-        EOG channels. If 'auto', the channel names containing
-        ``EOG`` or ``EYE`` are used. Defaults to empty tuple.
+        Names or indices of channels that should be designated EOG channels.
+        If 'auto', the channel names containing ``EOG`` or ``EYE`` are used.
+        Defaults to empty tuple.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -213,19 +235,37 @@ class RawEEGLAB(_BaseRaw):
         Path to the .set file. If the data is stored in a separate .fdt file,
         it is expected to be in the same folder as the .set file.
     montage : str | None | instance of montage
-        Path or instance of montage containing electrode positions.
-        If None, sensor locations are (0,0,0). See the documentation of
+        Path or instance of montage containing electrode positions. If None,
+        sensor locations are (0,0,0). See the documentation of
         :func:`mne.channels.read_montage` for more information.
+    eog : list | tuple | 'auto'
+        Names or indices of channels that should be designated EOG channels.
+        If 'auto', the channel names containing ``EOG`` or ``EYE`` are used.
+        Defaults to empty tuple.
+    event_id : dict | None
+        The ids of the events to consider. If None (default), an empty dict is
+        used and ``event_id_func`` (see below) is called on every event value.
+        If dict, the keys will be mapped to trigger values on the stimulus
+        channel and only keys not in ``event_id`` will be handled by
+        ``event_id_func``. Keys are case-sensitive.
+        Example::
+
+            {'SyncStatus': 1; 'Pulse Artifact': 3}
+
+    event_id_func : None | str | callable
+        What to do for events not found in ``event_id``. Must take one ``str``
+        argument and return an ``int``. If string, must be 'strip-to-integer',
+        in which case it defaults to stripping event codes such as "D128" or
+        "S  1" of their non-integer parts and returns the integer.
+        If the event is not in the ``event_id`` and calling ``event_id_func``
+        on it results in a ``TypeError`` (e.g. if ``event_id_func`` is
+        ``None``) or a ``ValueError``, the event is dropped.
     preload : bool or str (default False)
         Preload data into memory for data manipulation and faster indexing.
-        If True, the data will be preloaded into memory (fast, requires
-        large amount of memory). If preload is a string, preload is the
-        file name of a memory-mapped file which is used to store the data
-        on the hard drive (slower, requires less memory).
-    eog : list | tuple | 'auto'
-        Names or indices of channels that should be designated
-        EOG channels. If 'auto', the channel names containing
-        ``EOG`` or ``EYE`` are used. Defaults to empty tuple.
+        If True, the data will be preloaded into memory (fast, requires large
+        amount of memory). If preload is a string, preload is the file name of
+        a memory-mapped file which is used to store the data on the hard
+        drive (slower, requires less memory).
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -243,7 +283,8 @@ class RawEEGLAB(_BaseRaw):
     mne.io.Raw : Documentation of attribute and methods.
     """
     @verbose
-    def __init__(self, input_fname, montage, preload=False, eog=(),
+    def __init__(self, input_fname, montage, eog=(), event_id=None,
+                 event_id_func='strip_to_integer', preload=False,
                  verbose=None):
         """Read EEGLAB .set file.
         """
@@ -259,6 +300,19 @@ class RawEEGLAB(_BaseRaw):
 
         last_samps = [eeg.pnts - 1]
         info = _get_info(eeg, montage, eog=eog)
+
+        stim_chan = dict(ch_name='STI 014', coil_type=FIFF.FIFFV_COIL_NONE,
+                         kind=FIFF.FIFFV_STIM_CH, logno=len(info["chs"]) + 1,
+                         scanno=len(info["chs"]) + 1, cal=1., range=1.,
+                         loc=np.zeros(12), unit=FIFF.FIFF_UNIT_NONE,
+                         unit_mul=0., coord_frame=FIFF.FIFFV_COORD_UNKNOWN)
+        info['chs'].append(stim_chan)
+        info._update_redundant()
+
+        events = _read_eeglab_events(eeg, event_id=event_id,
+                                     event_id_func=event_id_func)
+        self._create_event_ch(events, n_samples=eeg.pnts)
+
         # read the data
         if isinstance(eeg.data, string_types):
             data_fname = op.join(basedir, eeg.data)
@@ -270,22 +324,35 @@ class RawEEGLAB(_BaseRaw):
                 orig_format='double', verbose=verbose)
         else:
             if preload is False or isinstance(preload, string_types):
-                warnings.warn('Data will be preloaded. preload=False or a'
-                              ' string preload is not supported when the data'
-                              ' is stored in the .set file')
+                warn('Data will be preloaded. preload=False or a string '
+                     'preload is not supported when the data is stored in '
+                     'the .set file')
             # can't be done in standard way with preload=True because of
             # different reading path (.set file)
-            data = eeg.data.reshape(eeg.nbchan, -1, order='F')
-            data = data.astype(np.double)
+            n_chan, n_times = eeg.data.shape
+            data = np.empty((n_chan + 1, n_times), dtype=np.double)
+            data[:-1] = eeg.data
             data *= CAL
+            data[-1] = self._event_ch
             super(RawEEGLAB, self).__init__(
                 info, data, last_samps=last_samps, orig_format='double',
                 verbose=verbose)
 
+    def _create_event_ch(self, events, n_samples=None):
+        """Create the event channel"""
+        if n_samples is None:
+            n_samples = self.last_samp - self.first_samp + 1
+        events = np.array(events, int)
+        if events.ndim != 2 or events.shape[1] != 3:
+            raise ValueError("[n_events x 3] shaped array required")
+        # update events
+        self._event_ch = _synthesize_stim_channel(events, n_samples)
+
     def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
         """Read a chunk of raw data"""
         _read_segments_file(self, data, idx, fi, start, stop, cals, mult,
-                            dtype=np.float32)
+                            dtype=np.float32, trigger_ch=self._event_ch,
+                            n_channels=self.info['nchan'] - 1)
 
 
 class EpochsEEGLAB(_BaseEpochs):
@@ -304,11 +371,11 @@ class EpochsEEGLAB(_BaseEpochs):
         with each unique event encoded with a different integer.
     event_id : int | list of int | dict | None
         The id of the event to consider. If dict,
-        the keys can later be used to acces associated events. Example:
+        the keys can later be used to access associated events. Example:
         dict(auditory=1, visual=3). If int, a dict will be created with
         the id as string. If a list, all events with the IDs specified
         in the list are used. If None, the event_id is constructed from the
-        EEGLAB (.set) file with each descriptions copied from `eventtype`.
+        EEGLAB (.set) file with each descriptions copied from ``eventtype``.
     tmin : float
         Start time before event.
     baseline : None or tuple of length 2 (default (None, 0))
@@ -328,8 +395,8 @@ class EpochsEEGLAB(_BaseEpochs):
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels)
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels)
                           )
     flat : dict | None
         Rejection parameters based on flatness of signal.
@@ -347,9 +414,9 @@ class EpochsEEGLAB(_BaseEpochs):
         If None, sensor locations are (0,0,0). See the documentation of
         :func:`mne.channels.read_montage` for more information.
     eog : list | tuple | 'auto'
-        Names or indices of channels that should be designated
-        EOG channels. If 'auto', the channel names containing
-        ``EOG`` or ``EYE`` are used. Defaults to empty tuple.
+        Names or indices of channels that should be designated EOG channels.
+        If 'auto', the channel names containing ``EOG`` or ``EYE`` are used.
+        Defaults to empty tuple.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -386,9 +453,8 @@ class EpochsEEGLAB(_BaseEpochs):
                     # store latency of only first event
                     event_latencies.append(eeg.event[ev_idx].latency)
                     ev_idx += len(ep.eventtype)
-                    warnings.warn('An epoch has multiple events. '
-                                  'Only the latency of first event will be '
-                                  'retained.')
+                    warn('An epoch has multiple events. Only the latency of '
+                         'the first event will be retained.')
                 else:
                     event_type = ep.eventtype
                     event_name.append(ep.eventtype)
@@ -445,3 +511,71 @@ class EpochsEEGLAB(_BaseEpochs):
             reject=reject, flat=flat, reject_tmin=reject_tmin,
             reject_tmax=reject_tmax, add_eeg_ref=False, verbose=verbose)
         logger.info('Ready.')
+
+
+def _read_eeglab_events(eeg, event_id=None, event_id_func='strip_to_integer'):
+    """Create events array from EEGLAB structure
+
+    An event array is constructed by looking up events in the
+    event_id, trying to reduce them to their integer part otherwise, and
+    entirely dropping them (with a warning) if this is impossible.
+    Returns a 1x3 array of zeros if no events are found."""
+    if event_id_func is 'strip_to_integer':
+        event_id_func = _strip_to_integer
+    if event_id is None:
+        event_id = dict()
+
+    if isinstance(eeg.event, np.ndarray):
+        types = [event.type for event in eeg.event]
+        latencies = [event.latency for event in eeg.event]
+    else:
+        # only one event - TypeError: 'mat_struct' object is not iterable
+        types = [eeg.event.type]
+        latencies = [eeg.event.latency]
+    if "boundary" in types and "boundary" not in event_id:
+        warn("The data contains 'boundary' events, indicating data "
+             "discontinuities. Be cautious of filtering and epoching around "
+             "these events.")
+
+    not_in_event_id = set(x for x in types if x not in event_id)
+    not_purely_numeric = set(x for x in not_in_event_id if not x.isdigit())
+    no_numbers = set([x for x in not_purely_numeric
+                      if not any([d.isdigit() for d in x])])
+    have_integers = set([x for x in not_purely_numeric
+                         if x not in no_numbers])
+    if len(not_purely_numeric) > 0:
+        basewarn = "Events like the following will be dropped"
+        n_no_numbers, n_have_integers = len(no_numbers), len(have_integers)
+        if n_no_numbers > 0:
+            no_num_warm = " entirely: {0}, {1} in total"
+            warn(basewarn + no_num_warm.format(list(no_numbers)[:5],
+                                               n_no_numbers))
+        if n_have_integers > 0 and event_id_func is None:
+            intwarn = (", but could be reduced to their integer part "
+                       "instead with the default `event_id_func`: "
+                       "{0}, {1} in total")
+            warn(basewarn + intwarn.format(list(have_integers)[:5],
+                                           n_have_integers))
+
+    events = list()
+    for tt, latency in zip(types, latencies):
+        try:  # look up the event in event_id and if not, try event_id_func
+            event_code = event_id[tt] if tt in event_id else event_id_func(tt)
+            events.append([int(latency), 1, event_code])
+        except (ValueError, TypeError):  # if event_id_func fails
+            pass  # We're already raising warnings above, so we just drop
+
+    if len(events) < len(types):
+        warn("Some event codes could not be mapped to integers. Use the "
+             "`event_id` parameter to map such events to integers manually.")
+    if len(events) < 1:
+        warn("No events found, consider adding an `event_id`. As is, the "
+             "trigger channel will consist entirely of zeros.")
+        return np.zeros((0, 3))
+    else:
+        return np.asarray(events)
+
+
+def _strip_to_integer(trigger):
+    """Return only the integer part of a string."""
+    return int("".join([x for x in trigger if x.isdigit()]))
diff --git a/mne/io/eeglab/tests/test_eeglab.py b/mne/io/eeglab/tests/test_eeglab.py
index 3e5a65e..6ca046a 100644
--- a/mne/io/eeglab/tests/test_eeglab.py
+++ b/mne/io/eeglab/tests/test_eeglab.py
@@ -9,7 +9,7 @@ import warnings
 from nose.tools import assert_raises, assert_equal
 from numpy.testing import assert_array_equal
 
-from mne import write_events, read_epochs_eeglab
+from mne import write_events, read_epochs_eeglab, Epochs, find_events
 from mne.io import read_raw_eeglab
 from mne.io.tests.test_raw import _test_raw_reader
 from mne.datasets import testing
@@ -30,17 +30,37 @@ warnings.simplefilter('always')  # enable b/c these tests throw warnings
 def test_io_set():
     """Test importing EEGLAB .set files"""
     from scipy import io
-
-    _test_raw_reader(read_raw_eeglab, input_fname=raw_fname, montage=montage)
-    with warnings.catch_warnings(record=True) as w:
+    with warnings.catch_warnings(record=True) as w1:
         warnings.simplefilter('always')
+        # main tests, and test missing event_id
+        _test_raw_reader(read_raw_eeglab, input_fname=raw_fname,
+                         montage=montage)
         _test_raw_reader(read_raw_eeglab, input_fname=raw_fname_onefile,
                          montage=montage)
-        raw = read_raw_eeglab(input_fname=raw_fname_onefile, montage=montage)
-        raw2 = read_raw_eeglab(input_fname=raw_fname, montage=montage)
-        assert_array_equal(raw[:][0], raw2[:][0])
-    # one warning per each preload=False or str with raw_fname_onefile
-    assert_equal(len(w), 3)
+        assert_equal(len(w1), 20)
+        # f3 or preload_false and a lot for dropping events
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        # test finding events in continuous data
+        event_id = {'rt': 1, 'square': 2}
+        raw0 = read_raw_eeglab(input_fname=raw_fname, montage=montage,
+                               event_id=event_id, preload=True)
+        raw1 = read_raw_eeglab(input_fname=raw_fname, montage=montage,
+                               event_id=event_id, preload=False)
+        raw2 = read_raw_eeglab(input_fname=raw_fname_onefile, montage=montage,
+                               event_id=event_id)
+        raw3 = read_raw_eeglab(input_fname=raw_fname, montage=montage,
+                               event_id=event_id)
+        raw4 = read_raw_eeglab(input_fname=raw_fname, montage=montage)
+        Epochs(raw0, find_events(raw0), event_id)
+        epochs = Epochs(raw1, find_events(raw1), event_id)
+        assert_equal(len(find_events(raw4)), 0)  # no events without event_id
+        assert_equal(epochs["square"].average().nave, 80)  # 80 with
+        assert_array_equal(raw0[:][0], raw1[:][0], raw2[:][0], raw3[:][0])
+        assert_array_equal(raw0[:][-1], raw1[:][-1], raw2[:][-1], raw3[:][-1])
+        assert_equal(len(w), 4)
+        # 1 for preload=False / str with fname_onefile, 3 for dropped events
+        raw0.filter(1, None)  # test that preloading works
 
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
@@ -64,6 +84,21 @@ def test_io_set():
     assert_raises(ValueError, read_epochs_eeglab, epochs_fname,
                   epochs.events, None)
 
+    # test reading file with one event
+    eeg = io.loadmat(raw_fname, struct_as_record=False,
+                     squeeze_me=True)['EEG']
+    one_event_fname = op.join(temp_dir, 'test_one_event.set')
+    io.savemat(one_event_fname, {'EEG':
+               {'trials': eeg.trials, 'srate': eeg.srate,
+                'nbchan': eeg.nbchan, 'data': 'test_one_event.fdt',
+                'epoch': eeg.epoch, 'event': eeg.event[0],
+                'chanlocs': eeg.chanlocs, 'pnts': eeg.pnts}})
+    shutil.copyfile(op.join(base_dir, 'test_raw.fdt'),
+                    op.join(temp_dir, 'test_one_event.fdt'))
+    event_id = {eeg.event[0].type: 1}
+    read_raw_eeglab(input_fname=one_event_fname, montage=montage,
+                    event_id=event_id, preload=True)
+
     # test if .dat file raises an error
     eeg = io.loadmat(epochs_fname, struct_as_record=False,
                      squeeze_me=True)['EEG']
diff --git a/mne/io/egi/egi.py b/mne/io/egi/egi.py
index c2bc13b..e270dce 100644
--- a/mne/io/egi/egi.py
+++ b/mne/io/egi/egi.py
@@ -5,15 +5,14 @@
 
 import datetime
 import time
-import warnings
 
 import numpy as np
 
 from ..base import _BaseRaw, _check_update_montage
-from ..utils import _mult_cal_one
+from ..utils import _read_segments_file, _create_chs
 from ..meas_info import _empty_info
 from ..constants import FIFF
-from ...utils import verbose, logger
+from ...utils import verbose, logger, warn
 
 
 def _read_header(fid):
@@ -65,7 +64,7 @@ def _read_header(fid):
             info['event_codes'].append(event_codes)
         info['event_codes'] = np.array(info['event_codes'])
     else:
-        raise NotImplementedError('Only continous files are supported')
+        raise NotImplementedError('Only continuous files are supported')
     info['unsegmented'] = unsegmented
     info['dtype'], info['orig_format'] = {2: ('>i2', 'short'),
                                           4: ('>f4', 'float'),
@@ -105,7 +104,7 @@ def _combine_triggers(data, remapping=None):
 
 @verbose
 def read_raw_egi(input_fname, montage=None, eog=None, misc=None,
-                 include=None, exclude=None, preload=None, verbose=None):
+                 include=None, exclude=None, preload=False, verbose=None):
     """Read EGI simple binary as raw object
 
     .. note:: The trigger channel names are based on the
@@ -177,12 +176,7 @@ class RawEGI(_BaseRaw):
     """
     @verbose
     def __init__(self, input_fname, montage=None, eog=None, misc=None,
-                 include=None, exclude=None, preload=None, verbose=None):
-        if preload is None:
-            warnings.warn('preload is True by default but will be changed to '
-                          'False in v0.12. Please explicitly set preload.',
-                          DeprecationWarning)
-            preload = True
+                 include=None, exclude=None, preload=False, verbose=None):
         if eog is None:
             eog = []
         if misc is None:
@@ -211,8 +205,8 @@ class RawEGI(_BaseRaw):
                         if event.sum() <= 1 and event_codes[ii]:
                             more_excludes.append(ii)
                 if len(exclude_inds) + len(more_excludes) == len(event_codes):
-                    warnings.warn('Did not find any event code with more '
-                                  'than one event.', RuntimeWarning)
+                    warn('Did not find any event code with more than one '
+                         'event.', RuntimeWarning)
                 else:
                     exclude_inds.extend(more_excludes)
 
@@ -260,27 +254,20 @@ class RawEGI(_BaseRaw):
         ch_names.extend(list(egi_info['event_codes']))
         if self._new_trigger is not None:
             ch_names.append('STI 014')  # our new_trigger
-        info['nchan'] = nchan = len(ch_names)
-        info['ch_names'] = ch_names
-        for ii, ch_name in enumerate(ch_names):
-            ch_info = {
-                'cal': cal, 'logno': ii + 1, 'scanno': ii + 1, 'range': 1.0,
-                'unit_mul': 0, 'ch_name': ch_name, 'unit': FIFF.FIFF_UNIT_V,
-                'coord_frame': FIFF.FIFFV_COORD_HEAD,
-                'coil_type': FIFF.FIFFV_COIL_EEG, 'kind': FIFF.FIFFV_EEG_CH,
-                'loc': np.array([0, 0, 0, 1] * 3, dtype='f4')}
-            if ch_name in eog or ii in eog or ii - nchan in eog:
-                ch_info.update(coil_type=FIFF.FIFFV_COIL_NONE,
-                               kind=FIFF.FIFFV_EOG_CH)
-            if ch_name in misc or ii in misc or ii - nchan in misc:
-                ch_info.update(coil_type=FIFF.FIFFV_COIL_NONE,
-                               kind=FIFF.FIFFV_MISC_CH)
-            if len(ch_name) == 4 or ch_name.startswith('STI'):
-                ch_info.update(
-                    {'unit_mul': 0, 'cal': 1, 'kind': FIFF.FIFFV_STIM_CH,
-                     'coil_type': FIFF.FIFFV_COIL_NONE,
-                     'unit': FIFF.FIFF_UNIT_NONE})
-            info['chs'].append(ch_info)
+        nchan = len(ch_names)
+        cals = np.repeat(cal, nchan)
+        ch_coil = FIFF.FIFFV_COIL_EEG
+        ch_kind = FIFF.FIFFV_EEG_CH
+        chs = _create_chs(ch_names, cals, ch_coil, ch_kind, eog, (), (), misc)
+        sti_ch_idx = [i for i, name in enumerate(ch_names) if
+                      name.startswith('STI') or len(name) == 4]
+        for idx in sti_ch_idx:
+            chs[idx].update({'unit_mul': 0, 'cal': 1,
+                             'kind': FIFF.FIFFV_STIM_CH,
+                             'coil_type': FIFF.FIFFV_COIL_NONE,
+                             'unit': FIFF.FIFF_UNIT_NONE})
+        info['chs'] = chs
+        info._update_redundant()
         _check_update_montage(info, montage)
         super(RawEGI, self).__init__(
             info, preload, orig_format=egi_info['orig_format'],
@@ -290,18 +277,9 @@ class RawEGI(_BaseRaw):
     def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
         """Read a segment of data from a file"""
         egi_info = self._raw_extras[fi]
+        dtype = egi_info['dtype']
         n_chan_read = egi_info['n_channels'] + egi_info['n_events']
-        data_start = (36 + egi_info['n_events'] * 4 +
-                      start * n_chan_read * egi_info['dtype'].itemsize)
-        n_chan_out = n_chan_read + (1 if self._new_trigger is not None else 0)
-        one = np.empty((n_chan_out, stop - start))
-        with open(self._filenames[fi], 'rb') as fid:
-            fid.seek(data_start, 0)  # skip header
-            final_shape = (stop - start, n_chan_read)
-            one_ = np.fromfile(fid, egi_info['dtype'], np.prod(final_shape))
-            one_.shape = final_shape
-            one[:n_chan_read] = one_.T
-        # reads events as well
-        if self._new_trigger is not None:
-            one[-1] = self._new_trigger[start:stop]
-        _mult_cal_one(data, one, idx, cals, mult)
+        offset = 36 + egi_info['n_events'] * 4
+        _read_segments_file(self, data, idx, fi, start, stop, cals, mult,
+                            dtype=dtype, n_channels=n_chan_read, offset=offset,
+                            trigger_ch=self._new_trigger)
diff --git a/mne/io/egi/tests/test_egi.py b/mne/io/egi/tests/test_egi.py
index c038a8a..2041280 100644
--- a/mne/io/egi/tests/test_egi.py
+++ b/mne/io/egi/tests/test_egi.py
@@ -36,11 +36,10 @@ def test_io_egi():
         warnings.simplefilter('always')
         raw = read_raw_egi(egi_fname, include=None)
         assert_true('RawEGI' in repr(raw))
-        assert_equal(len(w), 2)
-        assert_true(w[0].category == DeprecationWarning)  # preload=None
-        assert_true(w[1].category == RuntimeWarning)
+        assert_equal(len(w), 1)
+        assert_true(w[0].category == RuntimeWarning)
         msg = 'Did not find any event code with more than one event.'
-        assert_true(msg in '%s' % w[1].message)
+        assert_true(msg in '%s' % w[0].message)
     data_read, t_read = raw[:256]
     assert_allclose(t_read, t)
     assert_allclose(data_read, data, atol=1e-10)
diff --git a/mne/io/fiff/__init__.py b/mne/io/fiff/__init__.py
index 1a9952e..a90a4af 100644
--- a/mne/io/fiff/__init__.py
+++ b/mne/io/fiff/__init__.py
@@ -1,2 +1,2 @@
-from .raw import RawFIF
+from .raw import Raw
 from .raw import read_raw_fif
diff --git a/mne/io/fiff/raw.py b/mne/io/fiff/raw.py
index 78b8374..64f588e 100644
--- a/mne/io/fiff/raw.py
+++ b/mne/io/fiff/raw.py
@@ -7,7 +7,6 @@
 # License: BSD (3-clause)
 
 import copy
-import warnings
 import os
 import os.path as op
 
@@ -23,11 +22,13 @@ from ..compensator import get_current_comp, set_current_comp, make_compensator
 from ..base import _BaseRaw, _RawShell, _check_raw_compatibility
 from ..utils import _mult_cal_one
 
-from ...utils import check_fname, logger, verbose
+from ...annotations import Annotations, _combine_annotations
+from ...externals.six import string_types
+from ...utils import check_fname, logger, verbose, warn
 
 
-class RawFIF(_BaseRaw):
-    """Raw data
+class Raw(_BaseRaw):
+    """Raw data in FIF format
 
     Parameters
     ----------
@@ -37,10 +38,11 @@ class RawFIF(_BaseRaw):
         name of the first file has to be specified. Filenames should end
         with raw.fif, raw.fif.gz, raw_sss.fif, raw_sss.fif.gz,
         raw_tsss.fif or raw_tsss.fif.gz.
-    allow_maxshield : bool, (default False)
+    allow_maxshield : bool | str (default False)
         allow_maxshield if True, allow loading of data that has been
         processed with Maxshield. Maxshield-processed data should generally
         not be loaded directly, but should be processed using SSS first.
+        Can also be "yes" to load without eliciting a warning.
     preload : bool or str (default False)
         Preload data into memory for data manipulation and faster indexing.
         If True, the data will be preloaded into memory (fast, requires
@@ -96,8 +98,8 @@ class RawFIF(_BaseRaw):
             raws.append(raw)
             if next_fname is not None:
                 if not op.exists(next_fname):
-                    logger.warning('Split raw file detected but next file %s '
-                                   'does not exist.' % next_fname)
+                    warn('Split raw file detected but next file %s does not '
+                         'exist.' % next_fname)
                     continue
                 if next_fname in fnames:
                     # the user manually specified the split files
@@ -113,7 +115,7 @@ class RawFIF(_BaseRaw):
 
         _check_raw_compatibility(raws)
 
-        super(RawFIF, self).__init__(
+        super(Raw, self).__init__(
             copy.deepcopy(raws[0].info), False,
             [r.first_samp for r in raws], [r.last_samp for r in raws],
             [r.filename for r in raws], [r._raw_extras for r in raws],
@@ -125,6 +127,19 @@ class RawFIF(_BaseRaw):
             eeg_ref = make_eeg_average_ref_proj(self.info, activate=False)
             self.add_proj(eeg_ref)
 
+        # combine annotations
+        self.annotations = raws[0].annotations
+        if any([r.annotations for r in raws[1:]]):
+            first_samps = list()
+            last_samps = list()
+            for r in raws:
+                first_samps = np.r_[first_samps, r.first_samp]
+                last_samps = np.r_[last_samps, r.last_samp]
+                self.annotations = _combine_annotations((self.annotations,
+                                                         r.annotations),
+                                                        last_samps,
+                                                        first_samps,
+                                                        r.info['sfreq'])
         if preload:
             self._preload_data(preload)
         else:
@@ -154,6 +169,28 @@ class RawFIF(_BaseRaw):
 
             info, meas = read_meas_info(fid, tree, clean_bads=True)
 
+            annotations = None
+            annot_data = dir_tree_find(tree, FIFF.FIFFB_MNE_ANNOTATIONS)
+            if len(annot_data) > 0:
+                annot_data = annot_data[0]
+                for k in range(annot_data['nent']):
+                    kind = annot_data['directory'][k].kind
+                    pos = annot_data['directory'][k].pos
+                    orig_time = None
+                    tag = read_tag(fid, pos)
+                    if kind == FIFF.FIFF_MNE_BASELINE_MIN:
+                        onset = tag.data
+                    elif kind == FIFF.FIFF_MNE_BASELINE_MAX:
+                        duration = tag.data - onset
+                    elif kind == FIFF.FIFF_COMMENT:
+                        description = tag.data.split(':')
+                        description = [d.replace(';', ':') for d in
+                                       description]
+                    elif kind == FIFF.FIFF_MEAS_DATE:
+                        orig_time = float(tag.data)
+                annotations = Annotations(onset, duration, description,
+                                          orig_time)
+
             #   Locate the data of interest
             raw_node = dir_tree_find(meas, FIFF.FIFFB_RAW_DATA)
             if len(raw_node) == 0:
@@ -169,7 +206,9 @@ class RawFIF(_BaseRaw):
                         raise ValueError('No raw data in %s' % fname)
                     elif allow_maxshield:
                         info['maxshield'] = True
-                        warnings.warn(msg)
+                        if not (isinstance(allow_maxshield, string_types) and
+                                allow_maxshield == 'yes'):
+                            warn(msg)
                     else:
                         msg += (' Use allow_maxshield=True if you are sure you'
                                 ' want to load the data despite this warning.')
@@ -194,6 +233,7 @@ class RawFIF(_BaseRaw):
                 tag = read_tag(fid, directory[first].pos)
                 first_samp = int(tag.data)
                 first += 1
+                _check_entry(first, nent)
 
             #   Omit initial skip
             if directory[first].kind == FIFF.FIFF_DATA_SKIP:
@@ -201,15 +241,18 @@ class RawFIF(_BaseRaw):
                 tag = read_tag(fid, directory[first].pos)
                 first_skip = int(tag.data)
                 first += 1
+                _check_entry(first, nent)
 
             raw = _RawShell()
             raw.filename = fname
             raw.first_samp = first_samp
+            raw.annotations = annotations
 
             #   Go through the remaining tags in the directory
             raw_extras = list()
             nskip = 0
             orig_format = None
+
             for k in range(first, nent):
                 ent = directory[k]
                 if ent.kind == FIFF.FIFF_DATA_SKIP:
@@ -417,13 +460,19 @@ class RawFIF(_BaseRaw):
 
         .. note:: The effect of the difference between the coil sizes on the
                   current estimates computed by the MNE software is very small.
-                  Therefore the use of mne_fix_mag_coil_types is not mandatory.
+                  Therefore the use of this function is not mandatory.
         """
         from ...channels import fix_mag_coil_types
         fix_mag_coil_types(self.info)
         return self
 
 
+def _check_entry(first, nent):
+    """Helper to sanity check entries"""
+    if first >= nent:
+        raise IOError('Could not read data, perhaps this is a corrupt file')
+
+
 def read_raw_fif(fnames, allow_maxshield=False, preload=False,
                  proj=False, compensation=None, add_eeg_ref=True,
                  verbose=None):
@@ -466,13 +515,13 @@ def read_raw_fif(fnames, allow_maxshield=False, preload=False,
 
     Returns
     -------
-    raw : Instance of RawFIF
+    raw : instance of Raw
         A Raw object containing FIF data.
 
     Notes
     -----
     .. versionadded:: 0.9.0
     """
-    return RawFIF(fnames=fnames, allow_maxshield=allow_maxshield,
-                  preload=preload, proj=proj, compensation=compensation,
-                  add_eeg_ref=add_eeg_ref, verbose=verbose)
+    return Raw(fnames=fnames, allow_maxshield=allow_maxshield,
+               preload=preload, proj=proj, compensation=compensation,
+               add_eeg_ref=add_eeg_ref, verbose=verbose)
diff --git a/mne/io/fiff/tests/test_raw_fiff.py b/mne/io/fiff/tests/test_raw_fiff.py
index 5feafab..4076c56 100644
--- a/mne/io/fiff/tests/test_raw_fiff.py
+++ b/mne/io/fiff/tests/test_raw_fiff.py
@@ -26,11 +26,15 @@ from mne.utils import (_TempDir, requires_pandas, slow_test,
 from mne.externals.six.moves import zip, cPickle as pickle
 from mne.io.proc_history import _get_sss_rank
 from mne.io.pick import _picks_by_type
+from mne.annotations import Annotations
+from mne.tests.common import assert_naming
 
 warnings.simplefilter('always')  # enable b/c these tests throw warnings
 
-data_dir = op.join(testing.data_path(download=False), 'MEG', 'sample')
+testing_path = testing.data_path(download=False)
+data_dir = op.join(testing_path, 'MEG', 'sample')
 fif_fname = op.join(data_dir, 'sample_audvis_trunc_raw.fif')
+ms_fname = op.join(testing_path, 'SSS', 'test_move_anon_raw.fif')
 
 base_dir = op.join(op.dirname(__file__), '..', '..', 'tests', 'data')
 test_fif_fname = op.join(base_dir, 'test_raw.fif')
@@ -89,9 +93,9 @@ def test_hash_raw():
     """
     raw = read_raw_fif(fif_fname)
     assert_raises(RuntimeError, raw.__hash__)
-    raw = Raw(fif_fname).crop(0, 0.5, False)
+    raw = Raw(fif_fname).crop(0, 0.5, copy=False)
     raw.load_data()
-    raw_2 = Raw(fif_fname).crop(0, 0.5, False)
+    raw_2 = Raw(fif_fname).crop(0, 0.5, copy=False)
     raw_2.load_data()
     assert_equal(hash(raw), hash(raw_2))
     # do NOT use assert_equal here, failing output is terrible
@@ -102,11 +106,22 @@ def test_hash_raw():
 
 
 @testing.requires_testing_data
+def test_maxshield():
+    """Test maxshield warning
+    """
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        Raw(ms_fname, allow_maxshield=True)
+    assert_equal(len(w), 1)
+    assert_true('test_raw_fiff.py' in w[0].filename)
+
+
+ at testing.requires_testing_data
 def test_subject_info():
     """Test reading subject information
     """
     tempdir = _TempDir()
-    raw = Raw(fif_fname).crop(0, 1, False)
+    raw = Raw(fif_fname).crop(0, 1, copy=False)
     assert_true(raw.info['subject_info'] is None)
     # fake some subject data
     keys = ['id', 'his_id', 'last_name', 'first_name', 'birthday', 'sex',
@@ -121,12 +136,19 @@ def test_subject_info():
     raw_read = Raw(out_fname)
     for key in keys:
         assert_equal(subject_info[key], raw_read.info['subject_info'][key])
-    raw_read.anonymize()
-    assert_true(raw_read.info.get('subject_info') is None)
-    out_fname_anon = op.join(tempdir, 'test_subj_info_anon_raw.fif')
-    raw_read.save(out_fname_anon, overwrite=True)
-    raw_read = Raw(out_fname_anon)
-    assert_true(raw_read.info.get('subject_info') is None)
+    assert_equal(raw.info['meas_date'], raw_read.info['meas_date'])
+    raw.anonymize()
+    raw.save(out_fname, overwrite=True)
+    raw_read = Raw(out_fname)
+    for this_raw in (raw, raw_read):
+        assert_true(this_raw.info.get('subject_info') is None)
+        assert_equal(this_raw.info['meas_date'], [0, 0])
+    assert_equal(raw.info['file_id']['secs'], 0)
+    assert_equal(raw.info['meas_id']['secs'], 0)
+    # When we write out with raw.save, these get overwritten with the
+    # new save time
+    assert_true(raw_read.info['file_id']['secs'] > 0)
+    assert_true(raw_read.info['meas_id']['secs'] > 0)
 
 
 @testing.requires_testing_data
@@ -221,7 +243,7 @@ def test_multiple_files():
     """
     # split file
     tempdir = _TempDir()
-    raw = Raw(fif_fname).crop(0, 10, False)
+    raw = Raw(fif_fname).crop(0, 10, copy=False)
     raw.load_data()
     raw.load_data()  # test no operation
     split_size = 3.  # in seconds
@@ -239,6 +261,9 @@ def test_multiple_files():
         fname = op.join(tempdir, 'test_raw_split-%d_raw.fif' % ri)
         raw.save(fname, tmin=tmins[ri], tmax=tmaxs[ri])
         raws[ri] = Raw(fname)
+        assert_equal(len(raws[ri].times),
+                     int(round((tmaxs[ri] - tmins[ri]) *
+                               raw.info['sfreq'])) + 1)  # + 1 b/c inclusive
     events = [find_events(r, stim_channel='STI 014') for r in raws]
     last_samps = [r.last_samp for r in raws]
     first_samps = [r.first_samp for r in raws]
@@ -247,6 +272,7 @@ def test_multiple_files():
     assert_raises(ValueError, concatenate_raws, raws, True, events[1:])
     all_raw_1, events1 = concatenate_raws(raws, preload=False,
                                           events_list=events)
+    assert_allclose(all_raw_1.times, raw.times)
     assert_equal(raw.first_samp, all_raw_1.first_samp)
     assert_equal(raw.last_samp, all_raw_1.last_samp)
     assert_allclose(raw[:, :][0], all_raw_1[:, :][0])
@@ -340,6 +366,9 @@ def test_split_files():
     """
     tempdir = _TempDir()
     raw_1 = Raw(fif_fname, preload=True)
+    # Test a very close corner case
+    raw_crop = raw_1.copy().crop(0, 1., copy=False)
+
     assert_allclose(raw_1.info['buffer_size_sec'], 10., atol=1e-2)  # samp rate
     split_fname = op.join(tempdir, 'split_raw.fif')
     raw_1.save(split_fname, buffer_size_sec=1.0, split_size='10MB')
@@ -361,6 +390,64 @@ def test_split_files():
     assert_array_equal(data_1, data_2)
     assert_array_equal(times_1, times_2)
 
+    # test the case where we only end up with one buffer to write
+    # (GH#3210). These tests rely on writing meas info and annotations
+    # taking up a certain number of bytes, so if we change those functions
+    # somehow, the numbers below for e.g. split_size might need to be
+    # adjusted.
+    raw_crop = raw_1.copy().crop(0, 5, copy=False)
+    try:
+        raw_crop.save(split_fname, split_size='1MB',  # too small a size
+                      buffer_size_sec=1., overwrite=True)
+    except ValueError as exp:
+        assert_true('after writing measurement information' in str(exp), exp)
+    try:
+        raw_crop.save(split_fname,
+                      split_size=3002276,  # still too small, now after Info
+                      buffer_size_sec=1., overwrite=True)
+    except ValueError as exp:
+        assert_true('too large for the given split size' in str(exp), exp)
+    # just barely big enough here; the right size to write exactly one buffer
+    # at a time so we hit GH#3210 if we aren't careful
+    raw_crop.save(split_fname, split_size='4.5MB',
+                  buffer_size_sec=1., overwrite=True)
+    raw_read = read_raw_fif(split_fname)
+    assert_allclose(raw_crop[:][0], raw_read[:][0], atol=1e-20)
+
+    # Check our buffer arithmetic
+
+    # 1 buffer required
+    raw_crop = raw_1.copy().crop(0, 1, copy=False)
+    raw_crop.save(split_fname, buffer_size_sec=1., overwrite=True)
+    raw_read = read_raw_fif(split_fname)
+    assert_equal(len(raw_read._raw_extras[0]), 1)
+    assert_equal(raw_read._raw_extras[0][0]['nsamp'], 301)
+    assert_allclose(raw_crop[:][0], raw_read[:][0])
+    # 2 buffers required
+    raw_crop.save(split_fname, buffer_size_sec=0.5, overwrite=True)
+    raw_read = read_raw_fif(split_fname)
+    assert_equal(len(raw_read._raw_extras[0]), 2)
+    assert_equal(raw_read._raw_extras[0][0]['nsamp'], 151)
+    assert_equal(raw_read._raw_extras[0][1]['nsamp'], 150)
+    assert_allclose(raw_crop[:][0], raw_read[:][0])
+    # 2 buffers required
+    raw_crop.save(split_fname,
+                  buffer_size_sec=1. - 1.01 / raw_crop.info['sfreq'],
+                  overwrite=True)
+    raw_read = read_raw_fif(split_fname)
+    assert_equal(len(raw_read._raw_extras[0]), 2)
+    assert_equal(raw_read._raw_extras[0][0]['nsamp'], 300)
+    assert_equal(raw_read._raw_extras[0][1]['nsamp'], 1)
+    assert_allclose(raw_crop[:][0], raw_read[:][0])
+    raw_crop.save(split_fname,
+                  buffer_size_sec=1. - 2.01 / raw_crop.info['sfreq'],
+                  overwrite=True)
+    raw_read = read_raw_fif(split_fname)
+    assert_equal(len(raw_read._raw_extras[0]), 2)
+    assert_equal(raw_read._raw_extras[0][0]['nsamp'], 299)
+    assert_equal(raw_read._raw_extras[0][1]['nsamp'], 2)
+    assert_allclose(raw_crop[:][0], raw_read[:][0])
+
 
 def test_load_bad_channels():
     """Test reading/writing of bad channels
@@ -413,6 +500,7 @@ def test_io_raw():
     for chars in [b'\xc3\xa4\xc3\xb6\xc3\xa9', b'a']:
         with Raw(fif_fname) as r:
             assert_true('Raw' in repr(r))
+            assert_true(op.basename(fif_fname) in repr(r))
             desc1 = r.info['description'] = chars.decode('utf-8')
             temp_file = op.join(tempdir, 'raw.fif')
             r.save(temp_file, overwrite=True)
@@ -421,7 +509,7 @@ def test_io_raw():
             assert_equal(desc1, desc2)
 
     # Let's construct a simple test for IO first
-    raw = Raw(fif_fname).crop(0, 3.5, False)
+    raw = Raw(fif_fname).crop(0, 3.5, copy=False)
     raw.load_data()
     # put in some data that we know the values of
     data = rng.randn(raw._data.shape[0], raw._data.shape[1])
@@ -450,7 +538,7 @@ def test_io_raw():
                             if ch_names[k][0] == 'M']
         n_channels = 100
         meg_channels_idx = meg_channels_idx[:n_channels]
-        start, stop = raw.time_as_index([0, 5])
+        start, stop = raw.time_as_index([0, 5], use_rounding=True)
         data, times = raw[meg_channels_idx, start:(stop + 1)]
         meg_ch_names = [ch_names[k] for k in meg_channels_idx]
 
@@ -515,7 +603,7 @@ def test_io_raw():
         raw_badname = op.join(tempdir, 'test-bad-name.fif.gz')
         raw.save(raw_badname)
         Raw(raw_badname)
-    assert_true(len(w) > 0)  # len(w) should be 2 but Travis sometimes has more
+    assert_naming(w, 'test_raw_fiff.py', 2)
 
 
 @testing.requires_testing_data
@@ -575,6 +663,10 @@ def test_getitem():
         data1, times1 = raw[[0, 1]]
         assert_array_equal(data, data1)
         assert_array_equal(times, times1)
+        assert_array_equal(raw[-10:, :][0],
+                           raw[len(raw.ch_names) - 10:, :][0])
+        assert_raises(ValueError, raw.__getitem__,
+                      (slice(-len(raw.ch_names) - 1), slice(None)))
 
 
 @testing.requires_testing_data
@@ -602,9 +694,10 @@ def test_proj():
             raw.del_proj(0)
             assert_equal(len(raw.info['projs']), n_proj - 1)
             raw.add_proj(projs, remove_existing=False)
-            assert_equal(len(raw.info['projs']), 2 * n_proj - 1)
-            raw.add_proj(projs, remove_existing=True)
+            # Test that already existing projections are not added.
             assert_equal(len(raw.info['projs']), n_proj)
+            raw.add_proj(projs[:-1], remove_existing=True)
+            assert_equal(len(raw.info['projs']), n_proj - 1)
 
     # test apply_proj() with and without preload
     for preload in [True, False]:
@@ -682,7 +775,7 @@ def test_preload_modify():
 def test_filter():
     """Test filtering (FIR and IIR) and Raw.apply_function interface
     """
-    raw = Raw(fif_fname).crop(0, 7, False)
+    raw = Raw(fif_fname).crop(0, 7, copy=False)
     raw.load_data()
     sig_dec = 11
     sig_dec_notch = 12
@@ -690,17 +783,11 @@ def test_filter():
     picks_meg = pick_types(raw.info, meg=True, exclude='bads')
     picks = picks_meg[:4]
 
-    raw_lp = raw.copy()
-    raw_lp.filter(0., 4.0 - 0.25, picks=picks, n_jobs=2)
-
-    raw_hp = raw.copy()
-    raw_hp.filter(8.0 + 0.25, None, picks=picks, n_jobs=2)
-
-    raw_bp = raw.copy()
-    raw_bp.filter(4.0 + 0.25, 8.0 - 0.25, picks=picks)
-
-    raw_bs = raw.copy()
-    raw_bs.filter(8.0 + 0.25, 4.0 - 0.25, picks=picks, n_jobs=2)
+    filter_params = dict(picks=picks, n_jobs=2)
+    raw_lp = raw.copy().filter(0., 4.0 - 0.25, **filter_params)
+    raw_hp = raw.copy().filter(8.0 + 0.25, None, **filter_params)
+    raw_bp = raw.copy().filter(4.0 + 0.25, 8.0 - 0.25, **filter_params)
+    raw_bs = raw.copy().filter(8.0 + 0.25, 4.0 - 0.25, **filter_params)
 
     data, _ = raw[picks, :]
 
@@ -712,12 +799,11 @@ def test_filter():
     assert_array_almost_equal(data, lp_data + bp_data + hp_data, sig_dec)
     assert_array_almost_equal(data, bp_data + bs_data, sig_dec)
 
-    raw_lp_iir = raw.copy()
-    raw_lp_iir.filter(0., 4.0, picks=picks, n_jobs=2, method='iir')
-    raw_hp_iir = raw.copy()
-    raw_hp_iir.filter(8.0, None, picks=picks, n_jobs=2, method='iir')
-    raw_bp_iir = raw.copy()
-    raw_bp_iir.filter(4.0, 8.0, picks=picks, method='iir')
+    filter_params_iir = dict(picks=picks, n_jobs=2, method='iir')
+    raw_lp_iir = raw.copy().filter(0., 4.0, **filter_params_iir)
+    raw_hp_iir = raw.copy().filter(8.0, None, **filter_params_iir)
+    raw_bp_iir = raw.copy().filter(4.0, 8.0, **filter_params_iir)
+    del filter_params_iir
     lp_data_iir, _ = raw_lp_iir[picks, :]
     hp_data_iir, _ = raw_hp_iir[picks, :]
     bp_data_iir, _ = raw_bp_iir[picks, :]
@@ -732,25 +818,58 @@ def test_filter():
     bp_data_iir, _ = raw_bp_iir[picks_meg[4:], :]
     assert_array_equal(data, bp_data_iir)
 
+    # ... and that inplace changes are inplace
+    raw_copy = raw.copy()
+    raw_copy.filter(None, 20., picks=picks, n_jobs=2)
+    assert_true(raw._data[0, 0] != raw_copy._data[0, 0])
+    assert_equal(raw.copy().filter(None, 20., **filter_params)._data,
+                 raw_copy._data)
+
     # do a very simple check on line filtering
-    raw_bs = raw.copy()
     with warnings.catch_warnings(record=True):
         warnings.simplefilter('always')
-        raw_bs.filter(60.0 + 0.5, 60.0 - 0.5, picks=picks, n_jobs=2)
+        raw_bs = raw.copy().filter(60.0 + 0.5, 60.0 - 0.5, **filter_params)
         data_bs, _ = raw_bs[picks, :]
-        raw_notch = raw.copy()
-        raw_notch.notch_filter(60.0, picks=picks, n_jobs=2, method='fft')
+        raw_notch = raw.copy().notch_filter(
+            60.0, picks=picks, n_jobs=2, method='fft')
     data_notch, _ = raw_notch[picks, :]
     assert_array_almost_equal(data_bs, data_notch, sig_dec_notch)
 
     # now use the sinusoidal fitting
-    raw_notch = raw.copy()
-    raw_notch.notch_filter(None, picks=picks, n_jobs=2, method='spectrum_fit')
+    raw_notch = raw.copy().notch_filter(
+        None, picks=picks, n_jobs=2, method='spectrum_fit')
     data_notch, _ = raw_notch[picks, :]
     data, _ = raw[picks, :]
     assert_array_almost_equal(data, data_notch, sig_dec_notch_fit)
 
 
+def test_filter_picks():
+    """Test filtering default channel picks"""
+    ch_types = ['mag', 'grad', 'eeg', 'seeg', 'misc', 'stim', 'ecog']
+    info = create_info(ch_names=ch_types, ch_types=ch_types, sfreq=256)
+    raw = RawArray(data=np.zeros((len(ch_types), 1000)), info=info)
+
+    # -- Deal with meg mag grad exception
+    ch_types = ('misc', 'stim', 'meg', 'eeg', 'seeg', 'ecog')
+
+    # -- Filter data channels
+    for ch_type in ('mag', 'grad', 'eeg', 'seeg', 'ecog'):
+        picks = dict((ch, ch == ch_type) for ch in ch_types)
+        picks['meg'] = ch_type if ch_type in ('mag', 'grad') else False
+        raw_ = raw.copy().pick_types(**picks)
+        # Avoid RuntimeWarning due to Attenuation
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter('always')
+            raw_.filter(10, 30)
+        assert_true(any(['Attenuation' in str(ww.message) for ww in w]))
+
+    # -- Error if no data channel
+    for ch_type in ('misc', 'stim'):
+        picks = dict((ch, ch == ch_type) for ch in ch_types)
+        raw_ = raw.copy().pick_types(**picks)
+        assert_raises(RuntimeError, raw_.filter, 10, 30)
+
+
 @testing.requires_testing_data
 def test_crop():
     """Test cropping raw files
@@ -769,7 +888,7 @@ def test_crop():
     tmins /= sfreq
     raws = [None] * len(tmins)
     for ri, (tmin, tmax) in enumerate(zip(tmins, tmaxs)):
-        raws[ri] = raw.crop(tmin, tmax, True)
+        raws[ri] = raw.copy().crop(tmin, tmax, copy=False)
     all_raw_2 = concatenate_raws(raws, preload=False)
     assert_equal(raw.first_samp, all_raw_2.first_samp)
     assert_equal(raw.last_samp, all_raw_2.last_samp)
@@ -783,29 +902,36 @@ def test_crop():
     # going in revere order so the last fname is the first file (need it later)
     raws = [None] * len(tmins)
     for ri, (tmin, tmax) in enumerate(zip(tmins, tmaxs)):
-        raws[ri] = raw.copy()
-        raws[ri].crop(tmin, tmax, False)
+        raws[ri] = raw.copy().crop(tmin, tmax, copy=False)
     # test concatenation of split file
     all_raw_1 = concatenate_raws(raws, preload=False)
 
-    all_raw_2 = raw.crop(0, None, True)
+    all_raw_2 = raw.copy().crop(0, None, copy=False)
     for ar in [all_raw_1, all_raw_2]:
         assert_equal(raw.first_samp, ar.first_samp)
         assert_equal(raw.last_samp, ar.last_samp)
         assert_array_equal(raw[:, :][0], ar[:, :][0])
 
+    # test shape consistency of cropped raw
+    data = np.zeros((1, 1002001))
+    info = create_info(1, 1000)
+    raw = RawArray(data, info)
+    for tmin in range(0, 1001, 100):
+        raw1 = raw.copy().crop(tmin=tmin, tmax=tmin + 2, copy=False)
+        assert_equal(raw1[:][0].shape, (1, 2001))
+
 
 @testing.requires_testing_data
 def test_resample():
     """Test resample (with I/O and multiple files)
     """
     tempdir = _TempDir()
-    raw = Raw(fif_fname).crop(0, 3, False)
+    raw = Raw(fif_fname).crop(0, 3, copy=False)
     raw.load_data()
     raw_resamp = raw.copy()
     sfreq = raw.info['sfreq']
     # test parallel on upsample
-    raw_resamp.resample(sfreq * 2, n_jobs=2)
+    raw_resamp.resample(sfreq * 2, n_jobs=2, npad='auto')
     assert_equal(raw_resamp.n_times, len(raw_resamp.times))
     raw_resamp.save(op.join(tempdir, 'raw_resamp-raw.fif'))
     raw_resamp = Raw(op.join(tempdir, 'raw_resamp-raw.fif'), preload=True)
@@ -814,7 +940,7 @@ def test_resample():
     assert_equal(raw_resamp._data.shape[1], raw_resamp.n_times)
     assert_equal(raw._data.shape[0], raw_resamp._data.shape[0])
     # test non-parallel on downsample
-    raw_resamp.resample(sfreq, n_jobs=1)
+    raw_resamp.resample(sfreq, n_jobs=1, npad='auto')
     assert_equal(raw_resamp.info['sfreq'], sfreq)
     assert_equal(raw._data.shape, raw_resamp._data.shape)
     assert_equal(raw.first_samp, raw_resamp.first_samp)
@@ -837,9 +963,9 @@ def test_resample():
     raw3 = raw.copy()
     raw4 = raw.copy()
     raw1 = concatenate_raws([raw1, raw2])
-    raw1.resample(10.)
-    raw3.resample(10.)
-    raw4.resample(10.)
+    raw1.resample(10., npad='auto')
+    raw3.resample(10., npad='auto')
+    raw4.resample(10., npad='auto')
     raw3 = concatenate_raws([raw3, raw4])
     assert_array_equal(raw1._data, raw3._data)
     assert_array_equal(raw1._first_samps, raw3._first_samps)
@@ -854,12 +980,12 @@ def test_resample():
     # basic decimation
     stim = [1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]
     raw = RawArray([stim], create_info(1, len(stim), ['stim']))
-    assert_allclose(raw.resample(8.)._data,
+    assert_allclose(raw.resample(8., npad='auto')._data,
                     [[1, 1, 0, 0, 1, 1, 0, 0]])
 
     # decimation of multiple stim channels
     raw = RawArray(2 * [stim], create_info(2, len(stim), 2 * ['stim']))
-    assert_allclose(raw.resample(8.)._data,
+    assert_allclose(raw.resample(8., npad='auto')._data,
                     [[1, 1, 0, 0, 1, 1, 0, 0],
                      [1, 1, 0, 0, 1, 1, 0, 0]])
 
@@ -867,7 +993,7 @@ def test_resample():
     # done naively
     stim = [0, 0, 0, 1, 1, 0, 0, 0]
     raw = RawArray([stim], create_info(1, len(stim), ['stim']))
-    assert_allclose(raw.resample(4.)._data,
+    assert_allclose(raw.resample(4., npad='auto')._data,
                     [[0, 1, 1, 0]])
 
     # two events are merged in this case (warning)
@@ -875,7 +1001,7 @@ def test_resample():
     raw = RawArray([stim], create_info(1, len(stim), ['stim']))
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
-        raw.resample(8.)
+        raw.resample(8., npad='auto')
         assert_true(len(w) == 1)
 
     # events are dropped in this case (warning)
@@ -883,28 +1009,30 @@ def test_resample():
     raw = RawArray([stim], create_info(1, len(stim), ['stim']))
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
-        raw.resample(4.)
+        raw.resample(4., npad='auto')
         assert_true(len(w) == 1)
 
     # test resampling events: this should no longer give a warning
     stim = [0, 1, 1, 0, 0, 1, 1, 0]
     raw = RawArray([stim], create_info(1, len(stim), ['stim']))
     events = find_events(raw)
-    raw, events = raw.resample(4., events=events)
+    raw, events = raw.resample(4., events=events, npad='auto')
     assert_equal(events, np.array([[0, 0, 1], [2, 0, 1]]))
 
     # test copy flag
     stim = [1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]
     raw = RawArray([stim], create_info(1, len(stim), ['stim']))
-    raw_resampled = raw.resample(4., copy=True)
+    raw_resampled = raw.copy().resample(4., npad='auto')
     assert_true(raw_resampled is not raw)
-    raw_resampled = raw.resample(4., copy=False)
+    raw_resampled = raw.resample(4., npad='auto')
     assert_true(raw_resampled is raw)
 
     # resample should still work even when no stim channel is present
     raw = RawArray(np.random.randn(1, 100), create_info(1, 100, ['eeg']))
-    raw.resample(10)
-    assert_true(len(raw) == 10)
+    raw.info['lowpass'] = 50.
+    raw.resample(10, npad='auto')
+    assert_equal(raw.info['lowpass'], 5.)
+    assert_equal(len(raw), 10)
 
 
 @testing.requires_testing_data
@@ -972,53 +1100,42 @@ def test_to_data_frame():
     assert_array_equal(df.values[:, 2], raw._data[2] * 1e15)
 
 
- at testing.requires_testing_data
-def test_raw_index_as_time():
-    """ Test index as time conversion"""
-    raw = Raw(fif_fname, preload=True)
-    t0 = raw.index_as_time([0], True)[0]
-    t1 = raw.index_as_time([100], False)[0]
-    t2 = raw.index_as_time([100], True)[0]
-    assert_equal(t2 - t1, t0)
-    # ensure we can go back and forth
-    t3 = raw.index_as_time(raw.time_as_index([0], True), True)
-    assert_array_almost_equal(t3, [0.0], 2)
-    t3 = raw.index_as_time(raw.time_as_index(raw.info['sfreq'], True), True)
-    assert_array_almost_equal(t3, [raw.info['sfreq']], 2)
-    t3 = raw.index_as_time(raw.time_as_index(raw.info['sfreq'], False), False)
-    assert_array_almost_equal(t3, [raw.info['sfreq']], 2)
-    i0 = raw.time_as_index(raw.index_as_time([0], True), True)
-    assert_equal(i0[0], 0)
-    i1 = raw.time_as_index(raw.index_as_time([100], True), True)
-    assert_equal(i1[0], 100)
-    # Have to add small amount of time because we truncate via int casting
-    i1 = raw.time_as_index(raw.index_as_time([100.0001], False), False)
-    assert_equal(i1[0], 100)
-
-
 def test_add_channels():
     """Test raw splitting / re-appending channel types
     """
-    raw = Raw(test_fif_fname).crop(0, 1).load_data()
+    raw = Raw(test_fif_fname).crop(0, 1, copy=False).load_data()
     raw_nopre = Raw(test_fif_fname, preload=False)
-    raw_eeg_meg = raw.pick_types(meg=True, eeg=True, copy=True)
-    raw_eeg = raw.pick_types(meg=False, eeg=True, copy=True)
-    raw_meg = raw.pick_types(meg=True, eeg=False, copy=True)
-    raw_stim = raw.pick_types(meg=False, eeg=False, stim=True, copy=True)
-    raw_new = raw_meg.add_channels([raw_eeg, raw_stim], copy=True)
-    assert_true(all(ch in raw_new.ch_names
-                    for ch in raw_stim.ch_names + raw_meg.ch_names))
-    raw_new = raw_meg.add_channels([raw_eeg], copy=True)
+    raw_eeg_meg = raw.copy().pick_types(meg=True, eeg=True)
+    raw_eeg = raw.copy().pick_types(meg=False, eeg=True)
+    raw_meg = raw.copy().pick_types(meg=True, eeg=False)
+    raw_stim = raw.copy().pick_types(meg=False, eeg=False, stim=True)
+    raw_new = raw_meg.copy().add_channels([raw_eeg, raw_stim])
+    assert_true(
+        all(ch in raw_new.ch_names
+            for ch in list(raw_stim.ch_names) + list(raw_meg.ch_names))
+    )
+    raw_new = raw_meg.copy().add_channels([raw_eeg])
 
     assert_true(ch in raw_new.ch_names for ch in raw.ch_names)
     assert_array_equal(raw_new[:, :][0], raw_eeg_meg[:, :][0])
     assert_array_equal(raw_new[:, :][1], raw[:, :][1])
     assert_true(all(ch not in raw_new.ch_names for ch in raw_stim.ch_names))
 
+    # Testing force updates
+    raw_arr_info = create_info(['1', '2'], raw_meg.info['sfreq'], 'eeg')
+    orig_head_t = raw_arr_info['dev_head_t']
+    raw_arr = np.random.randn(2, raw_eeg.n_times)
+    raw_arr = RawArray(raw_arr, raw_arr_info)
+    # This should error because of conflicts in Info
+    assert_raises(ValueError, raw_meg.copy().add_channels, [raw_arr])
+    raw_meg.copy().add_channels([raw_arr], force_update_info=True)
+    # Make sure that values didn't get overwritten
+    assert_true(raw_arr.info['dev_head_t'] is orig_head_t)
+
     # Now test errors
     raw_badsf = raw_eeg.copy()
     raw_badsf.info['sfreq'] = 3.1415927
-    raw_eeg = raw_eeg.crop(.5)
+    raw_eeg.crop(.5, copy=False)
 
     assert_raises(AssertionError, raw_meg.add_channels, [raw_nopre])
     assert_raises(RuntimeError, raw_meg.add_channels, [raw_badsf])
@@ -1031,7 +1148,8 @@ def test_add_channels():
 def test_raw_time_as_index():
     """ Test time as index conversion"""
     raw = Raw(fif_fname, preload=True)
-    first_samp = raw.time_as_index([0], True)[0]
+    with warnings.catch_warnings(record=True):  # deprecation
+        first_samp = raw.time_as_index([0], True)[0]
     assert_equal(raw.first_samp, -first_samp)
 
 
@@ -1046,11 +1164,17 @@ def test_save():
     # can't overwrite file without overwrite=True
     assert_raises(IOError, raw.save, fif_fname)
 
-    # test abspath support
+    # test abspath support and annotations
+    annot = Annotations([10], [10], ['test'], raw.info['meas_date'])
+    raw.annotations = annot
     new_fname = op.join(op.abspath(op.curdir), 'break-raw.fif')
     raw.save(op.join(tempdir, new_fname), overwrite=True)
     new_raw = Raw(op.join(tempdir, new_fname), preload=False)
     assert_raises(ValueError, new_raw.save, new_fname)
+    assert_array_equal(annot.onset, new_raw.annotations.onset)
+    assert_array_equal(annot.duration, new_raw.annotations.duration)
+    assert_array_equal(annot.description, new_raw.annotations.description)
+    assert_equal(annot.orig_time, new_raw.annotations.orig_time)
     # make sure we can overwrite the file we loaded when preload=True
     new_raw = Raw(op.join(tempdir, new_fname), preload=True)
     new_raw.save(op.join(tempdir, new_fname), overwrite=True)
@@ -1132,7 +1256,7 @@ def test_drop_channels_mixin():
     ch_names = raw.ch_names[3:]
 
     ch_names_orig = raw.ch_names
-    dummy = raw.drop_channels(drop_ch, copy=True)
+    dummy = raw.copy().drop_channels(drop_ch)
     assert_equal(ch_names, dummy.ch_names)
     assert_equal(ch_names_orig, raw.ch_names)
     assert_equal(len(ch_names_orig), raw._data.shape[0])
@@ -1153,12 +1277,12 @@ def test_pick_channels_mixin():
     ch_names = raw.ch_names[:3]
 
     ch_names_orig = raw.ch_names
-    dummy = raw.pick_channels(ch_names, copy=True)  # copy is True
+    dummy = raw.copy().pick_channels(ch_names)
     assert_equal(ch_names, dummy.ch_names)
     assert_equal(ch_names_orig, raw.ch_names)
     assert_equal(len(ch_names_orig), raw._data.shape[0])
 
-    raw.pick_channels(ch_names, copy=False)  # copy is False
+    raw.pick_channels(ch_names)  # copy is False
     assert_equal(ch_names, raw.ch_names)
     assert_equal(len(ch_names), len(raw._cals))
     assert_equal(len(ch_names), raw._data.shape[0])
diff --git a/mne/io/kit/constants.py b/mne/io/kit/constants.py
index 3e96ea4..012f99a 100644
--- a/mne/io/kit/constants.py
+++ b/mne/io/kit/constants.py
@@ -33,7 +33,7 @@ KIT.UNIT_MUL = 0  # default is 0 mne_manual p.273
 
 # gain: 0:x1, 1:x2, 2:x5, 3:x10, 4:x20, 5:x50, 6:x100, 7:x200
 KIT.GAINS = [1, 2, 5, 10, 20, 50, 100, 200]
-# BEF options: 0:THRU, 1:50Hz, 2:60Hz, 3:50Hz
+# BEF options: 0:THROUGH, 1:50Hz, 2:60Hz, 3:50Hz
 KIT.BEFS = [0, 50, 60, 50]
 
 # coreg constants
diff --git a/mne/io/kit/kit.py b/mne/io/kit/kit.py
index fa46233..62edec9 100644
--- a/mne/io/kit/kit.py
+++ b/mne/io/kit/kit.py
@@ -11,14 +11,13 @@ RawKIT class is adapted from Denis Engemann et al.'s mne_bti2fiff.py
 from os import SEEK_CUR, path as op
 from struct import unpack
 import time
-from warnings import warn
 
 import numpy as np
 from scipy import linalg
 
 from ..pick import pick_types
 from ...coreg import fit_matched_points, _decimate_points
-from ...utils import verbose, logger
+from ...utils import verbose, logger, warn
 from ...transforms import (apply_trans, als_ras_trans, als_ras_trans_mm,
                            get_ras_to_neuromag_trans, Transform)
 from ..base import _BaseRaw
@@ -39,17 +38,17 @@ class RawKIT(_BaseRaw):
     ----------
     input_fname : str
         Path to the sqd file.
-    mrk : None | str | array_like, shape = (5, 3) | list of str or array_like
+    mrk : None | str | array_like, shape (5, 3) | list of str or array_like
         Marker points representing the location of the marker coils with
         respect to the MEG Sensors, or path to a marker file.
         If list, all of the markers will be averaged together.
-    elp : None | str | array_like, shape = (8, 3)
+    elp : None | str | array_like, shape (8, 3)
         Digitizer points representing the location of the fiducials and the
         marker coils with respect to the digitized head shape, or path to a
         file containing these points.
-    hsp : None | str | array, shape = (n_points, 3)
+    hsp : None | str | array, shape (n_points, 3)
         Digitizer head shape points, or path to head shape file. If more than
-        10`000 points are in the head shape, they are automatically decimated.
+        10,000 points are in the head shape, they are automatically decimated.
     stim : list of int | '<' | '>'
         Channel-value correspondence when converting KIT trigger channels to a
         Neuromag-style stim channel. For '<', the largest values are assigned
@@ -195,12 +194,12 @@ class RawKIT(_BaseRaw):
                                  % (np.max(stim),
                                     self._raw_extras[0]['nchan']))
             # modify info
-            info['nchan'] = self._raw_extras[0]['nchan'] + 1
+            nchan = self._raw_extras[0]['nchan'] + 1
             ch_name = 'STI 014'
             chan_info = {}
             chan_info['cal'] = KIT.CALIB_FACTOR
-            chan_info['logno'] = info['nchan']
-            chan_info['scanno'] = info['nchan']
+            chan_info['logno'] = nchan
+            chan_info['scanno'] = nchan
             chan_info['range'] = 1.0
             chan_info['unit'] = FIFF.FIFF_UNIT_NONE
             chan_info['unit_mul'] = 0
@@ -209,7 +208,7 @@ class RawKIT(_BaseRaw):
             chan_info['loc'] = np.zeros(12)
             chan_info['kind'] = FIFF.FIFFV_STIM_CH
             info['chs'].append(chan_info)
-            info['ch_names'].append(ch_name)
+            info._update_redundant()
         if self.preload:
             err = "Can't change stim channel after preloading data"
             raise NotImplementedError(err)
@@ -220,21 +219,9 @@ class RawKIT(_BaseRaw):
     @verbose
     def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
         """Read a chunk of raw data"""
-        with open(self._filenames[fi], 'rb', buffering=0) as fid:
-            # extract data
-            data_offset = KIT.RAW_OFFSET
-            fid.seek(data_offset)
-            # data offset info
-            data_offset = unpack('i', fid.read(KIT.INT))[0]
-            nchan = self._raw_extras[fi]['nchan']
-            buffer_size = stop - start
-            count = buffer_size * nchan
-            pointer = start * nchan * KIT.SHORT
-            fid.seek(data_offset + pointer)
-            data_ = np.fromfile(fid, dtype='h', count=count)
-
+        nchan = self._raw_extras[fi]['nchan']
+        data_left = (stop - start) * nchan
         # amplifier applies only to the sensor channels
-        data_.shape = (buffer_size, nchan)
         n_sens = self._raw_extras[fi]['n_sens']
         sensor_gain = self._raw_extras[fi]['sensor_gain'].copy()
         sensor_gain[:n_sens] = (sensor_gain[:n_sens] /
@@ -242,29 +229,47 @@ class RawKIT(_BaseRaw):
         conv_factor = np.array((KIT.VOLTAGE_RANGE /
                                 self._raw_extras[fi]['DYNAMIC_RANGE']) *
                                sensor_gain)
-        data_ = conv_factor[:, np.newaxis] * data_.T
-
-        # Create a synthetic channel
-        if self._raw_extras[fi]['stim'] is not None:
-            trig_chs = data_[self._raw_extras[fi]['stim'], :]
-            if self._raw_extras[fi]['slope'] == '+':
-                trig_chs = trig_chs > self._raw_extras[0]['stimthresh']
-            elif self._raw_extras[fi]['slope'] == '-':
-                trig_chs = trig_chs < self._raw_extras[0]['stimthresh']
-            else:
-                raise ValueError("slope needs to be '+' or '-'")
-
-            # trigger value
-            if self._raw_extras[0]['stim_code'] == 'binary':
-                ntrigchan = len(self._raw_extras[0]['stim'])
-                trig_vals = np.array(2 ** np.arange(ntrigchan), ndmin=2).T
-            else:
-                trig_vals = np.reshape(self._raw_extras[0]['stim'], (-1, 1))
-            trig_chs = trig_chs * trig_vals
-            stim_ch = np.array(trig_chs.sum(axis=0), ndmin=2)
-            data_ = np.vstack((data_, stim_ch))
+        n_bytes = 2
+        # Read up to 100 MB of data at a time.
+        blk_size = min(data_left, (100000000 // n_bytes // nchan) * nchan)
+        with open(self._filenames[fi], 'rb', buffering=0) as fid:
+            # extract data
+            data_offset = KIT.RAW_OFFSET
+            fid.seek(data_offset)
+            # data offset info
+            data_offset = unpack('i', fid.read(KIT.INT))[0]
+            pointer = start * nchan * KIT.SHORT
+            fid.seek(data_offset + pointer)
+            for blk_start in np.arange(0, data_left, blk_size) // nchan:
+                blk_size = min(blk_size, data_left - blk_start * nchan)
+                block = np.fromfile(fid, dtype='h', count=blk_size)
+                block = block.reshape(nchan, -1, order='F').astype(float)
+                blk_stop = blk_start + block.shape[1]
+                data_view = data[:, blk_start:blk_stop]
+                block *= conv_factor[:, np.newaxis]
+
+                # Create a synthetic channel
+                if self._raw_extras[fi]['stim'] is not None:
+                    trig_chs = block[self._raw_extras[fi]['stim'], :]
+                    if self._raw_extras[fi]['slope'] == '+':
+                        trig_chs = trig_chs > self._raw_extras[0]['stimthresh']
+                    elif self._raw_extras[fi]['slope'] == '-':
+                        trig_chs = trig_chs < self._raw_extras[0]['stimthresh']
+                    else:
+                        raise ValueError("slope needs to be '+' or '-'")
+                    # trigger value
+                    if self._raw_extras[0]['stim_code'] == 'binary':
+                        ntrigchan = len(self._raw_extras[0]['stim'])
+                        trig_vals = np.array(2 ** np.arange(ntrigchan),
+                                             ndmin=2).T
+                    else:
+                        trig_vals = np.reshape(self._raw_extras[0]['stim'],
+                                               (-1, 1))
+                    trig_chs = trig_chs * trig_vals
+                    stim_ch = np.array(trig_chs.sum(axis=0), ndmin=2)
+                    block = np.vstack((block, stim_ch))
+                _mult_cal_one(data_view, block, idx, None, mult)
         # cals are all unity, so can be ignored
-        _mult_cal_one(data, data_, idx, None, mult)
 
 
 class EpochsKIT(_BaseEpochs):
@@ -281,7 +286,7 @@ class EpochsKIT(_BaseEpochs):
         in the drop log.
     event_id : int | list of int | dict | None
         The id of the event to consider. If dict,
-        the keys can later be used to acces associated events. Example:
+        the keys can later be used to access associated events. Example:
         dict(auditory=1, visual=3). If int, a dict will be created with
         the id as string. If a list, all events with the IDs specified
         in the list are used. If None, all events will be used with
@@ -306,8 +311,8 @@ class EpochsKIT(_BaseEpochs):
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels)
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels)
                           )
     flat : dict | None
         Rejection parameters based on flatness of signal.
@@ -481,12 +486,11 @@ def _set_dig_kit(mrk, elp, hsp):
     if n_pts > KIT.DIG_POINTS:
         hsp = _decimate_points(hsp, res=5)
         n_new = len(hsp)
-        msg = ("The selected head shape contained {n_in} points, which is "
-               "more than recommended ({n_rec}), and was automatically "
-               "downsampled to {n_new} points. The preferred way to "
-               "downsample is using FastScan."
-               ).format(n_in=n_pts, n_rec=KIT.DIG_POINTS, n_new=n_new)
-        logger.warning(msg)
+        warn("The selected head shape contained {n_in} points, which is "
+             "more than recommended ({n_rec}), and was automatically "
+             "downsampled to {n_new} points. The preferred way to "
+             "downsample is using FastScan.".format(
+                 n_in=n_pts, n_rec=KIT.DIG_POINTS, n_new=n_new))
 
     if isinstance(elp, string_types):
         elp_points = _read_dig_points(elp)
@@ -655,7 +659,7 @@ def get_kit_info(rawfile):
         info = _empty_info(float(sqd['sfreq']))
         info.update(meas_date=int(time.time()), lowpass=sqd['lowpass'],
                     highpass=sqd['highpass'], filename=rawfile,
-                    nchan=sqd['nchan'], buffer_size_sec=1.)
+                    buffer_size_sec=1.)
 
         # Creates a list of dicts of meg channels for raw.info
         logger.info('Setting channel info structure...')
@@ -731,9 +735,7 @@ def get_kit_info(rawfile):
             chan_info['loc'] = np.zeros(12)
             chan_info['kind'] = FIFF.FIFFV_MISC_CH
             info['chs'].append(chan_info)
-
-        info['ch_names'] = ch_names['MEG'] + ch_names['MISC']
-
+    info._update_redundant()
     return info, sqd
 
 
@@ -746,17 +748,17 @@ def read_raw_kit(input_fname, mrk=None, elp=None, hsp=None, stim='>',
     ----------
     input_fname : str
         Path to the sqd file.
-    mrk : None | str | array_like, shape = (5, 3) | list of str or array_like
+    mrk : None | str | array_like, shape (5, 3) | list of str or array_like
         Marker points representing the location of the marker coils with
         respect to the MEG Sensors, or path to a marker file.
         If list, all of the markers will be averaged together.
-    elp : None | str | array_like, shape = (8, 3)
+    elp : None | str | array_like, shape (8, 3)
         Digitizer points representing the location of the fiducials and the
         marker coils with respect to the digitized head shape, or path to a
         file containing these points.
-    hsp : None | str | array, shape = (n_points, 3)
+    hsp : None | str | array, shape (n_points, 3)
         Digitizer head shape points, or path to head shape file. If more than
-        10`000 points are in the head shape, they are automatically decimated.
+        10,000 points are in the head shape, they are automatically decimated.
     stim : list of int | '<' | '>'
         Channel-value correspondence when converting KIT trigger channels to a
         Neuromag-style stim channel. For '<', the largest values are assigned
@@ -808,23 +810,23 @@ def read_epochs_kit(input_fname, events, event_id=None,
         by event_id, they will be marked as 'IGNORED' in the drop log.
     event_id : int | list of int | dict | None
         The id of the event to consider. If dict,
-        the keys can later be used to acces associated events. Example:
+        the keys can later be used to access associated events. Example:
         dict(auditory=1, visual=3). If int, a dict will be created with
         the id as string. If a list, all events with the IDs specified
         in the list are used. If None, all events will be used with
         and a dict is created with string integer names corresponding
         to the event id integers.
-    mrk : None | str | array_like, shape = (5, 3) | list of str or array_like
+    mrk : None | str | array_like, shape (5, 3) | list of str or array_like
         Marker points representing the location of the marker coils with
         respect to the MEG Sensors, or path to a marker file.
         If list, all of the markers will be averaged together.
-    elp : None | str | array_like, shape = (8, 3)
+    elp : None | str | array_like, shape (8, 3)
         Digitizer points representing the location of the fiducials and the
         marker coils with respect to the digitized head shape, or path to a
         file containing these points.
-    hsp : None | str | array, shape = (n_points, 3)
+    hsp : None | str | array, shape (n_points, 3)
         Digitizer head shape points, or path to head shape file. If more than
-        10`000 points are in the head shape, they are automatically decimated.
+        10,000 points are in the head shape, they are automatically decimated.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
diff --git a/mne/io/matrix.py b/mne/io/matrix.py
index caecafa..e636a65 100644
--- a/mne/io/matrix.py
+++ b/mne/io/matrix.py
@@ -10,19 +10,16 @@ from .write import (write_int, start_block, end_block, write_float_matrix,
 from ..utils import logger, verbose
 
 
-def _transpose_named_matrix(mat, copy=True):
-    """Transpose mat inplace (no copy)
-    """
-    if copy is True:
-        mat = mat.copy()
+def _transpose_named_matrix(mat):
+    """Transpose mat inplace (no copy)"""
     mat['nrow'], mat['ncol'] = mat['ncol'], mat['nrow']
     mat['row_names'], mat['col_names'] = mat['col_names'], mat['row_names']
     mat['data'] = mat['data'].T
-    return mat
 
 
 @verbose
-def _read_named_matrix(fid, node, matkind, indent='    ', verbose=None):
+def _read_named_matrix(fid, node, matkind, indent='    ', transpose=False,
+                       verbose=None):
     """Read named matrix from the given node
 
     Parameters
@@ -33,6 +30,8 @@ def _read_named_matrix(fid, node, matkind, indent='    ', verbose=None):
         The node in the tree.
     matkind : int
         The type of matrix.
+    transpose : bool
+        If True, transpose the matrix. Default is False.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -84,6 +83,8 @@ def _read_named_matrix(fid, node, matkind, indent='    ', verbose=None):
 
     mat = dict(nrow=nrow, ncol=ncol, row_names=row_names, col_names=col_names,
                data=data)
+    if transpose:
+        _transpose_named_matrix(mat)
     return mat
 
 
diff --git a/mne/io/meas_info.py b/mne/io/meas_info.py
index efaf554..4ecd461 100644
--- a/mne/io/meas_info.py
+++ b/mne/io/meas_info.py
@@ -4,7 +4,6 @@
 #
 # License: BSD (3-clause)
 
-from warnings import warn
 from copy import deepcopy
 from datetime import datetime as dt
 import os.path as op
@@ -17,14 +16,14 @@ from .constants import FIFF
 from .open import fiff_open
 from .tree import dir_tree_find
 from .tag import read_tag, find_tag
-from .proj import _read_proj, _write_proj, _uniquify_projs
+from .proj import _read_proj, _write_proj, _uniquify_projs, _normalize_proj
 from .ctf_comp import read_ctf_comp, write_ctf_comp
 from .write import (start_file, end_file, start_block, end_block,
                     write_string, write_dig_point, write_float, write_int,
                     write_coord_trans, write_ch_info, write_name_list,
                     write_julian, write_float_matrix)
 from .proc_history import _read_proc_history, _write_proc_history
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from ..fixes import Counter
 from .. import __version__
 from ..externals.six import b, BytesIO, string_types, text_type
@@ -34,11 +33,15 @@ _kind_dict = dict(
     eeg=(FIFF.FIFFV_EEG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
     mag=(FIFF.FIFFV_MEG_CH, FIFF.FIFFV_COIL_VV_MAG_T3, FIFF.FIFF_UNIT_T),
     grad=(FIFF.FIFFV_MEG_CH, FIFF.FIFFV_COIL_VV_PLANAR_T1, FIFF.FIFF_UNIT_T_M),
+    ref_meg=(FIFF.FIFFV_REF_MEG_CH, FIFF.FIFFV_COIL_VV_MAG_T3,
+             FIFF.FIFF_UNIT_T),
     misc=(FIFF.FIFFV_MISC_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_NONE),
     stim=(FIFF.FIFFV_STIM_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
     eog=(FIFF.FIFFV_EOG_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
     ecg=(FIFF.FIFFV_ECG_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
-    seeg=(FIFF.FIFFV_SEEG_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
+    seeg=(FIFF.FIFFV_SEEG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
+    bio=(FIFF.FIFFV_BIO_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
+    ecog=(FIFF.FIFFV_ECOG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
 )
 
 
@@ -60,8 +63,11 @@ class Info(dict):
     bads : list of str
         List of bad (noisy/broken) channels, by name. These channels will by
         default be ignored by many processing steps.
-    ch_names : list of str
+    ch_names : list-like of str (read-only)
         The names of the channels.
+        This object behaves like a read-only Python list. Behind the scenes
+        it iterates over the channels dictionaries in `info['chs']`:
+        `info['ch_names'][x] == info['chs'][x]['ch_name']`
     chs : list of dict
         A list of channel information structures.
         See: :ref:`faq` for details.
@@ -142,7 +148,6 @@ class Info(dict):
         Name of the project the experiment belongs to.
     subject_info : dict | None
         Information about the subject.
-        See: :ref:`subject_info` for details
     proc_history : list of dict | None | not present in dict
         The SSS info, the CTC correction and the calibaraions from the SSS
         processing logs inside of a raw file.
@@ -157,7 +162,23 @@ class Info(dict):
         info : instance of Info
             The copied info.
         """
-        return Info(super(Info, self).copy())
+        return Info(deepcopy(self))
+
+    def normalize_proj(self):
+        """(Re-)Normalize projection vectors after subselection
+
+        Applying projection after sub-selecting a set of channels that
+        were originally used to compute the original projection vectors
+        can be dangerous (e.g., if few channels remain, most power was
+        in channels that are no longer picked, etc.). By default, mne
+        will emit a warning when this is done.
+
+        This function will re-normalize projectors to use only the
+        remaining channels, thus avoiding that warning. Only use this
+        function if you're confident that the projection vectors still
+        adequately capture the original signal of interest.
+        """
+        _normalize_proj(self)
 
     def __repr__(self):
         """Summarize info instead of printing all"""
@@ -179,7 +200,7 @@ class Info(dict):
                 if len(entr) >= 56:
                     entr = _summarize_str(entr)
             elif k == 'meas_date' and np.iterable(v):
-                # first entire in meas_date is meaningful
+                # first entry in meas_date is meaningful
                 entr = dt.fromtimestamp(v[0]).strftime('%Y-%m-%d %H:%M:%S')
             else:
                 this_len = (len(v) if hasattr(v, '__len__') else
@@ -208,6 +229,10 @@ class Info(dict):
     def _anonymize(self):
         if self.get('subject_info') is not None:
             del self['subject_info']
+        self['meas_date'] = [0, 0]
+        for key_1 in ('file_id', 'meas_id'):
+            for key_2 in ('secs', 'msecs', 'usecs'):
+                self[key_1][key_2] = 0
 
     def _check_consistency(self):
         """Do some self-consistency checks and datatype tweaks"""
@@ -215,17 +240,32 @@ class Info(dict):
         if len(missing) > 0:
             raise RuntimeError('bad channel(s) %s marked do not exist in info'
                                % (missing,))
+
         chs = [ch['ch_name'] for ch in self['chs']]
         if len(self['ch_names']) != len(chs) or any(
                 ch_1 != ch_2 for ch_1, ch_2 in zip(self['ch_names'], chs)) or \
                 self['nchan'] != len(chs):
             raise RuntimeError('info channel name inconsistency detected, '
                                'please notify mne-python developers')
+
         # make sure we have the proper datatypes
         for key in ('sfreq', 'highpass', 'lowpass'):
             if self.get(key) is not None:
                 self[key] = float(self[key])
 
+        # make sure channel names are unique
+        unique_ids = np.unique(self['ch_names'], return_index=True)[1]
+        if len(unique_ids) != self['nchan']:
+            dups = set(self['ch_names'][x]
+                       for x in np.setdiff1d(range(self['nchan']), unique_ids))
+            raise RuntimeError('Channel names are not unique, found '
+                               'duplicates for: %s' % dups)
+
+    def _update_redundant(self):
+        """Update the redundant entries"""
+        self['ch_names'] = [ch['ch_name'] for ch in self['chs']]
+        self['nchan'] = len(self['chs'])
+
 
 def read_fiducials(fname):
     """Read fiducials from a fiff file
@@ -302,8 +342,29 @@ def write_fiducials(fname, pts, coord_frame=0):
     end_file(fid)
 
 
+def _read_dig_fif(fid, meas_info):
+    """Helper to read digitizer data from a FIFF file"""
+    isotrak = dir_tree_find(meas_info, FIFF.FIFFB_ISOTRAK)
+    dig = None
+    if len(isotrak) == 0:
+        logger.info('Isotrak not found')
+    elif len(isotrak) > 1:
+        warn('Multiple Isotrak found')
+    else:
+        isotrak = isotrak[0]
+        dig = []
+        for k in range(isotrak['nent']):
+            kind = isotrak['directory'][k].kind
+            pos = isotrak['directory'][k].pos
+            if kind == FIFF.FIFF_DIG_POINT:
+                tag = read_tag(fid, pos)
+                dig.append(tag.data)
+                dig[-1]['coord_frame'] = FIFF.FIFFV_COORD_HEAD
+    return dig
+
+
 def _read_dig_points(fname, comments='%'):
-    """Read digitizer data from file.
+    """Read digitizer data from a text file.
 
     This function can read space-delimited text files of digitizer data.
 
@@ -329,7 +390,7 @@ def _read_dig_points(fname, comments='%'):
 
 
 def _write_dig_points(fname, dig_points):
-    """Write points to file
+    """Write points to text file
 
     Parameters
     ----------
@@ -362,7 +423,7 @@ def _write_dig_points(fname, dig_points):
 
 
 def _make_dig_points(nasion=None, lpa=None, rpa=None, hpi=None,
-                     dig_points=None):
+                     dig_points=None, dig_ch_pos=None):
     """Constructs digitizer info for the info.
 
     Parameters
@@ -377,6 +438,8 @@ def _make_dig_points(nasion=None, lpa=None, rpa=None, hpi=None,
         Points designated as head position indicator points.
     dig_points : array-like | numpy.ndarray, shape (n_points, 3)
         Points designed as the headshape points.
+    dig_ch_pos : dict
+        Dict of EEG channel positions.
 
     Returns
     -------
@@ -389,7 +452,7 @@ def _make_dig_points(nasion=None, lpa=None, rpa=None, hpi=None,
         if lpa.shape == (3,):
             dig.append({'r': lpa, 'ident': FIFF.FIFFV_POINT_LPA,
                         'kind': FIFF.FIFFV_POINT_CARDINAL,
-                        'coord_frame':  FIFF.FIFFV_COORD_HEAD})
+                        'coord_frame': FIFF.FIFFV_COORD_HEAD})
         else:
             msg = ('LPA should have the shape (3,) instead of %s'
                    % (lpa.shape,))
@@ -399,7 +462,7 @@ def _make_dig_points(nasion=None, lpa=None, rpa=None, hpi=None,
         if nasion.shape == (3,):
             dig.append({'r': nasion, 'ident': FIFF.FIFFV_POINT_NASION,
                         'kind': FIFF.FIFFV_POINT_CARDINAL,
-                        'coord_frame':  FIFF.FIFFV_COORD_HEAD})
+                        'coord_frame': FIFF.FIFFV_COORD_HEAD})
         else:
             msg = ('Nasion should have the shape (3,) instead of %s'
                    % (nasion.shape,))
@@ -409,7 +472,7 @@ def _make_dig_points(nasion=None, lpa=None, rpa=None, hpi=None,
         if rpa.shape == (3,):
             dig.append({'r': rpa, 'ident': FIFF.FIFFV_POINT_RPA,
                         'kind': FIFF.FIFFV_POINT_CARDINAL,
-                        'coord_frame':  FIFF.FIFFV_COORD_HEAD})
+                        'coord_frame': FIFF.FIFFV_COORD_HEAD})
         else:
             msg = ('RPA should have the shape (3,) instead of %s'
                    % (rpa.shape,))
@@ -436,7 +499,12 @@ def _make_dig_points(nasion=None, lpa=None, rpa=None, hpi=None,
             msg = ('Points should have the shape (n_points, 3) instead of '
                    '%s' % (dig_points.shape,))
             raise ValueError(msg)
-
+    if dig_ch_pos is not None:
+        keys = sorted(dig_ch_pos.keys())
+        for key in keys:
+            dig.append({'r': dig_ch_pos[key], 'ident': int(key[-3:]),
+                        'kind': FIFF.FIFFV_POINT_EEG,
+                        'coord_frame': FIFF.FIFFV_COORD_HEAD})
     return dig
 
 
@@ -453,8 +521,8 @@ def read_info(fname, verbose=None):
 
     Returns
     -------
-    info : instance of mne.io.meas_info.Info
-       Info on dataset.
+    info : instance of Info
+       Measurement information for the dataset.
     """
     f, tree, _ = fiff_open(fname)
     with f as fid:
@@ -508,7 +576,7 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
 
     Returns
     -------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
        Info on dataset.
     meas : dict
         Node in tree that contains the info.
@@ -545,7 +613,7 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
     proj_name = None
     line_freq = None
     custom_ref_applied = False
-    p = 0
+    xplotter_layout = None
     for k in range(meas_info['nent']):
         kind = meas_info['directory'][k].kind
         pos = meas_info['directory'][k].pos
@@ -558,7 +626,6 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
         elif kind == FIFF.FIFF_CH_INFO:
             tag = read_tag(fid, pos)
             chs.append(tag.data)
-            p += 1
         elif kind == FIFF.FIFF_LOWPASS:
             tag = read_tag(fid, pos)
             lowpass = float(tag.data)
@@ -599,10 +666,13 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
         elif kind in [FIFF.FIFF_MNE_CUSTOM_REF, 236]:  # 236 used before v0.11
             tag = read_tag(fid, pos)
             custom_ref_applied = bool(tag.data)
+        elif kind == FIFF.FIFF_XPLOTTER_LAYOUT:
+            tag = read_tag(fid, pos)
+            xplotter_layout = str(tag.data)
 
     # Check that we have everything we need
     if nchan is None:
-        raise ValueError('Number of channels in not defined')
+        raise ValueError('Number of channels is not defined')
 
     if sfreq is None:
         raise ValueError('Sampling frequency is not defined')
@@ -633,22 +703,7 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
                         ctf_head_t = cand
 
     #   Locate the Polhemus data
-    isotrak = dir_tree_find(meas_info, FIFF.FIFFB_ISOTRAK)
-    dig = None
-    if len(isotrak) == 0:
-        logger.info('Isotrak not found')
-    elif len(isotrak) > 1:
-        warn('Multiple Isotrak found')
-    else:
-        isotrak = isotrak[0]
-        dig = []
-        for k in range(isotrak['nent']):
-            kind = isotrak['directory'][k].kind
-            pos = isotrak['directory'][k].pos
-            if kind == FIFF.FIFF_DIG_POINT:
-                tag = read_tag(fid, pos)
-                dig.append(tag.data)
-                dig[-1]['coord_frame'] = FIFF.FIFFV_COORD_HEAD
+    dig = _read_dig_fif(fid, meas_info)
 
     #   Locate the acquisition information
     acqpars = dir_tree_find(meas_info, FIFF.FIFFB_DACQ_PARS)
@@ -858,11 +913,9 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
     info['proj_name'] = proj_name
 
     if meas_date is None:
-        info['meas_date'] = [info['meas_id']['secs'], info['meas_id']['usecs']]
-    else:
-        info['meas_date'] = meas_date
+        meas_date = [info['meas_id']['secs'], info['meas_id']['usecs']]
+    info['meas_date'] = meas_date
 
-    info['nchan'] = nchan
     info['sfreq'] = sfreq
     info['highpass'] = highpass if highpass is not None else 0.
     info['lowpass'] = lowpass if lowpass is not None else info['sfreq'] / 2.0
@@ -871,7 +924,6 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
     #   Add the channel information and make a list of channel names
     #   for convenience
     info['chs'] = chs
-    info['ch_names'] = [ch['ch_name'] for ch in chs]
 
     #
     #  Add the coordinate transformations
@@ -888,6 +940,7 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
     #   All kinds of auxliary stuff
     info['dig'] = dig
     info['bads'] = bads
+    info._update_redundant()
     if clean_bads:
         info['bads'] = [b for b in bads if b in info['ch_names']]
     info['projs'] = projs
@@ -895,8 +948,8 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
     info['acq_pars'] = acq_pars
     info['acq_stim'] = acq_stim
     info['custom_ref_applied'] = custom_ref_applied
+    info['xplotter_layout'] = xplotter_layout
     info._check_consistency()
-
     return info, meas
 
 
@@ -907,7 +960,7 @@ def write_meas_info(fid, info, data_type=None, reset_range=True):
     ----------
     fid : file
         Open file descriptor.
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info structure.
     data_type : int
         The data_type in case it is necessary. Should be 4 (FIFFT_FLOAT),
@@ -1056,6 +1109,8 @@ def write_meas_info(fid, info, data_type=None, reset_range=True):
         write_int(fid, FIFF.FIFF_DATA_PACK, data_type)
     if info.get('custom_ref_applied'):
         write_int(fid, FIFF.FIFF_MNE_CUSTOM_REF, info['custom_ref_applied'])
+    if info.get('xplotter_layout'):
+        write_string(fid, FIFF.FIFF_XPLOTTER_LAYOUT, info['xplotter_layout'])
 
     #  Channel information
     for k, c in enumerate(info['chs']):
@@ -1121,7 +1176,7 @@ def write_info(fname, info, data_type=None, reset_range=True):
     ----------
     fname : str
         The name of the file. Should end by -info.fif.
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info structure
     data_type : int
         The data_type in case it is necessary. Should be 4 (FIFFT_FLOAT),
@@ -1143,8 +1198,13 @@ def _is_equal_dict(dicts):
     is_equal = []
     for d in tests:
         k0, v0 = d[0]
-        is_equal.append(all(np.all(k == k0) and
-                        np.all(v == v0) for k, v in d))
+        if (isinstance(v0, (list, np.ndarray)) and len(v0) > 0 and
+                isinstance(v0[0], dict)):
+            for k, v in d:
+                is_equal.append((k0 == k) and _is_equal_dict(v))
+        else:
+            is_equal.append(all(np.all(k == k0) and
+                            np.all(v == v0) for k, v in d))
     return all(is_equal)
 
 
@@ -1229,7 +1289,7 @@ def _merge_dict_values(dicts, key, verbose=None):
 
 
 @verbose
-def _merge_info(infos, verbose=None):
+def _merge_info(infos, force_update_to_first=False, verbose=None):
     """Merge multiple measurement info dictionaries.
 
      - Fields that are present in only one info object will be used in the
@@ -1245,6 +1305,10 @@ def _merge_info(infos, verbose=None):
     ----------
     infos | list of instance of Info
         Info objects to merge into one info object.
+    force_update_to_first : bool
+        If True, force the fields for objects in `info` will be updated
+        to match those in the first item. Use at your own risk, as this
+        may overwrite important metadata.
     verbose : bool, str, int, or NonIe
         If not None, override default verbose level (see mne.verbose).
 
@@ -1255,18 +1319,20 @@ def _merge_info(infos, verbose=None):
     """
     for info in infos:
         info._check_consistency()
+    if force_update_to_first is True:
+        infos = deepcopy(infos)
+        _force_update_info(infos[0], infos[1:])
     info = Info()
-    ch_names = _merge_dict_values(infos, 'ch_names')
-    duplicates = set([ch for ch in ch_names if ch_names.count(ch) > 1])
+    info['chs'] = []
+    for this_info in infos:
+        info['chs'].extend(this_info['chs'])
+    info._update_redundant()
+    duplicates = set([ch for ch in info['ch_names']
+                      if info['ch_names'].count(ch) > 1])
     if len(duplicates) > 0:
         msg = ("The following channels are present in more than one input "
                "measurement info objects: %s" % list(duplicates))
         raise ValueError(msg)
-    info['nchan'] = len(ch_names)
-    info['ch_names'] = ch_names
-    info['chs'] = []
-    for this_info in infos:
-        info['chs'].extend(this_info['chs'])
 
     transforms = ['ctf_head_t', 'dev_head_t', 'dev_ctf_t']
     for trans_name in transforms:
@@ -1290,7 +1356,7 @@ def _merge_info(infos, verbose=None):
                     'hpi_results', 'hpi_meas', 'hpi_subsystem', 'events',
                     'line_freq', 'lowpass', 'meas_date', 'meas_id',
                     'proj_id', 'proj_name', 'projs', 'sfreq',
-                    'subject_info', 'sfreq']
+                    'subject_info', 'sfreq', 'xplotter_layout']
 
     for k in other_fields:
         info[k] = _merge_dict_values(infos, k)
@@ -1310,7 +1376,8 @@ def create_info(ch_names, sfreq, ch_types=None, montage=None):
         Sample rate of the data.
     ch_types : list of str | str
         Channel types. If None, data are assumed to be misc.
-        Currently supported fields are "mag", "grad", "eeg", and "misc".
+        Currently supported fields are 'ecg', 'bio', 'stim', 'eog', 'misc',
+        'seeg', 'ecog', 'mag', 'eeg', 'ref_meg' or 'grad'.
         If str, then all channels are assumed to be of the same type.
     montage : None | str | Montage | DigMontage | list
         A montage containing channel positions. If str or Montage is
@@ -1346,8 +1413,6 @@ def create_info(ch_names, sfreq, ch_types=None, montage=None):
         raise ValueError('ch_types and ch_names must be the same length')
     info = _empty_info(sfreq)
     info['meas_date'] = np.array([0, 0], np.int32)
-    info['ch_names'] = ch_names
-    info['nchan'] = nchan
     loc = np.concatenate((np.zeros(3), np.eye(3).ravel())).astype(np.float32)
     for ci, (name, kind) in enumerate(zip(ch_names, ch_types)):
         if not isinstance(name, string_types):
@@ -1363,6 +1428,7 @@ def create_info(ch_names, sfreq, ch_types=None, montage=None):
                          unit=kind[2], coord_frame=FIFF.FIFFV_COORD_UNKNOWN,
                          ch_name=name, scanno=ci + 1, logno=ci + 1)
         info['chs'].append(chan_info)
+    info._update_redundant()
     if montage is not None:
         from ..channels.montage import (Montage, DigMontage, _set_montage,
                                         read_montage)
@@ -1378,6 +1444,7 @@ def create_info(ch_names, sfreq, ch_types=None, montage=None):
                 raise TypeError('Montage must be an instance of Montage, '
                                 'DigMontage, a list of montages, or filepath, '
                                 'not %s.' % type(montage))
+    info._check_consistency()
     return info
 
 
@@ -1388,6 +1455,7 @@ RAW_INFO_FIELDS = (
     'file_id', 'filename', 'highpass', 'hpi_meas', 'hpi_results',
     'hpi_subsystem', 'line_freq', 'lowpass', 'meas_date', 'meas_id', 'nchan',
     'proj_id', 'proj_name', 'projs', 'sfreq', 'subject_info',
+    'xplotter_layout',
 )
 
 
@@ -1399,23 +1467,49 @@ def _empty_info(sfreq):
         'dev_ctf_t', 'dig', 'experimenter',
         'file_id', 'filename', 'highpass', 'hpi_subsystem', 'line_freq',
         'lowpass', 'meas_date', 'meas_id', 'proj_id', 'proj_name',
-        'subject_info',
-    )
-    _list_keys = (
-        'bads', 'ch_names', 'chs', 'comps', 'events', 'hpi_meas',
-        'hpi_results', 'projs',
+        'subject_info', 'xplotter_layout',
     )
+    _list_keys = ('bads', 'chs', 'comps', 'events', 'hpi_meas', 'hpi_results',
+                  'projs')
     info = Info()
     for k in _none_keys:
         info[k] = None
     for k in _list_keys:
         info[k] = list()
     info['custom_ref_applied'] = False
-    info['nchan'] = 0
     info['dev_head_t'] = Transform('meg', 'head', np.eye(4))
     info['highpass'] = 0.
     info['sfreq'] = float(sfreq)
     info['lowpass'] = info['sfreq'] / 2.
-    assert set(info.keys()) == set(RAW_INFO_FIELDS)
+    info._update_redundant()
     info._check_consistency()
     return info
+
+
+def _force_update_info(info_base, info_target):
+    """Update target info objects with values from info base.
+
+    Note that values in info_target will be overwritten by those in info_base.
+    This will overwrite all fields except for: 'chs', 'ch_names', 'nchan'.
+
+    Parameters
+    ----------
+    info_base : mne.Info
+        The Info object you want to use for overwriting values
+        in target Info objects.
+    info_target : mne.Info | list of mne.Info
+        The Info object(s) you wish to overwrite using info_base. These objects
+        will be modified in-place.
+    """
+    exclude_keys = ['chs', 'ch_names', 'nchan']
+    info_target = np.atleast_1d(info_target).ravel()
+    all_infos = np.hstack([info_base, info_target])
+    for ii in all_infos:
+        if not isinstance(ii, Info):
+            raise ValueError('Inputs must be of type Info. '
+                             'Found type %s' % type(ii))
+    for key, val in info_base.items():
+        if key in exclude_keys:
+            continue
+        for i_targ in info_target:
+            i_targ[key] = val
diff --git a/mne/io/nicolet/nicolet.py b/mne/io/nicolet/nicolet.py
index 85954d4..05956bb 100644
--- a/mne/io/nicolet/nicolet.py
+++ b/mne/io/nicolet/nicolet.py
@@ -8,7 +8,7 @@ import datetime
 import calendar
 
 from ...utils import logger
-from ..utils import _read_segments_file, _find_channels
+from ..utils import _read_segments_file, _find_channels, _create_chs
 from ..base import _BaseRaw, _check_update_montage
 from ..meas_info import _empty_info
 from ..constants import FIFF
@@ -103,10 +103,9 @@ def _get_nicolet_info(fname, ch_type, eog, ecg, emg, misc):
     date = datetime.datetime(int(date[0]), int(date[1]), int(date[2]),
                              int(time[0]), int(time[1]), int(sec), int(msec))
     info = _empty_info(header_info['sample_freq'])
-    info.update({'filename': fname, 'nchan': header_info['num_channels'],
+    info.update({'filename': fname,
                  'meas_date': calendar.timegm(date.utctimetuple()),
-                 'ch_names': ch_names, 'description': None,
-                 'buffer_size_sec': 10.})
+                 'description': None, 'buffer_size_sec': 10.})
 
     if ch_type == 'eeg':
         ch_coil = FIFF.FIFFV_COIL_EEG
@@ -117,33 +116,12 @@ def _get_nicolet_info(fname, ch_type, eog, ecg, emg, misc):
     else:
         raise TypeError("Channel type not recognized. Available types are "
                         "'eeg' and 'seeg'.")
-    cal = header_info['conversion_factor'] * 1e-6
-    for idx, ch_name in enumerate(ch_names):
-        if ch_name in eog or idx in eog:
-            coil_type = FIFF.FIFFV_COIL_NONE
-            kind = FIFF.FIFFV_EOG_CH
-        elif ch_name in ecg or idx in ecg:
-            coil_type = FIFF.FIFFV_COIL_NONE
-            kind = FIFF.FIFFV_ECG_CH
-        elif ch_name in emg or idx in emg:
-            coil_type = FIFF.FIFFV_COIL_NONE
-            kind = FIFF.FIFFV_EMG_CH
-        elif ch_name in misc or idx in misc:
-            coil_type = FIFF.FIFFV_COIL_NONE
-            kind = FIFF.FIFFV_MISC_CH
-        else:
-            coil_type = ch_coil
-            kind = ch_kind
-        chan_info = {'cal': cal, 'logno': idx + 1, 'scanno': idx + 1,
-                     'range': 1.0, 'unit_mul': 0., 'ch_name': ch_name,
-                     'unit': FIFF.FIFF_UNIT_V,
-                     'coord_frame': FIFF.FIFFV_COORD_HEAD,
-                     'coil_type': coil_type, 'kind': kind, 'loc': np.zeros(12)}
-        info['chs'].append(chan_info)
-
+    cals = np.repeat(header_info['conversion_factor'] * 1e-6, len(ch_names))
+    info['chs'] = _create_chs(ch_names, cals, ch_coil, ch_kind, eog, ecg, emg,
+                              misc)
     info['highpass'] = 0.
     info['lowpass'] = info['sfreq'] / 2.0
-
+    info._update_redundant()
     return info, header_info
 
 
diff --git a/mne/io/pick.py b/mne/io/pick.py
index 8370e53..6b8d6d8 100644
--- a/mne/io/pick.py
+++ b/mne/io/pick.py
@@ -28,7 +28,7 @@ def channel_type(info, idx):
     -------
     type : 'grad' | 'mag' | 'eeg' | 'stim' | 'eog' | 'emg' | 'ecg'
            'ref_meg' | 'resp' | 'exci' | 'ias' | 'syst' | 'misc'
-           'seeg' | 'chpi'
+           'seeg' | 'bio' | 'chpi' | 'dipole' | 'gof' | 'ecog'
         Type of channel
     """
     kind = info['chs'][idx]['kind']
@@ -59,13 +59,21 @@ def channel_type(info, idx):
         return 'ias'
     elif kind == FIFF.FIFFV_SYST_CH:
         return 'syst'
-    elif kind in [FIFF.FIFFV_SEEG_CH, 702]:  # 702 was used before v0.11
+    elif kind == FIFF.FIFFV_SEEG_CH:
         return 'seeg'
+    elif kind == FIFF.FIFFV_BIO_CH:
+        return 'bio'
     elif kind in [FIFF.FIFFV_QUAT_0, FIFF.FIFFV_QUAT_1, FIFF.FIFFV_QUAT_2,
                   FIFF.FIFFV_QUAT_3, FIFF.FIFFV_QUAT_4, FIFF.FIFFV_QUAT_5,
                   FIFF.FIFFV_QUAT_6, FIFF.FIFFV_HPI_G, FIFF.FIFFV_HPI_ERR,
                   FIFF.FIFFV_HPI_MOV]:
         return 'chpi'  # channels relative to head position monitoring
+    elif kind == FIFF.FIFFV_DIPOLE_WAVE:
+        return 'dipole'
+    elif kind == FIFF.FIFFV_GOODNESS_FIT:
+        return 'gof'
+    elif kind == FIFF.FIFFV_ECOG_CH:
+        return 'ecog'
     raise Exception('Unknown channel type')
 
 
@@ -97,6 +105,11 @@ def pick_channels(ch_names, include, exclude=[]):
         raise RuntimeError('ch_names is not a unique list, picking is unsafe')
     _check_excludes_includes(include)
     _check_excludes_includes(exclude)
+    if not isinstance(include, set):
+        include = set(include)
+    if not isinstance(exclude, set):
+        exclude = set(exclude)
+
     sel = []
     for k, name in enumerate(ch_names):
         if (len(include) == 0 or name in include) and name not in exclude:
@@ -166,8 +179,9 @@ def _check_meg_type(meg, allow_auto=False):
 
 def pick_types(info, meg=True, eeg=False, stim=False, eog=False, ecg=False,
                emg=False, ref_meg='auto', misc=False, resp=False, chpi=False,
-               exci=False, ias=False, syst=False, seeg=False,
-               include=[], exclude='bads', selection=None):
+               exci=False, ias=False, syst=False, seeg=False, dipole=False,
+               gof=False, bio=False, ecog=False, include=[], exclude='bads',
+               selection=None):
     """Pick channels by type and names
 
     Parameters
@@ -207,7 +221,15 @@ def pick_types(info, meg=True, eeg=False, stim=False, eog=False, ecg=False,
     syst : bool
         System status channel information (on Triux systems only).
     seeg : bool
-        Stereotactic EEG channels
+        Stereotactic EEG channels.
+    dipole : bool
+        Dipole time course channels.
+    gof : bool
+        Dipole goodness of fit channels.
+    bio : bool
+        Bio channels.
+    ecog : bool
+        Electrocorticography channels.
     include : list of string
         List of additional channels to include. If empty do not include any.
     exclude : list of string | str
@@ -246,8 +268,16 @@ def pick_types(info, meg=True, eeg=False, stim=False, eog=False, ecg=False,
         ref_meg = ('comps' in info and info['comps'] is not None and
                    len(info['comps']) > 0)
 
+    for param in (eeg, stim, eog, ecg, emg, misc, resp, chpi, exci,
+                  ias, syst, seeg, dipole, gof, bio, ecog):
+        if not isinstance(param, bool):
+            w = ('Parameters for all channel types (with the exception '
+                 'of "meg" and "ref_meg") must be of type bool, not {}.')
+            raise ValueError(w.format(type(param)))
+
     for k in range(nchan):
         kind = info['chs'][k]['kind']
+        # XXX eventually we should de-duplicate this with channel_type!
         if kind == FIFF.FIFFV_MEG_CH:
             pick[k] = _triage_meg_pick(info['chs'][k], meg)
         elif kind == FIFF.FIFFV_EEG_CH and eeg:
@@ -268,8 +298,7 @@ def pick_types(info, meg=True, eeg=False, stim=False, eog=False, ecg=False,
             pick[k] = True
         elif kind == FIFF.FIFFV_SYST_CH and syst:
             pick[k] = True
-        elif kind in [FIFF.FIFFV_SEEG_CH, 702] and seeg:
-            # Constant 702 was used before v0.11
+        elif kind == FIFF.FIFFV_SEEG_CH and seeg:
             pick[k] = True
         elif kind == FIFF.FIFFV_IAS_CH and ias:
             pick[k] = True
@@ -280,6 +309,14 @@ def pick_types(info, meg=True, eeg=False, stim=False, eog=False, ecg=False,
                       FIFF.FIFFV_QUAT_6, FIFF.FIFFV_HPI_G, FIFF.FIFFV_HPI_ERR,
                       FIFF.FIFFV_HPI_MOV] and chpi:
             pick[k] = True
+        elif kind == FIFF.FIFFV_DIPOLE_WAVE and dipole:
+            pick[k] = True
+        elif kind == FIFF.FIFFV_GOODNESS_FIT and gof:
+            pick[k] = True
+        elif kind == FIFF.FIFFV_BIO_CH and bio:
+            pick[k] = True
+        elif kind == FIFF.FIFFV_ECOG_CH and ecog:
+            pick[k] = True
 
     # restrict channels to selection if provided
     if selection is not None:
@@ -302,7 +339,7 @@ def pick_types(info, meg=True, eeg=False, stim=False, eog=False, ecg=False,
     return sel
 
 
-def pick_info(info, sel=[], copy=True):
+def pick_info(info, sel=(), copy=True):
     """Restrict an info structure to a selection of channels
 
     Parameters
@@ -328,8 +365,7 @@ def pick_info(info, sel=[], copy=True):
         raise ValueError('No channels match the selection.')
 
     info['chs'] = [info['chs'][k] for k in sel]
-    info['ch_names'] = [info['ch_names'][k] for k in sel]
-    info['nchan'] = len(sel)
+    info._update_redundant()
     info['bads'] = [ch for ch in info['bads'] if ch in info['ch_names']]
 
     comps = deepcopy(info['comps'])
@@ -343,7 +379,7 @@ def pick_info(info, sel=[], copy=True):
         c['data']['row_names'] = row_names
         c['data']['data'] = c['data']['data'][row_idx]
     info['comps'] = comps
-
+    info._check_consistency()
     return info
 
 
@@ -463,9 +499,8 @@ def pick_channels_forward(orig, include=[], exclude=[], verbose=None):
     fwd['sol']['row_names'] = ch_names
 
     # Pick the appropriate channel names from the info-dict using sel_info
-    fwd['info']['ch_names'] = [fwd['info']['ch_names'][k] for k in sel_info]
     fwd['info']['chs'] = [fwd['info']['chs'][k] for k in sel_info]
-    fwd['info']['nchan'] = nuse
+    fwd['info']._update_redundant()
     fwd['info']['bads'] = [b for b in fwd['info']['bads'] if b in ch_names]
 
     if fwd['sol_grad'] is not None:
@@ -479,7 +514,7 @@ def pick_channels_forward(orig, include=[], exclude=[], verbose=None):
 
 
 def pick_types_forward(orig, meg=True, eeg=False, ref_meg=True, seeg=False,
-                       include=[], exclude=[]):
+                       ecog=False, include=[], exclude=[]):
     """Pick by channel type and names from a forward operator
 
     Parameters
@@ -496,6 +531,8 @@ def pick_types_forward(orig, meg=True, eeg=False, ref_meg=True, seeg=False,
         If True include CTF / 4D reference channels
     seeg : bool
         If True include stereotactic EEG channels
+    ecog : bool
+        If True include electrocorticography channels
     include : list of string
         List of additional channels to include. If empty do not include any.
     exclude : list of string | str
@@ -508,20 +545,20 @@ def pick_types_forward(orig, meg=True, eeg=False, ref_meg=True, seeg=False,
         Forward solution restricted to selected channel types.
     """
     info = orig['info']
-    sel = pick_types(info, meg, eeg, ref_meg=ref_meg, seeg=seeg,
+    sel = pick_types(info, meg, eeg, ref_meg=ref_meg, seeg=seeg, ecog=ecog,
                      include=include, exclude=exclude)
     if len(sel) == 0:
         raise ValueError('No valid channels found')
     include_ch_names = [info['ch_names'][k] for k in sel]
+
     return pick_channels_forward(orig, include_ch_names)
 
 
 def channel_indices_by_type(info):
     """Get indices of channels by type
     """
-    idx = dict(grad=[], mag=[], eeg=[], seeg=[], eog=[], ecg=[], stim=[],
-               emg=[], ref_meg=[], misc=[], resp=[], chpi=[], exci=[], ias=[],
-               syst=[])
+    idx = dict((key, list()) for key in _PICK_TYPES_KEYS if key != 'meg')
+    idx.update(mag=list(), grad=list())
     for k, ch in enumerate(info['chs']):
         for key in idx.keys():
             if channel_type(info, k) == key:
@@ -637,10 +674,35 @@ def _check_excludes_includes(chs, info=None, allow_bads=False):
     return chs
 
 
-def _pick_data_channels(info, exclude='bads'):
+_PICK_TYPES_DATA_DICT = dict(
+    meg=True, eeg=True, stim=False, eog=False, ecg=False, emg=False,
+    misc=False, resp=False, chpi=False, exci=False, ias=False, syst=False,
+    seeg=True, dipole=False, gof=False, bio=False, ecog=True)
+_PICK_TYPES_KEYS = tuple(list(_PICK_TYPES_DATA_DICT.keys()) + ['ref_meg'])
+_DATA_CH_TYPES_SPLIT = ['mag', 'grad', 'eeg', 'seeg', 'ecog']
+
+
+def _pick_data_channels(info, exclude='bads', with_ref_meg=True):
     """Convenience function for picking only data channels."""
-    return pick_types(info, meg=True, eeg=True, stim=False, eog=False,
-                      ecg=False, emg=False, ref_meg=True, misc=False,
-                      resp=False, chpi=False, exci=False, ias=False,
-                      syst=False, seeg=True, include=[], exclude=exclude,
-                      selection=None)
+    return pick_types(info, ref_meg=with_ref_meg, include=[], exclude=exclude,
+                      selection=None, **_PICK_TYPES_DATA_DICT)
+
+
+def _pick_aux_channels(info, exclude='bads'):
+    """Convenience function for picking only auxiliary channels
+
+    Corresponds to EOG, ECG, EMG and BIO
+    """
+    return pick_types(info, meg=False, eog=True, ecg=True, emg=True, bio=True,
+                      ref_meg=False, exclude=exclude)
+
+
+def _pick_data_or_ica(info):
+    """Convenience function for picking only data or ICA channels."""
+    ch_names = [c['ch_name'] for c in info['chs']]
+    if 'ICA ' in ','.join(ch_names):
+        picks = pick_types(info, exclude=[], misc=True)
+    else:
+        picks = _pick_data_channels(info, exclude=[],
+                                    with_ref_meg=False)
+    return picks
diff --git a/mne/io/proc_history.py b/mne/io/proc_history.py
index a2df522..5ce1038 100644
--- a/mne/io/proc_history.py
+++ b/mne/io/proc_history.py
@@ -4,7 +4,6 @@
 # License: Simplified BSD
 
 from os import path as op
-import warnings
 
 import numpy as np
 from scipy.sparse import csc_matrix
@@ -17,6 +16,7 @@ from .write import (start_block, end_block, write_int, write_float,
 from .tag import find_tag
 from .constants import FIFF
 from ..externals.six import text_type, string_types
+from ..utils import warn
 
 
 _proc_keys = ['parent_file_id', 'block_id', 'parent_block_id',
@@ -95,7 +95,7 @@ def _read_proc_history(fid, tree, info):
                         record[key] = cast(tag.data)
                         break
                 else:
-                    warnings.warn('Unknown processing history item %s' % kind)
+                    warn('Unknown processing history item %s' % kind)
             record['max_info'] = _read_maxfilter_record(fid, proc_record)
             smartshields = dir_tree_find(proc_record,
                                          FIFF.FIFFB_SMARTSHIELD)
diff --git a/mne/io/proj.py b/mne/io/proj.py
index c69efe1..634b753 100644
--- a/mne/io/proj.py
+++ b/mne/io/proj.py
@@ -6,11 +6,11 @@
 # License: BSD (3-clause)
 
 from copy import deepcopy
+from itertools import count
 from math import sqrt
+
 import numpy as np
 from scipy import linalg
-from itertools import count
-import warnings
 
 from .tree import dir_tree_find
 from .tag import find_tag
@@ -18,7 +18,7 @@ from .constants import FIFF
 from .pick import pick_types
 from .write import (write_int, write_float, write_string, write_name_list,
                     write_float_matrix, end_block, start_block)
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from ..externals.six import string_types
 
 
@@ -64,7 +64,8 @@ class ProjMixin(object):
         return (len(self.info['projs']) > 0 and
                 all(p['active'] for p in self.info['projs']))
 
-    def add_proj(self, projs, remove_existing=False):
+    @verbose
+    def add_proj(self, projs, remove_existing=False, verbose=None):
         """Add SSP projection vectors
 
         Parameters
@@ -73,6 +74,8 @@ class ProjMixin(object):
             List with projection vectors.
         remove_existing : bool
             Remove the projection vectors currently in the file.
+        verbose : bool, str, int, or None
+            If not None, override default verbose level (see mne.verbose).
 
         Returns
         -------
@@ -97,7 +100,9 @@ class ProjMixin(object):
             self.info['projs'] = projs
         else:
             self.info['projs'].extend(projs)
-
+        # We don't want to add projectors that are activated again.
+        self.info['projs'] = _uniquify_projs(self.info['projs'],
+                                             check_active=False, sort=False)
         return self
 
     def add_eeg_average_proj(self):
@@ -140,7 +145,7 @@ class ProjMixin(object):
         from ..epochs import _BaseEpochs
         from .base import _BaseRaw
         if self.info['projs'] is None or len(self.info['projs']) == 0:
-            logger.info('No projector specified for this dataset.'
+            logger.info('No projector specified for this dataset. '
                         'Please consider the method self.add_proj.')
             return self
 
@@ -239,8 +244,7 @@ class ProjMixin(object):
                     if ch in self:
                         layout.append(find_layout(self.info, ch, exclude=[]))
                     else:
-                        err = 'Channel type %s is not found in info.' % ch
-                        warnings.warn(err)
+                        warn('Channel type %s is not found in info.' % ch)
             fig = plot_projs_topomap(self.info['projs'], layout, axes=axes)
         else:
             raise ValueError("Info is missing projs. Nothing to plot.")
@@ -248,10 +252,10 @@ class ProjMixin(object):
         return fig
 
 
-def _proj_equal(a, b):
+def _proj_equal(a, b, check_active=True):
     """ Test if two projectors are equal """
 
-    equal = (a['active'] == b['active'] and
+    equal = ((a['active'] == b['active'] or not check_active) and
              a['kind'] == b['kind'] and
              a['desc'] == b['desc'] and
              a['data']['col_names'] == b['data']['col_names'] and
@@ -292,10 +296,8 @@ def _read_proj(fid, node, verbose=None):
         global_nchan = int(tag.data)
 
     items = dir_tree_find(nodes[0], FIFF.FIFFB_PROJ_ITEM)
-    for i in range(len(items)):
-
+    for item in items:
         #   Find all desired tags in one item
-        item = items[i]
         tag = find_tag(fid, item, FIFF.FIFF_NCHAN)
         if tag is not None:
             nchan = int(tag.data)
@@ -426,17 +428,16 @@ def _write_proj(fid, projs):
 
 ###############################################################################
 # Utils
-
-def make_projector(projs, ch_names, bads=[], include_active=True):
+def make_projector(projs, ch_names, bads=(), include_active=True):
     """Create an SSP operator from SSP projection vectors
 
     Parameters
     ----------
     projs : list
         List of projection vectors.
-    ch_names : list of strings
+    ch_names : list of str
         List of channels to include in the projection matrix.
-    bads : list of strings
+    bads : list of str
         Some bad channels to exclude. If bad channels were marked
         in the raw file when projs were calculated using mne-python,
         they should not need to be included here as they will
@@ -453,6 +454,17 @@ def make_projector(projs, ch_names, bads=[], include_active=True):
     U : array
         The orthogonal basis of the projection vectors (optional).
     """
+    return _make_projector(projs, ch_names, bads, include_active)
+
+
+def _make_projector(projs, ch_names, bads=(), include_active=True,
+                    inplace=False):
+    """Helper to subselect projs based on ch_names and bads
+
+    Use inplace=True mode to modify ``projs`` inplace so that no
+    warning will be raised next time projectors are constructed with
+    the given inputs. If inplace=True, no meaningful data are returned.
+    """
     nchan = len(ch_names)
     if nchan == 0:
         raise ValueError('No channel names specified')
@@ -494,21 +506,37 @@ def make_projector(projs, ch_names, bads=[], include_active=True):
                     vecsel.append(p['data']['col_names'].index(name))
 
             # If there is something to pick, pickit
+            nrow = p['data']['nrow']
+            this_vecs = vecs[:, nvec:nvec + nrow]
             if len(sel) > 0:
-                nrow = p['data']['nrow']
-                vecs[sel, nvec:nvec + nrow] = p['data']['data'][:, vecsel].T
+                this_vecs[sel] = p['data']['data'][:, vecsel].T
 
             # Rescale for better detection of small singular values
             for v in range(p['data']['nrow']):
-                psize = sqrt(np.sum(vecs[:, nvec + v] * vecs[:, nvec + v]))
+                psize = sqrt(np.sum(this_vecs[:, v] * this_vecs[:, v]))
                 if psize > 0:
-                    vecs[:, nvec + v] /= psize
+                    orig_n = p['data']['data'].shape[1]
+                    if len(vecsel) < 0.9 * orig_n and not inplace:
+                        warn('Projection vector "%s" has magnitude %0.2f '
+                             '(should be unity), applying projector with '
+                             '%s/%s of the original channels available may '
+                             'be dangerous, consider recomputing and adding '
+                             'projection vectors for channels that are '
+                             'eventually used. If this is intentional, '
+                             'consider using info.normalize_proj()'
+                             % (p['desc'], psize, len(vecsel), orig_n))
+                    this_vecs[:, v] /= psize
                     nonzero += 1
-
+            # If doing "inplace" mode, "fix" the projectors to only operate
+            # on this subset of channels.
+            if inplace:
+                p['data']['data'] = this_vecs[sel].T
+                p['data']['col_names'] = [p['data']['col_names'][ii]
+                                          for ii in vecsel]
             nvec += p['data']['nrow']
 
     #   Check whether all of the vectors are exactly zero
-    if nonzero == 0:
+    if nonzero == 0 or inplace:
         return default_return
 
     # Reorthogonalize the vectors
@@ -524,6 +552,18 @@ def make_projector(projs, ch_names, bads=[], include_active=True):
     return proj, nproj, U
 
 
+def _normalize_proj(info):
+    """Helper to normalize proj after subselection to avoid warnings
+
+    This is really only useful for tests, and might not be needed
+    eventually if we change or improve our handling of projectors
+    with picks.
+    """
+    # Here we do info.get b/c info can actually be a noise cov
+    _make_projector(info['projs'], info.get('ch_names', info.get('names')),
+                    info['bads'], include_active=True, inplace=True)
+
+
 def make_projector_info(info, include_active=True):
     """Make an SSP operator using the measurement info
 
@@ -644,7 +684,8 @@ def make_eeg_average_ref_proj(info, activate=True, verbose=None):
     if n_eeg == 0:
         raise ValueError('Cannot create EEG average reference projector '
                          '(no EEG data found)')
-    vec = np.ones((1, n_eeg)) / n_eeg
+    vec = np.ones((1, n_eeg))
+    vec /= n_eeg
     explained_var = None
     eeg_proj_data = dict(col_names=eeg_names, row_names=None,
                          data=vec, nrow=1, ncol=n_eeg)
@@ -678,8 +719,7 @@ def _needs_eeg_average_ref_proj(info):
 
 
 @verbose
-def setup_proj(info, add_eeg_ref=True, activate=True,
-               verbose=None):
+def setup_proj(info, add_eeg_ref=True, activate=True, verbose=None):
     """Set up projection for Raw and Epochs
 
     Parameters
@@ -724,11 +764,11 @@ def setup_proj(info, add_eeg_ref=True, activate=True,
     return projector, info
 
 
-def _uniquify_projs(projs):
+def _uniquify_projs(projs, check_active=True, sort=True):
     """Aux function"""
     final_projs = []
     for proj in projs:  # flatten
-        if not any(_proj_equal(p, proj) for p in final_projs):
+        if not any(_proj_equal(p, proj, check_active) for p in final_projs):
             final_projs.append(proj)
 
     my_count = count(len(final_projs))
@@ -742,4 +782,4 @@ def _uniquify_projs(projs):
             sort_idx = next(my_count)
         return (sort_idx, x['desc'])
 
-    return sorted(final_projs, key=sorter)
+    return sorted(final_projs, key=sorter) if sort else final_projs
diff --git a/mne/io/reference.py b/mne/io/reference.py
index 1585dd4..f62b2ea 100644
--- a/mne/io/reference.py
+++ b/mne/io/reference.py
@@ -12,10 +12,10 @@ from .pick import pick_types
 from .base import _BaseRaw
 from ..evoked import Evoked
 from ..epochs import _BaseEpochs
-from ..utils import logger
+from ..utils import logger, warn
 
 
-def _apply_reference(inst, ref_from, ref_to=None, copy=True):
+def _apply_reference(inst, ref_from, ref_to=None):
     """Apply a custom EEG referencing scheme.
 
     Calculates a reference signal by taking the mean of a set of channels and
@@ -33,9 +33,6 @@ def _apply_reference(inst, ref_from, ref_to=None, copy=True):
     ref_to : list of str | None
         The names of the channels to apply the reference to. By default,
         all EEG channels are chosen.
-    copy : bool
-        Specifies whether the data will be copied (True) or modified in place
-        (False). Defaults to True.
 
     Returns
     -------
@@ -46,6 +43,8 @@ def _apply_reference(inst, ref_from, ref_to=None, copy=True):
 
     Notes
     -----
+    This function operates in-place.
+
     1. Do not use this function to apply an average reference. By default, an
        average reference projection has already been added upon loading raw
        data.
@@ -74,9 +73,6 @@ def _apply_reference(inst, ref_from, ref_to=None, copy=True):
     if ref_to is None:
         ref_to = [inst.ch_names[i] for i in eeg_idx]
 
-    if copy:
-        inst = inst.copy()
-
     # After referencing, existing SSPs might not be valid anymore.
     for i, proj in enumerate(inst.info['projs']):
         if (not proj['active'] and
@@ -198,11 +194,10 @@ def add_reference_channels(inst, ref_channels, copy=True):
                      'coord_frame': FIFF.FIFFV_COORD_HEAD,
                      'loc': np.zeros(12)}
         inst.info['chs'].append(chan_info)
-    inst.info['ch_names'].extend(ref_channels)
-    inst.info['nchan'] = len(inst.info['ch_names'])
+        inst.info._update_redundant()
     if isinstance(inst, _BaseRaw):
         inst._cals = np.hstack((inst._cals, [1] * len(ref_channels)))
-
+    inst.info._check_consistency()
     return inst
 
 
@@ -254,8 +249,8 @@ def set_eeg_reference(inst, ref_channels=None, copy=True):
     if ref_channels is None:
         # CAR requested
         if _has_eeg_average_ref_proj(inst.info['projs']):
-            logger.warning('An average reference projection was already '
-                           'added. The data has been left untouched.')
+            warn('An average reference projection was already added. The data '
+                 'has been left untouched.')
             return inst, None
         else:
             inst.info['custom_ref_applied'] = False
@@ -263,7 +258,8 @@ def set_eeg_reference(inst, ref_channels=None, copy=True):
             return inst, None
     else:
         logger.info('Applying a custom EEG reference.')
-        return _apply_reference(inst, ref_channels, copy=copy)
+        inst = inst.copy() if copy else inst
+        return _apply_reference(inst, ref_channels)
 
 
 def set_bipolar_reference(inst, anode, cathode, ch_name=None, ch_info=None,
@@ -275,7 +271,7 @@ def set_bipolar_reference(inst, anode, cathode, ch_name=None, ch_info=None,
     channels will be dropped.
 
     Multiple anodes and cathodes can be specified, in which case multiple
-    vitual channels will be created. The 1st anode will be substracted from the
+    virtual channels will be created. The 1st anode will be subtracted from the
     1st cathode, the 2nd anode from the 2nd cathode, etc.
 
     By default, the virtual channels will be annotated with channel info of
@@ -374,12 +370,12 @@ def set_bipolar_reference(inst, anode, cathode, ch_name=None, ch_info=None,
 
     # Perform bipolar referencing
     for an, ca, name, info in zip(anode, cathode, ch_name, new_ch_info):
-        inst, _ = _apply_reference(inst, [ca], [an], copy=False)
+        _apply_reference(inst, [ca], [an])
         an_idx = inst.ch_names.index(an)
         inst.info['chs'][an_idx] = info
         inst.info['chs'][an_idx]['ch_name'] = name
-        inst.info['ch_names'][an_idx] = name
         logger.info('Bipolar channel added as "%s".' % name)
+    inst.info._update_redundant()
 
     # Drop cathode channels
     inst.drop_channels(cathode)
diff --git a/mne/io/tag.py b/mne/io/tag.py
index 61fed6c..152bb28 100644
--- a/mne/io/tag.py
+++ b/mne/io/tag.py
@@ -3,16 +3,21 @@
 #
 # License: BSD (3-clause)
 
-import os
 import gzip
+import os
+import struct
+
 import numpy as np
 
 from .constants import FIFF
-
+from ..fixes import partial
 from ..externals.six import text_type
 from ..externals.jdcal import jd2jcal
 
 
+##############################################################################
+# HELPERS
+
 class Tag(object):
     """Tag in FIF tree structure
 
@@ -48,16 +53,12 @@ class Tag(object):
         return out
 
     def __cmp__(self, tag):
-        is_equal = (self.kind == tag.kind and
-                    self.type == tag.type and
-                    self.size == tag.size and
-                    self.next == tag.next and
-                    self.pos == tag.pos and
-                    self.data == tag.data)
-        if is_equal:
-            return 0
-        else:
-            return 1
+        return int(self.kind == tag.kind and
+                   self.type == tag.type and
+                   self.size == tag.size and
+                   self.next == tag.next and
+                   self.pos == tag.pos and
+                   self.data == tag.data)
 
 
 def read_big(fid, size=None):
@@ -137,10 +138,9 @@ def read_big(fid, size=None):
 def read_tag_info(fid):
     """Read Tag info (or header)
     """
-    s = fid.read(4 * 4)
-    if len(s) == 0:
+    tag = _read_tag_header(fid)
+    if tag is None:
         return None
-    tag = Tag(*np.fromstring(s, '>i4'))
     if tag.next == 0:
         fid.seek(tag.size, 1)
     elif tag.next > 0:
@@ -203,6 +203,272 @@ def _loc_to_eeg_loc(loc):
         return loc[0:3][:, np.newaxis].copy()
 
 
+##############################################################################
+# READING FUNCTIONS
+
+# None of these functions have docstring because it's more compact that way,
+# and hopefully it's clear what they do by their names and variable values.
+# See ``read_tag`` for variable descriptions. Return values are implied
+# by the function names.
+
+_is_matrix = 4294901760  # ffff0000
+_matrix_coding_dense = 16384      # 4000
+_matrix_coding_CCS = 16400      # 4010
+_matrix_coding_RCS = 16416      # 4020
+_data_type = 65535      # ffff
+
+
+def _read_tag_header(fid):
+    """Read only the header of a Tag"""
+    s = fid.read(4 * 4)
+    if len(s) == 0:
+        return None
+    # struct.unpack faster than np.fromstring, saves ~10% of time some places
+    return Tag(*struct.unpack('>iIii', s))
+
+
+def _read_matrix(fid, tag, shape, rlims, matrix_coding):
+    """Read a matrix (dense or sparse) tag"""
+    matrix_coding = matrix_coding >> 16
+
+    # This should be easy to implement (see _fromstring_rows)
+    # if we need it, but for now, it's not...
+    if shape is not None:
+        raise ValueError('Row reading not implemented for matrices '
+                         'yet')
+
+    #   Matrices
+    if matrix_coding == _matrix_coding_dense:
+        # Find dimensions and return to the beginning of tag data
+        pos = fid.tell()
+        fid.seek(tag.size - 4, 1)
+        ndim = int(np.fromstring(fid.read(4), dtype='>i4'))
+        fid.seek(-(ndim + 1) * 4, 1)
+        dims = np.fromstring(fid.read(4 * ndim), dtype='>i4')[::-1]
+        #
+        # Back to where the data start
+        #
+        fid.seek(pos, 0)
+
+        if ndim > 3:
+            raise Exception('Only 2 or 3-dimensional matrices are '
+                            'supported at this time')
+
+        matrix_type = _data_type & tag.type
+
+        if matrix_type == FIFF.FIFFT_INT:
+            data = np.fromstring(read_big(fid, 4 * dims.prod()), dtype='>i4')
+        elif matrix_type == FIFF.FIFFT_JULIAN:
+            data = np.fromstring(read_big(fid, 4 * dims.prod()), dtype='>i4')
+        elif matrix_type == FIFF.FIFFT_FLOAT:
+            data = np.fromstring(read_big(fid, 4 * dims.prod()), dtype='>f4')
+        elif matrix_type == FIFF.FIFFT_DOUBLE:
+            data = np.fromstring(read_big(fid, 8 * dims.prod()), dtype='>f8')
+        elif matrix_type == FIFF.FIFFT_COMPLEX_FLOAT:
+            data = np.fromstring(read_big(fid, 4 * 2 * dims.prod()),
+                                 dtype='>f4')
+            # Note: we need the non-conjugate transpose here
+            data = (data[::2] + 1j * data[1::2])
+        elif matrix_type == FIFF.FIFFT_COMPLEX_DOUBLE:
+            data = np.fromstring(read_big(fid, 8 * 2 * dims.prod()),
+                                 dtype='>f8')
+            # Note: we need the non-conjugate transpose here
+            data = (data[::2] + 1j * data[1::2])
+        else:
+            raise Exception('Cannot handle matrix of type %d yet'
+                            % matrix_type)
+        data.shape = dims
+    elif matrix_coding in (_matrix_coding_CCS, _matrix_coding_RCS):
+        from scipy import sparse
+        # Find dimensions and return to the beginning of tag data
+        pos = fid.tell()
+        fid.seek(tag.size - 4, 1)
+        ndim = int(np.fromstring(fid.read(4), dtype='>i4'))
+        fid.seek(-(ndim + 2) * 4, 1)
+        dims = np.fromstring(fid.read(4 * (ndim + 1)), dtype='>i4')
+        if ndim != 2:
+            raise Exception('Only two-dimensional matrices are '
+                            'supported at this time')
+
+        # Back to where the data start
+        fid.seek(pos, 0)
+        nnz = int(dims[0])
+        nrow = int(dims[1])
+        ncol = int(dims[2])
+        sparse_data = np.fromstring(fid.read(4 * nnz), dtype='>f4')
+        shape = (dims[1], dims[2])
+        if matrix_coding == _matrix_coding_CCS:
+            #    CCS
+            tmp_indices = fid.read(4 * nnz)
+            sparse_indices = np.fromstring(tmp_indices, dtype='>i4')
+            tmp_ptrs = fid.read(4 * (ncol + 1))
+            sparse_ptrs = np.fromstring(tmp_ptrs, dtype='>i4')
+            if (sparse_ptrs[-1] > len(sparse_indices) or
+                    np.any(sparse_ptrs < 0)):
+                # There was a bug in MNE-C that caused some data to be
+                # stored without byte swapping
+                sparse_indices = np.concatenate(
+                    (np.fromstring(tmp_indices[:4 * (nrow + 1)], dtype='>i4'),
+                     np.fromstring(tmp_indices[4 * (nrow + 1):], dtype='<i4')))
+                sparse_ptrs = np.fromstring(tmp_ptrs, dtype='<i4')
+            data = sparse.csc_matrix((sparse_data, sparse_indices,
+                                     sparse_ptrs), shape=shape)
+        else:
+            #    RCS
+            sparse_indices = np.fromstring(fid.read(4 * nnz), dtype='>i4')
+            sparse_ptrs = np.fromstring(fid.read(4 * (nrow + 1)), dtype='>i4')
+            data = sparse.csr_matrix((sparse_data, sparse_indices,
+                                     sparse_ptrs), shape=shape)
+    else:
+        raise Exception('Cannot handle other than dense or sparse '
+                        'matrices yet')
+    return data
+
+
+def _read_simple(fid, tag, shape, rlims, dtype):
+    """Read simple datatypes from tag (typically used with partial)"""
+    return _fromstring_rows(fid, tag.size, dtype=dtype, shape=shape,
+                            rlims=rlims)
+
+
+def _read_string(fid, tag, shape, rlims):
+    """Read a string tag"""
+    # Always decode to unicode.
+    d = _fromstring_rows(fid, tag.size, dtype='>c', shape=shape, rlims=rlims)
+    return text_type(d.tostring().decode('utf-8', 'ignore'))
+
+
+def _read_complex_float(fid, tag, shape, rlims):
+    """Read complex float tag"""
+    # data gets stored twice as large
+    if shape is not None:
+        shape = (shape[0], shape[1] * 2)
+    d = _fromstring_rows(fid, tag.size, dtype=">f4", shape=shape, rlims=rlims)
+    d = d[::2] + 1j * d[1::2]
+    return d
+
+
+def _read_complex_double(fid, tag, shape, rlims):
+    """Read complex double tag"""
+    # data gets stored twice as large
+    if shape is not None:
+        shape = (shape[0], shape[1] * 2)
+    d = _fromstring_rows(fid, tag.size, dtype=">f8", shape=shape, rlims=rlims)
+    d = d[::2] + 1j * d[1::2]
+    return d
+
+
+def _read_id_struct(fid, tag, shape, rlims):
+    """Read ID struct tag"""
+    return dict(
+        version=int(np.fromstring(fid.read(4), dtype=">i4")),
+        machid=np.fromstring(fid.read(8), dtype=">i4"),
+        secs=int(np.fromstring(fid.read(4), dtype=">i4")),
+        usecs=int(np.fromstring(fid.read(4), dtype=">i4")))
+
+
+def _read_dig_point_struct(fid, tag, shape, rlims):
+    """Read dig point struct tag"""
+    return dict(
+        kind=int(np.fromstring(fid.read(4), dtype=">i4")),
+        ident=int(np.fromstring(fid.read(4), dtype=">i4")),
+        r=np.fromstring(fid.read(12), dtype=">f4"),
+        coord_frame=FIFF.FIFFV_COORD_UNKNOWN)
+
+
+def _read_coord_trans_struct(fid, tag, shape, rlims):
+    """Read coord trans struct tag"""
+    from ..transforms import Transform
+    fro = int(np.fromstring(fid.read(4), dtype=">i4"))
+    to = int(np.fromstring(fid.read(4), dtype=">i4"))
+    rot = np.fromstring(fid.read(36), dtype=">f4").reshape(3, 3)
+    move = np.fromstring(fid.read(12), dtype=">f4")
+    trans = np.r_[np.c_[rot, move],
+                  np.array([[0], [0], [0], [1]]).T]
+    data = Transform(fro, to, trans)
+    fid.seek(48, 1)  # Skip over the inverse transformation
+    return data
+
+
+_coord_dict = {
+    FIFF.FIFFV_MEG_CH: FIFF.FIFFV_COORD_DEVICE,
+    FIFF.FIFFV_REF_MEG_CH: FIFF.FIFFV_COORD_DEVICE,
+    FIFF.FIFFV_EEG_CH: FIFF.FIFFV_COORD_HEAD,
+}
+
+
+def _read_ch_info_struct(fid, tag, shape, rlims):
+    """Read channel info struct tag"""
+    d = dict(
+        scanno=int(np.fromstring(fid.read(4), dtype=">i4")),
+        logno=int(np.fromstring(fid.read(4), dtype=">i4")),
+        kind=int(np.fromstring(fid.read(4), dtype=">i4")),
+        range=float(np.fromstring(fid.read(4), dtype=">f4")),
+        cal=float(np.fromstring(fid.read(4), dtype=">f4")),
+        coil_type=int(np.fromstring(fid.read(4), dtype=">i4")),
+        # deal with really old OSX Anaconda bug by casting to float64
+        loc=np.fromstring(fid.read(48), dtype=">f4").astype(np.float64),
+        # unit and exponent
+        unit=int(np.fromstring(fid.read(4), dtype=">i4")),
+        unit_mul=int(np.fromstring(fid.read(4), dtype=">i4")),
+    )
+    # channel name
+    ch_name = np.fromstring(fid.read(16), dtype=">c")
+    ch_name = ch_name[:np.argmax(ch_name == b'')].tostring()
+    d['ch_name'] = ch_name.decode()
+    # coil coordinate system definition
+    d['coord_frame'] = _coord_dict.get(d['kind'], FIFF.FIFFV_COORD_UNKNOWN)
+    return d
+
+
+def _read_old_pack(fid, tag, shape, rlims):
+    """Read old pack tag"""
+    offset = float(np.fromstring(fid.read(4), dtype=">f4"))
+    scale = float(np.fromstring(fid.read(4), dtype=">f4"))
+    data = np.fromstring(fid.read(tag.size - 8), dtype=">i2")
+    data = data * scale  # to float64
+    data += offset
+    return data
+
+
+def _read_dir_entry_struct(fid, tag, shape, rlims):
+    """Read dir entry struct tag"""
+    return [_read_tag_header(fid) for _ in range(tag.size // 16 - 1)]
+
+
+def _read_julian(fid, tag, shape, rlims):
+    """Read julian tag"""
+    return jd2jcal(int(np.fromstring(fid.read(4), dtype=">i4")))
+
+# Read types call dict
+_call_dict = {
+    FIFF.FIFFT_STRING: _read_string,
+    FIFF.FIFFT_COMPLEX_FLOAT: _read_complex_float,
+    FIFF.FIFFT_COMPLEX_DOUBLE: _read_complex_double,
+    FIFF.FIFFT_ID_STRUCT: _read_id_struct,
+    FIFF.FIFFT_DIG_POINT_STRUCT: _read_dig_point_struct,
+    FIFF.FIFFT_COORD_TRANS_STRUCT: _read_coord_trans_struct,
+    FIFF.FIFFT_CH_INFO_STRUCT: _read_ch_info_struct,
+    FIFF.FIFFT_OLD_PACK: _read_old_pack,
+    FIFF.FIFFT_DIR_ENTRY_STRUCT: _read_dir_entry_struct,
+    FIFF.FIFFT_JULIAN: _read_julian,
+}
+
+#  Append the simple types
+_simple_dict = {
+    FIFF.FIFFT_BYTE: '>B1',
+    FIFF.FIFFT_SHORT: '>i2',
+    FIFF.FIFFT_INT: '>i4',
+    FIFF.FIFFT_USHORT: '>u2',
+    FIFF.FIFFT_UINT: '>u4',
+    FIFF.FIFFT_FLOAT: '>f4',
+    FIFF.FIFFT_DOUBLE: '>f8',
+    FIFF.FIFFT_DAU_PACK16: '>i2',
+}
+for key, dtype in _simple_dict.items():
+    _call_dict[key] = partial(_read_simple, dtype=dtype)
+
+
 def read_tag(fid, pos=None, shape=None, rlims=None):
     """Read a Tag from a file at a given position
 
@@ -228,255 +494,18 @@ def read_tag(fid, pos=None, shape=None, rlims=None):
     """
     if pos is not None:
         fid.seek(pos, 0)
-
-    s = fid.read(4 * 4)
-
-    tag = Tag(*np.fromstring(s, dtype='>i4,>u4,>i4,>i4')[0])
-
-    #
-    #   The magic hexadecimal values
-    #
-    is_matrix = 4294901760  # ffff0000
-    matrix_coding_dense = 16384      # 4000
-    matrix_coding_CCS = 16400      # 4010
-    matrix_coding_RCS = 16416      # 4020
-    data_type = 65535      # ffff
-    #
+    tag = _read_tag_header(fid)
     if tag.size > 0:
-        matrix_coding = is_matrix & tag.type
+        matrix_coding = _is_matrix & tag.type
         if matrix_coding != 0:
-            matrix_coding = matrix_coding >> 16
-
-            # This should be easy to implement (see _fromstring_rows)
-            # if we need it, but for now, it's not...
-            if shape is not None:
-                raise ValueError('Row reading not implemented for matrices '
-                                 'yet')
-
-            #   Matrices
-            if matrix_coding == matrix_coding_dense:
-                # Find dimensions and return to the beginning of tag data
-                pos = fid.tell()
-                fid.seek(tag.size - 4, 1)
-                ndim = int(np.fromstring(fid.read(4), dtype='>i4'))
-                fid.seek(-(ndim + 1) * 4, 1)
-                dims = np.fromstring(fid.read(4 * ndim), dtype='>i4')[::-1]
-                #
-                # Back to where the data start
-                #
-                fid.seek(pos, 0)
-
-                if ndim > 3:
-                    raise Exception('Only 2 or 3-dimensional matrices are '
-                                    'supported at this time')
-
-                matrix_type = data_type & tag.type
-
-                if matrix_type == FIFF.FIFFT_INT:
-                    tag.data = np.fromstring(read_big(fid, 4 * dims.prod()),
-                                             dtype='>i4').reshape(dims)
-                elif matrix_type == FIFF.FIFFT_JULIAN:
-                    tag.data = np.fromstring(read_big(fid, 4 * dims.prod()),
-                                             dtype='>i4').reshape(dims)
-                elif matrix_type == FIFF.FIFFT_FLOAT:
-                    tag.data = np.fromstring(read_big(fid, 4 * dims.prod()),
-                                             dtype='>f4').reshape(dims)
-                elif matrix_type == FIFF.FIFFT_DOUBLE:
-                    tag.data = np.fromstring(read_big(fid, 8 * dims.prod()),
-                                             dtype='>f8').reshape(dims)
-                elif matrix_type == FIFF.FIFFT_COMPLEX_FLOAT:
-                    data = np.fromstring(read_big(fid, 4 * 2 * dims.prod()),
-                                         dtype='>f4')
-                    # Note: we need the non-conjugate transpose here
-                    tag.data = (data[::2] + 1j * data[1::2]).reshape(dims)
-                elif matrix_type == FIFF.FIFFT_COMPLEX_DOUBLE:
-                    data = np.fromstring(read_big(fid, 8 * 2 * dims.prod()),
-                                         dtype='>f8')
-                    # Note: we need the non-conjugate transpose here
-                    tag.data = (data[::2] + 1j * data[1::2]).reshape(dims)
-                else:
-                    raise Exception('Cannot handle matrix of type %d yet'
-                                    % matrix_type)
-
-            elif matrix_coding in (matrix_coding_CCS, matrix_coding_RCS):
-                from scipy import sparse
-                # Find dimensions and return to the beginning of tag data
-                pos = fid.tell()
-                fid.seek(tag.size - 4, 1)
-                ndim = int(np.fromstring(fid.read(4), dtype='>i4'))
-                fid.seek(-(ndim + 2) * 4, 1)
-                dims = np.fromstring(fid.read(4 * (ndim + 1)), dtype='>i4')
-                if ndim != 2:
-                    raise Exception('Only two-dimensional matrices are '
-                                    'supported at this time')
-
-                # Back to where the data start
-                fid.seek(pos, 0)
-                nnz = int(dims[0])
-                nrow = int(dims[1])
-                ncol = int(dims[2])
-                sparse_data = np.fromstring(fid.read(4 * nnz), dtype='>f4')
-                shape = (dims[1], dims[2])
-                if matrix_coding == matrix_coding_CCS:
-                    #    CCS
-                    tmp_indices = fid.read(4 * nnz)
-                    sparse_indices = np.fromstring(tmp_indices, dtype='>i4')
-                    tmp_ptrs = fid.read(4 * (ncol + 1))
-                    sparse_ptrs = np.fromstring(tmp_ptrs, dtype='>i4')
-                    if (sparse_ptrs[-1] > len(sparse_indices) or
-                            np.any(sparse_ptrs < 0)):
-                        # There was a bug in MNE-C that caused some data to be
-                        # stored without byte swapping
-                        sparse_indices = np.concatenate(
-                            (np.fromstring(tmp_indices[:4 * (nrow + 1)],
-                                           dtype='>i4'),
-                             np.fromstring(tmp_indices[4 * (nrow + 1):],
-                                           dtype='<i4')))
-                        sparse_ptrs = np.fromstring(tmp_ptrs, dtype='<i4')
-                    tag.data = sparse.csc_matrix((sparse_data, sparse_indices,
-                                                 sparse_ptrs), shape=shape)
-                else:
-                    #    RCS
-                    sparse_indices = np.fromstring(fid.read(4 * nnz),
-                                                   dtype='>i4')
-                    sparse_ptrs = np.fromstring(fid.read(4 * (nrow + 1)),
-                                                dtype='>i4')
-                    tag.data = sparse.csr_matrix((sparse_data, sparse_indices,
-                                                 sparse_ptrs), shape=shape)
-            else:
-                raise Exception('Cannot handle other than dense or sparse '
-                                'matrices yet')
+            tag.data = _read_matrix(fid, tag, shape, rlims, matrix_coding)
         else:
             #   All other data types
-
-            #   Simple types
-            if tag.type == FIFF.FIFFT_BYTE:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">B1",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_SHORT:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">i2",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_INT:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">i4",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_USHORT:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">u2",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_UINT:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">u4",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_FLOAT:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">f4",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_DOUBLE:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">f8",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_STRING:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">c",
-                                            shape=shape, rlims=rlims)
-
-                # Always decode to unicode.
-                td = tag.data.tostring().decode('utf-8', 'ignore')
-                tag.data = text_type(td)
-
-            elif tag.type == FIFF.FIFFT_DAU_PACK16:
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">i2",
-                                            shape=shape, rlims=rlims)
-            elif tag.type == FIFF.FIFFT_COMPLEX_FLOAT:
-                # data gets stored twice as large
-                if shape is not None:
-                    shape = (shape[0], shape[1] * 2)
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">f4",
-                                            shape=shape, rlims=rlims)
-                tag.data = tag.data[::2] + 1j * tag.data[1::2]
-            elif tag.type == FIFF.FIFFT_COMPLEX_DOUBLE:
-                # data gets stored twice as large
-                if shape is not None:
-                    shape = (shape[0], shape[1] * 2)
-                tag.data = _fromstring_rows(fid, tag.size, dtype=">f8",
-                                            shape=shape, rlims=rlims)
-                tag.data = tag.data[::2] + 1j * tag.data[1::2]
-            #
-            #   Structures
-            #
-            elif tag.type == FIFF.FIFFT_ID_STRUCT:
-                tag.data = dict()
-                tag.data['version'] = int(np.fromstring(fid.read(4),
-                                                        dtype=">i4"))
-                tag.data['machid'] = np.fromstring(fid.read(8), dtype=">i4")
-                tag.data['secs'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                tag.data['usecs'] = int(np.fromstring(fid.read(4),
-                                                      dtype=">i4"))
-            elif tag.type == FIFF.FIFFT_DIG_POINT_STRUCT:
-                tag.data = dict()
-                tag.data['kind'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                tag.data['ident'] = int(np.fromstring(fid.read(4),
-                                                      dtype=">i4"))
-                tag.data['r'] = np.fromstring(fid.read(12), dtype=">f4")
-                tag.data['coord_frame'] = FIFF.FIFFV_COORD_UNKNOWN
-            elif tag.type == FIFF.FIFFT_COORD_TRANS_STRUCT:
-                from ..transforms import Transform
-                fro = int(np.fromstring(fid.read(4), dtype=">i4"))
-                to = int(np.fromstring(fid.read(4), dtype=">i4"))
-                rot = np.fromstring(fid.read(36), dtype=">f4").reshape(3, 3)
-                move = np.fromstring(fid.read(12), dtype=">f4")
-                trans = np.r_[np.c_[rot, move],
-                              np.array([[0], [0], [0], [1]]).T]
-                tag.data = Transform(fro, to, trans)
-                # Skip over the inverse transformation
-                fid.seek(12 * 4, 1)
-            elif tag.type == FIFF.FIFFT_CH_INFO_STRUCT:
-                d = tag.data = dict()
-                d['scanno'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                d['logno'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                d['kind'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                d['range'] = float(np.fromstring(fid.read(4), dtype=">f4"))
-                d['cal'] = float(np.fromstring(fid.read(4), dtype=">f4"))
-                d['coil_type'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                #
-                #   Read the coil coordinate system definition
-                #
-                d['loc'] = np.fromstring(fid.read(48), dtype=">f4")
-                # deal with nasty OSX Anaconda bug by casting to float64
-                d['loc'] = d['loc'].astype(np.float64)
-                #
-                #   Convert loc into a more useful format
-                #
-                kind = d['kind']
-                if kind in [FIFF.FIFFV_MEG_CH, FIFF.FIFFV_REF_MEG_CH]:
-                    d['coord_frame'] = FIFF.FIFFV_COORD_DEVICE
-                elif d['kind'] == FIFF.FIFFV_EEG_CH:
-                    d['coord_frame'] = FIFF.FIFFV_COORD_HEAD
-                else:
-                    d['coord_frame'] = FIFF.FIFFV_COORD_UNKNOWN
-                #
-                #   Unit and exponent
-                #
-                d['unit'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                d['unit_mul'] = int(np.fromstring(fid.read(4), dtype=">i4"))
-                #
-                #   Handle the channel name
-                #
-                ch_name = np.fromstring(fid.read(16), dtype=">c")
-                ch_name = ch_name[:np.argmax(ch_name == b'')].tostring()
-                d['ch_name'] = ch_name.decode()
-            elif tag.type == FIFF.FIFFT_OLD_PACK:
-                offset = float(np.fromstring(fid.read(4), dtype=">f4"))
-                scale = float(np.fromstring(fid.read(4), dtype=">f4"))
-                tag.data = np.fromstring(fid.read(tag.size - 8), dtype=">h2")
-                tag.data = scale * tag.data + offset
-            elif tag.type == FIFF.FIFFT_DIR_ENTRY_STRUCT:
-                tag.data = list()
-                for _ in range(tag.size // 16 - 1):
-                    s = fid.read(4 * 4)
-                    tag.data.append(Tag(*np.fromstring(
-                        s, dtype='>i4,>u4,>i4,>i4')[0]))
-            elif tag.type == FIFF.FIFFT_JULIAN:
-                tag.data = int(np.fromstring(fid.read(4), dtype=">i4"))
-                tag.data = jd2jcal(tag.data)
+            fun = _call_dict.get(tag.type)
+            if fun is not None:
+                tag.data = fun(fid, tag, shape, rlims)
             else:
                 raise Exception('Unimplemented tag data type %s' % tag.type)
-
     if tag.next != FIFF.FIFFV_NEXT_SEQ:
         # f.seek(tag.next,0)
         fid.seek(tag.next, 1)  # XXX : fix? pb when tag.next < 0
@@ -501,9 +530,10 @@ def find_tag(fid, node, findkind):
     tag : instance of Tag
         The first tag found.
     """
-    for p in range(node['nent']):
-        if node['directory'][p].kind == findkind:
-            return read_tag(fid, node['directory'][p].pos)
+    if node['directory'] is not None:
+        for subnode in node['directory']:
+            if subnode.kind == findkind:
+                return read_tag(fid, subnode.pos)
     return None
 
 
diff --git a/mne/io/tests/test_meas_info.py b/mne/io/tests/test_meas_info.py
index 4c81bfb..b794f0b 100644
--- a/mne/io/tests/test_meas_info.py
+++ b/mne/io/tests/test_meas_info.py
@@ -11,7 +11,8 @@ from mne.io import (read_fiducials, write_fiducials, _coil_trans_to_loc,
                     _loc_to_coil_trans, Raw, read_info, write_info)
 from mne.io.constants import FIFF
 from mne.io.meas_info import (Info, create_info, _write_dig_points,
-                              _read_dig_points, _make_dig_points)
+                              _read_dig_points, _make_dig_points, _merge_info,
+                              _force_update_info, RAW_INFO_FIELDS)
 from mne.utils import _TempDir, run_tests_if_main
 from mne.channels.montage import read_montage, read_dig_montage
 
@@ -41,6 +42,8 @@ def test_make_info():
     """
     n_ch = 1
     info = create_info(n_ch, 1000., 'eeg')
+    assert_equal(sorted(info.keys()), sorted(RAW_INFO_FIELDS))
+
     coil_types = set([ch['coil_type'] for ch in info['chs']])
     assert_true(FIFF.FIFFV_COIL_EEG in coil_types)
 
@@ -126,13 +129,39 @@ def test_info():
     info[42] = 'foo'
     assert_true(info[42] == 'foo')
 
-    # test info attribute in API objects
+    # Test info attribute in API objects
     for obj in [raw, epochs, evoked]:
         assert_true(isinstance(obj.info, Info))
         info_str = '%s' % obj.info
-        assert_equal(len(info_str.split('\n')), (len(obj.info.keys()) + 2))
+        assert_equal(len(info_str.split('\n')), len(obj.info.keys()) + 2)
         assert_true(all(k in info_str for k in obj.info.keys()))
 
+    # Test read-only fields
+    info = raw.info.copy()
+    nchan = len(info['chs'])
+    ch_names = [ch['ch_name'] for ch in info['chs']]
+    assert_equal(info['nchan'], nchan)
+    assert_equal(list(info['ch_names']), ch_names)
+
+    # Deleting of regular fields should work
+    info['foo'] = 'bar'
+    del info['foo']
+
+    # Test updating of fields
+    del info['chs'][-1]
+    info._update_redundant()
+    assert_equal(info['nchan'], nchan - 1)
+    assert_equal(list(info['ch_names']), ch_names[:-1])
+
+    info['chs'][0]['ch_name'] = 'foo'
+    info._update_redundant()
+    assert_equal(info['ch_names'][0], 'foo')
+
+    # Test casting to and from a dict
+    info_dict = dict(info)
+    info2 = Info(info_dict)
+    assert_equal(info, info2)
+
 
 def test_read_write_info():
     """Test IO of info
@@ -208,4 +237,93 @@ def test_make_dig_points():
     assert_raises(ValueError, _make_dig_points, None, None, None, None,
                   dig_points[:, :2])
 
+
+def test_redundant():
+    """Test some of the redundant properties of info"""
+    # Indexing
+    info = create_info(ch_names=['a', 'b', 'c'], sfreq=1000., ch_types=None)
+    assert_equal(info['ch_names'][0], 'a')
+    assert_equal(info['ch_names'][1], 'b')
+    assert_equal(info['ch_names'][2], 'c')
+
+    # Equality
+    assert_equal(info['ch_names'], info['ch_names'])
+    assert_equal(info['ch_names'], ['a', 'b', 'c'])
+
+    # No channels in info
+    info = create_info(ch_names=[], sfreq=1000., ch_types=None)
+    assert_equal(info['ch_names'], [])
+
+    # List should be read-only
+    info = create_info(ch_names=['a', 'b', 'c'], sfreq=1000., ch_types=None)
+
+
+def test_merge_info():
+    """Test merging of multiple Info objects"""
+    info_a = create_info(ch_names=['a', 'b', 'c'], sfreq=1000., ch_types=None)
+    info_b = create_info(ch_names=['d', 'e', 'f'], sfreq=1000., ch_types=None)
+    info_merged = _merge_info([info_a, info_b])
+    assert_equal(info_merged['nchan'], 6)
+    assert_equal(info_merged['ch_names'], ['a', 'b', 'c', 'd', 'e', 'f'])
+    assert_raises(ValueError, _merge_info, [info_a, info_a])
+
+    # Testing for force updates before merging
+    info_c = create_info(ch_names=['g', 'h', 'i'], sfreq=500., ch_types=None)
+    # This will break because sfreq is not equal
+    assert_raises(RuntimeError, _merge_info, [info_a, info_c])
+    _force_update_info(info_a, info_c)
+    assert_true(info_c['sfreq'] == info_a['sfreq'])
+    assert_true(info_c['ch_names'][0] != info_a['ch_names'][0])
+    # Make sure it works now
+    _merge_info([info_a, info_c])
+    # Check that you must supply Info
+    assert_raises(ValueError, _force_update_info, info_a,
+                  dict([('sfreq', 1000.)]))
+
+
+def test_check_consistency():
+    """Test consistency check of Info objects"""
+    info = create_info(ch_names=['a', 'b', 'c'], sfreq=1000.)
+
+    # This should pass
+    info._check_consistency()
+
+    # Info without any channels
+    info_empty = create_info(ch_names=[], sfreq=1000.)
+    info_empty._check_consistency()
+
+    # Bad channels that are not in the info object
+    info2 = info.copy()
+    info2['bads'] = ['b', 'foo', 'bar']
+    assert_raises(RuntimeError, info2._check_consistency)
+
+    # Bad data types
+    info2 = info.copy()
+    info2['sfreq'] = 'foo'
+    assert_raises(ValueError, info2._check_consistency)
+
+    info2 = info.copy()
+    info2['highpass'] = 'foo'
+    assert_raises(ValueError, info2._check_consistency)
+
+    info2 = info.copy()
+    info2['lowpass'] = 'foo'
+    assert_raises(ValueError, info2._check_consistency)
+
+    # Silent type conversion to float
+    info2 = info.copy()
+    info2['sfreq'] = 1
+    info2['highpass'] = 2
+    info2['lowpass'] = 2
+    info2._check_consistency()
+    assert_true(isinstance(info2['sfreq'], float))
+    assert_true(isinstance(info2['highpass'], float))
+    assert_true(isinstance(info2['lowpass'], float))
+
+    # Duplicate channel names
+    info2 = info.copy()
+    info2['chs'][2]['ch_name'] = 'b'
+    assert_raises(RuntimeError, info2._check_consistency)
+
+
 run_tests_if_main()
diff --git a/mne/io/tests/test_pick.py b/mne/io/tests/test_pick.py
index 2e1f512..a1b1d40 100644
--- a/mne/io/tests/test_pick.py
+++ b/mne/io/tests/test_pick.py
@@ -1,14 +1,15 @@
-import os.path as op
 import inspect
+import os.path as op
+import warnings
 
-from nose.tools import assert_equal, assert_raises
+from nose.tools import assert_equal, assert_raises, assert_true
 from numpy.testing import assert_array_equal
 import numpy as np
 
 from mne import (pick_channels_regexp, pick_types, Epochs,
                  read_forward_solution, rename_channels,
                  pick_info, pick_channels, __file__, create_info)
-from mne.io import Raw, RawArray, read_raw_bti, read_raw_kit
+from mne.io import Raw, RawArray, read_raw_bti, read_raw_kit, read_info
 from mne.io.pick import (channel_indices_by_type, channel_type,
                          pick_types_forward, _picks_by_type)
 from mne.io.constants import FIFF
@@ -19,6 +20,7 @@ io_dir = op.join(op.dirname(inspect.getfile(inspect.currentframe())), '..')
 data_path = testing.data_path(download=False)
 fname_meeg = op.join(data_path, 'MEG', 'sample',
                      'sample_audvis_trunc-meg-eeg-oct-4-fwd.fif')
+fname_mc = op.join(data_path, 'SSS', 'test_move_anon_movecomp_raw_sss.fif')
 
 
 def test_pick_refs():
@@ -38,7 +40,8 @@ def test_pick_refs():
     bti_pdf = op.join(bti_dir, 'test_pdf_linux')
     bti_config = op.join(bti_dir, 'test_config_linux')
     bti_hs = op.join(bti_dir, 'test_hs_linux')
-    raw_bti = read_raw_bti(bti_pdf, bti_config, bti_hs, preload=False)
+    with warnings.catch_warnings(record=True):  # weight tables
+        raw_bti = read_raw_bti(bti_pdf, bti_config, bti_hs, preload=False)
     infos.append(raw_bti.info)
     # CTF
     fname_ctf_raw = op.join(io_dir, 'tests', 'data', 'test_ctf_comp_raw.fif')
@@ -88,26 +91,53 @@ def test_pick_channels_regexp():
     assert_array_equal(pick_channels_regexp(ch_names, 'MEG *'), [0, 1, 2])
 
 
-def test_pick_seeg():
-    """Test picking with SEEG
+def test_pick_seeg_ecog():
+    """Test picking with sEEG and ECoG
     """
-    names = 'A1 A2 Fz O OTp1 OTp2 OTp3'.split()
-    types = 'mag mag eeg eeg seeg seeg seeg'.split()
+    names = 'A1 A2 Fz O OTp1 OTp2 E1 OTp3 E2 E3'.split()
+    types = 'mag mag eeg eeg seeg seeg ecog seeg ecog ecog'.split()
     info = create_info(names, 1024., types)
     idx = channel_indices_by_type(info)
     assert_array_equal(idx['mag'], [0, 1])
     assert_array_equal(idx['eeg'], [2, 3])
-    assert_array_equal(idx['seeg'], [4, 5, 6])
-    assert_array_equal(pick_types(info, meg=False, seeg=True), [4, 5, 6])
+    assert_array_equal(idx['seeg'], [4, 5, 7])
+    assert_array_equal(idx['ecog'], [6, 8, 9])
+    assert_array_equal(pick_types(info, meg=False, seeg=True), [4, 5, 7])
     for i, t in enumerate(types):
         assert_equal(channel_type(info, i), types[i])
     raw = RawArray(np.zeros((len(names), 10)), info)
     events = np.array([[1, 0, 0], [2, 0, 0]])
     epochs = Epochs(raw, events, {'event': 0}, -1e-5, 1e-5)
     evoked = epochs.average(pick_types(epochs.info, meg=True, seeg=True))
-    e_seeg = evoked.pick_types(meg=False, seeg=True, copy=True)
-    for l, r in zip(e_seeg.ch_names, names[4:]):
+    e_seeg = evoked.copy().pick_types(meg=False, seeg=True)
+    for l, r in zip(e_seeg.ch_names, [names[4], names[5], names[7]]):
         assert_equal(l, r)
+    # Deal with constant debacle
+    raw = Raw(op.join(io_dir, 'tests', 'data', 'test_chpi_raw_sss.fif'))
+    assert_equal(len(pick_types(raw.info, meg=False, seeg=True, ecog=True)), 0)
+
+
+def test_pick_chpi():
+    """Test picking cHPI
+    """
+    # Make sure we don't mis-classify cHPI channels
+    info = read_info(op.join(io_dir, 'tests', 'data', 'test_chpi_raw_sss.fif'))
+    channel_types = set([channel_type(info, idx)
+                         for idx in range(info['nchan'])])
+    assert_true('chpi' in channel_types)
+    assert_true('seeg' not in channel_types)
+    assert_true('ecog' not in channel_types)
+
+
+def test_pick_bio():
+    """Test picking BIO channels."""
+    names = 'A1 A2 Fz O BIO1 BIO2 BIO3'.split()
+    types = 'mag mag eeg eeg bio bio bio'.split()
+    info = create_info(names, 1024., types)
+    idx = channel_indices_by_type(info)
+    assert_array_equal(idx['mag'], [0, 1])
+    assert_array_equal(idx['eeg'], [2, 3])
+    assert_array_equal(idx['bio'], [4, 5, 6])
 
 
 def _check_fwd_n_chan_consistent(fwd, n_expected):
@@ -118,42 +148,51 @@ def _check_fwd_n_chan_consistent(fwd, n_expected):
 
 
 @testing.requires_testing_data
-def test_pick_forward_seeg():
-    """Test picking forward with SEEG
+def test_pick_forward_seeg_ecog():
+    """Test picking forward with SEEG and ECoG
     """
     fwd = read_forward_solution(fname_meeg)
     counts = channel_indices_by_type(fwd['info'])
     for key in counts.keys():
         counts[key] = len(counts[key])
     counts['meg'] = counts['mag'] + counts['grad']
-    fwd_ = pick_types_forward(fwd, meg=True, eeg=False, seeg=False)
+    fwd_ = pick_types_forward(fwd, meg=True)
     _check_fwd_n_chan_consistent(fwd_, counts['meg'])
-    fwd_ = pick_types_forward(fwd, meg=False, eeg=True, seeg=False)
+    fwd_ = pick_types_forward(fwd, meg=False, eeg=True)
     _check_fwd_n_chan_consistent(fwd_, counts['eeg'])
     # should raise exception related to emptiness
-    assert_raises(ValueError, pick_types_forward, fwd, meg=False, eeg=False,
-                  seeg=True)
-    # change last chan from EEG to sEEG
+    assert_raises(ValueError, pick_types_forward, fwd, meg=False, seeg=True)
+    assert_raises(ValueError, pick_types_forward, fwd, meg=False, ecog=True)
+    # change last chan from EEG to sEEG, second-to-last to ECoG
+    ecog_name = 'E1'
     seeg_name = 'OTp1'
+    rename_channels(fwd['info'], {'EEG 059': ecog_name})
     rename_channels(fwd['info'], {'EEG 060': seeg_name})
     for ch in fwd['info']['chs']:
         if ch['ch_name'] == seeg_name:
             ch['kind'] = FIFF.FIFFV_SEEG_CH
             ch['coil_type'] = FIFF.FIFFV_COIL_EEG
+        elif ch['ch_name'] == ecog_name:
+            ch['kind'] = FIFF.FIFFV_ECOG_CH
+            ch['coil_type'] = FIFF.FIFFV_COIL_EEG
     fwd['sol']['row_names'][-1] = fwd['info']['chs'][-1]['ch_name']
-    counts['eeg'] -= 1
+    fwd['sol']['row_names'][-2] = fwd['info']['chs'][-2]['ch_name']
+    counts['eeg'] -= 2
     counts['seeg'] += 1
+    counts['ecog'] += 1
     # repick & check
-    fwd_seeg = pick_types_forward(fwd, meg=False, eeg=False, seeg=True)
+    fwd_seeg = pick_types_forward(fwd, meg=False, seeg=True)
     assert_equal(fwd_seeg['sol']['row_names'], [seeg_name])
     assert_equal(fwd_seeg['info']['ch_names'], [seeg_name])
     # should work fine
-    fwd_ = pick_types_forward(fwd, meg=True, eeg=False, seeg=False)
+    fwd_ = pick_types_forward(fwd, meg=True)
     _check_fwd_n_chan_consistent(fwd_, counts['meg'])
-    fwd_ = pick_types_forward(fwd, meg=False, eeg=True, seeg=False)
+    fwd_ = pick_types_forward(fwd, meg=False, eeg=True)
     _check_fwd_n_chan_consistent(fwd_, counts['eeg'])
-    fwd_ = pick_types_forward(fwd, meg=False, eeg=False, seeg=True)
+    fwd_ = pick_types_forward(fwd, meg=False, seeg=True)
     _check_fwd_n_chan_consistent(fwd_, counts['seeg'])
+    fwd_ = pick_types_forward(fwd, meg=False, ecog=True)
+    _check_fwd_n_chan_consistent(fwd_, counts['ecog'])
 
 
 def test_picks_by_channels():
@@ -197,6 +236,9 @@ def test_picks_by_channels():
     assert_equal(len(pick_list), len(pick_list2))
     assert_equal(pick_list2[0][0], 'mag')
 
+    # pick_types type check
+    assert_raises(ValueError, raw.pick_types, eeg='string')
+
 
 def test_clean_info_bads():
     """Test cleaning info['bads'] when bad_channels are excluded """
@@ -235,13 +277,5 @@ def test_clean_info_bads():
     info._check_consistency()
     info['bads'] += ['EEG 053']
     assert_raises(RuntimeError, info._check_consistency)
-    info = pick_info(raw.info, picks_meg)
-    info._check_consistency()
-    info['ch_names'][0] += 'f'
-    assert_raises(RuntimeError, info._check_consistency)
-    info = pick_info(raw.info, picks_meg)
-    info._check_consistency()
-    info['nchan'] += 1
-    assert_raises(RuntimeError, info._check_consistency)
 
 run_tests_if_main()
diff --git a/mne/io/tests/test_proc_history.py b/mne/io/tests/test_proc_history.py
index 555b08d..876a097 100644
--- a/mne/io/tests/test_proc_history.py
+++ b/mne/io/tests/test_proc_history.py
@@ -15,7 +15,7 @@ raw_fname = op.join(base_dir, 'test_chpi_raw_sss.fif')
 
 def test_maxfilter_io():
     """test maxfilter io"""
-    raw = io.Raw(raw_fname)
+    raw = io.read_raw_fif(raw_fname)
     mf = raw.info['proc_history'][1]['max_info']
 
     assert_true(mf['sss_info']['frame'], FIFF.FIFFV_COORD_HEAD)
@@ -40,7 +40,7 @@ def test_maxfilter_io():
 
 def test_maxfilter_get_rank():
     """test maxfilter rank lookup"""
-    raw = io.Raw(raw_fname)
+    raw = io.read_raw_fif(raw_fname)
     mf = raw.info['proc_history'][0]['max_info']
     rank1 = mf['sss_info']['nfree']
     rank2 = _get_sss_rank(mf)
diff --git a/mne/io/tests/test_raw.py b/mne/io/tests/test_raw.py
index 378b598..c5969b0 100644
--- a/mne/io/tests/test_raw.py
+++ b/mne/io/tests/test_raw.py
@@ -37,7 +37,8 @@ def _test_raw_reader(reader, test_preloading=True, **kwargs):
         raw = reader(preload=True, **kwargs)
         # don't assume the first is preloaded
         buffer_fname = op.join(tempdir, 'buffer')
-        picks = rng.permutation(np.arange(len(raw.ch_names)))[:10]
+        picks = rng.permutation(np.arange(len(raw.ch_names) - 1))[:10]
+        picks = np.append(picks, len(raw.ch_names) - 1)  # test trigger channel
         bnd = min(int(round(raw.info['buffer_size_sec'] *
                             raw.info['sfreq'])), raw.n_times)
         slices = [slice(0, bnd), slice(bnd - 1, bnd), slice(3, bnd),
@@ -102,11 +103,12 @@ def _test_concat(reader, *args):
         data = raw[:, :][0]
         for preloads in ((True, True), (True, False), (False, False)):
             for last_preload in (True, False):
-                print(first_preload, preloads, last_preload)
-                raw1 = raw.crop(0, 0.4999)
+                t_crops = raw.times[np.argmin(np.abs(raw.times - 0.5)) +
+                                    [0, 1]]
+                raw1 = raw.copy().crop(0, t_crops[0], copy=False)
                 if preloads[0]:
                     raw1.load_data()
-                raw2 = raw.crop(0.5, None)
+                raw2 = raw.copy().crop(t_crops[1], None, copy=False)
                 if preloads[1]:
                     raw2.load_data()
                 raw1.append(raw2)
diff --git a/mne/io/tests/test_reference.py b/mne/io/tests/test_reference.py
index ea01708..b01fb88 100644
--- a/mne/io/tests/test_reference.py
+++ b/mne/io/tests/test_reference.py
@@ -12,6 +12,7 @@ from nose.tools import assert_true, assert_equal, assert_raises
 from numpy.testing import assert_array_equal, assert_allclose
 
 from mne import pick_channels, pick_types, Evoked, Epochs, read_events
+from mne.epochs import _BaseEpochs
 from mne.io.constants import FIFF
 from mne.io import (set_eeg_reference, set_bipolar_reference,
                     add_reference_channels)
@@ -59,7 +60,7 @@ def _test_reference(raw, reref, ref_data, ref_from):
     reref_other_data = _reref[..., picks_other, :]
 
     # Undo rereferencing of EEG channels
-    if isinstance(raw, Epochs):
+    if isinstance(raw, _BaseEpochs):
         unref_eeg_data = reref_eeg_data + ref_data[:, np.newaxis, :]
     else:
         unref_eeg_data = reref_eeg_data + ref_data
@@ -75,8 +76,8 @@ def test_apply_reference():
     raw = Raw(fif_fname, preload=True)
 
     # Rereference raw data by creating a copy of original data
-    reref, ref_data = _apply_reference(raw, ref_from=['EEG 001', 'EEG 002'],
-                                       copy=True)
+    reref, ref_data = _apply_reference(
+        raw.copy(), ref_from=['EEG 001', 'EEG 002'])
     assert_true(reref.info['custom_ref_applied'])
     _test_reference(raw, reref, ref_data, ['EEG 001', 'EEG 002'])
 
@@ -88,8 +89,7 @@ def test_apply_reference():
     assert_array_equal(raw._data, reref._data)
 
     # Test that data is modified in place when copy=False
-    reref, ref_data = _apply_reference(raw, ['EEG 001', 'EEG 002'],
-                                       copy=False)
+    reref, ref_data = _apply_reference(raw, ['EEG 001', 'EEG 002'])
     assert_true(raw is reref)
 
     # Test re-referencing Epochs object
@@ -98,15 +98,15 @@ def test_apply_reference():
     picks_eeg = pick_types(raw.info, meg=False, eeg=True)
     epochs = Epochs(raw, events=events, event_id=1, tmin=-0.2, tmax=0.5,
                     picks=picks_eeg, preload=True)
-    reref, ref_data = _apply_reference(epochs, ref_from=['EEG 001', 'EEG 002'],
-                                       copy=True)
+    reref, ref_data = _apply_reference(
+        epochs.copy(), ref_from=['EEG 001', 'EEG 002'])
     assert_true(reref.info['custom_ref_applied'])
     _test_reference(epochs, reref, ref_data, ['EEG 001', 'EEG 002'])
 
     # Test re-referencing Evoked object
     evoked = epochs.average()
-    reref, ref_data = _apply_reference(evoked, ref_from=['EEG 001', 'EEG 002'],
-                                       copy=True)
+    reref, ref_data = _apply_reference(
+        evoked.copy(), ref_from=['EEG 001', 'EEG 002'])
     assert_true(reref.info['custom_ref_applied'])
     _test_reference(evoked, reref, ref_data, ['EEG 001', 'EEG 002'])
 
@@ -128,7 +128,8 @@ def test_set_eeg_reference():
     assert_true(ref_data is None)
 
     # Test setting an average reference when one was already present
-    reref, ref_data = set_eeg_reference(raw, copy=False)
+    with warnings.catch_warnings(record=True):  # weight tables
+        reref, ref_data = set_eeg_reference(raw, copy=False)
     assert_true(ref_data is None)
 
     # Rereference raw data by creating a copy of original data
@@ -152,9 +153,9 @@ def test_set_bipolar_reference():
     assert_true(reref.info['custom_ref_applied'])
 
     # Compare result to a manual calculation
-    a = raw.pick_channels(['EEG 001', 'EEG 002'], copy=True)
+    a = raw.copy().pick_channels(['EEG 001', 'EEG 002'])
     a = a._data[0, :] - a._data[1, :]
-    b = reref.pick_channels(['bipolar'], copy=True)._data[0, :]
+    b = reref.copy().pick_channels(['bipolar'])._data[0, :]
     assert_allclose(a, b)
 
     # Original channels should be replaced by a virtual one
diff --git a/mne/io/utils.py b/mne/io/utils.py
index 0cf45fc..cf8dcaa 100644
--- a/mne/io/utils.py
+++ b/mne/io/utils.py
@@ -10,6 +10,9 @@
 
 import numpy as np
 
+from ..externals.six import b
+from .constants import FIFF
+
 
 def _find_channels(ch_names, ch_type='EOG'):
     """Helper to find EOG channel.
@@ -139,13 +142,15 @@ def _blk_read_lims(start, stop, buf_len):
 
 
 def _read_segments_file(raw, data, idx, fi, start, stop, cals, mult,
-                        dtype='<i2'):
+                        dtype='<i2', n_channels=None, offset=0,
+                        trigger_ch=None):
     """Read a chunk of raw data"""
-    n_channels = raw.info['nchan']
+    if n_channels is None:
+        n_channels = raw.info['nchan']
     n_bytes = np.dtype(dtype).itemsize
     # data_offset and data_left count data samples (channels x time points),
     # not bytes.
-    data_offset = n_channels * start * n_bytes
+    data_offset = n_channels * start * n_bytes + offset
     data_left = (stop - start) * n_channels
 
     # Read up to 100 MB of data at a time, block_size is in data samples
@@ -155,11 +160,78 @@ def _read_segments_file(raw, data, idx, fi, start, stop, cals, mult,
         fid.seek(data_offset)
         # extract data in chunks
         for sample_start in np.arange(0, data_left, block_size) // n_channels:
-
             count = min(block_size, data_left - sample_start * n_channels)
             block = np.fromfile(fid, dtype, count)
             block = block.reshape(n_channels, -1, order='F')
             n_samples = block.shape[1]  # = count // n_channels
             sample_stop = sample_start + n_samples
+            if trigger_ch is not None:
+                stim_ch = trigger_ch[start:stop][sample_start:sample_stop]
+                block = np.vstack((block, stim_ch))
             data_view = data[:, sample_start:sample_stop]
             _mult_cal_one(data_view, block, idx, cals, mult)
+
+
+def read_str(fid, count=1):
+    """Read string from a binary file in a python version compatible way."""
+    dtype = np.dtype('>S%i' % count)
+    string = fid.read(dtype.itemsize)
+    data = np.fromstring(string, dtype=dtype)[0]
+    bytestr = b('').join([data[0:data.index(b('\x00')) if
+                          b('\x00') in data else count]])
+
+    return str(bytestr.decode('ascii'))  # Return native str type for Py2/3
+
+
+def _create_chs(ch_names, cals, ch_coil, ch_kind, eog, ecg, emg, misc):
+    """Helper for initializing info['chs'] for eeg channels."""
+    chs = list()
+    for idx, ch_name in enumerate(ch_names):
+        if ch_name in eog or idx in eog:
+            coil_type = FIFF.FIFFV_COIL_NONE
+            kind = FIFF.FIFFV_EOG_CH
+        elif ch_name in ecg or idx in ecg:
+            coil_type = FIFF.FIFFV_COIL_NONE
+            kind = FIFF.FIFFV_ECG_CH
+        elif ch_name in emg or idx in emg:
+            coil_type = FIFF.FIFFV_COIL_NONE
+            kind = FIFF.FIFFV_EMG_CH
+        elif ch_name in misc or idx in misc:
+            coil_type = FIFF.FIFFV_COIL_NONE
+            kind = FIFF.FIFFV_MISC_CH
+        else:
+            coil_type = ch_coil
+            kind = ch_kind
+
+        chan_info = {'cal': cals[idx], 'logno': idx + 1, 'scanno': idx + 1,
+                     'range': 1.0, 'unit_mul': 0., 'ch_name': ch_name,
+                     'unit': FIFF.FIFF_UNIT_V,
+                     'coord_frame': FIFF.FIFFV_COORD_HEAD,
+                     'coil_type': coil_type, 'kind': kind, 'loc': np.zeros(12)}
+        chs.append(chan_info)
+    return chs
+
+
+def _synthesize_stim_channel(events, n_samples):
+    """Synthesize a stim channel from events read from an event file
+
+    Parameters
+    ----------
+    events : array, shape (n_events, 3)
+        Each row representing an event as (onset, duration, trigger) sequence
+        (the format returned by `_read_vmrk_events` or `_read_eeglab_events`).
+    n_samples : int
+        The number of samples.
+
+    Returns
+    -------
+    stim_channel : array, shape (n_samples,)
+        An array containing the whole recording's event marking.
+    """
+    # select events overlapping buffer
+    onset = events[:, 0]
+    # create output buffer
+    stim_channel = np.zeros(n_samples, int)
+    for onset, duration, trigger in events:
+        stim_channel[onset:onset + duration] = trigger
+    return stim_channel
diff --git a/mne/io/write.py b/mne/io/write.py
index 2c602fc..56e49f9 100644
--- a/mne/io/write.py
+++ b/mne/io/write.py
@@ -98,11 +98,11 @@ def write_julian(fid, kind, data):
 
 def write_string(fid, kind, data):
     """Writes a string tag"""
-
     str_data = data.encode('utf-8')  # Use unicode or bytes depending on Py2/3
     data_size = len(str_data)  # therefore compute size here
     my_dtype = '>a'  # py2/3 compatible on writing -- don't ask me why
-    _write(fid, str_data, kind, data_size, FIFF.FIFFT_STRING, my_dtype)
+    if data_size > 0:
+        _write(fid, str_data, kind, data_size, FIFF.FIFFT_STRING, my_dtype)
 
 
 def write_name_list(fid, kind, data):
diff --git a/mne/label.py b/mne/label.py
index 8452a31..a56319a 100644
--- a/mne/label.py
+++ b/mne/label.py
@@ -15,14 +15,15 @@ import numpy as np
 from scipy import linalg, sparse
 
 from .fixes import digitize, in1d
-from .utils import get_subjects_dir, _check_subject, logger, verbose
+from .utils import (get_subjects_dir, _check_subject, logger, verbose, warn,
+                    _check_copy_dep)
 from .source_estimate import (morph_data, SourceEstimate,
                               spatial_src_connectivity)
 from .source_space import add_source_space_distances
 from .surface import read_surface, fast_cross_3d, mesh_edges, mesh_dist
 from .source_space import SourceSpaces
 from .parallel import parallel_func, check_n_jobs
-from .stats.cluster_level import _find_clusters
+from .stats.cluster_level import _find_clusters, _get_components
 from .externals.six import b, string_types
 from .externals.six.moves import zip, xrange
 
@@ -427,10 +428,10 @@ class Label(object):
 
         nearest = hemi_src['nearest']
         if nearest is None:
-            logger.warn("Computing patch info for source space, this can take "
-                        "a while. In order to avoid this in the future, run "
-                        "mne.add_source_space_distances() on the source space "
-                        "and save it.")
+            warn("Computing patch info for source space, this can take "
+                 "a while. In order to avoid this in the future, run "
+                 "mne.add_source_space_distances() on the source space "
+                 "and save it.")
             add_source_space_distances(src)
             nearest = hemi_src['nearest']
 
@@ -452,7 +453,7 @@ class Label(object):
 
     @verbose
     def smooth(self, subject=None, smooth=2, grade=None,
-               subjects_dir=None, n_jobs=1, copy=True, verbose=None):
+               subjects_dir=None, n_jobs=1, copy=None, verbose=None):
         """Smooth the label
 
         Useful for filling in labels made in a
@@ -484,7 +485,9 @@ class Label(object):
         n_jobs : int
             Number of jobs to run in parallel
         copy : bool
-            If False, smoothing is done in-place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
         verbose : bool, str, int, or None
             If not None, override default verbose level (see mne.verbose).
             Defaults to self.verbose.
@@ -502,11 +505,11 @@ class Label(object):
         """
         subject = _check_subject(self.subject, subject)
         return self.morph(subject, subject, smooth, grade, subjects_dir,
-                          n_jobs, copy)
+                          n_jobs, copy=copy)
 
     @verbose
     def morph(self, subject_from=None, subject_to=None, smooth=5, grade=None,
-              subjects_dir=None, n_jobs=1, copy=True, verbose=None):
+              subjects_dir=None, n_jobs=1, copy=None, verbose=None):
         """Morph the label
 
         Useful for transforming a label from one subject to another.
@@ -538,7 +541,9 @@ class Label(object):
         n_jobs : int
             Number of jobs to run in parallel.
         copy : bool
-            If False, the morphing is done in-place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
         verbose : bool, str, int, or None
             If not None, override default verbose level (see mne.verbose).
 
@@ -578,10 +583,7 @@ class Label(object):
                          smooth=smooth, subjects_dir=subjects_dir,
                          warn=False, n_jobs=n_jobs)
         inds = np.nonzero(stc.data)[0]
-        if copy is True:
-            label = self.copy()
-        else:
-            label = self
+        label = _check_copy_dep(self, copy, default=True)
         label.values = stc.data[inds, :].ravel()
         label.pos = np.zeros((len(inds), 3))
         if label.hemi == 'lh':
@@ -597,10 +599,11 @@ class Label(object):
 
         Parameters
         ----------
-        parts : int >= 2 | tuple of str
-            A sequence of strings specifying label names for the new labels
-            (from posterior to anterior), or the number of new labels to create
-            (default is 2). If a number is specified, names of the new labels
+        parts : int >= 2 | tuple of str | str
+            Number of labels to create (default is 2), or tuple of strings
+            specifying label names for new labels (from posterior to anterior),
+            or 'contiguous' to split the label into connected components.
+            If a number or 'contiguous' is specified, names of the new labels
             will be the input label's name with div1, div2 etc. appended.
         subject : None | str
             Subject which this label belongs to (needed to locate surface file;
@@ -621,11 +624,22 @@ class Label(object):
 
         Notes
         -----
-        Works by finding the label's principal eigen-axis on the spherical
-        surface, projecting all label vertex coordinates onto this axis and
-        dividing them at regular spatial intervals.
+        If using 'contiguous' split, you must ensure that the label being split
+        uses the same triangular resolution as the surface mesh files in
+        ``subjects_dir`` Also, some small fringe labels may be returned that
+        are close (but not connected) to the large components.
+
+        The spatial split finds the label's principal eigen-axis on the
+        spherical surface, projects all label vertex coordinates onto this
+        axis, and divides them at regular spatial intervals.
         """
-        return split_label(self, parts, subject, subjects_dir, freesurfer)
+        if isinstance(parts, string_types) and parts == 'contiguous':
+            return _split_label_contig(self, subject, subjects_dir)
+        elif isinstance(parts, (tuple, int)):
+            return split_label(self, parts, subject, subjects_dir, freesurfer)
+        else:
+            raise ValueError("Need integer, tuple of strings, or string "
+                             "('contiguous'). Got %s)" % type(parts))
 
     def get_vertices_used(self, vertices=None):
         """Get the source space's vertices inside the label
@@ -711,6 +725,7 @@ class BiHemiLabel(object):
         A name for the label. It is OK to change that attribute manually.
     subject : str | None
         Subject the label is from.
+
     """
 
     def __init__(self, lh, rh, name=None, color=None):
@@ -896,6 +911,111 @@ def write_label(filename, label, verbose=None):
     return label
 
 
+def _prep_label_split(label, subject=None, subjects_dir=None):
+    """Helper to get label and subject information prior to label spliting"""
+
+    # If necessary, find the label
+    if isinstance(label, BiHemiLabel):
+        raise TypeError("Can only split labels restricted to one hemisphere.")
+    elif isinstance(label, string_types):
+        label = read_label(label)
+
+    # Find the subject
+    subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)
+    if label.subject is None and subject is None:
+        raise ValueError("The subject needs to be specified.")
+    elif subject is None:
+        subject = label.subject
+    elif label.subject is None:
+        pass
+    elif subject != label.subject:
+        raise ValueError("The label specifies a different subject (%r) from "
+                         "the subject parameter (%r)."
+                         % label.subject, subject)
+
+    return label, subject, subjects_dir
+
+
+def _split_label_contig(label_to_split, subject=None, subjects_dir=None):
+    """Split label into contiguous regions (i.e., connected components)
+
+    Parameters
+    ----------
+    label_to_split : Label | str
+        Label which is to be split (Label object or path to a label file).
+    subject : None | str
+        Subject which this label belongs to (needed to locate surface file;
+        should only be specified if it is not specified in the label).
+    subjects_dir : None | str
+        Path to SUBJECTS_DIR if it is not set in the environment.
+
+    Returns
+    -------
+    labels : list of Label
+        The contiguous labels, in order of decending size.
+    """
+    # Convert to correct input if necessary
+    label_to_split, subject, subjects_dir = _prep_label_split(label_to_split,
+                                                              subject,
+                                                              subjects_dir)
+
+    # Find the spherical surface to get vertices and tris
+    surf_fname = '.'.join((label_to_split.hemi, 'sphere'))
+    surf_path = op.join(subjects_dir, subject, 'surf', surf_fname)
+    surface_points, surface_tris = read_surface(surf_path)
+
+    # Get vertices we want to keep and compute mesh edges
+    verts_arr = label_to_split.vertices
+    edges_all = mesh_edges(surface_tris)
+
+    # Subselect rows and cols of vertices that belong to the label
+    select_edges = edges_all[verts_arr][:, verts_arr].tocoo()
+
+    # Compute connected components and store as lists of vertex numbers
+    comp_labels = _get_components(verts_arr, select_edges)
+
+    # Convert to indices in the original surface space
+    label_divs = []
+    for comp in comp_labels:
+        label_divs.append(verts_arr[comp])
+
+    # Construct label division names
+    n_parts = len(label_divs)
+    if label_to_split.name.endswith(('lh', 'rh')):
+        basename = label_to_split.name[:-3]
+        name_ext = label_to_split.name[-3:]
+    else:
+        basename = label_to_split.name
+        name_ext = ''
+    name_pattern = "%s_div%%i%s" % (basename, name_ext)
+    names = tuple(name_pattern % i for i in range(1, n_parts + 1))
+
+    # Colors
+    if label_to_split.color is None:
+        colors = (None,) * n_parts
+    else:
+        colors = _split_colors(label_to_split.color, n_parts)
+
+    # Sort label divisions by their size (in vertices)
+    label_divs.sort(key=lambda x: len(x), reverse=True)
+    labels = []
+    for div, name, color in zip(label_divs, names, colors):
+        # Get indices of dipoles within this division of the label
+        verts = np.array(sorted(list(div)))
+        vert_indices = in1d(verts_arr, verts, assume_unique=True)
+
+        # Set label attributes
+        pos = label_to_split.pos[vert_indices]
+        values = label_to_split.values[vert_indices]
+        hemi = label_to_split.hemi
+        comment = label_to_split.comment
+        lbl = Label(verts, pos, values, hemi, comment, name, None, subject,
+                    color)
+        labels.append(lbl)
+
+    return labels
+
+
 def split_label(label, parts=2, subject=None, subjects_dir=None,
                 freesurfer=False):
     """Split a Label into two or more parts
@@ -932,11 +1052,9 @@ def split_label(label, parts=2, subject=None, subjects_dir=None,
     projecting all label vertex coordinates onto this axis and dividing them at
     regular spatial intervals.
     """
-    # find the label
-    if isinstance(label, BiHemiLabel):
-        raise TypeError("Can only split labels restricted to one hemisphere.")
-    elif isinstance(label, string_types):
-        label = read_label(label)
+
+    label, subject, subjects_dir = _prep_label_split(label, subject,
+                                                     subjects_dir)
 
     # find the parts
     if np.isscalar(parts):
@@ -956,22 +1074,9 @@ def split_label(label, parts=2, subject=None, subjects_dir=None,
     if n_parts < 2:
         raise ValueError("Can't split label into %i parts" % n_parts)
 
-    # find the subject
-    subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)
-    if label.subject is None and subject is None:
-        raise ValueError("The subject needs to be specified.")
-    elif subject is None:
-        subject = label.subject
-    elif label.subject is None:
-        pass
-    elif subject != label.subject:
-        raise ValueError("The label specifies a different subject (%r) from "
-                         "the subject parameter (%r)."
-                         % label.subject, subject)
-
     # find the spherical surface
     surf_fname = '.'.join((label.hemi, 'sphere'))
-    surf_path = os.path.join(subjects_dir, subject, "surf", surf_fname)
+    surf_path = op.join(subjects_dir, subject, "surf", surf_fname)
     surface_points, surface_tris = read_surface(surf_path)
     # find the label coordinates on the surface
     points = surface_points[label.vertices]
@@ -1081,7 +1186,7 @@ def label_sign_flip(label, src):
     _, _, Vh = linalg.svd(ori, full_matrices=False)
 
     # Comparing to the direction of the first right singular vector
-    flip = np.sign(np.dot(ori, Vh[:, 0] if len(vertno_sel) > 3 else Vh[0]))
+    flip = np.sign(np.dot(ori, Vh[0]))
     return flip
 
 
@@ -1822,10 +1927,9 @@ def write_labels_to_annot(labels, subject=None, parc=None, overwrite=False,
                 if color == (0, 0, 0):
                     # we cannot have an all-zero color, otherw. e.g. tksurfer
                     # refuses to read the parcellation
-                    msg = ('At least one label contains a color with, "r=0, '
-                           'g=0, b=0" value. Some FreeSurfer tools may fail '
-                           'to read the parcellation')
-                    logger.warning(msg)
+                    warn('At least one label contains a color with, "r=0, '
+                         'g=0, b=0" value. Some FreeSurfer tools may fail '
+                         'to read the parcellation')
 
                 if any(i > 255 for i in color):
                     msg = ("%s: %s (%s)" % (color, ', '.join(names), hemi))
@@ -1853,8 +1957,7 @@ def write_labels_to_annot(labels, subject=None, parc=None, overwrite=False,
 
         # find number of vertices in surface
         if subject is not None and subjects_dir is not None:
-            fpath = os.path.join(subjects_dir, subject, 'surf',
-                                 '%s.white' % hemi)
+            fpath = op.join(subjects_dir, subject, 'surf', '%s.white' % hemi)
             points, _ = read_surface(fpath)
             n_vertices = len(points)
         else:
@@ -1863,10 +1966,9 @@ def write_labels_to_annot(labels, subject=None, parc=None, overwrite=False,
                 n_vertices = max_vert + 1
             else:
                 n_vertices = 1
-            msg = ('    Number of vertices in the surface could not be '
-                   'verified because the surface file could not be found; '
-                   'specify subject and subjects_dir parameters.')
-            logger.warning(msg)
+            warn('Number of vertices in the surface could not be '
+                 'verified because the surface file could not be found; '
+                 'specify subject and subjects_dir parameters.')
 
         # Create annot and color table array to write
         annot = np.empty(n_vertices, dtype=np.int)
diff --git a/mne/minimum_norm/inverse.py b/mne/minimum_norm/inverse.py
index 713de53..2de13b5 100644
--- a/mne/minimum_norm/inverse.py
+++ b/mne/minimum_norm/inverse.py
@@ -4,7 +4,6 @@
 #
 # License: BSD (3-clause)
 
-import warnings
 from copy import deepcopy
 from math import sqrt
 import numpy as np
@@ -32,7 +31,7 @@ from ..source_space import (_read_source_spaces_from_tree,
                             _write_source_spaces_to_fid, label_src_vertno_sel)
 from ..transforms import _ensure_trans, transform_surface_to
 from ..source_estimate import _make_stc
-from ..utils import check_fname, logger, verbose
+from ..utils import check_fname, logger, verbose, warn
 from functools import reduce
 
 
@@ -40,6 +39,10 @@ class InverseOperator(dict):
     """InverseOperator class to represent info from inverse operator
     """
 
+    def copy(self):
+        """Return a copy of the InverseOperator"""
+        return InverseOperator(deepcopy(self))
+
     def __repr__(self):
         """Summarize inverse info instead of printing all"""
 
@@ -197,18 +200,18 @@ def read_inverse_operator(fname, verbose=None):
         #   The eigenleads and eigenfields
         #
         inv['eigen_leads_weighted'] = False
-        eigen_leads = _read_named_matrix(
-            fid, invs, FIFF.FIFF_MNE_INVERSE_LEADS)
-        if eigen_leads is None:
+        inv['eigen_leads'] = _read_named_matrix(
+            fid, invs, FIFF.FIFF_MNE_INVERSE_LEADS, transpose=True)
+        if inv['eigen_leads'] is None:
             inv['eigen_leads_weighted'] = True
-            eigen_leads = _read_named_matrix(
-                fid, invs, FIFF.FIFF_MNE_INVERSE_LEADS_WEIGHTED)
-        if eigen_leads is None:
+            inv['eigen_leads'] = _read_named_matrix(
+                fid, invs, FIFF.FIFF_MNE_INVERSE_LEADS_WEIGHTED,
+                transpose=True)
+        if inv['eigen_leads'] is None:
             raise ValueError('Eigen leads not found in inverse operator.')
         #
         #   Having the eigenleads as cols is better for the inverse calcs
         #
-        inv['eigen_leads'] = _transpose_named_matrix(eigen_leads, copy=False)
         inv['eigen_fields'] = _read_named_matrix(fid, invs,
                                                  FIFF.FIFF_MNE_INVERSE_FIELDS)
         logger.info('    [done]')
@@ -410,11 +413,12 @@ def write_inverse_operator(fname, inv, verbose=None):
     #   The eigenleads and eigenfields
     #
     if inv['eigen_leads_weighted']:
-        write_named_matrix(fid, FIFF.FIFF_MNE_INVERSE_LEADS_WEIGHTED,
-                           _transpose_named_matrix(inv['eigen_leads']))
+        kind = FIFF.FIFF_MNE_INVERSE_LEADS_WEIGHTED
     else:
-        write_named_matrix(fid, FIFF.FIFF_MNE_INVERSE_LEADS,
-                           _transpose_named_matrix(inv['eigen_leads']))
+        kind = FIFF.FIFF_MNE_INVERSE_LEADS
+    _transpose_named_matrix(inv['eigen_leads'])
+    write_named_matrix(fid, kind, inv['eigen_leads'])
+    _transpose_named_matrix(inv['eigen_leads'])
 
     #
     #   Done!
@@ -507,7 +511,7 @@ def prepare_inverse_operator(orig, nave, lambda2, method, verbose=None):
         raise ValueError('The number of averages should be positive')
 
     logger.info('Preparing the inverse operator for use...')
-    inv = deepcopy(orig)
+    inv = orig.copy()
     #
     #   Scale some of the stuff
     #
@@ -1235,8 +1239,8 @@ def make_inverse_operator(info, forward, noise_cov, loose=0.2, depth=0.8,
     is_fixed_ori = is_fixed_orient(forward)
 
     if fixed and loose is not None:
-        warnings.warn("When invoking make_inverse_operator with fixed=True, "
-                      "the loose parameter is ignored.")
+        warn('When invoking make_inverse_operator with fixed=True, the loose '
+             'parameter is ignored.')
         loose = None
 
     if is_fixed_ori and not fixed:
@@ -1570,7 +1574,7 @@ def estimate_snr(evoked, inv, verbose=None):
             break
         lambda2_est[remaining] *= lambda_mult
     else:
-        warnings.warn('SNR estimation did not converge')
+        warn('SNR estimation did not converge')
     snr_est = 1.0 / np.sqrt(lambda2_est)
     snr = np.sqrt(snr)
     return snr, snr_est
diff --git a/mne/minimum_norm/tests/test_inverse.py b/mne/minimum_norm/tests/test_inverse.py
index 22747ce..8247e85 100644
--- a/mne/minimum_norm/tests/test_inverse.py
+++ b/mne/minimum_norm/tests/test_inverse.py
@@ -16,13 +16,14 @@ from mne.source_estimate import read_source_estimate, VolSourceEstimate
 from mne import (read_cov, read_forward_solution, read_evokeds, pick_types,
                  pick_types_forward, make_forward_solution,
                  convert_forward_solution, Covariance)
-from mne.io import Raw
+from mne.io import Raw, Info
 from mne.minimum_norm.inverse import (apply_inverse, read_inverse_operator,
                                       apply_inverse_raw, apply_inverse_epochs,
                                       make_inverse_operator,
                                       write_inverse_operator,
                                       compute_rank_inverse,
                                       prepare_inverse_operator)
+from mne.tests.common import assert_naming
 from mne.utils import _TempDir, run_tests_if_main, slow_test
 from mne.externals import six
 
@@ -81,8 +82,8 @@ def _compare(a, b):
     skip_types = ['whitener', 'proj', 'reginv', 'noisenorm', 'nchan',
                   'command_line', 'working_dir', 'mri_file', 'mri_id']
     try:
-        if isinstance(a, dict):
-            assert_true(isinstance(b, dict))
+        if isinstance(a, (dict, Info)):
+            assert_true(isinstance(b, (dict, Info)))
             for k, v in six.iteritems(a):
                 if k not in b and k not in skip_types:
                     raise ValueError('First one had one second one didn\'t:\n'
@@ -225,8 +226,9 @@ def test_inverse_operator_channel_ordering():
     randomiser = np.random.RandomState(42)
     randomiser.shuffle(new_order)
     evoked.data = evoked.data[new_order]
-    evoked.info['ch_names'] = [evoked.info['ch_names'][n] for n in new_order]
     evoked.info['chs'] = [evoked.info['chs'][n] for n in new_order]
+    evoked.info._update_redundant()
+    evoked.info._check_consistency()
 
     cov_ch_reorder = [c for c in evoked.info['ch_names']
                       if (c in noise_cov.ch_names)]
@@ -378,9 +380,9 @@ def test_make_inverse_operator_diag():
     """Test MNE inverse computation with diagonal noise cov
     """
     evoked = _get_evoked()
-    noise_cov = read_cov(fname_cov)
+    noise_cov = read_cov(fname_cov).as_diag(copy=False)
     fwd_op = read_forward_solution(fname_fwd, surf_ori=True)
-    inv_op = make_inverse_operator(evoked.info, fwd_op, noise_cov.as_diag(),
+    inv_op = make_inverse_operator(evoked.info, fwd_op, noise_cov,
                                    loose=0.2, depth=0.8)
     _compare_io(inv_op)
     inverse_operator_diag = read_inverse_operator(fname_inv_meeg_diag)
@@ -445,7 +447,7 @@ def test_io_inverse_operator():
         inv_badname = op.join(tempdir, 'test-bad-name.fif.gz')
         write_inverse_operator(inv_badname, inverse_operator)
         read_inverse_operator(inv_badname)
-    assert_true(len(w) == 2)
+    assert_naming(w, 'test_inverse.py', 2)
 
     # make sure we can write and read
     inv_fname = op.join(tempdir, 'test-inv.fif')
diff --git a/mne/minimum_norm/tests/test_time_frequency.py b/mne/minimum_norm/tests/test_time_frequency.py
index c20e0d3..0aa1ec3 100644
--- a/mne/minimum_norm/tests/test_time_frequency.py
+++ b/mne/minimum_norm/tests/test_time_frequency.py
@@ -18,7 +18,7 @@ from mne.minimum_norm.time_frequency import (source_band_induced_power,
                                              compute_source_psd_epochs)
 
 
-from mne.time_frequency import multitaper_psd
+from mne.time_frequency.multitaper import _psd_multitaper
 
 data_path = testing.data_path(download=False)
 fname_inv = op.join(data_path, 'MEG', 'sample',
@@ -36,7 +36,7 @@ def test_tfr_with_inverse_operator():
     tmin, tmax, event_id = -0.2, 0.5, 1
 
     # Setup for reading the raw data
-    raw = io.Raw(fname_data)
+    raw = io.read_raw_fif(fname_data)
     events = find_events(raw, stim_channel='STI 014')
     inverse_operator = read_inverse_operator(fname_inv)
     inv = prepare_inverse_operator(inverse_operator, nave=1,
@@ -95,7 +95,7 @@ def test_tfr_with_inverse_operator():
 @testing.requires_testing_data
 def test_source_psd():
     """Test source PSD computation in label"""
-    raw = io.Raw(fname_data)
+    raw = io.read_raw_fif(fname_data)
     inverse_operator = read_inverse_operator(fname_inv)
     label = read_label(fname_label)
     tmin, tmax = 0, 20  # seconds
@@ -116,7 +116,7 @@ def test_source_psd():
 def test_source_psd_epochs():
     """Test multi-taper source PSD computation in label from epochs"""
 
-    raw = io.Raw(fname_data)
+    raw = io.read_raw_fif(fname_data)
     inverse_operator = read_inverse_operator(fname_inv)
     label = read_label(fname_label)
 
@@ -135,7 +135,7 @@ def test_source_psd_epochs():
                     baseline=(None, 0), reject=reject)
 
     # only look at one epoch
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
     one_epochs = epochs[:1]
 
     inv = prepare_inverse_operator(inverse_operator, nave=1,
@@ -169,8 +169,8 @@ def test_source_psd_epochs():
                                prepared=True)[0]
 
     sfreq = epochs.info['sfreq']
-    psd, freqs = multitaper_psd(stc.data, sfreq=sfreq, bandwidth=bandwidth,
-                                fmin=fmin, fmax=fmax)
+    psd, freqs = _psd_multitaper(stc.data, sfreq=sfreq, bandwidth=bandwidth,
+                                 fmin=fmin, fmax=fmax)
 
     assert_array_almost_equal(psd, stc_psd.data)
     assert_array_almost_equal(freqs, stc_psd.times)
diff --git a/mne/minimum_norm/time_frequency.py b/mne/minimum_norm/time_frequency.py
index 81e037b..eb87303 100644
--- a/mne/minimum_norm/time_frequency.py
+++ b/mne/minimum_norm/time_frequency.py
@@ -3,23 +3,20 @@
 #
 # License: BSD (3-clause)
 
-from warnings import warn
-
 import numpy as np
 from scipy import linalg, fftpack
-import warnings
 
 from ..io.constants import FIFF
 from ..source_estimate import _make_stc
 from ..time_frequency.tfr import cwt, morlet
 from ..time_frequency.multitaper import (dpss_windows, _psd_from_mt,
                                          _psd_from_mt_adaptive, _mt_spectra)
-from ..baseline import rescale
+from ..baseline import rescale, _log_rescale
 from .inverse import (combine_xyz, prepare_inverse_operator, _assemble_kernel,
                       _pick_channels_inverse_operator, _check_method,
                       _check_ori, _subject_from_inverse)
 from ..parallel import parallel_func
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from ..externals import six
 
 
@@ -94,13 +91,11 @@ def source_band_induced_power(epochs, inverse_operator, bands, label=None,
     decim : int
         Temporal decimation factor.
     baseline : None (default) or tuple of length 2
-        The time interval to apply baseline correction.
-        If None do not apply it. If baseline is (a, b)
-        the interval is between "a (s)" and "b (s)".
-        If a is None the beginning of the data is used
-        and if b is None then b is set to the end of the interval.
-        If baseline is equal ot (None, None) all the time
-        interval is used.
+        The time interval to apply baseline correction. If None do not apply
+        it. If baseline is (a, b) the interval is between "a (s)" and "b (s)".
+        If a is None the beginning of the data is used and if b is None then b
+        is set to the end of the interval. If baseline is equal to (None, None)
+        all the time interval is used.
     baseline_mode : None | 'logratio' | 'zscore'
         Do baseline correction with ratio (power is divided by mean
         power during baseline) or zscore (power is divided by standard
@@ -137,6 +132,7 @@ def source_band_induced_power(epochs, inverse_operator, bands, label=None,
     stcs = dict()
 
     subject = _subject_from_inverse(inverse_operator)
+    _log_rescale(baseline, baseline_mode)
     for name, band in six.iteritems(bands):
         idx = [k for k, f in enumerate(frequencies) if band[0] <= f <= band[1]]
 
@@ -145,7 +141,7 @@ def source_band_induced_power(epochs, inverse_operator, bands, label=None,
 
         # Run baseline correction
         power = rescale(power, epochs.times[::decim], baseline, baseline_mode,
-                        copy=False)
+                        copy=False, verbose=False)
 
         tmin = epochs.times[0]
         tstep = float(decim) / Fs
@@ -370,10 +366,8 @@ def source_induced_power(epochs, inverse_operator, frequencies, label=None,
                                                prepared=False)
 
     # Run baseline correction
-    if baseline is not None:
-        power = rescale(power, epochs.times[::decim], baseline, baseline_mode,
-                        copy=False)
-
+    power = rescale(power, epochs.times[::decim], baseline, baseline_mode,
+                    copy=False)
     return power, plv
 
 
@@ -520,7 +514,7 @@ def _compute_source_psd_epochs(epochs, inverse_operator, lambda2=1. / 9.,
     # compute standardized half-bandwidth
     half_nbw = float(bandwidth) * n_times / (2 * sfreq)
     if half_nbw < 0.5:
-        warnings.warn('Bandwidth too small, using minimum (normalized 0.5)')
+        warn('Bandwidth too small, using minimum (normalized 0.5)')
         half_nbw = 0.5
     n_tapers_max = int(2 * half_nbw)
 
diff --git a/mne/parallel.py b/mne/parallel.py
index 71f5d62..990a996 100644
--- a/mne/parallel.py
+++ b/mne/parallel.py
@@ -10,7 +10,7 @@ import logging
 import os
 
 from . import get_config
-from .utils import logger, verbose
+from .utils import logger, verbose, warn
 from .fixes import _get_args
 
 if 'MNE_FORCE_SERIAL' in os.environ:
@@ -63,7 +63,7 @@ def parallel_func(func, n_jobs, verbose=None, max_nbytes='auto'):
         try:
             from sklearn.externals.joblib import Parallel, delayed
         except ImportError:
-            logger.warning('joblib not installed. Cannot run in parallel.')
+            warn('joblib not installed. Cannot run in parallel.')
             n_jobs = 1
             my_func = func
             parallel = list
@@ -79,8 +79,8 @@ def parallel_func(func, n_jobs, verbose=None, max_nbytes='auto'):
 
     if max_nbytes is not None:
         if not joblib_mmap and cache_dir is not None:
-            logger.warning('"MNE_CACHE_DIR" is set but a newer version of '
-                           'joblib is needed to use the memmapping pool.')
+            warn('"MNE_CACHE_DIR" is set but a newer version of joblib is '
+                 'needed to use the memmapping pool.')
         if joblib_mmap and cache_dir is None:
             logger.info('joblib supports memapping pool but "MNE_CACHE_DIR" '
                         'is not set in MNE-Python config. To enable it, use, '
@@ -141,8 +141,7 @@ def check_n_jobs(n_jobs, allow_cuda=False):
         except ImportError:
             # only warn if they tried to use something other than 1 job
             if n_jobs != 1:
-                logger.warning('multiprocessing not installed. Cannot run in '
-                               'parallel.')
+                warn('multiprocessing not installed. Cannot run in parallel.')
                 n_jobs = 1
 
     return n_jobs
diff --git a/mne/preprocessing/ecg.py b/mne/preprocessing/ecg.py
index cd50cec..822af12 100644
--- a/mne/preprocessing/ecg.py
+++ b/mne/preprocessing/ecg.py
@@ -4,12 +4,11 @@
 #
 # License: BSD (3-clause)
 
-import warnings
-from ..externals.six import string_types
 import numpy as np
 
 from .. import pick_types, pick_channels
-from ..utils import logger, verbose, sum_squared
+from ..externals.six import string_types
+from ..utils import logger, verbose, sum_squared, warn
 from ..filter import band_pass_filter
 from ..epochs import Epochs, _BaseEpochs
 from ..io.base import _BaseRaw
@@ -117,7 +116,7 @@ def qrs_detector(sfreq, ecg, thresh_value=0.6, levels=2.5, n_thresh=3,
     rates = np.array([60. * len(cev) / (len(ecg) / float(sfreq))
                       for cev in clean_events])
 
-    # now find heart rates that seem reasonable (infant thru adult athlete)
+    # now find heart rates that seem reasonable (infant through adult athlete)
     idx = np.where(np.logical_and(rates <= 160., rates >= 40.))[0]
     if len(idx) > 0:
         ideal_rate = np.median(rates[idx])  # get close to the median
@@ -218,8 +217,8 @@ def _get_ecg_channel_index(ch_name, inst):
         #                    'parameter e.g. MEG 1531')
 
     if len(ecg_idx) > 1:
-        warnings.warn('More than one ECG channel found. Using only %s.'
-                      % inst.ch_names[ecg_idx[0]])
+        warn('More than one ECG channel found. Using only %s.'
+             % inst.ch_names[ecg_idx[0]])
 
     return ecg_idx[0]
 
@@ -260,8 +259,8 @@ def create_ecg_epochs(raw, ch_name=None, event_id=999, picks=None,
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels)
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels)
                           )
 
     flat : dict | None
@@ -290,7 +289,7 @@ def create_ecg_epochs(raw, ch_name=None, event_id=999, picks=None,
     ecg_epochs : instance of Epochs
         Data epoched around ECG r-peaks.
     """
-    not_has_ecg = 'ecg' not in raw
+    not_has_ecg = 'ecg' not in raw and ch_name is None
     if not_has_ecg:
         ecg, times = _make_ecg(raw, None, None, verbose)
 
@@ -337,7 +336,7 @@ def _make_ecg(inst, start, stop, verbose=None):
     """Create ECG signal from cross channel average
     """
     if not any(c in inst for c in ['mag', 'grad']):
-        raise ValueError('Unable to generate artifical ECG channel')
+        raise ValueError('Unable to generate artificial ECG channel')
     for ch in ['mag', 'grad']:
         if ch in inst:
             break
@@ -348,7 +347,7 @@ def _make_ecg(inst, start, stop, verbose=None):
     if isinstance(inst, _BaseRaw):
         ecg, times = inst[picks, start:stop]
     elif isinstance(inst, _BaseEpochs):
-        ecg = np.hstack(inst.crop(start, stop, copy=True).get_data())
+        ecg = np.hstack(inst.copy().crop(start, stop).get_data())
         times = inst.times
     elif isinstance(inst, Evoked):
         ecg = inst.data
diff --git a/mne/preprocessing/eog.py b/mne/preprocessing/eog.py
index ece895c..cd96bdc 100644
--- a/mne/preprocessing/eog.py
+++ b/mne/preprocessing/eog.py
@@ -11,6 +11,7 @@ from .. import pick_types, pick_channels
 from ..utils import logger, verbose
 from ..filter import band_pass_filter
 from ..epochs import Epochs
+from ..externals.six import string_types
 
 
 @verbose
@@ -102,7 +103,7 @@ def _find_eog_events(eog, event_id, l_freq, h_freq, sampling_rate, first_samp,
 
 
 def _get_eog_channel_index(ch_name, inst):
-    if isinstance(ch_name, str):
+    if isinstance(ch_name, string_types):
         # Check if multiple EOG Channels
         if ',' in ch_name:
             ch_name = ch_name.split(',')
@@ -170,8 +171,8 @@ def create_eog_epochs(raw, ch_name=None, event_id=998, picks=None,
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels)
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels)
                           )
 
     flat : dict | None
diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py
index b416e42..76113a4 100644
--- a/mne/preprocessing/ica.py
+++ b/mne/preprocessing/ica.py
@@ -21,7 +21,8 @@ from .infomax_ import infomax
 
 from ..cov import compute_whitener
 from .. import Covariance, Evoked
-from ..io.pick import (pick_types, pick_channels, pick_info)
+from ..io.pick import (pick_types, pick_channels, pick_info,
+                       _pick_data_channels, _DATA_CH_TYPES_SPLIT)
 from ..io.write import (write_double_matrix, write_string,
                         write_name_list, write_int, start_block,
                         end_block)
@@ -37,13 +38,13 @@ from ..viz import (plot_ica_components, plot_ica_scores,
 from ..viz.utils import (_prepare_trellis, tight_layout, plt_show,
                          _setup_vmin_vmax)
 from ..viz.topomap import (_prepare_topo_plot, _check_outlines,
-                           plot_topomap)
+                           plot_topomap, _hide_frame)
 
 from ..channels.channels import _contains_ch_type, ContainsMixin
 from ..io.write import start_file, end_file, write_id
 from ..utils import (check_version, logger, check_fname, verbose,
                      _reject_data_segments, check_random_state,
-                     _get_fast_dot, compute_corr)
+                     _get_fast_dot, compute_corr, _check_copy_dep)
 from ..fixes import _get_args
 from ..filter import band_pass_filter
 from .bads import find_outliers
@@ -88,7 +89,6 @@ def get_score_funcs():
 
 
 class ICA(ContainsMixin):
-
     """M/EEG signal decomposition using Independent Component Analysis (ICA)
 
     This object can be used to estimate ICA components and then
@@ -104,12 +104,18 @@ class ICA(ContainsMixin):
         >> ica.fit(raw)
         >> raw.info['projs'] = projs
 
+    .. note:: Methods implemented are FastICA (default), Infomax and
+              Extended-Infomax. Infomax can be quite sensitive to differences
+              in floating point arithmetic due to exponential non-linearity.
+              Extended-Infomax seems to be more stable in this respect
+              enhancing reproducibility and stability of results.
+
     Parameters
     ----------
     n_components : int | float | None
         The number of components used for ICA decomposition. If int, it must be
         smaller then max_pca_components. If None, all PCA components will be
-        used. If float between 0 and 1 components can will be selected by the
+        used. If float between 0 and 1 components will be selected by the
         cumulative percentage of explained variance.
     max_pca_components : int | None
         The number of components used for PCA decomposition. If None, no
@@ -176,7 +182,7 @@ class ICA(ContainsMixin):
         .exclude attribute. When saving the ICA also the indices are restored.
         Hence, artifact components once identified don't have to be added
         again. To dump this 'artifact memory' say: ica.exclude = []
-    info : None | instance of mne.io.meas_info.Info
+    info : None | instance of Info
         The measurement info copied from the object fitted.
     `n_samples_` : int
         the number of samples used on fit.
@@ -220,6 +226,10 @@ class ICA(ContainsMixin):
 
         if fit_params is None:
             fit_params = {}
+        fit_params = deepcopy(fit_params)  # avoid side effects
+        if "extended" in fit_params:
+            raise ValueError("'extended' parameter provided. You should "
+                             "rather use method='extended-infomax'.")
         if method == 'fastica':
             update = {'algorithm': 'parallel', 'fun': 'logcosh',
                       'fun_args': None}
@@ -253,7 +263,7 @@ class ICA(ContainsMixin):
               hasattr(self, 'n_components_') else
               'no dimension reduction')
         if self.info is not None:
-            ch_fit = ['"%s"' % c for c in ['mag', 'grad', 'eeg'] if c in self]
+            ch_fit = ['"%s"' % c for c in _DATA_CH_TYPES_SPLIT if c in self]
             s += ', channels used: {0}'.format('; '.join(ch_fit))
         if self.exclude:
             s += ', %i sources marked for exclusion' % len(self.exclude)
@@ -288,21 +298,21 @@ class ICA(ContainsMixin):
             within ``start`` and ``stop`` are used.
         reject : dict | None
             Rejection parameters based on peak-to-peak amplitude.
-            Valid keys are 'grad' | 'mag' | 'eeg' | 'eog' | 'ecg'.
+            Valid keys are 'grad', 'mag', 'eeg', 'seeg', 'ecog', 'eog', 'ecg'.
             If reject is None then no rejection is done. Example::
 
                 reject = dict(grad=4000e-13, # T / m (gradiometers)
                               mag=4e-12, # T (magnetometers)
-                              eeg=40e-6, # uV (EEG channels)
-                              eog=250e-6 # uV (EOG channels)
+                              eeg=40e-6, # V (EEG channels)
+                              eog=250e-6 # V (EOG channels)
                               )
 
             It only applies if `inst` is of type Raw.
         flat : dict | None
             Rejection parameters based on flatness of signal.
-            Valid keys are 'grad' | 'mag' | 'eeg' | 'eog' | 'ecg', and values
-            are floats that set the minimum acceptable peak-to-peak amplitude.
-            If flat is None then no rejection is done.
+            Valid keys are 'grad', 'mag', 'eeg', 'seeg', 'ecog', 'eog', 'ecg'.
+            Values are floats that set the minimum acceptable peak-to-peak
+            amplitude. If flat is None then no rejection is done.
             It only applies if `inst` is of type Raw.
         tstep : float
             Length of data chunks for artifact rejection in seconds.
@@ -346,9 +356,9 @@ class ICA(ContainsMixin):
             self._reset()
 
         if picks is None:  # just use good data channels
-            picks = pick_types(raw.info, meg=True, eeg=True, eog=False,
-                               ecg=False, misc=False, stim=False,
-                               ref_meg=False, exclude='bads')
+            picks = _pick_data_channels(raw.info, exclude='bads',
+                                        with_ref_meg=False)
+
         logger.info('Fitting ICA to data using %i channels. \n'
                     'Please be patient, this may take some time' % len(picks))
 
@@ -362,19 +372,21 @@ class ICA(ContainsMixin):
         self.ch_names = self.info['ch_names']
         start, stop = _check_start_stop(raw, start, stop)
 
+        # this will be a copy
         data = raw[picks, start:stop][0]
+        # this will be a view
         if decim is not None:
-            data = data[:, ::decim].copy()
+            data = data[:, ::decim]
 
+        # this will make a copy
         if (reject is not None) or (flat is not None):
             data, self.drop_inds_ = _reject_data_segments(data, reject, flat,
                                                           decim, self.info,
                                                           tstep)
 
         self.n_samples_ = data.shape[1]
-
-        data, self._pre_whitener = self._pre_whiten(data,
-                                                    raw.info, picks)
+        # this may operate inplace or make a copy
+        data, self._pre_whitener = self._pre_whiten(data, raw.info, picks)
 
         self._fit(data, self.max_pca_components, 'raw')
 
@@ -387,9 +399,8 @@ class ICA(ContainsMixin):
             self._reset()
 
         if picks is None:
-            picks = pick_types(epochs.info, meg=True, eeg=True, eog=False,
-                               ecg=False, misc=False, stim=False,
-                               ref_meg=False, exclude='bads')
+            picks = _pick_data_channels(epochs.info, exclude='bads',
+                                        with_ref_meg=False)
         logger.info('Fitting ICA to data using %i channels. \n'
                     'Please be patient, this may take some time' % len(picks))
 
@@ -403,12 +414,16 @@ class ICA(ContainsMixin):
             self.max_pca_components = len(picks)
             logger.info('Inferring max_pca_components from picks.')
 
+        # this should be a copy (picks a list of int)
         data = epochs.get_data()[:, picks]
+        # this will be a view
         if decim is not None:
-            data = data[:, :, ::decim].copy()
+            data = data[:, :, ::decim]
 
         self.n_samples_ = np.prod(data[:, 0, :].shape)
 
+        # This will make at least one copy (one from hstack, maybe one
+        # more from _pre_whiten)
         data, self._pre_whitener = \
             self._pre_whiten(np.hstack(data), epochs.info, picks)
 
@@ -425,12 +440,16 @@ class ICA(ContainsMixin):
             # Scale (z-score) the data by channel type
             info = pick_info(info, picks)
             pre_whitener = np.empty([len(data), 1])
-            for ch_type in ['mag', 'grad', 'eeg']:
+            for ch_type in _DATA_CH_TYPES_SPLIT:
                 if _contains_ch_type(info, ch_type):
-                    if ch_type == 'eeg':
+                    if ch_type == 'seeg':
+                        this_picks = pick_types(info, meg=False, seeg=True)
+                    elif ch_type == 'ecog':
+                        this_picks = pick_types(info, meg=False, ecog=True)
+                    elif ch_type == 'eeg':
                         this_picks = pick_types(info, meg=False, eeg=True)
                     else:
-                        this_picks = pick_types(info, meg=ch_type, eeg=False)
+                        this_picks = pick_types(info, meg=ch_type)
                     pre_whitener[this_picks] = np.std(data[this_picks])
             data /= pre_whitener
         elif not has_pre_whitener and self.noise_cov is not None:
@@ -649,7 +668,7 @@ class ICA(ContainsMixin):
         # populate copied raw.
         start, stop = _check_start_stop(raw, start, stop)
         if add_channels is not None:
-            raw_picked = raw.pick_channels(add_channels, copy=True)
+            raw_picked = raw.copy().pick_channels(add_channels)
             data_, times_ = raw_picked[:, start:stop]
             data_ = np.r_[sources, data_]
         else:
@@ -713,7 +732,7 @@ class ICA(ContainsMixin):
         """Aux method
         """
         # set channel names and info
-        ch_names = info['ch_names'] = []
+        ch_names = []
         ch_info = info['chs'] = []
         for ii in range(self.n_components_):
             this_source = 'ICA %03d' % (ii + 1)
@@ -732,12 +751,10 @@ class ICA(ContainsMixin):
             # re-append additionally picked ch_info
             ch_info += [k for k in container.info['chs'] if k['ch_name'] in
                         add_channels]
-            # update number of channels
-        info['nchan'] = self.n_components_
-        if add_channels is not None:
-            info['nchan'] += len(add_channels)
         info['bads'] = [ch_names[k] for k in self.exclude]
         info['projs'] = []  # make sure projections are removed.
+        info._update_redundant()
+        info._check_consistency()
 
     @verbose
     def score_sources(self, inst, target=None, score_func='pearsonr',
@@ -918,7 +935,7 @@ class ICA(ContainsMixin):
             extra_picks = pick_types(inst.info, meg=False, ecg=True)
             ch_names_to_pick = (self.ch_names +
                                 [inst.ch_names[k] for k in extra_picks])
-            inst = inst.pick_channels(ch_names_to_pick, copy=True)
+            inst = inst.copy().pick_channels(ch_names_to_pick)
 
         if method == 'ctps':
             if threshold is None:
@@ -1014,7 +1031,7 @@ class ICA(ContainsMixin):
         targets = [self._check_target(k, inst, start, stop) for k in eog_chs]
 
         if inst.ch_names != self.ch_names:
-            inst = inst.pick_channels(self.ch_names, copy=True)
+            inst = inst.copy().pick_channels(self.ch_names)
 
         if not hasattr(self, 'labels_'):
             self.labels_ = dict()
@@ -1050,7 +1067,7 @@ class ICA(ContainsMixin):
 
     def apply(self, inst, include=None, exclude=None,
               n_pca_components=None, start=None, stop=None,
-              copy=False):
+              copy=None):
         """Remove selected components from the signal.
 
         Given the unmixing matrix, transform data,
@@ -1063,10 +1080,10 @@ class ICA(ContainsMixin):
         inst : instance of Raw, Epochs or Evoked
             The data to be processed.
         include : array_like of int.
-            The indices refering to columns in the ummixing matrix. The
+            The indices referring to columns in the ummixing matrix. The
             components to be kept.
         exclude : array_like of int.
-            The indices refering to columns in the ummixing matrix. The
+            The indices referring to columns in the ummixing matrix. The
             components to be zeroed out.
         n_pca_components : int | float | None
             The number of PCA components to be kept, either absolute (int)
@@ -1079,31 +1096,30 @@ class ICA(ContainsMixin):
             Last sample to not include. If float, data will be interpreted as
             time in seconds. If None, data will be used to the last sample.
         copy : bool
-            Whether to return a copy or whether to apply the solution in place.
-            Defaults to False.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
         """
+        inst = _check_copy_dep(inst, copy)
         if isinstance(inst, _BaseRaw):
             out = self._apply_raw(raw=inst, include=include,
                                   exclude=exclude,
                                   n_pca_components=n_pca_components,
-                                  start=start, stop=stop, copy=copy)
+                                  start=start, stop=stop)
         elif isinstance(inst, _BaseEpochs):
             out = self._apply_epochs(epochs=inst, include=include,
                                      exclude=exclude,
-                                     n_pca_components=n_pca_components,
-                                     copy=copy)
+                                     n_pca_components=n_pca_components)
         elif isinstance(inst, Evoked):
             out = self._apply_evoked(evoked=inst, include=include,
                                      exclude=exclude,
-                                     n_pca_components=n_pca_components,
-                                     copy=copy)
+                                     n_pca_components=n_pca_components)
         else:
             raise ValueError('Data input must be of Raw, Epochs or Evoked '
                              'type')
         return out
 
-    def _apply_raw(self, raw, include, exclude, n_pca_components, start, stop,
-                   copy=True):
+    def _apply_raw(self, raw, include, exclude, n_pca_components, start, stop):
         """Aux method"""
         if not raw.preload:
             raise ValueError('Raw data must be preloaded to apply ICA')
@@ -1126,14 +1142,10 @@ class ICA(ContainsMixin):
 
         data = self._pick_sources(data, include, exclude)
 
-        if copy is True:
-            raw = raw.copy()
-
         raw[picks, start:stop] = data
         return raw
 
-    def _apply_epochs(self, epochs, include, exclude,
-                      n_pca_components, copy):
+    def _apply_epochs(self, epochs, include, exclude, n_pca_components):
 
         if not epochs.preload:
             raise ValueError('Epochs must be preloaded to apply ICA')
@@ -1157,9 +1169,6 @@ class ICA(ContainsMixin):
         data, _ = self._pre_whiten(data, epochs.info, picks)
         data = self._pick_sources(data, include=include, exclude=exclude)
 
-        if copy is True:
-            epochs = epochs.copy()
-
         # restore epochs, channels, tsl order
         epochs._data[:, picks] = np.array(np.split(data,
                                           len(epochs.events), 1))
@@ -1167,8 +1176,7 @@ class ICA(ContainsMixin):
 
         return epochs
 
-    def _apply_evoked(self, evoked, include, exclude,
-                      n_pca_components, copy):
+    def _apply_evoked(self, evoked, include, exclude, n_pca_components):
 
         picks = pick_types(evoked.info, meg=False, ref_meg=False,
                            include=self.ch_names,
@@ -1190,9 +1198,6 @@ class ICA(ContainsMixin):
         data = self._pick_sources(data, include=include,
                                   exclude=exclude)
 
-        if copy is True:
-            evoked = evoked.copy()
-
         # restore evoked
         evoked.data[picks] = data
 
@@ -1219,28 +1224,30 @@ class ICA(ContainsMixin):
         if self.pca_mean_ is not None:
             data -= self.pca_mean_[:, None]
 
-        pca_data = fast_dot(self.pca_components_, data)
-        # Apply unmixing to low dimension PCA
-        sources = fast_dot(self.unmixing_matrix_, pca_data[:n_components])
-
+        sel_keep = np.arange(n_components)
         if include not in (None, []):
-            mask = np.ones(len(sources), dtype=np.bool)
-            mask[np.unique(include)] = False
-            sources[mask] = 0.
-            logger.info('Zeroing out %i ICA components' % mask.sum())
+            sel_keep = np.unique(include)
         elif exclude not in (None, []):
-            exclude_ = np.unique(exclude)
-            sources[exclude_] = 0.
-            logger.info('Zeroing out %i ICA components' % len(exclude_))
-        logger.info('Inverse transforming to PCA space')
-        pca_data[:n_components] = fast_dot(self.mixing_matrix_, sources)
-        data = fast_dot(self.pca_components_[:n_components].T,
-                        pca_data[:n_components])
-        logger.info('Reconstructing sensor space signals from %i PCA '
-                    'components' % max(_n_pca_comp, n_components))
+            sel_keep = np.setdiff1d(np.arange(n_components), exclude)
+
+        logger.info('Zeroing out %i ICA components'
+                    % (n_components - len(sel_keep)))
+
+        unmixing = np.eye(_n_pca_comp)
+        unmixing[:n_components, :n_components] = self.unmixing_matrix_
+        unmixing = np.dot(unmixing, self.pca_components_[:_n_pca_comp])
+
+        mixing = np.eye(_n_pca_comp)
+        mixing[:n_components, :n_components] = self.mixing_matrix_
+        mixing = np.dot(self.pca_components_[:_n_pca_comp].T, mixing)
+
         if _n_pca_comp > n_components:
-            data += fast_dot(self.pca_components_[n_components:_n_pca_comp].T,
-                             pca_data[n_components:_n_pca_comp])
+            sel_keep = np.concatenate(
+                (sel_keep, range(n_components, _n_pca_comp)))
+
+        proj_mat = np.dot(mixing[:, sel_keep], unmixing[sel_keep, :])
+
+        data = fast_dot(proj_mat, data)
 
         if self.pca_mean_ is not None:
             data += self.pca_mean_[:, None]
@@ -1304,7 +1311,8 @@ class ICA(ContainsMixin):
         ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
             The channel type to plot. For 'grad', the gradiometers are
             collected in pairs and the RMS for each pair is plotted.
-            If None, then channels are chosen in the order given above.
+            If None, then first available channel type from order given
+            above is used. Defaults to None.
         res : int
             The resolution of the topomap image (n pixels along each side).
         layout : None | Layout
@@ -1444,7 +1452,7 @@ class ICA(ContainsMixin):
         labels : str | list | 'ecg' | 'eog' | None
             The labels to consider for the axes tests. Defaults to None.
             If list, should match the outer shape of `scores`.
-            If 'ecg' or 'eog', the labels_ attributes will be looked up.
+            If 'ecg' or 'eog', the ``labels_`` attributes will be looked up.
             Note that '/' is used internally for sublabels specifying ECG and
             EOG channels.
         axhline : float
@@ -2171,7 +2179,7 @@ def run_ica(raw, n_components, max_pca_components=100,
                       ecg_score_func=ecg_score_func,
                       ecg_criterion=ecg_criterion, eog_ch=eog_ch,
                       eog_score_func=eog_score_func,
-                      eog_criterion=ecg_criterion,
+                      eog_criterion=eog_criterion,
                       skew_criterion=skew_criterion,
                       kurt_criterion=kurt_criterion,
                       var_criterion=var_criterion,
@@ -2250,11 +2258,14 @@ def _find_max_corrs(all_maps, target, threshold):
 
 
 def _plot_corrmap(data, subjs, indices, ch_type, ica, label, show, outlines,
-                  layout, cmap, contours):
+                  layout, cmap, contours, template=True):
     """Customized ica.plot_components for corrmap"""
-    title = 'Detected components'
-    if label is not None:
-        title += ' of type ' + label
+    if not template:
+        title = 'Detected components'
+        if label is not None:
+            title += ' of type ' + label
+    else:
+        title = "Supplied template"
 
     picks = list(range(len(data)))
 
@@ -2284,17 +2295,16 @@ def _plot_corrmap(data, subjs, indices, ch_type, ica, label, show, outlines,
     if merge_grads:
         from ..channels.layout import _merge_grad_data
     for ii, data_, ax, subject, idx in zip(picks, data, axes, subjs, indices):
-        ttl = 'Subj. {0}, IC {1}'.format(subject, idx)
-        ax.set_title(ttl, fontsize=12)
+        if template:
+            ttl = 'Subj. {0}, IC {1}'.format(subject, idx)
+            ax.set_title(ttl, fontsize=12)
         data_ = _merge_grad_data(data_) if merge_grads else data_
         vmin_, vmax_ = _setup_vmin_vmax(data_, None, None)
         plot_topomap(data_.flatten(), pos, vmin=vmin_, vmax=vmax_,
-                     res=64, axis=ax, cmap=cmap, outlines=outlines,
+                     res=64, axes=ax, cmap=cmap, outlines=outlines,
                      image_mask=None, contours=contours, show=False,
                      image_interp='bilinear')[0]
-        ax.set_yticks([])
-        ax.set_xticks([])
-        ax.set_frame_on(False)
+        _hide_frame(ax)
     tight_layout(fig=fig)
     fig.subplots_adjust(top=0.8)
     fig.canvas.draw()
@@ -2303,9 +2313,9 @@ def _plot_corrmap(data, subjs, indices, ch_type, ica, label, show, outlines,
 
 
 @verbose
-def corrmap(icas, template, threshold="auto", label=None,
-            ch_type="eeg", plot=True, show=True, verbose=None, outlines='head',
-            layout=None, sensors=True, contours=6, cmap='RdBu_r'):
+def corrmap(icas, template, threshold="auto", label=None, ch_type="eeg",
+            plot=True, show=True, verbose=None, outlines='head', layout=None,
+            sensors=True, contours=6, cmap=None):
     """Find similar Independent Components across subjects by map similarity.
 
     Corrmap (Viola et al. 2009 Clin Neurophysiol) identifies the best group
@@ -2332,10 +2342,13 @@ def corrmap(icas, template, threshold="auto", label=None,
     ----------
     icas : list of mne.preprocessing.ICA
         A list of fitted ICA objects.
-    template : tuple
-        A tuple with two elements (int, int) representing the list indices of
-        the set from which the template should be chosen, and the template.
-        E.g., if template=(1, 0), the first IC of the 2nd ICA object is used.
+    template : tuple | np.ndarray, shape (n_components,)
+        Either a tuple with two elements (int, int) representing the list
+        indices of the set from which the template should be chosen, and the
+        template. E.g., if template=(1, 0), the first IC of the 2nd ICA object
+        is used.
+        Or a numpy array whose size corresponds to each IC map from the
+        supplied maps, in which case this map is chosen as the template.
     threshold : "auto" | list of float | float
         Correlation threshold for identifying ICs
         If "auto", search for the best map by trying all correlations between
@@ -2349,8 +2362,9 @@ def corrmap(icas, template, threshold="auto", label=None,
         Defaults to "auto".
     label : None | str
         If not None, categorised ICs are stored in a dictionary "labels_" under
-        the given name. Preexisting entries will be appended to
-        (excluding repeats), not overwritten. If None, a dry run is performed.
+        the given name. Preexisting entries will be appended to (excluding
+        repeats), not overwritten. If None, a dry run is performed and
+        the supplied ICs are not changed.
     ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg'
             The channel type to plot. Defaults to 'eeg'.
     plot : bool
@@ -2362,8 +2376,9 @@ def corrmap(icas, template, threshold="auto", label=None,
         Layout instance specifying sensor positions (does not need to be
         specified for Neuromag data). Or a list of Layout if projections
         are from different sensor types.
-    cmap : matplotlib colormap
-        Colormap.
+    cmap : None | matplotlib colormap
+        Colormap for the plot. If ``None``, defaults to 'Reds_r' for norm data,
+        otherwise to 'RdBu_r'.
     sensors : bool | str
         Add markers for sensor locations to the plot. Accepts matplotlib plot
         format string (e.g., 'r+' for red plusses). If True, a circle will be
@@ -2385,7 +2400,7 @@ def corrmap(icas, template, threshold="auto", label=None,
     Returns
     -------
     template_fig : fig
-        Figure showing the mean template.
+        Figure showing the template.
     labelled_ics : fig
         Figure showing the labelled ICs in all ICA decompositions.
     """
@@ -2397,20 +2412,37 @@ def corrmap(icas, template, threshold="auto", label=None,
 
     all_maps = [_get_ica_map(ica) for ica in icas]
 
-    target = all_maps[template[0]][template[1]]
+    # check if template is an index to one IC in one ICA object, or an array
+    if len(template) == 2:
+        target = all_maps[template[0]][template[1]]
+        is_subject = True
+    elif template.ndim == 1 and len(template) == all_maps[0].shape[1]:
+        target = template
+        is_subject = False
+    else:
+        raise ValueError("`template` must be a length-2 tuple or an array the "
+                         "size of the ICA maps.")
 
     template_fig, labelled_ics = None, None
     if plot is True:
-        ttl = 'Template from subj. {0}'.format(str(template[0]))
-        template_fig = icas[template[0]].plot_components(
-            picks=template[1], ch_type=ch_type, title=ttl, outlines=outlines,
-            cmap=cmap, contours=contours, layout=layout, show=show)
+        if is_subject:  # plotting from an ICA object
+            ttl = 'Template from subj. {0}'.format(str(template[0]))
+            template_fig = icas[template[0]].plot_components(
+                picks=template[1], ch_type=ch_type, title=ttl,
+                outlines=outlines, cmap=cmap, contours=contours, layout=layout,
+                show=show)
+        else:  # plotting an array
+            template_fig = _plot_corrmap([template], [0], [0], ch_type,
+                                         icas[0].copy(), "Template",
+                                         outlines=outlines, cmap=cmap,
+                                         contours=contours, layout=layout,
+                                         show=show, template=True)
         template_fig.subplots_adjust(top=0.8)
         template_fig.canvas.draw()
 
     # first run: use user-selected map
     if isinstance(threshold, (int, float)):
-        if len(all_maps) == 0 or len(target) == 0:
+        if len(all_maps) == 0:
             logger.info('No component detected using find_outliers.'
                         ' Consider using threshold="auto"')
             return icas
diff --git a/mne/preprocessing/infomax_.py b/mne/preprocessing/infomax_.py
index 053efde..7deb657 100644
--- a/mne/preprocessing/infomax_.py
+++ b/mne/preprocessing/infomax_.py
@@ -71,17 +71,17 @@ def infomax(data, weights=None, l_rate=None, block=None, w_change=1e-12,
         If random_state is already a np.random.RandomState instance, use
         random_state as random number generator.
     blowup : float
-        The maximum difference allowed between two succesive estimations of the
-        unmixing matrix. Defaults to 1e4
+        The maximum difference allowed between two successive estimations of
+        the unmixing matrix. Defaults to 1e4
     blowup_fac : float
         The factor by which the learning rate will be reduced if the
-        difference between two succesive estimations of the
+        difference between two successive estimations of the
         unmixing matrix exceededs ``blowup``:
             l_rate *= blowup_fac
         Defaults to 0.5
     n_small_angle : int | None
         The maximum number of allowed steps in which the angle between two
-        succesive estimations of the unmixing matrix is less than
+        successive estimations of the unmixing matrix is less than
         ``anneal_deg``.
         If None, this parameter is not taken into account to stop the
         iterations.
diff --git a/mne/preprocessing/maxfilter.py b/mne/preprocessing/maxfilter.py
index 2d76a44..c4d955b 100644
--- a/mne/preprocessing/maxfilter.py
+++ b/mne/preprocessing/maxfilter.py
@@ -6,12 +6,11 @@
 
 from ..externals.six import string_types
 import os
-from warnings import warn
 
 
 from ..bem import fit_sphere_to_headshape
 from ..io import Raw
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from ..externals.six.moves import map
 
 
@@ -138,7 +137,7 @@ def apply_maxfilter(in_fname, out_fname, origin=None, frame='device',
     if origin is None:
         logger.info('Estimating head origin from headshape points..')
         raw = Raw(in_fname)
-        r, o_head, o_dev = fit_sphere_to_headshape(raw.info)
+        r, o_head, o_dev = fit_sphere_to_headshape(raw.info, units='mm')
         raw.close()
         logger.info('[done]')
         if frame == 'head':
diff --git a/mne/preprocessing/maxwell.py b/mne/preprocessing/maxwell.py
index 8d7df5e..c2c72b4 100644
--- a/mne/preprocessing/maxwell.py
+++ b/mne/preprocessing/maxwell.py
@@ -6,25 +6,26 @@
 
 # License: BSD (3-clause)
 
-from copy import deepcopy
-import numpy as np
-from scipy import linalg
 from math import factorial
 from os import path as op
 
+import numpy as np
+from scipy import linalg
+
 from .. import __version__
 from ..bem import _check_origin
-from ..transforms import _str_to_frame, _get_trans
-from ..forward._compute_forward import _concatenate_coils
-from ..forward._make_forward import _prep_meg_channels
+from ..chpi import quat_to_rot, rot_to_quat
+from ..transforms import (_str_to_frame, _get_trans, Transform, apply_trans,
+                          _find_vector_rotation)
+from ..forward import _concatenate_coils, _prep_meg_channels
 from ..surface import _normalize_vectors
 from ..io.constants import FIFF
 from ..io.proc_history import _read_ctc
 from ..io.write import _generate_meas_id, _date_now
 from ..io import _loc_to_coil_trans, _BaseRaw
 from ..io.pick import pick_types, pick_info, pick_channels
-from ..utils import verbose, logger, _clean_names
-from ..fixes import _get_args
+from ..utils import verbose, logger, _clean_names, warn, _time_mask
+from ..fixes import _get_args, partial
 from ..externals.six import string_types
 from ..channels.channels import _get_T1T2_mag_inds
 
@@ -39,7 +40,7 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
                    calibration=None, cross_talk=None, st_duration=None,
                    st_correlation=0.98, coord_frame='head', destination=None,
                    regularize='in', ignore_ref=False, bad_condition='error',
-                   verbose=None):
+                   head_pos=None, st_fixed=True, st_only=False, verbose=None):
     """Apply Maxwell filter to data using multipole moments
 
     .. warning:: Automatic bad channel detection is not currently implemented.
@@ -57,9 +58,9 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
         Data to be filtered
     origin : array-like, shape (3,) | str
         Origin of internal and external multipolar moment space in meters.
-        The default is ``'auto'``, which means ``(0., 0., 0.)`` for
-        ``coord_frame='meg'``, and a head-digitization-based origin fit
-        for ``coord_frame='head'``.
+        The default is ``'auto'``, which means a head-digitization-based
+        origin fit when ``coord_frame='head'``, and ``(0., 0., 0.)`` when
+        ``coord_frame='meg'``.
     int_order : int
         Order of internal component of spherical expansion.
     ext_order : int
@@ -107,6 +108,34 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
     bad_condition : str
         How to deal with ill-conditioned SSS matrices. Can be "error"
         (default), "warning", or "ignore".
+    head_pos : array | None
+        If array, movement compensation will be performed.
+        The array should be of shape (N, 10), holding the position
+        parameters as returned by e.g. `read_head_pos`.
+
+        .. versionadded:: 0.12
+
+    st_fixed : bool
+        If True (default), do tSSS using the median head position during the
+        ``st_duration`` window. This is the default behavior of MaxFilter
+        and has been most extensively tested.
+
+        .. versionadded:: 0.12
+
+    st_only : bool
+        If True, only tSSS (temporal) projection of MEG data will be
+        performed on the output data. The non-tSSS parameters (e.g.,
+        ``int_order``, ``calibration``, ``head_pos``, etc.) will still be
+        used to form the SSS bases used to calculate temporal projectors,
+        but the ouptut MEG data will *only* have temporal projections
+        performed. Noise reduction from SSS basis multiplication,
+        cross-talk cancellation, movement compensation, and so forth
+        will not be applied to the data. This is useful, for example, when
+        evoked movement compensation will be performed with
+        :func:`mne.epochs.average_movements`.
+
+        .. versionadded:: 0.12
+
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose)
 
@@ -118,6 +147,7 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
     See Also
     --------
     mne.epochs.average_movements
+    mne.chpi.read_head_pos
 
     Notes
     -----
@@ -136,18 +166,20 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
         * tSSS
         * Coordinate frame translation
         * Regularization of internal components using information theory
+        * Raw movement compensation
+          (using head positions estimated by MaxFilter)
+        * cHPI subtraction (see :func:`mne.chpi.filter_chpi`)
 
     The following features are not yet implemented:
 
         * **Not certified for clinical use**
-        * Raw movement compensation
         * Automatic bad channel detection
-        * cHPI subtraction
+        * Head position estimation
 
     Our algorithm has the following enhancements:
 
         * Double floating point precision
-        * Handling of 3D (in additon to 1D) fine calibration files
+        * Handling of 3D (in addition to 1D) fine calibration files
         * Automated processing of split (-1.fif) and concatenated files
         * Epoch-based movement compensation as described in [1]_ through
           :func:`mne.epochs.average_movements`
@@ -200,8 +232,8 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
     # Our code follows the same standard that ``scipy`` uses for ``sph_harm``.
 
     # triage inputs ASAP to avoid late-thrown errors
-
-    _check_raw(raw)
+    if not isinstance(raw, _BaseRaw):
+        raise TypeError('raw must be Raw, not %s' % type(raw))
     _check_usable(raw)
     _check_regularize(regularize)
     st_correlation = float(st_correlation)
@@ -212,21 +244,7 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
         raise ValueError('coord_frame must be either "head" or "meg", not "%s"'
                          % coord_frame)
     head_frame = True if coord_frame == 'head' else False
-    if destination is not None:
-        if not head_frame:
-            raise RuntimeError('destination can only be set if using the '
-                               'head coordinate frame')
-        if isinstance(destination, string_types):
-            recon_trans = _get_trans(destination, 'meg', 'head')[0]['trans']
-        else:
-            destination = np.array(destination, float)
-            if destination.shape != (3,):
-                raise ValueError('destination must be a 3-element vector, '
-                                 'str, or None')
-            recon_trans = np.eye(4)
-            recon_trans[:3, 3] = destination
-    else:
-        recon_trans = None
+    recon_trans = _check_destination(destination, raw.info, head_frame)
     if st_duration is not None:
         st_duration = float(st_duration)
         if not 0. < st_duration <= raw.times[-1]:
@@ -240,12 +258,27 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
             bad_condition not in ['error', 'warning', 'ignore']:
         raise ValueError('bad_condition must be "error", "warning", or '
                          '"ignore", not %s' % bad_condition)
+    if raw.info['dev_head_t'] is None and coord_frame == 'head':
+        raise RuntimeError('coord_frame cannot be "head" because '
+                           'info["dev_head_t"] is None; if this is an '
+                           'empty room recording, consider using '
+                           'coord_frame="meg"')
+    if st_only and st_duration is None:
+        raise ValueError('st_duration must not be None if st_only is True')
+    head_pos = _check_pos(head_pos, head_frame, raw, st_fixed,
+                          raw.info['sfreq'])
+    _check_info(raw.info, sss=not st_only, tsss=st_duration is not None,
+                calibration=not st_only and calibration is not None,
+                ctc=not st_only and cross_talk is not None)
 
     # Now we can actually get moving
 
     logger.info('Maxwell filtering raw data')
-    raw_sss = raw.copy().load_data(verbose=False)
+    add_channels = (head_pos[0] is not None) and not st_only
+    raw_sss, pos_picks = _copy_preload_add_channels(
+        raw, add_channels=add_channels)
     del raw
+    _remove_meg_projs(raw_sss)  # remove MEG projectors, they won't apply now
     info, times = raw_sss.info, raw_sss.times
     meg_picks, mag_picks, grad_picks, good_picks, coil_scale, mag_or_fine = \
         _get_mf_picks(info, int_order, ext_order, ignore_ref, mag_scale=100.)
@@ -253,21 +286,15 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
     #
     # Fine calibration processing (load fine cal and overwrite sensor geometry)
     #
+    sss_cal = dict()
     if calibration is not None:
-        grad_imbalances, mag_cals, sss_cal = \
-            _update_sensor_geometry(info, calibration)
-    else:
-        sss_cal = dict()
-
-    # Get indices of MEG channels
-    if info['dev_head_t'] is None and coord_frame == 'head':
-        raise RuntimeError('coord_frame cannot be "head" because '
-                           'info["dev_head_t"] is None; if this is an '
-                           'empty room recording, consider using '
-                           'coord_frame="meg"')
+        calibration, sss_cal = _update_sensor_geometry(info, calibration,
+                                                       head_frame, ignore_ref)
+        mag_or_fine.fill(True)  # all channels now have some mag-type data
 
     # Determine/check the origin of the expansion
     origin = _check_origin(origin, raw_sss.info, coord_frame, disp=True)
+    origin.setflags(write=False)
     n_in, n_out = _get_n_moments([int_order, ext_order])
 
     #
@@ -276,8 +303,14 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
     if cross_talk is not None:
         sss_ctc = _read_ctc(cross_talk)
         ctc_chs = sss_ctc['proj_items_chs']
-        if set(info['ch_names'][p] for p in meg_picks) != set(ctc_chs):
-            raise RuntimeError('ctc channels and raw channels do not match')
+        meg_ch_names = [info['ch_names'][p] for p in meg_picks]
+        missing = sorted(list(set(meg_ch_names) - set(ctc_chs)))
+        if len(missing) != 0:
+            raise RuntimeError('Missing MEG channels in cross-talk matrix:\n%s'
+                               % missing)
+        missing = sorted(list(set(ctc_chs) - set(meg_ch_names)))
+        if len(missing) > 0:
+            warn('Not all cross-talk channels in raw:\n%s' % missing)
         ctc_picks = pick_channels(ctc_chs,
                                   [info['ch_names'][c] for c in good_picks])
         ctc = sss_ctc['decoupler'][ctc_picks][:, ctc_picks]
@@ -287,71 +320,23 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
         sss_ctc = dict()
 
     #
-    # Fine calibration processing (point-like magnetometers and calib. coeffs)
-    #
-    S_decomp = _info_sss_basis(info, None, origin, int_order, ext_order,
-                               head_frame, ignore_ref, coil_scale)
-    if calibration is not None:
-        # Compute point-like mags to incorporate gradiometer imbalance
-        grad_info = pick_info(info, grad_picks)
-        S_fine = _sss_basis_point(origin, grad_info, int_order, ext_order,
-                                  grad_imbalances, ignore_ref, head_frame)
-        # Add point like magnetometer data to bases.
-        S_decomp[grad_picks, :] += S_fine
-        # Scale magnetometers by calibration coefficient
-        S_decomp[mag_picks, :] /= mag_cals
-        mag_or_fine.fill(True)
-        # We need to be careful about KIT gradiometers
-    S_decomp = S_decomp[good_picks]
-
-    #
     # Translate to destination frame (always use non-fine-cal bases)
     #
-    S_recon = _info_sss_basis(info, recon_trans, origin, int_order, 0,
-                              head_frame, ignore_ref, coil_scale)
+    exp = dict(origin=origin, int_order=int_order, ext_order=0,
+               head_frame=head_frame)
+    all_coils = _prep_mf_coils(info, ignore_ref)
+    S_recon = _trans_sss_basis(exp, all_coils, recon_trans, coil_scale)
+    exp['ext_order'] = ext_order
+    # Reconstruct data from internal space only (Eq. 38), and rescale S_recon
+    S_recon /= coil_scale
     if recon_trans is not None:
         # warn if we have translated too far
         diff = 1000 * (info['dev_head_t']['trans'][:3, 3] -
-                       recon_trans[:3, 3])
+                       recon_trans['trans'][:3, 3])
         dist = np.sqrt(np.sum(_sq(diff)))
         if dist > 25.:
-            logger.warning('Head position change is over 25 mm (%s) = %0.1f mm'
-                           % (', '.join('%0.1f' % x for x in diff), dist))
-
-    #
-    # Regularization
-    #
-    reg_moments, n_use_in = _regularize(regularize, int_order, ext_order,
-                                        S_decomp, mag_or_fine)
-    if n_use_in != n_in:
-        S_decomp = S_decomp.take(reg_moments, axis=1)
-        S_recon = S_recon.take(reg_moments[:n_use_in], axis=1)
-
-    #
-    # Do the heavy lifting
-    #
-
-    # Pseudo-inverse of total multipolar moment basis set (Part of Eq. 37)
-    pS_decomp_good, sing = _col_norm_pinv(S_decomp.copy())
-    cond = sing[0] / sing[-1]
-    logger.debug('    Decomposition matrix condition: %0.1f' % cond)
-    if bad_condition != 'ignore' and cond >= 1000.:
-        msg = 'Matrix is badly conditioned: %0.0f >= 1000' % cond
-        if bad_condition == 'error':
-            raise RuntimeError(msg)
-        else:  # condition == 'warning':
-            logger.warning(msg)
-
-    # Build in our data scaling here
-    pS_decomp_good *= coil_scale[good_picks].T
-
-    # Split into inside and outside versions
-    pS_decomp_in = pS_decomp_good[:n_use_in]
-    pS_decomp_out = pS_decomp_good[n_use_in:]
-    del pS_decomp_good
-
-    # Reconstruct data from internal space only (Eq. 38), first rescale S_recon
-    S_recon /= coil_scale
+            warn('Head position change is over 25 mm (%s) = %0.1f mm'
+                 % (', '.join('%0.1f' % x for x in diff), dist))
 
     # Reconstruct raw file object with spatiotemporal processed data
     max_st = dict()
@@ -359,70 +344,411 @@ def maxwell_filter(raw, origin='auto', int_order=8, ext_order=3,
         max_st.update(job=10, subspcorr=st_correlation, buflen=st_duration)
         logger.info('    Processing data using tSSS with st_duration=%s'
                     % st_duration)
+        st_when = 'before' if st_fixed else 'after'  # relative to movecomp
     else:
         st_duration = min(raw_sss.times[-1], 10.)  # chunk size
         st_correlation = None
+        st_when = 'never'
+    del st_fixed
 
-    # Generate time points to break up data in to windows
-    lims = raw_sss.time_as_index(np.arange(times[0], times[-1],
-                                           st_duration))
-    len_last_buf = raw_sss.times[-1] - raw_sss.index_as_time(lims[-1])[0]
+    # Generate time points to break up data into windows
+    chunk_times = np.arange(times[0], times[-1], st_duration)
+    read_lims = raw_sss.time_as_index(chunk_times)
+    len_last_buf = raw_sss.times[-1] - raw_sss.times[read_lims[-1]]
     if len_last_buf == st_duration:
-        lims = np.concatenate([lims, [len(raw_sss.times)]])
+        read_lims = np.concatenate([read_lims, [len(raw_sss.times)]])
     else:
         # len_last_buf < st_dur so fold it into the previous buffer
-        lims[-1] = len(raw_sss.times)
+        read_lims[-1] = len(raw_sss.times)
         if st_correlation is not None:
             logger.info('    Spatiotemporal window did not fit evenly into '
                         'raw object. The final %0.2f seconds were lumped '
                         'onto the previous window.' % len_last_buf)
 
-    S_decomp /= coil_scale[good_picks]
-    logger.info('    Processing data in chunks of %0.1f sec' % st_duration)
+    #
+    # Do the heavy lifting
+    #
+
+    # Figure out which transforms we need for each tSSS block
+    # (and transform pos[1] to times)
+    head_pos[1] = raw_sss.time_as_index(head_pos[1], use_rounding=True)
+    # Compute the first bit of pos_data for cHPI reporting
+    if raw_sss.info['dev_head_t'] is not None and head_pos[0] is not None:
+        this_pos_quat = np.concatenate([
+            rot_to_quat(info['dev_head_t']['trans'][:3, :3]),
+            info['dev_head_t']['trans'][:3, 3],
+            np.zeros(3)])
+    else:
+        this_pos_quat = None
+    _get_this_decomp_trans = partial(
+        _get_decomp, all_coils=all_coils,
+        cal=calibration, regularize=regularize,
+        exp=exp, ignore_ref=ignore_ref, coil_scale=coil_scale,
+        grad_picks=grad_picks, mag_picks=mag_picks, good_picks=good_picks,
+        mag_or_fine=mag_or_fine, bad_condition=bad_condition)
+    S_decomp, pS_decomp, reg_moments, n_use_in = _get_this_decomp_trans(
+        info['dev_head_t'])
+    reg_moments_0 = reg_moments.copy()
     # Loop through buffer windows of data
-    for start, stop in zip(lims[:-1], lims[1:]):
-        # Compute multipolar moments of (magnetometer scaled) data (Eq. 37)
+    pl = 's' if len(read_lims) != 2 else ''
+    logger.info('    Processing %s data chunk%s of (at least) %0.1f sec'
+                % (len(read_lims) - 1, pl, st_duration))
+    for start, stop in zip(read_lims[:-1], read_lims[1:]):
+        t_str = '% 8.2f - % 8.2f sec' % tuple(raw_sss.times[[start, stop - 1]])
+
+        # Get original data
         orig_data = raw_sss._data[good_picks, start:stop]
+        # This could just be np.empty if not st_only, but shouldn't be slow
+        # this way so might as well just always take the original data
+        out_meg_data = raw_sss._data[meg_picks, start:stop]
+        # Apply cross-talk correction
         if cross_talk is not None:
             orig_data = ctc.dot(orig_data)
-        mm_in = np.dot(pS_decomp_in, orig_data)
-        in_data = np.dot(S_recon, mm_in)
+        out_pos_data = np.empty((len(pos_picks), stop - start))
 
+        # Figure out which positions to use
+        t_s_s_q_a = _trans_starts_stops_quats(head_pos, start, stop,
+                                              this_pos_quat)
+        n_positions = len(t_s_s_q_a[0])
+
+        # Set up post-tSSS or do pre-tSSS
         if st_correlation is not None:
-            # Reconstruct data using original location from external
-            # and internal spaces and compute residual
-            mm_out = np.dot(pS_decomp_out, orig_data)
-            resid = orig_data  # we will operate inplace but it's safe
-            orig_in_data = np.dot(S_decomp[:, :n_use_in], mm_in)
-            orig_out_data = np.dot(S_decomp[:, n_use_in:], mm_out)
-            resid -= orig_in_data
-            resid -= orig_out_data
-            _check_finite(resid)
-
-            # Compute SSP-like projection vectors based on minimal correlation
-            _check_finite(orig_in_data)
-            t_proj = _overlap_projector(orig_in_data, resid, st_correlation)
-
-            # Apply projector according to Eq. 12 in [2]_
-            logger.info('        Projecting %s intersecting tSSS components '
-                        'for %0.3f-%0.3f sec'
-                        % (t_proj.shape[1], start / raw_sss.info['sfreq'],
-                           stop / raw_sss.info['sfreq']))
-            in_data -= np.dot(np.dot(in_data, t_proj), t_proj.T)
-        raw_sss._data[meg_picks, start:stop] = in_data
+            # If doing tSSS before movecomp...
+            resid = orig_data.copy()  # to be safe let's operate on a copy
+            if st_when == 'after':
+                orig_in_data = np.empty((len(meg_picks), stop - start))
+            else:  # 'before'
+                avg_trans = t_s_s_q_a[-1]
+                if avg_trans is not None:
+                    # if doing movecomp
+                    S_decomp_st, pS_decomp_st, _, n_use_in_st = \
+                        _get_this_decomp_trans(avg_trans, verbose=False)
+                else:
+                    S_decomp_st, pS_decomp_st = S_decomp, pS_decomp
+                    n_use_in_st = n_use_in
+                orig_in_data = np.dot(np.dot(S_decomp_st[:, :n_use_in_st],
+                                             pS_decomp_st[:n_use_in_st]),
+                                      resid)
+                resid -= np.dot(np.dot(S_decomp_st[:, n_use_in_st:],
+                                       pS_decomp_st[n_use_in_st:]), resid)
+                resid -= orig_in_data
+                # Here we operate on our actual data
+                proc = out_meg_data if st_only else orig_data
+                _do_tSSS(proc, orig_in_data, resid, st_correlation,
+                         n_positions, t_str)
+
+        if not st_only or st_when == 'after':
+            # Do movement compensation on the data
+            for trans, rel_start, rel_stop, this_pos_quat in \
+                    zip(*t_s_s_q_a[:4]):
+                # Recalculate bases if necessary (trans will be None iff the
+                # first position in this interval is the same as last of the
+                # previous interval)
+                if trans is not None:
+                    S_decomp, pS_decomp, reg_moments, n_use_in = \
+                        _get_this_decomp_trans(trans, verbose=False)
+
+                # Determine multipole moments for this interval
+                mm_in = np.dot(pS_decomp[:n_use_in],
+                               orig_data[:, rel_start:rel_stop])
+
+                # Our output data
+                if not st_only:
+                    out_meg_data[:, rel_start:rel_stop] = \
+                        np.dot(S_recon.take(reg_moments[:n_use_in], axis=1),
+                               mm_in)
+                if len(pos_picks) > 0:
+                    out_pos_data[:, rel_start:rel_stop] = \
+                        this_pos_quat[:, np.newaxis]
+
+                # Transform orig_data to store just the residual
+                if st_when == 'after':
+                    # Reconstruct data using original location from external
+                    # and internal spaces and compute residual
+                    rel_resid_data = resid[:, rel_start:rel_stop]
+                    orig_in_data[:, rel_start:rel_stop] = \
+                        np.dot(S_decomp[:, :n_use_in], mm_in)
+                    rel_resid_data -= np.dot(np.dot(S_decomp[:, n_use_in:],
+                                                    pS_decomp[n_use_in:]),
+                                             rel_resid_data)
+                    rel_resid_data -= orig_in_data[:, rel_start:rel_stop]
+
+        # If doing tSSS at the end
+        if st_when == 'after':
+            _do_tSSS(out_meg_data, orig_in_data, resid, st_correlation,
+                     n_positions, t_str)
+        else:
+            pl = 's' if n_positions > 1 else ''
+            logger.info('        Used % 2d head position%s for %s'
+                        % (n_positions, pl, t_str))
+        raw_sss._data[meg_picks, start:stop] = out_meg_data
+        raw_sss._data[pos_picks, start:stop] = out_pos_data
 
     # Update info
+    info['dev_head_t'] = recon_trans  # set the reconstruction transform
     _update_sss_info(raw_sss, origin, int_order, ext_order, len(good_picks),
-                     coord_frame, sss_ctc, sss_cal, max_st, reg_moments)
+                     coord_frame, sss_ctc, sss_cal, max_st, reg_moments_0,
+                     st_only)
     logger.info('[done]')
     return raw_sss
 
 
-def _regularize(regularize, int_order, ext_order, S_decomp, mag_or_fine):
+def _remove_meg_projs(inst):
+    """Helper to remove inplace existing MEG projectors (assumes inactive)"""
+    meg_picks = pick_types(inst.info, meg=True, exclude=[])
+    meg_channels = [inst.ch_names[pi] for pi in meg_picks]
+    non_meg_proj = list()
+    for proj in inst.info['projs']:
+        if not any(c in meg_channels for c in proj['data']['col_names']):
+            non_meg_proj.append(proj)
+    inst.add_proj(non_meg_proj, remove_existing=True, verbose=False)
+
+
+def _check_destination(destination, info, head_frame):
+    """Helper to triage our reconstruction trans"""
+    if destination is None:
+        return info['dev_head_t']
+    if not head_frame:
+        raise RuntimeError('destination can only be set if using the '
+                           'head coordinate frame')
+    if isinstance(destination, string_types):
+        recon_trans = _get_trans(destination, 'meg', 'head')[0]
+    elif isinstance(destination, Transform):
+        recon_trans = destination
+    else:
+        destination = np.array(destination, float)
+        if destination.shape != (3,):
+            raise ValueError('destination must be a 3-element vector, '
+                             'str, or None')
+        recon_trans = np.eye(4)
+        recon_trans[:3, 3] = destination
+        recon_trans = Transform('meg', 'head', recon_trans)
+    if recon_trans.to_str != 'head' or recon_trans.from_str != 'MEG device':
+        raise RuntimeError('Destination transform is not MEG device -> head, '
+                           'got %s -> %s' % (recon_trans.from_str,
+                                             recon_trans.to_str))
+    return recon_trans
+
+
+def _prep_mf_coils(info, ignore_ref=True):
+    """Helper to get all coil integration information loaded and sorted"""
+    coils, comp_coils = _prep_meg_channels(
+        info, accurate=True, elekta_defs=True, head_frame=False,
+        ignore_ref=ignore_ref, verbose=False)[:2]
+    mag_mask = _get_mag_mask(coils)
+    if len(comp_coils) > 0:
+        meg_picks = pick_types(info, meg=True, ref_meg=False, exclude=[])
+        ref_picks = pick_types(info, meg=False, ref_meg=True, exclude=[])
+        inserts = np.searchsorted(meg_picks, ref_picks)
+        # len(inserts) == len(comp_coils)
+        for idx, comp_coil in zip(inserts[::-1], comp_coils[::-1]):
+            coils.insert(idx, comp_coil)
+        # Now we have:
+        # [c['chname'] for c in coils] ==
+        # [info['ch_names'][ii]
+        #  for ii in pick_types(info, meg=True, ref_meg=True)]
+
+    # Now coils is a sorted list of coils. Time to do some vectorization.
+    n_coils = len(coils)
+    rmags = np.concatenate([coil['rmag'] for coil in coils])
+    cosmags = np.concatenate([coil['cosmag'] for coil in coils])
+    ws = np.concatenate([coil['w'] for coil in coils])
+    cosmags *= ws[:, np.newaxis]
+    del ws
+    n_int = np.array([len(coil['rmag']) for coil in coils])
+    bins = np.repeat(np.arange(len(n_int)), n_int)
+    bd = np.concatenate(([0], np.cumsum(n_int)))
+    slice_map = dict((ii, slice(start, stop))
+                     for ii, (start, stop) in enumerate(zip(bd[:-1], bd[1:])))
+    return rmags, cosmags, bins, n_coils, mag_mask, slice_map
+
+
+def _trans_starts_stops_quats(pos, start, stop, this_pos_data):
+    """Helper to get all trans and limits we need"""
+    pos_idx = np.arange(*np.searchsorted(pos[1], [start, stop]))
+    used = np.zeros(stop - start, bool)
+    trans = list()
+    rel_starts = list()
+    rel_stops = list()
+    quats = list()
+    if this_pos_data is None:
+        avg_trans = None
+    else:
+        avg_trans = np.zeros(6)
+    for ti in range(-1, len(pos_idx)):
+        # first iteration for this block of data
+        if ti < 0:
+            rel_start = 0
+            rel_stop = pos[1][pos_idx[0]] if len(pos_idx) > 0 else stop
+            rel_stop = rel_stop - start
+            if rel_start == rel_stop:
+                continue  # our first pos occurs on first time sample
+            # Don't calculate S_decomp here, use the last one
+            trans.append(None)  # meaning: use previous
+            quats.append(this_pos_data)
+        else:
+            rel_start = pos[1][pos_idx[ti]] - start
+            if ti == len(pos_idx) - 1:
+                rel_stop = stop - start
+            else:
+                rel_stop = pos[1][pos_idx[ti + 1]] - start
+            trans.append(pos[0][pos_idx[ti]])
+            quats.append(pos[2][pos_idx[ti]])
+        assert 0 <= rel_start
+        assert rel_start < rel_stop
+        assert rel_stop <= stop - start
+        assert not used[rel_start:rel_stop].any()
+        used[rel_start:rel_stop] = True
+        rel_starts.append(rel_start)
+        rel_stops.append(rel_stop)
+        if this_pos_data is not None:
+            avg_trans += quats[-1][:6] * (rel_stop - rel_start)
+    assert used.all()
+    # Use weighted average for average trans over the window
+    if avg_trans is not None:
+        avg_trans /= (stop - start)
+        avg_trans = np.vstack([
+            np.hstack([quat_to_rot(avg_trans[:3]),
+                       avg_trans[3:][:, np.newaxis]]),
+            [[0., 0., 0., 1.]]])
+    return trans, rel_starts, rel_stops, quats, avg_trans
+
+
+def _do_tSSS(clean_data, orig_in_data, resid, st_correlation,
+             n_positions, t_str):
+    """Compute and apply SSP-like projection vectors based on min corr"""
+    np.asarray_chkfinite(resid)
+    t_proj = _overlap_projector(orig_in_data, resid, st_correlation)
+    # Apply projector according to Eq. 12 in [2]_
+    msg = ('        Projecting % 2d intersecting tSSS components '
+           'for %s' % (t_proj.shape[1], t_str))
+    if n_positions > 1:
+        msg += ' (across % 2d positions)' % n_positions
+    logger.info(msg)
+    clean_data -= np.dot(np.dot(clean_data, t_proj), t_proj.T)
+
+
+def _copy_preload_add_channels(raw, add_channels):
+    """Helper to load data for processing and (maybe) add cHPI pos channels"""
+    raw = raw.copy()
+    if add_channels:
+        kinds = [FIFF.FIFFV_QUAT_1, FIFF.FIFFV_QUAT_2, FIFF.FIFFV_QUAT_3,
+                 FIFF.FIFFV_QUAT_4, FIFF.FIFFV_QUAT_5, FIFF.FIFFV_QUAT_6,
+                 FIFF.FIFFV_HPI_G, FIFF.FIFFV_HPI_ERR, FIFF.FIFFV_HPI_MOV]
+        out_shape = (len(raw.ch_names) + len(kinds), len(raw.times))
+        out_data = np.zeros(out_shape, np.float64)
+        msg = '    Appending head position result channels and '
+        if raw.preload:
+            logger.info(msg + 'copying original raw data')
+            out_data[:len(raw.ch_names)] = raw._data
+            raw._data = out_data
+        else:
+            logger.info(msg + 'loading raw data from disk')
+            raw._preload_data(out_data[:len(raw.ch_names)], verbose=False)
+            raw._data = out_data
+        assert raw.preload is True
+        off = len(raw.ch_names)
+        chpi_chs = [
+            dict(ch_name='CHPI%03d' % (ii + 1), logno=ii + 1,
+                 scanno=off + ii + 1, unit_mul=-1, range=1., unit=-1,
+                 kind=kinds[ii], coord_frame=FIFF.FIFFV_COORD_UNKNOWN,
+                 cal=1e-4, coil_type=FIFF.FWD_COIL_UNKNOWN, loc=np.zeros(12))
+            for ii in range(len(kinds))]
+        raw.info['chs'].extend(chpi_chs)
+        raw.info._update_redundant()
+        raw.info._check_consistency()
+        assert raw._data.shape == (raw.info['nchan'], len(raw.times))
+        # Return the pos picks
+        pos_picks = np.arange(len(raw.ch_names) - len(chpi_chs),
+                              len(raw.ch_names))
+        return raw, pos_picks
+    else:
+        if not raw.preload:
+            logger.info('    Loading raw data from disk')
+            raw.load_data(verbose=False)
+        else:
+            logger.info('    Using loaded raw data')
+        return raw, np.array([], int)
+
+
+def _check_pos(pos, head_frame, raw, st_fixed, sfreq):
+    """Check for a valid pos array and transform it to a more usable form"""
+    if pos is None:
+        return [None, np.array([-1])]
+    if not head_frame:
+        raise ValueError('positions can only be used if coord_frame="head"')
+    if not st_fixed:
+        warn('st_fixed=False is untested, use with caution!')
+    if not isinstance(pos, np.ndarray):
+        raise TypeError('pos must be an ndarray')
+    if pos.ndim != 2 or pos.shape[1] != 10:
+        raise ValueError('pos must be an array of shape (N, 10)')
+    t = pos[:, 0]
+    t_off = raw.first_samp / raw.info['sfreq']
+    if not np.array_equal(t, np.unique(t)):
+        raise ValueError('Time points must unique and in ascending order')
+    # We need an extra 1e-3 (1 ms) here because MaxFilter outputs values
+    # only out to 3 decimal places
+    if not _time_mask(t, tmin=t_off - 1e-3, tmax=None, sfreq=sfreq).all():
+        raise ValueError('Head position time points must be greater than '
+                         'first sample offset, but found %0.4f < %0.4f'
+                         % (t[0], t_off))
+    dev_head_ts = np.zeros((len(t), 4, 4))
+    dev_head_ts[:, 3, 3] = 1.
+    dev_head_ts[:, :3, 3] = pos[:, 4:7]
+    dev_head_ts[:, :3, :3] = quat_to_rot(pos[:, 1:4])
+    pos = [dev_head_ts, t - t_off, pos[:, 1:]]
+    return pos
+
+
+ at verbose
+def _get_decomp(trans, all_coils, cal, regularize, exp, ignore_ref,
+                coil_scale, grad_picks, mag_picks, good_picks, mag_or_fine,
+                bad_condition, verbose=None):
+    """Helper to get a decomposition matrix"""
+    #
+    # Fine calibration processing (point-like magnetometers and calib. coeffs)
+    #
+    S_decomp = _trans_sss_basis(exp, all_coils, trans, coil_scale)
+    if cal is not None:
+        # Compute point-like mags to incorporate gradiometer imbalance
+        cal['grad_cals'] = _sss_basis_point(exp, trans, cal, ignore_ref)
+        # Add point like magnetometer data to bases.
+        S_decomp[grad_picks, :] += cal['grad_cals']
+        # Scale magnetometers by calibration coefficient
+        S_decomp[mag_picks, :] /= cal['mag_cals']
+        # We need to be careful about KIT gradiometers
+    S_decomp = S_decomp[good_picks]
+
+    #
+    # Regularization
+    #
+    reg_moments, n_use_in = _regularize(regularize, exp, S_decomp, mag_or_fine)
+    S_decomp = S_decomp.take(reg_moments, axis=1)
+
+    # Pseudo-inverse of total multipolar moment basis set (Part of Eq. 37)
+    pS_decomp, sing = _col_norm_pinv(S_decomp.copy())
+    cond = sing[0] / sing[-1]
+    logger.debug('    Decomposition matrix condition: %0.1f' % cond)
+    if bad_condition != 'ignore' and cond >= 1000.:
+        msg = 'Matrix is badly conditioned: %0.0f >= 1000' % cond
+        if bad_condition == 'error':
+            raise RuntimeError(msg)
+        else:  # condition == 'warning':
+            warn(msg)
+
+    # Build in our data scaling here
+    pS_decomp *= coil_scale[good_picks].T
+    S_decomp /= coil_scale[good_picks]
+    return S_decomp, pS_decomp, reg_moments, n_use_in
+
+
+def _regularize(regularize, exp, S_decomp, mag_or_fine):
     """Regularize a decomposition matrix"""
     # ALWAYS regularize the out components according to norm, since
     # gradiometer-only setups (e.g., KIT) can have zero first-order
     # components
+    int_order, ext_order = exp['int_order'], exp['ext_order']
     n_in, n_out = _get_n_moments([int_order, ext_order])
     if regularize is not None:  # regularize='in'
         logger.info('    Computing regularization')
@@ -449,9 +775,9 @@ def _get_mf_picks(info, int_order, ext_order, ignore_ref=False,
     # Check for T1/T2 mag types
     mag_inds_T1T2 = _get_T1T2_mag_inds(info)
     if len(mag_inds_T1T2) > 0:
-        logger.warning('%d T1/T2 magnetometer channel types found. If using '
-                       ' SSS, it is advised to replace coil types using '
-                       ' `fix_mag_coil_types`.' % len(mag_inds_T1T2))
+        warn('%d T1/T2 magnetometer channel types found. If using SSS, it is '
+             'advised to replace coil types using "fix_mag_coil_types".'
+             % len(mag_inds_T1T2))
     # Get indices of channels to use in multipolar moment calculation
     ref = not ignore_ref
     meg_picks = pick_types(info, meg=True, ref_meg=ref, exclude=[])
@@ -615,26 +941,25 @@ def _concatenate_sph_coils(coils):
 _mu_0 = 4e-7 * np.pi  # magnetic permeability
 
 
-def _get_coil_scale(coils, mag_scale=100.):
+def _get_mag_mask(coils):
     """Helper to get the coil_scale for Maxwell filtering"""
-    coil_scale = np.ones((len(coils), 1))
-    coil_scale[np.array([coil['coil_class'] == FIFF.FWD_COILC_MAG
-                         for coil in coils])] = mag_scale
-    return coil_scale
+    return np.array([coil['coil_class'] == FIFF.FWD_COILC_MAG
+                     for coil in coils])
 
 
-def _sss_basis_basic(origin, coils, int_order, ext_order, mag_scale=100.,
-                     method='standard'):
+def _sss_basis_basic(exp, coils, mag_scale=100., method='standard'):
     """Compute SSS basis using non-optimized (but more readable) algorithms"""
+    int_order, ext_order = exp['int_order'], exp['ext_order']
+    origin = exp['origin']
     # Compute vector between origin and coil, convert to spherical coords
     if method == 'standard':
         # Get position, normal, weights, and number of integration pts.
-        rmags, cosmags, wcoils, bins = _concatenate_coils(coils)
+        rmags, cosmags, ws, bins = _concatenate_coils(coils)
         rmags -= origin
         # Convert points to spherical coordinates
         rad, az, pol = _cart_to_sph(rmags).T
-        cosmags *= wcoils[:, np.newaxis]
-        del rmags, wcoils
+        cosmags *= ws[:, np.newaxis]
+        del rmags, ws
         out_type = np.float64
     else:  # testing equivalence method
         rs, wcoils, ezs, bins = _concatenate_sph_coils(coils)
@@ -650,7 +975,8 @@ def _sss_basis_basic(origin, coils, int_order, ext_order, mag_scale=100.,
     S_tot = np.empty((len(coils), n_in + n_out), out_type)
     S_in = S_tot[:, :n_in]
     S_out = S_tot[:, n_in:]
-    coil_scale = _get_coil_scale(coils)
+    coil_scale = np.ones((len(coils), 1))
+    coil_scale[_get_mag_mask(coils)] = 100.
 
     # Compute internal/external basis vectors (exclude degree 0; L/RHS Eq. 5)
     for degree in range(1, max(int_order, ext_order) + 1):
@@ -722,32 +1048,26 @@ def _sss_basis_basic(origin, coils, int_order, ext_order, mag_scale=100.,
     return S_tot
 
 
-def _prep_bases(coils, int_order, ext_order):
-    """Helper to prepare for basis computation"""
-    # Get position, normal, weights, and number of integration pts.
-    rmags, cosmags, wcoils, bins = _concatenate_coils(coils)
-    cosmags *= wcoils[:, np.newaxis]
-    n_in, n_out = _get_n_moments([int_order, ext_order])
-    S_tot = np.empty((len(coils), n_in + n_out), np.float64)
-    return rmags, cosmags, bins, len(coils), S_tot, n_in
-
-
-def _sss_basis(origin, coils, int_order, ext_order):
+def _sss_basis(exp, all_coils):
     """Compute SSS basis for given conditions.
 
     Parameters
     ----------
-    origin : ndarray, shape (3,)
-        Origin of the multipolar moment space in millimeters
+    exp : dict
+        Must contain the following keys:
+
+            origin : ndarray, shape (3,)
+                Origin of the multipolar moment space in millimeters
+            int_order : int
+                Order of the internal multipolar moment space
+            ext_order : int
+                Order of the external multipolar moment space
+
     coils : list
         List of MEG coils. Each should contain coil information dict specifying
         position, normals, weights, number of integration points and channel
         type. All coil geometry must be in the same coordinate frame
         as ``origin`` (``head`` or ``meg``).
-    int_order : int
-        Order of the internal multipolar moment space
-    ext_order : int
-        Order of the external multipolar moment space
 
     Returns
     -------
@@ -760,9 +1080,12 @@ def _sss_basis(origin, coils, int_order, ext_order):
 
     Adapted from code provided by Jukka Nenonen.
     """
-    rmags, cosmags, bins, n_coils, S_tot, n_in = _prep_bases(
-        coils, int_order, ext_order)
-    rmags = rmags - origin
+    rmags, cosmags, bins, n_coils = all_coils[:4]
+    int_order, ext_order = exp['int_order'], exp['ext_order']
+    n_in, n_out = _get_n_moments([int_order, ext_order])
+    S_tot = np.empty((n_coils, n_in + n_out), np.float64)
+
+    rmags = rmags - exp['origin']
     S_in = S_tot[:, :n_in]
     S_out = S_tot[:, n_in:]
 
@@ -811,8 +1134,9 @@ def _sss_basis(origin, coils, int_order, ext_order):
                     cos_az, sin_az, cos_pol, sin_pol, b_r, 0., b_pol,
                     cosmags, bins, n_coils)
         for order in range(1, degree + 1):
-            sin_order = np.sin(order * phi)
-            cos_order = np.cos(order * phi)
+            ord_phi = order * phi
+            sin_order = np.sin(ord_phi)
+            cos_order = np.cos(ord_phi)
             mult /= np.sqrt((degree - order + 1) * (degree + order))
             factor = mult * np.sqrt(2)  # equivalence fix (Elekta uses 2.)
 
@@ -1135,22 +1459,22 @@ def _cart_to_sph(cart_pts):
     return np.array([rad, az, pol]).T
 
 
-def _check_raw(raw):
+def _check_info(info, sss=True, tsss=True, calibration=True, ctc=True):
     """Ensure that Maxwell filtering has not been applied yet"""
-    if not isinstance(raw, _BaseRaw):
-        raise TypeError('raw must be Raw, not %s' % type(raw))
-    for ent in raw.info.get('proc_history', []):
-        for msg, key in (('SSS', 'sss_info'),
-                         ('tSSS', 'max_st'),
-                         ('fine calibration', 'sss_cal'),
-                         ('cross-talk cancellation',  'sss_ctc')):
+    for ent in info.get('proc_history', []):
+        for msg, key, doing in (('SSS', 'sss_info', sss),
+                                ('tSSS', 'max_st', tsss),
+                                ('fine calibration', 'sss_cal', calibration),
+                                ('cross-talk cancellation',  'sss_ctc', ctc)):
+            if not doing:
+                continue
             if len(ent['max_info'][key]) > 0:
                 raise RuntimeError('Maxwell filtering %s step has already '
-                                   'been applied' % msg)
+                                   'been applied, cannot reapply' % msg)
 
 
 def _update_sss_info(raw, origin, int_order, ext_order, nchan, coord_frame,
-                     sss_ctc, sss_cal, max_st, reg_moments):
+                     sss_ctc, sss_cal, max_st, reg_moments, st_only):
     """Helper function to update info inplace after Maxwell filtering
 
     Parameters
@@ -1174,6 +1498,8 @@ def _update_sss_info(raw, origin, int_order, ext_order, nchan, coord_frame,
         The tSSS information.
     reg_moments : ndarray | slice
         The moments that were used.
+    st_only : bool
+        Whether tSSS only was performed.
     """
     n_in, n_out = _get_n_moments([int_order, ext_order])
     raw.info['maxshield'] = False
@@ -1184,15 +1510,19 @@ def _update_sss_info(raw, origin, int_order, ext_order, nchan, coord_frame,
                          job=np.array([2]), nfree=np.sum(components[:n_in]),
                          frame=_str_to_frame[coord_frame],
                          components=components)
-    max_info_dict = dict(sss_info=sss_info_dict, max_st=max_st,
-                         sss_cal=sss_cal, sss_ctc=sss_ctc)
+    max_info_dict = dict(max_st=max_st)
+    if st_only:
+        max_info_dict.update(sss_info=dict(), sss_cal=dict(), sss_ctc=dict())
+    else:
+        max_info_dict.update(sss_info=sss_info_dict, sss_cal=sss_cal,
+                             sss_ctc=sss_ctc)
+        # Reset 'bads' for any MEG channels since they've been reconstructed
+        _reset_meg_bads(raw.info)
     block_id = _generate_meas_id()
     proc_block = dict(max_info=max_info_dict, block_id=block_id,
                       creator='mne-python v%s' % __version__,
                       date=_date_now(), experimentor='')
     raw.info['proc_history'] = [proc_block] + raw.info.get('proc_history', [])
-    # Reset 'bads' for any MEG channels since they've been reconstructed
-    _reset_meg_bads(raw.info)
 
 
 def _reset_meg_bads(info):
@@ -1232,10 +1562,12 @@ def _overlap_projector(data_int, data_res, corr):
     # Normalize data, then compute orth to get temporal bases. Matrices
     # must have shape (n_samps x effective_rank) when passed into svd
     # computation
-    n = np.sqrt(np.sum(data_int * data_int))
+
+    # we use np.linalg.norm instead of sp.linalg.norm here: ~2x faster!
+    n = np.linalg.norm(data_int)
     Q_int = linalg.qr(_orth_overwrite((data_int / n).T),
                       overwrite_a=True, mode='economic', **check_disable)[0].T
-    n = np.sqrt(np.sum(data_res * data_res))
+    n = np.linalg.norm(data_res)
     Q_res = linalg.qr(_orth_overwrite((data_res / n).T),
                       overwrite_a=True, mode='economic', **check_disable)[0]
     assert data_int.shape[1] > 0
@@ -1286,28 +1618,7 @@ def _read_fine_cal(fine_cal):
     return cal_chs, cal_ch_numbers
 
 
-def _skew_symmetric_cross(a):
-    """The skew-symmetric cross product of a vector"""
-    return np.array([[0., -a[2], a[1]], [a[2], 0., -a[0]], [-a[1], a[0], 0.]])
-
-
-def _find_vector_rotation(a, b):
-    """Find the rotation matrix that maps unit vector a to b"""
-    # Rodrigues' rotation formula:
-    #   https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
-    #   http://math.stackexchange.com/a/476311
-    R = np.eye(3)
-    v = np.cross(a, b)
-    if np.allclose(v, 0.):  # identical
-        return R
-    s = np.sqrt(np.sum(v * v))  # sine of the angle between them
-    c = np.sqrt(np.sum(a * b))  # cosine of the angle between them
-    vx = _skew_symmetric_cross(v)
-    R += vx + np.dot(vx, vx) * (1 - c) / s
-    return R
-
-
-def _update_sensor_geometry(info, fine_cal):
+def _update_sensor_geometry(info, fine_cal, head_frame, ignore_ref):
     """Helper to replace sensor geometry information and reorder cal_chs"""
     logger.info('    Using fine calibration %s' % op.basename(fine_cal))
     cal_chs, cal_ch_numbers = _read_fine_cal(fine_cal)
@@ -1316,11 +1627,13 @@ def _update_sensor_geometry(info, fine_cal):
     meg_info = pick_info(info, pick_types(info, meg=True, exclude=[]))
     clean_meg_names = _clean_names(meg_info['ch_names'],
                                    remove_whitespace=True)
-    order = pick_channels([c['ch_name'] for c in cal_chs], clean_meg_names)
-    if not (len(cal_chs) == meg_info['nchan'] == len(order)):
-        raise RuntimeError('Number of channels in fine calibration file (%i) '
-                           'does not equal number of channels in info (%i)' %
-                           (len(cal_chs), meg_info['nchan']))
+    cal_names = [c['ch_name'] for c in cal_chs]
+    order = pick_channels(cal_names, clean_meg_names)
+    if meg_info['nchan'] != len(order):
+        raise RuntimeError('Not all MEG channels found in fine calibration '
+                           'file, missing:\n%s'
+                           % sorted(list(set(clean_meg_names) -
+                                         set(cal_names))))
     # ensure they're ordered like our data
     cal_chs = [cal_chs[ii] for ii in order]
 
@@ -1390,38 +1703,45 @@ def _update_sensor_geometry(info, fine_cal):
     mag_picks = pick_types(info, meg='mag', exclude=[])
     grad_imbalances = np.array([cal_chs[ii]['calib_coeff']
                                 for ii in grad_picks]).T
+    if grad_imbalances.shape[0] not in [1, 3]:
+        raise ValueError('Must have 1 (x) or 3 (x, y, z) point-like ' +
+                         'magnetometers. Currently have %i' %
+                         grad_imbalances.shape[0])
     mag_cals = np.array([cal_chs[ii]['calib_coeff'] for ii in mag_picks])
-    return grad_imbalances, mag_cals, sss_cal
 
+    # Now let's actually construct our point-like adjustment coils for grads
+    grad_coilsets = _get_grad_point_coilsets(
+        info, n_types=len(grad_imbalances), ignore_ref=ignore_ref)
+    calibration = dict(grad_imbalances=grad_imbalances,
+                       grad_coilsets=grad_coilsets, mag_cals=mag_cals)
+    return calibration, sss_cal
 
-def _sss_basis_point(origin, info, int_order, ext_order, imbalances,
-                     ignore_ref=False, head_frame=True):
-    """Compute multipolar moments for point-like magnetometers (in fine cal)"""
-
-    # Construct 'coils' with r, weights, normal vecs, # integration pts, and
-    # channel type.
-    if imbalances.shape[0] not in [1, 3]:
-        raise ValueError('Must have 1 (x) or 3 (x, y, z) point-like ' +
-                         'magnetometers. Currently have %i' %
-                         imbalances.shape[0])
 
+def _get_grad_point_coilsets(info, n_types, ignore_ref):
+    """Helper to get point-type coilsets for gradiometers"""
+    grad_coilsets = list()
+    grad_info = pick_info(
+        info, pick_types(info, meg='grad', exclude=[]), copy=True)
     # Coil_type values for x, y, z point magnetometers
     # Note: 1D correction files only have x-direction corrections
     pt_types = [FIFF.FIFFV_COIL_POINT_MAGNETOMETER_X,
                 FIFF.FIFFV_COIL_POINT_MAGNETOMETER_Y,
                 FIFF.FIFFV_COIL_POINT_MAGNETOMETER]
+    for pt_type in pt_types[:n_types]:
+        for ch in grad_info['chs']:
+            ch['coil_type'] = pt_type
+        grad_coilsets.append(_prep_mf_coils(grad_info, ignore_ref))
+    return grad_coilsets
 
+
+def _sss_basis_point(exp, trans, cal, ignore_ref=False, mag_scale=100.):
+    """Compute multipolar moments for point-like magnetometers (in fine cal)"""
     # Loop over all coordinate directions desired and create point mags
     S_tot = 0.
     # These are magnetometers, so use a uniform coil_scale of 100.
-    this_coil_scale = np.array([100.])
-    for imb, pt_type in zip(imbalances, pt_types):
-        temp_info = deepcopy(info)
-        for ch in temp_info['chs']:
-            ch['coil_type'] = pt_type
-        S_add = _info_sss_basis(temp_info, None, origin,
-                                int_order, ext_order, head_frame,
-                                ignore_ref, this_coil_scale)
+    this_cs = np.array([mag_scale], float)
+    for imb, coils in zip(cal['grad_imbalances'], cal['grad_coilsets']):
+        S_add = _trans_sss_basis(exp, coils, trans, this_cs)
         # Scale spaces by gradiometer imbalance
         S_add *= imb[:, np.newaxis]
         S_tot += S_add
@@ -1448,7 +1768,7 @@ def _regularize_in(int_order, ext_order, S_decomp, mag_or_fine):
     degrees, orders = _get_degrees_orders(int_order)
     a_lm_sq = a_lm_sq[degrees]
 
-    I_tots = np.empty(n_in)
+    I_tots = np.zeros(n_in)  # we might not traverse all, so use np.zeros
     in_keepers = list(range(n_in))
     out_removes = _regularize_out(int_order, ext_order, mag_or_fine)
     out_keepers = list(np.setdiff1d(np.arange(n_in, n_in + n_out),
@@ -1485,6 +1805,7 @@ def _regularize_in(int_order, ext_order, S_decomp, mag_or_fine):
         this_S = S_decomp.take(in_keepers + out_keepers, axis=1)
         u, s, v = linalg.svd(this_S, full_matrices=False, overwrite_a=True,
                              **check_disable)
+        del this_S
         eigs[ii] = s[[0, -1]]
         v = v.T[:len(in_keepers)]
         v /= use_norm[in_keepers][:, np.newaxis]
@@ -1502,6 +1823,9 @@ def _regularize_in(int_order, ext_order, S_decomp, mag_or_fine):
         I_tots[ii] = 0.5 * np.log2(snr + 1.).sum()
         remove_order.append(in_keepers[np.argmin(snr)])
         in_keepers.pop(in_keepers.index(remove_order[-1]))
+        # heuristic to quit if we're past the peak to save cycles
+        if ii > 10 and (I_tots[ii - 1:ii + 1] < 0.95 * I_tots.max()).all():
+            break
         # if plot and ii == 0:
         #     axs[0].semilogy(snr[plot_ord[in_keepers]], color='k')
     # if plot:
@@ -1572,30 +1896,19 @@ def _compute_sphere_activation_in(degrees):
     return a_power, rho_i
 
 
-def _info_sss_basis(info, trans, origin, int_order, ext_order, head_frame,
-                    ignore_ref=False, coil_scale=100.):
-    """SSS basis using an info structure and dev<->head trans"""
+def _trans_sss_basis(exp, all_coils, trans=None, coil_scale=100.):
+    """SSS basis (optionally) using a dev<->head trans"""
     if trans is not None:
-        info = info.copy()
-        info['dev_head_t'] = info['dev_head_t'].copy()
-        info['dev_head_t']['trans'] = trans
-    coils, comp_coils = _prep_meg_channels(
-        info, accurate=True, elekta_defs=True, head_frame=head_frame,
-        ignore_ref=ignore_ref, verbose=False)[:2]
-    if len(comp_coils) > 0:
-        meg_picks = pick_types(info, meg=True, ref_meg=False, exclude=[])
-        ref_picks = pick_types(info, meg=False, ref_meg=True, exclude=[])
-        inserts = np.searchsorted(meg_picks, ref_picks)
-        # len(inserts) == len(comp_coils)
-        for idx, comp_coil in zip(inserts[::-1], comp_coils[::-1]):
-            coils.insert(idx, comp_coil)
-        # Now we have:
-        # [c['chname'] for c in coils] ==
-        # [info['ch_names'][ii]
-        #  for ii in pick_types(info, meg=True, ref_meg=True)]
+        if not isinstance(trans, Transform):
+            trans = Transform('meg', 'head', trans)
+        all_coils = (apply_trans(trans, all_coils[0]),
+                     apply_trans(trans, all_coils[1], move=False),
+                     ) + all_coils[2:]
     if not isinstance(coil_scale, np.ndarray):
         # Scale all magnetometers (with `coil_class` == 1.0) by `mag_scale`
-        coil_scale = _get_coil_scale(coils, coil_scale)
-    S_tot = _sss_basis(origin, coils, int_order, ext_order)
+        cs = coil_scale
+        coil_scale = np.ones((all_coils[3], 1))
+        coil_scale[all_coils[4]] = cs
+    S_tot = _sss_basis(exp, all_coils)
     S_tot *= coil_scale
     return S_tot
diff --git a/mne/preprocessing/ssp.py b/mne/preprocessing/ssp.py
index 63fac16..758e55b 100644
--- a/mne/preprocessing/ssp.py
+++ b/mne/preprocessing/ssp.py
@@ -5,11 +5,11 @@
 # License: BSD (3-clause)
 
 import copy as cp
-from warnings import warn
+
 import numpy as np
 
 from .. import Epochs, compute_proj_evoked, compute_proj_epochs
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from .. import pick_types
 from ..io import make_eeg_average_ref_proj
 from .ecg import find_ecg_events
@@ -185,7 +185,7 @@ def _compute_exg_proj(mode, raw, raw_event, tmin, tmax,
     epochs = Epochs(raw, events, None, tmin, tmax, baseline=None, preload=True,
                     picks=picks, reject=reject, flat=flat, proj=True)
 
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
     if epochs.events.shape[0] < 1:
         warn('No good epochs found, returning None for projs')
         return None, events
diff --git a/mne/preprocessing/stim.py b/mne/preprocessing/stim.py
index 12da024..9728b7d 100644
--- a/mne/preprocessing/stim.py
+++ b/mne/preprocessing/stim.py
@@ -5,10 +5,11 @@
 import numpy as np
 from ..evoked import Evoked
 from ..epochs import _BaseEpochs
-from ..io import Raw
+from ..io import _BaseRaw
 from ..event import find_events
 
 from ..io.pick import pick_channels
+from ..utils import _check_copy_dep
 
 
 def _get_window(start, end):
@@ -43,7 +44,7 @@ def _fix_artifact(data, window, picks, first_samp, last_samp, mode):
 
 
 def fix_stim_artifact(inst, events=None, event_id=None, tmin=0.,
-                      tmax=0.01, mode='linear', stim_channel=None, copy=False):
+                      tmax=0.01, mode='linear', stim_channel=None, copy=None):
     """Eliminate stimulation's artifacts from instance
 
     Parameters
@@ -75,9 +76,7 @@ def fix_stim_artifact(inst, events=None, event_id=None, tmin=0.,
     """
     if mode not in ('linear', 'window'):
         raise ValueError("mode has to be 'linear' or 'window' (got %s)" % mode)
-
-    if copy:
-        inst = inst.copy()
+    inst = _check_copy_dep(inst, copy)
     s_start = int(np.ceil(inst.info['sfreq'] * tmin))
     s_end = int(np.ceil(inst.info['sfreq'] * tmax))
     if (mode == "window") and (s_end - s_start) < 4:
@@ -89,7 +88,7 @@ def fix_stim_artifact(inst, events=None, event_id=None, tmin=0.,
     ch_names = inst.info['ch_names']
     picks = pick_channels(ch_names, ch_names)
 
-    if isinstance(inst, Raw):
+    if isinstance(inst, _BaseRaw):
         _check_preload(inst)
         if events is None:
             events = find_events(inst, stim_channel=stim_channel)
diff --git a/mne/preprocessing/tests/test_ctps.py b/mne/preprocessing/tests/test_ctps.py
index c562775..bd66982 100644
--- a/mne/preprocessing/tests/test_ctps.py
+++ b/mne/preprocessing/tests/test_ctps.py
@@ -1,11 +1,13 @@
 # Authors: Denis A. Engemann <denis.engemann at gmail.com>
 #
 # License: BSD 3 clause
+import warnings
 
-import numpy as np
-from mne.time_frequency import morlet
 from nose.tools import assert_true, assert_raises
+import numpy as np
 from numpy.testing import assert_array_equal
+
+from mne.time_frequency import morlet
 from mne.preprocessing.ctps_ import (ctps, _prob_kuiper,
                                      _compute_normalized_phase)
 
@@ -33,7 +35,8 @@ def get_data(n_trials, j_extent):
     ground_truth = np.tile(single_trial,  n_trials)
     my_shape = n_trials, 1, 600
     random_data = rng.random_sample(my_shape)
-    rand_ints = rng.random_integers(-j_extent, j_extent, n_trials)
+    with warnings.catch_warnings(record=True):  # weight tables
+        rand_ints = rng.random_integers(-j_extent, j_extent, n_trials)
     jittered_data = np.array([np.roll(single_trial, i) for i in rand_ints])
     data = np.concatenate([ground_truth.reshape(my_shape),
                            jittered_data.reshape(my_shape),
diff --git a/mne/preprocessing/tests/test_eeglab_infomax.py b/mne/preprocessing/tests/test_eeglab_infomax.py
index 99ef5af..0711441 100644
--- a/mne/preprocessing/tests/test_eeglab_infomax.py
+++ b/mne/preprocessing/tests/test_eeglab_infomax.py
@@ -37,7 +37,7 @@ def generate_data_for_comparing_against_eeglab_infomax(ch_type, random_state):
     raw.filter(1, 45, n_jobs=2)
     X = raw[picks, :][0][:, ::20]
 
-    # Substract the mean
+    # Subtract the mean
     mean_X = X.mean(axis=1)
     X -= mean_X[:, None]
 
@@ -86,7 +86,7 @@ def test_mne_python_vs_eeglab():
         N = Y.shape[0]
         T = Y.shape[1]
 
-        # For comparasion against eeglab, make sure the folowing
+        # For comparasion against eeglab, make sure the following
         # parameters have the same value in mne_python and eeglab:
         #
         # - starting point
diff --git a/mne/preprocessing/tests/test_ica.py b/mne/preprocessing/tests/test_ica.py
index cada286..fd8df37 100644
--- a/mne/preprocessing/tests/test_ica.py
+++ b/mne/preprocessing/tests/test_ica.py
@@ -20,8 +20,9 @@ from mne import Epochs, read_events, pick_types
 from mne.cov import read_cov
 from mne.preprocessing import (ICA, ica_find_ecg_events, ica_find_eog_events,
                                read_ica, run_ica)
-from mne.preprocessing.ica import get_score_funcs, corrmap
+from mne.preprocessing.ica import get_score_funcs, corrmap, _get_ica_map
 from mne.io import Raw, Info
+from mne.tests.common import assert_naming
 from mne.utils import (catch_logging, _TempDir, requires_sklearn, slow_test,
                        run_tests_if_main)
 
@@ -53,13 +54,14 @@ except:
 def test_ica_full_data_recovery():
     """Test recovery of full data when no source is rejected"""
     # Most basic recovery
-    raw = Raw(raw_fname).crop(0.5, stop, False)
+    raw = Raw(raw_fname).crop(0.5, stop, copy=False)
     raw.load_data()
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')[:10]
-    epochs = Epochs(raw, events[:4], event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0), preload=True)
+    with warnings.catch_warnings(record=True):  # bad proj
+        epochs = Epochs(raw, events[:4], event_id, tmin, tmax, picks=picks,
+                        baseline=(None, 0), preload=True)
     evoked = epochs.average()
     n_channels = 5
     data = raw._data[:n_channels].copy()
@@ -74,7 +76,7 @@ def test_ica_full_data_recovery():
                       method=method, max_iter=1)
             with warnings.catch_warnings(record=True):
                 ica.fit(raw, picks=list(range(n_channels)))
-            raw2 = ica.apply(raw, exclude=[], copy=True)
+            raw2 = ica.apply(raw.copy(), exclude=[])
             if ok:
                 assert_allclose(data[:n_channels], raw2._data[:n_channels],
                                 rtol=1e-10, atol=1e-15)
@@ -87,7 +89,7 @@ def test_ica_full_data_recovery():
                       n_pca_components=n_pca_components)
             with warnings.catch_warnings(record=True):
                 ica.fit(epochs, picks=list(range(n_channels)))
-            epochs2 = ica.apply(epochs, exclude=[], copy=True)
+            epochs2 = ica.apply(epochs.copy(), exclude=[])
             data2 = epochs2.get_data()[:, :n_channels]
             if ok:
                 assert_allclose(data_epochs[:, :n_channels], data2,
@@ -96,7 +98,7 @@ def test_ica_full_data_recovery():
                 diff = np.abs(data_epochs[:, :n_channels] - data2)
                 assert_true(np.max(diff) > 1e-14)
 
-            evoked2 = ica.apply(evoked, exclude=[], copy=True)
+            evoked2 = ica.apply(evoked.copy(), exclude=[])
             data2 = evoked2.data[:n_channels]
             if ok:
                 assert_allclose(data_evoked[:n_channels], data2,
@@ -109,9 +111,9 @@ def test_ica_full_data_recovery():
 
 @requires_sklearn
 def test_ica_rank_reduction():
-    """Test recovery of full data when no source is rejected"""
+    """Test recovery ICA rank reduction"""
     # Most basic recovery
-    raw = Raw(raw_fname).crop(0.5, stop, False)
+    raw = Raw(raw_fname).crop(0.5, stop, copy=False)
     raw.load_data()
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')[:10]
@@ -127,7 +129,7 @@ def test_ica_rank_reduction():
 
         rank_before = raw.estimate_rank(picks=picks)
         assert_equal(rank_before, len(picks))
-        raw_clean = ica.apply(raw, copy=True)
+        raw_clean = ica.apply(raw.copy())
         rank_after = raw_clean.estimate_rank(picks=picks)
         # interaction between ICA rejection and PCA components difficult
         # to preduct. Rank_after often seems to be 1 higher then
@@ -139,7 +141,7 @@ def test_ica_rank_reduction():
 @requires_sklearn
 def test_ica_reset():
     """Test ICA resetting"""
-    raw = Raw(raw_fname).crop(0.5, stop, False)
+    raw = Raw(raw_fname).crop(0.5, stop, copy=False)
     raw.load_data()
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')[:10]
@@ -167,7 +169,7 @@ def test_ica_reset():
 @requires_sklearn
 def test_ica_core():
     """Test ICA on raw and epochs"""
-    raw = Raw(raw_fname).crop(1.5, stop, False)
+    raw = Raw(raw_fname).crop(1.5, stop, copy=False)
     raw.load_data()
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
@@ -253,7 +255,7 @@ def test_ica_core():
     # test for bug with whitener updating
     _pre_whitener = ica._pre_whitener.copy()
     epochs._data[:, 0, 10:15] *= 1e12
-    ica.apply(epochs, copy=True)
+    ica.apply(epochs.copy())
     assert_array_equal(_pre_whitener, ica._pre_whitener)
 
     # test expl. var threshold leading to empty sel
@@ -272,7 +274,7 @@ def test_ica_additional():
     """Test additional ICA functionality"""
     tempdir = _TempDir()
     stop2 = 500
-    raw = Raw(raw_fname).crop(1.5, stop, False)
+    raw = Raw(raw_fname).crop(1.5, stop, copy=False)
     raw.load_data()
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
@@ -311,11 +313,16 @@ def test_ica_additional():
 
     # test corrmap
     ica2 = ica.copy()
+    ica3 = ica.copy()
     corrmap([ica, ica2], (0, 0), threshold='auto', label='blinks', plot=True,
             ch_type="mag")
     corrmap([ica, ica2], (0, 0), threshold=2, plot=False, show=False)
     assert_true(ica.labels_["blinks"] == ica2.labels_["blinks"])
     assert_true(0 in ica.labels_["blinks"])
+    template = _get_ica_map(ica)[0]
+    corrmap([ica, ica3], template, threshold='auto', label='blinks', plot=True,
+            ch_type="mag")
+    assert_true(ica2.labels_["blinks"] == ica3.labels_["blinks"])
     plt.close('all')
 
     # test warnings on bad filenames
@@ -324,7 +331,7 @@ def test_ica_additional():
         ica_badname = op.join(op.dirname(tempdir), 'test-bad-name.fif.gz')
         ica.save(ica_badname)
         read_ica(ica_badname)
-    assert_true(len(w) == 2)
+    assert_naming(w, 'test_ica.py', 2)
 
     # test decim
     ica = ICA(n_components=3, max_pca_components=4,
@@ -535,7 +542,7 @@ def test_ica_additional():
 @requires_sklearn
 def test_run_ica():
     """Test run_ica function"""
-    raw = Raw(raw_fname, preload=True).crop(0, stop, False).crop(1.5)
+    raw = Raw(raw_fname, preload=True).crop(1.5, stop, copy=False)
     params = []
     params += [(None, -1, slice(2), [0, 1])]  # varicance, kurtosis idx
     params += [(None, 'MEG 1531')]  # ECG / EOG channel params
@@ -550,7 +557,7 @@ def test_run_ica():
 @requires_sklearn
 def test_ica_reject_buffer():
     """Test ICA data raw buffer rejection"""
-    raw = Raw(raw_fname).crop(1.5, stop, False)
+    raw = Raw(raw_fname).crop(1.5, stop, copy=False)
     raw.load_data()
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')
@@ -568,7 +575,7 @@ def test_ica_reject_buffer():
 @requires_sklearn
 def test_ica_twice():
     """Test running ICA twice"""
-    raw = Raw(raw_fname).crop(1.5, stop, False)
+    raw = Raw(raw_fname).crop(1.5, stop, copy=False)
     raw.load_data()
     picks = pick_types(raw.info, meg='grad', exclude='bads')
     n_components = 0.9
@@ -587,4 +594,13 @@ def test_ica_twice():
         ica2.fit(raw_new, picks=picks, decim=3)
         assert_equal(ica1.n_components_, ica2.n_components_)
 
+
+ at requires_sklearn
+def test_fit_params():
+    """Test fit_params for ICA"""
+    assert_raises(ValueError, ICA, fit_params=dict(extended=True))
+    fit_params = {}
+    ICA(fit_params=fit_params)  # test no side effects
+    assert_equal(fit_params, {})
+
 run_tests_if_main()
diff --git a/mne/preprocessing/tests/test_maxwell.py b/mne/preprocessing/tests/test_maxwell.py
index 9a6faca..38d9c5c 100644
--- a/mne/preprocessing/tests/test_maxwell.py
+++ b/mne/preprocessing/tests/test_maxwell.py
@@ -13,16 +13,15 @@ from nose.plugins.skip import SkipTest
 from distutils.version import LooseVersion
 
 from mne import compute_raw_covariance, pick_types
+from mne.chpi import read_head_pos, filter_chpi
 from mne.forward import _prep_meg_channels
 from mne.cov import _estimate_rank_meeg_cov
 from mne.datasets import testing
 from mne.io import Raw, proc_history, read_info, read_raw_bti, read_raw_kit
-from mne.preprocessing.maxwell import (maxwell_filter, _get_n_moments,
-                                       _sss_basis_basic, _sh_complex_to_real,
-                                       _sh_real_to_complex, _sh_negate,
-                                       _bases_complex_to_real, _sss_basis,
-                                       _bases_real_to_complex, _sph_harm,
-                                       _get_coil_scale)
+from mne.preprocessing.maxwell import (
+    maxwell_filter, _get_n_moments, _sss_basis_basic, _sh_complex_to_real,
+    _sh_real_to_complex, _sh_negate, _bases_complex_to_real, _trans_sss_basis,
+    _bases_real_to_complex, _sph_harm, _prep_mf_coils)
 from mne.tests.common import assert_meg_snr
 from mne.utils import (_TempDir, run_tests_if_main, slow_test, catch_logging,
                        requires_version, object_diff)
@@ -46,6 +45,9 @@ sss_st1FineCalCrossTalkRegIn_fname = \
     pre + 'st1FineCalCrossTalkRegIn_raw_sss.fif'
 sss_st1FineCalCrossTalkRegInTransSample_fname = \
     pre + 'st1FineCalCrossTalkRegInTransSample_raw_sss.fif'
+sss_movecomp_fname = pre + 'movecomp_raw_sss.fif'
+sss_movecomp_reg_in_fname = pre + 'movecomp_regIn_raw_sss.fif'
+sss_movecomp_reg_in_st4s_fname = pre + 'movecomp_regIn_st4s_raw_sss.fif'
 
 erm_fname = pre + 'erm_raw.fif'
 sss_erm_std_fname = pre + 'erm_devOrigin_raw_sss.fif'
@@ -62,6 +64,8 @@ sss_samp_reg_in_fname = op.join(data_path, 'SSS',
                                 'sample_audvis_trunc_regIn_raw_sss.fif')
 sss_samp_fname = op.join(data_path, 'SSS', 'sample_audvis_trunc_raw_sss.fif')
 
+pos_fname = op.join(data_path, 'SSS', 'test_move_anon_raw.pos')
+
 bases_fname = op.join(sss_path, 'sss_data.mat')
 fine_cal_fname = op.join(sss_path, 'sss_cal_3053.dat')
 fine_cal_fname_3d = op.join(sss_path, 'sss_cal_3053_3d.dat')
@@ -72,6 +76,9 @@ ctc_mgh_fname = op.join(sss_path, 'ct_sparse_mgh.fif')
 sample_fname = op.join(data_path, 'MEG', 'sample',
                        'sample_audvis_trunc_raw.fif')
 
+io_dir = op.join(op.dirname(__file__), '..', '..', 'io')
+fname_ctf_raw = op.join(io_dir, 'tests', 'data', 'test_ctf_comp_raw.fif')
+
 int_order, ext_order = 8, 3
 mf_head_origin = (0., 0., 0.04)
 mf_meg_origin = (0., 0.013, -0.006)
@@ -96,11 +103,85 @@ def _assert_n_free(raw_sss, lower, upper=None):
 
 
 @slow_test
+ at testing.requires_testing_data
+def test_movement_compensation():
+    """Test movement compensation"""
+    temp_dir = _TempDir()
+    lims = (0, 4, False)
+    raw = Raw(raw_fname, allow_maxshield='yes', preload=True).crop(*lims)
+    head_pos = read_head_pos(pos_fname)
+
+    #
+    # Movement compensation, no regularization, no tSSS
+    #
+    raw_sss = maxwell_filter(raw, head_pos=head_pos, origin=mf_head_origin,
+                             regularize=None, bad_condition='ignore')
+    assert_meg_snr(raw_sss, Raw(sss_movecomp_fname).crop(*lims),
+                   4.6, 12.4, chpi_med_tol=58)
+    # IO
+    temp_fname = op.join(temp_dir, 'test_raw_sss.fif')
+    raw_sss.save(temp_fname)
+    raw_sss = Raw(temp_fname)
+    assert_meg_snr(raw_sss, Raw(sss_movecomp_fname).crop(*lims),
+                   4.6, 12.4, chpi_med_tol=58)
+
+    #
+    # Movement compensation,    regularization, no tSSS
+    #
+    raw_sss = maxwell_filter(raw, head_pos=head_pos, origin=mf_head_origin)
+    assert_meg_snr(raw_sss, Raw(sss_movecomp_reg_in_fname).crop(*lims),
+                   0.5, 1.9, chpi_med_tol=121)
+
+    #
+    # Movement compensation,    regularization,    tSSS at the end
+    #
+    raw_nohpi = filter_chpi(raw.copy())
+    with warnings.catch_warnings(record=True) as w:  # untested feature
+        raw_sss_mv = maxwell_filter(raw_nohpi, head_pos=head_pos,
+                                    st_duration=4., origin=mf_head_origin,
+                                    st_fixed=False)
+    assert_equal(len(w), 1)
+    assert_true('is untested' in str(w[0].message))
+    # Neither match is particularly good because our algorithm actually differs
+    assert_meg_snr(raw_sss_mv, Raw(sss_movecomp_reg_in_st4s_fname).crop(*lims),
+                   0.6, 1.3)
+    tSSS_fname = op.join(sss_path, 'test_move_anon_st4s_raw_sss.fif')
+    assert_meg_snr(raw_sss_mv, Raw(tSSS_fname).crop(*lims),
+                   0.6, 1.0, chpi_med_tol=None)
+    assert_meg_snr(Raw(sss_movecomp_reg_in_st4s_fname), Raw(tSSS_fname),
+                   0.8, 1.0, chpi_med_tol=None)
+
+    #
+    # Movement compensation,    regularization,    tSSS at the beginning
+    #
+    raw_sss_mc = maxwell_filter(raw_nohpi, head_pos=head_pos, st_duration=4.,
+                                origin=mf_head_origin)
+    assert_meg_snr(raw_sss_mc, Raw(tSSS_fname).crop(*lims),
+                   0.6, 1.0, chpi_med_tol=None)
+    assert_meg_snr(raw_sss_mc, raw_sss_mv, 0.6, 1.4)
+
+    # some degenerate cases
+    raw_erm = Raw(erm_fname, allow_maxshield='yes')
+    assert_raises(ValueError, maxwell_filter, raw_erm, coord_frame='meg',
+                  head_pos=head_pos)  # can't do ERM file
+    assert_raises(ValueError, maxwell_filter, raw,
+                  head_pos=head_pos[:, :9])  # bad shape
+    assert_raises(TypeError, maxwell_filter, raw, head_pos='foo')  # bad type
+    assert_raises(ValueError, maxwell_filter, raw, head_pos=head_pos[::-1])
+    head_pos_bad = head_pos.copy()
+    head_pos_bad[0, 0] = raw.first_samp / raw.info['sfreq'] - 1e-2
+    assert_raises(ValueError, maxwell_filter, raw, head_pos=head_pos_bad)
+    # make sure numerical error doesn't screw it up, though
+    head_pos_bad[0, 0] = raw.first_samp / raw.info['sfreq'] - 5e-4
+    raw_sss_tweak = maxwell_filter(raw, head_pos=head_pos_bad,
+                                   origin=mf_head_origin)
+    assert_meg_snr(raw_sss_tweak, raw_sss, 2., 10., chpi_med_tol=11)
+
+
+ at slow_test
 def test_other_systems():
     """Test Maxwell filtering on KIT, BTI, and CTF files
     """
-    io_dir = op.join(op.dirname(__file__), '..', '..', 'io')
-
     # KIT
     kit_dir = op.join(io_dir, 'kit', 'tests', 'data')
     sqd_path = op.join(kit_dir, 'test.sqd')
@@ -108,33 +189,38 @@ def test_other_systems():
     elp_path = op.join(kit_dir, 'test_elp.txt')
     hsp_path = op.join(kit_dir, 'test_hsp.txt')
     raw_kit = read_raw_kit(sqd_path, mrk_path, elp_path, hsp_path)
-    assert_raises(RuntimeError, maxwell_filter, raw_kit)
+    with warnings.catch_warnings(record=True):  # head fit
+        assert_raises(RuntimeError, maxwell_filter, raw_kit)
     raw_sss = maxwell_filter(raw_kit, origin=(0., 0., 0.04), ignore_ref=True)
-    _assert_n_free(raw_sss, 65)
+    _assert_n_free(raw_sss, 65, 65)
     # XXX this KIT origin fit is terrible! Eventually we should get a
     # corrected HSP file with proper coverage
-    with catch_logging() as log_file:
-        assert_raises(RuntimeError, maxwell_filter, raw_kit,
-                      ignore_ref=True, regularize=None)  # bad condition
-        raw_sss = maxwell_filter(raw_kit, origin='auto',
-                                 ignore_ref=True, bad_condition='warning',
-                                 verbose='warning')
+    with warnings.catch_warnings(record=True):
+        with catch_logging() as log_file:
+            assert_raises(RuntimeError, maxwell_filter, raw_kit,
+                          ignore_ref=True, regularize=None)  # bad condition
+            raw_sss = maxwell_filter(raw_kit, origin='auto',
+                                     ignore_ref=True, bad_condition='warning',
+                                     verbose='warning')
     log_file = log_file.getvalue()
     assert_true('badly conditioned' in log_file)
     assert_true('more than 20 mm from' in log_file)
-    _assert_n_free(raw_sss, 28, 32)  # bad origin == brutal reg
+    # fits can differ slightly based on scipy version, so be lenient here
+    _assert_n_free(raw_sss, 28, 34)  # bad origin == brutal reg
     # Let's set the origin
-    with catch_logging() as log_file:
-        raw_sss = maxwell_filter(raw_kit, origin=(0., 0., 0.04),
-                                 ignore_ref=True, bad_condition='warning',
-                                 regularize=None, verbose='warning')
+    with warnings.catch_warnings(record=True):
+        with catch_logging() as log_file:
+            raw_sss = maxwell_filter(raw_kit, origin=(0., 0., 0.04),
+                                     ignore_ref=True, bad_condition='warning',
+                                     regularize=None, verbose='warning')
     log_file = log_file.getvalue()
     assert_true('badly conditioned' in log_file)
     _assert_n_free(raw_sss, 80)
     # Now with reg
-    with catch_logging() as log_file:
-        raw_sss = maxwell_filter(raw_kit, origin=(0., 0., 0.04),
-                                 ignore_ref=True, verbose=True)
+    with warnings.catch_warnings(record=True):
+        with catch_logging() as log_file:
+            raw_sss = maxwell_filter(raw_kit, origin=(0., 0., 0.04),
+                                     ignore_ref=True, verbose=True)
     log_file = log_file.getvalue()
     assert_true('badly conditioned' not in log_file)
     _assert_n_free(raw_sss, 65)
@@ -144,12 +230,12 @@ def test_other_systems():
     bti_pdf = op.join(bti_dir, 'test_pdf_linux')
     bti_config = op.join(bti_dir, 'test_config_linux')
     bti_hs = op.join(bti_dir, 'test_hs_linux')
-    raw_bti = read_raw_bti(bti_pdf, bti_config, bti_hs, preload=False)
+    with warnings.catch_warnings(record=True):  # weght table
+        raw_bti = read_raw_bti(bti_pdf, bti_config, bti_hs, preload=False)
     raw_sss = maxwell_filter(raw_bti)
     _assert_n_free(raw_sss, 70)
 
     # CTF
-    fname_ctf_raw = op.join(io_dir, 'tests', 'data', 'test_ctf_comp_raw.fif')
     raw_ctf = Raw(fname_ctf_raw, compensation=2)
     assert_raises(RuntimeError, maxwell_filter, raw_ctf)  # compensated
     raw_ctf = Raw(fname_ctf_raw)
@@ -204,14 +290,15 @@ def test_multipolar_bases():
     from scipy.io import loadmat
     # Test our basis calculations
     info = read_info(raw_fname)
-    coils = _prep_meg_channels(info, accurate=True, elekta_defs=True)[0]
+    coils = _prep_meg_channels(info, accurate=True, elekta_defs=True,
+                               do_es=True)[0]
     # Check against a known benchmark
     sss_data = loadmat(bases_fname)
+    exp = dict(int_order=int_order, ext_order=ext_order)
     for origin in ((0, 0, 0.04), (0, 0.02, 0.02)):
         o_str = ''.join('%d' % (1000 * n) for n in origin)
-
-        S_tot = _sss_basis_basic(origin, coils, int_order, ext_order,
-                                 method='alternative')
+        exp.update(origin=origin)
+        S_tot = _sss_basis_basic(exp, coils, method='alternative')
         # Test our real<->complex conversion functions
         S_tot_complex = _bases_real_to_complex(S_tot, int_order, ext_order)
         S_tot_round = _bases_complex_to_real(S_tot_complex,
@@ -244,9 +331,9 @@ def test_multipolar_bases():
         assert_allclose(S_tot_complex, S_tot_mat, rtol=1e-4, atol=1e-8)
 
         # Now test our optimized version
-        S_tot = _sss_basis_basic(origin, coils, int_order, ext_order)
-        S_tot_fast = _sss_basis(origin, coils, int_order, ext_order)
-        S_tot_fast *= _get_coil_scale(coils)
+        S_tot = _sss_basis_basic(exp, coils)
+        S_tot_fast = _trans_sss_basis(
+            exp, all_coils=_prep_mf_coils(info), trans=info['dev_head_t'])
         # there are some sign differences for columns (order/degrees)
         # in here, likely due to Condon-Shortley. Here we use a
         # Magnetometer channel to figure out the flips because the
@@ -261,10 +348,9 @@ def test_multipolar_bases():
 def test_basic():
     """Test Maxwell filter basic version"""
     # Load testing data (raw, SSS std origin, SSS non-standard origin)
-    with warnings.catch_warnings(record=True):  # maxshield
-        raw = Raw(raw_fname, allow_maxshield=True).crop(0., 1., False)
-        raw_err = Raw(raw_fname, proj=True, allow_maxshield=True)
-        raw_erm = Raw(erm_fname, allow_maxshield=True)
+    raw = Raw(raw_fname, allow_maxshield='yes').crop(0., 1., copy=False)
+    raw_err = Raw(raw_fname, proj=True, allow_maxshield='yes')
+    raw_erm = Raw(erm_fname, allow_maxshield='yes')
     assert_raises(RuntimeError, maxwell_filter, raw_err)
     assert_raises(TypeError, maxwell_filter, 1.)  # not a raw
     assert_raises(ValueError, maxwell_filter, raw, int_order=20)  # too many
@@ -277,8 +363,11 @@ def test_basic():
     assert_equal(_get_n_moments([int_order, ext_order]).sum(), nbases)
 
     # Test SSS computation at the standard head origin
+    assert_equal(len(raw.info['projs']), 12)  # 11 MEG projs + 1 AVG EEG
     raw_sss = maxwell_filter(raw, origin=mf_head_origin, regularize=None,
                              bad_condition='ignore')
+    assert_equal(len(raw_sss.info['projs']), 1)  # avg EEG
+    assert_equal(raw_sss.info['projs'][0]['desc'], 'Average EEG reference')
     assert_meg_snr(raw_sss, Raw(sss_std_fname), 200., 1000.)
     py_cal = raw_sss.info['proc_history'][0]['max_info']['sss_cal']
     assert_equal(len(py_cal), 0)
@@ -332,9 +421,8 @@ def test_maxwell_filter_additional():
 
     raw_fname = op.join(data_path, 'SSS', file_name + '_raw.fif')
 
-    with warnings.catch_warnings(record=True):  # maxshield
-        # Use 2.0 seconds of data to get stable cov. estimate
-        raw = Raw(raw_fname, allow_maxshield=True).crop(0., 2., False)
+    # Use 2.0 seconds of data to get stable cov. estimate
+    raw = Raw(raw_fname, allow_maxshield='yes').crop(0., 2., copy=False)
 
     # Get MEG channels, compute Maxwell filtered data
     raw.load_data()
@@ -370,8 +458,7 @@ def test_maxwell_filter_additional():
 @testing.requires_testing_data
 def test_bads_reconstruction():
     """Test Maxwell filter reconstruction of bad channels"""
-    with warnings.catch_warnings(record=True):  # maxshield
-        raw = Raw(raw_fname, allow_maxshield=True).crop(0., 1., False)
+    raw = Raw(raw_fname, allow_maxshield='yes').crop(0., 1.)
     raw.info['bads'] = bads
     raw_sss = maxwell_filter(raw, origin=mf_head_origin, regularize=None,
                              bad_condition='ignore')
@@ -383,8 +470,7 @@ def test_bads_reconstruction():
 def test_spatiotemporal_maxwell():
     """Test Maxwell filter (tSSS) spatiotemporal processing"""
     # Load raw testing data
-    with warnings.catch_warnings(record=True):  # maxshield
-        raw = Raw(raw_fname, allow_maxshield=True)
+    raw = Raw(raw_fname, allow_maxshield='yes')
 
     # Test that window is less than length of data
     assert_raises(ValueError, maxwell_filter, raw, st_duration=1000.)
@@ -414,7 +500,14 @@ def test_spatiotemporal_maxwell():
         else:
             raw_tsss = maxwell_filter(raw, st_duration=st_duration,
                                       origin=mf_head_origin, regularize=None,
-                                      bad_condition='ignore')
+                                      bad_condition='ignore', verbose=True)
+            raw_tsss_2 = maxwell_filter(raw, st_duration=st_duration,
+                                        origin=mf_head_origin, regularize=None,
+                                        bad_condition='ignore', st_fixed=False,
+                                        verbose=True)
+            assert_meg_snr(raw_tsss, raw_tsss_2, 100., 1000.)
+            assert_equal(raw_tsss.estimate_rank(), 140)
+            assert_equal(raw_tsss_2.estimate_rank(), 140)
         assert_meg_snr(raw_tsss, tsss_bench, tol)
         py_st = raw_tsss.info['proc_history'][0]['max_info']['max_st']
         assert_true(len(py_st) > 0)
@@ -426,20 +519,72 @@ def test_spatiotemporal_maxwell():
                   st_correlation=0.)
 
 
+ at requires_svd_convergence
 @testing.requires_testing_data
-def test_maxwell_filter_fine_calibration():
+def test_spatiotemporal_only():
+    """Test tSSS-only processing"""
+    # Load raw testing data
+    raw = Raw(raw_fname,
+              allow_maxshield='yes').crop(0, 2, copy=False).load_data()
+    picks = pick_types(raw.info, meg='mag', exclude=())
+    power = np.sqrt(np.sum(raw[picks][0] ** 2))
+    # basics
+    raw_tsss = maxwell_filter(raw, st_duration=1., st_only=True)
+    assert_equal(raw_tsss.estimate_rank(), 366)
+    _assert_shielding(raw_tsss, power, 10)
+    # temporal proj will actually reduce spatial DOF with small windows!
+    raw_tsss = maxwell_filter(raw, st_duration=0.1, st_only=True)
+    assert_true(raw_tsss.estimate_rank() < 350)
+    _assert_shielding(raw_tsss, power, 40)
+    # with movement
+    head_pos = read_head_pos(pos_fname)
+    raw_tsss = maxwell_filter(raw, st_duration=1., st_only=True,
+                              head_pos=head_pos)
+    assert_equal(raw_tsss.estimate_rank(), 366)
+    _assert_shielding(raw_tsss, power, 12)
+    with warnings.catch_warnings(record=True):  # st_fixed False
+        raw_tsss = maxwell_filter(raw, st_duration=1., st_only=True,
+                                  head_pos=head_pos, st_fixed=False)
+    assert_equal(raw_tsss.estimate_rank(), 366)
+    _assert_shielding(raw_tsss, power, 12)
+    # should do nothing
+    raw_tsss = maxwell_filter(raw, st_duration=1., st_correlation=1.,
+                              st_only=True)
+    assert_allclose(raw[:][0], raw_tsss[:][0])
+    # degenerate
+    assert_raises(ValueError, maxwell_filter, raw, st_only=True)  # no ST
+    # two-step process equivalent to single-step process
+    raw_tsss = maxwell_filter(raw, st_duration=1., st_only=True)
+    raw_tsss = maxwell_filter(raw_tsss)
+    raw_tsss_2 = maxwell_filter(raw, st_duration=1.)
+    assert_meg_snr(raw_tsss, raw_tsss_2, 1e5)
+    # now also with head movement, and a bad MEG channel
+    assert_equal(len(raw.info['bads']), 0)
+    raw.info['bads'] = ['EEG001', 'MEG2623']
+    raw_tsss = maxwell_filter(raw, st_duration=1., st_only=True,
+                              head_pos=head_pos)
+    assert_equal(raw.info['bads'], ['EEG001', 'MEG2623'])
+    assert_equal(raw_tsss.info['bads'], ['EEG001', 'MEG2623'])  # don't reset
+    raw_tsss = maxwell_filter(raw_tsss, head_pos=head_pos)
+    assert_equal(raw_tsss.info['bads'], ['EEG001'])  # do reset MEG bads
+    raw_tsss_2 = maxwell_filter(raw, st_duration=1., head_pos=head_pos)
+    assert_equal(raw_tsss_2.info['bads'], ['EEG001'])
+    assert_meg_snr(raw_tsss, raw_tsss_2, 1e5)
+
+
+ at testing.requires_testing_data
+def test_fine_calibration():
     """Test Maxwell filter fine calibration"""
 
     # Load testing data (raw, SSS std origin, SSS non-standard origin)
-    with warnings.catch_warnings(record=True):  # maxshield
-        raw = Raw(raw_fname, allow_maxshield=True).crop(0., 1., False)
+    raw = Raw(raw_fname, allow_maxshield='yes').crop(0., 1., copy=False)
     sss_fine_cal = Raw(sss_fine_cal_fname)
 
     # Test 1D SSS fine calibration
     raw_sss = maxwell_filter(raw, calibration=fine_cal_fname,
                              origin=mf_head_origin, regularize=None,
                              bad_condition='ignore')
-    assert_meg_snr(raw_sss, sss_fine_cal, 70, 500)
+    assert_meg_snr(raw_sss, sss_fine_cal, 82, 611)
     py_cal = raw_sss.info['proc_history'][0]['max_info']['sss_cal']
     assert_true(py_cal is not None)
     assert_true(len(py_cal) > 0)
@@ -456,11 +601,14 @@ def test_maxwell_filter_fine_calibration():
                                 origin=mf_head_origin, regularize=None,
                                 bad_condition='ignore')
     assert_meg_snr(raw_sss_3D, sss_fine_cal, 1.0, 6.)
+    raw_ctf = Raw(fname_ctf_raw)
+    assert_raises(RuntimeError, maxwell_filter, raw_ctf, origin=(0., 0., 0.04),
+                  calibration=fine_cal_fname)
 
 
 @slow_test
 @testing.requires_testing_data
-def test_maxwell_filter_regularization():
+def test_regularization():
     """Test Maxwell filter regularization"""
     # Load testing data (raw, SSS std origin, SSS non-standard origin)
     min_tols = (100., 2.6, 1.0)
@@ -472,8 +620,7 @@ def test_maxwell_filter_regularization():
                   sss_samp_reg_in_fname)
     comp_tols = [0, 1, 4]
     for ii, rf in enumerate(raw_fnames):
-        with warnings.catch_warnings(record=True):  # maxshield
-            raw = Raw(rf, allow_maxshield=True).crop(0., 1., False)
+        raw = Raw(rf, allow_maxshield='yes').crop(0., 1.)
         sss_reg_in = Raw(sss_fnames[ii])
 
         # Test "in" regularization
@@ -500,8 +647,7 @@ def test_maxwell_filter_regularization():
 @testing.requires_testing_data
 def test_cross_talk():
     """Test Maxwell filter cross-talk cancellation"""
-    with warnings.catch_warnings(record=True):  # maxshield
-        raw = Raw(raw_fname, allow_maxshield=True).crop(0., 1., False)
+    raw = Raw(raw_fname, allow_maxshield='yes').crop(0., 1., copy=False)
     raw.info['bads'] = bads
     sss_ctc = Raw(sss_ctc_fname)
     raw_sss = maxwell_filter(raw, cross_talk=ctc_fname,
@@ -515,32 +661,54 @@ def test_cross_talk():
     mf_ctc = sss_ctc.info['proc_history'][0]['max_info']['sss_ctc']
     del mf_ctc['block_id']  # we don't write this
     assert_equal(object_diff(py_ctc, mf_ctc), '')
+    raw_ctf = Raw(fname_ctf_raw)
+    assert_raises(ValueError, maxwell_filter, raw_ctf)  # cannot fit headshape
+    raw_sss = maxwell_filter(raw_ctf, origin=(0., 0., 0.04))
+    _assert_n_free(raw_sss, 68)
+    raw_sss = maxwell_filter(raw_ctf, origin=(0., 0., 0.04), ignore_ref=True)
+    _assert_n_free(raw_sss, 70)
+    raw_missing = raw.copy().crop(0, 0.1).load_data().pick_channels(
+        [raw.ch_names[pi] for pi in pick_types(raw.info, meg=True,
+                                               exclude=())[3:]])
+    with warnings.catch_warnings(record=True) as w:
+        maxwell_filter(raw_missing, cross_talk=ctc_fname)
+    assert_equal(len(w), 1)
+    assert_true('Not all cross-talk channels in raw' in str(w[0].message))
+    # MEG channels not in cross-talk
+    assert_raises(RuntimeError, maxwell_filter, raw_ctf, origin=(0., 0., 0.04),
+                  cross_talk=ctc_fname)
 
 
 @testing.requires_testing_data
 def test_head_translation():
     """Test Maxwell filter head translation"""
-    with warnings.catch_warnings(record=True):  # maxshield
-        raw = Raw(raw_fname, allow_maxshield=True).crop(0., 1., False)
+    raw = Raw(raw_fname, allow_maxshield='yes').crop(0., 1., copy=False)
     # First try with an unchanged destination
     raw_sss = maxwell_filter(raw, destination=raw_fname,
                              origin=mf_head_origin, regularize=None,
                              bad_condition='ignore')
-    assert_meg_snr(raw_sss, Raw(sss_std_fname).crop(0., 1., False), 200.)
+    assert_meg_snr(raw_sss, Raw(sss_std_fname).crop(0., 1.), 200.)
     # Now with default
-    with catch_logging() as log:
-        raw_sss = maxwell_filter(raw, destination=mf_head_origin,
-                                 origin=mf_head_origin, regularize=None,
-                                 bad_condition='ignore', verbose='warning')
+    with warnings.catch_warnings(record=True):
+        with catch_logging() as log:
+            raw_sss = maxwell_filter(raw, destination=mf_head_origin,
+                                     origin=mf_head_origin, regularize=None,
+                                     bad_condition='ignore', verbose='warning')
     assert_true('over 25 mm' in log.getvalue())
     assert_meg_snr(raw_sss, Raw(sss_trans_default_fname), 125.)
+    destination = np.eye(4)
+    destination[2, 3] = 0.04
+    assert_allclose(raw_sss.info['dev_head_t']['trans'], destination)
     # Now to sample's head pos
-    with catch_logging() as log:
-        raw_sss = maxwell_filter(raw, destination=sample_fname,
-                                 origin=mf_head_origin, regularize=None,
-                                 bad_condition='ignore', verbose='warning')
+    with warnings.catch_warnings(record=True):
+        with catch_logging() as log:
+            raw_sss = maxwell_filter(raw, destination=sample_fname,
+                                     origin=mf_head_origin, regularize=None,
+                                     bad_condition='ignore', verbose='warning')
     assert_true('= 25.6 mm' in log.getvalue())
     assert_meg_snr(raw_sss, Raw(sss_trans_sample_fname), 350.)
+    assert_allclose(raw_sss.info['dev_head_t']['trans'],
+                    read_info(sample_fname)['dev_head_t']['trans'])
     # Degenerate cases
     assert_raises(RuntimeError, maxwell_filter, raw,
                   destination=mf_head_origin, coord_frame='meg')
@@ -564,12 +732,11 @@ def _assert_shielding(raw_sss, erm_power, shielding_factor, meg='mag'):
 @slow_test
 @requires_svd_convergence
 @testing.requires_testing_data
-def test_noise_rejection():
+def test_shielding_factor():
     """Test Maxwell filter shielding factor using empty room"""
-    with warnings.catch_warnings(record=True):  # maxshield
-        raw_erm = Raw(erm_fname, allow_maxshield=True, preload=True)
+    raw_erm = Raw(erm_fname, allow_maxshield='yes', preload=True)
     picks = pick_types(raw_erm.info, meg='mag')
-    erm_power = raw_erm[picks][0].ravel()
+    erm_power = raw_erm[picks][0]
     erm_power = np.sqrt(np.sum(erm_power * erm_power))
 
     # Vanilla SSS (second value would be for meg=True instead of meg='mag')
@@ -681,13 +848,12 @@ def test_all():
                mf_meg_origin,
                mf_head_origin)
     for ii, rf in enumerate(raw_fnames):
-        with warnings.catch_warnings(record=True):  # maxshield
-            raw = Raw(rf, allow_maxshield=True).crop(0., 1., copy=False)
-        sss_py = maxwell_filter(raw, calibration=fine_cals[ii],
-                                cross_talk=ctcs[ii], st_duration=st_durs[ii],
-                                coord_frame=coord_frames[ii],
-                                destination=destinations[ii],
-                                origin=origins[ii])
+        raw = Raw(rf, allow_maxshield='yes').crop(0., 1.)
+        with warnings.catch_warnings(record=True):  # head fit off-center
+            sss_py = maxwell_filter(
+                raw, calibration=fine_cals[ii], cross_talk=ctcs[ii],
+                st_duration=st_durs[ii], coord_frame=coord_frames[ii],
+                destination=destinations[ii], origin=origins[ii])
         sss_mf = Raw(sss_fnames[ii])
         assert_meg_snr(sss_py, sss_mf, mins[ii], meds[ii], msg=rf)
 
diff --git a/mne/preprocessing/tests/test_ssp.py b/mne/preprocessing/tests/test_ssp.py
index 4974f01..8a6f3a7 100644
--- a/mne/preprocessing/tests/test_ssp.py
+++ b/mne/preprocessing/tests/test_ssp.py
@@ -20,7 +20,7 @@ eog_times = np.array([0.5, 2.3, 3.6, 14.5])
 
 def test_compute_proj_ecg():
     """Test computation of ECG SSP projectors"""
-    raw = Raw(raw_fname).crop(0, 10, False)
+    raw = Raw(raw_fname).crop(0, 10, copy=False)
     raw.load_data()
     for average in [False, True]:
         # For speed, let's not filter here (must also not reject then)
@@ -61,7 +61,7 @@ def test_compute_proj_ecg():
 
 def test_compute_proj_eog():
     """Test computation of EOG SSP projectors"""
-    raw = Raw(raw_fname).crop(0, 10, False)
+    raw = Raw(raw_fname).crop(0, 10, copy=False)
     raw.load_data()
     for average in [False, True]:
         n_projs_init = len(raw.info['projs'])
@@ -100,7 +100,7 @@ def test_compute_proj_eog():
 
 def test_compute_proj_parallel():
     """Test computation of ExG projectors using parallelization"""
-    raw_0 = Raw(raw_fname).crop(0, 10, False)
+    raw_0 = Raw(raw_fname).crop(0, 10, copy=False)
     raw_0.load_data()
     raw = raw_0.copy()
     projs, _ = compute_proj_eog(raw, n_mag=2, n_grad=2, n_eeg=2,
diff --git a/mne/preprocessing/tests/test_xdawn.py b/mne/preprocessing/tests/test_xdawn.py
index 453ead0..6f46134 100644
--- a/mne/preprocessing/tests/test_xdawn.py
+++ b/mne/preprocessing/tests/test_xdawn.py
@@ -21,7 +21,8 @@ event_id = dict(cond2=2, cond3=3)
 
 
 def _get_data():
-    raw = io.Raw(raw_fname, add_eeg_ref=False, verbose=False, preload=True)
+    raw = io.read_raw_fif(raw_fname, add_eeg_ref=False, verbose=False,
+                          preload=True)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg=False, eeg=True, stim=False,
                        ecg=False, eog=False,
diff --git a/mne/preprocessing/xdawn.py b/mne/preprocessing/xdawn.py
index a113e45..2e7ad00 100644
--- a/mne/preprocessing/xdawn.py
+++ b/mne/preprocessing/xdawn.py
@@ -158,7 +158,7 @@ class Xdawn(TransformerMixin, ContainsMixin):
     """Implementation of the Xdawn Algorithm.
 
     Xdawn is a spatial filtering method designed to improve the signal
-    to signal + noise ratio (SSNR) of the ERP responses. Xdawn was originaly
+    to signal + noise ratio (SSNR) of the ERP responses. Xdawn was originally
     designed for P300 evoked potential by enhancing the target response with
     respect to the non-target response. This implementation is a generalization
     to any type of ERP.
@@ -348,11 +348,11 @@ class Xdawn(TransformerMixin, ContainsMixin):
             The kind of event to apply. if None, a dict of inst will be return
             one for each type of event xdawn has been fitted.
         include : array_like of int | None (default None)
-            The indices refering to columns in the ummixing matrix. The
+            The indices referring to columns in the ummixing matrix. The
             components to be kept. If None, the first n_components (as defined
             in the Xdawn constructor) will be kept.
         exclude : array_like of int | None (default None)
-            The indices refering to columns in the ummixing matrix. The
+            The indices referring to columns in the ummixing matrix. The
             components to be zeroed out. If None, all the components except the
             first n_components will be exclude.
 
diff --git a/mne/proj.py b/mne/proj.py
index 0bcbc35..42082c6 100644
--- a/mne/proj.py
+++ b/mne/proj.py
@@ -319,18 +319,19 @@ def sensitivity_map(fwd, projs=None, ch_type='grad', mode='fixed', exclude=[],
     # check forward
     if is_fixed_orient(fwd, orig=True):
         raise ValueError('fwd should must be computed with free orientation')
-    fwd = convert_forward_solution(fwd, surf_ori=True, force_fixed=False,
-                                   verbose=False)
-    if not fwd['surf_ori'] or is_fixed_orient(fwd):
-        raise RuntimeError('Error converting solution, please notify '
-                           'mne-python developers')
 
-    # limit forward
+    # limit forward (this will make a copy of the data for us)
     if ch_type == 'eeg':
         fwd = pick_types_forward(fwd, meg=False, eeg=True, exclude=exclude)
     else:
         fwd = pick_types_forward(fwd, meg=ch_type, eeg=False, exclude=exclude)
 
+    convert_forward_solution(fwd, surf_ori=True, force_fixed=False,
+                             copy=False, verbose=False)
+    if not fwd['surf_ori'] or is_fixed_orient(fwd):
+        raise RuntimeError('Error converting solution, please notify '
+                           'mne-python developers')
+
     gain = fwd['sol']['data']
 
     # Make sure EEG has average
@@ -342,15 +343,18 @@ def sensitivity_map(fwd, projs=None, ch_type='grad', mode='fixed', exclude=[],
         projs = eeg_ave if projs is None else projs + eeg_ave
 
     # Construct the projector
+    residual_types = ['angle', 'remaining', 'dampening']
     if projs is not None:
         proj, ncomp, U = make_projector(projs, fwd['sol']['row_names'],
                                         include_active=True)
         # do projection for most types
-        if mode not in ['angle', 'remaining', 'dampening']:
+        if mode not in residual_types:
             gain = np.dot(proj, gain)
-
+        elif ncomp == 0:
+            raise RuntimeError('No valid projectors found for channel type '
+                               '%s, cannot compute %s' % (ch_type, mode))
     # can only run the last couple methods if there are projectors
-    elif mode in ['angle', 'remaining', 'dampening']:
+    elif mode in residual_types:
         raise ValueError('No projectors used, cannot compute %s' % mode)
 
     n_sensors, n_dipoles = gain.shape
diff --git a/mne/realtime/epochs.py b/mne/realtime/epochs.py
index 8a6efe1..785a8c3 100644
--- a/mne/realtime/epochs.py
+++ b/mne/realtime/epochs.py
@@ -70,8 +70,8 @@ class RtEpochs(_BaseEpochs):
 
             reject = dict(grad=4000e-13, # T / m (gradiometers)
                           mag=4e-12, # T (magnetometers)
-                          eeg=40e-6, # uV (EEG channels)
-                          eog=250e-6 # uV (EOG channels))
+                          eeg=40e-6, # V (EEG channels)
+                          eog=250e-6 # V (EOG channels))
 
     flat : dict | None
         Rejection parameters based on flatness of signal.
diff --git a/mne/realtime/fieldtrip_client.py b/mne/realtime/fieldtrip_client.py
index 6b35cb0..3787da3 100644
--- a/mne/realtime/fieldtrip_client.py
+++ b/mne/realtime/fieldtrip_client.py
@@ -2,18 +2,18 @@
 #
 # License: BSD (3-clause)
 
-import re
 import copy
-import time
+import re
 import threading
-import warnings
+import time
+
 import numpy as np
 
 from ..io import _empty_info
 from ..io.pick import pick_info
 from ..io.constants import FIFF
 from ..epochs import EpochsArray
-from ..utils import logger
+from ..utils import logger, warn
 from ..externals.FieldTrip import Client as FtClient
 
 
@@ -133,18 +133,13 @@ class FieldTripClient(object):
         Creates a minimal Info dictionary required for epoching, averaging
         et al.
         """
-
         if self.info is None:
-
-            warnings.warn('Info dictionary not provided. Trying to guess it '
-                          'from FieldTrip Header object')
+            warn('Info dictionary not provided. Trying to guess it from '
+                 'FieldTrip Header object')
 
             info = _empty_info(self.ft_header.fSample)  # create info
 
             # modify info attributes according to the FieldTrip Header object
-            info['nchan'] = self.ft_header.nChannels
-            info['ch_names'] = self.ft_header.labels
-
             info['comps'] = list()
             info['projs'] = list()
             info['bads'] = list()
@@ -152,7 +147,7 @@ class FieldTripClient(object):
             # channel dictionary list
             info['chs'] = []
 
-            for idx, ch in enumerate(info['ch_names']):
+            for idx, ch in enumerate(self.ft_header.labels):
                 this_info = dict()
 
                 this_info['scanno'] = idx
@@ -202,6 +197,8 @@ class FieldTripClient(object):
                 this_info['unit_mul'] = 0
 
                 info['chs'].append(this_info)
+                info._update_redundant()
+                info._check_consistency()
 
         else:
 
@@ -261,7 +258,7 @@ class FieldTripClient(object):
         # create epoch from data
         info = self.info
         if picks is not None:
-            info = pick_info(info, picks, copy=True)
+            info = pick_info(info, picks)
         epoch = EpochsArray(data[picks][np.newaxis], info, events)
 
         return epoch
diff --git a/mne/realtime/tests/test_mockclient.py b/mne/realtime/tests/test_mockclient.py
index 4dbb860..3e7b158 100644
--- a/mne/realtime/tests/test_mockclient.py
+++ b/mne/realtime/tests/test_mockclient.py
@@ -18,7 +18,7 @@ events = read_events(event_name)
 def test_mockclient():
     """Test the RtMockClient."""
 
-    raw = mne.io.Raw(raw_fname, preload=True, verbose=False)
+    raw = mne.io.read_raw_fif(raw_fname, preload=True, verbose=False)
     picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True,
                            stim=True, exclude=raw.info['bads'])
 
@@ -44,7 +44,7 @@ def test_mockclient():
 def test_get_event_data():
     """Test emulation of realtime data stream."""
 
-    raw = mne.io.Raw(raw_fname, preload=True, verbose=False)
+    raw = mne.io.read_raw_fif(raw_fname, preload=True, verbose=False)
     picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True,
                            stim=True, exclude=raw.info['bads'])
 
@@ -66,7 +66,7 @@ def test_get_event_data():
 def test_find_events():
     """Test find_events in rt_epochs."""
 
-    raw = mne.io.Raw(raw_fname, preload=True, verbose=False)
+    raw = mne.io.read_raw_fif(raw_fname, preload=True, verbose=False)
     picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True,
                            stim=True, exclude=raw.info['bads'])
 
diff --git a/mne/report.py b/mne/report.py
index ee0418f..1c6378f 100644
--- a/mne/report.py
+++ b/mne/report.py
@@ -14,7 +14,6 @@ import re
 import codecs
 import time
 from glob import glob
-import warnings
 import base64
 from datetime import datetime as dt
 
@@ -22,7 +21,7 @@ import numpy as np
 
 from . import read_evokeds, read_events, pick_types, read_cov
 from .io import Raw, read_info
-from .utils import _TempDir, logger, verbose, get_subjects_dir
+from .utils import _TempDir, logger, verbose, get_subjects_dir, warn
 from .viz import plot_events, plot_trans, plot_cov
 from .viz._3d import _plot_mri_contours
 from .forward import read_forward_solution
@@ -59,9 +58,9 @@ def _fig_to_img(function=None, fig=None, image_format='png',
             from mayavi import mlab  # noqa, mlab imported
             import mayavi
         except:  # on some systems importing Mayavi raises SystemExit (!)
-            warnings.warn('Could not import mayavi. Trying to render '
-                          '`mayavi.core.scene.Scene` figure instances'
-                          ' will throw an error.')
+            warn('Could not import mayavi. Trying to render'
+                 '`mayavi.core.scene.Scene` figure instances'
+                 ' will throw an error.')
         tempdir = _TempDir()
         temp_fname = op.join(tempdir, 'test')
         if fig.scene is not None:
@@ -298,7 +297,7 @@ def _iterate_files(report, fnames, info, cov, baseline, sfreq, on_error):
                 report_sectionlabel = None
         except Exception as e:
             if on_error == 'warn':
-                logger.warning('Failed to process file %s:\n"%s"' % (fname, e))
+                warn('Failed to process file %s:\n"%s"' % (fname, e))
             elif on_error == 'raise':
                 raise
             html = None
@@ -1231,8 +1230,8 @@ class Report(object):
             info = read_info(self.info_fname)
             sfreq = info['sfreq']
         else:
-            warnings.warn('`info_fname` not provided. Cannot render'
-                          '-cov.fif(.gz) and -trans.fif(.gz) files.')
+            warn('`info_fname` not provided. Cannot render -cov.fif(.gz) and '
+                 '-trans.fif(.gz) files.')
             info, sfreq = None, None
 
         cov = None
@@ -1269,8 +1268,8 @@ class Report(object):
             self.fnames.append('bem')
             self._sectionlabels.append('mri')
         else:
-            warnings.warn('`subjects_dir` and `subject` not provided.'
-                          ' Cannot render MRI and -trans.fif(.gz) files.')
+            warn('`subjects_dir` and `subject` not provided. Cannot render '
+                 'MRI and -trans.fif(.gz) files.')
 
     def save(self, fname=None, open_browser=True, overwrite=False):
         """Save html report and open it in browser.
@@ -1288,8 +1287,8 @@ class Report(object):
         if fname is None:
             if not hasattr(self, 'data_path'):
                 self.data_path = op.dirname(__file__)
-                warnings.warn('`data_path` not provided. Using %s instead'
-                              % self.data_path)
+                warn('`data_path` not provided. Using %s instead'
+                     % self.data_path)
             fname = op.realpath(op.join(self.data_path, 'report.html'))
         else:
             fname = op.realpath(fname)
@@ -1731,14 +1730,13 @@ class Report(object):
         # Get the MRI filename
         mri_fname = op.join(subjects_dir, subject, 'mri', 'T1.mgz')
         if not op.isfile(mri_fname):
-            warnings.warn('MRI file "%s" does not exist' % mri_fname)
+            warn('MRI file "%s" does not exist' % mri_fname)
 
         # Get the BEM surface filenames
         bem_path = op.join(subjects_dir, subject, 'bem')
 
         if not op.isdir(bem_path):
-            warnings.warn('Subject bem directory "%s" does not exist' %
-                          bem_path)
+            warn('Subject bem directory "%s" does not exist' % bem_path)
             return self._render_image(mri_fname, cmap='gray', n_jobs=n_jobs)
 
         surf_fnames = []
@@ -1747,10 +1745,10 @@ class Report(object):
             if len(surf_fname) > 0:
                 surf_fnames.append(surf_fname[0])
             else:
-                warnings.warn('No surface found for %s.' % surf_name)
+                warn('No surface found for %s.' % surf_name)
                 continue
         if len(surf_fnames) == 0:
-            warnings.warn('No surfaces found at all, rendering empty MRI')
+            warn('No surfaces found at all, rendering empty MRI')
             return self._render_image(mri_fname, cmap='gray')
         # XXX : find a better way to get max range of slices
         nim = nib.load(mri_fname)
diff --git a/mne/selection.py b/mne/selection.py
index cef816a..22d8148 100644
--- a/mne/selection.py
+++ b/mne/selection.py
@@ -6,43 +6,49 @@
 
 from os import path
 
+from .io.meas_info import Info
+from . import pick_types
 from .utils import logger, verbose
-from .externals import six
 
 
 @verbose
-def read_selection(name, fname=None, verbose=None):
+def read_selection(name, fname=None, info=None, verbose=None):
     """Read channel selection from file
 
-    By default, the selections used in mne_browse_raw are supported*.
+    By default, the selections used in ``mne_browse_raw`` are supported.
     Additional selections can be added by specifying a selection file (e.g.
-    produced using mne_browse_raw) using the fname parameter.
+    produced using ``mne_browse_raw``) using the ``fname`` parameter.
 
-    The name parameter can be a string or a list of string. The returned
+    The ``name`` parameter can be a string or a list of string. The returned
     selection will be the combination of all selections in the file where
     (at least) one element in name is a substring of the selection name in
-    the file. For example, "name = ['temporal', 'Right-frontal']" will produce
-    a comination of "Left-temporal", "Right-temporal", and "Right-frontal".
+    the file. For example, ``name=['temporal', 'Right-frontal']`` will produce
+    a combination of ``'Left-temporal'``, ``'Right-temporal'``, and
+    ``'Right-frontal'``.
 
     The included selections are:
 
-        * ``Vertex``
-        * ``Left-temporal``
-        * ``Right-temporal``
-        * ``Left-parietal``
-        * ``Right-parietal``
-        * ``Left-occipital``
-        * ``Right-occipital``
-        * ``Left-frontal``
-        * ``Right-frontal``
+        * ``'Vertex'``
+        * ``'Left-temporal'``
+        * ``'Right-temporal'``
+        * ``'Left-parietal'``
+        * ``'Right-parietal'``
+        * ``'Left-occipital'``
+        * ``'Right-occipital'``
+        * ``'Left-frontal'``
+        * ``'Right-frontal'``
 
 
     Parameters
     ----------
-    name : string or list of string
+    name : str or list of str
         Name of the selection. If is a list, the selections are combined.
-    fname : string
+    fname : str
         Filename of the selection file (if None, built-in selections are used).
+    info : instance of Info
+        Measurement info file, which will be used to determine the spacing
+        of channel names to return, e.g. ``'MEG 0111'`` for old Neuromag
+        systems and ``'MEG0111'`` for new ones.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -53,59 +59,58 @@ def read_selection(name, fname=None, verbose=None):
     """
 
     # convert name to list of string
-    if isinstance(name, tuple):
-        name = list(name)
-
-    if not isinstance(name, list):
+    if not isinstance(name, (list, tuple)):
         name = [name]
+    if isinstance(info, Info):
+        picks = pick_types(info, meg=True, exclude=())
+        if len(picks) > 0 and ' ' not in info['ch_names'][picks[0]]:
+            spacing = 'new'
+        else:
+            spacing = 'old'
+    elif info is not None:
+        raise TypeError('info must be an instance of Info or None, not %s'
+                        % (type(info),))
+    else:  # info is None
+        spacing = 'old'
 
     # use built-in selections by default
     if fname is None:
         fname = path.join(path.dirname(__file__), 'data', 'mne_analyze.sel')
 
-    if not path.exists(fname):
+    if not path.isfile(fname):
         raise ValueError('The file %s does not exist.' % fname)
 
     # use this to make sure we find at least one match for each name
-    name_found = {}
-    for n in name:
-        name_found[n] = False
-
-    fid = open(fname, 'r')
-    sel = []
-
-    for line in fid:
-        line = line.strip()
-
-        # skip blank lines and comments
-        if len(line) == 0 or line[0] == '#':
-            continue
-
-        # get the name of the selection in the file
-        pos = line.find(':')
-        if pos < 0:
-            logger.info('":" delimiter not found in selections file, '
-                        'skipping line')
-            continue
-
-        sel_name_file = line[:pos]
-
-        # search for substring match with name provided
-        for n in name:
-            if sel_name_file.find(n) >= 0:
-                sel.extend(line[pos + 1:].split('|'))
-                name_found[n] = True
-                break
-
-    fid.close()
+    name_found = dict((n, False) for n in name)
+    with open(fname, 'r') as fid:
+        sel = []
+        for line in fid:
+            line = line.strip()
+            # skip blank lines and comments
+            if len(line) == 0 or line[0] == '#':
+                continue
+            # get the name of the selection in the file
+            pos = line.find(':')
+            if pos < 0:
+                logger.info('":" delimiter not found in selections file, '
+                            'skipping line')
+                continue
+            sel_name_file = line[:pos]
+            # search for substring match with name provided
+            for n in name:
+                if sel_name_file.find(n) >= 0:
+                    sel.extend(line[pos + 1:].split('|'))
+                    name_found[n] = True
+                    break
 
     # make sure we found at least one match for each name
-    for n, found in six.iteritems(name_found):
+    for n, found in name_found.items():
         if not found:
             raise ValueError('No match for selection name "%s" found' % n)
 
     # make the selection a sorted list with unique elements
     sel = list(set(sel))
     sel.sort()
-
+    if spacing == 'new':  # "new" or "old" by now, "old" is default
+        sel = [s.replace('MEG ', 'MEG') for s in sel]
     return sel
diff --git a/mne/simulation/evoked.py b/mne/simulation/evoked.py
index bc2d540..a88f448 100644
--- a/mne/simulation/evoked.py
+++ b/mne/simulation/evoked.py
@@ -138,7 +138,7 @@ def add_noise_evoked(evoked, noise, snr, tmin=None, tmax=None):
         An instance of evoked corrupted by noise
     """
     evoked = copy.deepcopy(evoked)
-    tmask = _time_mask(evoked.times, tmin, tmax)
+    tmask = _time_mask(evoked.times, tmin, tmax, sfreq=evoked.info['sfreq'])
     tmp = 10 * np.log10(np.mean((evoked.data[:, tmask] ** 2).ravel()) /
                         np.mean((noise.data ** 2).ravel()))
     noise.data = 10 ** ((tmp - float(snr)) / 20) * noise.data
diff --git a/mne/simulation/raw.py b/mne/simulation/raw.py
index 39742c5..43a4817 100644
--- a/mne/simulation/raw.py
+++ b/mne/simulation/raw.py
@@ -5,10 +5,10 @@
 #
 # License: BSD (3-clause)
 
-import numpy as np
-import warnings
 from copy import deepcopy
 
+import numpy as np
+
 from .evoked import _generate_noise
 from ..event import _get_stim_channel
 from ..io.pick import pick_types, pick_info, pick_channels
@@ -16,16 +16,17 @@ from ..source_estimate import VolSourceEstimate
 from ..cov import make_ad_hoc_cov, read_cov
 from ..bem import fit_sphere_to_headshape, make_sphere_model, read_bem_solution
 from ..io import RawArray, _BaseRaw
-from ..chpi import get_chpi_positions, _get_hpi_info
+from ..chpi import read_head_pos, head_pos_to_trans_rot_t, _get_hpi_info
 from ..io.constants import FIFF
 from ..forward import (_magnetic_dipole_field_vec, _merge_meg_eeg_fwds,
                        _stc_src_sel, convert_forward_solution,
-                       _prepare_for_forward, _prep_meg_channels,
+                       _prepare_for_forward, _transform_orig_meg_coils,
                        _compute_forwards, _to_forward_dict)
 from ..transforms import _get_trans, transform_surface_to
 from ..source_space import _ensure_src, _points_outside_surface
 from ..source_estimate import _BaseSourceEstimate
-from ..utils import logger, verbose, check_random_state
+from ..utils import logger, verbose, check_random_state, warn
+from ..parallel import check_n_jobs
 from ..externals.six import string_types
 
 
@@ -80,16 +81,14 @@ def simulate_raw(raw, stc, trans, src, bem, cov='simple',
         If true, simulate continuous head position indicator information.
         Valid cHPI information must encoded in ``raw.info['hpi_meas']``
         to use this option.
-
-        .. warning:: This feature is currently experimental.
-
-    head_pos : None | str | dict | tuple
+    head_pos : None | str | dict | tuple | array
         Name of the position estimates file. Should be in the format of
         the files produced by maxfilter. If dict, keys should
         be the time points and entries should be 4x4 ``dev_head_t``
         matrices. If None, the original head position (from
         ``info['dev_head_t']``) will be used. If tuple, should have the
-        same format as data returned by `get_chpi_positions`.
+        same format as data returned by `head_pos_to_trans_rot_t`.
+        If array, should be of the form returned by `read_head_pos`.
     mindist : float
         Minimum distance between sources and the inner skull boundary
         to use during forward calculation.
@@ -112,6 +111,10 @@ def simulate_raw(raw, stc, trans, src, bem, cov='simple',
     raw : instance of Raw
         The simulated raw file.
 
+    See Also
+    --------
+    read_head_pos
+
     Notes
     -----
     Events coded with the position number (starting at 1) will be stored
@@ -173,6 +176,7 @@ def simulate_raw(raw, stc, trans, src, bem, cov='simple',
         raise ValueError('stc must have at least three time points')
 
     stim = False if len(pick_types(info, meg=False, stim=True)) == 0 else True
+    n_jobs = check_n_jobs(n_jobs)
 
     rng = check_random_state(random_state)
     if interp not in ('cos2', 'linear', 'zero'):
@@ -185,7 +189,9 @@ def simulate_raw(raw, stc, trans, src, bem, cov='simple',
     # Use position data to simulate head movement
     else:
         if isinstance(head_pos, string_types):
-            head_pos = get_chpi_positions(head_pos, verbose=False)
+            head_pos = read_head_pos(head_pos)
+        if isinstance(head_pos, np.ndarray):
+            head_pos = head_pos_to_trans_rot_t(head_pos)
         if isinstance(head_pos, tuple):  # can be an already-loaded pos file
             transs, rots, ts = head_pos
             ts -= first_samp / info['sfreq']  # MF files need reref
@@ -253,20 +259,18 @@ def simulate_raw(raw, stc, trans, src, bem, cov='simple',
     raw_data = np.zeros((len(info['ch_names']), len(times)))
 
     # figure out our cHPI, ECG, and blink dipoles
-    R, r0 = fit_sphere_to_headshape(info, verbose=False)[:2]
-    R /= 1000.
-    r0 /= 1000.
     ecg_rr = blink_rrs = exg_bem = hpi_rrs = None
     ecg = ecg and len(meg_picks) > 0
     chpi = chpi and len(meg_picks) > 0
     if chpi:
-        hpi_freqs, hpi_rrs, hpi_pick, hpi_on = _get_hpi_info(info)[:4]
+        hpi_freqs, hpi_rrs, hpi_pick, hpi_ons = _get_hpi_info(info)[:4]
         hpi_nns = hpi_rrs / np.sqrt(np.sum(hpi_rrs * hpi_rrs,
                                            axis=1))[:, np.newaxis]
         # turn on cHPI in file
-        raw_data[hpi_pick, :] = hpi_on
+        raw_data[hpi_pick, :] = hpi_ons.sum()
         _log_ch('cHPI status bits enbled and', info, hpi_pick)
     if blink or ecg:
+        R, r0 = fit_sphere_to_headshape(info, units='m', verbose=False)[:2]
         exg_bem = make_sphere_model(r0, head_radius=R,
                                     relative_radii=(0.97, 0.98, 0.99, 1.),
                                     sigmas=(0.33, 1.0, 0.004, 0.33),
@@ -378,8 +382,7 @@ def simulate_raw(raw, stc, trans, src, bem, cov='simple',
             verts = [verts] if isinstance(stc, VolSourceEstimate) else verts
             diff_ = sum([len(v) for v in verts]) - len(src_sel)
             if diff_ != 0:
-                warnings.warn('%s STC vertices omitted due to fwd calculation'
-                              % (diff_,))
+                warn('%s STC vertices omitted due to fwd calculation' % diff_)
         if last_fwd is None:
             last_fwd, last_fwd_blink, last_fwd_ecg, last_fwd_chpi = \
                 fwd, fwd_blink, fwd_ecg, fwd_chpi
@@ -454,7 +457,7 @@ def simulate_raw(raw, stc, trans, src, bem, cov='simple',
         last_fwd, last_fwd_blink, last_fwd_ecg, last_fwd_chpi = \
             fwd, fwd_blink, fwd_ecg, fwd_chpi
     assert used.all()
-    raw = RawArray(raw_data, info, verbose=False)
+    raw = RawArray(raw_data, info, first_samp=first_samp, verbose=False)
     raw.verbose = raw_verbose
     logger.info('Done')
     return raw
@@ -498,10 +501,8 @@ def _iter_forward_solutions(info, trans, src, bem, exg_bem, dev_head_ts,
         # but the cost here is tiny compared to actual fwd calculation
         logger.info('Computing gain matrix for transform #%s/%s'
                     % (ti + 1, len(dev_head_ts)))
-        info = deepcopy(info)
-        info['dev_head_t'] = dev_head_t
-        megcoils, compcoils, megnames, meg_info = \
-            _prep_meg_channels(info, True, [], False, verbose=False)
+        _transform_orig_meg_coils(megcoils, dev_head_t)
+        _transform_orig_meg_coils(compcoils, dev_head_t)
 
         # Make sure our sensors are all outside our BEM
         coil_rr = [coil['r0'] for coil in megcoils]
diff --git a/mne/simulation/source.py b/mne/simulation/source.py
index b4c5c44..20ff6b9 100644
--- a/mne/simulation/source.py
+++ b/mne/simulation/source.py
@@ -8,7 +8,7 @@ import numpy as np
 
 from ..source_estimate import SourceEstimate, VolSourceEstimate
 from ..source_space import _ensure_src
-from ..utils import check_random_state, logger
+from ..utils import check_random_state, warn
 from ..externals.six.moves import zip
 
 
@@ -100,9 +100,9 @@ def simulate_sparse_stc(src, n_dipoles, times,
         datas = data
     else:
         if n_dipoles != len(labels):
-            logger.warning('The number of labels is different from the number '
-                           'of dipoles. %s dipole(s) will be generated.'
-                           % min(n_dipoles, len(labels)))
+            warn('The number of labels is different from the number of '
+                 'dipoles. %s dipole(s) will be generated.'
+                 % min(n_dipoles, len(labels)))
         labels = labels[:n_dipoles] if n_dipoles < len(labels) else labels
 
         vertno = [[], []]
diff --git a/mne/simulation/tests/test_raw.py b/mne/simulation/tests/test_raw.py
index 186ae3e..45f35b1 100644
--- a/mne/simulation/tests/test_raw.py
+++ b/mne/simulation/tests/test_raw.py
@@ -14,13 +14,13 @@ from nose.tools import assert_true, assert_raises
 
 from mne import (read_source_spaces, pick_types, read_trans, read_cov,
                  make_sphere_model, create_info, setup_volume_source_space)
-from mne.chpi import (_calculate_chpi_positions, get_chpi_positions,
-                      _get_hpi_info)
+from mne.chpi import (_calculate_chpi_positions, read_head_pos,
+                      _get_hpi_info, head_pos_to_trans_rot_t)
 from mne.tests.test_chpi import _compare_positions
 from mne.datasets import testing
 from mne.simulation import simulate_sparse_stc, simulate_raw
 from mne.io import Raw, RawArray
-from mne.time_frequency import compute_raw_psd
+from mne.time_frequency import psd_welch
 from mne.utils import _TempDir, run_tests_if_main, requires_version, slow_test
 from mne.fixes import isclose
 
@@ -55,7 +55,7 @@ def _make_stc(raw, src):
 def _get_data():
     """Helper to get some starting data"""
     # raw with ECG channel
-    raw = Raw(raw_fname).crop(0., 5.0).load_data()
+    raw = Raw(raw_fname).crop(0., 5.0, copy=False).load_data()
     data_picks = pick_types(raw.info, meg=True, eeg=True)
     other_picks = pick_types(raw.info, meg=False, stim=True, eog=True)
     picks = np.sort(np.concatenate((data_picks[::16], other_picks)))
@@ -107,8 +107,7 @@ def test_simulate_raw_sphere():
     test_outname = op.join(tempdir, 'sim_test_raw.fif')
     raw_sim.save(test_outname)
 
-    raw_sim_loaded = Raw(test_outname, preload=True, proj=False,
-                         allow_maxshield=True)
+    raw_sim_loaded = Raw(test_outname, preload=True, proj=False)
     assert_allclose(raw_sim_loaded[:][0], raw_sim[:][0], rtol=1e-6, atol=1e-20)
     del raw_sim, raw_sim_2
     # with no cov (no noise) but with artifacts, most time periods should match
@@ -132,13 +131,13 @@ def test_simulate_raw_sphere():
     del raw_sim_3, raw_sim_4
 
     # make sure it works with EEG-only and MEG-only
-    raw_sim_meg = simulate_raw(raw.pick_types(meg=True, eeg=False, copy=True),
+    raw_sim_meg = simulate_raw(raw.copy().pick_types(meg=True, eeg=False),
                                stc, trans, src, sphere, cov=None,
                                ecg=True, blink=True, random_state=seed)
-    raw_sim_eeg = simulate_raw(raw.pick_types(meg=False, eeg=True, copy=True),
+    raw_sim_eeg = simulate_raw(raw.copy().pick_types(meg=False, eeg=True),
                                stc, trans, src, sphere, cov=None,
                                ecg=True, blink=True, random_state=seed)
-    raw_sim_meeg = simulate_raw(raw.pick_types(meg=True, eeg=True, copy=True),
+    raw_sim_meeg = simulate_raw(raw.copy().pick_types(meg=True, eeg=True),
                                 stc, trans, src, sphere, cov=None,
                                 ecg=True, blink=True, random_state=seed)
     assert_allclose(np.concatenate((raw_sim_meg[:][0], raw_sim_eeg[:][0])),
@@ -184,6 +183,10 @@ def test_simulate_raw_sphere():
     head_pos_sim_err[-1.] = head_pos_sim_err[1.]  # negative time
     assert_raises(RuntimeError, simulate_raw, raw, stc, trans, src, sphere,
                   head_pos=head_pos_sim_err)
+    raw_bad = raw.copy()
+    raw_bad.info['dig'] = None
+    assert_raises(RuntimeError, simulate_raw, raw_bad, stc, trans, src, sphere,
+                  blink=True)
 
 
 @testing.requires_testing_data
@@ -212,8 +215,7 @@ def test_simulate_raw_bem():
 @testing.requires_testing_data
 def test_simulate_raw_chpi():
     """Test simulation of raw data with cHPI"""
-    with warnings.catch_warnings(record=True):  # MaxShield
-        raw = Raw(raw_chpi_fname, allow_maxshield=True)
+    raw = Raw(raw_chpi_fname, allow_maxshield='yes')
     sphere = make_sphere_model('auto', 'auto', raw.info)
     # make sparse spherical source space
     sphere_vol = tuple(sphere['r0'] * 1000.) + (sphere.radius * 1000.,)
@@ -225,22 +227,30 @@ def test_simulate_raw_chpi():
     raw_chpi = simulate_raw(raw, stc, None, src, sphere, cov=None, chpi=True,
                             head_pos=pos_fname)
     # test cHPI indication
-    hpi_freqs, _, hpi_pick, hpi_on, _ = _get_hpi_info(raw.info)
+    hpi_freqs, _, hpi_pick, hpi_ons = _get_hpi_info(raw.info)[:4]
     assert_allclose(raw_sim[hpi_pick][0], 0.)
-    assert_allclose(raw_chpi[hpi_pick][0], hpi_on)
+    assert_allclose(raw_chpi[hpi_pick][0], hpi_ons.sum())
     # test that the cHPI signals make some reasonable values
-    psd_sim, freqs_sim = compute_raw_psd(raw_sim)
-    psd_chpi, freqs_chpi = compute_raw_psd(raw_chpi)
-    assert_array_equal(freqs_sim, freqs_chpi)
-    freq_idx = np.sort([np.argmin(np.abs(freqs_sim - f)) for f in hpi_freqs])
     picks_meg = pick_types(raw.info, meg=True, eeg=False)
     picks_eeg = pick_types(raw.info, meg=False, eeg=True)
-    assert_allclose(psd_sim[picks_eeg], psd_chpi[picks_eeg], atol=1e-20)
-    assert_true((psd_chpi[picks_meg][:, freq_idx] >
-                 100 * psd_sim[picks_meg][:, freq_idx]).all())
+
+    for picks in [picks_meg, picks_eeg]:
+        psd_sim, freqs_sim = psd_welch(raw_sim, picks=picks)
+        psd_chpi, freqs_chpi = psd_welch(raw_chpi, picks=picks)
+
+        assert_array_equal(freqs_sim, freqs_chpi)
+        freq_idx = np.sort([np.argmin(np.abs(freqs_sim - f))
+                           for f in hpi_freqs])
+        if picks is picks_meg:
+            assert_true((psd_chpi[:, freq_idx] >
+                         100 * psd_sim[:, freq_idx]).all())
+        else:
+            assert_allclose(psd_sim, psd_chpi, atol=1e-20)
+
     # test localization based on cHPI information
-    trans_sim, rot_sim, t_sim = _calculate_chpi_positions(raw_chpi)
-    trans, rot, t = get_chpi_positions(pos_fname)
+    quats_sim = _calculate_chpi_positions(raw_chpi)
+    trans_sim, rot_sim, t_sim = head_pos_to_trans_rot_t(quats_sim)
+    trans, rot, t = head_pos_to_trans_rot_t(read_head_pos(pos_fname))
     t -= raw.first_samp / raw.info['sfreq']
     _compare_positions((trans, rot, t), (trans_sim, rot_sim, t_sim),
                        max_dist=0.005)
diff --git a/mne/source_estimate.py b/mne/source_estimate.py
index 2d46c77..8429f72 100644
--- a/mne/source_estimate.py
+++ b/mne/source_estimate.py
@@ -5,8 +5,8 @@
 #
 # License: BSD (3-clause)
 
-import os
 import copy
+import os
 from math import ceil
 import warnings
 
@@ -22,10 +22,10 @@ from .surface import (read_surface, _get_ico_surface, read_morph_map,
 from .source_space import (_ensure_src, _get_morph_src_reordering,
                            _ensure_src_subject)
 from .utils import (get_subjects_dir, _check_subject, logger, verbose,
-                    _time_mask)
+                    _time_mask, warn as warn_)
 from .viz import plot_source_estimates
 from .fixes import in1d, sparse_block_diag
-from .io.base import ToDataFrameMixin
+from .io.base import ToDataFrameMixin, TimeMixin
 from .externals.six.moves import zip
 from .externals.six import string_types
 from .externals.h5io import read_hdf5, write_hdf5
@@ -386,7 +386,7 @@ def _verify_source_estimate_compat(a, b):
                          'names, %r and %r' % (a.subject, b.subject))
 
 
-class _BaseSourceEstimate(ToDataFrameMixin, object):
+class _BaseSourceEstimate(ToDataFrameMixin, TimeMixin):
     """Abstract base class for source estimates
 
     Parameters
@@ -470,6 +470,11 @@ class _BaseSourceEstimate(ToDataFrameMixin, object):
         self._update_times()
         self.subject = _check_subject(None, subject, False)
 
+    @property
+    def sfreq(self):
+        """Sample rate of the data"""
+        return 1. / self.tstep
+
     def _remove_kernel_sens_data_(self):
         """Remove kernel and sensor space data and compute self._data
         """
@@ -489,7 +494,7 @@ class _BaseSourceEstimate(ToDataFrameMixin, object):
         tmax : float | None
             The last time point in seconds. If None the last present is used.
         """
-        mask = _time_mask(self.times, tmin, tmax)
+        mask = _time_mask(self.times, tmin, tmax, sfreq=self.sfreq)
         self.tmin = self.times[np.where(mask)[0][0]]
         if self._kernel is not None and self._sens_data is not None:
             self._sens_data = self._sens_data[:, mask]
@@ -500,7 +505,7 @@ class _BaseSourceEstimate(ToDataFrameMixin, object):
         return self  # return self for chaining methods
 
     @verbose
-    def resample(self, sfreq, npad=100, window='boxcar', n_jobs=1,
+    def resample(self, sfreq, npad=None, window='boxcar', n_jobs=1,
                  verbose=None):
         """Resample data
 
@@ -508,8 +513,10 @@ class _BaseSourceEstimate(ToDataFrameMixin, object):
         ----------
         sfreq : float
             New sample rate to use.
-        npad : int
+        npad : int | str
             Amount to pad the start and end of the data.
+            Can also be "auto" to use a padding that will result in
+            a power-of-two size (can be much faster).
         window : string or tuple
             Window to use in resampling. See scipy.signal.resample.
         n_jobs : int
@@ -525,6 +532,11 @@ class _BaseSourceEstimate(ToDataFrameMixin, object):
 
         Note that the sample rate of the original data is inferred from tstep.
         """
+        if npad is None:
+            npad = 100
+            warn_('npad is currently taken to be 100, but will be changed to '
+                  '"auto" in 0.12. Please set the value explicitly.',
+                  DeprecationWarning)
         # resampling in sensor instead of source space gives a somewhat
         # different result, so we don't allow it
         self._remove_kernel_sens_data_()
@@ -770,8 +782,8 @@ class _BaseSourceEstimate(ToDataFrameMixin, object):
 
         if self._kernel is None and self._sens_data is None:
             if self._kernel_removed:
-                warnings.warn('Performance can be improved by not accessing '
-                              'the data attribute before calling this method.')
+                warn_('Performance can be improved by not accessing the data '
+                      'attribute before calling this method.')
 
             # transform source space data directly
             data_t = func(self.data[idx, tmin_idx:tmax_idx])
@@ -851,8 +863,8 @@ class _BaseSourceEstimate(ToDataFrameMixin, object):
         """
 
         # min and max data indices to include
-        times = np.round(1000 * self.times)
-        t_idx = np.where(_time_mask(times, tmin, tmax))[0]
+        times = 1000. * self.times
+        t_idx = np.where(_time_mask(times, tmin, tmax, sfreq=self.sfreq))[0]
         if tmin is None:
             tmin_idx = None
         else:
@@ -1949,9 +1961,9 @@ def _morph_buffer(data, idx_use, e, smooth, n_vertices, nearest, maps,
     else:
         data[idx_use, :] /= data_sum[idx_use][:, None]
     if len(idx_use) != len(data_sum) and warn:
-        warnings.warn('%s/%s vertices not included in smoothing, consider '
-                      'increasing the number of steps'
-                      % (len(data_sum) - len(idx_use), len(data_sum)))
+        warn_('%s/%s vertices not included in smoothing, consider increasing '
+              'the number of steps'
+              % (len(data_sum) - len(idx_use), len(data_sum)))
 
     logger.info('    %d smooth iterations done.' % (k + 1))
     data_morphed = maps[nearest, :] * data
@@ -2359,11 +2371,10 @@ def spatio_temporal_src_connectivity(src, n_times, dist=None, verbose=None):
         masks = np.concatenate(masks)
         missing = 100 * float(len(masks) - np.sum(masks)) / len(masks)
         if missing:
-            warnings.warn('%0.1f%% of original source space vertices have been'
-                          ' omitted, tri-based connectivity will have holes.\n'
-                          'Consider using distance-based connectivity or '
-                          'morphing data to all source space vertices.'
-                          % missing)
+            warn_('%0.1f%% of original source space vertices have been'
+                  ' omitted, tri-based connectivity will have holes.\n'
+                  'Consider using distance-based connectivity or '
+                  'morphing data to all source space vertices.' % missing)
             masks = np.tile(masks, n_times)
             masks = np.where(masks)[0]
             connectivity = connectivity.tocsr()
@@ -2741,13 +2752,12 @@ def _gen_extract_label_time_course(stcs, labels, src, mode='mean',
             if not allow_empty:
                 raise ValueError(msg)
             else:
-                logger.warning(msg + '. Assigning all-zero time series to '
-                               'label.')
+                warn_(msg + '. Assigning all-zero time series to label.')
             this_vertidx = None  # to later check if label is empty
 
         label_vertidx.append(this_vertidx)
 
-    # mode-dependent initalization
+    # mode-dependent initialization
     if mode == 'mean':
         pass  # we have this here to catch invalid values for mode
     elif mode == 'mean_flip':
diff --git a/mne/source_space.py b/mne/source_space.py
index 1f70d57..60ad8fe 100644
--- a/mne/source_space.py
+++ b/mne/source_space.py
@@ -26,12 +26,12 @@ from .surface import (read_surface, _create_surf_spacing, _get_ico_surface,
                       _triangle_neighbors)
 from .utils import (get_subjects_dir, run_subprocess, has_freesurfer,
                     has_nibabel, check_fname, logger, verbose,
-                    check_version, _get_call_line)
+                    check_version, _get_call_line, warn)
 from .fixes import in1d, partial, gzip_open, meshgrid
 from .parallel import parallel_func, check_n_jobs
 from .transforms import (invert_transform, apply_trans, _print_coord_trans,
                          combine_transforms, _get_trans,
-                         _coord_frame_name, Transform)
+                         _coord_frame_name, Transform, _str_to_frame)
 from .externals.six import string_types
 
 
@@ -316,9 +316,8 @@ class SourceSpaces(list):
                                        iz_orig != iz_clip)).any(0).sum()
                     # generate use warnings for clipping
                     if n_diff > 0:
-                        logger.warning('%s surface vertices lay outside '
-                                       'of volume space. Consider using a '
-                                       'larger volume space.' % n_diff)
+                        warn('%s surface vertices lay outside of volume space.'
+                             ' Consider using a larger volume space.' % n_diff)
                     # get surface id or use default value
                     i = _get_lut_id(lut, surf_names[i], use_lut)
                     # update image to include surface voxels
@@ -344,9 +343,9 @@ class SourceSpaces(list):
                                        iz_orig != iz_clip)).any(0).sum()
                     # generate use warnings for clipping
                     if n_diff > 0:
-                        logger.warning('%s discrete vertices lay outside '
-                                       'of volume space. Consider using a '
-                                       'larger volume space.' % n_diff)
+                        warn('%s discrete vertices lay outside of volume '
+                             'space. Consider using a larger volume space.'
+                             % n_diff)
                     # set default value
                     img[ix_clip, iy_clip, iz_clip] = 1
                     if use_lut:
@@ -1128,9 +1127,7 @@ def _read_talxfm(subject, subjects_dir, mode=None, verbose=None):
     if use_nibabel is True and mode == 'freesurfer':
         use_nibabel = False
     if use_nibabel:
-        import nibabel as nib
-        img = nib.load(path)
-        hdr = img.get_header()
+        hdr = _get_mri_header(path)
         # read the MRI_VOXEL to RAS transform
         n_orig = hdr.get_vox2ras()
         # read the MRI_VOXEL to MRI transform
@@ -1550,19 +1547,28 @@ def _make_voxel_ras_trans(move, ras, voxel_size):
     return t
 
 
-def _make_discrete_source_space(pos):
+def _make_discrete_source_space(pos, coord_frame='mri'):
     """Use a discrete set of source locs/oris to make src space
 
     Parameters
     ----------
     pos : dict
         Must have entries "rr" and "nn". Data should be in meters.
+    coord_frame : str
+        The coordinate frame in which the positions are given; default: 'mri'.
+        The frame must be one defined in transforms.py:_str_to_frame
 
     Returns
     -------
     src : dict
         The source space.
     """
+    # Check that coordinate frame is valid
+    if coord_frame not in _str_to_frame:  # will fail if coord_frame not string
+        raise KeyError('coord_frame must be one of %s, not "%s"'
+                       % (list(_str_to_frame.keys()), coord_frame))
+    coord_frame = _str_to_frame[coord_frame]  # now an int
+
     # process points
     rr = pos['rr'].copy()
     nn = pos['nn'].copy()
@@ -1579,7 +1585,6 @@ def _make_discrete_source_space(pos):
     logger.info('%d sources' % npts)
 
     # Ready to make the source space
-    coord_frame = FIFF.FIFFV_COORD_MRI
     sp = dict(coord_frame=coord_frame, type='discrete', nuse=npts, np=npts,
               inuse=np.ones(npts, int), vertno=np.arange(npts), rr=rr, nn=nn,
               id=-1)
@@ -1802,6 +1807,16 @@ def _vol_vertex(width, height, jj, kk, pp):
     return jj + width * kk + pp * (width * height)
 
 
+def _get_mri_header(fname):
+    """Get MRI header using nibabel"""
+    import nibabel as nib
+    img = nib.load(fname)
+    try:
+        return img.header
+    except AttributeError:  # old nibabel
+        return img.get_header()
+
+
 def _get_mgz_header(fname):
     """Adapted from nibabel to quickly extract header info"""
     if not fname.endswith('.mgz'):
@@ -2023,6 +2038,14 @@ def _filter_source_spaces(surf, limit, mri_head_t, src, n_jobs=1,
             extras += [limit]
             logger.info('%d source space point%s omitted because of the '
                         '%6.1f-mm distance limit.' % tuple(extras))
+        # Adjust the patch inds as well if necessary
+        if omit + omit_outside > 0 and s.get('patch_inds') is not None:
+            if s['nearest'] is None:
+                # This shouldn't happen, but if it does, we can probably come
+                # up with a more clever solution
+                raise RuntimeError('Cannot adjust patch information properly, '
+                                   'please contact the mne-python developers')
+            _add_patch_info(s)
     logger.info('Thank you for waiting.')
 
 
@@ -2479,7 +2502,8 @@ def _get_morph_src_reordering(vertices, src_from, subject_from, subject_to,
     return data_idx, from_vertices
 
 
-def _compare_source_spaces(src0, src1, mode='exact', dist_tol=1.5e-3):
+def _compare_source_spaces(src0, src1, mode='exact', nearest=True,
+                           dist_tol=1.5e-3):
     """Compare two source spaces
 
     Note: this function is also used by forward/tests/test_make_forward.py
@@ -2518,27 +2542,29 @@ def _compare_source_spaces(src0, src1, mode='exact', dist_tol=1.5e-3):
         for name in ['seg_name']:
             if name in s0 or name in s1:
                 assert_equal(s0[name], s1[name], name)
-        if mode == 'exact':
-            for name in ['inuse', 'vertno', 'use_tris']:
-                assert_array_equal(s0[name], s1[name], err_msg=name)
-            # these fields will exist if patch info was added, these are
-            # not tested in mode == 'approx'
-            for name in ['nearest', 'nearest_dist']:
+        # these fields will exist if patch info was added
+        if nearest:
+            for name in ['nearest', 'nearest_dist', 'patch_inds']:
                 if s0[name] is None:
                     assert_true(s1[name] is None, name)
                 else:
                     assert_array_equal(s0[name], s1[name])
+            for name in ['pinfo']:
+                if s0[name] is None:
+                    assert_true(s1[name] is None, name)
+                else:
+                    assert_true(len(s0[name]) == len(s1[name]), name)
+                    for p1, p2 in zip(s0[name], s1[name]):
+                        assert_true(all(p1 == p2), name)
+        if mode == 'exact':
+            for name in ['inuse', 'vertno', 'use_tris']:
+                assert_array_equal(s0[name], s1[name], err_msg=name)
             for name in ['dist_limit']:
                 assert_true(s0[name] == s1[name], name)
             for name in ['dist']:
                 if s0[name] is not None:
                     assert_equal(s1[name].shape, s0[name].shape)
                     assert_true(len((s0['dist'] - s1['dist']).data) == 0)
-            for name in ['pinfo']:
-                if s0[name] is not None:
-                    assert_true(len(s0[name]) == len(s1[name]))
-                    for p1, p2 in zip(s0[name], s1[name]):
-                        assert_true(all(p1 == p2))
         else:  # 'approx' in mode:
             # deal with vertno, inuse, and use_tris carefully
             assert_array_equal(s0['vertno'], np.where(s0['inuse'])[0],
diff --git a/mne/stats/cluster_level.py b/mne/stats/cluster_level.py
index d0b1ec6..620dbea 100755
--- a/mne/stats/cluster_level.py
+++ b/mne/stats/cluster_level.py
@@ -9,14 +9,14 @@
 #
 # License: Simplified BSD
 
-import numpy as np
-import warnings
 import logging
+
+import numpy as np
 from scipy import sparse
 
 from .parametric import f_oneway
 from ..parallel import parallel_func, check_n_jobs
-from ..utils import split_list, logger, verbose, ProgressBar
+from ..utils import split_list, logger, verbose, ProgressBar, warn
 from ..fixes import in1d, unravel_index
 from ..source_estimate import SourceEstimate
 
@@ -325,11 +325,9 @@ def _find_clusters(x, threshold, tail=0, connectivity=None, max_step=1,
         e_power = threshold.get('e_power', 0.5)
         if show_info is True:
             if len(thresholds) == 0:
-                txt = ('threshold["start"] (%s) is more extreme than '
-                       'data statistics with most extreme value %s'
-                       % (threshold['start'], stop))
-                logger.warning(txt)
-                warnings.warn(txt)
+                warn('threshold["start"] (%s) is more extreme than data '
+                     'statistics with most extreme value %s'
+                     % (threshold['start'], stop))
             else:
                 logger.info('Using %d thresholds from %0.2f to %0.2f for TFCE '
                             'computation (h_power=%0.2f, e_power=%0.2f)'
@@ -723,8 +721,8 @@ def _permutation_cluster_test(X, threshold, n_permutations, tail, stat_fun,
                 stat_fun(*[x[:, pos: pos + buffer_size] for x in X])
 
         if not np.alltrue(T_obs == T_obs_buffer):
-            logger.warning('Provided stat_fun does not treat variables '
-                           'independently. Setting buffer_size to None.')
+            warn('Provided stat_fun does not treat variables independently. '
+                 'Setting buffer_size to None.')
             buffer_size = None
 
     # The stat should have the same shape as the samples for no conn.
@@ -741,7 +739,7 @@ def _permutation_cluster_test(X, threshold, n_permutations, tail, stat_fun,
         partitions = _get_partitions_from_connectivity(connectivity, n_times)
     else:
         partitions = None
-    logger.info('Running intial clustering')
+    logger.info('Running initial clustering')
     out = _find_clusters(T_obs, threshold, tail, connectivity,
                          max_step=max_step, include=include,
                          partitions=partitions, t_power=t_power,
diff --git a/mne/stats/parametric.py b/mne/stats/parametric.py
index e42db60..37a8e5b 100644
--- a/mne/stats/parametric.py
+++ b/mne/stats/parametric.py
@@ -302,7 +302,7 @@ def f_mway_rm(data, factor_levels, effects='all', alpha=0.05,
     n_obs = data.shape[2]
     n_replications = data.shape[0]
 
-    # pute last axis in fornt to 'iterate' over mass univariate instances.
+    # put last axis in front to 'iterate' over mass univariate instances.
     data = np.rollaxis(data, 2)
     fvalues, pvalues = [], []
     for c_, df1, df2 in _iter_contrasts(n_replications, factor_levels,
@@ -323,7 +323,10 @@ def f_mway_rm(data, factor_levels, effects='all', alpha=0.05,
 
         df1, df2 = np.zeros(n_obs) + df1, np.zeros(n_obs) + df2
         if correction:
-            df1, df2 = [d[None, :] * eps for d in (df1, df2)]
+            # numerical imprecision can cause eps=0.99999999999999989
+            # even with a single category, so never let our degrees of
+            # freedom drop below 1.
+            df1, df2 = [np.maximum(d[None, :] * eps, 1.) for d in (df1, df2)]
 
         if return_pvals:
             pvals = f(df1, df2).sf(fvals)
diff --git a/mne/stats/regression.py b/mne/stats/regression.py
index e314458..e747250 100644
--- a/mne/stats/regression.py
+++ b/mne/stats/regression.py
@@ -6,18 +6,17 @@
 #
 # License: BSD (3-clause)
 
-from collections import namedtuple
 from inspect import isgenerator
-import warnings
-from ..externals.six import string_types
+from collections import namedtuple
 
 import numpy as np
 from scipy import linalg, sparse
 
+from ..externals.six import string_types
 from ..source_estimate import SourceEstimate
 from ..epochs import _BaseEpochs
 from ..evoked import Evoked, EvokedArray
-from ..utils import logger, _reject_data_segments, _get_fast_dot
+from ..utils import logger, _reject_data_segments, warn
 from ..io.pick import pick_types, pick_info
 from ..fixes import in1d
 
@@ -67,8 +66,8 @@ def linear_regression(inst, design_matrix, names=None):
                            stim=False, eog=False, ecg=False,
                            emg=False, exclude=['bads'])
         if [inst.ch_names[p] for p in picks] != inst.ch_names:
-            warnings.warn('Fitting linear model to non-data or bad '
-                          'channels. Check picking', UserWarning)
+            warn('Fitting linear model to non-data or bad channels. '
+                 'Check picking')
         msg = 'Fitting linear model to epochs'
         data = inst.get_data()
         out = EvokedArray(np.zeros(data.shape[1:]), inst.info, inst.tmin)
@@ -156,7 +155,7 @@ def _fit_lm(data, design_matrix, names):
 
 def linear_regression_raw(raw, events, event_id=None, tmin=-.1, tmax=1,
                           covariates=None, reject=None, flat=None, tstep=1.,
-                          decim=1, picks=None, solver='pinv'):
+                          decim=1, picks=None, solver='cholesky'):
     """Estimate regression-based evoked potentials/fields by linear modelling
 
     This models the full M/EEG time course, including correction for
@@ -208,8 +207,8 @@ def linear_regression_raw(raw, events, event_id=None, tmin=-.1, tmax=1,
 
             reject = dict(grad=4000e-12, # T / m (gradiometers)
                           mag=4e-11, # T (magnetometers)
-                          eeg=40e-5, # uV (EEG channels)
-                          eog=250e-5 # uV (EOG channels))
+                          eeg=40e-5, # V (EEG channels)
+                          eog=250e-5 # V (EOG channels))
 
     flat : None | dict
         or cleaning raw data before the regression is performed: set up
@@ -229,8 +228,8 @@ def linear_regression_raw(raw, events, event_id=None, tmin=-.1, tmax=1,
     solver : str | function
         Either a function which takes as its inputs the sparse predictor
         matrix X and the observation matrix Y, and returns the coefficient
-        matrix b; or a string (for now, only 'pinv'), in which case the
-        solver used is dot(scipy.linalg.pinv(dot(X.T, X)), dot(X.T, Y.T)).T.
+        matrix b; or a string. If str, must be ``'cholesky'``, in which case
+        the solver used is ``linalg.solve(dot(X.T, X), dot(X.T, y))``.
 
     Returns
     -------
@@ -247,44 +246,84 @@ def linear_regression_raw(raw, events, event_id=None, tmin=-.1, tmax=1,
     """
 
     if isinstance(solver, string_types):
-        if solver == 'pinv':
-            fast_dot = _get_fast_dot()
+        if solver == 'cholesky':
+            def solver(X, y):
+                a = (X.T * X).toarray()  # dot product of sparse matrices
+                return linalg.solve(a, X.T * y.T, sym_pos=True,
+                                    overwrite_a=True, overwrite_b=True).T
 
-            # inv is slightly (~10%) faster, but pinv seemingly more stable
-            def solver(X, Y):
-                return fast_dot(linalg.pinv(X.T.dot(X).todense()),
-                                X.T.dot(Y.T)).T
         else:
             raise ValueError("No such solver: {0}".format(solver))
 
-    # prepare raw and events
+    # build data
+    data, info, events = _prepare_rerp_data(raw, events, picks=picks,
+                                            decim=decim)
+
+    # build predictors
+    X, conds, cond_length, tmin_s, tmax_s = _prepare_rerp_preds(
+        n_samples=data.shape[1], sfreq=info["sfreq"], events=events,
+        event_id=event_id, tmin=tmin, tmax=tmax, covariates=covariates)
+
+    # remove "empty" and contaminated data points
+    X, data = _clean_rerp_input(X, data, reject, flat, decim, info, tstep)
+
+    # solve linear system
+    coefs = solver(X, data)
+
+    # construct Evoked objects to be returned from output
+    evokeds = _make_evokeds(coefs, conds, cond_length, tmin_s, tmax_s, info)
+
+    return evokeds
+
+
+def _prepare_rerp_data(raw, events, picks=None, decim=1):
+    """Prepare events and data, primarily for `linear_regression_raw`. See
+    there for an explanation of parameters and output."""
     if picks is None:
         picks = pick_types(raw.info, meg=True, eeg=True, ref_meg=True)
-    info = pick_info(raw.info, picks, copy=True)
+    info = pick_info(raw.info, picks)
     decim = int(decim)
     info["sfreq"] /= decim
     data, times = raw[:]
     data = data[picks, ::decim]
-    times = times[::decim]
+    if len(set(events[:, 0])) < len(events[:, 0]):
+        raise ValueError("`events` contains duplicate time points. Make "
+                         "sure all entries in the first column of `events` "
+                         "are unique.")
+
     events = events.copy()
     events[:, 0] -= raw.first_samp
     events[:, 0] //= decim
+    if len(set(events[:, 0])) < len(events[:, 0]):
+        raise ValueError("After decimating, `events` contains duplicate time "
+                         "points. This means some events are too closely "
+                         "spaced for the requested decimation factor. Choose "
+                         "different events, drop close events, or choose a "
+                         "different decimation factor.")
+
+    return data, info, events
+
 
+def _prepare_rerp_preds(n_samples, sfreq, events, event_id=None, tmin=-.1,
+                        tmax=1, covariates=None):
+    """Build predictor matrix as well as metadata (e.g. condition time
+    windows), primarily for `linear_regression_raw`. See there for
+    an explanation of parameters and output."""
     conds = list(event_id)
     if covariates is not None:
         conds += list(covariates)
 
     # time windows (per event type) are converted to sample points from times
     if isinstance(tmin, (float, int)):
-        tmin_s = dict((cond, int(tmin * info["sfreq"])) for cond in conds)
+        tmin_s = dict((cond, int(tmin * sfreq)) for cond in conds)
     else:
-        tmin_s = dict((cond, int(tmin.get(cond, -.1) * info["sfreq"]))
+        tmin_s = dict((cond, int(tmin.get(cond, -.1) * sfreq))
                       for cond in conds)
     if isinstance(tmax, (float, int)):
         tmax_s = dict(
-            (cond, int((tmax * info["sfreq"]) + 1.)) for cond in conds)
+            (cond, int((tmax * sfreq) + 1.)) for cond in conds)
     else:
-        tmax_s = dict((cond, int((tmax.get(cond, 1.) * info["sfreq"]) + 1))
+        tmax_s = dict((cond, int((tmax.get(cond, 1.) * sfreq) + 1))
                       for cond in conds)
 
     # Construct predictor matrix
@@ -318,34 +357,38 @@ def linear_regression_raw(raw, events, event_id=None, tmin=-.1, tmax=1,
 
         cond_length[cond] = len(onsets)
         xs.append(sparse.dia_matrix((values, onsets),
-                                    shape=(data.shape[1], n_lags)))
+                                    shape=(n_samples, n_lags)))
 
-    X = sparse.hstack(xs)
+    return sparse.hstack(xs), conds, cond_length, tmin_s, tmax_s
 
+
+def _clean_rerp_input(X, data, reject, flat, decim, info, tstep):
+    """Remove empty and contaminated points from data and predictor matrices,
+    for `linear_regression_raw`. See there for an explanation of parameters."""
     # find only those positions where at least one predictor isn't 0
     has_val = np.unique(X.nonzero()[0])
 
-    # additionally, reject positions based on extreme steps in the data
+    # reject positions based on extreme steps in the data
     if reject is not None:
         _, inds = _reject_data_segments(data, reject, flat, decim=None,
                                         info=info, tstep=tstep)
         for t0, t1 in inds:
             has_val = np.setdiff1d(has_val, range(t0, t1))
 
-    # solve linear system
-    X, data = X.tocsr()[has_val], data[:, has_val]
-    coefs = solver(X, data)
+    return X.tocsr()[has_val], data[:, has_val]
 
-    # construct Evoked objects to be returned from output
+
+def _make_evokeds(coefs, conds, cond_length, tmin_s, tmax_s, info):
+    """Create a dictionary of Evoked objects from a coefs matrix and condition
+    durations, primarily for `linear_regression_raw`. See there for an
+    explanation of parameters and output."""
     evokeds = dict()
-    cum = 0
+    cumul = 0
     for cond in conds:
         tmin_, tmax_ = tmin_s[cond], tmax_s[cond]
-        evokeds[cond] = EvokedArray(coefs[:, cum:cum + tmax_ - tmin_],
-                                    info=info, comment=cond,
-                                    tmin=tmin_ / float(info["sfreq"]),
-                                    nave=cond_length[cond],
-                                    kind='mean')  # note that nave and kind are
-        cum += tmax_ - tmin_                      # technically not correct
-
+        evokeds[cond] = EvokedArray(
+            coefs[:, cumul:cumul + tmax_ - tmin_], info=info, comment=cond,
+            tmin=tmin_ / float(info["sfreq"]), nave=cond_length[cond],
+            kind='average')  # nave and kind are technically incorrect
+        cumul += tmax_ - tmin_
     return evokeds
diff --git a/mne/stats/tests/test_cluster_level.py b/mne/stats/tests/test_cluster_level.py
index a19b57c..6fb3cda 100644
--- a/mne/stats/tests/test_cluster_level.py
+++ b/mne/stats/tests/test_cluster_level.py
@@ -62,9 +62,10 @@ def test_cache_dir():
             # ensure that non-independence yields warning
             stat_fun = partial(ttest_1samp_no_p, sigma=1e-3)
             assert_true('independently' not in log_file.getvalue())
-            permutation_cluster_1samp_test(
-                X, buffer_size=10, n_jobs=2, n_permutations=1,
-                seed=0, stat_fun=stat_fun, verbose=False)
+            with warnings.catch_warnings(record=True):  # independently
+                permutation_cluster_1samp_test(
+                    X, buffer_size=10, n_jobs=2, n_permutations=1,
+                    seed=0, stat_fun=stat_fun, verbose=False)
             assert_true('independently' in log_file.getvalue())
     finally:
         if orig_dir is not None:
@@ -173,12 +174,12 @@ def test_cluster_permutation_t_test():
 
             # test with 2 jobs and buffer_size enabled
             buffer_size = condition1.shape[1] // 10
-            T_obs_neg_buff, _, cluster_p_values_neg_buff, _ = \
-                permutation_cluster_1samp_test(-condition1, n_permutations=100,
-                                               tail=-1, threshold=-1.67,
-                                               seed=1, n_jobs=2,
-                                               stat_fun=stat_fun,
-                                               buffer_size=buffer_size)
+            with warnings.catch_warnings(record=True):  # independently
+                T_obs_neg_buff, _, cluster_p_values_neg_buff, _ = \
+                    permutation_cluster_1samp_test(
+                        -condition1, n_permutations=100, tail=-1,
+                        threshold=-1.67, seed=1, n_jobs=2, stat_fun=stat_fun,
+                        buffer_size=buffer_size)
 
             assert_array_equal(T_obs_neg, T_obs_neg_buff)
             assert_array_equal(cluster_p_values_neg, cluster_p_values_neg_buff)
diff --git a/mne/stats/tests/test_regression.py b/mne/stats/tests/test_regression.py
index 95a2a33..d9bafce 100644
--- a/mne/stats/tests/test_regression.py
+++ b/mne/stats/tests/test_regression.py
@@ -37,7 +37,7 @@ def test_regression():
     event_id = dict(aud_l=1, aud_r=2)
 
     # Setup for reading the raw data
-    raw = mne.io.Raw(raw_fname)
+    raw = mne.io.read_raw_fif(raw_fname)
     events = mne.read_events(event_fname)[:10]
     epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
                         baseline=(None, 0))
@@ -51,7 +51,7 @@ def test_regression():
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
         lm = linear_regression(epochs, design_matrix, ['intercept', 'aud'])
-        assert_true(w[0].category == UserWarning)
+        assert_true(w[0].category == RuntimeWarning)
         assert_true('non-data' in '%s' % w[0].message)
 
     for predictor, parameters in lm.items():
@@ -90,7 +90,8 @@ def test_continuous_regression_no_overlap():
     """Test regression without overlap correction, on real data"""
     tmin, tmax = -.1, .5
 
-    raw = mne.io.Raw(raw_fname, preload=True)
+    raw = mne.io.read_raw_fif(raw_fname, preload=True)
+    raw.apply_proj()
     events = mne.read_events(event_fname)
     event_id = dict(audio_l=1, audio_r=2)
 
@@ -103,9 +104,21 @@ def test_continuous_regression_no_overlap():
                                      tmin=tmin, tmax=tmax,
                                      reject=None)
 
+    # Check that evokeds and revokeds are nearly equivalent
     for cond in event_id.keys():
         assert_allclose(revokeds[cond].data,
-                        epochs[cond].average().data)
+                        epochs[cond].average().data, rtol=1e-15)
+
+    # Test events that will lead to "duplicate" errors
+    old_latency = events[1, 0]
+    events[1, 0] = events[0, 0]
+    assert_raises(ValueError, linear_regression_raw,
+                  raw, events, event_id, tmin, tmax)
+
+    events[1, 0] = old_latency
+    events[:, 0] = range(len(events))
+    assert_raises(ValueError, linear_regression_raw, raw,
+                  events, event_id, tmin, tmax, decim=2)
 
 
 def test_continuous_regression_with_overlap():
diff --git a/mne/surface.py b/mne/surface.py
index 8013042..45a2abe 100644
--- a/mne/surface.py
+++ b/mne/surface.py
@@ -23,7 +23,7 @@ from .io.write import (write_int, start_file, end_block,
                        write_float_sparse_rcs)
 from .channels.channels import _get_meg_system
 from .transforms import transform_surface_to
-from .utils import logger, verbose, get_subjects_dir
+from .utils import logger, verbose, get_subjects_dir, warn
 from .externals.six import string_types
 
 
@@ -59,6 +59,8 @@ def get_head_surf(subject, source=('bem', 'head'), subjects_dir=None,
     """
     # Load the head surface from the BEM
     subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)
+    if not isinstance(subject, string_types):
+        raise TypeError('subject must be a string, not %s' % (type(subject,)))
     # use realpath to allow for linked surfaces (c.f. MNE manual 196-197)
     if isinstance(source, string_types):
         source = [source]
@@ -103,7 +105,7 @@ def get_meg_helmet_surf(info, trans=None, verbose=None):
 
     Parameters
     ----------
-    info : instance of io.meas_info.Info
+    info : instance of Info
         Measurement info.
     trans : dict
         The head<->MRI transformation, usually obtained using
@@ -797,8 +799,7 @@ def read_morph_map(subject_from, subject_to, subjects_dir=None,
         try:
             os.mkdir(mmap_dir)
         except Exception:
-            logger.warning('Could not find or make morph map directory "%s"'
-                           % mmap_dir)
+            warn('Could not find or make morph map directory "%s"' % mmap_dir)
 
     # Does the file exist
     fname = op.join(mmap_dir, '%s-%s-morph.fif' % (subject_from, subject_to))
@@ -806,9 +807,8 @@ def read_morph_map(subject_from, subject_to, subjects_dir=None,
         fname = op.join(mmap_dir, '%s-%s-morph.fif'
                         % (subject_to, subject_from))
         if not op.exists(fname):
-            logger.warning('Morph map "%s" does not exist, '
-                           'creating it and saving it to disk (this may take '
-                           'a few minutes)' % fname)
+            warn('Morph map "%s" does not exist, creating it and saving it to '
+                 'disk (this may take a few minutes)' % fname)
             logger.info('Creating morph map %s -> %s'
                         % (subject_from, subject_to))
             mmap_1 = _make_morph_map(subject_from, subject_to, subjects_dir)
@@ -819,8 +819,8 @@ def read_morph_map(subject_from, subject_to, subjects_dir=None,
                 _write_morph_map(fname, subject_from, subject_to,
                                  mmap_1, mmap_2)
             except Exception as exp:
-                logger.warning('Could not write morph-map file "%s" '
-                               '(error: %s)' % (fname, exp))
+                warn('Could not write morph-map file "%s" (error: %s)'
+                     % (fname, exp))
             return mmap_1
 
     f, tree, _ = fiff_open(fname)
diff --git a/mne/tests/common.py b/mne/tests/common.py
index 0e9ffc6..eef5b3c 100644
--- a/mne/tests/common.py
+++ b/mne/tests/common.py
@@ -3,7 +3,9 @@
 # License: BSD (3-clause)
 
 import numpy as np
-from numpy.testing import assert_allclose, assert_equal
+from numpy.testing import assert_allclose, assert_equal, assert_array_equal
+
+from scipy import linalg
 
 from .. import pick_types, Evoked
 from ..io import _BaseRaw
@@ -19,24 +21,15 @@ def _get_data(x, ch_idx):
         return x.data[ch_idx]
 
 
-def assert_meg_snr(actual, desired, min_tol, med_tol=500., msg=None):
-    """Helper to assert channel SNR of a certain level
-
-    Mostly useful for operations like Maxwell filtering that modify
-    MEG channels while leaving EEG and others intact.
-    """
+def _check_snr(actual, desired, picks, min_tol, med_tol, msg, kind='MEG'):
+    """Helper to check the SNR of a set of channels"""
     from nose.tools import assert_true
-    picks = pick_types(desired.info, meg=True, exclude=[])
-    others = np.setdiff1d(np.arange(len(actual.ch_names)), picks)
-    if len(others) > 0:  # if non-MEG channels present
-        assert_allclose(_get_data(actual, others),
-                        _get_data(desired, others), atol=1e-11, rtol=1e-5,
-                        err_msg='non-MEG channel mismatch')
     actual_data = _get_data(actual, picks)
     desired_data = _get_data(desired, picks)
     bench_rms = np.sqrt(np.mean(desired_data * desired_data, axis=1))
     error = actual_data - desired_data
     error_rms = np.sqrt(np.mean(error * error, axis=1))
+    np.clip(error_rms, 1e-60, np.inf, out=error_rms)  # avoid division by zero
     snrs = bench_rms / error_rms
     # min tol
     snr = snrs.min()
@@ -46,8 +39,45 @@ def assert_meg_snr(actual, desired, min_tol, med_tol=500., msg=None):
                 'channels%s' % (snr, min_tol, bad_count, len(picks), msg))
     # median tol
     snr = np.median(snrs)
-    assert_true(snr >= med_tol, 'SNR median %0.2f < %0.2f%s'
-                % (snr, med_tol, msg))
+    assert_true(snr >= med_tol, '%s SNR median %0.2f < %0.2f%s'
+                % (kind, snr, med_tol, msg))
+
+
+def assert_meg_snr(actual, desired, min_tol, med_tol=500., chpi_med_tol=500.,
+                   msg=None):
+    """Helper to assert channel SNR of a certain level
+
+    Mostly useful for operations like Maxwell filtering that modify
+    MEG channels while leaving EEG and others intact.
+    """
+    picks = pick_types(desired.info, meg=True, exclude=[])
+    picks_desired = pick_types(desired.info, meg=True, exclude=[])
+    assert_array_equal(picks, picks_desired, err_msg='MEG pick mismatch')
+    chpis = pick_types(actual.info, meg=False, chpi=True, exclude=[])
+    chpis_desired = pick_types(desired.info, meg=False, chpi=True, exclude=[])
+    if chpi_med_tol is not None:
+        assert_array_equal(chpis, chpis_desired, err_msg='cHPI pick mismatch')
+    others = np.setdiff1d(np.arange(len(actual.ch_names)),
+                          np.concatenate([picks, chpis]))
+    others_desired = np.setdiff1d(np.arange(len(desired.ch_names)),
+                                  np.concatenate([picks_desired,
+                                                  chpis_desired]))
+    assert_array_equal(others, others_desired, err_msg='Other pick mismatch')
+    if len(others) > 0:  # if non-MEG channels present
+        assert_allclose(_get_data(actual, others),
+                        _get_data(desired, others), atol=1e-11, rtol=1e-5,
+                        err_msg='non-MEG channel mismatch')
+    _check_snr(actual, desired, picks, min_tol, med_tol, msg, kind='MEG')
+    if chpi_med_tol is not None and len(chpis) > 0:
+        _check_snr(actual, desired, chpis, 0., chpi_med_tol, msg, kind='cHPI')
+
+
+def assert_snr(actual, desired, tol):
+    """Assert actual and desired arrays are within some SNR tolerance"""
+    from nose.tools import assert_true
+    snr = (linalg.norm(desired, ord='fro') /
+           linalg.norm(desired - actual, ord='fro'))
+    assert_true(snr >= tol, msg='%f < %f' % (snr, tol))
 
 
 def _dig_sort_key(dig):
@@ -67,8 +97,31 @@ def assert_dig_allclose(info_py, info_bin):
                         err_msg='Failure on %s:\n%s\n%s'
                         % (ii, d_py['r'], d_bin['r']))
     if any(d['kind'] == FIFF.FIFFV_POINT_EXTRA for d in dig_py):
-        R_bin, o_head_bin, o_dev_bin = fit_sphere_to_headshape(info_bin)
-        R_py, o_head_py, o_dev_py = fit_sphere_to_headshape(info_py)
-        assert_allclose(R_py, R_bin)
-        assert_allclose(o_dev_py, o_dev_bin, rtol=1e-5, atol=1e-3)  # mm
-        assert_allclose(o_head_py, o_head_bin, rtol=1e-5, atol=1e-3)  # mm
+        r_bin, o_head_bin, o_dev_bin = fit_sphere_to_headshape(info_bin,
+                                                               units='m')
+        r_py, o_head_py, o_dev_py = fit_sphere_to_headshape(info_py, units='m')
+        assert_allclose(r_py, r_bin, atol=1e-6)
+        assert_allclose(o_dev_py, o_dev_bin, rtol=1e-5, atol=1e-6)
+        assert_allclose(o_head_py, o_head_bin, rtol=1e-5, atol=1e-6)
+
+
+def assert_naming(warns, fname, n_warn):
+    """Assert a non-standard naming scheme was used while saving or loading
+
+    Parameters
+    ----------
+    warns : list
+        List of warnings from ``warnings.catch_warnings(record=True)``.
+    fname : str
+        Filename that should appear in the warning message.
+    n_warn : int
+        Number of warnings that should have naming convention errors.
+    """
+    from nose.tools import assert_true
+    assert_true(sum('naming conventions' in str(ww.message)
+                    for ww in warns) == n_warn)
+    # check proper stacklevel reporting
+    for ww in warns:
+        if 'naming conventions' in str(ww.message):
+            assert_true(fname in ww.filename,
+                        msg='"%s" not in "%s"' % (fname, ww.filename))
diff --git a/mne/tests/test_annotations.py b/mne/tests/test_annotations.py
new file mode 100644
index 0000000..0c00613
--- /dev/null
+++ b/mne/tests/test_annotations.py
@@ -0,0 +1,58 @@
+# Authors: Jaakko Leppakangas <jaeilepp at student.jyu.fi>
+#
+# License: BSD 3 clause
+
+from datetime import datetime
+from nose.tools import assert_raises
+from numpy.testing import assert_array_equal
+import os.path as op
+
+import numpy as np
+
+from mne.utils import run_tests_if_main
+from mne.io import Raw, concatenate_raws
+from mne.annotations import Annotations
+from mne.datasets import testing
+
+data_dir = op.join(testing.data_path(download=False), 'MEG', 'sample')
+fif_fname = op.join(data_dir, 'sample_audvis_trunc_raw.fif')
+
+
+ at testing.requires_testing_data
+def test_annotations():
+    """Test annotation class."""
+    raw = Raw(fif_fname)
+    onset = np.array(range(10))
+    duration = np.ones(10) + raw.first_samp
+    description = np.repeat('test', 10)
+    dt = datetime.utcnow()
+    meas_date = raw.info['meas_date']
+    # Test time shifts.
+    for orig_time in [None, dt, meas_date[0], meas_date]:
+        annot = Annotations(onset, duration, description, orig_time)
+
+    assert_raises(ValueError, Annotations, onset, duration, description[:9])
+    assert_raises(ValueError, Annotations, [onset, 1], duration, description)
+    assert_raises(ValueError, Annotations, onset, [duration, 1], description)
+
+    # Test combining annotations with concatenate_raws
+    annot = Annotations(onset, duration, description, dt)
+    sfreq = raw.info['sfreq']
+    raw2 = raw.copy()
+    raw2.annotations = annot
+    concatenate_raws([raw, raw2])
+    assert_array_equal(annot.onset, raw.annotations.onset)
+    assert_array_equal(annot.duration, raw.annotations.duration)
+
+    raw2.annotations = Annotations(onset, duration * 2, description, None)
+    last_samp = raw.last_samp - 1
+    concatenate_raws([raw, raw2])
+    onsets = np.concatenate([onset,
+                             onset + (last_samp - raw.first_samp) / sfreq])
+    assert_array_equal(raw.annotations.onset, onsets)
+    assert_array_equal(raw.annotations.onset[:10], onset)
+    assert_array_equal(raw.annotations.duration[:10], duration)
+    assert_array_equal(raw.annotations.duration[10:], duration * 2)
+    assert_array_equal(raw.annotations.description, np.repeat('test', 20))
+
+run_tests_if_main()
diff --git a/mne/tests/test_bem.py b/mne/tests/test_bem.py
index c4ff661..44a730d 100644
--- a/mne/tests/test_bem.py
+++ b/mne/tests/test_bem.py
@@ -2,8 +2,9 @@
 #
 # License: BSD 3 clause
 
-import os.path as op
 from copy import deepcopy
+import os.path as op
+import warnings
 
 import numpy as np
 from nose.tools import assert_raises, assert_true
@@ -11,7 +12,7 @@ from numpy.testing import assert_equal, assert_allclose
 
 from mne import (make_bem_model, read_bem_surfaces, write_bem_surfaces,
                  make_bem_solution, read_bem_solution, write_bem_solution,
-                 make_sphere_model, Transform)
+                 make_sphere_model, Transform, Info)
 from mne.preprocessing.maxfilter import fit_sphere_to_headshape
 from mne.io.constants import FIFF
 from mne.transforms import translation
@@ -22,6 +23,8 @@ from mne.bem import (_ico_downsample, _get_ico_map, _order_surfaces,
                      _check_surface_size, _bem_find_surface)
 from mne.io import read_info
 
+warnings.simplefilter('always')
+
 fname_raw = op.join(op.dirname(__file__), '..', 'io', 'tests', 'data',
                     'test_raw.fif')
 subjects_dir = op.join(testing.data_path(download=False), 'subjects')
@@ -91,6 +94,10 @@ def test_make_sphere_model():
     info = read_info(fname_raw)
     assert_raises(ValueError, make_sphere_model, 'foo', 'auto', info)
     assert_raises(ValueError, make_sphere_model, 'auto', 'auto', None)
+    assert_raises(ValueError, make_sphere_model, 'auto', 'auto', info,
+                  relative_radii=(), sigmas=())
+    assert_raises(ValueError, make_sphere_model, 'auto', 'auto', info,
+                  relative_radii=(1,))  # wrong number of radii
     # here we just make sure it works -- the functionality is actually
     # tested more extensively e.g. in the forward and dipole code
     bem = make_sphere_model('auto', 'auto', info)
@@ -172,10 +179,10 @@ def test_bem_solution():
 def test_fit_sphere_to_headshape():
     """Test fitting a sphere to digitization points"""
     # Create points of various kinds
-    rad = 90.  # mm
-    big_rad = 120.
-    center = np.array([0.5, -10., 40.])  # mm
-    dev_trans = np.array([0., -0.005, -10.])
+    rad = 0.09
+    big_rad = 0.12
+    center = np.array([0.0005, -0.01, 0.04])
+    dev_trans = np.array([0., -0.005, -0.01])
     dev_center = center - dev_trans
     dig = [
         # Left auricular
@@ -222,78 +229,111 @@ def test_fit_sphere_to_headshape():
          'r': np.array([0, -.72, .69])},
     ]
     for d in dig:
-        d['r'] *= rad / 1000.
-        d['r'] += center / 1000.
+        d['r'] *= rad
+        d['r'] += center
 
     # Device to head transformation (rotate .2 rad over X-axis)
-    dev_head_t = Transform('meg', 'head', translation(*(dev_trans / 1000.)))
-
-    info = {'dig': dig, 'dev_head_t': dev_head_t}
+    dev_head_t = Transform('meg', 'head', translation(*(dev_trans)))
+    info = Info(dig=dig, dev_head_t=dev_head_t)
 
     # Degenerate conditions
+    with warnings.catch_warnings(record=True) as w:
+        assert_raises(ValueError, fit_sphere_to_headshape, info,
+                      dig_kinds=(FIFF.FIFFV_POINT_HPI,))
+    assert_equal(len(w), 1)
+    assert_true(w[0].category == DeprecationWarning)
     assert_raises(ValueError, fit_sphere_to_headshape, info,
-                  dig_kinds=(FIFF.FIFFV_POINT_HPI,))
+                  dig_kinds='foo', units='m')
     info['dig'][0]['coord_frame'] = FIFF.FIFFV_COORD_DEVICE
-    assert_raises(RuntimeError, fit_sphere_to_headshape, info)
+    assert_raises(RuntimeError, fit_sphere_to_headshape, info, units='m')
     info['dig'][0]['coord_frame'] = FIFF.FIFFV_COORD_HEAD
 
     #  # Test with 4 points that match a perfect sphere
     dig_kinds = (FIFF.FIFFV_POINT_CARDINAL, FIFF.FIFFV_POINT_EXTRA)
-    r, oh, od = fit_sphere_to_headshape(info, dig_kinds=dig_kinds)
-    kwargs = dict(rtol=1e-3, atol=1e-2)  # in mm
+    with warnings.catch_warnings(record=True):  # not enough points
+        r, oh, od = fit_sphere_to_headshape(info, dig_kinds=dig_kinds,
+                                            units='m')
+    kwargs = dict(rtol=1e-3, atol=1e-5)
     assert_allclose(r, rad, **kwargs)
     assert_allclose(oh, center, **kwargs)
     assert_allclose(od, dev_center, **kwargs)
 
     # Test with all points
-    dig_kinds = (FIFF.FIFFV_POINT_CARDINAL, FIFF.FIFFV_POINT_EXTRA,
-                 FIFF.FIFFV_POINT_EEG)
-    kwargs = dict(rtol=1e-3, atol=1.)  # in mm
-    r, oh, od = fit_sphere_to_headshape(info, dig_kinds=dig_kinds)
+    dig_kinds = ('cardinal', FIFF.FIFFV_POINT_EXTRA, 'eeg')
+    kwargs = dict(rtol=1e-3, atol=1e-3)
+    with warnings.catch_warnings(record=True):  # not enough points
+        r, oh, od = fit_sphere_to_headshape(info, dig_kinds=dig_kinds,
+                                            units='m')
     assert_allclose(r, rad, **kwargs)
     assert_allclose(oh, center, **kwargs)
     assert_allclose(od, dev_center, **kwargs)
 
     # Test with some noisy EEG points only.
-    dig_kinds = (FIFF.FIFFV_POINT_EEG,)
-    r, oh, od = fit_sphere_to_headshape(info, dig_kinds=dig_kinds)
-    kwargs = dict(rtol=1e-3, atol=10.)  # in mm
+    dig_kinds = 'eeg'
+    with warnings.catch_warnings(record=True):  # not enough points
+        r, oh, od = fit_sphere_to_headshape(info, dig_kinds=dig_kinds,
+                                            units='m')
+    kwargs = dict(rtol=1e-3, atol=1e-2)
     assert_allclose(r, rad, **kwargs)
     assert_allclose(oh, center, **kwargs)
     assert_allclose(od, center, **kwargs)
 
     # Test big size
-    dig_kinds = (FIFF.FIFFV_POINT_CARDINAL, FIFF.FIFFV_POINT_EXTRA)
+    dig_kinds = ('cardinal', 'extra')
     info_big = deepcopy(info)
     for d in info_big['dig']:
-        d['r'] -= center / 1000.
+        d['r'] -= center
         d['r'] *= big_rad / rad
-        d['r'] += center / 1000.
-    with catch_logging() as log_file:
-        r, oh, od = fit_sphere_to_headshape(info_big, dig_kinds=dig_kinds,
-                                            verbose='warning')
+        d['r'] += center
+    with warnings.catch_warnings(record=True):  # fit
+        with catch_logging() as log_file:
+            r, oh, od = fit_sphere_to_headshape(info_big, dig_kinds=dig_kinds,
+                                                verbose='warning', units='mm')
     log_file = log_file.getvalue().strip()
-    assert_equal(len(log_file.split('\n')), 1)
-    assert_true(log_file.startswith('Estimated head size'))
-    assert_allclose(oh, center, atol=1e-3)
-    assert_allclose(r, big_rad, atol=1e-3)
+    assert_equal(len(log_file.split('\n')), 2)
+    assert_true('Estimated head size' in log_file)
+    assert_allclose(oh, center * 1000, atol=1e-3)
+    assert_allclose(r, big_rad * 1000, atol=1e-3)
     del info_big
 
     # Test offcenter
-    dig_kinds = (FIFF.FIFFV_POINT_CARDINAL, FIFF.FIFFV_POINT_EXTRA)
+    dig_kinds = ('cardinal', 'extra')
     info_shift = deepcopy(info)
-    shift_center = np.array([0., -30, 0.])
+    shift_center = np.array([0., -0.03, 0.])
     for d in info_shift['dig']:
-        d['r'] -= center / 1000.
-        d['r'] += shift_center / 1000.
-    with catch_logging() as log_file:
-        r, oh, od = fit_sphere_to_headshape(info_shift, dig_kinds=dig_kinds,
-                                            verbose='warning')
+        d['r'] -= center
+        d['r'] += shift_center
+    with warnings.catch_warnings(record=True):
+        with catch_logging() as log_file:
+            r, oh, od = fit_sphere_to_headshape(
+                info_shift, dig_kinds=dig_kinds, verbose='warning', units='m')
     log_file = log_file.getvalue().strip()
-    assert_equal(len(log_file.split('\n')), 1)
+    assert_equal(len(log_file.split('\n')), 2)
     assert_true('from head frame origin' in log_file)
-    assert_allclose(oh, shift_center, atol=1e-3)
-    assert_allclose(r, rad, atol=1e-3)
+    assert_allclose(oh, shift_center, atol=1e-6)
+    assert_allclose(r, rad, atol=1e-6)
+
+    # Test "auto" mode (default)
+    # Should try "extra", fail, and go on to EEG
+    with warnings.catch_warnings(record=True):  # not enough points
+        r, oh, od = fit_sphere_to_headshape(info, units='m')
+    kwargs = dict(rtol=1e-3, atol=1e-3)
+    assert_allclose(r, rad, **kwargs)
+    assert_allclose(oh, center, **kwargs)
+    assert_allclose(od, dev_center, **kwargs)
+    with warnings.catch_warnings(record=True):  # not enough points
+        r2, oh2, od2 = fit_sphere_to_headshape(info, units='m')
+    assert_allclose(r, r2, atol=1e-7)
+    assert_allclose(oh, oh2, atol=1e-7)
+    assert_allclose(od, od2, atol=1e-7)
+    # this one should pass, 1 EXTRA point and 3 EEG (but the fit is terrible)
+    info = Info(dig=dig[:7], dev_head_t=dev_head_t)
+    with warnings.catch_warnings(record=True):  # bad fit
+        r, oh, od = fit_sphere_to_headshape(info, units='m')
+    # this one should fail, 1 EXTRA point and 3 EEG (but the fit is terrible)
+    info = Info(dig=dig[:6], dev_head_t=dev_head_t)
+    assert_raises(ValueError, fit_sphere_to_headshape, info, units='m')
+    assert_raises(TypeError, fit_sphere_to_headshape, 1, units='m')
 
 
 run_tests_if_main()
diff --git a/mne/tests/test_chpi.py b/mne/tests/test_chpi.py
index ec73e13..8dff103 100644
--- a/mne/tests/test_chpi.py
+++ b/mne/tests/test_chpi.py
@@ -8,13 +8,17 @@ from numpy.testing import assert_allclose
 from nose.tools import assert_raises, assert_equal, assert_true
 import warnings
 
-from mne.io import read_info, Raw
+from mne.io import Raw
 from mne.io.constants import FIFF
-from mne.chpi import (_rot_to_quat, _quat_to_rot, get_chpi_positions,
-                      _calculate_chpi_positions, _angle_between_quats)
+from mne.chpi import (get_chpi_positions, _calculate_chpi_positions,
+                      head_pos_to_trans_rot_t, read_head_pos,
+                      write_head_pos, filter_chpi)
+from mne.fixes import assert_raises_regex
+from mne.transforms import rot_to_quat, quat_to_rot, _angle_between_quats
 from mne.utils import (run_tests_if_main, _TempDir, slow_test, catch_logging,
                        requires_version)
 from mne.datasets import testing
+from mne.tests.common import assert_meg_snr
 
 base_dir = op.join(op.dirname(__file__), '..', 'io', 'tests', 'data')
 test_fif_fname = op.join(base_dir, 'test_raw.fif')
@@ -23,73 +27,58 @@ hp_fif_fname = op.join(base_dir, 'test_chpi_raw_sss.fif')
 hp_fname = op.join(base_dir, 'test_chpi_raw_hp.txt')
 
 data_path = testing.data_path(download=False)
-raw_fif_fname = op.join(data_path, 'SSS', 'test_move_anon_raw.fif')
+chpi_fif_fname = op.join(data_path, 'SSS', 'test_move_anon_raw.fif')
 pos_fname = op.join(data_path, 'SSS', 'test_move_anon_raw.pos')
 sss_fif_fname = op.join(data_path, 'SSS', 'test_move_anon_raw_sss.fif')
+sss_hpisubt_fname = op.join(data_path, 'SSS', 'test_move_anon_hpisubt_raw.fif')
 
 warnings.simplefilter('always')
 
 
-def test_quaternions():
-    """Test quaternion calculations
-    """
-    rots = [np.eye(3)]
-    for fname in [test_fif_fname, ctf_fname, hp_fif_fname]:
-        rots += [read_info(fname)['dev_head_t']['trans'][:3, :3]]
-    # nasty numerical cases
-    rots += [np.array([
-        [-0.99978541, -0.01873462, -0.00898756],
-        [-0.01873462, 0.62565561, 0.77987608],
-        [-0.00898756, 0.77987608, -0.62587152],
-    ])]
-    rots += [np.array([
-        [0.62565561, -0.01873462, 0.77987608],
-        [-0.01873462, -0.99978541, -0.00898756],
-        [0.77987608, -0.00898756, -0.62587152],
-    ])]
-    rots += [np.array([
-        [-0.99978541, -0.00898756, -0.01873462],
-        [-0.00898756, -0.62587152, 0.77987608],
-        [-0.01873462, 0.77987608, 0.62565561],
-    ])]
-    for rot in rots:
-        assert_allclose(rot, _quat_to_rot(_rot_to_quat(rot)),
-                        rtol=1e-5, atol=1e-5)
-        rot = rot[np.newaxis, np.newaxis, :, :]
-        assert_allclose(rot, _quat_to_rot(_rot_to_quat(rot)),
-                        rtol=1e-5, atol=1e-5)
-
-    # let's make sure our angle function works in some reasonable way
-    for ii in range(3):
-        for jj in range(3):
-            a = np.zeros(3)
-            b = np.zeros(3)
-            a[ii] = 1.
-            b[jj] = 1.
-            expected = np.pi if ii != jj else 0.
-            assert_allclose(_angle_between_quats(a, b), expected, atol=1e-5)
+ at testing.requires_testing_data
+def test_read_write_head_pos():
+    """Test reading and writing head position quaternion parameters"""
+    tempdir = _TempDir()
+    temp_name = op.join(tempdir, 'temp.pos')
+    # This isn't a 100% valid quat matrix but it should be okay for tests
+    head_pos_rand = np.random.RandomState(0).randn(20, 10)
+    # This one is valid
+    head_pos_read = read_head_pos(pos_fname)
+    for head_pos_orig in (head_pos_rand, head_pos_read):
+        write_head_pos(temp_name, head_pos_orig)
+        head_pos = read_head_pos(temp_name)
+        assert_allclose(head_pos_orig, head_pos, atol=1e-3)
+    # Degenerate cases
+    assert_raises(TypeError, write_head_pos, 0, head_pos_read)  # not filename
+    assert_raises(ValueError, write_head_pos, temp_name, 'foo')  # not array
+    assert_raises(ValueError, write_head_pos, temp_name, head_pos_read[:, :9])
+    assert_raises(TypeError, read_head_pos, 0)
+    assert_raises(IOError, read_head_pos, temp_name + 'foo')
 
 
 def test_get_chpi():
     """Test CHPI position computation
     """
-    trans0, rot0, _, quat0 = get_chpi_positions(hp_fname, return_quat=True)
-    assert_allclose(rot0[0], _quat_to_rot(quat0[0]))
+    with warnings.catch_warnings(record=True):  # deprecation
+        trans0, rot0, _, quat0 = get_chpi_positions(hp_fname, return_quat=True)
+    assert_allclose(rot0[0], quat_to_rot(quat0[0]))
     trans0, rot0 = trans0[:-1], rot0[:-1]
     raw = Raw(hp_fif_fname)
-    out = get_chpi_positions(raw)
+    with warnings.catch_warnings(record=True):  # deprecation
+        out = get_chpi_positions(raw)
     trans1, rot1, t1 = out
     trans1, rot1 = trans1[2:], rot1[2:]
     # these will not be exact because they don't use equiv. time points
     assert_allclose(trans0, trans1, atol=1e-5, rtol=1e-1)
     assert_allclose(rot0, rot1, atol=1e-6, rtol=1e-1)
     # run through input checking
-    assert_raises(TypeError, get_chpi_positions, 1)
-    assert_raises(ValueError, get_chpi_positions, hp_fname, [1])
     raw_no_chpi = Raw(test_fif_fname)
-    assert_raises(RuntimeError, get_chpi_positions, raw_no_chpi)
-    assert_raises(ValueError, get_chpi_positions, raw, t_step='foo')
-    assert_raises(IOError, get_chpi_positions, 'foo')
+    with warnings.catch_warnings(record=True):  # deprecation
+        assert_raises(TypeError, get_chpi_positions, 1)
+        assert_raises(ValueError, get_chpi_positions, hp_fname, [1])
+        assert_raises(RuntimeError, get_chpi_positions, raw_no_chpi)
+        assert_raises(ValueError, get_chpi_positions, raw, t_step='foo')
+        assert_raises(IOError, get_chpi_positions, 'foo')
 
 
 @testing.requires_testing_data
@@ -98,15 +87,11 @@ def test_hpi_info():
     """
     tempdir = _TempDir()
     temp_name = op.join(tempdir, 'temp_raw.fif')
-    for fname in (raw_fif_fname, sss_fif_fname):
-        with warnings.catch_warnings(record=True):
-            warnings.simplefilter('always')
-            raw = Raw(fname, allow_maxshield=True)
+    for fname in (chpi_fif_fname, sss_fif_fname):
+        raw = Raw(fname, allow_maxshield='yes')
         assert_true(len(raw.info['hpi_subsystem']) > 0)
         raw.save(temp_name, overwrite=True)
-        with warnings.catch_warnings(record=True):
-            warnings.simplefilter('always')
-            raw_2 = Raw(temp_name, allow_maxshield=True)
+        raw_2 = Raw(temp_name, allow_maxshield='yes')
         assert_equal(len(raw_2.info['hpi_subsystem']),
                      len(raw.info['hpi_subsystem']))
 
@@ -116,13 +101,13 @@ def _compare_positions(a, b, max_dist=0.003, max_angle=5.):
     from scipy.interpolate import interp1d
     trans, rot, t = a
     trans_est, rot_est, t_est = b
-    quats_est = _rot_to_quat(rot_est)
+    quats_est = rot_to_quat(rot_est)
 
     # maxfilter produces some times that are implausibly large (weird)
     use_mask = (t >= t_est[0]) & (t <= t_est[-1])
     t = t[use_mask]
     trans = trans[use_mask]
-    quats = _rot_to_quat(rot)
+    quats = rot_to_quat(rot)
     quats = quats[use_mask]
 
     # double-check our angle function
@@ -150,12 +135,12 @@ def _compare_positions(a, b, max_dist=0.003, max_angle=5.):
 def test_calculate_chpi_positions():
     """Test calculation of cHPI positions
     """
-    trans, rot, t = get_chpi_positions(pos_fname)
-    with warnings.catch_warnings(record=True):
-        raw = Raw(raw_fif_fname, allow_maxshield=True, preload=True)
+    trans, rot, t = head_pos_to_trans_rot_t(read_head_pos(pos_fname))
+    raw = Raw(chpi_fif_fname, allow_maxshield='yes', preload=True)
     t -= raw.first_samp / raw.info['sfreq']
-    trans_est, rot_est, t_est = _calculate_chpi_positions(raw, verbose='debug')
-    _compare_positions((trans, rot, t), (trans_est, rot_est, t_est))
+    quats = _calculate_chpi_positions(raw, verbose='debug')
+    trans_est, rot_est, t_est = head_pos_to_trans_rot_t(quats)
+    _compare_positions((trans, rot, t), (trans_est, rot_est, t_est), 0.003)
 
     # degenerate conditions
     raw_no_chpi = Raw(test_fif_fname)
@@ -171,9 +156,53 @@ def test_calculate_chpi_positions():
         if d['kind'] == FIFF.FIFFV_POINT_HPI:
             d['r'] = np.ones(3)
     raw_bad.crop(0, 1., copy=False)
-    with catch_logging() as log_file:
-        _calculate_chpi_positions(raw_bad)
-    for line in log_file.getvalue().split('\n')[:-1]:
-        assert_true('0/5 acceptable' in line)
+    with warnings.catch_warnings(record=True):  # bad pos
+        with catch_logging() as log_file:
+            _calculate_chpi_positions(raw_bad, verbose=True)
+    # ignore HPI info header and [done] footer
+    for line in log_file.getvalue().strip().split('\n')[4:-1]:
+        assert_true('0/5 good' in line)
+
+    # half the rate cuts off cHPI coils
+    with warnings.catch_warnings(record=True):  # uint cast suggestion
+        raw.resample(300., npad='auto')
+    assert_raises_regex(RuntimeError, 'above the',
+                        _calculate_chpi_positions, raw)
+
+
+ at testing.requires_testing_data
+def test_chpi_subtraction():
+    """Test subtraction of cHPI signals"""
+    raw = Raw(chpi_fif_fname, allow_maxshield='yes', preload=True)
+    with catch_logging() as log:
+        filter_chpi(raw, include_line=False, verbose=True)
+    assert_true('5 cHPI' in log.getvalue())
+    # MaxFilter doesn't do quite as well as our algorithm with the last bit
+    raw.crop(0, 16, copy=False)
+    # remove cHPI status chans
+    raw_c = Raw(sss_hpisubt_fname).crop(0, 16, copy=False).load_data()
+    raw_c.pick_types(
+        meg=True, eeg=True, eog=True, ecg=True, stim=True, misc=True)
+    assert_meg_snr(raw, raw_c, 143, 624)
+
+    # Degenerate cases
+    raw_nohpi = Raw(test_fif_fname, preload=True)
+    assert_raises(RuntimeError, filter_chpi, raw_nohpi)
+
+    # When MaxFliter downsamples, like::
+    #     $ maxfilter -nosss -ds 2 -f test_move_anon_raw.fif \
+    #           -o test_move_anon_ds2_raw.fif
+    # it can strip out some values of info, which we emulate here:
+    raw = Raw(chpi_fif_fname, allow_maxshield='yes')
+    with warnings.catch_warnings(record=True):  # uint cast suggestion
+        raw = raw.crop(0, 1).load_data().resample(600., npad='auto')
+    raw.info['buffer_size_sec'] = np.float64(2.)
+    raw.info['lowpass'] = 200.
+    del raw.info['maxshield']
+    del raw.info['hpi_results'][0]['moments']
+    del raw.info['hpi_subsystem']['event_channel']
+    with catch_logging() as log:
+        filter_chpi(raw, verbose=True)
+    assert_true('2 cHPI' in log.getvalue())
 
 run_tests_if_main()
diff --git a/mne/tests/test_cov.py b/mne/tests/test_cov.py
index 6619b04..4ec2601 100644
--- a/mne/tests/test_cov.py
+++ b/mne/tests/test_cov.py
@@ -5,8 +5,9 @@
 
 import os.path as op
 
-from nose.tools import assert_true, assert_equal
-from numpy.testing import assert_array_almost_equal, assert_array_equal
+from nose.tools import assert_true
+from numpy.testing import (assert_array_almost_equal, assert_array_equal,
+                           assert_equal)
 from nose.tools import assert_raises
 import numpy as np
 from scipy import linalg
@@ -15,14 +16,15 @@ import itertools as itt
 
 from mne.cov import (regularize, whiten_evoked, _estimate_rank_meeg_cov,
                      _auto_low_rank_model, _apply_scaling_cov,
-                     _undo_scaling_cov)
+                     _undo_scaling_cov, prepare_noise_cov)
 
 from mne import (read_cov, write_cov, Epochs, merge_events,
                  find_events, compute_raw_covariance,
                  compute_covariance, read_evokeds, compute_proj_raw,
                  pick_channels_cov, pick_channels, pick_types, pick_info,
                  make_ad_hoc_cov)
-from mne.io import Raw
+from mne.io import Raw, RawArray
+from mne.tests.common import assert_naming, assert_snr
 from mne.utils import (_TempDir, slow_test, requires_sklearn_0_15,
                        run_tests_if_main)
 from mne.io.proc_history import _get_sss_rank
@@ -53,8 +55,7 @@ def test_ad_hoc_cov():
 
 
 def test_io_cov():
-    """Test IO for noise covariance matrices
-    """
+    """Test IO for noise covariance matrices"""
     tempdir = _TempDir()
     cov = read_cov(cov_fname)
     cov['method'] = 'empirical'
@@ -90,45 +91,80 @@ def test_io_cov():
         cov_badname = op.join(tempdir, 'test-bad-name.fif.gz')
         write_cov(cov_badname, cov)
         read_cov(cov_badname)
-    assert_true(len(w) == 2)
+    assert_naming(w, 'test_cov.py', 2)
 
 
-def test_cov_estimation_on_raw_segment():
-    """Test estimation from raw on continuous recordings (typically empty room)
-    """
+def test_cov_estimation_on_raw():
+    """Test estimation from raw (typically empty room)"""
     tempdir = _TempDir()
-    raw = Raw(raw_fname, preload=False)
-    cov = compute_raw_covariance(raw)
+    raw = Raw(raw_fname, preload=True)
     cov_mne = read_cov(erm_cov_fname)
-    assert_true(cov_mne.ch_names == cov.ch_names)
-    assert_true(linalg.norm(cov.data - cov_mne.data, ord='fro') /
-                linalg.norm(cov.data, ord='fro') < 1e-4)
 
-    # test IO when computation done in Python
-    cov.save(op.join(tempdir, 'test-cov.fif'))  # test saving
-    cov_read = read_cov(op.join(tempdir, 'test-cov.fif'))
-    assert_true(cov_read.ch_names == cov.ch_names)
-    assert_true(cov_read.nfree == cov.nfree)
-    assert_array_almost_equal(cov.data, cov_read.data)
-
-    # test with a subset of channels
-    picks = pick_channels(raw.ch_names, include=raw.ch_names[:5])
-    cov = compute_raw_covariance(raw, picks=picks)
-    assert_true(cov_mne.ch_names[:5] == cov.ch_names)
-    assert_true(linalg.norm(cov.data - cov_mne.data[picks][:, picks],
-                ord='fro') / linalg.norm(cov.data, ord='fro') < 1e-4)
-    # make sure we get a warning with too short a segment
-    raw_2 = raw.crop(0, 1)
-    with warnings.catch_warnings(record=True) as w:
+    # The pure-string uses the more efficient numpy-based method, the
+    # the list gets triaged to compute_covariance (should be equivalent
+    # but use more memory)
+    for method in ('empirical', ['empirical']):
+        cov = compute_raw_covariance(raw, tstep=None, method=method)
+        assert_equal(cov.ch_names, cov_mne.ch_names)
+        assert_equal(cov.nfree, cov_mne.nfree)
+        assert_snr(cov.data, cov_mne.data, 1e4)
+
+        cov = compute_raw_covariance(raw, method=method)  # tstep=0.2 (default)
+        assert_equal(cov.nfree, cov_mne.nfree - 119)  # cutoff some samples
+        assert_snr(cov.data, cov_mne.data, 1e2)
+
+        # test IO when computation done in Python
+        cov.save(op.join(tempdir, 'test-cov.fif'))  # test saving
+        cov_read = read_cov(op.join(tempdir, 'test-cov.fif'))
+        assert_true(cov_read.ch_names == cov.ch_names)
+        assert_true(cov_read.nfree == cov.nfree)
+        assert_array_almost_equal(cov.data, cov_read.data)
+
+        # test with a subset of channels
+        picks = pick_channels(raw.ch_names, include=raw.ch_names[:5])
+        raw_pick = raw.copy().pick_channels(
+            [raw.ch_names[pick] for pick in picks])
+        raw_pick.info.normalize_proj()
+        cov = compute_raw_covariance(raw_pick, picks=picks, tstep=None,
+                                     method=method)
+        assert_true(cov_mne.ch_names[:5] == cov.ch_names)
+        assert_snr(cov.data, cov_mne.data[picks][:, picks], 1e4)
+        cov = compute_raw_covariance(raw_pick, picks=picks, method=method)
+        assert_snr(cov.data, cov_mne.data[picks][:, picks], 90)  # cutoff samps
+        # make sure we get a warning with too short a segment
+        raw_2 = Raw(raw_fname).crop(0, 1, copy=False)
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter('always')
+            cov = compute_raw_covariance(raw_2, method=method)
+        assert_true(any('Too few samples' in str(ww.message) for ww in w))
+        # no epochs found due to rejection
+        assert_raises(ValueError, compute_raw_covariance, raw, tstep=None,
+                      method='empirical', reject=dict(eog=200e-6))
+        # but this should work
+        cov = compute_raw_covariance(raw.copy().crop(0, 10., copy=False),
+                                     tstep=None, method=method,
+                                     reject=dict(eog=1000e-6))
+
+
+ at slow_test
+ at requires_sklearn_0_15
+def test_cov_estimation_on_raw_reg():
+    """Test estimation from raw with regularization"""
+    raw = Raw(raw_fname, preload=True)
+    raw.info['sfreq'] /= 10.
+    raw = RawArray(raw._data[:, ::10].copy(), raw.info)  # decimate for speed
+    cov_mne = read_cov(erm_cov_fname)
+    with warnings.catch_warnings(record=True):  # too few samples
         warnings.simplefilter('always')
-        cov = compute_raw_covariance(raw_2)
-    assert_true(len(w) == 1)
+        # XXX don't use "shrunk" here, for some reason it makes Travis 2.7
+        # hang... "diagonal_fixed" is much faster. Use long epochs for speed.
+        cov = compute_raw_covariance(raw, tstep=5., method='diagonal_fixed')
+    assert_snr(cov.data, cov_mne.data, 5)
 
 
 @slow_test
 def test_cov_estimation_with_triggers():
-    """Test estimation from raw with triggers
-    """
+    """Test estimation from raw with triggers"""
     tempdir = _TempDir()
     raw = Raw(raw_fname, preload=False)
     events = find_events(raw, stim_channel='STI 014')
@@ -206,8 +242,7 @@ def test_cov_estimation_with_triggers():
 
 
 def test_arithmetic_cov():
-    """Test arithmetic with noise covariance matrices
-    """
+    """Test arithmetic with noise covariance matrices"""
     cov = read_cov(cov_fname)
     cov_sum = cov + cov
     assert_array_almost_equal(2 * cov.nfree, cov_sum.nfree)
@@ -221,8 +256,7 @@ def test_arithmetic_cov():
 
 
 def test_regularize_cov():
-    """Test cov regularization
-    """
+    """Test cov regularization"""
     raw = Raw(raw_fname, preload=False)
     raw.info['bads'].append(raw.ch_names[0])  # test with bad channels
     noise_cov = read_cov(cov_fname)
@@ -259,6 +293,17 @@ def test_evoked_whiten():
 @slow_test
 def test_rank():
     """Test cov rank estimation"""
+    # Test that our rank estimation works properly on a simple case
+    evoked = read_evokeds(ave_fname, condition=0, baseline=(None, 0),
+                          proj=False)
+    cov = read_cov(cov_fname)
+    ch_names = [ch for ch in evoked.info['ch_names'] if '053' not in ch and
+                ch.startswith('EEG')]
+    cov = prepare_noise_cov(cov, evoked.info, ch_names, None)
+    assert_equal(cov['eig'][0], 0.)  # avg projector should set this to zero
+    assert_true((cov['eig'][1:] > 0).all())  # all else should be > 0
+
+    # Now do some more comprehensive tests
     raw_sample = Raw(raw_fname)
 
     raw_sss = Raw(hp_fif_fname)
@@ -417,18 +462,20 @@ def test_auto_low_rank():
 def test_compute_covariance_auto_reg():
     """Test automated regularization"""
 
-    raw = Raw(raw_fname, preload=False)
+    raw = Raw(raw_fname, preload=True)
+    raw.resample(100, npad='auto')  # much faster estimation
     events = find_events(raw, stim_channel='STI 014')
     event_ids = [1, 2, 3, 4]
     reject = dict(mag=4e-12)
 
     # cov with merged events and keep_sample_mean=True
     events_merged = merge_events(events, event_ids, 1234)
-    picks = pick_types(raw.info, meg='mag', eeg=False)
+    # we need a few channels for numerical reasons in PCA/FA
+    picks = pick_types(raw.info, meg='mag', eeg=False)[:10]
+    raw.pick_channels([raw.ch_names[pick] for pick in picks])
+    raw.info.normalize_proj()
     epochs = Epochs(
         raw, events_merged, 1234, tmin=-0.2, tmax=0,
-        picks=picks[:10],  # we need a few channels for numerical reasons
-        # in PCA/FA.
         baseline=(-0.2, -0.1), proj=True, reject=reject, preload=True)
     epochs = epochs.crop(None, 0)[:10]
 
diff --git a/mne/tests/test_dipole.py b/mne/tests/test_dipole.py
index f56655d..6555e64 100644
--- a/mne/tests/test_dipole.py
+++ b/mne/tests/test_dipole.py
@@ -1,15 +1,18 @@
+import os
 import os.path as op
+import sys
+import warnings
+
 import numpy as np
 from nose.tools import assert_true, assert_equal, assert_raises
 from numpy.testing import assert_allclose
-import warnings
 
 from mne import (read_dipole, read_forward_solution,
                  convert_forward_solution, read_evokeds, read_cov,
                  SourceEstimate, write_evokeds, fit_dipole,
                  transform_surface_to, make_sphere_model, pick_types,
                  pick_info, EvokedArray, read_source_spaces, make_ad_hoc_cov,
-                 make_forward_solution)
+                 make_forward_solution, Dipole, DipoleFixed)
 from mne.simulation import simulate_evoked
 from mne.datasets import testing
 from mne.utils import (run_tests_if_main, _TempDir, slow_test, requires_mne,
@@ -36,6 +39,7 @@ fname_trans = op.join(data_path, 'MEG', 'sample',
                       'sample_audvis_trunc-trans.fif')
 fname_fwd = op.join(data_path, 'MEG', 'sample',
                     'sample_audvis_trunc-meg-eeg-oct-6-fwd.fif')
+fname_xfit_dip = op.join(data_path, 'misc', 'fam_115_LH.fif')
 subjects_dir = op.join(data_path, 'subjects')
 
 
@@ -59,8 +63,7 @@ def _check_dipole(dip, n_dipoles):
 
 @testing.requires_testing_data
 def test_io_dipoles():
-    """Test IO for .dip files
-    """
+    """Test IO for .dip files"""
     tempdir = _TempDir()
     dipole = read_dipole(fname_dip)
     print(dipole)  # test repr
@@ -114,7 +117,14 @@ def test_dipole_fitting():
     # Sanity check: do our residuals have less power than orig data?
     data_rms = np.sqrt(np.sum(evoked.data ** 2, axis=0))
     resi_rms = np.sqrt(np.sum(residuals ** 2, axis=0))
-    assert_true((data_rms > resi_rms).all())
+    factor = 1.
+    # XXX weird, inexplicable differenc for 3.5 build we'll assume is due to
+    # Anaconda bug for now...
+    if os.getenv('TRAVIS', 'false') == 'true' and \
+            sys.version[:3] in ('3.5', '2.7'):
+        factor = 0.8
+    assert_true((data_rms > factor * resi_rms).all(),
+                msg='%s (factor: %s)' % ((data_rms / resi_rms).min(), factor))
 
     # Compare to original points
     transform_surface_to(fwd['src'][0], 'head', fwd['mri_head_t'])
@@ -139,17 +149,62 @@ def test_dipole_fitting():
                                                      axis=1)))]
         amp_errs += [np.sqrt(np.mean((amp - d.amplitude) ** 2))]
         gofs += [np.mean(d.gof)]
-    assert_true(dists[0] >= dists[1], 'dists: %s' % dists)
-    assert_true(corrs[0] <= corrs[1], 'corrs: %s' % corrs)
-    assert_true(gc_dists[0] >= gc_dists[1], 'gc-dists (ori): %s' % gc_dists)
-    assert_true(amp_errs[0] >= amp_errs[1], 'amplitude errors: %s' % amp_errs)
-    assert_true(gofs[0] <= gofs[1], 'gof: %s' % gofs)
+    assert_true(dists[0] >= dists[1] * factor, 'dists: %s' % dists)
+    assert_true(corrs[0] <= corrs[1] / factor, 'corrs: %s' % corrs)
+    assert_true(gc_dists[0] >= gc_dists[1] * factor,
+                'gc-dists (ori): %s' % gc_dists)
+    assert_true(amp_errs[0] >= amp_errs[1] * factor,
+                'amplitude errors: %s' % amp_errs)
+    assert_true(gofs[0] <= gofs[1] / factor, 'gof: %s' % gofs)
+
+
+ at testing.requires_testing_data
+def test_dipole_fitting_fixed():
+    """Test dipole fitting with a fixed position"""
+    tpeak = 0.073
+    sphere = make_sphere_model(head_radius=0.1)
+    evoked = read_evokeds(fname_evo, baseline=(None, 0))[0]
+    evoked.pick_types(meg=True, copy=False)
+    t_idx = np.argmin(np.abs(tpeak - evoked.times))
+    evoked_crop = evoked.copy().crop(tpeak, tpeak, copy=False)
+    assert_equal(len(evoked_crop.times), 1)
+    cov = read_cov(fname_cov)
+    dip_seq, resid = fit_dipole(evoked_crop, cov, sphere)
+    assert_true(isinstance(dip_seq, Dipole))
+    assert_equal(len(dip_seq.times), 1)
+    pos, ori, gof = dip_seq.pos[0], dip_seq.ori[0], dip_seq.gof[0]
+    amp = dip_seq.amplitude[0]
+    # Fix position, allow orientation to change
+    dip_free, resid_free = fit_dipole(evoked, cov, sphere, pos=pos)
+    assert_true(isinstance(dip_free, Dipole))
+    assert_allclose(dip_free.times, evoked.times)
+    assert_allclose(np.tile(pos[np.newaxis], (len(evoked.times), 1)),
+                    dip_free.pos)
+    assert_allclose(ori, dip_free.ori[t_idx])  # should find same ori
+    assert_true(np.dot(dip_free.ori, ori).mean() < 0.9)  # but few the same
+    assert_allclose(gof, dip_free.gof[t_idx])  # ... same gof
+    assert_allclose(amp, dip_free.amplitude[t_idx])  # and same amp
+    assert_allclose(resid, resid_free[:, [t_idx]])
+    # Fix position and orientation
+    dip_fixed, resid_fixed = fit_dipole(evoked, cov, sphere, pos=pos, ori=ori)
+    assert_true(isinstance(dip_fixed, DipoleFixed))
+    assert_allclose(dip_fixed.times, evoked.times)
+    assert_allclose(dip_fixed.info['chs'][0]['loc'][:3], pos)
+    assert_allclose(dip_fixed.info['chs'][0]['loc'][3:6], ori)
+    assert_allclose(dip_fixed.data[1, t_idx], gof)
+    assert_allclose(resid, resid_fixed[:, [t_idx]])
+    _check_roundtrip_fixed(dip_fixed)
+    # Degenerate conditions
+    assert_raises(ValueError, fit_dipole, evoked, cov, sphere, pos=[0])
+    assert_raises(ValueError, fit_dipole, evoked, cov, sphere, ori=[1, 0, 0])
+    assert_raises(ValueError, fit_dipole, evoked, cov, sphere, pos=[0, 0, 0],
+                  ori=[2, 0, 0])
+    assert_raises(ValueError, fit_dipole, evoked, cov, sphere, pos=[0.1, 0, 0])
 
 
 @testing.requires_testing_data
 def test_len_index_dipoles():
-    """Test len and indexing of Dipole objects
-    """
+    """Test len and indexing of Dipole objects"""
     dipole = read_dipole(fname_dip)
     d0 = dipole[0]
     d1 = dipole[:1]
@@ -212,43 +267,67 @@ def _compute_depth(dip, fname_bem, fname_trans, subject, subjects_dir):
 
 @testing.requires_testing_data
 def test_accuracy():
-    """Test dipole fitting to sub-mm accuracy
-    """
+    """Test dipole fitting to sub-mm accuracy"""
     evoked = read_evokeds(fname_evo)[0].crop(0., 0.,)
     evoked.pick_types(meg=True, eeg=False)
     evoked.pick_channels([c for c in evoked.ch_names[::4]])
-    bem = make_sphere_model('auto', 0.09, evoked.info,
-                            relative_radii=(0.999, 0.998, 0.997, 0.995))
-    src = read_source_spaces(fname_src)
-
-    fwd = make_forward_solution(evoked.info, None, src, bem)
-    fwd = convert_forward_solution(fwd, force_fixed=True)
-    vertices = [src[0]['vertno'], src[1]['vertno']]
-    n_vertices = sum(len(v) for v in vertices)
-    amp = 10e-9
-    data = np.eye(n_vertices + 1)[:n_vertices]
-    data[-1, -1] = 1.
-    data *= amp
-    stc = SourceEstimate(data, vertices, 0., 1e-3, 'sample')
-    sim = simulate_evoked(fwd, stc, evoked.info, cov=None, snr=np.inf)
-
-    cov = make_ad_hoc_cov(evoked.info)
-    dip = fit_dipole(sim, cov, bem, min_dist=0.001)[0]
-
-    ds = []
-    for vi in range(n_vertices):
-        if vi < len(vertices[0]):
-            hi = 0
-            vertno = vi
-        else:
-            hi = 1
-            vertno = vi - len(vertices[0])
-        vertno = src[hi]['vertno'][vertno]
-        rr = src[hi]['rr'][vertno]
-        d = np.sqrt(np.sum((rr - dip.pos[vi]) ** 2))
-        ds.append(d)
-    # make sure that our median is sub-mm and the large majority are very close
-    # (we expect some to be off by a bit e.g. because they are radial)
-    assert_true((np.percentile(ds, [50, 90]) < [0.0005, 0.002]).all())
+    for rad, perc_90 in zip((0.09, None), (0.002, 0.004)):
+        bem = make_sphere_model('auto', rad, evoked.info,
+                                relative_radii=(0.999, 0.998, 0.997, 0.995))
+        src = read_source_spaces(fname_src)
+
+        fwd = make_forward_solution(evoked.info, None, src, bem)
+        fwd = convert_forward_solution(fwd, force_fixed=True)
+        vertices = [src[0]['vertno'], src[1]['vertno']]
+        n_vertices = sum(len(v) for v in vertices)
+        amp = 10e-9
+        data = np.eye(n_vertices + 1)[:n_vertices]
+        data[-1, -1] = 1.
+        data *= amp
+        stc = SourceEstimate(data, vertices, 0., 1e-3, 'sample')
+        sim = simulate_evoked(fwd, stc, evoked.info, cov=None, snr=np.inf)
+
+        cov = make_ad_hoc_cov(evoked.info)
+        dip = fit_dipole(sim, cov, bem, min_dist=0.001)[0]
+
+        ds = []
+        for vi in range(n_vertices):
+            if vi < len(vertices[0]):
+                hi = 0
+                vertno = vi
+            else:
+                hi = 1
+                vertno = vi - len(vertices[0])
+            vertno = src[hi]['vertno'][vertno]
+            rr = src[hi]['rr'][vertno]
+            d = np.sqrt(np.sum((rr - dip.pos[vi]) ** 2))
+            ds.append(d)
+        # make sure that our median is sub-mm and the large majority are very
+        # close (we expect some to be off by a bit e.g. because they are
+        # radial)
+        assert_true((np.percentile(ds, [50, 90]) < [0.0005, perc_90]).all())
+
+
+ at testing.requires_testing_data
+def test_dipole_fixed():
+    """Test reading a fixed-position dipole (from Xfit)"""
+    dip = read_dipole(fname_xfit_dip)
+    _check_roundtrip_fixed(dip)
+
+
+def _check_roundtrip_fixed(dip):
+    """Helper to test roundtrip IO for fixed dipoles"""
+    tempdir = _TempDir()
+    dip.save(op.join(tempdir, 'test-dip.fif.gz'))
+    dip_read = read_dipole(op.join(tempdir, 'test-dip.fif.gz'))
+    assert_allclose(dip_read.data, dip_read.data)
+    assert_allclose(dip_read.times, dip.times)
+    assert_equal(dip_read.info['xplotter_layout'], dip.info['xplotter_layout'])
+    assert_equal(dip_read.ch_names, dip.ch_names)
+    for ch_1, ch_2 in zip(dip_read.info['chs'], dip.info['chs']):
+        assert_equal(ch_1['ch_name'], ch_2['ch_name'])
+        for key in ('loc', 'kind', 'unit_mul', 'range', 'coord_frame', 'unit',
+                    'cal', 'coil_type', 'scanno', 'logno'):
+            assert_allclose(ch_1[key], ch_2[key], err_msg=key)
 
 run_tests_if_main(False)
diff --git a/mne/tests/test_docstring_parameters.py b/mne/tests/test_docstring_parameters.py
index 0216ecd..5d1bb5c 100644
--- a/mne/tests/test_docstring_parameters.py
+++ b/mne/tests/test_docstring_parameters.py
@@ -66,6 +66,7 @@ def get_name(func):
 _docstring_ignores = [
     'mne.io.write',  # always ignore these
     'mne.fixes._in1d',  # fix function
+    'mne.epochs.average_movements',  # deprecated pos param
 ]
 
 _tab_ignores = [
diff --git a/mne/tests/test_epochs.py b/mne/tests/test_epochs.py
index d75adf0..9803d09 100644
--- a/mne/tests/test_epochs.py
+++ b/mne/tests/test_epochs.py
@@ -17,10 +17,9 @@ import warnings
 from scipy import fftpack
 import matplotlib
 
-from mne import (Epochs, read_events, pick_events, read_epochs,
+from mne import (Epochs, Annotations, read_events, pick_events, read_epochs,
                  equalize_channels, pick_types, pick_channels, read_evokeds,
-                 write_evokeds, create_info, make_fixed_length_events,
-                 get_chpi_positions)
+                 write_evokeds, create_info, make_fixed_length_events)
 from mne.preprocessing import maxwell_filter
 from mne.epochs import (
     bootstrap, equalize_epoch_counts, combine_event_ids, add_channels_epochs,
@@ -28,6 +27,7 @@ from mne.epochs import (
 from mne.utils import (_TempDir, requires_pandas, slow_test,
                        clean_warning_registry, run_tests_if_main,
                        requires_version)
+from mne.chpi import read_head_pos, head_pos_to_trans_rot_t
 
 from mne.io import RawArray, Raw
 from mne.io.proj import _has_eeg_average_ref_proj
@@ -36,7 +36,7 @@ from mne.io.constants import FIFF
 from mne.externals.six import text_type
 from mne.externals.six.moves import zip, cPickle as pickle
 from mne.datasets import testing
-from mne.tests.common import assert_meg_snr
+from mne.tests.common import assert_meg_snr, assert_naming
 
 matplotlib.use('Agg')  # for testing don't use X server
 
@@ -80,8 +80,7 @@ def test_average_movements():
     # usable data
     crop = 0., 10.
     origin = (0., 0., 0.04)
-    with warnings.catch_warnings(record=True):  # MaxShield
-        raw = Raw(fname_raw_move, allow_maxshield=True)
+    raw = Raw(fname_raw_move, allow_maxshield='yes')
     raw.info['bads'] += ['MEG2443']  # mark some bad MEG channel
     raw.crop(*crop, copy=False).load_data()
     raw.filter(None, 20, method='iir')
@@ -99,20 +98,23 @@ def test_average_movements():
                              picks=picks, proj=False)
     evoked_sss_stat = epochs_sss_stat.average()
     del raw_sss_stat, epochs_sss_stat
-    pos = get_chpi_positions(fname_raw_move_pos)
-    ts = pos[2]
+    head_pos = read_head_pos(fname_raw_move_pos)
     trans = epochs.info['dev_head_t']['trans']
-    pos_stat = (np.array([trans[:3, 3]]),
-                np.array([trans[:3, :3]]),
-                np.array([0.]))
+    head_pos_stat = (np.array([trans[:3, 3]]),
+                     np.array([trans[:3, :3]]),
+                     np.array([0.]))
 
     # SSS-based
-    evoked_move_non = average_movements(epochs, pos=pos, weight_all=False,
-                                        origin=origin)
-    evoked_move_all = average_movements(epochs, pos=pos, weight_all=True,
-                                        origin=origin)
-    evoked_stat_all = average_movements(epochs, pos=pos_stat, weight_all=True,
-                                        origin=origin)
+    assert_raises(TypeError, average_movements, epochs, None)
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')  # deprecated param, pos -> head_pos
+        evoked_move_non = average_movements(epochs, pos=head_pos,
+                                            weight_all=False, origin=origin)
+    assert_equal(len(w), 1)
+    evoked_move_all = average_movements(epochs, head_pos=head_pos,
+                                        weight_all=True, origin=origin)
+    evoked_stat_all = average_movements(epochs, head_pos=head_pos_stat,
+                                        weight_all=True, origin=origin)
     evoked_std = epochs.average()
     for ev in (evoked_move_non, evoked_move_all, evoked_stat_all):
         assert_equal(ev.nave, evoked_std.nave)
@@ -126,8 +128,7 @@ def test_average_movements():
     assert_allclose(evoked_move_non.data[meg_picks],
                     evoked_move_all.data[meg_picks])
     # compare to averaged movecomp version (should be fairly similar)
-    raw_sss = Raw(fname_raw_movecomp_sss)
-    raw_sss.crop(*crop, copy=False).load_data()
+    raw_sss = Raw(fname_raw_movecomp_sss).crop(*crop, copy=False).load_data()
     raw_sss.filter(None, 20, method='iir')
     picks_sss = pick_types(raw_sss.info, meg=True, eeg=True, stim=True,
                            ecg=True, eog=True, exclude=())
@@ -144,14 +145,30 @@ def test_average_movements():
     # these should be close to numerical precision
     assert_allclose(evoked_sss_stat.data, evoked_stat_all.data, atol=1e-20)
 
+    # pos[0] > epochs.events[0] uses dev_head_t, so make it equivalent
+    destination = deepcopy(epochs.info['dev_head_t'])
+    x = head_pos_to_trans_rot_t(head_pos[1])
+    epochs.info['dev_head_t']['trans'][:3, :3] = x[1]
+    epochs.info['dev_head_t']['trans'][:3, 3] = x[0]
+    assert_raises(AssertionError, assert_allclose,
+                  epochs.info['dev_head_t']['trans'],
+                  destination['trans'])
+    evoked_miss = average_movements(epochs, head_pos=head_pos[2:],
+                                    origin=origin, destination=destination)
+    assert_allclose(evoked_miss.data, evoked_move_all.data)
+    assert_allclose(evoked_miss.info['dev_head_t']['trans'],
+                    destination['trans'])
+
     # degenerate cases
-    ts += 10.
-    assert_raises(RuntimeError, average_movements, epochs, pos=pos)  # bad pos
-    ts -= 10.
-    assert_raises(TypeError, average_movements, 'foo', pos=pos)
-    assert_raises(RuntimeError, average_movements, epochs_proj, pos=pos)  # prj
+    destination['to'] = destination['from']  # bad dest
+    assert_raises(RuntimeError, average_movements, epochs, head_pos,
+                  origin=origin, destination=destination)
+    assert_raises(TypeError, average_movements, 'foo', head_pos=head_pos)
+    assert_raises(RuntimeError, average_movements, epochs_proj,
+                  head_pos=head_pos)  # prj
     epochs.info['comps'].append([0])
-    assert_raises(RuntimeError, average_movements, epochs, pos=pos)
+    assert_raises(RuntimeError, average_movements, epochs, head_pos=head_pos)
+    epochs.info['comps'].pop()
 
 
 def test_reject():
@@ -168,6 +185,9 @@ def test_reject():
                   picks=picks, preload=False, reject='foo')
     assert_raises(ValueError, Epochs, raw, events, event_id, tmin, tmax,
                   picks=picks_meg, preload=False, reject=dict(eeg=1.))
+    # this one is okay because it's not actually requesting rejection
+    Epochs(raw, events, event_id, tmin, tmax, picks=picks_meg,
+           preload=False, reject=dict(eeg=np.inf))
     for val in (None, -1):  # protect against older MNE-C types
         for kwarg in ('reject', 'flat'):
             assert_raises(ValueError, Epochs, raw, events, event_id,
@@ -183,8 +203,8 @@ def test_reject():
             # no rejection
             epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                             preload=preload)
-            assert_raises(ValueError, epochs.drop_bad_epochs, reject='foo')
-            epochs.drop_bad_epochs()
+            assert_raises(ValueError, epochs.drop_bad, reject='foo')
+            epochs.drop_bad()
             assert_equal(len(epochs), len(events))
             assert_array_equal(epochs.selection, np.arange(len(events)))
             assert_array_equal(epochs.drop_log, [[]] * 7)
@@ -195,7 +215,7 @@ def test_reject():
             # with rejection
             epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                             reject=reject, preload=preload)
-            epochs.drop_bad_epochs()
+            epochs.drop_bad()
             assert_equal(len(epochs), len(events) - 4)
             assert_array_equal(epochs.selection, selection)
             assert_array_equal(epochs.drop_log, drop_log)
@@ -204,10 +224,10 @@ def test_reject():
             # rejection post-hoc
             epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                             preload=preload)
-            epochs.drop_bad_epochs()
+            epochs.drop_bad()
             assert_equal(len(epochs), len(events))
             assert_array_equal(epochs.get_data(), data_7[proj])
-            epochs.drop_bad_epochs(reject)
+            epochs.drop_bad(reject)
             assert_equal(len(epochs), len(events) - 4)
             assert_equal(len(epochs), len(epochs.get_data()))
             assert_array_equal(epochs.selection, selection)
@@ -218,21 +238,21 @@ def test_reject():
             reject_part = dict(grad=1100e-12, mag=4e-12, eeg=80e-6, eog=150e-6)
             epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                             reject=reject_part, preload=preload)
-            epochs.drop_bad_epochs()
+            epochs.drop_bad()
             assert_equal(len(epochs), len(events) - 1)
-            epochs.drop_bad_epochs(reject)
+            epochs.drop_bad(reject)
             assert_equal(len(epochs), len(events) - 4)
             assert_array_equal(epochs.selection, selection)
             assert_array_equal(epochs.drop_log, drop_log)
             assert_array_equal(epochs.get_data(), data_7[proj][keep_idx])
 
             # ensure that thresholds must become more stringent, not less
-            assert_raises(ValueError, epochs.drop_bad_epochs, reject_part)
+            assert_raises(ValueError, epochs.drop_bad, reject_part)
             assert_equal(len(epochs), len(events) - 4)
             assert_array_equal(epochs.get_data(), data_7[proj][keep_idx])
-            epochs.drop_bad_epochs(flat=dict(mag=1.))
+            epochs.drop_bad(flat=dict(mag=1.))
             assert_equal(len(epochs), 0)
-            assert_raises(ValueError, epochs.drop_bad_epochs,
+            assert_raises(ValueError, epochs.drop_bad,
                           flat=dict(mag=0.))
 
             # rejection of subset of trials (ensure array ownership)
@@ -240,10 +260,20 @@ def test_reject():
             epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                             reject=None, preload=preload)
             epochs = epochs[:-1]
-            epochs.drop_bad_epochs(reject=reject)
+            epochs.drop_bad(reject=reject)
             assert_equal(len(epochs), len(events) - 4)
             assert_array_equal(epochs.get_data(), data_7[proj][keep_idx])
 
+            # rejection on annotations
+            raw.annotations = Annotations([events[0][0] / raw.info['sfreq']],
+                                          [1], ['BAD'])
+            epochs = Epochs(raw, events, event_id, tmin, tmax, picks=[0],
+                            reject=None, preload=preload)
+            epochs.drop_bad()
+            assert_equal(len(events) - 1, len(epochs.events))
+            assert_equal(epochs.drop_log[0][0], 'BAD')
+            raw.annotations = None
+
 
 def test_decim():
     """Test epochs decimation
@@ -259,10 +289,12 @@ def test_decim():
     info = create_info(n_channels, sfreq, 'eeg')
     info['lowpass'] = sfreq_new / float(decim)
     epochs = EpochsArray(data, info, events)
-    data_epochs = epochs.decimate(decim, copy=True).get_data()
-    data_epochs_2 = epochs.decimate(dec_1).decimate(dec_2).get_data()
+    data_epochs = epochs.copy().decimate(decim).get_data()
+    data_epochs_2 = epochs.copy().decimate(decim, offset=1).get_data()
+    data_epochs_3 = epochs.decimate(dec_1).decimate(dec_2).get_data()
     assert_array_equal(data_epochs, data[:, :, ::decim])
-    assert_array_equal(data_epochs, data_epochs_2)
+    assert_array_equal(data_epochs_2, data[:, :, 1::decim])
+    assert_array_equal(data_epochs, data_epochs_3)
 
     # Now let's do it with some real data
     raw, events, picks = _get_data()
@@ -271,6 +303,8 @@ def test_decim():
     epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                     preload=False)
     assert_raises(ValueError, epochs.decimate, -1)
+    assert_raises(ValueError, epochs.decimate, 2, offset=-1)
+    assert_raises(ValueError, epochs.decimate, 2, offset=2)
     expected_data = epochs.get_data()[:, :, ::decim]
     expected_times = epochs.times[::decim]
     for preload in (True, False):
@@ -450,7 +484,7 @@ def test_read_epochs_bad_events():
     epochs = Epochs(raw, np.array([[raw.first_samp, 0, event_id]]),
                     event_id, tmin, tmax, picks=picks, baseline=(None, 0))
     assert_true(repr(epochs))  # test repr
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
     assert_true(repr(epochs))
     with warnings.catch_warnings(record=True):
         evoked = epochs.average()
@@ -469,7 +503,7 @@ def test_read_epochs_bad_events():
 def test_read_write_epochs():
     """Test epochs from raw files with IO as fif file
     """
-    raw, events, picks = _get_data()
+    raw, events, picks = _get_data(preload=True)
     tempdir = _TempDir()
     temp_fname = op.join(tempdir, 'test-epo.fif')
     temp_fname_no_bl = op.join(tempdir, 'test_no_bl-epo.fif')
@@ -575,10 +609,13 @@ def test_read_write_epochs():
         epochs_read = read_epochs(temp_fname)
         epochs_no_bl_read = read_epochs(temp_fname_no_bl)
         assert_raises(ValueError, epochs.apply_baseline, baseline=[1, 2, 3])
-        epochs_no_bl_read.apply_baseline(baseline)
-        assert_true(epochs_no_bl_read.baseline == baseline)
+        epochs_with_bl = epochs_no_bl_read.copy().apply_baseline(baseline)
+        assert_true(isinstance(epochs_with_bl, _BaseEpochs))
+        assert_true(epochs_with_bl.baseline == baseline)
+        assert_true(epochs_no_bl_read.baseline != baseline)
         assert_true(str(epochs_read).startswith('<Epochs'))
 
+        epochs_no_bl_read.apply_baseline(baseline)
         assert_array_equal(epochs_no_bl_read.times, epochs.times)
         assert_array_almost_equal(epochs_read.get_data(), epochs.get_data())
         assert_array_almost_equal(epochs.get_data(),
@@ -618,9 +655,9 @@ def test_read_write_epochs():
         epochs_read4 = epochs_read3.copy()
         assert_array_almost_equal(epochs_read4.get_data(), data)
         # test equalizing loaded one (drop_log property)
-        epochs_read4.equalize_event_counts(epochs.event_id)
+        epochs_read4.equalize_event_counts(epochs.event_id, copy=False)
 
-        epochs.drop_epochs([1, 2], reason='can we recover orig ID?')
+        epochs.drop([1, 2], reason='can we recover orig ID?')
         epochs.save(temp_fname)
         epochs_read5 = read_epochs(temp_fname, preload=preload)
         assert_array_equal(epochs_read5.selection, epochs.selection)
@@ -637,7 +674,7 @@ def test_read_write_epochs():
             epochs_badname = op.join(tempdir, 'test-bad-name.fif.gz')
             epochs.save(epochs_badname)
             read_epochs(epochs_badname, preload=preload)
-        assert_true(len(w) == 2)
+        assert_naming(w, 'test_epochs.py', 2)
 
         # test loading epochs with missing events
         epochs = Epochs(raw, events, dict(foo=1, bar=999), tmin, tmax,
@@ -658,8 +695,7 @@ def test_read_write_epochs():
         assert_equal(epochs.drop_log, epochs_read.drop_log)
 
         # Test that having a single time point works
-        epochs.load_data()
-        epochs.crop(0, 0, copy=False)
+        epochs.load_data().crop(0, 0)
         assert_equal(len(epochs.times), 1)
         assert_equal(epochs.get_data().shape[-1], 1)
         epochs.save(temp_fname)
@@ -737,7 +773,7 @@ def test_epochs_proj():
                     baseline=(None, 0), preload=True, add_eeg_ref=False)
     epochs.pick_channels(['EEG 001', 'EEG 002'])
     assert_equal(len(epochs), 7)  # sufficient for testing
-    temp_fname = 'test.fif'
+    temp_fname = op.join(tempdir, 'test-epo.fif')
     epochs.save(temp_fname)
     for preload in (True, False):
         epochs = read_epochs(temp_fname, add_eeg_ref=True, proj=True,
@@ -787,8 +823,10 @@ def test_evoked_io_from_epochs():
                         picks=picks, baseline=(None, 0), decim=5)
     assert_true(len(w) == 1)
     evoked = epochs.average()
+    evoked.info['proj_name'] = ''  # Test that empty string shortcuts to None.
     evoked.save(op.join(tempdir, 'evoked-ave.fif'))
     evoked2 = read_evokeds(op.join(tempdir, 'evoked-ave.fif'))[0]
+    assert_equal(evoked2.info['proj_name'], None)
     assert_allclose(evoked.data, evoked2.data, rtol=1e-4, atol=1e-20)
     assert_allclose(evoked.times, evoked2.times, rtol=1e-4,
                     atol=1 / evoked.info['sfreq'])
@@ -871,7 +909,7 @@ def test_reject_epochs():
     reject_crazy = dict(grad=1000e-15, mag=4e-15, eeg=80e-9, eog=150e-9)
     epochs = Epochs(raw_2, events1, event_id, tmin, tmax, baseline=(None, 0),
                     reject=reject_crazy, flat=flat)
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
 
     assert_true(all('MEG 2442' in e for e in epochs.drop_log))
     assert_true(all('MEG 2443' not in e for e in epochs.drop_log))
@@ -945,7 +983,7 @@ def test_indexing_slicing():
                          reject=reject, flat=flat)
 
         if not preload:
-            epochs2.drop_bad_epochs()
+            epochs2.drop_bad()
 
         # using slicing
         epochs2_sliced = epochs2[start_index:end_index]
@@ -1020,12 +1058,17 @@ def test_crop():
     tmask = (epochs.times >= tmin_window) & (epochs.times <= tmax_window)
     assert_true(tmin_window > tmin)
     assert_true(tmax_window < tmax)
-    epochs3 = epochs2.crop(tmin_window, tmax_window, copy=True)
+    epochs3 = epochs2.copy().crop(tmin_window, tmax_window)
     data3 = epochs3.get_data()
     epochs2.crop(tmin_window, tmax_window)
     data2 = epochs2.get_data()
     assert_array_equal(data2, data_normal[:, :, tmask])
     assert_array_equal(data3, data_normal[:, :, tmask])
+    assert_array_equal(epochs.time_as_index([tmin, tmax], use_rounding=True),
+                       [0, len(epochs.times) - 1])
+    assert_array_equal(epochs3.time_as_index([tmin_window, tmax_window],
+                                             use_rounding=True),
+                       [0, len(epochs3.times) - 1])
 
     # test time info is correct
     epochs = EpochsArray(np.zeros((1, 1, 1000)), create_info(1, 1000., 'eeg'),
@@ -1036,6 +1079,19 @@ def test_crop():
         epochs.decimate(10)
     assert_allclose(last_time, epochs.times[-1])
 
+    epochs = Epochs(raw, events[:5], event_id, -1, 1,
+                    picks=picks, baseline=(None, 0), preload=True,
+                    reject=reject, flat=flat)
+    # We include nearest sample, so actually a bit beyound our bounds here
+    assert_allclose(epochs.tmin, -1.0006410259015925, rtol=1e-12)
+    assert_allclose(epochs.tmax, 1.0006410259015925, rtol=1e-12)
+    epochs_crop = epochs.copy().crop(-1, 1)
+    assert_allclose(epochs.times, epochs_crop.times, rtol=1e-12)
+    # Ensure we don't allow silly crops
+    with warnings.catch_warnings(record=True):  # tmin/tmax out of bounds
+        assert_raises(ValueError, epochs.crop, 1000, 2000)
+        assert_raises(ValueError, epochs.crop, 0.1, 0)
+
 
 def test_resample():
     """Test of resample of epochs
@@ -1080,9 +1136,9 @@ def test_resample():
 
     # test copy flag
     epochs = epochs_o.copy()
-    epochs_resampled = epochs.resample(sfreq_normal * 2, npad=0, copy=True)
+    epochs_resampled = epochs.copy().resample(sfreq_normal * 2, npad=0)
     assert_true(epochs_resampled is not epochs)
-    epochs_resampled = epochs.resample(sfreq_normal * 2, npad=0, copy=False)
+    epochs_resampled = epochs.resample(sfreq_normal * 2, npad=0)
     assert_true(epochs_resampled is epochs)
 
     # test proper setting of times (#2645)
@@ -1093,8 +1149,8 @@ def test_resample():
     epochs1 = EpochsArray(data, deepcopy(info), events)
     epochs2 = EpochsArray(data, deepcopy(info), events)
     epochs = concatenate_epochs([epochs1, epochs2])
-    epochs1.resample(epochs1.info['sfreq'] // 2)
-    epochs2.resample(epochs2.info['sfreq'] // 2)
+    epochs1.resample(epochs1.info['sfreq'] // 2, npad='auto')
+    epochs2.resample(epochs2.info['sfreq'] // 2, npad='auto')
     epochs = concatenate_epochs([epochs1, epochs2])
     for e in epochs1, epochs2, epochs:
         assert_equal(e.times[0], epochs.tmin)
@@ -1222,7 +1278,7 @@ def test_epoch_eq():
     # equalizing epochs objects
     epochs_1 = Epochs(raw, events, event_id, tmin, tmax, picks=picks)
     epochs_2 = Epochs(raw, events, event_id_2, tmin, tmax, picks=picks)
-    epochs_1.drop_bad_epochs()  # make sure drops are logged
+    epochs_1.drop_bad()  # make sure drops are logged
     assert_true(len([l for l in epochs_1.drop_log if not l]) ==
                 len(epochs_1.events))
     drop_log1 = epochs_1.drop_log = [[] for _ in range(len(epochs_1.events))]
@@ -1243,7 +1299,7 @@ def test_epoch_eq():
     # equalizing conditions
     epochs = Epochs(raw, events, {'a': 1, 'b': 2, 'c': 3, 'd': 4},
                     tmin, tmax, picks=picks, reject=reject)
-    epochs.drop_bad_epochs()  # make sure drops are logged
+    epochs.drop_bad()  # make sure drops are logged
     assert_true(len([l for l in epochs.drop_log if not l]) ==
                 len(epochs.events))
     drop_log1 = deepcopy(epochs.drop_log)
@@ -1266,16 +1322,16 @@ def test_epoch_eq():
     new_shapes = [epochs[key].events.shape[0] for key in ['a', 'b', 'c', 'd']]
     assert_true(new_shapes[0] + new_shapes[1] == new_shapes[2])
     assert_true(new_shapes[3] == old_shapes[3])
-    assert_raises(KeyError, epochs.equalize_event_counts, [1, 'a'])
+    assert_raises(KeyError, epochs.equalize_event_counts, [1, 'a'], copy=False)
 
     # now let's combine conditions
     old_shapes = new_shapes
-    epochs = epochs.equalize_event_counts([['a', 'b'], ['c', 'd']])[0]
+    epochs.equalize_event_counts([['a', 'b'], ['c', 'd']], copy=False)
     new_shapes = [epochs[key].events.shape[0] for key in ['a', 'b', 'c', 'd']]
     assert_true(old_shapes[0] + old_shapes[1] == new_shapes[0] + new_shapes[1])
     assert_true(new_shapes[0] + new_shapes[1] == new_shapes[2] + new_shapes[3])
     assert_raises(ValueError, combine_event_ids, epochs, ['a', 'b'],
-                  {'ab': 1})
+                  {'ab': 1}, copy=False)
 
     combine_event_ids(epochs, ['a', 'b'], {'ab': 12}, copy=False)
     caught = 0
@@ -1284,7 +1340,7 @@ def test_epoch_eq():
             epochs[key]
         except KeyError:
             caught += 1
-    assert_raises(Exception, caught == 2)
+    assert_equal(caught, 2)
     assert_true(not np.any(epochs.events[:, 2] == 1))
     assert_true(not np.any(epochs.events[:, 2] == 2))
     epochs = combine_event_ids(epochs, ['c', 'd'], {'cd': 34})
@@ -1297,11 +1353,21 @@ def test_epoch_eq():
     epochs = Epochs(raw, events, {'a/x': 1, 'b/x': 2, 'a/y': 3, 'b/y': 4},
                     tmin, tmax, picks=picks, reject=reject)
     cond1, cond2 = ['a', ['b/x', 'b/y']], [['a/x', 'a/y'], 'b']
-    es = [epochs.equalize_event_counts(c)[0] for c in (cond1, cond2)]
+    es = [epochs.copy().equalize_event_counts(c, copy=False)[0]
+          for c in (cond1, cond2)]
     assert_array_equal(es[0].events[:, 0], es[1].events[:, 0])
     cond1, cond2 = ['a', ['b', 'b/y']], [['a/x', 'a/y'], 'x']
     for c in (cond1, cond2):  # error b/c tag and id mix/non-orthogonal tags
-        assert_raises(ValueError, epochs.equalize_event_counts, c)
+        assert_raises(ValueError, epochs.equalize_event_counts, c, copy=False)
+    assert_raises(KeyError, epochs.equalize_event_counts,
+                  ["a/no_match", "b"], copy=False)
+    # test equalization with no events of one type
+    epochs.drop(np.arange(10))
+    assert_equal(len(epochs['a/x']), 0)
+    assert_true(len(epochs['a/y']) > 0)
+    epochs.equalize_event_counts(['a/x', 'a/y'], copy=False)
+    assert_equal(len(epochs['a/x']), 0)
+    assert_equal(len(epochs['a/y']), 0)
 
 
 def test_access_by_name():
@@ -1435,10 +1501,11 @@ def test_epochs_proj_mixin():
             n_proj = len(epochs.info['projs'])
             epochs.del_proj(0)
             assert_true(len(epochs.info['projs']) == n_proj - 1)
+            # Test that already existing projections are not added.
             epochs.add_proj(projs, remove_existing=False)
-            assert_true(len(epochs.info['projs']) == 2 * n_proj - 1)
-            epochs.add_proj(projs, remove_existing=True)
             assert_true(len(epochs.info['projs']) == n_proj)
+            epochs.add_proj(projs[:-1], remove_existing=True)
+            assert_true(len(epochs.info['projs']) == n_proj - 1)
 
     # catch no-gos.
     # wrong proj argument
@@ -1449,17 +1516,26 @@ def test_epochs_proj_mixin():
         epochs = Epochs(raw, events[:4], event_id, tmin, tmax, picks=picks,
                         baseline=(None, 0), proj='delayed', preload=preload,
                         add_eeg_ref=True, reject=reject)
-        epochs2 = Epochs(raw, events[:4], event_id, tmin, tmax, picks=picks,
-                         baseline=(None, 0), proj=True, preload=preload,
-                         add_eeg_ref=True, reject=reject)
+        epochs_proj = Epochs(
+            raw, events[:4], event_id, tmin, tmax, picks=picks,
+            baseline=(None, 0), proj=True, preload=preload, add_eeg_ref=True,
+            reject=reject)
+
+        epochs_noproj = Epochs(
+            raw, events[:4], event_id, tmin, tmax, picks=picks,
+            baseline=(None, 0), proj=False, preload=preload, add_eeg_ref=True,
+            reject=reject)
 
-        assert_allclose(epochs.copy().apply_proj().get_data()[0],
-                        epochs2.get_data()[0], rtol=1e-10, atol=1e-25)
+        assert_allclose(epochs.copy().apply_proj().get_data(),
+                        epochs_proj.get_data(), rtol=1e-10, atol=1e-25)
+        assert_allclose(epochs.get_data(),
+                        epochs_noproj.get_data(), rtol=1e-10, atol=1e-25)
 
         # make sure data output is constant across repeated calls
         # e.g. drop bads
         assert_array_equal(epochs.get_data(), epochs.get_data())
-        assert_array_equal(epochs2.get_data(), epochs2.get_data())
+        assert_array_equal(epochs_proj.get_data(), epochs_proj.get_data())
+        assert_array_equal(epochs_noproj.get_data(), epochs_noproj.get_data())
 
     # test epochs.next calls
     data = epochs.get_data().copy()
@@ -1468,8 +1544,8 @@ def test_epochs_proj_mixin():
 
     # cross application from processing stream 1 to 2
     epochs.apply_proj()
-    assert_array_equal(epochs._projector, epochs2._projector)
-    assert_allclose(epochs._data, epochs2.get_data())
+    assert_array_equal(epochs._projector, epochs_proj._projector)
+    assert_allclose(epochs._data, epochs_proj.get_data())
 
     # test mixin against manual application
     epochs = Epochs(raw, events[:4], event_id, tmin, tmax, picks=picks,
@@ -1480,34 +1556,65 @@ def test_epochs_proj_mixin():
 
 
 def test_delayed_epochs():
-    """Test delayed projection
-    """
+    """Test delayed projection on Epochs"""
     raw, events, picks = _get_data()
     events = events[:10]
     picks = np.concatenate([pick_types(raw.info, meg=True, eeg=True)[::22],
                             pick_types(raw.info, meg=False, eeg=False,
                                        ecg=True, eog=True)])
     picks = np.sort(picks)
+    raw.load_data().pick_channels([raw.ch_names[pick] for pick in picks])
+    raw.info.normalize_proj()
+    del picks
+    n_epochs = 2  # number we expect after rejection
     raw.info['lowpass'] = 40.  # fake the LP info so no warnings
-    for preload in (True, False):
-        for proj in (True, False, 'delayed'):
-            for decim in (1, 3):
-                for ii in range(2):
-                    epochs = Epochs(raw, events, event_id, tmin, tmax,
-                                    picks=picks, proj=proj, reject=reject,
-                                    preload=preload, decim=decim)
+    for decim in (1, 3):
+        proj_data = Epochs(raw, events, event_id, tmin, tmax, proj=True,
+                           reject=reject, decim=decim)
+        use_tmin = proj_data.tmin
+        proj_data = proj_data.get_data()
+        noproj_data = Epochs(raw, events, event_id, tmin, tmax, proj=False,
+                             reject=reject, decim=decim).get_data()
+        assert_equal(proj_data.shape, noproj_data.shape)
+        assert_equal(proj_data.shape[0], n_epochs)
+        for preload in (True, False):
+            for proj in (True, False, 'delayed'):
+                for ii in range(3):
+                    print(decim, preload, proj, ii)
+                    comp = proj_data if proj is True else noproj_data
+                    if ii in (0, 1):
+                        epochs = Epochs(raw, events, event_id, tmin, tmax,
+                                        proj=proj, reject=reject,
+                                        preload=preload, decim=decim)
+                    else:
+                        fake_events = np.zeros((len(comp), 3), int)
+                        fake_events[:, 0] = np.arange(len(comp))
+                        fake_events[:, 2] = 1
+                        epochs = EpochsArray(comp, raw.info, tmin=use_tmin,
+                                             event_id=1, events=fake_events,
+                                             proj=proj)
+                        epochs.info['sfreq'] /= decim
+                        assert_equal(len(epochs), n_epochs)
+                    assert_true(raw.proj is False)
+                    assert_true(epochs.proj is
+                                (True if proj is True else False))
                     if ii == 1:
                         epochs.load_data()
                     picks_data = pick_types(epochs.info, meg=True, eeg=True)
                     evoked = epochs.average(picks=picks_data)
+                    assert_equal(evoked.nave, n_epochs, epochs.drop_log)
                     if proj is True:
                         evoked.apply_proj()
-                    epochs_data = epochs.get_data().mean(axis=0)[picks_data]
+                    else:
+                        assert_true(evoked.proj is False)
                     assert_array_equal(evoked.ch_names,
                                        np.array(epochs.ch_names)[picks_data])
                     assert_allclose(evoked.times, epochs.times)
-                    assert_allclose(evoked.data, epochs_data,
-                                    rtol=1e-5, atol=1e-15)
+                    epochs_data = epochs.get_data()
+                    assert_allclose(evoked.data,
+                                    epochs_data.mean(axis=0)[picks_data],
+                                    rtol=1e-5, atol=1e-20)
+                    assert_allclose(epochs_data, comp, rtol=1e-5, atol=1e-20)
 
 
 def test_drop_epochs():
@@ -1519,9 +1626,9 @@ def test_drop_epochs():
     events1 = events[events[:, 2] == event_id]
 
     # Bound checks
-    assert_raises(IndexError, epochs.drop_epochs, [len(epochs.events)])
-    assert_raises(IndexError, epochs.drop_epochs, [-1])
-    assert_raises(ValueError, epochs.drop_epochs, [[1, 2], [3, 4]])
+    assert_raises(IndexError, epochs.drop, [len(epochs.events)])
+    assert_raises(IndexError, epochs.drop, [-1])
+    assert_raises(ValueError, epochs.drop, [[1, 2], [3, 4]])
 
     # Test selection attribute
     assert_array_equal(epochs.selection,
@@ -1532,7 +1639,7 @@ def test_drop_epochs():
 
     selection = epochs.selection.copy()
     n_events = len(epochs.events)
-    epochs.drop_epochs([2, 4], reason='d')
+    epochs.drop([2, 4], reason='d')
     assert_equal(epochs.drop_log_stats(), 2. / n_events * 100)
     assert_equal(len(epochs.drop_log), len(events))
     assert_equal([epochs.drop_log[k]
@@ -1617,7 +1724,7 @@ def test_drop_channels_mixin():
     ch_names = epochs.ch_names[3:]
 
     ch_names_orig = epochs.ch_names
-    dummy = epochs.drop_channels(drop_ch, copy=True)
+    dummy = epochs.copy().drop_channels(drop_ch)
     assert_equal(ch_names, dummy.ch_names)
     assert_equal(ch_names_orig, epochs.ch_names)
     assert_equal(len(ch_names_orig), epochs.get_data().shape[1])
@@ -1638,7 +1745,7 @@ def test_pick_channels_mixin():
     assert_raises(RuntimeError, epochs.drop_channels, ['foo'])
     epochs.preload = True
     ch_names_orig = epochs.ch_names
-    dummy = epochs.pick_channels(ch_names, copy=True)
+    dummy = epochs.copy().pick_channels(ch_names)
     assert_equal(ch_names, dummy.ch_names)
     assert_equal(ch_names_orig, epochs.ch_names)
     assert_equal(len(ch_names_orig), epochs.get_data().shape[1])
@@ -1700,6 +1807,8 @@ def test_add_channels_epochs():
         epochs2 = add_channels_epochs([epochs_meg, epochs_eeg])
 
         assert_equal(len(epochs.info['projs']), len(epochs2.info['projs']))
+        assert_equal(len(epochs.info.keys()), len(epochs_meg.info.keys()))
+        assert_equal(len(epochs.info.keys()), len(epochs_eeg.info.keys()))
         assert_equal(len(epochs.info.keys()), len(epochs2.info.keys()))
 
         data1 = epochs.get_data()
@@ -1727,8 +1836,7 @@ def test_add_channels_epochs():
                   [epochs_meg, epochs_eeg[:2]])
 
     epochs_meg.info['chs'].pop(0)
-    epochs_meg.info['ch_names'].pop(0)
-    epochs_meg.info['nchan'] -= 1
+    epochs_meg.info._update_redundant()
     assert_raises(RuntimeError, add_channels_epochs,
                   [epochs_meg, epochs_eeg])
 
@@ -1743,9 +1851,9 @@ def test_add_channels_epochs():
                   [epochs_meg2, epochs_eeg])
 
     epochs_meg2 = epochs_meg.copy()
-    epochs_meg2.info['ch_names'][1] = epochs_meg2.info['ch_names'][0]
-    epochs_meg2.info['chs'][1]['ch_name'] = epochs_meg2.info['ch_names'][1]
-    assert_raises(ValueError, add_channels_epochs,
+    epochs_meg2.info['chs'][1]['ch_name'] = epochs_meg2.info['ch_names'][0]
+    epochs_meg2.info._update_redundant()
+    assert_raises(RuntimeError, add_channels_epochs,
                   [epochs_meg2, epochs_eeg])
 
     epochs_meg2 = epochs_meg.copy()
@@ -1866,6 +1974,12 @@ def test_array_epochs():
     epochs = EpochsArray(data_1, info, events=events_1, event_id=1,
                          tmin=-0.2, baseline=(None, 0))
 
+    # default events
+    epochs = EpochsArray(data_1, info)
+    assert_array_equal(epochs.events[:, 0], np.arange(len(data_1)))
+    assert_array_equal(epochs.events[:, 1], np.zeros(len(data_1), int))
+    assert_array_equal(epochs.events[:, 2], np.ones(len(data_1), int))
+
 
 def test_concatenate_epochs():
     """Test concatenate epochs"""
@@ -1891,7 +2005,7 @@ def test_concatenate_epochs():
     epochs2.preload = True
     assert_raises(
         ValueError, concatenate_epochs,
-        [epochs, epochs2.drop_channels(epochs2.ch_names[:1], copy=True)])
+        [epochs, epochs2.copy().drop_channels(epochs2.ch_names[:1])])
 
     epochs2.times = np.delete(epochs2.times, 1)
     assert_raises(
@@ -1915,14 +2029,14 @@ def test_add_channels():
     epoch = Epochs(
         raw=raw, events=events, event_id=event_id, tmin=tmin, tmax=tmax,
         picks=picks, preload=True)
-    epoch_eeg = epoch.pick_types(meg=False, eeg=True, copy=True)
-    epoch_meg = epoch.pick_types(meg=True, copy=True)
-    epoch_stim = epoch.pick_types(meg=False, stim=True, copy=True)
-    epoch_eeg_meg = epoch.pick_types(meg=True, eeg=True, copy=True)
-    epoch_new = epoch_meg.add_channels([epoch_eeg, epoch_stim], copy=True)
+    epoch_eeg = epoch.copy().pick_types(meg=False, eeg=True)
+    epoch_meg = epoch.copy().pick_types(meg=True)
+    epoch_stim = epoch.copy().pick_types(meg=False, stim=True)
+    epoch_eeg_meg = epoch.copy().pick_types(meg=True, eeg=True)
+    epoch_new = epoch_meg.copy().add_channels([epoch_eeg, epoch_stim])
     assert_true(all(ch in epoch_new.ch_names
                     for ch in epoch_stim.ch_names + epoch_meg.ch_names))
-    epoch_new = epoch_meg.add_channels([epoch_eeg], copy=True)
+    epoch_new = epoch_meg.copy().add_channels([epoch_eeg])
 
     assert_true(ch in epoch_new.ch_names for ch in epoch.ch_names)
     assert_array_equal(epoch_new._data, epoch_eeg_meg._data)
@@ -1941,16 +2055,28 @@ def test_add_channels():
     assert_raises(AssertionError, epoch_meg.add_channels, epoch_badsf)
 
 
-def test_seeg():
-    """Test the compatibility of the Epoch object with SEEG data."""
+def test_seeg_ecog():
+    """Test the compatibility of the Epoch object with SEEG and ECoG data."""
     n_epochs, n_channels, n_times, sfreq = 5, 10, 20, 1000.
     data = np.ones((n_epochs, n_channels, n_times))
     events = np.array([np.arange(n_epochs), [0] * n_epochs, [1] * n_epochs]).T
-    info = create_info(n_channels, sfreq, 'seeg')
-    epochs = EpochsArray(data, info, events)
-    picks = pick_types(epochs.info, meg=False, eeg=False, stim=False,
-                       eog=False, ecg=False, seeg=True, emg=False, exclude=[])
-    assert_equal(len(picks), n_channels)
+    pick_dict = dict(meg=False, exclude=[])
+    for key in ('seeg', 'ecog'):
+        info = create_info(n_channels, sfreq, key)
+        epochs = EpochsArray(data, info, events)
+        pick_dict.update({key: True})
+        picks = pick_types(epochs.info, **pick_dict)
+        del pick_dict[key]
+        assert_equal(len(picks), n_channels)
+
+
+def test_default_values():
+    """Test default event_id, tmax tmin values are working correctly"""
+    raw, events = _get_data()[:2]
+    epoch_1 = Epochs(raw, events[:1], preload=True)
+    epoch_2 = Epochs(raw, events[:1], event_id=None, tmin=-0.2, tmax=0.5,
+                     preload=True)
+    assert_equal(hash(epoch_1), hash(epoch_2))
 
 
 run_tests_if_main()
diff --git a/mne/tests/test_event.py b/mne/tests/test_event.py
index 2c5dd99..5df9092 100644
--- a/mne/tests/test_event.py
+++ b/mne/tests/test_event.py
@@ -3,11 +3,13 @@ import os
 
 from nose.tools import assert_true, assert_raises
 import numpy as np
-from numpy.testing import assert_array_almost_equal, assert_array_equal
+from numpy.testing import (assert_array_almost_equal, assert_array_equal,
+                           assert_equal)
 import warnings
 
 from mne import (read_events, write_events, make_fixed_length_events,
                  find_events, pick_events, find_stim_steps, io, pick_channels)
+from mne.tests.common import assert_naming
 from mne.utils import _TempDir, run_tests_if_main
 from mne.event import define_target_events, merge_events
 
@@ -26,13 +28,27 @@ fname_old_txt = op.join(base_dir, 'test-eve-old-style.eve')
 raw_fname = op.join(base_dir, 'test_raw.fif')
 
 
+def test_fix_stim():
+    """Test fixing stim STI016 for Neuromag"""
+    raw = io.read_raw_fif(raw_fname, preload=True)
+    # 32768 (016) + 3 (002+001) bits gets incorrectly coded during acquisition
+    raw._data[raw.ch_names.index('STI 014'), :3] = [0, -32765, 0]
+    with warnings.catch_warnings(record=True) as w:
+        events = find_events(raw, 'STI 014')
+    assert_true(len(w) >= 1)
+    assert_true(any('STI016' in str(ww.message) for ww in w))
+    assert_array_equal(events[0], [raw.first_samp + 1, 0, 32765])
+    events = find_events(raw, 'STI 014', uint_cast=True)
+    assert_array_equal(events[0], [raw.first_samp + 1, 0, 32771])
+
+
 def test_add_events():
     """Test adding events to a Raw file"""
     # need preload
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
     events = np.array([[raw.first_samp, 0, 1]])
     assert_raises(RuntimeError, raw.add_events, events, 'STI 014')
-    raw = io.Raw(raw_fname, preload=True)
+    raw = io.read_raw_fif(raw_fname, preload=True)
     orig_events = find_events(raw, 'STI 014')
     # add some events
     events = np.array([raw.first_samp, 0, 1])
@@ -50,26 +66,39 @@ def test_add_events():
 
 
 def test_merge_events():
-    """Test event merging
-    """
-    events = read_events(fname)  # Use as the gold standard
-    merges = [1, 2, 3, 4]
-    events_out = merge_events(events, merges, 1234)
-    events_out2 = events.copy()
-    for m in merges:
-        assert_true(not np.any(events_out[:, 2] == m))
-        events_out2[events[:, 2] == m, 2] = 1234
-    assert_array_equal(events_out, events_out2)
-    # test non-replacement functionality, should be sorted union of orig & new
-    events_out2 = merge_events(events, merges, 1234, False)
-    events_out = np.concatenate((events_out, events))
-    events_out = events_out[np.argsort(events_out[:, 0])]
-    assert_array_equal(events_out, events_out2)
+    """Test event merging"""
+    events_orig = [[1, 0, 1], [3, 0, 2], [10, 0, 3], [20, 0, 4]]
+
+    events_replacement = \
+        [[1, 0, 12],
+         [3, 0, 12],
+         [10, 0, 34],
+         [20, 0, 34]]
+
+    events_no_replacement = \
+        [[1, 0, 1],
+         [1, 0, 12],
+         [1, 0, 1234],
+         [3, 0, 2],
+         [3, 0, 12],
+         [3, 0, 1234],
+         [10, 0, 3],
+         [10, 0, 34],
+         [10, 0, 1234],
+         [20, 0, 4],
+         [20, 0, 34],
+         [20, 0, 1234]]
+
+    for replace_events, events_good in [(True, events_replacement),
+                                        (False, events_no_replacement)]:
+        events = merge_events(events_orig, [1, 2], 12, replace_events)
+        events = merge_events(events, [3, 4], 34, replace_events)
+        events = merge_events(events, [1, 2, 3, 4], 1234, replace_events)
+        assert_array_equal(events, events_good)
 
 
 def test_io_events():
-    """Test IO for events
-    """
+    """Test IO for events"""
     tempdir = _TempDir()
     # Test binary fif IO
     events = read_events(fname)  # Use as the gold standard
@@ -88,7 +117,10 @@ def test_io_events():
     write_events(op.join(tempdir, 'events.eve'), events)
     events2 = read_events(op.join(tempdir, 'events.eve'))
     assert_array_almost_equal(events, events2)
-    events2 = read_events(fname_txt_mpr)
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        events2 = read_events(fname_txt_mpr)
+        assert_true(sum('first row of' in str(ww.message) for ww in w) == 1)
     assert_array_almost_equal(events, events2)
 
     # Test old format text file IO
@@ -109,6 +141,13 @@ def test_io_events():
     assert_array_equal(a, c)
     assert_array_equal(a, d)
 
+    # test reading file with mask=None
+    events2 = events.copy()
+    events2[:, -1] = range(events2.shape[0])
+    write_events(op.join(tempdir, 'events-eve.fif'), events2)
+    events3 = read_events(op.join(tempdir, 'events-eve.fif'), mask=None)
+    assert_array_almost_equal(events2, events3)
+
     # Test binary file IO for 1 event
     events = read_events(fname_1)  # Use as the new gold standard
     write_events(op.join(tempdir, 'events-eve.fif'), events)
@@ -126,14 +165,13 @@ def test_io_events():
         fname2 = op.join(tempdir, 'test-bad-name.fif')
         write_events(fname2, events)
         read_events(fname2)
-    assert_true(len(w) == 2)
+    assert_naming(w, 'test_event.py', 2)
 
 
 def test_find_events():
-    """Test find events in raw file
-    """
+    """Test find events in raw file"""
     events = read_events(fname)
-    raw = io.Raw(raw_fname, preload=True)
+    raw = io.read_raw_fif(raw_fname, preload=True)
     # let's test the defaulting behavior while we're at it
     extra_ends = ['', '_1']
     orig_envs = [os.getenv('MNE_STIM_CHANNEL%s' % s) for s in extra_ends]
@@ -144,7 +182,10 @@ def test_find_events():
     assert_array_almost_equal(events, events2)
     # now test with mask
     events11 = find_events(raw, mask=3)
-    events22 = read_events(fname, mask=3)
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        events22 = read_events(fname, mask=3)
+        assert_true(sum('events masked' in str(ww.message) for ww in w) == 1)
     assert_array_equal(events11, events22)
 
     # Reset some data for ease of comparison
@@ -163,13 +204,13 @@ def test_find_events():
 
     assert_raises(TypeError, find_events, raw, mask="0")
     assert_array_equal(find_events(raw, shortest_event=1, mask=1),
-                       [[2,    0,    2], [4,    2,    4]])
+                       [[2, 0, 2], [4, 2, 4]])
     assert_array_equal(find_events(raw, shortest_event=1, mask=2),
-                       [[1,    0,    1], [3,    0,    1], [4,    1,    4]])
+                       [[1, 0, 1], [3, 0, 1], [4, 1, 4]])
     assert_array_equal(find_events(raw, shortest_event=1, mask=3),
-                       [[4,    0,    4]])
+                       [[4, 0, 4]])
     assert_array_equal(find_events(raw, shortest_event=1, mask=4),
-                       [[1,    0,    1], [2,    1,    2], [3,    2,    3]])
+                       [[1, 0, 1], [2, 1, 2], [3, 2, 3]])
 
     # test empty events channel
     raw._data[stim_channel_idx, :] = 0
@@ -277,8 +318,7 @@ def test_find_events():
 
 
 def test_pick_events():
-    """Test pick events in a events ndarray
-    """
+    """Test pick events in a events ndarray"""
     events = np.array([[1, 0, 1],
                        [2, 1, 0],
                        [3, 0, 4],
@@ -298,18 +338,27 @@ def test_pick_events():
 
 
 def test_make_fixed_length_events():
-    """Test making events of a fixed length
-    """
-    raw = io.Raw(raw_fname)
+    """Test making events of a fixed length"""
+    raw = io.read_raw_fif(raw_fname)
     events = make_fixed_length_events(raw, id=1)
     assert_true(events.shape[1], 3)
+    events_zero = make_fixed_length_events(raw, 1, first_samp=False)
+    assert_equal(events_zero[0, 0], 0)
+    assert_array_equal(events_zero[:, 0], events[:, 0] - raw.first_samp)
+    # With limits
+    tmin, tmax = raw.times[[0, -1]]
+    duration = tmax - tmin
+    events = make_fixed_length_events(raw, 1, tmin, tmax, duration)
+    assert_equal(events.shape[0], 1)
+    # With bad limits (no resulting events)
+    assert_raises(ValueError, make_fixed_length_events, raw, 1,
+                  tmin, tmax - 1e-3, duration)
 
 
 def test_define_events():
-    """Test defining response events
-    """
+    """Test defining response events"""
     events = read_events(fname)
-    raw = io.Raw(raw_fname)
+    raw = io.read_raw_fif(raw_fname)
     events_, _ = define_target_events(events, 5, 32, raw.info['sfreq'],
                                       .2, 0.7, 42, 99)
     n_target = events[events[:, 2] == 5].shape[0]
diff --git a/mne/tests/test_evoked.py b/mne/tests/test_evoked.py
index 5639b75..f3b680e 100644
--- a/mne/tests/test_evoked.py
+++ b/mne/tests/test_evoked.py
@@ -19,9 +19,9 @@ from mne import (equalize_channels, pick_types, read_evokeds, write_evokeds,
                  grand_average, combine_evoked, create_info)
 from mne.evoked import _get_peak, Evoked, EvokedArray
 from mne.epochs import EpochsArray
-
-from mne.utils import _TempDir, requires_pandas, slow_test, requires_version
-
+from mne.tests.common import assert_naming
+from mne.utils import (_TempDir, requires_pandas, slow_test, requires_version,
+                       run_tests_if_main)
 from mne.externals.six.moves import cPickle as pickle
 
 warnings.simplefilter('always')
@@ -43,8 +43,8 @@ def test_savgol_filter():
     match_mask = np.logical_and(freqs >= 0, freqs <= h_freq / 2.)
     mismatch_mask = np.logical_and(freqs >= h_freq * 2, freqs < 50.)
     assert_raises(ValueError, evoked.savgol_filter, evoked.info['sfreq'])
-    evoked.savgol_filter(h_freq)
-    data_filt = np.abs(fftpack.fft(evoked.data))
+    evoked_sg = evoked.copy().savgol_filter(h_freq)
+    data_filt = np.abs(fftpack.fft(evoked_sg.data))
     # decent in pass-band
     assert_allclose(np.mean(data[:, match_mask], 0),
                     np.mean(data_filt[:, match_mask], 0),
@@ -52,6 +52,8 @@ def test_savgol_filter():
     # suppression in stop-band
     assert_true(np.mean(data[:, mismatch_mask]) >
                 np.mean(data_filt[:, mismatch_mask]) * 5)
+    # original preserved
+    assert_allclose(data, np.abs(fftpack.fft(evoked.data)), atol=1e-16)
 
 
 def test_hash_evoked():
@@ -122,7 +124,7 @@ def test_io_evoked():
         fname2 = op.join(tempdir, 'test-bad-name.fif')
         write_evokeds(fname2, ave)
         read_evokeds(fname2)
-    assert_true(len(w) == 2)
+    assert_naming(w, 'test_evoked.py', 2)
 
     # constructor
     assert_raises(TypeError, Evoked, fname)
@@ -176,7 +178,7 @@ def test_evoked_resample():
     # upsample, write it out, read it in
     ave = read_evokeds(fname, 0)
     sfreq_normal = ave.info['sfreq']
-    ave.resample(2 * sfreq_normal)
+    ave.resample(2 * sfreq_normal, npad=100)
     write_evokeds(op.join(tempdir, 'evoked-ave.fif'), ave)
     ave_up = read_evokeds(op.join(tempdir, 'evoked-ave.fif'), 0)
 
@@ -185,7 +187,7 @@ def test_evoked_resample():
 
     # and compare the original to the downsampled upsampled version
     ave_new = read_evokeds(op.join(tempdir, 'evoked-ave.fif'), 0)
-    ave_new.resample(sfreq_normal)
+    ave_new.resample(sfreq_normal, npad=100)
 
     assert_array_almost_equal(ave_normal.data, ave_new.data, 2)
     assert_array_almost_equal(ave_normal.times, ave_new.times)
@@ -243,10 +245,11 @@ def test_evoked_proj():
             n_proj = len(ave.info['projs'])
             ave.del_proj(0)
             assert_true(len(ave.info['projs']) == n_proj - 1)
+            # Test that already existing projections are not added.
             ave.add_proj(projs, remove_existing=False)
-            assert_true(len(ave.info['projs']) == 2 * n_proj - 1)
-            ave.add_proj(projs, remove_existing=True)
             assert_true(len(ave.info['projs']) == n_proj)
+            ave.add_proj(projs[:-1], remove_existing=True)
+            assert_true(len(ave.info['projs']) == n_proj - 1)
 
     ave = read_evokeds(fname, condition=0, proj=False)
     data = ave.data.copy()
@@ -305,7 +308,7 @@ def test_drop_channels_mixin():
     ch_names = evoked.ch_names[3:]
 
     ch_names_orig = evoked.ch_names
-    dummy = evoked.drop_channels(drop_ch, copy=True)
+    dummy = evoked.copy().drop_channels(drop_ch)
     assert_equal(ch_names, dummy.ch_names)
     assert_equal(ch_names_orig, evoked.ch_names)
     assert_equal(len(ch_names_orig), len(evoked.data))
@@ -322,7 +325,7 @@ def test_pick_channels_mixin():
     ch_names = evoked.ch_names[:3]
 
     ch_names_orig = evoked.ch_names
-    dummy = evoked.pick_channels(ch_names, copy=True)
+    dummy = evoked.copy().pick_channels(ch_names)
     assert_equal(ch_names, dummy.ch_names)
     assert_equal(ch_names_orig, evoked.ch_names)
     assert_equal(len(ch_names_orig), len(evoked.data))
@@ -448,6 +451,10 @@ def test_array_epochs():
     assert_equal(evoked1.kind, evoked3.kind)
     assert_equal(evoked1.nave, evoked3.nave)
 
+    # test kind check
+    assert_raises(TypeError, EvokedArray, data1, info, tmin=0, kind=1)
+    assert_raises(ValueError, EvokedArray, data1, info, tmin=0, kind='mean')
+
     # test match between channels info and data
     ch_names = ['EEG %03d' % (i + 1) for i in range(19)]
     types = ['eeg'] * 19
@@ -455,19 +462,29 @@ def test_array_epochs():
     assert_raises(ValueError, EvokedArray, data1, info, tmin=-0.01)
 
 
+def test_time_as_index():
+    """Test time as index"""
+    evoked = read_evokeds(fname, condition=0).crop(-.1, .1)
+    assert_array_equal(evoked.time_as_index([-.1, .1], use_rounding=True),
+                       [0, len(evoked.times) - 1])
+
+
 def test_add_channels():
-    """Test evoked splitting / re-appending channel types
-    """
+    """Test evoked splitting / re-appending channel types"""
     evoked = read_evokeds(fname, condition=0)
     evoked.info['buffer_size_sec'] = None
-    evoked_eeg = evoked.pick_types(meg=False, eeg=True, copy=True)
-    evoked_meg = evoked.pick_types(meg=True, copy=True)
-    evoked_stim = evoked.pick_types(meg=False, stim=True, copy=True)
-    evoked_eeg_meg = evoked.pick_types(meg=True, eeg=True, copy=True)
-    evoked_new = evoked_meg.add_channels([evoked_eeg, evoked_stim], copy=True)
+    hpi_coils = [{'event_bits': []},
+                 {'event_bits': np.array([256,   0, 256, 256])},
+                 {'event_bits': np.array([512,   0, 512, 512])}]
+    evoked.info['hpi_subsystem'] = dict(hpi_coils=hpi_coils, ncoil=2)
+    evoked_eeg = evoked.copy().pick_types(meg=False, eeg=True)
+    evoked_meg = evoked.copy().pick_types(meg=True)
+    evoked_stim = evoked.copy().pick_types(meg=False, stim=True)
+    evoked_eeg_meg = evoked.copy().pick_types(meg=True, eeg=True)
+    evoked_new = evoked_meg.copy().add_channels([evoked_eeg, evoked_stim])
     assert_true(all(ch in evoked_new.ch_names
                     for ch in evoked_stim.ch_names + evoked_meg.ch_names))
-    evoked_new = evoked_meg.add_channels([evoked_eeg], copy=True)
+    evoked_new = evoked_meg.copy().add_channels([evoked_eeg])
 
     assert_true(ch in evoked_new.ch_names for ch in evoked.ch_names)
     assert_array_equal(evoked_new.data, evoked_eeg_meg.data)
@@ -483,3 +500,6 @@ def test_add_channels():
     assert_raises(AssertionError, evoked_meg.add_channels, [evoked_eeg])
     assert_raises(ValueError, evoked_meg.add_channels, [evoked_meg])
     assert_raises(AssertionError, evoked_meg.add_channels, evoked_badsf)
+
+
+run_tests_if_main()
diff --git a/mne/tests/test_filter.py b/mne/tests/test_filter.py
index 216f017..fc89752 100644
--- a/mne/tests/test_filter.py
+++ b/mne/tests/test_filter.py
@@ -32,7 +32,7 @@ def test_1d_filter():
                     h = np.concatenate([[1.], np.zeros(n_filter - 1)])
                 # ensure we pad the signal the same way for both filters
                 n_pad = max(min(n_filter, n_signal - 1), 0)
-                x_pad = _smart_pad(x, n_pad)
+                x_pad = _smart_pad(x, np.array([n_pad, n_pad]))
                 for zero_phase in (True, False):
                     # compute our expected result the slow way
                     if zero_phase:
@@ -342,12 +342,15 @@ def test_cuda():
                      for o in out]) == tot)
 
     # check resampling
-    a = rng.randn(3, sig_len_secs * sfreq)
-    a1 = resample(a, 1, 2, n_jobs=2, npad=0)
-    a2 = resample(a, 1, 2, n_jobs='cuda', npad=0)
-    a3 = resample(a, 2, 1, n_jobs=2, npad=0)
-    a4 = resample(a, 2, 1, n_jobs='cuda', npad=0)
-    assert_array_almost_equal(a3, a4, 14)
+    for window in ('boxcar', 'triang'):
+        for N in (997, 1000):  # one prime, one even
+            a = rng.randn(2, N)
+            for fro, to in ((1, 2), (2, 1), (1, 3), (3, 1)):
+                a1 = resample(a, fro, to, n_jobs=1, npad='auto',
+                              window=window)
+                a2 = resample(a, fro, to, n_jobs='cuda', npad='auto',
+                              window=window)
+                assert_allclose(a1, a2, rtol=1e-7, atol=1e-14)
     assert_array_almost_equal(a1, a2, 14)
     assert_array_equal(resample([0, 0], 2, 1, n_jobs='cuda'), [0., 0., 0., 0.])
     assert_array_equal(resample(np.zeros(2, np.float32), 2, 1, n_jobs='cuda'),
diff --git a/mne/tests/test_label.py b/mne/tests/test_label.py
index be9761e..26907fe 100644
--- a/mne/tests/test_label.py
+++ b/mne/tests/test_label.py
@@ -16,7 +16,7 @@ from mne import (read_label, stc_to_label, read_source_estimate,
                  read_source_spaces, grow_labels, read_labels_from_annot,
                  write_labels_to_annot, split_label, spatial_tris_connectivity,
                  read_surface)
-from mne.label import Label, _blend_colors
+from mne.label import Label, _blend_colors, label_sign_flip
 from mne.utils import (_TempDir, requires_sklearn, get_subjects_dir,
                        run_tests_if_main, slow_test)
 from mne.fixes import digitize, in1d, assert_is, assert_is_not
@@ -166,6 +166,14 @@ def assert_labels_equal(l0, l1, decimal=5, comment=True, color=True):
         assert_array_almost_equal(a0, a1, decimal)
 
 
+def test_copy():
+    """Test label copying"""
+    label = read_label(label_fname)
+    label_2 = label.copy()
+    label_2.pos += 1
+    assert_array_equal(label.pos, label_2.pos - 1)
+
+
 def test_label_subject():
     """Test label subject name extraction
     """
@@ -485,9 +493,10 @@ def test_write_labels_to_annot():
                   'test4', subjects_dir=tempdir)
 
     # write left and right hemi labels with filenames:
-    fnames = ['%s/%s-myparc' % (tempdir, hemi) for hemi in ['lh', 'rh']]
-    for fname in fnames:
-        write_labels_to_annot(labels, annot_fname=fname)
+    fnames = [op.join(tempdir, hemi + '-myparc') for hemi in ['lh', 'rh']]
+    with warnings.catch_warnings(record=True):  # specify subject_dir param
+        for fname in fnames:
+                write_labels_to_annot(labels, annot_fname=fname)
 
     # read it back
     labels2 = read_labels_from_annot('sample', subjects_dir=subjects_dir,
@@ -504,7 +513,8 @@ def test_write_labels_to_annot():
 
     # same with label-internal colors
     for fname in fnames:
-        write_labels_to_annot(labels, annot_fname=fname, overwrite=True)
+        write_labels_to_annot(labels, 'sample', annot_fname=fname,
+                              overwrite=True, subjects_dir=subjects_dir)
     labels3 = read_labels_from_annot('sample', subjects_dir=subjects_dir,
                                      annot_fname=fnames[0])
     labels33 = read_labels_from_annot('sample', subjects_dir=subjects_dir,
@@ -516,35 +526,40 @@ def test_write_labels_to_annot():
         assert_labels_equal(label, labels3[idx])
 
     # make sure we can't overwrite things
-    assert_raises(ValueError, write_labels_to_annot, labels,
-                  annot_fname=fnames[0])
+    assert_raises(ValueError, write_labels_to_annot, labels, 'sample',
+                  annot_fname=fnames[0], subjects_dir=subjects_dir)
 
     # however, this works
-    write_labels_to_annot(labels, annot_fname=fnames[0], overwrite=True)
+    write_labels_to_annot(labels, 'sample', annot_fname=fnames[0],
+                          overwrite=True, subjects_dir=subjects_dir)
 
     # label without color
     labels_ = labels[:]
     labels_[0] = labels_[0].copy()
     labels_[0].color = None
-    write_labels_to_annot(labels_, annot_fname=fnames[0], overwrite=True)
+    write_labels_to_annot(labels_, 'sample', annot_fname=fnames[0],
+                          overwrite=True, subjects_dir=subjects_dir)
 
     # duplicate color
     labels_[0].color = labels_[2].color
-    assert_raises(ValueError, write_labels_to_annot, labels_,
-                  annot_fname=fnames[0], overwrite=True)
+    assert_raises(ValueError, write_labels_to_annot, labels_, 'sample',
+                  annot_fname=fnames[0], overwrite=True,
+                  subjects_dir=subjects_dir)
 
     # invalid color inputs
     labels_[0].color = (1.1, 1., 1., 1.)
-    assert_raises(ValueError, write_labels_to_annot, labels_,
-                  annot_fname=fnames[0], overwrite=True)
+    assert_raises(ValueError, write_labels_to_annot, labels_, 'sample',
+                  annot_fname=fnames[0], overwrite=True,
+                  subjects_dir=subjects_dir)
 
     # overlapping labels
     labels_ = labels[:]
     cuneus_lh = labels[6]
     precuneus_lh = labels[50]
     labels_.append(precuneus_lh + cuneus_lh)
-    assert_raises(ValueError, write_labels_to_annot, labels_,
-                  annot_fname=fnames[0], overwrite=True)
+    assert_raises(ValueError, write_labels_to_annot, labels_, 'sample',
+                  annot_fname=fnames[0], overwrite=True,
+                  subjects_dir=subjects_dir)
 
     # unlabeled vertices
     labels_lh = [label for label in labels if label.name.endswith('lh')]
@@ -565,6 +580,7 @@ def test_write_labels_to_annot():
                   annot_fname=fnames[0])
 
 
+ at requires_sklearn
 @testing.requires_testing_data
 def test_split_label():
     """Test splitting labels"""
@@ -572,6 +588,9 @@ def test_split_label():
                                    regexp='lingual', subjects_dir=subjects_dir)
     lingual = aparc[0]
 
+    # Test input error
+    assert_raises(ValueError, lingual.split, 'bad_input_string')
+
     # split with names
     parts = ('lingual_post', 'lingual_ant')
     post, ant = split_label(lingual, parts, subjects_dir=subjects_dir)
@@ -604,6 +623,15 @@ def test_split_label():
     # check default label name
     assert_equal(antmost.name, "lingual_div40-lh")
 
+    # Apply contiguous splitting to DMN label from parcellation in Yeo, 2011
+    label_default_mode = read_label(op.join(subjects_dir, 'fsaverage', 'label',
+                                            'lh.7Networks_7.label'))
+    DMN_sublabels = label_default_mode.split(parts='contiguous',
+                                             subject='fsaverage',
+                                             subjects_dir=subjects_dir)
+    assert_equal([len(label.vertices) for label in DMN_sublabels],
+                 [16181, 7022, 5965, 5300, 823] + [1] * 23)
+
 
 @slow_test
 @testing.requires_testing_data
@@ -683,10 +711,8 @@ def test_morph():
         # this should throw an error because the label has all zero values
         assert_raises(ValueError, label.morph, 'sample', 'fsaverage')
         label.values.fill(1)
-        label.morph(None, 'fsaverage', 5, grade, subjects_dir, 1,
-                    copy=False)
-        label.morph('fsaverage', 'sample', 5, None, subjects_dir, 2,
-                    copy=False)
+        label = label.morph(None, 'fsaverage', 5, grade, subjects_dir, 1)
+        label = label.morph('fsaverage', 'sample', 5, None, subjects_dir, 2)
         assert_true(np.mean(in1d(label_orig.vertices, label.vertices)) == 1.0)
         assert_true(len(label.vertices) < 3 * len(label_orig.vertices))
         vals.append(label.vertices)
@@ -701,7 +727,8 @@ def test_morph():
                   subjects_dir, 2)
     assert_raises(TypeError, label.morph, None, 'fsaverage', 5.5, verts,
                   subjects_dir, 2)
-    label.smooth(subjects_dir=subjects_dir)  # make sure this runs
+    with warnings.catch_warnings(record=True):  # morph map could be missing
+        label.smooth(subjects_dir=subjects_dir)  # make sure this runs
 
 
 @testing.requires_testing_data
@@ -746,4 +773,22 @@ def test_grow_labels():
     assert_array_equal(l1.vertices, l0.vertices)
 
 
+ at testing.requires_testing_data
+def test_label_sign_flip():
+    src = read_source_spaces(src_fname)
+    label = Label(vertices=src[0]['vertno'][:5], hemi='lh')
+    src[0]['nn'][label.vertices] = np.array(
+        [[1., 0., 0.],
+         [0.,  1., 0.],
+         [0,  0, 1.],
+         [1. / np.sqrt(2), 1. / np.sqrt(2), 0.],
+         [1. / np.sqrt(2), 1. / np.sqrt(2), 0.]])
+    known_flips = np.array([1, 1, np.nan, 1, 1])
+    idx = [0, 1, 3, 4]  # indices that are usable (third row is orthognoal)
+    flip = label_sign_flip(label, src)
+    # Need the abs here because the direction is arbitrary
+    assert_array_almost_equal(np.abs(np.dot(flip[idx], known_flips[idx])),
+                              len(idx))
+
+
 run_tests_if_main()
diff --git a/mne/tests/test_proj.py b/mne/tests/test_proj.py
index 3dc4fa8..959e0cc 100644
--- a/mne/tests/test_proj.py
+++ b/mne/tests/test_proj.py
@@ -18,6 +18,7 @@ from mne.io.proj import (make_projector, activate_proj,
 from mne.proj import (read_proj, write_proj, make_eeg_average_ref_proj,
                       _has_eeg_average_ref_proj)
 from mne import read_events, Epochs, sensitivity_map, read_source_estimate
+from mne.tests.common import assert_naming
 from mne.utils import (_TempDir, run_tests_if_main, clean_warning_registry,
                        slow_test)
 
@@ -35,8 +36,36 @@ fwd_fname = op.join(sample_path, 'sample_audvis_trunc-meg-eeg-oct-4-fwd.fif')
 sensmap_fname = op.join(sample_path,
                         'sample_audvis_trunc-%s-oct-4-fwd-sensmap-%s.w')
 
-# sample dataset should be updated to reflect mne conventions
-eog_fname = op.join(sample_path, 'sample_audvis_eog_proj.fif')
+eog_fname = op.join(sample_path, 'sample_audvis_eog-proj.fif')
+ecg_fname = op.join(sample_path, 'sample_audvis_ecg-proj.fif')
+
+
+def test_bad_proj():
+    """Test dealing with bad projection application
+    """
+    raw = Raw(raw_fname, preload=True)
+    events = read_events(event_fname)
+    picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
+                       eog=False, exclude='bads')
+    picks = picks[2:9:3]
+    _check_warnings(raw, events, picks)
+    # still bad
+    raw.pick_channels([raw.ch_names[ii] for ii in picks])
+    _check_warnings(raw, events, np.arange(len(raw.ch_names)))
+    # "fixed"
+    raw.info.normalize_proj()  # avoid projection warnings
+    _check_warnings(raw, events, np.arange(len(raw.ch_names)), count=0)
+
+
+def _check_warnings(raw, events, picks, count=3):
+    """Helper to count warnings"""
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        Epochs(raw, events, dict(aud_l=1, vis_l=3),
+               -0.2, 0.5, picks=picks, preload=True, proj=True)
+    assert_equal(len(w), count)
+    for ww in w:
+        assert_true('dangerous' in str(ww.message))
 
 
 @testing.requires_testing_data
@@ -45,7 +74,8 @@ def test_sensitivity_maps():
     fwd = mne.read_forward_solution(fwd_fname, surf_ori=True)
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
-        proj_eog = read_proj(eog_fname)
+        projs = read_proj(eog_fname)
+        projs.extend(read_proj(ecg_fname))
     decim = 6
     for ch_type in ['eeg', 'grad', 'mag']:
         w = read_source_estimate(sensmap_fname % (ch_type, 'lh')).data
@@ -72,13 +102,16 @@ def test_sensitivity_maps():
             ends = ['4-lh', '5-lh', '6-lh', '7-lh']
             for mode, end in zip(modes, ends):
                 w = read_source_estimate(sensmap_fname % (ch_type, end)).data
-                stc = sensitivity_map(fwd, projs=proj_eog, mode=mode,
+                stc = sensitivity_map(fwd, projs=projs, mode=mode,
                                       ch_type=ch_type, exclude='bads')
                 assert_array_almost_equal(stc.data, w, decim)
 
     # test corner case for EEG
     stc = sensitivity_map(fwd, projs=[make_eeg_average_ref_proj(fwd['info'])],
                           ch_type='eeg', exclude='bads')
+    # test corner case for projs being passed but no valid ones (#3135)
+    assert_raises(ValueError, sensitivity_map, fwd, projs=None, mode='angle')
+    assert_raises(RuntimeError, sensitivity_map, fwd, projs=[], mode='angle')
     # test volume source space
     fname = op.join(sample_path, 'sample_audvis_trunc-meg-vol-7-fwd.fif')
     fwd = mne.read_forward_solution(fname)
@@ -159,8 +192,7 @@ def test_compute_proj_epochs():
         proj_badname = op.join(tempdir, 'test-bad-name.fif.gz')
         write_proj(proj_badname, projs)
         read_proj(proj_badname)
-        print([ww.message for ww in w])
-    assert_equal(len(w), 2)
+    assert_naming(w, 'test_proj.py', 2)
 
 
 @slow_test
@@ -169,7 +201,7 @@ def test_compute_proj_raw():
     tempdir = _TempDir()
     # Test that the raw projectors work
     raw_time = 2.5  # Do shorter amount for speed
-    raw = Raw(raw_fname).crop(0, raw_time, False)
+    raw = Raw(raw_fname).crop(0, raw_time)
     raw.load_data()
     for ii in (0.25, 0.5, 1, 2):
         with warnings.catch_warnings(record=True) as w:
@@ -210,7 +242,7 @@ def test_compute_proj_raw():
     # test resampled-data projector, upsampling instead of downsampling
     # here to save an extra filtering (raw would have to be LP'ed to be equiv)
     raw_resamp = cp.deepcopy(raw)
-    raw_resamp.resample(raw.info['sfreq'] * 2, n_jobs=2)
+    raw_resamp.resample(raw.info['sfreq'] * 2, n_jobs=2, npad='auto')
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
         projs = compute_proj_raw(raw_resamp, duration=None, stop=raw_time,
diff --git a/mne/tests/test_selection.py b/mne/tests/test_selection.py
index 4272ed0..ba6a8aa 100644
--- a/mne/tests/test_selection.py
+++ b/mne/tests/test_selection.py
@@ -1,4 +1,14 @@
+import os.path as op
+
+from nose.tools import assert_true, assert_equal, assert_raises
+
 from mne import read_selection
+from mne.io import read_raw_fif
+from mne.utils import run_tests_if_main
+
+test_path = op.join(op.split(__file__)[0], '..', 'io', 'tests', 'data')
+raw_fname = op.join(test_path, 'test_raw.fif')
+raw_new_fname = op.join(test_path, 'test_chpi_raw_sss.fif')
 
 
 def test_read_selection():
@@ -10,18 +20,31 @@ def test_read_selection():
                  'Right-parietal', 'Left-occipital', 'Right-occipital',
                  'Left-frontal', 'Right-frontal']
 
+    raw = read_raw_fif(raw_fname)
     for i, name in enumerate(sel_names):
         sel = read_selection(name)
-        assert(ch_names[i] in sel)
+        assert_true(ch_names[i] in sel)
+        sel_info = read_selection(name, info=raw.info)
+        assert_equal(sel, sel_info)
 
     # test some combinations
     all_ch = read_selection(['L', 'R'])
     left = read_selection('L')
     right = read_selection('R')
 
-    assert(len(all_ch) == len(left) + len(right))
-    assert(len(set(left).intersection(set(right))) == 0)
+    assert_true(len(all_ch) == len(left) + len(right))
+    assert_true(len(set(left).intersection(set(right))) == 0)
 
     frontal = read_selection('frontal')
     occipital = read_selection('Right-occipital')
-    assert(len(set(frontal).intersection(set(occipital))) == 0)
+    assert_true(len(set(frontal).intersection(set(occipital))) == 0)
+
+    ch_names_new = [ch.replace(' ', '') for ch in ch_names]
+    raw_new = read_raw_fif(raw_new_fname)
+    for i, name in enumerate(sel_names):
+        sel = read_selection(name, info=raw_new.info)
+        assert_true(ch_names_new[i] in sel)
+
+    assert_raises(TypeError, read_selection, name, info='foo')
+
+run_tests_if_main()
diff --git a/mne/tests/test_source_estimate.py b/mne/tests/test_source_estimate.py
index 3a91b75..f5d2743 100644
--- a/mne/tests/test_source_estimate.py
+++ b/mne/tests/test_source_estimate.py
@@ -385,8 +385,9 @@ def test_extract_label_time_course():
                   src, mode='mean')
 
     # but this works:
-    tc = extract_label_time_course(stcs, empty_label, src, mode='mean',
-                                   allow_empty=True)
+    with warnings.catch_warnings(record=True):  # empty label
+        tc = extract_label_time_course(stcs, empty_label, src, mode='mean',
+                                       allow_empty=True)
     for arr in tc:
         assert_true(arr.shape == (1, n_times))
         assert_array_equal(arr, np.zeros((1, n_times)))
@@ -433,6 +434,8 @@ def test_morph_data():
     # make sure we can specify grade
     stc_from.crop(0.09, 0.1)  # for faster computation
     stc_to.crop(0.09, 0.1)  # for faster computation
+    assert_array_equal(stc_to.time_as_index([0.09, 0.1], use_rounding=True),
+                       [0, len(stc_to.times) - 1])
     assert_raises(ValueError, stc_from.morph, subject_to, grade=3, smooth=-1,
                   subjects_dir=subjects_dir)
     stc_to1 = stc_from.morph(subject_to, grade=3, smooth=12, buffer_size=1000,
diff --git a/mne/tests/test_source_space.py b/mne/tests/test_source_space.py
index b58ac5e..5fe5b8d 100644
--- a/mne/tests/test_source_space.py
+++ b/mne/tests/test_source_space.py
@@ -17,10 +17,11 @@ from mne.utils import (_TempDir, requires_fs_or_nibabel, requires_nibabel,
                        requires_freesurfer, run_subprocess, slow_test,
                        requires_mne, requires_version, run_tests_if_main)
 from mne.surface import _accumulate_normals, _triangle_neighbors
-from mne.source_space import _get_mgz_header
+from mne.source_space import _get_mri_header, _get_mgz_header
 from mne.externals.six.moves import zip
 from mne.source_space import (get_volume_labels_from_aseg, SourceSpaces,
                               _compare_source_spaces)
+from mne.tests.common import assert_naming
 from mne.io.constants import FIFF
 
 warnings.simplefilter('always')
@@ -46,9 +47,8 @@ rng = np.random.RandomState(0)
 @requires_nibabel(vox2ras_tkr=True)
 def test_mgz_header():
     """Test MGZ header reading"""
-    import nibabel as nib
     header = _get_mgz_header(fname_mri)
-    mri_hdr = nib.load(fname_mri).get_header()
+    mri_hdr = _get_mri_header(fname_mri)
     assert_allclose(mri_hdr.get_data_shape(), header['dims'])
     assert_allclose(mri_hdr.get_vox2ras_tkr(), header['vox2ras_tkr'])
     assert_allclose(mri_hdr.get_ras2vox(), header['ras2vox'])
@@ -352,9 +352,9 @@ def test_setup_source_space():
         src_new = setup_source_space('sample', temp_name, spacing='oct6',
                                      subjects_dir=subjects_dir,
                                      overwrite=True, add_dist=False)
-    _compare_source_spaces(src, src_new, mode='approx')
+    _compare_source_spaces(src, src_new, mode='approx', nearest=False)
     src_new = read_source_spaces(temp_name)
-    _compare_source_spaces(src, src_new, mode='approx')
+    _compare_source_spaces(src, src_new, mode='approx', nearest=False)
 
     # all source points - no file writing
     src_new = setup_source_space('sample', None, spacing='all',
@@ -408,7 +408,7 @@ def test_write_source_space():
         src_badname = op.join(tempdir, 'test-bad-name.fif.gz')
         write_source_spaces(src_badname, src0)
         read_source_spaces(src_badname)
-    assert_equal(len(w), 2)
+    assert_naming(w, 'test_source_space.py', 2)
 
 
 @testing.requires_testing_data
@@ -551,8 +551,9 @@ def test_combine_source_spaces():
 
     # unrecognized file type
     bad_image_fname = op.join(tempdir, 'temp-image.png')
-    assert_raises(ValueError, src.export_volume, bad_image_fname,
-                  verbose='error')
+    with warnings.catch_warnings(record=True):  # vertices outside vol space
+        assert_raises(ValueError, src.export_volume, bad_image_fname,
+                      verbose='error')
 
     # mixed coordinate frames
     disc3 = disc.copy()
diff --git a/mne/tests/test_surface.py b/mne/tests/test_surface.py
index f4d9b23..7394882 100644
--- a/mne/tests/test_surface.py
+++ b/mne/tests/test_surface.py
@@ -52,6 +52,8 @@ def test_head():
     surf_1 = get_head_surf('sample', subjects_dir=subjects_dir)
     surf_2 = get_head_surf('sample', 'head', subjects_dir=subjects_dir)
     assert_true(len(surf_1['rr']) < len(surf_2['rr']))  # BEM vs dense head
+    assert_raises(TypeError, get_head_surf, subject=None,
+                  subjects_dir=subjects_dir)
 
 
 def test_huge_cross():
@@ -102,7 +104,8 @@ def test_make_morph_maps():
                      op.join(tempdir, *args))
 
     # this should trigger the creation of morph-maps dir and create the map
-    mmap = read_morph_map('fsaverage_ds', 'sample_ds', tempdir)
+    with warnings.catch_warnings(record=True):
+        mmap = read_morph_map('fsaverage_ds', 'sample_ds', tempdir)
     mmap2 = read_morph_map('fsaverage_ds', 'sample_ds', subjects_dir)
     assert_equal(len(mmap), len(mmap2))
     for m1, m2 in zip(mmap, mmap2):
@@ -111,7 +114,8 @@ def test_make_morph_maps():
         assert_allclose(diff, np.zeros_like(diff), atol=1e-3, rtol=0)
 
     # This will also trigger creation, but it's trivial
-    mmap = read_morph_map('sample', 'sample', subjects_dir=tempdir)
+    with warnings.catch_warnings(record=True):
+        mmap = read_morph_map('sample', 'sample', subjects_dir=tempdir)
     for mm in mmap:
         assert_true((mm - sparse.eye(mm.shape[0], mm.shape[0])).sum() == 0)
 
diff --git a/mne/tests/test_transforms.py b/mne/tests/test_transforms.py
index fd9a54f..9f81adf 100644
--- a/mne/tests/test_transforms.py
+++ b/mne/tests/test_transforms.py
@@ -9,12 +9,16 @@ import warnings
 
 from mne.datasets import testing
 from mne import read_trans, write_trans
+from mne.io import read_info
 from mne.utils import _TempDir, run_tests_if_main
+from mne.tests.common import assert_naming
 from mne.transforms import (invert_transform, _get_trans,
                             rotation, rotation3d, rotation_angles, _find_trans,
                             combine_transforms, apply_trans, translation,
                             get_ras_to_neuromag_trans, _sphere_to_cartesian,
-                            _polar_to_cartesian, _cartesian_to_sphere)
+                            _polar_to_cartesian, _cartesian_to_sphere,
+                            quat_to_rot, rot_to_quat, _angle_between_quats,
+                            _find_vector_rotation)
 
 warnings.simplefilter('always')  # enable b/c these tests throw warnings
 
@@ -22,8 +26,12 @@ data_path = testing.data_path(download=False)
 fname = op.join(data_path, 'MEG', 'sample', 'sample_audvis_trunc-trans.fif')
 fname_eve = op.join(data_path, 'MEG', 'sample',
                     'sample_audvis_trunc_raw-eve.fif')
-fname_trans = op.join(op.split(__file__)[0], '..', 'io', 'tests',
-                      'data', 'sample-audvis-raw-trans.txt')
+
+base_dir = op.join(op.dirname(__file__), '..', 'io', 'tests', 'data')
+fname_trans = op.join(base_dir, 'sample-audvis-raw-trans.txt')
+test_fif_fname = op.join(base_dir, 'test_raw.fif')
+ctf_fname = op.join(base_dir, 'test_ctf_raw.fif')
+hp_fif_fname = op.join(base_dir, 'test_chpi_raw_sss.fif')
 
 
 @testing.requires_testing_data
@@ -62,7 +70,7 @@ def test_io_trans():
     with warnings.catch_warnings(record=True) as w:
         fname2 = op.join(tempdir, 'trans-test-bad-name.fif')
         write_trans(fname2, trans0)
-    assert_true(len(w) >= 1)
+    assert_naming(w, 'test_transforms.py', 1)
 
 
 def test_get_ras_to_neuromag_trans():
@@ -160,4 +168,56 @@ def test_combine():
                   trans['from'], trans['to'])
 
 
+def test_quaternions():
+    """Test quaternion calculations
+    """
+    rots = [np.eye(3)]
+    for fname in [test_fif_fname, ctf_fname, hp_fif_fname]:
+        rots += [read_info(fname)['dev_head_t']['trans'][:3, :3]]
+    # nasty numerical cases
+    rots += [np.array([
+        [-0.99978541, -0.01873462, -0.00898756],
+        [-0.01873462, 0.62565561, 0.77987608],
+        [-0.00898756, 0.77987608, -0.62587152],
+    ])]
+    rots += [np.array([
+        [0.62565561, -0.01873462, 0.77987608],
+        [-0.01873462, -0.99978541, -0.00898756],
+        [0.77987608, -0.00898756, -0.62587152],
+    ])]
+    rots += [np.array([
+        [-0.99978541, -0.00898756, -0.01873462],
+        [-0.00898756, -0.62587152, 0.77987608],
+        [-0.01873462, 0.77987608, 0.62565561],
+    ])]
+    for rot in rots:
+        assert_allclose(rot, quat_to_rot(rot_to_quat(rot)),
+                        rtol=1e-5, atol=1e-5)
+        rot = rot[np.newaxis, np.newaxis, :, :]
+        assert_allclose(rot, quat_to_rot(rot_to_quat(rot)),
+                        rtol=1e-5, atol=1e-5)
+
+    # let's make sure our angle function works in some reasonable way
+    for ii in range(3):
+        for jj in range(3):
+            a = np.zeros(3)
+            b = np.zeros(3)
+            a[ii] = 1.
+            b[jj] = 1.
+            expected = np.pi if ii != jj else 0.
+            assert_allclose(_angle_between_quats(a, b), expected, atol=1e-5)
+
+
+def test_vector_rotation():
+    """Test basic rotation matrix math
+    """
+    x = np.array([1., 0., 0.])
+    y = np.array([0., 1., 0.])
+    rot = _find_vector_rotation(x, y)
+    assert_array_equal(rot,
+                       [[0, -1, 0], [1, 0, 0], [0, 0, 1]])
+    quat_1 = rot_to_quat(rot)
+    quat_2 = rot_to_quat(np.eye(3))
+    assert_allclose(_angle_between_quats(quat_1, quat_2), np.pi / 2.)
+
 run_tests_if_main()
diff --git a/mne/tests/test_utils.py b/mne/tests/test_utils.py
index 6475049..33f079e 100644
--- a/mne/tests/test_utils.py
+++ b/mne/tests/test_utils.py
@@ -7,6 +7,9 @@ from scipy import sparse
 import os
 import warnings
 
+from mne import read_evokeds
+from mne.externals.six.moves import StringIO
+from mne.io import show_fiff
 from mne.utils import (set_log_level, set_log_file, _TempDir,
                        get_config, set_config, deprecated, _fetch_file,
                        sum_squared, estimate_rank,
@@ -17,10 +20,8 @@ from mne.utils import (set_log_level, set_log_file, _TempDir,
                        _check_mayavi_version, requires_mayavi,
                        set_memmap_min_size, _get_stim_channel, _check_fname,
                        create_slices, _time_mask, random_permutation,
-                       _get_call_line, compute_corr, verbose)
-from mne.io import show_fiff
-from mne import Evoked
-from mne.externals.six.moves import StringIO
+                       _get_call_line, compute_corr, sys_info, verbose,
+                       check_fname, requires_ftp)
 
 
 warnings.simplefilter('always')  # enable b/c these tests throw warnings
@@ -37,6 +38,15 @@ def clean_lines(lines=[]):
     return [l if 'Reading ' not in l else 'Reading test file' for l in lines]
 
 
+def test_sys_info():
+    """Test info-showing utility
+    """
+    out = StringIO()
+    sys_info(fid=out)
+    out = out.getvalue()
+    assert_true('numpy:' in out)
+
+
 def test_get_call_line():
     """Test getting a call line
     """
@@ -69,6 +79,7 @@ def test_misc():
     assert_raises(TypeError, _get_stim_channel, 1, None)
     assert_raises(TypeError, _get_stim_channel, [1], None)
     assert_raises(TypeError, _check_fname, 1)
+    assert_raises(IOError, check_fname, 'foo', 'tets-dip.x', (), ('.fif',))
     assert_raises(ValueError, _check_subject, None, None)
     assert_raises(ValueError, _check_subject, None, 1)
     assert_raises(ValueError, _check_subject, 1, None)
@@ -226,6 +237,7 @@ def test_estimate_rank():
                        np.ones(10))
     data[0, 0] = 0
     assert_equal(estimate_rank(data), 9)
+    assert_raises(ValueError, estimate_rank, data, 'foo')
 
 
 def test_logging():
@@ -238,6 +250,13 @@ def test_logging():
         old_lines = clean_lines(old_log_file.readlines())
     with open(fname_log_2, 'r') as old_log_file_2:
         old_lines_2 = clean_lines(old_log_file_2.readlines())
+    # we changed our logging a little bit
+    old_lines = [o.replace('No baseline correction applied...',
+                           'No baseline correction applied')
+                 for o in old_lines]
+    old_lines_2 = [o.replace('No baseline correction applied...',
+                             'No baseline correction applied')
+                   for o in old_lines_2]
 
     if op.isfile(test_name):
         os.remove(test_name)
@@ -245,19 +264,19 @@ def test_logging():
     set_log_file(test_name)
     set_log_level('WARNING')
     # should NOT print
-    evoked = Evoked(fname_evoked, condition=1)
+    evoked = read_evokeds(fname_evoked, condition=1)
     with open(test_name) as fid:
         assert_true(fid.readlines() == [])
     # should NOT print
-    evoked = Evoked(fname_evoked, condition=1, verbose=False)
+    evoked = read_evokeds(fname_evoked, condition=1, verbose=False)
     with open(test_name) as fid:
         assert_true(fid.readlines() == [])
     # should NOT print
-    evoked = Evoked(fname_evoked, condition=1, verbose='WARNING')
+    evoked = read_evokeds(fname_evoked, condition=1, verbose='WARNING')
     with open(test_name) as fid:
         assert_true(fid.readlines() == [])
     # SHOULD print
-    evoked = Evoked(fname_evoked, condition=1, verbose=True)
+    evoked = read_evokeds(fname_evoked, condition=1, verbose=True)
     with open(test_name, 'r') as new_log_file:
         new_lines = clean_lines(new_log_file.readlines())
     assert_equal(new_lines, old_lines)
@@ -268,15 +287,15 @@ def test_logging():
     set_log_file(test_name)
     set_log_level('INFO')
     # should NOT print
-    evoked = Evoked(fname_evoked, condition=1, verbose='WARNING')
+    evoked = read_evokeds(fname_evoked, condition=1, verbose='WARNING')
     with open(test_name) as fid:
         assert_true(fid.readlines() == [])
     # should NOT print
-    evoked = Evoked(fname_evoked, condition=1, verbose=False)
+    evoked = read_evokeds(fname_evoked, condition=1, verbose=False)
     with open(test_name) as fid:
         assert_true(fid.readlines() == [])
     # SHOULD print
-    evoked = Evoked(fname_evoked, condition=1)
+    evoked = read_evokeds(fname_evoked, condition=1)
     with open(test_name, 'r') as new_log_file:
         new_lines = clean_lines(new_log_file.readlines())
     with open(fname_log, 'r') as old_log_file:
@@ -285,10 +304,11 @@ def test_logging():
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
         set_log_file(test_name, overwrite=False)
-        assert len(w) == 0
+        assert_equal(len(w), 0)
         set_log_file(test_name)
-        assert len(w) == 1
-    evoked = Evoked(fname_evoked, condition=1)
+    assert_equal(len(w), 1)
+    assert_true('test_utils.py' in w[0].filename)
+    evoked = read_evokeds(fname_evoked, condition=1)
     with open(test_name, 'r') as new_log_file:
         new_lines = clean_lines(new_log_file.readlines())
     assert_equal(new_lines, old_lines_2)
@@ -296,7 +316,7 @@ def test_logging():
     # make sure overwriting works
     set_log_file(test_name, overwrite=True)
     # this line needs to be called to actually do some logging
-    evoked = Evoked(fname_evoked, condition=1)
+    evoked = read_evokeds(fname_evoked, condition=1)
     del evoked
     with open(test_name, 'r') as new_log_file:
         new_lines = clean_lines(new_log_file.readlines())
@@ -313,6 +333,7 @@ def test_config():
     assert_true(get_config(key) == value)
     del os.environ[key]
     # catch the warning about it being a non-standard config key
+    assert_true(len(set_config(None, None)) > 10)  # tuple of valid keys
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter('always')
         set_config(key, None, home_dir=tempdir)
@@ -372,30 +393,38 @@ def test_deprecated():
     assert_true(len(w) == 1)
 
 
- at requires_good_network
-def test_fetch_file():
-    """Test file downloading
-    """
+def _test_fetch(url):
+    """Helper to test URL retrieval"""
     tempdir = _TempDir()
-    urls = ['http://google.com',
-            'ftp://ftp.openbsd.org/pub/OpenBSD/README']
     with ArgvSetter(disable_stderr=False):  # to capture stdout
-        for url in urls:
-            archive_name = op.join(tempdir, "download_test")
-            _fetch_file(url, archive_name, timeout=30., verbose=False,
-                        resume=False)
-            assert_raises(Exception, _fetch_file, 'NOT_AN_ADDRESS',
-                          op.join(tempdir, 'test'), verbose=False)
-            resume_name = op.join(tempdir, "download_resume")
-            # touch file
-            with open(resume_name + '.part', 'w'):
-                os.utime(resume_name + '.part', None)
-            _fetch_file(url, resume_name, resume=True, timeout=30.,
-                        verbose=False)
-            assert_raises(ValueError, _fetch_file, url, archive_name,
-                          hash_='a', verbose=False)
-            assert_raises(RuntimeError, _fetch_file, url, archive_name,
-                          hash_='a' * 32, verbose=False)
+        archive_name = op.join(tempdir, "download_test")
+        _fetch_file(url, archive_name, timeout=30., verbose=False,
+                    resume=False)
+        assert_raises(Exception, _fetch_file, 'NOT_AN_ADDRESS',
+                      op.join(tempdir, 'test'), verbose=False)
+        resume_name = op.join(tempdir, "download_resume")
+        # touch file
+        with open(resume_name + '.part', 'w'):
+            os.utime(resume_name + '.part', None)
+        _fetch_file(url, resume_name, resume=True, timeout=30.,
+                    verbose=False)
+        assert_raises(ValueError, _fetch_file, url, archive_name,
+                      hash_='a', verbose=False)
+        assert_raises(RuntimeError, _fetch_file, url, archive_name,
+                      hash_='a' * 32, verbose=False)
+
+
+ at requires_good_network
+def test_fetch_file_html():
+    """Test file downloading over http"""
+    _test_fetch('http://google.com')
+
+
+ at requires_ftp
+ at requires_good_network
+def test_fetch_file_ftp():
+    """Test file downloading over ftp"""
+    _test_fetch('ftp://speedtest.tele2.net/1KB.zip')
 
 
 def test_sum_squared():
@@ -498,8 +527,26 @@ def test_time_mask():
     N = 10
     x = np.arange(N).astype(float)
     assert_equal(_time_mask(x, 0, N - 1).sum(), N)
-    assert_equal(_time_mask(x - 1e-10, 0, N - 1).sum(), N)
-    assert_equal(_time_mask(x - 1e-10, 0, N - 1, strict=True).sum(), N - 1)
+    assert_equal(_time_mask(x - 1e-10, 0, N - 1, sfreq=1000.).sum(), N)
+    assert_equal(_time_mask(x - 1e-10, None, N - 1, sfreq=1000.).sum(), N)
+    assert_equal(_time_mask(x - 1e-10, None, None, sfreq=1000.).sum(), N)
+    assert_equal(_time_mask(x - 1e-10, -np.inf, None, sfreq=1000.).sum(), N)
+    assert_equal(_time_mask(x - 1e-10, None, np.inf, sfreq=1000.).sum(), N)
+    # non-uniformly spaced inputs
+    x = np.array([4, 10])
+    assert_equal(_time_mask(x[:1], tmin=10, sfreq=1,
+                            raise_error=False).sum(), 0)
+    assert_equal(_time_mask(x[:1], tmin=11, tmax=12, sfreq=1,
+                            raise_error=False).sum(), 0)
+    assert_equal(_time_mask(x, tmin=10, sfreq=1).sum(), 1)
+    assert_equal(_time_mask(x, tmin=6, sfreq=1).sum(), 1)
+    assert_equal(_time_mask(x, tmin=5, sfreq=1).sum(), 1)
+    assert_equal(_time_mask(x, tmin=4.5001, sfreq=1).sum(), 1)
+    assert_equal(_time_mask(x, tmin=4.4999, sfreq=1).sum(), 2)
+    assert_equal(_time_mask(x, tmin=4, sfreq=1).sum(), 2)
+    # degenerate cases
+    assert_raises(ValueError, _time_mask, x[:1], tmin=11, tmax=12)
+    assert_raises(ValueError, _time_mask, x[:1], tmin=10, sfreq=1)
 
 
 def test_random_permutation():
diff --git a/mne/time_frequency/__init__.py b/mne/time_frequency/__init__.py
index 14c92fb..327a5c8 100644
--- a/mne/time_frequency/__init__.py
+++ b/mne/time_frequency/__init__.py
@@ -3,7 +3,7 @@
 
 from .tfr import (single_trial_power, morlet, tfr_morlet, cwt_morlet,
                   AverageTFR, tfr_multitaper, read_tfrs, write_tfrs)
-from .psd import compute_raw_psd, compute_epochs_psd
+from .psd import compute_raw_psd, compute_epochs_psd, psd_welch, psd_multitaper
 from .csd import CrossSpectralDensity, compute_epochs_csd
 from .ar import fit_iir_model_raw
 from .multitaper import dpss_windows, multitaper_psd
diff --git a/mne/time_frequency/_stockwell.py b/mne/time_frequency/_stockwell.py
index a9f703e..4a4b867 100644
--- a/mne/time_frequency/_stockwell.py
+++ b/mne/time_frequency/_stockwell.py
@@ -10,7 +10,7 @@ from scipy import fftpack
 # XXX explore cuda optimazation at some point.
 
 from ..io.pick import pick_types, pick_info
-from ..utils import logger, verbose
+from ..utils import verbose, warn
 from ..parallel import parallel_func, check_n_jobs
 from .tfr import AverageTFR, _get_data
 
@@ -31,9 +31,8 @@ def _check_input_st(x_in, n_fft):
                          "Got %s < %s." % (n_fft, n_times))
     zero_pad = None
     if n_times < n_fft:
-        msg = ('The input signal is shorter ({0}) than "n_fft" ({1}). '
-               'Applying zero padding.').format(x_in.shape[-1], n_fft)
-        logger.warning(msg)
+        warn('The input signal is shorter ({0}) than "n_fft" ({1}). '
+             'Applying zero padding.'.format(x_in.shape[-1], n_fft))
         zero_pad = n_fft - n_times
         pad_array = np.zeros(x_in.shape[:-1] + (zero_pad,), x_in.dtype)
         x_in = np.concatenate((x_in, pad_array), axis=-1)
@@ -226,7 +225,7 @@ def tfr_stockwell(inst, fmin=None, fmax=None, n_fft=None,
     See Also
     --------
     cwt : Compute time-frequency decomposition with user-provided wavelets
-    cwt_morlet, multitaper_psd
+    cwt_morlet, psd_multitaper
 
     Notes
     -----
diff --git a/mne/time_frequency/csd.py b/mne/time_frequency/csd.py
index e147da2..4d5d25f 100644
--- a/mne/time_frequency/csd.py
+++ b/mne/time_frequency/csd.py
@@ -2,14 +2,13 @@
 #
 # License: BSD (3-clause)
 
-import warnings
 import copy as cp
 
 import numpy as np
 from scipy.fftpack import fftfreq
 
 from ..io.pick import pick_types
-from ..utils import logger, verbose
+from ..utils import logger, verbose, warn
 from ..time_frequency.multitaper import (dpss_windows, _mt_spectra,
                                          _csd_from_mt, _psd_from_mt_adaptive)
 
@@ -119,9 +118,9 @@ def compute_epochs_csd(epochs, mode='multitaper', fmin=0, fmax=np.inf,
     if tmax is not None and tmin is not None:
         if tmax < tmin:
             raise ValueError('tmax must be larger than tmin')
-    if epochs.baseline is None:
-        warnings.warn('Epochs are not baseline corrected, cross-spectral '
-                      'density may be inaccurate')
+    if epochs.baseline is None and epochs.info['highpass'] < 0.1:
+        warn('Epochs are not baseline corrected or enough highpass filtered. '
+             'Cross-spectral density may be inaccurate.')
 
     if projs is None:
         projs = cp.deepcopy(epochs.info['projs'])
@@ -172,8 +171,8 @@ def compute_epochs_csd(epochs, mode='multitaper', fmin=0, fmax=np.inf,
                     'windows' % n_tapers)
 
         if mt_adaptive and len(eigvals) < 3:
-            warnings.warn('Not adaptively combining the spectral estimators '
-                          'due to a low number of tapers.')
+            warn('Not adaptively combining the spectral estimators due to a '
+                 'low number of tapers.')
             mt_adaptive = False
     elif mode == 'fourier':
         logger.info('    using FFT with a Hanning window to estimate spectra')
diff --git a/mne/time_frequency/multitaper.py b/mne/time_frequency/multitaper.py
index 37061a9..a5b616e 100644
--- a/mne/time_frequency/multitaper.py
+++ b/mne/time_frequency/multitaper.py
@@ -2,14 +2,12 @@
 # License : BSD 3-clause
 
 # Parts of this code were copied from NiTime http://nipy.sourceforge.net/nitime
-from warnings import warn
 
 import numpy as np
 from scipy import fftpack, linalg
-import warnings
 
 from ..parallel import parallel_func
-from ..utils import verbose, sum_squared
+from ..utils import verbose, sum_squared, deprecated, warn
 
 
 def tridisolve(d, e, b, overwrite_b=True):
@@ -147,7 +145,7 @@ def dpss_windows(N, half_nbw, Kmax, low_bias=True, interp_from=None,
     uncertainty V: The discrete case. Bell System Technical Journal,
     Volume 57 (1978), 1371430
     """
-    from scipy.interpolate import interp1d
+    from scipy import interpolate
     Kmax = int(Kmax)
     W = float(half_nbw) / N
     nidx = np.arange(N, dtype='d')
@@ -164,9 +162,8 @@ def dpss_windows(N, half_nbw, Kmax, low_bias=True, interp_from=None,
         d, e = dpss_windows(interp_from, half_nbw, Kmax, low_bias=False)
         for this_d in d:
             x = np.arange(this_d.shape[-1])
-            I = interp1d(x, this_d, kind=interp_kind)
-            d_temp = I(np.arange(0, this_d.shape[-1] - 1,
-                                 float(this_d.shape[-1] - 1) / N))
+            I = interpolate.interp1d(x, this_d, kind=interp_kind)
+            d_temp = I(np.linspace(0, this_d.shape[-1] - 1, N, endpoint=False))
 
             # Rescale:
             d_temp = d_temp / np.sqrt(sum_squared(d_temp))
@@ -220,9 +217,11 @@ def dpss_windows(N, half_nbw, Kmax, low_bias=True, interp_from=None,
     for i, f in enumerate(fix_symmetric):
         if f:
             dpss[2 * i] *= -1
-    fix_skew = (dpss[1::2, 1] < 0)
-    for i, f in enumerate(fix_skew):
-        if f:
+    # rather than test the sign of one point, test the sign of the
+    # linear slope up to the first (largest) peak
+    pk = np.argmax(np.abs(dpss[1::2, :N // 2]), axis=1)
+    for i, p in enumerate(pk):
+        if np.sum(dpss[2 * i + 1, :p]) < 0:
             dpss[2 * i + 1] *= -1
 
     # Now find the eigenvalues of the original spectral concentration problem
@@ -242,11 +241,11 @@ def dpss_windows(N, half_nbw, Kmax, low_bias=True, interp_from=None,
     if low_bias:
         idx = (eigvals > 0.9)
         if not idx.any():
-            warnings.warn('Could not properly use low_bias, '
-                          'keeping lowest-bias taper')
+            warn('Could not properly use low_bias, keeping lowest-bias taper')
             idx = [np.argmax(eigvals)]
         dpss, eigvals = dpss[idx], eigvals[idx]
     assert len(dpss) > 0  # should never happen
+    assert dpss.shape[1] == N  # old nitime bug
     return dpss, eigvals
 
 
@@ -350,8 +349,7 @@ def _psd_from_mt_adaptive(x_mt, eigvals, freq_mask, max_iter=150,
             err = d_k
 
         if n == max_iter - 1:
-            warn('Iterative multi-taper PSD computation did not converge.',
-                 RuntimeWarning)
+            warn('Iterative multi-taper PSD computation did not converge.')
 
         psd[i, :] = psd_iter
 
@@ -380,7 +378,8 @@ def _psd_from_mt(x_mt, weights):
         The computed PSD
     """
     psd = weights * x_mt
-    psd = (psd * psd.conj()).real.sum(axis=-2)
+    psd *= psd.conj()
+    psd = psd.real.sum(axis=-2)
     psd *= 2 / (weights * weights.conj()).real.sum(axis=-2)
     return psd
 
@@ -439,27 +438,30 @@ def _mt_spectra(x, dpss, sfreq, n_fft=None):
 
     # remove mean (do not use in-place subtraction as it may modify input x)
     x = x - np.mean(x, axis=-1)[:, np.newaxis]
-    x_mt = fftpack.fft(x[:, np.newaxis, :] * dpss, n=n_fft)
 
     # only keep positive frequencies
     freqs = fftpack.fftfreq(n_fft, 1. / sfreq)
     freq_mask = (freqs >= 0)
-
-    x_mt = x_mt[:, :, freq_mask]
     freqs = freqs[freq_mask]
 
+    # The following is equivalent to this, but uses less memory:
+    # x_mt = fftpack.fft(x[:, np.newaxis, :] * dpss, n=n_fft)
+    n_tapers = dpss.shape[0] if dpss.ndim > 1 else 1
+    x_mt = np.zeros((len(x), n_tapers, freq_mask.sum()), dtype=np.complex128)
+    for idx, sig in enumerate(x):
+        x_mt[idx] = fftpack.fft(sig[np.newaxis, :] * dpss,
+                                n=n_fft)[:, freq_mask]
     return x_mt, freqs
 
 
- at verbose
-def multitaper_psd(x, sfreq=2 * np.pi, fmin=0, fmax=np.inf, bandwidth=None,
-                   adaptive=False, low_bias=True, n_jobs=1,
-                   normalization='length', verbose=None):
+def _psd_multitaper(x, sfreq, fmin=0, fmax=np.inf, bandwidth=None,
+                    adaptive=False, low_bias=True, normalization='length',
+                    n_jobs=1):
     """Compute power spectrum density (PSD) using a multi-taper method
 
     Parameters
     ----------
-    x : array, shape=(n_signals, n_times) or (n_times,)
+    x : array, shape=(..., n_times)
         The data to compute PSD from.
     sfreq : float
         The sampling frequency.
@@ -475,19 +477,18 @@ def multitaper_psd(x, sfreq=2 * np.pi, fmin=0, fmax=np.inf, bandwidth=None,
     low_bias : bool
         Only use tapers with more than 90% spectral concentration within
         bandwidth.
-    n_jobs : int
-        Number of parallel jobs to use (only used if adaptive=True).
     normalization : str
         Either "full" or "length" (default). If "full", the PSD will
         be normalized by the sampling rate as well as the length of
         the signal (as in nitime).
-    verbose : bool, str, int, or None
-        If not None, override default verbose level (see mne.verbose).
+    n_jobs : int
+        Number of parallel jobs to use (only used if adaptive=True).
 
     Returns
     -------
-    psd : array, shape=(n_signals, len(freqs)) or (len(freqs),)
-        The computed PSD.
+    psds : ndarray, shape (..., n_freqs) or
+        The power spectral densities. All dimensions up to the last will
+        be the same as input.
     freqs : array
         The frequency points in Hz of the PSD.
 
@@ -497,17 +498,18 @@ def multitaper_psd(x, sfreq=2 * np.pi, fmin=0, fmax=np.inf, bandwidth=None,
 
     Notes
     -----
-    .. versionadded:: 0.9.0
+    .. versionadded:: 0.12.0
     """
     if normalization not in ('length', 'full'):
         raise ValueError('Normalization must be "length" or "full", not %s'
                          % normalization)
-    if x.ndim > 2:
-        raise ValueError('x can only be 1d or 2d')
 
-    x_in = np.atleast_2d(x)
-
-    n_times = x_in.shape[1]
+    # Reshape data so its 2-D for parallelization
+    ndim_in = x.ndim
+    x = np.atleast_2d(x)
+    n_times = x.shape[-1]
+    dshape = x.shape[:-1]
+    x = x.reshape(-1, n_times)
 
     # compute standardized half-bandwidth
     if bandwidth is not None:
@@ -515,40 +517,101 @@ def multitaper_psd(x, sfreq=2 * np.pi, fmin=0, fmax=np.inf, bandwidth=None,
     else:
         half_nbw = 4
 
+    # Create tapers and compute spectra
     n_tapers_max = int(2 * half_nbw)
-
     dpss, eigvals = dpss_windows(n_times, half_nbw, n_tapers_max,
                                  low_bias=low_bias)
 
-    # compute the tapered spectra
-    x_mt, freqs = _mt_spectra(x_in, dpss, sfreq)
-
     # descide which frequencies to keep
+    freqs = fftpack.fftfreq(x.shape[1], 1. / sfreq)
+    freqs = freqs[(freqs >= 0)]  # what we get from _mt_spectra
     freq_mask = (freqs >= fmin) & (freqs <= fmax)
+    freqs = freqs[freq_mask]
 
     # combine the tapered spectra
     if adaptive and len(eigvals) < 3:
-        warn('Not adaptively combining the spectral estimators '
-             'due to a low number of tapers.')
+        warn('Not adaptively combining the spectral estimators due to a low '
+             'number of tapers.')
         adaptive = False
 
-    if not adaptive:
-        x_mt = x_mt[:, :, freq_mask]
-        weights = np.sqrt(eigvals)[np.newaxis, :, np.newaxis]
-        psd = _psd_from_mt(x_mt, weights)
-    else:
-        parallel, my_psd_from_mt_adaptive, n_jobs = \
-            parallel_func(_psd_from_mt_adaptive, n_jobs)
-        out = parallel(my_psd_from_mt_adaptive(x, eigvals, freq_mask)
-                       for x in np.array_split(x_mt, n_jobs))
-        psd = np.concatenate(out)
+    psd = np.zeros((x.shape[0], freq_mask.sum()))
+    # Let's go in up to 50 MB chunks of signals to save memory
+    n_chunk = max(50000000 // (len(freq_mask) * len(eigvals) * 16), n_jobs)
+    offsets = np.concatenate((np.arange(0, x.shape[0], n_chunk), [x.shape[0]]))
+    for start, stop in zip(offsets[:-1], offsets[1:]):
+        x_mt = _mt_spectra(x[start:stop], dpss, sfreq)[0]
+        if not adaptive:
+            weights = np.sqrt(eigvals)[np.newaxis, :, np.newaxis]
+            psd[start:stop] = _psd_from_mt(x_mt[:, :, freq_mask], weights)
+        else:
+            n_splits = min(stop - start, n_jobs)
+            parallel, my_psd_from_mt_adaptive, n_jobs = \
+                parallel_func(_psd_from_mt_adaptive, n_splits)
+            out = parallel(my_psd_from_mt_adaptive(x, eigvals, freq_mask)
+                           for x in np.array_split(x_mt, n_splits))
+            psd[start:stop] = np.concatenate(out)
 
-    if x.ndim == 1:
-        # return a 1d array if input was 1d
-        psd = psd[0, :]
-
-    freqs = freqs[freq_mask]
     if normalization == 'full':
         psd /= sfreq
 
+    # Combining/reshaping to original data shape
+    psd.shape = dshape + (-1,)
+    if ndim_in == 1:
+        psd = psd[0]
     return psd, freqs
+
+
+ at deprecated('This will be deprecated in release v0.12, see psd_multitaper.')
+ at verbose
+def multitaper_psd(x, sfreq=2 * np.pi, fmin=0, fmax=np.inf, bandwidth=None,
+                   adaptive=False, low_bias=True, n_jobs=1,
+                   normalization='length', verbose=None):
+    """Compute power spectrum density (PSD) using a multi-taper method
+
+    Parameters
+    ----------
+    x : array, shape=(n_signals, n_times) or (n_times,)
+        The data to compute PSD from.
+    sfreq : float
+        The sampling frequency.
+    fmin : float
+        The lower frequency of interest.
+    fmax : float
+        The upper frequency of interest.
+    bandwidth : float
+        The bandwidth of the multi taper windowing function in Hz.
+    adaptive : bool
+        Use adaptive weights to combine the tapered spectra into PSD
+        (slow, use n_jobs >> 1 to speed up computation).
+    low_bias : bool
+        Only use tapers with more than 90% spectral concentration within
+        bandwidth.
+    n_jobs : int
+        Number of parallel jobs to use (only used if adaptive=True).
+    normalization : str
+        Either "full" or "length" (default). If "full", the PSD will
+        be normalized by the sampling rate as well as the length of
+        the signal (as in nitime).
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    Returns
+    -------
+    psd : array, shape=(n_signals, len(freqs)) or (len(freqs),)
+        The computed PSD.
+    freqs : array
+        The frequency points in Hz of the PSD.
+
+    See Also
+    --------
+    mne.io.Raw.plot_psd
+    mne.Epochs.plot_psd
+
+    Notes
+    -----
+    .. versionadded:: 0.9.0
+    """
+    return _psd_multitaper(x=x, sfreq=sfreq, fmin=fmin, fmax=fmax,
+                           bandwidth=bandwidth, adaptive=adaptive,
+                           low_bias=low_bias,
+                           normalization=normalization, n_jobs=n_jobs)
diff --git a/mne/time_frequency/psd.py b/mne/time_frequency/psd.py
index 1544cbd..1de44d1 100644
--- a/mne/time_frequency/psd.py
+++ b/mne/time_frequency/psd.py
@@ -5,11 +5,12 @@
 import numpy as np
 
 from ..parallel import parallel_func
-from ..io.proj import make_projector_info
-from ..io.pick import pick_types
-from ..utils import logger, verbose, _time_mask
+from ..io.pick import _pick_data_channels
+from ..utils import logger, verbose, deprecated, _time_mask
+from .multitaper import _psd_multitaper
 
 
+ at deprecated('This will be deprecated in release v0.12, see psd_welch.')
 @verbose
 def compute_raw_psd(raw, tmin=0., tmax=None, picks=None, fmin=0,
                     fmax=np.inf, n_fft=2048, n_overlap=0,
@@ -51,22 +52,24 @@ def compute_raw_psd(raw, tmin=0., tmax=None, picks=None, fmin=0,
         The PSD for all channels
     freqs: array of float
         The frequencies
+
+    See Also
+    --------
+    psd_welch, psd_multitaper
     """
     from scipy.signal import welch
+    from ..io.base import _BaseRaw
+    if not isinstance(raw, _BaseRaw):
+        raise ValueError('Input must be an instance of Raw')
     tmax = raw.times[-1] if tmax is None else tmax
     start, stop = raw.time_as_index([tmin, tmax])
-    if picks is not None:
-        data, times = raw[picks, start:(stop + 1)]
-    else:
-        data, times = raw[:, start:(stop + 1)]
-    n_fft, n_overlap = _check_nfft(len(times), n_fft, n_overlap)
+    picks = slice(None) if picks is None else picks
 
     if proj:
-        proj, _ = make_projector_info(raw.info)
-        if picks is not None:
-            data = np.dot(proj[picks][:, picks], data)
-        else:
-            data = np.dot(proj, data)
+        # Copy first so it's not modified
+        raw = raw.copy().apply_proj()
+    data, times = raw[picks, start:(stop + 1)]
+    n_fft, n_overlap = _check_nfft(len(times), n_fft, n_overlap)
 
     n_fft = int(n_fft)
     Fs = raw.info['sfreq']
@@ -112,6 +115,235 @@ def _check_nfft(n, n_fft, n_overlap):
     return n_fft, n_overlap
 
 
+def _check_psd_data(inst, tmin, tmax, picks, proj):
+    """Helper to do checks on PSD data / pull arrays from inst"""
+    from ..io.base import _BaseRaw
+    from ..epochs import _BaseEpochs
+    from ..evoked import Evoked
+    if not isinstance(inst, (_BaseEpochs, _BaseRaw, Evoked)):
+        raise ValueError('epochs must be an instance of Epochs, Raw, or'
+                         'Evoked. Got type {0}'.format(type(inst)))
+
+    time_mask = _time_mask(inst.times, tmin, tmax, sfreq=inst.info['sfreq'])
+    if picks is None:
+        picks = _pick_data_channels(inst.info, with_ref_meg=False)
+    if proj:
+        # Copy first so it's not modified
+        inst = inst.copy().apply_proj()
+
+    sfreq = inst.info['sfreq']
+    if isinstance(inst, _BaseRaw):
+        start, stop = np.where(time_mask)[0][[0, -1]]
+        data, times = inst[picks, start:(stop + 1)]
+    elif isinstance(inst, _BaseEpochs):
+        data = inst.get_data()[:, picks][:, :, time_mask]
+    elif isinstance(inst, Evoked):
+        data = inst.data[picks][:, time_mask]
+
+    return data, sfreq
+
+
+def _psd_welch(x, sfreq, fmin=0, fmax=np.inf, n_fft=256, n_overlap=0,
+               n_jobs=1):
+    """Compute power spectral density (PSD) using Welch's method.
+
+    x : array, shape=(..., n_times)
+        The data to compute PSD from.
+    sfreq : float
+        The sampling frequency.
+    fmin : float
+        The lower frequency of interest.
+    fmax : float
+        The upper frequency of interest.
+    n_fft : int
+        The length of the tapers ie. the windows. The smaller
+        it is the smoother are the PSDs. The default value is 256.
+        If ``n_fft > len(inst.times)``, it will be adjusted down to
+        ``len(inst.times)``.
+    n_overlap : int
+        The number of points of overlap between blocks. Will be adjusted
+        to be <= n_fft. The default value is 0.
+    n_jobs : int
+        Number of CPUs to use in the computation.
+
+    Returns
+    -------
+    psds : ndarray, shape (..., n_freqs) or
+        The power spectral densities. All dimensions up to the last will
+        be the same as input.
+    freqs : ndarray, shape (n_freqs,)
+        The frequencies.
+    """
+    from scipy.signal import welch
+    dshape = x.shape[:-1]
+    n_times = x.shape[-1]
+    x = x.reshape(-1, n_times)
+
+    # Prep the PSD
+    n_fft, n_overlap = _check_nfft(n_times, n_fft, n_overlap)
+    win_size = n_fft / float(sfreq)
+    logger.info("Effective window size : %0.3f (s)" % win_size)
+    freqs = np.arange(n_fft // 2 + 1, dtype=float) * (sfreq / n_fft)
+    freq_mask = (freqs >= fmin) & (freqs <= fmax)
+    freqs = freqs[freq_mask]
+
+    # Parallelize across first N-1 dimensions
+    parallel, my_pwelch, n_jobs = parallel_func(_pwelch, n_jobs=n_jobs)
+    x_splits = np.array_split(x, n_jobs)
+    f_psd = parallel(my_pwelch(d, noverlap=n_overlap, nfft=n_fft,
+                     fs=sfreq, freq_mask=freq_mask,
+                     welch_fun=welch)
+                     for d in x_splits)
+
+    # Combining/reshaping to original data shape
+    psds = np.concatenate(f_psd, axis=0)
+    psds = psds.reshape(np.hstack([dshape, -1]))
+    return psds, freqs
+
+
+ at verbose
+def psd_welch(inst, fmin=0, fmax=np.inf, tmin=None, tmax=None, n_fft=256,
+              n_overlap=0, picks=None, proj=False, n_jobs=1, verbose=None):
+    """Compute the power spectral density (PSD) using Welch's method.
+
+    Calculates periodigrams for a sliding window over the
+    time dimension, then averages them together for each channel/epoch.
+
+    Parameters
+    ----------
+    inst : instance of Epochs or Raw or Evoked
+        The data for PSD calculation
+    fmin : float
+        Min frequency of interest
+    fmax : float
+        Max frequency of interest
+    tmin : float | None
+        Min time of interest
+    tmax : float | None
+        Max time of interest
+    n_fft : int
+        The length of the tapers ie. the windows. The smaller
+        it is the smoother are the PSDs. The default value is 256.
+        If ``n_fft > len(inst.times)``, it will be adjusted down to
+        ``len(inst.times)``.
+    n_overlap : int
+        The number of points of overlap between blocks. Will be adjusted
+        to be <= n_fft. The default value is 0.
+    picks : array-like of int | None
+        The selection of channels to include in the computation.
+        If None, take all channels.
+    proj : bool
+        Apply SSP projection vectors. If inst is ndarray this is not used.
+    n_jobs : int
+        Number of CPUs to use in the computation.
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    Returns
+    -------
+    psds : ndarray, shape (..., n_freqs)
+        The power spectral densities. If input is of type Raw,
+        then psds will be shape (n_channels, n_freqs), if input is type Epochs
+        then psds will be shape (n_epochs, n_channels, n_freqs).
+    freqs : ndarray, shape (n_freqs,)
+        The frequencies.
+
+    See Also
+    --------
+    mne.io.Raw.plot_psd, mne.Epochs.plot_psd, psd_multitaper
+
+    Notes
+    -----
+    .. versionadded:: 0.12.0
+    """
+    # Prep data
+    data, sfreq = _check_psd_data(inst, tmin, tmax, picks, proj)
+    return _psd_welch(data, sfreq, fmin=fmin, fmax=fmax, n_fft=n_fft,
+                      n_overlap=n_overlap, n_jobs=n_jobs)
+
+
+ at verbose
+def psd_multitaper(inst, fmin=0, fmax=np.inf, tmin=None, tmax=None,
+                   bandwidth=None, adaptive=False, low_bias=True,
+                   normalization='length', picks=None, proj=False,
+                   n_jobs=1, verbose=None):
+    """Compute the power spectral density (PSD) using multitapers.
+
+    Calculates spectral density for orthogonal tapers, then averages them
+    together for each channel/epoch. See [1] for a description of the tapers
+    and [2] for the general method.
+
+    Parameters
+    ----------
+    inst : instance of Epochs or Raw or Evoked
+        The data for PSD calculation.
+    fmin : float
+        Min frequency of interest
+    fmax : float
+        Max frequency of interest
+    tmin : float | None
+        Min time of interest
+    tmax : float | None
+        Max time of interest
+    bandwidth : float
+        The bandwidth of the multi taper windowing function in Hz. The default
+        value is a window half-bandwidth of 4.
+    adaptive : bool
+        Use adaptive weights to combine the tapered spectra into PSD
+        (slow, use n_jobs >> 1 to speed up computation).
+    low_bias : bool
+        Only use tapers with more than 90% spectral concentration within
+        bandwidth.
+    normalization : str
+        Either "full" or "length" (default). If "full", the PSD will
+        be normalized by the sampling rate as well as the length of
+        the signal (as in nitime).
+    picks : array-like of int | None
+        The selection of channels to include in the computation.
+        If None, take all channels.
+    proj : bool
+        Apply SSP projection vectors. If inst is ndarray this is not used.
+    n_jobs : int
+        Number of CPUs to use in the computation.
+    verbose : bool, str, int, or None
+        If not None, override default verbose level (see mne.verbose).
+
+    Returns
+    -------
+    psds : ndarray, shape (..., n_freqs)
+        The power spectral densities. If input is of type Raw,
+        then psds will be shape (n_channels, n_freqs), if input is type Epochs
+        then psds will be shape (n_epochs, n_channels, n_freqs).
+    freqs : ndarray, shape (n_freqs,)
+        The frequencies.
+
+    References
+    ----------
+    .. [1] Slepian, D. "Prolate spheroidal wave functions, Fourier analysis,
+           and uncertainty V: The discrete case." Bell System Technical
+           Journal, vol. 57, 1978.
+
+    .. [2] Percival D.B. and Walden A.T. "Spectral Analysis for Physical
+           Applications: Multitaper and Conventional Univariate Techniques."
+           Cambridge University Press, 1993.
+
+    See Also
+    --------
+    mne.io.Raw.plot_psd, mne.Epochs.plot_psd, psd_welch
+
+    Notes
+    -----
+    .. versionadded:: 0.12.0
+    """
+    # Prep data
+    data, sfreq = _check_psd_data(inst, tmin, tmax, picks, proj)
+    return _psd_multitaper(data, sfreq, fmin=fmin, fmax=fmax,
+                           bandwidth=bandwidth, adaptive=adaptive,
+                           low_bias=low_bias,
+                           normalization=normalization,  n_jobs=n_jobs)
+
+
+ at deprecated('This will be deprecated in release v0.12, see psd_welch.')
 @verbose
 def compute_epochs_psd(epochs, picks=None, fmin=0, fmax=np.inf, tmin=None,
                        tmax=None, n_fft=256, n_overlap=0, proj=False,
@@ -152,29 +384,32 @@ def compute_epochs_psd(epochs, picks=None, fmin=0, fmax=np.inf, tmin=None,
     -------
     psds : ndarray (n_epochs, n_channels, n_freqs)
         The power spectral densities.
-    freqs : ndarray (n_freqs)
+    freqs : ndarray, shape (n_freqs,)
         The frequencies.
+
+    See Also
+    --------
+    psd_welch, psd_multitaper
     """
     from scipy.signal import welch
+    from ..epochs import _BaseEpochs
+    if not isinstance(epochs, _BaseEpochs):
+        raise ValueError("Input must be an instance of Epochs")
     n_fft = int(n_fft)
     Fs = epochs.info['sfreq']
     if picks is None:
-        picks = pick_types(epochs.info, meg=True, eeg=True, ref_meg=False,
-                           exclude='bads')
+        picks = _pick_data_channels(epochs.info, with_ref_meg=False)
     n_fft, n_overlap = _check_nfft(len(epochs.times), n_fft, n_overlap)
 
     if tmin is not None or tmax is not None:
-        time_mask = _time_mask(epochs.times, tmin, tmax)
+        time_mask = _time_mask(epochs.times, tmin, tmax,
+                               sfreq=epochs.info['sfreq'])
     else:
         time_mask = slice(None)
-
-    data = epochs.get_data()[:, picks][..., time_mask]
     if proj:
-        proj, _ = make_projector_info(epochs.info)
-        if picks is not None:
-            data = np.dot(proj[picks][:, picks], data)
-        else:
-            data = np.dot(proj, data)
+        # Copy first so it's not modified
+        epochs = epochs.copy().apply_proj()
+    data = epochs.get_data()[:, picks][:, :, time_mask]
 
     logger.info("Effective window size : %0.3f (s)" % (n_fft / float(Fs)))
 
diff --git a/mne/time_frequency/tests/test_ar.py b/mne/time_frequency/tests/test_ar.py
index 01d49f4..146e2fd 100644
--- a/mne/time_frequency/tests/test_ar.py
+++ b/mne/time_frequency/tests/test_ar.py
@@ -28,7 +28,7 @@ def test_yule_walker():
 def test_ar_raw():
     """Test fitting AR model on raw data
     """
-    raw = io.Raw(raw_fname)
+    raw = io.read_raw_fif(raw_fname)
     # pick MEG gradiometers
     picks = pick_types(raw.info, meg='grad', exclude='bads')
     picks = picks[:2]
diff --git a/mne/time_frequency/tests/test_multitaper.py b/mne/time_frequency/tests/test_multitaper.py
index 2c4bdbe..e64e8ae 100644
--- a/mne/time_frequency/tests/test_multitaper.py
+++ b/mne/time_frequency/tests/test_multitaper.py
@@ -3,8 +3,11 @@ from nose.tools import assert_raises
 from numpy.testing import assert_array_almost_equal
 from distutils.version import LooseVersion
 
-from mne.time_frequency import dpss_windows, multitaper_psd
+from mne.time_frequency import psd_multitaper
+from mne.time_frequency.multitaper import dpss_windows
 from mne.utils import requires_nitime
+from mne.io import RawArray
+from mne import create_info
 
 
 @requires_nitime
@@ -37,17 +40,21 @@ def test_multitaper_psd():
 
     import nitime as ni
     n_times = 1000
-    x = np.random.randn(5, n_times)
+    n_channels = 5
+    data = np.random.RandomState(0).randn(n_channels, n_times)
     sfreq = 500
-    assert_raises(ValueError, multitaper_psd, x, sfreq, normalization='foo')
+    info = create_info(n_channels, sfreq, 'eeg')
+    raw = RawArray(data, info)
+    assert_raises(ValueError, psd_multitaper, raw, sfreq, normalization='foo')
     ni_5 = (LooseVersion(ni.__version__) >= LooseVersion('0.5'))
     norm = 'full' if ni_5 else 'length'
 
     for adaptive, n_jobs in zip((False, True, True), (1, 1, 2)):
-        psd, freqs = multitaper_psd(x, sfreq, adaptive=adaptive, n_jobs=n_jobs,
+        psd, freqs = psd_multitaper(raw, adaptive=adaptive,
+                                    n_jobs=n_jobs,
                                     normalization=norm)
         freqs_ni, psd_ni, _ = ni.algorithms.spectral.multi_taper_psd(
-            x, sfreq, adaptive=adaptive, jackknife=False)
+            data, sfreq, adaptive=adaptive, jackknife=False)
 
         # for some reason nitime returns n_times + 1 frequency points
         # causing the value at 0 to be different
diff --git a/mne/time_frequency/tests/test_psd.py b/mne/time_frequency/tests/test_psd.py
index ab90940..d950d25 100644
--- a/mne/time_frequency/tests/test_psd.py
+++ b/mne/time_frequency/tests/test_psd.py
@@ -1,11 +1,14 @@
 import numpy as np
+import warnings
 import os.path as op
-from numpy.testing import assert_array_almost_equal
+from numpy.testing import assert_array_almost_equal, assert_raises
 from nose.tools import assert_true
 
 from mne import io, pick_types, Epochs, read_events
+from mne.io import RawArray
 from mne.utils import requires_version, slow_test
-from mne.time_frequency import compute_raw_psd, compute_epochs_psd
+from mne.time_frequency import (compute_raw_psd, compute_epochs_psd,
+                                psd_welch, psd_multitaper)
 
 base_dir = op.join(op.dirname(__file__), '..', '..', 'io', 'tests', 'data')
 raw_fname = op.join(base_dir, 'test_raw.fif')
@@ -14,99 +17,125 @@ event_fname = op.join(base_dir, 'test-eve.fif')
 
 @requires_version('scipy', '0.12')
 def test_psd():
-    """Test PSD estimation
+    """Tests the welch and multitaper PSD
     """
-    raw = io.Raw(raw_fname)
-
-    exclude = raw.info['bads'] + ['MEG 2443', 'EEG 053']  # bads + 2 more
-
-    # picks MEG gradiometers
-    picks = pick_types(raw.info, meg='mag', eeg=False, stim=False,
-                       exclude=exclude)
-
-    picks = picks[:2]
-
-    tmin, tmax = 0, 10  # use the first 60s of data
-    fmin, fmax = 2, 70  # look at frequencies between 5 and 70Hz
-
+    raw = io.read_raw_fif(raw_fname)
+    picks_psd = [0, 1]
+
+    # Populate raw with sinusoids
+    rng = np.random.RandomState(40)
+    data = 0.1 * rng.randn(len(raw.ch_names), raw.n_times)
+    freqs_sig = [8., 50.]
+    for ix, freq in zip(picks_psd, freqs_sig):
+        data[ix, :] += 2 * np.sin(np.pi * 2. * freq * raw.times)
+    first_samp = raw._first_samps[0]
+    raw = RawArray(data, raw.info)
+
+    tmin, tmax = 0, 20  # use a few seconds of data
+    fmin, fmax = 2, 70  # look at frequencies between 2 and 70Hz
     n_fft = 128
-    psds, freqs = compute_raw_psd(raw, tmin=tmin, tmax=tmax, fmin=fmin,
-                                  fmax=fmax, proj=False, n_fft=n_fft,
-                                  picks=picks, n_jobs=1)
-    assert_true(psds.shape == (len(picks), len(freqs)))
-    assert_true(np.sum(freqs < 0) == 0)
-    assert_true(np.sum(psds < 0) == 0)
-
-    n_fft = 2048  # the FFT size (n_fft). Ideally a power of 2
-    psds, freqs = compute_raw_psd(raw, tmin=tmin, tmax=tmax, picks=picks,
-                                  fmin=fmin, fmax=fmax, n_fft=n_fft, n_jobs=1,
-                                  proj=False)
-    psds_proj, freqs = compute_raw_psd(raw, tmin=tmin, tmax=tmax, picks=picks,
-                                       fmin=fmin, fmax=fmax, n_fft=n_fft,
-                                       n_jobs=1, proj=True)
-
-    assert_array_almost_equal(psds, psds_proj)
-    assert_true(psds.shape == (len(picks), len(freqs)))
-    assert_true(np.sum(freqs < 0) == 0)
-    assert_true(np.sum(psds < 0) == 0)
-
-
- at requires_version('scipy', '0.12')
-def test_psd_epochs():
-    """Test PSD estimation on epochs
-    """
-    raw = io.Raw(raw_fname)
-
-    exclude = raw.info['bads'] + ['MEG 2443', 'EEG 053']  # bads + 2 more
-
-    # picks MEG gradiometers
-    picks = pick_types(raw.info, meg='mag', eeg=False, stim=False,
-                       exclude=exclude)
-
-    picks = picks[:2]
-
-    n_fft = 512  # the FFT size (n_fft). Ideally a power of 2
-
-    tmin, tmax, event_id = -0.5, 0.5, 1
-    include = []
-    raw.info['bads'] += ['MEG 2443']  # bads
-
-    # picks MEG gradiometers
-    picks = pick_types(raw.info, meg='grad', eeg=False, eog=True,
-                       stim=False, include=include, exclude='bads')
 
+    # -- Raw --
+    kws_psd = dict(tmin=tmin, tmax=tmax, fmin=fmin, fmax=fmax,
+                   picks=picks_psd)  # Common to all
+    kws_welch = dict(n_fft=n_fft)
+    kws_mt = dict(low_bias=True)
+    funcs = [(psd_welch, kws_welch),
+             (psd_multitaper, kws_mt),
+             (compute_raw_psd, kws_welch)]
+
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        for func, kws in funcs:
+            kws = kws.copy()
+            kws.update(kws_psd)
+            psds, freqs = func(raw, proj=False, **kws)
+            psds_proj, freqs_proj = func(raw, proj=True, **kws)
+
+            assert_true(psds.shape == (len(kws['picks']), len(freqs)))
+            assert_true(np.sum(freqs < 0) == 0)
+            assert_true(np.sum(psds < 0) == 0)
+
+            # Is power found where it should be
+            ixs_max = np.argmax(psds, axis=1)
+            for ixmax, ifreq in zip(ixs_max, freqs_sig):
+                # Find nearest frequency to the "true" freq
+                ixtrue = np.argmin(np.abs(ifreq - freqs))
+                assert_true(np.abs(ixmax - ixtrue) < 2)
+
+            # Make sure the projection doesn't change channels it shouldn't
+            assert_array_almost_equal(psds, psds_proj)
+            # Array input shouldn't work
+            assert_raises(ValueError, func, raw[:3, :20][0])
+        assert_true(len(w), 3)
+
+    # -- Epochs/Evoked --
     events = read_events(event_fname)
-
-    epochs = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0),
-                    reject=dict(grad=4000e-13, eog=150e-6), proj=False,
-                    preload=True)
+    events[:, 0] -= first_samp
+    tmin, tmax, event_id = -0.5, 0.5, 1
+    epochs = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks_psd,
+                    proj=False, preload=True, baseline=None)
+    evoked = epochs.average()
 
     tmin_full, tmax_full = -1, 1
-    epochs_full = Epochs(raw, events[:10], event_id, tmax=tmax_full,
-                         tmin=tmin_full, picks=picks,
-                         baseline=(None, 0),
-                         reject=dict(grad=4000e-13, eog=150e-6), proj=False,
-                         preload=True)
-
-    picks = pick_types(epochs.info, meg='grad', eeg=False, eog=True,
-                       stim=False, include=include, exclude='bads')
-    psds, freqs = compute_epochs_psd(epochs[:1], fmin=2, fmax=300,
-                                     n_fft=n_fft, picks=picks)
-
-    psds_t, freqs_t = compute_epochs_psd(epochs_full[:1], fmin=2, fmax=300,
-                                         tmin=tmin, tmax=tmax,
-                                         n_fft=n_fft, picks=picks)
-    # this one will fail if you add for example 0.1 to tmin
-    assert_array_almost_equal(psds, psds_t, 27)
-
-    psds_proj, _ = compute_epochs_psd(epochs[:1].apply_proj(), fmin=2,
-                                      fmax=300, n_fft=n_fft, picks=picks)
-
-    assert_array_almost_equal(psds, psds_proj)
-    assert_true(psds.shape == (1, len(picks), len(freqs)))
-    assert_true(np.sum(freqs < 0) == 0)
-    assert_true(np.sum(psds < 0) == 0)
+    epochs_full = Epochs(raw, events[:10], event_id, tmin_full, tmax_full,
+                         picks=picks_psd, proj=False, preload=True,
+                         baseline=None)
+    kws_psd = dict(tmin=tmin, tmax=tmax, fmin=fmin, fmax=fmax,
+                   picks=picks_psd)  # Common to all
+    funcs = [(psd_welch, kws_welch),
+             (psd_multitaper, kws_mt),
+             (compute_epochs_psd, kws_welch)]
+
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        for func, kws in funcs:
+            kws = kws.copy()
+            kws.update(kws_psd)
+
+            psds, freqs = func(
+                epochs[:1], proj=False, **kws)
+            psds_proj, freqs_proj = func(
+                epochs[:1], proj=True, **kws)
+            psds_f, freqs_f = func(
+                epochs_full[:1], proj=False, **kws)
+
+            # this one will fail if you add for example 0.1 to tmin
+            assert_array_almost_equal(psds, psds_f, 27)
+            # Make sure the projection doesn't change channels it shouldn't
+            assert_array_almost_equal(psds, psds_proj, 27)
+
+            # Is power found where it should be
+            ixs_max = np.argmax(psds.mean(0), axis=1)
+            for ixmax, ifreq in zip(ixs_max, freqs_sig):
+                # Find nearest frequency to the "true" freq
+                ixtrue = np.argmin(np.abs(ifreq - freqs))
+                assert_true(np.abs(ixmax - ixtrue) < 2)
+            assert_true(psds.shape == (1, len(kws['picks']), len(freqs)))
+            assert_true(np.sum(freqs < 0) == 0)
+            assert_true(np.sum(psds < 0) == 0)
+
+            # Array input shouldn't work
+            assert_raises(ValueError, func, epochs.get_data())
+
+            if func is not compute_epochs_psd:
+                # Testing evoked (doesn't work w/ compute_epochs_psd)
+                psds_ev, freqs_ev = func(
+                    evoked, proj=False, **kws)
+                psds_ev_proj, freqs_ev_proj = func(
+                    evoked, proj=True, **kws)
+
+                # Is power found where it should be
+                ixs_max = np.argmax(psds_ev, axis=1)
+                for ixmax, ifreq in zip(ixs_max, freqs_sig):
+                    # Find nearest frequency to the "true" freq
+                    ixtrue = np.argmin(np.abs(ifreq - freqs_ev))
+                    assert_true(np.abs(ixmax - ixtrue) < 2)
+
+                # Make sure the projection doesn't change channels it shouldn't
+                assert_array_almost_equal(psds_ev, psds_ev_proj, 27)
+                assert_true(psds_ev.shape == (len(kws['picks']), len(freqs)))
+        assert_true(len(w), 3)
 
 
 @slow_test
@@ -114,7 +143,7 @@ def test_psd_epochs():
 def test_compares_psd():
     """Test PSD estimation on raw for plt.psd and scipy.signal.welch
     """
-    raw = io.Raw(raw_fname)
+    raw = io.read_raw_fif(raw_fname)
 
     exclude = raw.info['bads'] + ['MEG 2443', 'EEG 053']  # bads + 2 more
 
@@ -127,10 +156,10 @@ def test_compares_psd():
     n_fft = 2048
 
     # Compute psds with the new implementation using Welch
-    psds_welch, freqs_welch = compute_raw_psd(raw, tmin=tmin, tmax=tmax,
-                                              fmin=fmin, fmax=fmax,
-                                              proj=False, picks=picks,
-                                              n_fft=n_fft, n_jobs=1)
+    psds_welch, freqs_welch = psd_welch(raw, tmin=tmin, tmax=tmax,
+                                        fmin=fmin, fmax=fmax,
+                                        proj=False, picks=picks,
+                                        n_fft=n_fft, n_jobs=1)
 
     # Compute psds with plt.psd
     start, stop = raw.time_as_index([tmin, tmax])
diff --git a/mne/time_frequency/tests/test_stockwell.py b/mne/time_frequency/tests/test_stockwell.py
index 1d57963..65ffa92 100644
--- a/mne/time_frequency/tests/test_stockwell.py
+++ b/mne/time_frequency/tests/test_stockwell.py
@@ -3,10 +3,12 @@
 #
 # License : BSD 3-clause
 
-import numpy as np
 import os.path as op
-from numpy.testing import assert_array_almost_equal, assert_allclose
+import warnings
+
 from nose.tools import assert_true, assert_equal
+import numpy as np
+from numpy.testing import assert_array_almost_equal, assert_allclose
 
 from scipy import fftpack
 
@@ -20,7 +22,7 @@ raw_fname = op.join(base_dir, 'test_raw.fif')
 
 event_id, tmin, tmax = 1, -0.2, 0.5
 event_id_2 = 2
-raw = io.Raw(raw_fname, add_eeg_ref=False)
+raw = io.read_raw_fif(raw_fname, add_eeg_ref=False)
 event_name = op.join(base_dir, 'test-eve.fif')
 events = read_events(event_name)
 picks = pick_types(raw.info, meg=True, eeg=True, stim=True,
@@ -77,12 +79,14 @@ def test_stockwell_api():
     epochs = Epochs(raw, events,  # XXX pick 2 has epochs of zeros.
                     event_id, tmin, tmax, picks=[0, 1, 3], baseline=(None, 0))
     for fmin, fmax in [(None, 50), (5, 50), (5, None)]:
-        power, itc = tfr_stockwell(epochs, fmin=fmin, fmax=fmax,
-                                   return_itc=True)
+        with warnings.catch_warnings(record=True):  # zero papdding
+            power, itc = tfr_stockwell(epochs, fmin=fmin, fmax=fmax,
+                                       return_itc=True)
         if fmax is not None:
             assert_true(power.freqs.max() <= fmax)
-        power_evoked = tfr_stockwell(epochs.average(), fmin=fmin, fmax=fmax,
-                                     return_itc=False)
+        with warnings.catch_warnings(record=True):  # padding
+            power_evoked = tfr_stockwell(epochs.average(), fmin=fmin,
+                                         fmax=fmax, return_itc=False)
         # for multitaper these don't necessarily match, but they seem to
         # for stockwell... if this fails, this maybe could be changed
         # just to check the shape
diff --git a/mne/time_frequency/tests/test_tfr.py b/mne/time_frequency/tests/test_tfr.py
index e54f60e..532ebb7 100644
--- a/mne/time_frequency/tests/test_tfr.py
+++ b/mne/time_frequency/tests/test_tfr.py
@@ -11,7 +11,7 @@ from mne.time_frequency import single_trial_power
 from mne.time_frequency.tfr import (cwt_morlet, morlet, tfr_morlet,
                                     _dpss_wavelet, tfr_multitaper,
                                     AverageTFR, read_tfrs, write_tfrs,
-                                    combine_tfr)
+                                    combine_tfr, cwt)
 
 import matplotlib
 matplotlib.use('Agg')  # for testing don't use X server
@@ -37,10 +37,10 @@ def test_time_frequency():
     # Set parameters
     event_id = 1
     tmin = -0.2
-    tmax = 0.5
+    tmax = 0.498  # Allows exhaustive decimation testing
 
     # Setup for reading the raw data
-    raw = io.Raw(raw_fname)
+    raw = io.read_raw_fif(raw_fname)
     events = read_events(event_fname)
 
     include = []
@@ -73,6 +73,8 @@ def test_time_frequency():
     assert_raises(ValueError, tfr_morlet, evoked, freqs, 1., return_itc=True)
     power, itc = tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles,
                             use_fft=True, return_itc=True)
+    power_, itc_ = tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles,
+                              use_fft=True, return_itc=True, decim=slice(0, 2))
     # Test picks argument
     power_picks, itc_picks = tfr_morlet(epochs_nopicks, freqs=freqs,
                                         n_cycles=n_cycles, use_fft=True,
@@ -98,6 +100,8 @@ def test_time_frequency():
     assert_equal(itc.nave, nave)
     assert_true(power.data.shape == (len(picks), len(freqs), len(times)))
     assert_true(power.data.shape == itc.data.shape)
+    assert_true(power_.data.shape == (len(picks), len(freqs), 2))
+    assert_true(power_.data.shape == itc_.data.shape)
     assert_true(np.sum(itc.data >= 1) == 0)
     assert_true(np.sum(itc.data <= 0) == 0)
 
@@ -133,11 +137,26 @@ def test_time_frequency():
     Fs = raw.info['sfreq']  # sampling in Hz
     tfr = cwt_morlet(data[0], Fs, freqs, use_fft=True, n_cycles=2)
     assert_true(tfr.shape == (len(picks), len(freqs), len(times)))
+    tfr2 = cwt_morlet(data[0], Fs, freqs, use_fft=True, n_cycles=2,
+                      decim=slice(0, 2))
+    assert_true(tfr2.shape == (len(picks), len(freqs), 2))
 
     single_power = single_trial_power(data, Fs, freqs, use_fft=False,
                                       n_cycles=2)
-
-    assert_array_almost_equal(np.mean(single_power), power.data)
+    single_power2 = single_trial_power(data, Fs, freqs, use_fft=False,
+                                       n_cycles=2, decim=slice(0, 2))
+    single_power3 = single_trial_power(data, Fs, freqs, use_fft=False,
+                                       n_cycles=2, decim=slice(1, 3))
+    single_power4 = single_trial_power(data, Fs, freqs, use_fft=False,
+                                       n_cycles=2, decim=slice(2, 4))
+
+    assert_array_almost_equal(np.mean(single_power, axis=0), power.data)
+    assert_array_almost_equal(np.mean(single_power2, axis=0),
+                              power.data[:, :, :2])
+    assert_array_almost_equal(np.mean(single_power3, axis=0),
+                              power.data[:, :, 1:3])
+    assert_array_almost_equal(np.mean(single_power4, axis=0),
+                              power.data[:, :, 2:4])
 
     power_pick = power.pick_channels(power.ch_names[:10:2])
     assert_equal(len(power_pick.ch_names), len(power.ch_names[:10:2]))
@@ -150,6 +169,44 @@ def test_time_frequency():
     assert_equal(power_pick.ch_names, power_drop.ch_names)
     assert_equal(power_pick.data.shape, power_drop.data.shape)
 
+    # Test decimation:
+    # 2: multiple of len(times) even
+    # 3: multiple odd
+    # 8: not multiple, even
+    # 9: not multiple, odd
+    for decim in [2, 3, 8, 9]:
+        for use_fft in [True, False]:
+            power, itc = tfr_morlet(epochs, freqs=freqs, n_cycles=2,
+                                    use_fft=use_fft, return_itc=True,
+                                    decim=decim)
+            assert_equal(power.data.shape[2],
+                         np.ceil(float(len(times)) / decim))
+    freqs = range(50, 55)
+    decim = 2
+    _, n_chan, n_time = data.shape
+    tfr = cwt_morlet(data[0, :, :], sfreq=epochs.info['sfreq'],
+                     freqs=freqs, decim=decim)
+    assert_equal(tfr.shape, (n_chan, len(freqs), n_time // decim))
+
+    # Test cwt modes
+    Ws = morlet(512, [10, 20], n_cycles=2)
+    assert_raises(ValueError, cwt, data[0, :, :], Ws, mode='foo')
+    for use_fft in [True, False]:
+        for mode in ['same', 'valid', 'full']:
+            # XXX JRK: full wavelet decomposition needs to be implemented
+            if (not use_fft) and mode == 'full':
+                assert_raises(ValueError, cwt, data[0, :, :], Ws,
+                              use_fft=use_fft, mode=mode)
+                continue
+            cwt(data[0, :, :], Ws, use_fft=use_fft, mode=mode)
+
+    # Test decim parameter checks
+    assert_raises(TypeError, single_trial_power, data, Fs, freqs,
+                  use_fft=False, n_cycles=2, decim=None)
+    assert_raises(TypeError, tfr_morlet, epochs, freqs=freqs,
+                  n_cycles=n_cycles, use_fft=True, return_itc=True,
+                  decim='decim')
+
 
 def test_dpsswavelet():
     """Test DPSS wavelet"""
@@ -198,6 +255,8 @@ def test_tfr_multitaper():
     freqs = np.arange(5, 100, 3, dtype=np.float)
     power, itc = tfr_multitaper(epochs, freqs=freqs, n_cycles=freqs / 2.,
                                 time_bandwidth=4.0)
+    power2, itc2 = tfr_multitaper(epochs, freqs=freqs, n_cycles=freqs / 2.,
+                                  time_bandwidth=4.0, decim=slice(0, 2))
     picks = np.arange(len(ch_names))
     power_picks, itc_picks = tfr_multitaper(epochs, freqs=freqs,
                                             n_cycles=freqs / 2.,
@@ -220,6 +279,15 @@ def test_tfr_multitaper():
     assert_true(tmax > 0.3 and tmax < 0.7)
     assert_false(np.any(itc.data < 0.))
     assert_true(fmax > 40 and fmax < 60)
+    assert_true(power2.data.shape == (len(picks), len(freqs), 2))
+    assert_true(power2.data.shape == itc2.data.shape)
+
+    # Test decim parameter checks and compatibility between wavelets length
+    # and instance length in the time dimension.
+    assert_raises(TypeError, tfr_multitaper, epochs, freqs=freqs,
+                  n_cycles=freqs / 2., time_bandwidth=4.0, decim=(1,))
+    assert_raises(ValueError, tfr_multitaper, epochs, freqs=freqs,
+                  n_cycles=1000, time_bandwidth=4.0)
 
 
 def test_crop():
@@ -272,7 +340,7 @@ def test_io():
     tfr3 = read_tfrs(fname, condition='test-A')
     assert_equal(tfr.comment, tfr3.comment)
 
-    assert_true(isinstance(tfr.info, io.meas_info.Info))
+    assert_true(isinstance(tfr.info, mne.Info))
 
     tfrs = read_tfrs(fname, condition=None)
     assert_equal(len(tfrs), 2)
@@ -319,14 +387,14 @@ def test_add_channels():
         1000., ['mag', 'mag', 'mag', 'eeg', 'eeg', 'stim'])
     tfr = AverageTFR(info, data=data, times=times, freqs=freqs,
                      nave=20, comment='test', method='crazy-tfr')
-    tfr_eeg = tfr.pick_types(meg=False, eeg=True, copy=True)
-    tfr_meg = tfr.pick_types(meg=True, copy=True)
-    tfr_stim = tfr.pick_types(meg=False, stim=True, copy=True)
-    tfr_eeg_meg = tfr.pick_types(meg=True, eeg=True, copy=True)
-    tfr_new = tfr_meg.add_channels([tfr_eeg, tfr_stim], copy=True)
+    tfr_eeg = tfr.copy().pick_types(meg=False, eeg=True)
+    tfr_meg = tfr.copy().pick_types(meg=True)
+    tfr_stim = tfr.copy().pick_types(meg=False, stim=True)
+    tfr_eeg_meg = tfr.copy().pick_types(meg=True, eeg=True)
+    tfr_new = tfr_meg.copy().add_channels([tfr_eeg, tfr_stim])
     assert_true(all(ch in tfr_new.ch_names
                     for ch in tfr_stim.ch_names + tfr_meg.ch_names))
-    tfr_new = tfr_meg.add_channels([tfr_eeg], copy=True)
+    tfr_new = tfr_meg.copy().add_channels([tfr_eeg])
 
     assert_true(ch in tfr_new.ch_names for ch in tfr.ch_names)
     assert_array_equal(tfr_new.data, tfr_eeg_meg.data)
diff --git a/mne/time_frequency/tfr.py b/mne/time_frequency/tfr.py
index 3040f7c..d40d136 100644
--- a/mne/time_frequency/tfr.py
+++ b/mne/time_frequency/tfr.py
@@ -4,12 +4,13 @@ Morlet code inspired by Matlab code from Sheraz Khan & Brainstorm & SPM
 """
 # Authors : Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
 #           Hari Bharadwaj <hari at nmr.mgh.harvard.edu>
+#           Clement Moutard <clement.moutard at polytechnique.org>
 #
 # License : BSD (3-clause)
 
-import warnings
-from math import sqrt
 from copy import deepcopy
+from math import sqrt
+
 import numpy as np
 from scipy import linalg
 from scipy.fftpack import fftn, ifftn
@@ -17,11 +18,11 @@ from scipy.fftpack import fftn, ifftn
 from ..fixes import partial
 from ..baseline import rescale
 from ..parallel import parallel_func
-from ..utils import logger, verbose, _time_mask
+from ..utils import (logger, verbose, _time_mask, warn, check_fname,
+                     _check_copy_dep)
 from ..channels.channels import ContainsMixin, UpdateChannelsMixin
 from ..io.pick import pick_info, pick_types
 from ..io.meas_info import Info
-from ..utils import check_fname
 from .multitaper import dpss_windows
 from ..viz.utils import figure_nobar, plt_show
 from ..externals.h5io import write_hdf5, read_hdf5
@@ -181,14 +182,23 @@ def _centered(arr, newsize):
     return arr[tuple(myslice)]
 
 
-def _cwt_fft(X, Ws, mode="same"):
-    """Compute cwt with fft based convolutions
+def _cwt(X, Ws, mode="same", decim=1, use_fft=True):
+    """Compute cwt with fft based convolutions or temporal convolutions.
     Return a generator over signals.
     """
+    if mode not in ['same', 'valid', 'full']:
+        raise ValueError("`mode` must be 'same', 'valid' or 'full', "
+                         "got %s instead." % mode)
+    if mode == 'full' and (not use_fft):
+        # XXX JRK: full wavelet decomposition needs to be implemented
+        raise ValueError('`full` decomposition with convolution is currently' +
+                         ' not supported.')
+    decim = _check_decim(decim)
     X = np.asarray(X)
 
     # Precompute wavelets for given frequency range to save time
     n_signals, n_times = X.shape
+    n_times_out = X[:, decim].shape[1]
     n_freqs = len(Ws)
 
     Ws_max_size = max(W.size for W in Ws)
@@ -197,58 +207,46 @@ def _cwt_fft(X, Ws, mode="same"):
     fsize = 2 ** int(np.ceil(np.log2(size)))
 
     # precompute FFTs of Ws
-    fft_Ws = np.empty((n_freqs, fsize), dtype=np.complex128)
+    if use_fft:
+        fft_Ws = np.empty((n_freqs, fsize), dtype=np.complex128)
     for i, W in enumerate(Ws):
         if len(W) > n_times:
             raise ValueError('Wavelet is too long for such a short signal. '
                              'Reduce the number of cycles.')
-        fft_Ws[i] = fftn(W, [fsize])
+        if use_fft:
+            fft_Ws[i] = fftn(W, [fsize])
 
-    for k, x in enumerate(X):
-        if mode == "full":
-            tfr = np.zeros((n_freqs, fsize), dtype=np.complex128)
-        elif mode == "same" or mode == "valid":
-            tfr = np.zeros((n_freqs, n_times), dtype=np.complex128)
+    # Make generator looping across signals
+    tfr = np.zeros((n_freqs, n_times_out), dtype=np.complex128)
+    for x in X:
+        if use_fft:
+            fft_x = fftn(x, [fsize])
 
-        fft_x = fftn(x, [fsize])
-        for i, W in enumerate(Ws):
-            ret = ifftn(fft_x * fft_Ws[i])[:n_times + W.size - 1]
-            if mode == "valid":
-                sz = abs(W.size - n_times) + 1
-                offset = (n_times - sz) / 2
-                tfr[i, offset:(offset + sz)] = _centered(ret, sz)
+        # Loop across wavelets
+        for ii, W in enumerate(Ws):
+            if use_fft:
+                ret = ifftn(fft_x * fft_Ws[ii])[:n_times + W.size - 1]
             else:
-                tfr[i, :] = _centered(ret, n_times)
-        yield tfr
-
-
-def _cwt_convolve(X, Ws, mode='same'):
-    """Compute time freq decomposition with temporal convolutions
-    Return a generator over signals.
-    """
-    X = np.asarray(X)
-
-    n_signals, n_times = X.shape
-    n_freqs = len(Ws)
+                ret = np.convolve(x, W, mode=mode)
 
-    # Compute convolutions
-    for x in X:
-        tfr = np.zeros((n_freqs, n_times), dtype=np.complex128)
-        for i, W in enumerate(Ws):
-            ret = np.convolve(x, W, mode=mode)
-            if len(W) > len(x):
-                raise ValueError('Wavelet is too long for such a short '
-                                 'signal. Reduce the number of cycles.')
+            # Center and decimate decomposition
             if mode == "valid":
                 sz = abs(W.size - n_times) + 1
                 offset = (n_times - sz) / 2
-                tfr[i, offset:(offset + sz)] = ret
+                this_slice = slice(offset // decim.step,
+                                   (offset + sz) // decim.step)
+                if use_fft:
+                    ret = _centered(ret, sz)
+                tfr[ii, this_slice] = ret[decim]
             else:
-                tfr[i] = ret
+                if use_fft:
+                    ret = _centered(ret, n_times)
+                tfr[ii, :] = ret[decim]
         yield tfr
 
 
-def cwt_morlet(X, sfreq, freqs, use_fft=True, n_cycles=7.0, zero_mean=False):
+def cwt_morlet(X, sfreq, freqs, use_fft=True, n_cycles=7.0, zero_mean=False,
+               decim=1):
     """Compute time freq decomposition with Morlet wavelets
 
     This function operates directly on numpy arrays. Consider using
@@ -256,10 +254,10 @@ def cwt_morlet(X, sfreq, freqs, use_fft=True, n_cycles=7.0, zero_mean=False):
 
     Parameters
     ----------
-    X : array of shape [n_signals, n_times]
-        signals (one per line)
+    X : array, shape (n_signals, n_times)
+        Signals (one per line)
     sfreq : float
-        sampling Frequency
+        Sampling frequency.
     freqs : array
         Array of frequencies of interest
     use_fft : bool
@@ -268,6 +266,13 @@ def cwt_morlet(X, sfreq, freqs, use_fft=True, n_cycles=7.0, zero_mean=False):
         Number of cycles. Fixed number or one per frequency.
     zero_mean : bool
         Make sure the wavelets are zero mean.
+    decim : int | slice
+        To reduce memory usage, decimation factor after time-frequency
+        decomposition.
+        If `int`, returns tfr[..., ::decim].
+        If `slice` returns tfr[..., decim].
+        Note that decimation may create aliasing artifacts.
+        Defaults to 1.
 
     Returns
     -------
@@ -280,18 +285,15 @@ def cwt_morlet(X, sfreq, freqs, use_fft=True, n_cycles=7.0, zero_mean=False):
     """
     mode = 'same'
     # mode = "valid"
-    n_signals, n_times = X.shape
-    n_frequencies = len(freqs)
+    decim = _check_decim(decim)
+    n_signals, n_times = X[:, decim].shape
 
     # Precompute wavelets for given frequency range to save time
     Ws = morlet(sfreq, freqs, n_cycles=n_cycles, zero_mean=zero_mean)
 
-    if use_fft:
-        coefs = _cwt_fft(X, Ws, mode)
-    else:
-        coefs = _cwt_convolve(X, Ws, mode)
+    coefs = cwt(X, Ws, use_fft=use_fft, mode=mode, decim=decim)
 
-    tfrs = np.empty((n_signals, n_frequencies, n_times), dtype=np.complex)
+    tfrs = np.empty((n_signals, len(freqs), n_times), dtype=np.complex)
     for k, tfr in enumerate(coefs):
         tfrs[k] = tfr
 
@@ -303,38 +305,41 @@ def cwt(X, Ws, use_fft=True, mode='same', decim=1):
 
     Parameters
     ----------
-    X : array of shape [n_signals, n_times]
-        signals (one per line)
+    X : array, shape (n_signals, n_times)
+        The signals.
     Ws : list of array
-        Wavelets time series
+        Wavelets time series.
     use_fft : bool
-        Use FFT for convolutions
+        Use FFT for convolutions. Defaults to True.
     mode : 'same' | 'valid' | 'full'
-        Convention for convolution
-    decim : int
-        Temporal decimation factor
+        Convention for convolution. 'full' is currently not implemented with
+        `use_fft=False`. Defaults to 'same'.
+    decim : int | slice
+        To reduce memory usage, decimation factor after time-frequency
+        decomposition.
+        If `int`, returns tfr[..., ::decim].
+        If `slice` returns tfr[..., decim].
+        Note that decimation may create aliasing artifacts.
+        Defaults to 1.
 
     Returns
     -------
-    tfr : 3D array
-        Time Frequency Decompositions (n_signals x n_frequencies x n_times)
+    tfr : array, shape (n_signals, n_frequencies, n_times)
+        The time frequency decompositions.
 
     See Also
     --------
     mne.time_frequency.cwt_morlet : Compute time-frequency decomposition
                                     with Morlet wavelets
     """
-    n_signals, n_times = X[:, ::decim].shape
-    n_frequencies = len(Ws)
+    decim = _check_decim(decim)
+    n_signals, n_times = X[:, decim].shape
 
-    if use_fft:
-        coefs = _cwt_fft(X, Ws, mode)
-    else:
-        coefs = _cwt_convolve(X, Ws, mode)
+    coefs = _cwt(X, Ws, mode, decim=decim, use_fft=use_fft)
 
-    tfrs = np.empty((n_signals, n_frequencies, n_times), dtype=np.complex)
+    tfrs = np.empty((n_signals, len(Ws), n_times), dtype=np.complex)
     for k, tfr in enumerate(coefs):
-        tfrs[k] = tfr[..., ::decim]
+        tfrs[k] = tfr
 
     return tfrs
 
@@ -342,20 +347,16 @@ def cwt(X, Ws, use_fft=True, mode='same', decim=1):
 def _time_frequency(X, Ws, use_fft, decim):
     """Aux of time_frequency for parallel computing over channels
     """
-    n_epochs, n_times = X.shape
-    n_times = n_times // decim + bool(n_times % decim)
+    decim = _check_decim(decim)
+    n_epochs, n_times = X[:, decim].shape
     n_frequencies = len(Ws)
     psd = np.zeros((n_frequencies, n_times))  # PSD
     plf = np.zeros((n_frequencies, n_times), np.complex)  # phase lock
 
     mode = 'same'
-    if use_fft:
-        tfrs = _cwt_fft(X, Ws, mode)
-    else:
-        tfrs = _cwt_convolve(X, Ws, mode)
+    tfrs = _cwt(X, Ws, mode, decim=decim, use_fft=use_fft)
 
     for tfr in tfrs:
-        tfr = tfr[:, ::decim]
         tfr_abs = np.abs(tfr)
         psd += tfr_abs ** 2
         plf += tfr / tfr_abs
@@ -398,8 +399,13 @@ def single_trial_power(data, sfreq, frequencies, use_fft=True, n_cycles=7,
         power = [power - mean(power_baseline)] / std(power_baseline))
     times : array
         Required to define baseline
-    decim : int
-        Temporal decimation factor
+    decim : int | slice
+        To reduce memory usage, decimation factor after time-frequency
+        decomposition.
+        If `int`, returns tfr[..., ::decim].
+        If `slice` returns tfr[..., decim].
+        Note that decimation may create aliasing artifacts.
+        Defaults to 1.
     n_jobs : int
         The number of epochs to process at the same time
     zero_mean : bool
@@ -412,9 +418,10 @@ def single_trial_power(data, sfreq, frequencies, use_fft=True, n_cycles=7,
     power : 4D array
         Power estimate (Epochs x Channels x Frequencies x Timepoints).
     """
+    decim = _check_decim(decim)
     mode = 'same'
     n_frequencies = len(frequencies)
-    n_epochs, n_channels, n_times = data[:, :, ::decim].shape
+    n_epochs, n_channels, n_times = data[:, :, decim].shape
 
     # Precompute wavelets for given frequency range to save time
     Ws = morlet(sfreq, frequencies, n_cycles=n_cycles, zero_mean=zero_mean)
@@ -442,7 +449,7 @@ def single_trial_power(data, sfreq, frequencies, use_fft=True, n_cycles=7,
     # Run baseline correction.  Be sure to decimate the times array as well if
     # needed.
     if times is not None:
-        times = times[::decim]
+        times = times[decim]
     power = rescale(power, times, baseline, baseline_mode, copy=False)
     return power
 
@@ -458,7 +465,7 @@ def _induced_power_cwt(data, sfreq, frequencies, use_fft=True, n_cycles=7,
     data : array
         3D array of shape [n_epochs, n_channels, n_times]
     sfreq : float
-        sampling Frequency
+        Sampling frequency.
     frequencies : array
         Array of frequencies of interest
     use_fft : bool
@@ -466,8 +473,13 @@ def _induced_power_cwt(data, sfreq, frequencies, use_fft=True, n_cycles=7,
         convolutions.
     n_cycles : float | array of float
         Number of cycles. Fixed number or one per frequency.
-    decim: int
-        Temporal decimation factor
+    decim : int | slice
+        To reduce memory usage, decimation factor after time-frequency
+        decomposition.
+        If `int`, returns tfr[..., ::decim].
+        If `slice` returns tfr[..., decim].
+        Note that decimation may create aliasing artifacts.
+        Defaults to 1.
     n_jobs : int
         The number of CPUs used in parallel. All CPUs are used in -1.
         Requires joblib package.
@@ -482,8 +494,9 @@ def _induced_power_cwt(data, sfreq, frequencies, use_fft=True, n_cycles=7,
     phase_lock : 2D array
         Phase locking factor in [0, 1] (Channels x Frequencies x Timepoints)
     """
+    decim = _check_decim(decim)
     n_frequencies = len(frequencies)
-    n_epochs, n_channels, n_times = data[:, :, ::decim].shape
+    n_epochs, n_channels, n_times = data[:, :, decim].shape
 
     # Precompute wavelets for given frequency range to save time
     Ws = morlet(sfreq, frequencies, n_cycles=n_cycles, zero_mean=zero_mean)
@@ -500,18 +513,16 @@ def _induced_power_cwt(data, sfreq, frequencies, use_fft=True, n_cycles=7,
 
 
 def _preproc_tfr(data, times, freqs, tmin, tmax, fmin, fmax, mode,
-                 baseline, vmin, vmax, dB):
+                 baseline, vmin, vmax, dB, sfreq):
     """Aux Function to prepare tfr computation"""
     from ..viz.utils import _setup_vmin_vmax
 
-    if mode is not None and baseline is not None:
-        logger.info("Applying baseline correction '%s' during %s" %
-                    (mode, baseline))
-        data = rescale(data.copy(), times, baseline, mode)
+    copy = baseline is not None
+    data = rescale(data, times, baseline, mode, copy=copy)
 
     # crop time
     itmin, itmax = None, None
-    idx = np.where(_time_mask(times, tmin, tmax))[0]
+    idx = np.where(_time_mask(times, tmin, tmax, sfreq=sfreq))[0]
     if tmin is not None:
         itmin = idx[0]
     if tmax is not None:
@@ -521,7 +532,7 @@ def _preproc_tfr(data, times, freqs, tmin, tmax, fmin, fmax, mode,
 
     # crop freqs
     ifmin, ifmax = None, None
-    idx = np.where(_time_mask(freqs, fmin, fmax))[0]
+    idx = np.where(_time_mask(freqs, fmin, fmax, sfreq=sfreq))[0]
     if fmin is not None:
         ifmin = idx[0]
     if fmax is not None:
@@ -589,8 +600,8 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
             raise ValueError("Number of times and data size don't match"
                              " (%d != %d)." % (n_times, len(times)))
         self.data = data
-        self.times = times
-        self.freqs = freqs
+        self.times = np.asarray(times)
+        self.freqs = np.asarray(freqs)
         self.nave = nave
         self.comment = comment
         self.method = method
@@ -599,7 +610,7 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
     def ch_names(self):
         return self.info['ch_names']
 
-    def crop(self, tmin=None, tmax=None, copy=False):
+    def crop(self, tmin=None, tmax=None, copy=None):
         """Crop data to a given time interval
 
         Parameters
@@ -609,12 +620,19 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
         tmax : float | None
             End time of selection in seconds.
         copy : bool
-            If False epochs is cropped in place.
+            This parameter has been deprecated and will be removed in 0.13.
+            Use inst.copy() instead.
+            Whether to return a new instance or modify in place.
+
+        Returns
+        -------
+        inst : instance of AverageTFR
+            The modified instance.
         """
-        inst = self if not copy else self.copy()
-        mask = _time_mask(inst.times, tmin, tmax)
+        inst = _check_copy_dep(self, copy)
+        mask = _time_mask(inst.times, tmin, tmax, sfreq=self.info['sfreq'])
         inst.times = inst.times[mask]
-        inst.data = inst.data[..., mask]
+        inst.data = inst.data[:, :, mask]
         return inst
 
     @verbose
@@ -699,7 +717,7 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
 
         data, times, freqs, vmin, vmax = \
             _preproc_tfr(data, times, freqs, tmin, tmax, fmin, fmax, mode,
-                         baseline, vmin, vmax, dB)
+                         baseline, vmin, vmax, dB, info['sfreq'])
 
         tmin, tmax = times[0], times[-1]
         if isinstance(axes, plt.Axes):
@@ -841,7 +859,7 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
         fig : matplotlib.figure.Figure
             The figure containing the topography.
         """
-        from ..viz.topo import _imshow_tfr, _plot_topo
+        from ..viz.topo import _imshow_tfr, _plot_topo, _imshow_tfr_unified
         times = self.times.copy()
         freqs = self.freqs
         data = self.data
@@ -852,22 +870,26 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
 
         data, times, freqs, vmin, vmax = \
             _preproc_tfr(data, times, freqs, tmin, tmax, fmin, fmax,
-                         mode, baseline, vmin, vmax, dB)
+                         mode, baseline, vmin, vmax, dB, info['sfreq'])
 
         if layout is None:
             from mne import find_layout
             layout = find_layout(self.info)
         onselect_callback = partial(self._onselect, baseline=baseline,
                                     mode=mode, layout=layout)
-        imshow = partial(_imshow_tfr, tfr=data, freq=freqs, cmap=cmap,
+
+        click_fun = partial(_imshow_tfr, tfr=data, freq=freqs, cmap=cmap,
+                            onselect=onselect_callback)
+        imshow = partial(_imshow_tfr_unified, tfr=data, freq=freqs, cmap=cmap,
                          onselect=onselect_callback)
 
         fig = _plot_topo(info=info, times=times, show_func=imshow,
-                         layout=layout, colorbar=colorbar, vmin=vmin,
-                         vmax=vmax, cmap=cmap, layout_scale=layout_scale,
-                         title=title, border=border, x_label='Time (ms)',
-                         y_label='Frequency (Hz)', fig_facecolor=fig_facecolor,
-                         font_color=font_color)
+                         click_func=click_fun, layout=layout,
+                         colorbar=colorbar, vmin=vmin, vmax=vmax, cmap=cmap,
+                         layout_scale=layout_scale, title=title, border=border,
+                         x_label='Time (ms)', y_label='Frequency (Hz)',
+                         fig_facecolor=fig_facecolor, font_color=font_color,
+                         unified=True, img=True)
         plt_show(show)
         return fig
 
@@ -909,7 +931,8 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
         s += ', channels : %d' % self.data.shape[0]
         return "<AverageTFR  |  %s>" % s
 
-    def apply_baseline(self, baseline, mode='mean'):
+    @verbose
+    def apply_baseline(self, baseline, mode='mean', verbose=None):
         """Baseline correct the data
 
         Parameters
@@ -928,8 +951,11 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
             deviation of power during baseline after subtracting the mean,
             power = [power - mean(power_baseline)] / std(power_baseline))
             If None, baseline no correction will be performed.
+        verbose : bool, str, int, or None
+            If not None, override default verbose level (see mne.verbose).
         """
-        self.data = rescale(self.data, self.times, baseline, mode, copy=False)
+        self.data = rescale(self.data, self.times, baseline, mode,
+                            copy=False)
 
     def plot_topomap(self, tmin=None, tmax=None, fmin=None, fmax=None,
                      ch_type=None, baseline=None, mode='mean',
@@ -956,7 +982,8 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
         ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
             The channel type to plot. For 'grad', the gradiometers are
             collected in pairs and the RMS for each pair is plotted.
-            If None, then channels are chosen in the order given above.
+            If None, then first available channel type from order given
+            above is used. Defaults to None.
         baseline : tuple or list of length 2
             The time interval to apply rescaling / baseline correction.
             If None do not apply it. If baseline is (a, b)
@@ -1065,8 +1092,9 @@ class AverageTFR(ContainsMixin, UpdateChannelsMixin):
 def _prepare_write_tfr(tfr, condition):
     """Aux function"""
     return (condition, dict(times=tfr.times, freqs=tfr.freqs,
-                            data=tfr.data, info=tfr.info, nave=tfr.nave,
-                            comment=tfr.comment, method=tfr.method))
+                            data=tfr.data, info=tfr.info,
+                            nave=tfr.nave, comment=tfr.comment,
+                            method=tfr.method))
 
 
 def write_tfrs(fname, tfr, overwrite=False):
@@ -1148,8 +1176,8 @@ def read_tfrs(fname, condition=None):
 
 
 @verbose
-def tfr_morlet(inst, freqs, n_cycles, use_fft=False,
-               return_itc=True, decim=1, n_jobs=1, picks=None, verbose=None):
+def tfr_morlet(inst, freqs, n_cycles, use_fft=False, return_itc=True, decim=1,
+               n_jobs=1, picks=None, verbose=None):
     """Compute Time-Frequency Representation (TFR) using Morlet wavelets
 
     Parameters
@@ -1165,8 +1193,13 @@ def tfr_morlet(inst, freqs, n_cycles, use_fft=False,
     return_itc : bool
         Return intertrial coherence (ITC) as well as averaged power.
         Must be ``False`` for evoked data.
-    decim : int
-        The decimation factor on the time axis. To reduce memory usage.
+    decim : int | slice
+        To reduce memory usage, decimation factor after time-frequency
+        decomposition.
+        If `int`, returns tfr[..., ::decim].
+        If `slice` returns tfr[..., decim].
+        Note that decimation may create aliasing artifacts.
+        Defaults to 1.
     n_jobs : int
         The number of jobs to run in parallel.
     picks : array-like of int | None
@@ -1187,18 +1220,19 @@ def tfr_morlet(inst, freqs, n_cycles, use_fft=False,
     --------
     tfr_multitaper, tfr_stockwell
     """
+    decim = _check_decim(decim)
     data = _get_data(inst, return_itc)
     info = inst.info
 
     info, data, picks = _prepare_picks(info, data, picks)
-    data = data = data[:, picks, :]
+    data = data[:, picks, :]
 
     power, itc = _induced_power_cwt(data, sfreq=info['sfreq'],
                                     frequencies=freqs,
                                     n_cycles=n_cycles, n_jobs=n_jobs,
                                     use_fft=use_fft, decim=decim,
                                     zero_mean=True)
-    times = inst.times[::decim].copy()
+    times = inst.times[decim].copy()
     nave = len(data)
     out = AverageTFR(info, power, times, freqs, nave, method='morlet-power')
     if return_itc:
@@ -1232,7 +1266,7 @@ def _induced_power_mtm(data, sfreq, frequencies, time_bandwidth=4.0,
     data : np.ndarray, shape (n_epochs, n_channels, n_times)
         The input data.
     sfreq : float
-        sampling Frequency
+        Sampling frequency.
     frequencies : np.ndarray, shape (n_frequencies,)
         Array of frequencies of interest
     time_bandwidth : float
@@ -1244,8 +1278,13 @@ def _induced_power_mtm(data, sfreq, frequencies, time_bandwidth=4.0,
         convolutions. Defaults to True.
     n_cycles : float | np.ndarray shape (n_frequencies,)
         Number of cycles. Fixed number or one per frequency. Defaults to 7.
-    decim: int
-        Temporal decimation factor. Defaults to 1.
+    decim : int | slice
+        To reduce memory usage, decimation factor after time-frequency
+        decomposition.
+        If `int`, returns tfr[..., ::decim].
+        If `slice` returns tfr[..., decim].
+        Note that decimation may create aliasing artifacts.
+        Defaults to 1.
     n_jobs : int
         The number of CPUs used in parallel. All CPUs are used in -1.
         Requires joblib package. Defaults to 1.
@@ -1261,7 +1300,8 @@ def _induced_power_mtm(data, sfreq, frequencies, time_bandwidth=4.0,
     itc : np.ndarray, shape (n_channels, n_frequencies, n_times)
         Phase locking value.
     """
-    n_epochs, n_channels, n_times = data[:, :, ::decim].shape
+    decim = _check_decim(decim)
+    n_epochs, n_channels, n_times = data[:, :, decim].shape
     logger.info('Data is %d trials and %d channels', n_epochs, n_channels)
     n_frequencies = len(frequencies)
     logger.info('Multitaper time-frequency analysis for %d frequencies',
@@ -1273,16 +1313,16 @@ def _induced_power_mtm(data, sfreq, frequencies, time_bandwidth=4.0,
     n_taps = len(Ws)
     logger.info('Using %d tapers', n_taps)
     n_times_wavelets = Ws[0][0].shape[0]
-    if n_times <= n_times_wavelets:
-        warnings.warn("Time windows are as long or longer than the epoch. "
-                      "Consider reducing n_cycles.")
+    if data.shape[2] <= n_times_wavelets:
+        warn('Time windows are as long or longer than the epoch. Consider '
+             'reducing n_cycles.')
     psd = np.zeros((n_channels, n_frequencies, n_times))
     itc = np.zeros((n_channels, n_frequencies, n_times))
     parallel, my_time_frequency, _ = parallel_func(_time_frequency,
                                                    n_jobs)
     for m in range(n_taps):
-        psd_itc = parallel(my_time_frequency(data[:, c, :],
-                                             Ws[m], use_fft, decim)
+        psd_itc = parallel(my_time_frequency(data[:, c, :], Ws[m], use_fft,
+                                             decim)
                            for c in range(n_channels))
         for c, (psd_c, itc_c) in enumerate(psd_itc):
             psd[c, :, :] += psd_c
@@ -1294,8 +1334,8 @@ def _induced_power_mtm(data, sfreq, frequencies, time_bandwidth=4.0,
 
 @verbose
 def tfr_multitaper(inst, freqs, n_cycles, time_bandwidth=4.0,
-                   use_fft=True, return_itc=True, decim=1, n_jobs=1,
-                   picks=None, verbose=None):
+                   use_fft=True, return_itc=True, decim=1,
+                   n_jobs=1, picks=None, verbose=None):
     """Compute Time-Frequency Representation (TFR) using DPSS wavelets
 
     Parameters
@@ -1321,9 +1361,12 @@ def tfr_multitaper(inst, freqs, n_cycles, time_bandwidth=4.0,
     return_itc : bool
         Return intertrial coherence (ITC) as well as averaged power.
         Defaults to True.
-    decim : int
-        The decimation factor on the time axis. To reduce memory usage.
-        Note than this is brute force decimation, no anti-aliasing is done.
+    decim : int | slice
+        To reduce memory usage, decimation factor after time-frequency
+        decomposition.
+        If `int`, returns tfr[..., ::decim].
+        If `slice` returns tfr[..., decim].
+        Note that decimation may create aliasing artifacts.
         Defaults to 1.
     n_jobs : int
         The number of jobs to run in parallel. Defaults to 1.
@@ -1349,7 +1392,7 @@ def tfr_multitaper(inst, freqs, n_cycles, time_bandwidth=4.0,
     -----
     .. versionadded:: 0.9.0
     """
-
+    decim = _check_decim(decim)
     data = _get_data(inst, return_itc)
     info = inst.info
 
@@ -1362,7 +1405,7 @@ def tfr_multitaper(inst, freqs, n_cycles, time_bandwidth=4.0,
                                     use_fft=use_fft, decim=decim,
                                     n_jobs=n_jobs, zero_mean=True,
                                     verbose='INFO')
-    times = inst.times[::decim].copy()
+    times = inst.times[decim].copy()
     nave = len(data)
     out = AverageTFR(info, power, times, freqs, nave,
                      method='mutlitaper-power')
@@ -1430,3 +1473,13 @@ def combine_tfr(all_tfr, weights='nave'):
     tfr.nave = max(int(1. / sum(w ** 2 / e.nave
                                 for w, e in zip(weights, all_tfr))), 1)
     return tfr
+
+
+def _check_decim(decim):
+    """ aux function checking the decim parameter """
+    if isinstance(decim, int):
+        decim = slice(None, None, decim)
+    elif not isinstance(decim, slice):
+        raise(TypeError, '`decim` must be int or slice, got %s instead'
+                         % type(decim))
+    return decim
diff --git a/mne/transforms.py b/mne/transforms.py
index d48d54f..9437cf5 100644
--- a/mne/transforms.py
+++ b/mne/transforms.py
@@ -570,3 +570,130 @@ def _topo_to_sphere(theta, radius):
     sph_phi = (0.5 - radius) * 180
     sph_theta = -theta
     return sph_phi, sph_theta
+
+
+def quat_to_rot(quat):
+    """Convert a set of quaternions to rotations
+
+    Parameters
+    ----------
+    quat : array, shape (..., 3)
+        q1, q2, and q3 (x, y, z) parameters of a unit quaternion.
+
+    Returns
+    -------
+    rot : array, shape (..., 3, 3)
+        The corresponding rotation matrices.
+
+    See Also
+    --------
+    rot_to_quat
+    """
+    # z = a + bi + cj + dk
+    b, c, d = quat[..., 0], quat[..., 1], quat[..., 2]
+    bb, cc, dd = b * b, c * c, d * d
+    # use max() here to be safe in case roundoff errs put us over
+    aa = np.maximum(1. - bb - cc - dd, 0.)
+    a = np.sqrt(aa)
+    ab_2 = 2 * a * b
+    ac_2 = 2 * a * c
+    ad_2 = 2 * a * d
+    bc_2 = 2 * b * c
+    bd_2 = 2 * b * d
+    cd_2 = 2 * c * d
+    rotation = np.array([(aa + bb - cc - dd, bc_2 - ad_2, bd_2 + ac_2),
+                         (bc_2 + ad_2, aa + cc - bb - dd, cd_2 - ab_2),
+                         (bd_2 - ac_2, cd_2 + ab_2, aa + dd - bb - cc),
+                         ])
+    if quat.ndim > 1:
+        rotation = np.rollaxis(np.rollaxis(rotation, 1, quat.ndim + 1),
+                               0, quat.ndim)
+    return rotation
+
+
+def _one_rot_to_quat(rot):
+    """Convert a rotation matrix to quaternions"""
+    # see e.g. http://www.euclideanspace.com/maths/geometry/rotations/
+    #                 conversions/matrixToQuaternion/
+    t = 1. + rot[0] + rot[4] + rot[8]
+    if t > np.finfo(rot.dtype).eps:
+        s = np.sqrt(t) * 2.
+        qx = (rot[7] - rot[5]) / s
+        qy = (rot[2] - rot[6]) / s
+        qz = (rot[3] - rot[1]) / s
+        # qw = 0.25 * s
+    elif rot[0] > rot[4] and rot[0] > rot[8]:
+        s = np.sqrt(1. + rot[0] - rot[4] - rot[8]) * 2.
+        qx = 0.25 * s
+        qy = (rot[1] + rot[3]) / s
+        qz = (rot[2] + rot[6]) / s
+        # qw = (rot[7] - rot[5]) / s
+    elif rot[4] > rot[8]:
+        s = np.sqrt(1. - rot[0] + rot[4] - rot[8]) * 2
+        qx = (rot[1] + rot[3]) / s
+        qy = 0.25 * s
+        qz = (rot[5] + rot[7]) / s
+        # qw = (rot[2] - rot[6]) / s
+    else:
+        s = np.sqrt(1. - rot[0] - rot[4] + rot[8]) * 2.
+        qx = (rot[2] + rot[6]) / s
+        qy = (rot[5] + rot[7]) / s
+        qz = 0.25 * s
+        # qw = (rot[3] - rot[1]) / s
+    return qx, qy, qz
+
+
+def rot_to_quat(rot):
+    """Convert a set of rotations to quaternions
+
+    Parameters
+    ----------
+    rot : array, shape (..., 3, 3)
+        The rotation matrices to convert.
+
+    Returns
+    -------
+    quat : array, shape (..., 3)
+        The q1, q2, and q3 (x, y, z) parameters of the corresponding
+        unit quaternions.
+
+    See Also
+    --------
+    quat_to_rot
+    """
+    rot = rot.reshape(rot.shape[:-2] + (9,))
+    return np.apply_along_axis(_one_rot_to_quat, -1, rot)
+
+
+def _angle_between_quats(x, y):
+    """Compute the angle between two quaternions w/3-element representations"""
+    # convert to complete quaternion representation
+    # use max() here to be safe in case roundoff errs put us over
+    x0 = np.sqrt(np.maximum(1. - x[..., 0] ** 2 -
+                            x[..., 1] ** 2 - x[..., 2] ** 2, 0.))
+    y0 = np.sqrt(np.maximum(1. - y[..., 0] ** 2 -
+                            y[..., 1] ** 2 - y[..., 2] ** 2, 0.))
+    # the difference z = x * conj(y), and theta = np.arccos(z0)
+    z0 = np.maximum(np.minimum(y0 * x0 + (x * y).sum(axis=-1), 1.), -1)
+    return 2 * np.arccos(z0)
+
+
+def _skew_symmetric_cross(a):
+    """The skew-symmetric cross product of a vector"""
+    return np.array([[0., -a[2], a[1]], [a[2], 0., -a[0]], [-a[1], a[0], 0.]])
+
+
+def _find_vector_rotation(a, b):
+    """Find the rotation matrix that maps unit vector a to b"""
+    # Rodrigues' rotation formula:
+    #   https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
+    #   http://math.stackexchange.com/a/476311
+    R = np.eye(3)
+    v = np.cross(a, b)
+    if np.allclose(v, 0.):  # identical
+        return R
+    s = np.dot(v, v)  # sine of the angle between them
+    c = np.dot(a, b)  # cosine of the angle between them
+    vx = _skew_symmetric_cross(v)
+    R += vx + np.dot(vx, vx) * (1 - c) / s
+    return R
diff --git a/mne/utils.py b/mne/utils.py
index 6ac4101..380cbc8 100644
--- a/mne/utils.py
+++ b/mne/utils.py
@@ -6,26 +6,26 @@ from __future__ import print_function
 #
 # License: BSD (3-clause)
 
-import warnings
-import logging
-import time
+import atexit
 from distutils.version import LooseVersion
-import os
-import os.path as op
 from functools import wraps
+import hashlib
 import inspect
+import json
+import logging
+from math import log, ceil
+import os
+import os.path as op
+import platform
+import shutil
+from shutil import rmtree
 from string import Formatter
 import subprocess
 import sys
 import tempfile
-import shutil
-from shutil import rmtree
-from math import log, ceil
-import json
+import time
+import warnings
 import ftplib
-import hashlib
-from functools import partial
-import atexit
 
 import numpy as np
 from scipy import linalg, sparse
@@ -34,7 +34,7 @@ from .externals.six.moves import urllib
 from .externals.six import string_types, StringIO, BytesIO
 from .externals.decorator import decorator
 
-from .fixes import isclose, _get_args
+from .fixes import _get_args, partial
 
 logger = logging.getLogger('mne')  # one selection here used across mne-python
 logger.propagate = False  # don't propagate (in case of multiple imports)
@@ -62,6 +62,24 @@ def nottest(f):
 ###############################################################################
 # RANDOM UTILITIES
 
+
+def _check_copy_dep(inst, copy, kind='inst', default=False):
+    """Check for copy deprecation for 0.13 and 0.14"""
+    # For methods with copy=False default, we only need one release cycle
+    # for deprecation (0.13). For copy=True, we first need to go to copy=False
+    # (one cycle; 0.13) then remove copy altogether (one cycle; 0.14).
+    if copy or (copy is None and default is True):
+        remove_version = '0.14' if default is True else '0.13'
+        warn('The copy parameter is deprecated and will be removed in %s. '
+             'In 0.13 the behavior will be to operate in-place '
+             '(like copy=False). In 0.12 the default is copy=%s. '
+             'Use %s.copy() if necessary.'
+             % (remove_version, default, kind), DeprecationWarning)
+    if copy is None:
+        copy = default
+    return inst.copy() if copy else inst
+
+
 def _get_call_line(in_verbose=False):
     """Helper to get the call line from within a function"""
     # XXX Eventually we could auto-triage whether in a `verbose` decorated
@@ -101,15 +119,12 @@ def object_hash(x, h=None):
     """
     if h is None:
         h = hashlib.md5()
-    if isinstance(x, dict):
+    if hasattr(x, 'keys'):
+        # dict-like types
         keys = _sort_keys(x)
         for key in keys:
             object_hash(key, h)
             object_hash(x[key], h)
-    elif isinstance(x, (list, tuple)):
-        h.update(str(type(x)).encode('utf-8'))
-        for xx in x:
-            object_hash(xx, h)
     elif isinstance(x, bytes):
         # must come before "str" below
         h.update(x)
@@ -121,6 +136,11 @@ def object_hash(x, h=None):
         h.update(str(x.shape).encode('utf-8'))
         h.update(str(x.dtype).encode('utf-8'))
         h.update(x.tostring())
+    elif hasattr(x, '__len__'):
+        # all other list-like types
+        h.update(str(type(x)).encode('utf-8'))
+        for xx in x:
+            object_hash(xx, h)
     else:
         raise RuntimeError('unsupported type: %s (%s)' % (type(x), x))
     return int(h.hexdigest(), 16)
@@ -249,7 +269,46 @@ def sum_squared(X):
     return np.dot(X_flat, X_flat)
 
 
-def check_fname(fname, filetype, endings):
+def warn(message, category=RuntimeWarning):
+    """Emit a warning with trace outside the mne namespace
+
+    This function takes arguments like warnings.warn, and sends messages
+    using both ``warnings.warn`` and ``logger.warn``. Warnings can be
+    generated deep within nested function calls. In order to provide a
+    more helpful warning, this function traverses the stack until it
+    reaches a frame outside the ``mne`` namespace that caused the error.
+
+    Parameters
+    ----------
+    message : str
+        Warning message.
+    category : instance of Warning
+        The warning class. Defaults to ``RuntimeWarning``.
+    """
+    import mne
+    root_dir = op.dirname(mne.__file__)
+    stacklevel = 1
+    frame = None
+    stack = inspect.stack()
+    last_fname = ''
+    for fi, frame in enumerate(stack):
+        fname = frame[1]
+        del frame
+        if fname == '<string>' and last_fname == 'utils.py':  # in verbose dec
+            last_fname = fname
+            continue
+        # treat tests as scripts
+        if not fname.startswith(root_dir) or \
+                op.basename(op.dirname(fname)) == 'tests':
+            stacklevel = fi + 1
+            break
+        last_fname = op.basename(fname)
+    del stack
+    warnings.warn(message, category, stacklevel=stacklevel)
+    logger.warning(message)
+
+
+def check_fname(fname, filetype, endings, endings_err=()):
     """Enforce MNE filename conventions
 
     Parameters
@@ -260,20 +319,33 @@ def check_fname(fname, filetype, endings):
         Type of file. e.g., ICA, Epochs etc.
     endings : tuple
         Acceptable endings for the filename.
+    endings_err : tuple
+        Obligatory possible endings for the filename.
     """
+    if len(endings_err) > 0 and not fname.endswith(endings_err):
+        print_endings = ' or '.join([', '.join(endings_err[:-1]),
+                                     endings_err[-1]])
+        raise IOError('The filename (%s) for file type %s must end with %s'
+                      % (fname, filetype, print_endings))
     print_endings = ' or '.join([', '.join(endings[:-1]), endings[-1]])
     if not fname.endswith(endings):
-        warnings.warn('This filename (%s) does not conform to MNE naming '
-                      'conventions. All %s files should end with '
-                      '%s' % (fname, filetype, print_endings))
+        warn('This filename (%s) does not conform to MNE naming conventions. '
+             'All %s files should end with %s'
+             % (fname, filetype, print_endings))
 
 
 class WrapStdOut(object):
-    """Ridiculous class to work around how doctest captures stdout"""
+    """Dynamically wrap to sys.stdout
+
+    This makes packages that monkey-patch sys.stdout (e.g.doctest,
+    sphinx-gallery) work properly."""
     def __getattr__(self, name):
         # Even more ridiculous than this class, this must be sys.stdout (not
         # just stdout) in order for this to work (tested on OSX and Linux)
-        return getattr(sys.stdout, name)
+        if hasattr(sys.stdout, name):
+            return getattr(sys.stdout, name)
+        else:
+            raise AttributeError("'file' object has not attribute '%s'" % name)
 
 
 class _TempDir(str):
@@ -298,8 +370,8 @@ class _TempDir(str):
         rmtree(self._path, ignore_errors=True)
 
 
-def estimate_rank(data, tol=1e-4, return_singular=False,
-                  norm=True, copy=True):
+def estimate_rank(data, tol='auto', return_singular=False,
+                  norm=True, copy=None):
     """Helper to estimate the rank of data
 
     This function will normalize the rows of the data (typically
@@ -310,11 +382,12 @@ def estimate_rank(data, tol=1e-4, return_singular=False,
     ----------
     data : array
         Data to estimate the rank of (should be 2-dimensional).
-    tol : float
+    tol : float | str
         Tolerance for singular values to consider non-zero in
         calculating the rank. The singular values are calculated
         in this method such that independent data are expected to
-        have singular value around one.
+        have singular value around one. Can be 'auto' to use the
+        same thresholding as ``scipy.linalg.orth``.
     return_singular : bool
         If True, also return the singular values that were used
         to determine the rank.
@@ -322,8 +395,8 @@ def estimate_rank(data, tol=1e-4, return_singular=False,
         If True, data will be scaled by their estimated row-wise norm.
         Else data are assumed to be scaled. Defaults to True.
     copy : bool
-        If False, values in data will be modified in-place during
-        rank estimation (saves memory).
+        This parameter has been deprecated and will be removed in 0.13.
+        It is ignored in 0.12.
 
     Returns
     -------
@@ -333,13 +406,20 @@ def estimate_rank(data, tol=1e-4, return_singular=False,
         If return_singular is True, the singular values that were
         thresholded to determine the rank are also returned.
     """
-    if copy is True:
-        data = data.copy()
+    if copy is not None:
+        warn('copy is deprecated and ignored. It will be removed in 0.13.')
+    data = data.copy()  # operate on a copy
     if norm is True:
         norms = _compute_row_norms(data)
         data /= norms[:, np.newaxis]
     s = linalg.svd(data, compute_uv=False, overwrite_a=True)
-    rank = np.sum(s >= tol)
+    if isinstance(tol, string_types):
+        if tol != 'auto':
+            raise ValueError('tol must be "auto" or float')
+        eps = np.finfo(float).eps
+        tol = np.max(data.shape) * np.amax(s) * eps
+    tol = float(tol)
+    rank = np.sum(s > tol)
     if return_singular is True:
         return rank, s
     else:
@@ -419,7 +499,7 @@ def trait_wraith(*args, **kwargs):
 # Following deprecated class copied from scikit-learn
 
 # force show of DeprecationWarning even on python 2.7
-warnings.simplefilter('default')
+warnings.filterwarnings('always', category=DeprecationWarning, module='mne')
 
 
 class deprecated(object):
@@ -504,7 +584,7 @@ class deprecated(object):
         return deprecation_wrapped
 
     def _update_doc(self, olddoc):
-        newdoc = "DEPRECATED"
+        newdoc = ".. warning:: DEPRECATED"
         if self.extra:
             newdoc = "%s: %s" % (newdoc, self.extra)
         if olddoc:
@@ -542,15 +622,30 @@ def verbose(function, *args, **kwargs):
     verbose_level = default_level if verbose_level is None else verbose_level
 
     if verbose_level is not None:
-        old_level = set_log_level(verbose_level, True)
         # set it back if we get an exception
-        try:
+        with use_log_level(verbose_level):
             return function(*args, **kwargs)
-        finally:
-            set_log_level(old_level)
     return function(*args, **kwargs)
 
 
+class use_log_level(object):
+    """Context handler for logging level
+
+    Parameters
+    ----------
+    level : int
+        The level to use.
+    """
+    def __init__(self, level):
+        self.level = level
+
+    def __enter__(self):
+        self.old_level = set_log_level(self.level, True)
+
+    def __exit__(self, *args):
+        set_log_level(self.old_level)
+
+
 @nottest
 def slow_test(f):
     """Decorator for slow tests"""
@@ -711,6 +806,10 @@ requires_good_network = partial(
     requires_module, name='good network connection',
     call='if int(os.environ.get("MNE_SKIP_NETWORK_TESTS", 0)):\n'
          '    raise ImportError')
+requires_ftp = partial(
+    requires_module, name='ftp downloading capability',
+    call='if int(os.environ.get("MNE_SKIP_FTP_TESTS", 0)):\n'
+         '    raise ImportError')
 requires_nitime = partial(requires_module, name='nitime',
                           call='import nitime')
 requires_traits = partial(requires_module, name='traits',
@@ -752,6 +851,35 @@ def _check_mayavi_version(min_version='4.3.0'):
         raise RuntimeError("Need mayavi >= %s" % min_version)
 
 
+def _check_pyface_backend():
+    """Check the currently selected Pyface backend
+
+    Returns
+    -------
+    backend : str
+        Name of the backend.
+    result : 0 | 1 | 2
+        0: the backend has been tested and works.
+        1: the backend has not been tested.
+        2: the backend not been tested.
+
+    Notes
+    -----
+    See also: http://docs.enthought.com/pyface/
+    """
+    try:
+        from traits.trait_base import ETSConfig
+    except ImportError:
+        return None, 2
+
+    backend = ETSConfig.toolkit
+    if backend == 'qt4':
+        status = 0
+    else:
+        status = 1
+    return backend, status
+
+
 @verbose
 def run_subprocess(command, verbose=None, *args, **kwargs):
     """Run command using subprocess.Popen
@@ -793,11 +921,10 @@ def run_subprocess(command, verbose=None, *args, **kwargs):
     # frequently this should be refactored so as to only check the path once.
     env = kwargs.get('env', os.environ)
     if any(p.startswith('~') for p in env['PATH'].split(os.pathsep)):
-        msg = ("Your PATH environment variable contains at least one path "
-               "starting with a tilde ('~') character. Such paths are not "
-               "interpreted correctly from within Python. It is recommended "
-               "that you use '$HOME' instead of '~'.")
-        warnings.warn(msg)
+        warn('Your PATH environment variable contains at least one path '
+             'starting with a tilde ("~") character. Such paths are not '
+             'interpreted correctly from within Python. It is recommended '
+             'that you use "$HOME" instead of "~".')
 
     logger.info("Running subprocess: %s" % ' '.join(command))
     try:
@@ -879,7 +1006,7 @@ def set_log_file(fname=None, output_format='%(message)s', overwrite=None):
             https://docs.python.org/dev/howto/logging.html
 
         e.g., "%(asctime)s - %(levelname)s - %(message)s".
-    overwrite : bool, or None
+    overwrite : bool | None
         Overwrite the log file (if it exists). Otherwise, statements
         will be appended to the log (default). None is the same as False,
         but additionally raises a warning to notify the user that log
@@ -888,15 +1015,20 @@ def set_log_file(fname=None, output_format='%(message)s', overwrite=None):
     logger = logging.getLogger('mne')
     handlers = logger.handlers
     for h in handlers:
-        if isinstance(h, logging.FileHandler):
-            h.close()
-        logger.removeHandler(h)
+        # only remove our handlers (get along nicely with nose)
+        if isinstance(h, (logging.FileHandler, logging.StreamHandler)):
+            if isinstance(h, logging.FileHandler):
+                h.close()
+            logger.removeHandler(h)
     if fname is not None:
         if op.isfile(fname) and overwrite is None:
+            # Don't use warn() here because we just want to
+            # emit a warnings.warn here (not logger.warn)
             warnings.warn('Log entries will be appended to the file. Use '
                           'overwrite=False to avoid this message in the '
-                          'future.')
-        mode = 'w' if overwrite is True else 'a'
+                          'future.', RuntimeWarning, stacklevel=2)
+            overwrite = False
+        mode = 'w' if overwrite else 'a'
         lh = logging.FileHandler(fname, mode=mode)
     else:
         """ we should just be able to do:
@@ -1050,30 +1182,35 @@ def set_memmap_min_size(memmap_min_size):
 
 
 # List the known configuration values
-known_config_types = [
+known_config_types = (
     'MNE_BROWSE_RAW_SIZE',
+    'MNE_CACHE_DIR',
     'MNE_CUDA_IGNORE_PRECISION',
     'MNE_DATA',
+    'MNE_DATASETS_BRAINSTORM_PATH',
+    'MNE_DATASETS_EEGBCI_PATH',
     'MNE_DATASETS_MEGSIM_PATH',
+    'MNE_DATASETS_MISC_PATH',
     'MNE_DATASETS_SAMPLE_PATH',
     'MNE_DATASETS_SOMATO_PATH',
+    'MNE_DATASETS_SPM_FACE_DATASETS_TESTS',
     'MNE_DATASETS_SPM_FACE_PATH',
-    'MNE_DATASETS_EEGBCI_PATH',
-    'MNE_DATASETS_BRAINSTORM_PATH',
     'MNE_DATASETS_TESTING_PATH',
+    'MNE_FORCE_SERIAL',
     'MNE_LOGGING_LEVEL',
-    'MNE_USE_CUDA',
-    'SUBJECTS_DIR',
-    'MNE_CACHE_DIR',
     'MNE_MEMMAP_MIN_SIZE',
+    'MNE_SKIP_FTP_TESTS',
+    'MNE_SKIP_NETWORK_TESTS',
     'MNE_SKIP_TESTING_DATASET_TESTS',
-    'MNE_DATASETS_SPM_FACE_DATASETS_TESTS'
-]
+    'MNE_STIM_CHANNEL',
+    'MNE_USE_CUDA',
+    'SUBJECTS_DIR',
+)
 
 # These allow for partial matches, e.g. 'MNE_STIM_CHANNEL_1' is okay key
-known_config_wildcards = [
+known_config_wildcards = (
     'MNE_STIM_CHANNEL',
-]
+)
 
 
 def get_config(key=None, default=None, raise_error=False, home_dir=None):
@@ -1143,8 +1280,9 @@ def set_config(key, value, home_dir=None):
 
     Parameters
     ----------
-    key : str
-        The preference key to set.
+    key : str | None
+        The preference key to set. If None, a tuple of the valid
+        keys is returned, and ``value`` and ``home_dir`` are ignored.
     value : str |  None
         The value to assign to the preference key. If None, the key is
         deleted.
@@ -1156,6 +1294,8 @@ def set_config(key, value, home_dir=None):
     --------
     get_config
     """
+    if key is None:
+        return known_config_types
     if not isinstance(key, string_types):
         raise TypeError('key must be a string')
     # While JSON allow non-string types, we allow users to override config
@@ -1164,7 +1304,7 @@ def set_config(key, value, home_dir=None):
         raise TypeError('value must be a string or None')
     if key not in known_config_types and not \
             any(k in key for k in known_config_wildcards):
-        warnings.warn('Setting non-standard config type: "%s"' % key)
+        warn('Setting non-standard config type: "%s"' % key)
 
     # Read all previous values
     config_path = get_config_path(home_dir=home_dir)
@@ -1458,7 +1598,7 @@ def _fetch_file(url, file_name, print_destination=True, resume=True,
         else:
             initial_size = 0
         # This should never happen if our functions work properly
-        if initial_size >= file_size:
+        if initial_size > file_size:
             raise RuntimeError('Local file (%s) is larger than remote '
                                'file (%s), cannot resume download'
                                % (sizeof_fmt(initial_size),
@@ -1565,10 +1705,12 @@ def _get_stim_channel(stim_channel, info):
                      "manually using the 'stim_channel' parameter.")
 
 
-def _check_fname(fname, overwrite):
+def _check_fname(fname, overwrite=False, must_exist=False):
     """Helper to check for file existence"""
     if not isinstance(fname, string_types):
         raise TypeError('file name is not a string')
+    if must_exist and not op.isfile(fname):
+        raise IOError('File "%s" does not exist' % fname)
     if op.isfile(fname):
         if not overwrite:
             raise IOError('Destination file exists. Please use option '
@@ -1775,7 +1917,7 @@ def md5sum(fname, block_size=1048576):  # 2 ** 20
     Returns
     -------
     hash_ : str
-        The hexidecimal digest of the hash.
+        The hexadecimal digest of the hash.
     """
     md5 = hashlib.md5()
     with open(fname, 'rb') as fid:
@@ -1787,15 +1929,6 @@ def md5sum(fname, block_size=1048576):  # 2 ** 20
     return md5.hexdigest()
 
 
-def _sphere_to_cartesian(theta, phi, r):
-    """Transform spherical coordinates to cartesian"""
-    z = r * np.sin(phi)
-    rcos_phi = r * np.cos(phi)
-    x = rcos_phi * np.cos(theta)
-    y = rcos_phi * np.sin(theta)
-    return x, y, z
-
-
 def create_slices(start, stop, step=None, length=1):
     """ Generate slices of time indexes
 
@@ -1827,15 +1960,30 @@ def create_slices(start, stop, step=None, length=1):
     return slices
 
 
-def _time_mask(times, tmin=None, tmax=None, strict=False):
+def _time_mask(times, tmin=None, tmax=None, sfreq=None, raise_error=True):
     """Helper to safely find sample boundaries"""
+    orig_tmin = tmin
+    orig_tmax = tmax
     tmin = -np.inf if tmin is None else tmin
     tmax = np.inf if tmax is None else tmax
+    if not np.isfinite(tmin):
+        tmin = times[0]
+    if not np.isfinite(tmax):
+        tmax = times[-1]
+    if sfreq is not None:
+        # Push to a bit past the nearest sample boundary first
+        sfreq = float(sfreq)
+        tmin = int(round(tmin * sfreq)) / sfreq - 0.5 / sfreq
+        tmax = int(round(tmax * sfreq)) / sfreq + 0.5 / sfreq
+    if raise_error and tmin > tmax:
+        raise ValueError('tmin (%s) must be less than or equal to tmax (%s)'
+                         % (orig_tmin, orig_tmax))
     mask = (times >= tmin)
     mask &= (times <= tmax)
-    if not strict:
-        mask |= isclose(times, tmin)
-        mask |= isclose(times, tmax)
+    if raise_error and not mask.any():
+        raise ValueError('No samples remain when using tmin=%s and tmax=%s '
+                         '(original time bounds are [%s, %s])'
+                         % (orig_tmin, orig_tmax, times[0], times[-1]))
     return mask
 
 
@@ -1964,7 +2112,7 @@ def grand_average(all_inst, interpolate_bads=True, drop_bads=True):
         bads = list(set((b for inst in all_inst for b in inst.info['bads'])))
         if bads:
             for inst in all_inst:
-                inst.drop_channels(bads, copy=False)
+                inst.drop_channels(bads)
 
     # make grand_average object using combine_[evoked/tfr]
     grand_average = combine(all_inst, weights='equal')
@@ -1983,3 +2131,83 @@ def _get_root_dir():
             op.isdir(op.join(up_dir, x)) for x in ('mne', 'examples', 'doc')):
         root_dir = op.abspath(up_dir)
     return root_dir
+
+
+def sys_info(fid=None, show_paths=False):
+    """Print the system information for debugging
+
+    This function is useful for printing system information
+    to help triage bugs.
+
+    Parameters
+    ----------
+    fid : file-like | None
+        The file to write to. Will be passed to :func:`print()`.
+        Can be None to use :data:`sys.stdout`.
+    show_paths : bool
+        If True, print paths for each module.
+
+    Examples
+    --------
+    Running this function with no arguments prints an output that is
+    useful when submitting bug reports::
+
+        >>> import mne
+        >>> mne.sys_info() # doctest: +SKIP
+        Platform:      Linux-4.2.0-27-generic-x86_64-with-Ubuntu-15.10-wily
+        Python:        2.7.10 (default, Oct 14 2015, 16:09:02)  [GCC 5.2.1 20151010]
+        Executable:    /usr/bin/python
+
+        mne:           0.12.dev0
+        numpy:         1.12.0.dev0+ec5bd81 {lapack=mkl_rt, blas=mkl_rt}
+        scipy:         0.18.0.dev0+3deede3
+        matplotlib:    1.5.1+1107.g1fa2697
+
+        sklearn:       0.18.dev0
+        nibabel:       2.1.0dev
+        nitime:        0.6
+        mayavi:        4.3.1
+        nose:          1.3.7
+        pandas:        0.17.1+25.g547750a
+        pycuda:        2015.1.3
+        skcuda:        0.5.2
+
+    """  # noqa
+    ljust = 15
+    out = 'Platform:'.ljust(ljust) + platform.platform() + '\n'
+    out += 'Python:'.ljust(ljust) + str(sys.version).replace('\n', ' ') + '\n'
+    out += 'Executable:'.ljust(ljust) + sys.executable + '\n\n'
+    old_stdout = sys.stdout
+    capture = StringIO()
+    try:
+        sys.stdout = capture
+        np.show_config()
+    finally:
+        sys.stdout = old_stdout
+    lines = capture.getvalue().split('\n')
+    libs = []
+    for li, line in enumerate(lines):
+        for key in ('lapack', 'blas'):
+            if line.startswith('%s_opt_info' % key):
+                libs += ['%s=' % key +
+                         lines[li + 1].split('[')[1].split("'")[1]]
+    libs = ', '.join(libs)
+    version_texts = dict(pycuda='VERSION_TEXT')
+    for mod_name in ('mne', 'numpy', 'scipy', 'matplotlib', '',
+                     'sklearn', 'nibabel', 'nitime', 'mayavi', 'nose',
+                     'pandas', 'pycuda', 'skcuda'):
+        if mod_name == '':
+            out += '\n'
+            continue
+        out += ('%s:' % mod_name).ljust(ljust)
+        try:
+            mod = __import__(mod_name)
+        except Exception:
+            out += 'Not found\n'
+        else:
+            version = getattr(mod, version_texts.get(mod_name, '__version__'))
+            extra = (' (%s)' % op.dirname(mod.__file__)) if show_paths else ''
+            if mod_name == 'numpy':
+                extra = ' {%s}%s' % (libs, extra)
+            out += '%s%s\n' % (version, extra)
+    print(out, end='', file=fid)
diff --git a/mne/viz/_3d.py b/mne/viz/_3d.py
index 202e976..4068918 100644
--- a/mne/viz/_3d.py
+++ b/mne/viz/_3d.py
@@ -11,24 +11,24 @@ from __future__ import print_function
 #
 # License: Simplified BSD
 
-from ..externals.six import string_types, advance_iterator
-
+import base64
+from itertools import cycle
 import os.path as op
 import warnings
-from itertools import cycle
-import base64
 
 import numpy as np
 from scipy import linalg
 
+from ..externals.six import string_types, advance_iterator
+from ..io import _loc_to_coil_trans, Info
 from ..io.pick import pick_types
 from ..io.constants import FIFF
 from ..surface import (get_head_surf, get_meg_helmet_surf, read_surface,
                        transform_surface_to)
 from ..transforms import (read_trans, _find_trans, apply_trans,
                           combine_transforms, _get_trans, _ensure_trans,
-                          invert_transform)
-from ..utils import get_subjects_dir, logger, _check_subject, verbose
+                          invert_transform, Transform)
+from ..utils import get_subjects_dir, logger, _check_subject, verbose, warn
 from ..fixes import _get_args
 from ..defaults import _handle_default
 from .utils import mne_analyze_colormap, _prepare_trellis, COLORS, plt_show
@@ -269,16 +269,18 @@ def _plot_mri_contours(mri_fname, surf_fnames, orientation='coronal',
 @verbose
 def plot_trans(info, trans='auto', subject=None, subjects_dir=None,
                ch_type=None, source=('bem', 'head'), coord_frame='head',
-               meg_sensors=False, dig=False, verbose=None):
+               meg_sensors=False, eeg_sensors=True, dig=False, ref_meg=False,
+               verbose=None):
     """Plot MEG/EEG head surface and helmet in 3D.
 
     Parameters
     ----------
     info : dict
         The measurement info.
-    trans : str | 'auto' | dict
+    trans : str | 'auto' | dict | None
         The full path to the head<->MRI transform ``*-trans.fif`` file
-        produced during coregistration.
+        produced during coregistration. If trans is None, no head
+        surface will be shown.
     subject : str | None
         The subject name corresponding to FreeSurfer environment
         variable SUBJECT.
@@ -298,8 +300,12 @@ def plot_trans(info, trans='auto', subject=None, subjects_dir=None,
         Coordinate frame to use, 'head', 'meg', or 'mri'.
     meg_sensors : bool
         If True, plot MEG sensors as points in addition to showing the helmet.
+    eeg_sensors : bool
+        If True, plot EEG sensors as points.
     dig : bool
         If True, plot the digitization points.
+    ref_meg : bool
+        If True (default False), include reference MEG sensors.
     verbose : bool, str, int, or None
         If not None, override default verbose level (see mne.verbose).
 
@@ -308,26 +314,40 @@ def plot_trans(info, trans='auto', subject=None, subjects_dir=None,
     fig : instance of mlab.Figure
         The mayavi figure.
     """
+    from ..forward import _create_meg_coils
+    if not isinstance(info, Info):
+        raise TypeError('info must be an instance of Info, got %s'
+                        % type(info))
     if coord_frame not in ['head', 'meg', 'mri']:
         raise ValueError('coord_frame must be "head" or "meg"')
     if ch_type not in [None, 'eeg', 'meg']:
         raise ValueError('Argument ch_type must be None | eeg | meg. Got %s.'
                          % ch_type)
 
+    show_head = (subject is not None)
     if isinstance(trans, string_types):
         if trans == 'auto':
             # let's try to do this in MRI coordinates so they're easy to plot
+            subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)
             trans = _find_trans(subject, subjects_dir)
         trans = read_trans(trans)
+    elif trans is None:
+        trans = Transform('head', 'mri', np.eye(4))
+        show_head = False
     elif not isinstance(trans, dict):
-        raise TypeError('trans must be str or dict')
+        raise TypeError('trans must be str, dict, or None')
     head_mri_t = _ensure_trans(trans, 'head', 'mri')
     del trans
 
     # both the head and helmet will be in MRI coordinates after this
-    surfs = [get_head_surf(subject, source=source, subjects_dir=subjects_dir)]
-    if ch_type is None or ch_type == 'meg':
-        surfs.append(get_meg_helmet_surf(info, head_mri_t))
+    meg_picks = pick_types(info, meg=True, ref_meg=ref_meg)
+    surfs = dict()
+    if show_head:
+        subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)
+        surfs['head'] = get_head_surf(subject, source=source,
+                                      subjects_dir=subjects_dir)
+    if (ch_type is None and len(meg_picks) > 0) or ch_type == 'meg':
+        surfs['helmet'] = get_meg_helmet_surf(info, head_mri_t)
     if coord_frame == 'meg':
         surf_trans = combine_transforms(info['dev_head_t'], head_mri_t,
                                         'meg', 'mri')
@@ -335,15 +355,16 @@ def plot_trans(info, trans='auto', subject=None, subjects_dir=None,
         surf_trans = head_mri_t
     else:  # coord_frame == 'mri'
         surf_trans = None
-    surfs = [transform_surface_to(surf, coord_frame, surf_trans)
-             for surf in surfs]
+    for key in surfs.keys():
+        surfs[key] = transform_surface_to(surfs[key], coord_frame, surf_trans)
     del surf_trans
 
     # determine points
-    meg_loc = list()
+    meg_rrs, meg_tris = list(), list()
     ext_loc = list()
     car_loc = list()
-    if ch_type is None or ch_type == 'eeg':
+    eeg_loc = list()
+    if eeg_sensors and (ch_type is None or ch_type == 'eeg'):
         eeg_loc = np.array([info['chs'][k]['loc'][:3]
                            for k in pick_types(info, meg=False, eeg=True)])
         if len(eeg_loc) > 0:
@@ -352,28 +373,40 @@ def plot_trans(info, trans='auto', subject=None, subjects_dir=None,
                 eeg_loc = apply_trans(invert_transform(info['dev_head_t']),
                                       eeg_loc)
             elif coord_frame == 'mri':
-                eeg_loc = apply_trans(invert_transform(head_mri_t), eeg_loc)
+                eeg_loc = apply_trans(head_mri_t, eeg_loc)
         else:
             # only warn if EEG explicitly requested, or EEG channels exist but
             # no locations are provided
             if (ch_type is not None or
                     len(pick_types(info, meg=False, eeg=True)) > 0):
-                warnings.warn('EEG electrode locations not found. '
-                              'Cannot plot EEG electrodes.')
+                warn('EEG electrode locations not found. Cannot plot EEG '
+                     'electrodes.')
     if meg_sensors:
-        meg_loc = np.array([info['chs'][k]['loc'][:3]
-                           for k in pick_types(info)])
-        if len(meg_loc) > 0:
-            # Transform MEG coordinates from meg if necessary
-            if coord_frame == 'head':
-                meg_loc = apply_trans(info['dev_head_t'], meg_loc)
-            elif coord_frame == 'mri':
-                t = combine_transforms(info['dev_head_t'], head_mri_t,
+        coil_transs = [_loc_to_coil_trans(info['chs'][pick]['loc'])
+                       for pick in meg_picks]
+        # Transform MEG coordinates from meg if necessary
+        trans = None
+        if coord_frame == 'head':
+            trans = info['dev_head_t']
+        elif coord_frame == 'mri':
+            trans = combine_transforms(info['dev_head_t'], head_mri_t,
                                        'meg', 'mri')
-                meg_loc = apply_trans(t, meg_loc)
+        coils = _create_meg_coils([info['chs'][pick] for pick in meg_picks],
+                                  acc='normal')
+        offset = 0
+        for coil, coil_trans in zip(coils, coil_transs):
+            rrs, tris = _sensor_shape(coil)
+            rrs = apply_trans(coil_trans, rrs)
+            if trans is not None:
+                rrs = apply_trans(trans, rrs)
+            meg_rrs.append(rrs)
+            meg_tris.append(tris + offset)
+            offset += len(meg_rrs[-1])
+        if len(meg_rrs) == 0:
+            warn('MEG electrodes not found. Cannot plot MEG locations.')
         else:
-            warnings.warn('MEG electrodes not found. '
-                          'Cannot plot MEG locations.')
+            meg_rrs = np.concatenate(meg_rrs, axis=0)
+            meg_tris = np.concatenate(meg_tris, axis=0)
     if dig:
         ext_loc = np.array([d['r'] for d in info['dig']
                            if d['kind'] == FIFF.FIFFV_POINT_EXTRA])
@@ -387,16 +420,15 @@ def plot_trans(info, trans='auto', subject=None, subjects_dir=None,
             ext_loc = apply_trans(head_mri_t, ext_loc)
             car_loc = apply_trans(head_mri_t, car_loc)
         if len(car_loc) == len(ext_loc) == 0:
-            warnings.warn('Digitization points not found. '
-                          'Cannot plot digitization.')
+            warn('Digitization points not found. Cannot plot digitization.')
 
     # do the plotting, surfaces then points
     from mayavi import mlab
     fig = mlab.figure(bgcolor=(0.0, 0.0, 0.0), size=(600, 600))
 
-    alphas = [1.0, 0.5]  # head, helmet
-    colors = [(0.6, 0.6, 0.6), (0.0, 0.0, 0.6)]
-    for surf, alpha, color in zip(surfs, alphas, colors):
+    alphas = dict(head=1.0, helmet=0.5)
+    colors = dict(head=(0.6, 0.6, 0.6), helmet=(0.0, 0.0, 0.6))
+    for key, surf in surfs.items():
         x, y, z = surf['rr'].T
         nn = surf['nn']
         # make absolutely sure these are normalized for Mayavi
@@ -407,21 +439,86 @@ def plot_trans(info, trans='auto', subject=None, subjects_dir=None,
             mesh = mlab.pipeline.triangular_mesh_source(x, y, z, surf['tris'])
         mesh.data.point_data.normals = nn
         mesh.data.cell_data.normals = None
-        mlab.pipeline.surface(mesh, color=color, opacity=alpha)
+        mlab.pipeline.surface(mesh, color=colors[key], opacity=alphas[key])
 
-    datas = (eeg_loc, meg_loc, car_loc, ext_loc)
-    colors = ((1., 0., 0.), (0., 0.25, 0.5), (1., 1., 0.), (1., 0.5, 0.))
-    alphas = (1.0, 0.25, 0.5, 0.25)
-    scales = (0.005, 0.0025, 0.015, 0.0075)
+    datas = (eeg_loc, car_loc, ext_loc)
+    colors = ((1., 0., 0.), (1., 1., 0.), (1., 0.5, 0.))
+    alphas = (1.0, 0.5, 0.25)
+    scales = (0.005, 0.015, 0.0075)
     for data, color, alpha, scale in zip(datas, colors, alphas, scales):
         if len(data) > 0:
             with warnings.catch_warnings(record=True):  # traits
                 mlab.points3d(data[:, 0], data[:, 1], data[:, 2],
                               color=color, scale_factor=scale, opacity=alpha)
+    if len(meg_rrs) > 0:
+        color, alpha = (0., 0.25, 0.5), 0.25
+        mlab.triangular_mesh(meg_rrs[:, 0], meg_rrs[:, 1], meg_rrs[:, 2],
+                             meg_tris, color=color, opacity=alpha)
     mlab.view(90, 90)
     return fig
 
 
+def _make_tris_fan(n_vert):
+    """Helper to make tris given a number of vertices of a circle-like obj"""
+    tris = np.zeros((n_vert - 2, 3), int)
+    tris[:, 2] = np.arange(2, n_vert)
+    tris[:, 1] = tris[:, 2] - 1
+    return tris
+
+
+def _sensor_shape(coil):
+    """Get the sensor shape vertices"""
+    rrs = np.empty([0, 2])
+    tris = np.empty([0, 3], int)
+    id_ = coil['type'] & 0xFFFF
+    if id_ in (2, 3012, 3013, 3011):
+        # square figure eight
+        # wound by right hand rule such that +x side is "up" (+z)
+        long_side = coil['size']  # length of long side (meters)
+        offset = 0.0025  # offset of the center portion of planar grad coil
+        rrs = np.array([
+            [offset, -long_side / 2.],
+            [long_side / 2., -long_side / 2.],
+            [long_side / 2., long_side / 2.],
+            [offset, long_side / 2.],
+            [-offset, -long_side / 2.],
+            [-long_side / 2., -long_side / 2.],
+            [-long_side / 2., long_side / 2.],
+            [-offset, long_side / 2.]])
+        tris = np.concatenate((_make_tris_fan(4),
+                               _make_tris_fan(4) + 4), axis=0)
+    elif id_ in (2000, 3022, 3023, 3024):
+        # square magnetometer (potentially point-type)
+        size = 0.001 if id_ == 2000 else (coil['size'] / 2.)
+        rrs = np.array([[-1., 1.], [1., 1.], [1., -1.], [-1., -1.]]) * size
+        tris = _make_tris_fan(4)
+    elif id_ in (4001, 4003, 5002, 7002, 7003):
+        # round magnetometer
+        n_pts = 15  # number of points for circle
+        circle = np.exp(2j * np.pi * np.arange(n_pts) / float(n_pts))
+        circle = np.concatenate(([0.], circle))
+        circle *= coil['size'] / 2.  # radius of coil
+        rrs = np.array([circle.real, circle.imag]).T
+        tris = _make_tris_fan(n_pts + 1)
+    elif id_ in (4002, 5001, 5003, 5004, 4004, 4005, 6001, 7001):
+        # round coil 1st order (off-diagonal) gradiometer
+        baseline = coil['base'] if id_ in (5004, 4005) else 0.
+        n_pts = 16  # number of points for circle
+        # This time, go all the way around circle to close it fully
+        circle = np.exp(2j * np.pi * np.arange(-1, n_pts) / float(n_pts - 1))
+        circle[0] = 0  # center pt for triangulation
+        circle *= coil['size'] / 2.
+        rrs = np.array([  # first, second coil
+            np.concatenate([circle.real + baseline / 2.,
+                            circle.real - baseline / 2.]),
+            np.concatenate([circle.imag, -circle.imag])]).T
+        tris = np.concatenate([_make_tris_fan(n_pts + 1),
+                               _make_tris_fan(n_pts + 1) + n_pts + 1])
+    # Go from (x,y) -> (x,y,z)
+    rrs = np.pad(rrs, ((0, 0), (0, 1)), mode='constant')
+    return rrs, tris
+
+
 def _limits_to_control_points(clim, stc_data, colormap):
     """Private helper function to convert limits (values or percentiles)
     to control points.
@@ -481,7 +578,7 @@ def _limits_to_control_points(clim, stc_data, colormap):
     if len(set(ctrl_pts)) != 3:
         if len(set(ctrl_pts)) == 1:  # three points match
             if ctrl_pts[0] == 0:  # all are zero
-                warnings.warn('All data were zero')
+                warn('All data were zero')
                 ctrl_pts = np.arange(3, dtype=float)
             else:
                 ctrl_pts *= [0., 0.5, 1]  # all nonzero pts == max
@@ -829,8 +926,8 @@ def plot_sparse_source_estimates(src, stcs, colors=None, linewidth=2,
 
 def plot_dipole_locations(dipoles, trans, subject, subjects_dir=None,
                           bgcolor=(1, 1, 1), opacity=0.3,
-                          brain_color=(0.7, 0.7, 0.7), mesh_color=(1, 1, 0),
-                          fig_name=None, fig_size=(600, 600), mode='cone',
+                          brain_color=(1, 1, 0), fig_name=None,
+                          fig_size=(600, 600), mode='cone',
                           scale_factor=0.1e-1, colors=None, verbose=None):
     """Plot dipole locations
 
@@ -855,8 +952,6 @@ def plot_dipole_locations(dipoles, trans, subject, subjects_dir=None,
         Opacity of brain mesh.
     brain_color : tuple of length 3
         Brain color.
-    mesh_color : tuple of length 3
-        Mesh color.
     fig_name : str
         Mayavi figure name.
     fig_size : tuple of length 2
@@ -904,7 +999,7 @@ def plot_dipole_locations(dipoles, trans, subject, subjects_dir=None,
     fig = mlab.figure(size=fig_size, bgcolor=bgcolor, fgcolor=(0, 0, 0))
     with warnings.catch_warnings(record=True):  # FutureWarning in traits
         mlab.triangular_mesh(points[:, 0], points[:, 1], points[:, 2],
-                             faces, color=mesh_color, opacity=opacity)
+                             faces, color=brain_color, opacity=opacity)
 
     for dip, color in zip(dipoles, colors):
         rgb_color = color_converter.to_rgb(color)
diff --git a/mne/viz/__init__.py b/mne/viz/__init__.py
index 14bafec..f0b4c32 100644
--- a/mne/viz/__init__.py
+++ b/mne/viz/__init__.py
@@ -3,16 +3,17 @@
 
 from .topomap import (plot_evoked_topomap, plot_projs_topomap,
                       plot_ica_components, plot_tfr_topomap, plot_topomap,
-                      plot_epochs_psd_topomap)
+                      plot_epochs_psd_topomap, plot_layout)
 from .topo import plot_topo_image_epochs, iter_topography
 from .utils import (tight_layout, mne_analyze_colormap, compare_fiff,
-                    ClickableImage, add_background_image)
+                    ClickableImage, add_background_image, plot_sensors)
 from ._3d import (plot_sparse_source_estimates, plot_source_estimates,
                   plot_trans, plot_evoked_field, plot_dipole_locations)
 from .misc import (plot_cov, plot_bem, plot_events, plot_source_spectrogram,
                    _get_presser, plot_dipole_amplitudes)
 from .evoked import (plot_evoked, plot_evoked_image, plot_evoked_white,
-                     plot_snr_estimate, plot_evoked_topo)
+                     plot_snr_estimate, plot_evoked_topo,
+                     plot_evoked_joint)
 from .circle import plot_connectivity_circle, circular_layout
 from .epochs import (plot_drop_log, plot_epochs, plot_epochs_psd,
                      plot_epochs_image)
diff --git a/mne/viz/circle.py b/mne/viz/circle.py
index 6c21293..ffa5e73 100644
--- a/mne/viz/circle.py
+++ b/mne/viz/circle.py
@@ -275,7 +275,7 @@ def plot_connectivity_circle(con, node_names, indices=None, n_lines=None,
     plt.xticks([])
     plt.yticks([])
 
-    # Set y axes limit, add additonal space if requested
+    # Set y axes limit, add additional space if requested
     plt.ylim(0, 10 + padding)
 
     # Remove the black axes border which may obscure the labels
@@ -318,7 +318,7 @@ def plot_connectivity_circle(con, node_names, indices=None, n_lines=None,
         nodes_n_con[i] += 1
         nodes_n_con[j] += 1
 
-    # initalize random number generator so plot is reproducible
+    # initialize random number generator so plot is reproducible
     rng = np.random.mtrand.RandomState(seed=0)
 
     n_con = len(indices[0])
diff --git a/mne/viz/decoding.py b/mne/viz/decoding.py
index 160252b..88e07b3 100644
--- a/mne/viz/decoding.py
+++ b/mne/viz/decoding.py
@@ -9,9 +9,9 @@ from __future__ import print_function
 # License: Simplified BSD
 
 import numpy as np
-import warnings
 
 from .utils import plt_show
+from ..utils import warn
 
 
 def plot_gat_matrix(gat, title=None, vmin=None, vmax=None, tlim=None,
@@ -151,8 +151,10 @@ def plot_gat_times(gat, train_time='diagonal', title=None, xmin=None,
     if chance is not False:
         if chance is True:
             chance = _get_chance_level(gat.scorer_, gat.y_train_)
-        ax.axhline(float(chance), color='k', linestyle='--',
-                   label="Chance level")
+        chance = float(chance)
+        if np.isfinite(chance):  # don't plot nan chance level
+            ax.axhline(chance, color='k', linestyle='--',
+                       label="Chance level")
     ax.axvline(0, color='k', label='')
 
     if isinstance(train_time, (str, float)):
@@ -231,6 +233,6 @@ def _get_chance_level(scorer, y_train):
         chance = 0.5
     else:
         chance = np.nan
-        warnings.warn('Cannot find chance level from %s, specify chance'
-                      ' level' % scorer.func_name)
+        warn('Cannot find chance level from %s, specify chance level'
+             % scorer.__name__)
     return chance
diff --git a/mne/viz/epochs.py b/mne/viz/epochs.py
index ee60cf4..2585458 100644
--- a/mne/viz/epochs.py
+++ b/mne/viz/epochs.py
@@ -14,46 +14,48 @@ import copy
 
 import numpy as np
 
-from ..utils import verbose, get_config, set_config, logger
+from ..utils import verbose, get_config, set_config, logger, warn
 from ..io.pick import pick_types, channel_type
 from ..io.proj import setup_proj
 from ..fixes import Counter, _in1d
-from ..time_frequency import compute_epochs_psd
+from ..time_frequency import psd_multitaper
 from .utils import (tight_layout, figure_nobar, _toggle_proj, _toggle_options,
                     _layout_figure, _setup_vmin_vmax, _channels_changed,
-                    _plot_raw_onscroll, _onclick_help, plt_show)
+                    _plot_raw_onscroll, _onclick_help, plt_show,
+                    _compute_scalings)
 from ..defaults import _handle_default
 
 
 def plot_epochs_image(epochs, picks=None, sigma=0., vmin=None,
                       vmax=None, colorbar=True, order=None, show=True,
-                      units=None, scalings=None, cmap='RdBu_r', fig=None):
+                      units=None, scalings=None, cmap='RdBu_r',
+                      fig=None, overlay_times=None):
     """Plot Event Related Potential / Fields image
 
     Parameters
     ----------
     epochs : instance of Epochs
-        The epochs
+        The epochs.
     picks : int | array-like of int | None
-        The indices of the channels to consider. If None, all good
-        data channels are plotted.
+        The indices of the channels to consider. If None, the first
+        five good channels are plotted.
     sigma : float
         The standard deviation of the Gaussian smoothing to apply along
         the epoch axis to apply in the image. If 0., no smoothing is applied.
     vmin : float
         The min value in the image. The unit is uV for EEG channels,
-        fT for magnetometers and fT/cm for gradiometers
+        fT for magnetometers and fT/cm for gradiometers.
     vmax : float
         The max value in the image. The unit is uV for EEG channels,
-        fT for magnetometers and fT/cm for gradiometers
+        fT for magnetometers and fT/cm for gradiometers.
     colorbar : bool
-        Display or not a colorbar
+        Display or not a colorbar.
     order : None | array of int | callable
         If not None, order is used to reorder the epochs on the y-axis
         of the image. If it's an array of int it should be of length
         the number of good epochs. If it's a callable the arguments
         passed are the times vector and the data as 2d array
-        (data.shape[1] == len(times)
+        (data.shape[1] == len(times).
     show : bool
         Show figure if True.
     units : dict | None
@@ -62,18 +64,23 @@ def plot_epochs_image(epochs, picks=None, sigma=0., vmin=None,
     scalings : dict | None
         The scalings of the channel types to be applied for plotting.
         If None, defaults to `scalings=dict(eeg=1e6, grad=1e13, mag=1e15,
-        eog=1e6)`
+        eog=1e6)`.
     cmap : matplotlib colormap
         Colormap.
     fig : matplotlib figure | None
         Figure instance to draw the image to. Figure must contain two axes for
         drawing the single trials and evoked responses. If None a new figure is
         created. Defaults to None.
+    overlay_times : array-like, shape (n_epochs,) | None
+        If not None the parameter is interpreted as time instants in seconds
+        and is added to the image. It is typically useful to display reaction
+        times. Note that it is defined with respect to the order
+        of epochs such that overlay_times[0] corresponds to epochs[0].
 
     Returns
     -------
-    figs : the list of matplotlib figures
-        One figure per channel displayed
+    figs : lists of matplotlib figures
+        One figure per channel displayed.
     """
     from scipy import ndimage
     units = _handle_default('units', units)
@@ -82,7 +89,7 @@ def plot_epochs_image(epochs, picks=None, sigma=0., vmin=None,
     import matplotlib.pyplot as plt
     if picks is None:
         picks = pick_types(epochs.info, meg=True, eeg=True, ref_meg=False,
-                           exclude='bads')
+                           exclude='bads')[:5]
 
     if set(units.keys()) != set(scalings.keys()):
         raise ValueError('Scalings and units must have the same keys.')
@@ -96,6 +103,20 @@ def plot_epochs_image(epochs, picks=None, sigma=0., vmin=None,
     scale_vmax = True if vmax is None else False
     vmin, vmax = _setup_vmin_vmax(data, vmin, vmax)
 
+    if overlay_times is not None and len(overlay_times) != len(data):
+        raise ValueError('size of overlay_times parameter (%s) do not '
+                         'match the number of epochs (%s).'
+                         % (len(overlay_times), len(data)))
+
+    if overlay_times is not None:
+        overlay_times = np.array(overlay_times)
+        times_min = np.min(overlay_times)
+        times_max = np.max(overlay_times)
+        if ((times_min < epochs.tmin) or (times_max > epochs.tmax)):
+            warn('Some values in overlay_times fall outside of the epochs '
+                 'time interval (between %s s and %s s)'
+                 % (epochs.tmin, epochs.tmax))
+
     figs = list()
     for i, (this_data, idx) in enumerate(zip(np.swapaxes(data, 0, 1), picks)):
         if fig is None:
@@ -114,8 +135,20 @@ def plot_epochs_image(epochs, picks=None, sigma=0., vmin=None,
         if callable(order):
             this_order = order(epochs.times, this_data)
 
+        if this_order is not None and (len(this_order) != len(this_data)):
+            raise ValueError('size of order parameter (%s) does not '
+                             'match the number of epochs (%s).'
+                             % (len(this_order), len(this_data)))
+
+        this_overlay_times = None
+        if overlay_times is not None:
+            this_overlay_times = overlay_times
+
         if this_order is not None:
+            this_order = np.asarray(this_order)
             this_data = this_data[this_order]
+            if this_overlay_times is not None:
+                this_overlay_times = this_overlay_times[this_order]
 
         if sigma > 0.:
             this_data = ndimage.gaussian_filter1d(this_data, sigma=sigma,
@@ -131,6 +164,9 @@ def plot_epochs_image(epochs, picks=None, sigma=0., vmin=None,
                                 0, len(data)],
                         aspect='auto', origin='lower', interpolation='nearest',
                         vmin=vmin, vmax=vmax, cmap=cmap)
+        if this_overlay_times is not None:
+            plt.plot(1e3 * this_overlay_times, 0.5 + np.arange(len(this_data)),
+                     'k', linewidth=2)
         ax2 = plt.subplot2grid((3, 10), (2, 0), colspan=9, rowspan=1)
         if colorbar:
             ax3 = plt.subplot2grid((3, 10), (0, 9), colspan=1, rowspan=3)
@@ -214,6 +250,7 @@ def plot_drop_log(drop_log, threshold=0, n_max_plot=20, subject='Unknown',
     plt.ylabel('% of epochs rejected')
     plt.xlim((-width / 2.0, (n_plot - 1) + width * 3 / 2))
     plt.grid(True, axis='y')
+    tight_layout(pad=1, fig=fig)
     plt_show(show)
     return fig
 
@@ -265,7 +302,7 @@ def _epochs_navigation_onclick(event, params):
         here = -1
     elif event.inaxes == p['reject-quit'].ax:
         if p['reject_idx']:
-            p['epochs'].drop_epochs(p['reject_idx'])
+            p['epochs'].drop(p['reject_idx'])
         plt.close(p['fig'])
         plt.close(event.inaxes.get_figure())
 
@@ -328,8 +365,12 @@ def plot_epochs(epochs, picks=None, scalings=None, n_epochs=20,
     picks : array-like of int | None
         Channels to be included. If None only good data channels are used.
         Defaults to None
-    scalings : dict | None
-        Scale factors for the traces. If None, defaults to::
+    scalings : dict | 'auto' | None
+        Scaling factors for the traces. If any fields in scalings are 'auto',
+        the scaling factor is set to match the 99.5th percentile of a subset of
+        the corresponding data. If scalings == 'auto', all scalings fields are
+        set to 'auto'. If any fields are 'auto' and data is not preloaded,
+        a subset of epochs up to 100mb will be loaded. If None, defaults to::
 
             dict(mag=1e-12, grad=4e-11, eeg=20e-6, eog=150e-6, ecg=5e-4,
                  emg=1e-3, ref_meg=1e-12, misc=1e-3, stim=1, resp=1, chpi=1e-4)
@@ -363,7 +404,8 @@ def plot_epochs(epochs, picks=None, scalings=None, n_epochs=20,
     with home/end and page down/page up keys. Butterfly plot can be toggled
     with ``b`` key. Right mouse click adds a vertical line to the plot.
     """
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
+    scalings = _compute_scalings(scalings, epochs)
     scalings = _handle_default('scalings_plot_raw', scalings)
 
     projs = epochs.info['projs']
@@ -391,10 +433,10 @@ def plot_epochs(epochs, picks=None, scalings=None, n_epochs=20,
 
 @verbose
 def plot_epochs_psd(epochs, fmin=0, fmax=np.inf, tmin=None, tmax=None,
-                    proj=False, n_fft=256,
-                    picks=None, ax=None, color='black', area_mode='std',
-                    area_alpha=0.33, n_overlap=0,
-                    dB=True, n_jobs=1, show=True, verbose=None):
+                    proj=False, bandwidth=None, adaptive=False, low_bias=True,
+                    normalization='length', picks=None, ax=None, color='black',
+                    area_mode='std', area_alpha=0.33, dB=True, n_jobs=1,
+                    show=True, verbose=None):
     """Plot the power spectral density across epochs
 
     Parameters
@@ -411,8 +453,19 @@ def plot_epochs_psd(epochs, fmin=0, fmax=np.inf, tmin=None, tmax=None,
         End time to consider.
     proj : bool
         Apply projection.
-    n_fft : int
-        Number of points to use in Welch FFT calculations.
+    bandwidth : float
+        The bandwidth of the multi taper windowing function in Hz. The default
+        value is a window half-bandwidth of 4.
+    adaptive : bool
+        Use adaptive weights to combine the tapered spectra into PSD
+        (slow, use n_jobs >> 1 to speed up computation).
+    low_bias : bool
+        Only use tapers with more than 90% spectral concentration within
+        bandwidth.
+    normalization : str
+        Either "full" or "length" (default). If "full", the PSD will
+        be normalized by the sampling rate as well as the length of
+        the signal (as in nitime).
     picks : array-like of int | None
         List of channels to use.
     ax : instance of matplotlib Axes | None
@@ -426,8 +479,6 @@ def plot_epochs_psd(epochs, fmin=0, fmax=np.inf, tmin=None, tmax=None,
         If None, no area will be plotted.
     area_alpha : float
         Alpha for the area.
-    n_overlap : int
-        The number of points of overlap between blocks.
     dB : bool
         If True, transform data to decibels.
     n_jobs : int
@@ -448,11 +499,12 @@ def plot_epochs_psd(epochs, fmin=0, fmax=np.inf, tmin=None, tmax=None,
 
     for ii, (picks, title, ax) in enumerate(zip(picks_list, titles_list,
                                                 ax_list)):
-        psds, freqs = compute_epochs_psd(epochs, picks=picks, fmin=fmin,
-                                         fmax=fmax, tmin=tmin, tmax=tmax,
-                                         n_fft=n_fft,
-                                         n_overlap=n_overlap, proj=proj,
-                                         n_jobs=n_jobs)
+        psds, freqs = psd_multitaper(epochs, picks=picks, fmin=fmin,
+                                     fmax=fmax, tmin=tmin, tmax=tmax,
+                                     bandwidth=bandwidth, adaptive=adaptive,
+                                     low_bias=low_bias,
+                                     normalization=normalization, proj=proj,
+                                     n_jobs=n_jobs)
 
         # Convert PSDs to dB
         if dB:
@@ -516,8 +568,8 @@ def _prepare_mne_browse_epochs(params, projs, n_channels, n_epochs, scalings,
         types += [t] * len(inds[-1])
     pick_kwargs = dict(meg=False, ref_meg=False, exclude=[])
     if order is None:
-        order = ['eeg', 'eog', 'ecg', 'emg', 'ref_meg', 'stim', 'resp', 'misc',
-                 'chpi', 'syst', 'ias', 'exci']
+        order = ['eeg', 'seeg', 'ecog', 'eog', 'ecg', 'emg', 'ref_meg', 'stim',
+                 'resp', 'misc', 'chpi', 'syst', 'ias', 'exci']
     for ch_type in order:
         pick_kwargs[ch_type] = True
         idxs = pick_types(params['info'], **pick_kwargs)
@@ -564,7 +616,7 @@ def _prepare_mne_browse_epochs(params, projs, n_channels, n_epochs, scalings,
     ax_vscroll = plt.subplot2grid((10, 15), (0, 14), rowspan=9)
     ax_vscroll.set_axis_off()
     ax_vscroll.add_patch(mpl.patches.Rectangle((0, 0), 1, len(picks),
-                                               facecolor='w', zorder=2))
+                                               facecolor='w', zorder=3))
 
     ax_help_button = plt.subplot2grid((10, 15), (9, 0), colspan=1)
     help_button = mpl.widgets.Button(ax_help_button, 'Help')
@@ -579,10 +631,10 @@ def _prepare_mne_browse_epochs(params, projs, n_channels, n_epochs, scalings,
         ax_vscroll.add_patch(mpl.patches.Rectangle((0, ci), 1, 1,
                                                    facecolor=this_color,
                                                    edgecolor=this_color,
-                                                   zorder=3))
+                                                   zorder=4))
 
     vsel_patch = mpl.patches.Rectangle((0, 0), 1, n_channels, alpha=0.5,
-                                       edgecolor='w', facecolor='w', zorder=4)
+                                       edgecolor='w', facecolor='w', zorder=5)
     ax_vscroll.add_patch(vsel_patch)
 
     ax_vscroll.set_ylim(len(types), 0)
@@ -600,7 +652,7 @@ def _prepare_mne_browse_epochs(params, projs, n_channels, n_epochs, scalings,
         if len(colors) - 1 < ch_idx:
             break
         lc = LineCollection(list(), antialiased=False, linewidths=0.5,
-                            zorder=2, picker=3.)
+                            zorder=3, picker=3.)
         ax.add_collection(lc)
         lines.append(lc)
 
@@ -646,7 +698,7 @@ def _prepare_mne_browse_epochs(params, projs, n_channels, n_epochs, scalings,
                                        facecolor=(0.75, 0.75, 0.75),
                                        alpha=0.25, linewidth=1, clip_on=False)
     ax_hscroll.add_patch(hsel_patch)
-    text = ax.text(0, 0, 'blank', zorder=2, verticalalignment='baseline',
+    text = ax.text(0, 0, 'blank', zorder=3, verticalalignment='baseline',
                    ha='left', fontweight='bold')
     text.set_visible(False)
 
@@ -800,10 +852,10 @@ def _plot_traces(params):
                     if bad_idx < start_idx or bad_idx > end_idx:
                         continue
                     this_color[bad_idx - start_idx] = (1., 0., 0.)
-                lines[line_idx].set_zorder(1)
+                lines[line_idx].set_zorder(2)
             else:
                 this_color = params['colors'][ch_idx][start_idx:end_idx]
-                lines[line_idx].set_zorder(2)
+                lines[line_idx].set_zorder(3)
                 if not butterfly:
                     ylabels[line_idx].set_color('black')
             lines[line_idx].set_segments(segments)
@@ -897,7 +949,7 @@ def _handle_picks(epochs):
                            exclude=[])
     else:
         picks = pick_types(epochs.info, meg=True, eeg=True, eog=True, ecg=True,
-                           ref_meg=False, exclude=[])
+                           seeg=True, ecog=True, ref_meg=False, exclude=[])
     return picks
 
 
@@ -929,10 +981,10 @@ def _plot_vert_lines(params):
             for event_idx in range(len(epochs.events)):
                 pos = [event_idx * len(epochs.times) + t_zero[0],
                        event_idx * len(epochs.times) + t_zero[0]]
-                ax.plot(pos, ax.get_ylim(), 'g', zorder=3, alpha=0.4)
+                ax.plot(pos, ax.get_ylim(), 'g', zorder=4, alpha=0.4)
     for epoch_idx in range(len(epochs.events)):
         pos = [epoch_idx * len(epochs.times), epoch_idx * len(epochs.times)]
-        ax.plot(pos, ax.get_ylim(), color='black', linestyle='--', zorder=1)
+        ax.plot(pos, ax.get_ylim(), color='black', linestyle='--', zorder=2)
 
 
 def _pick_bad_epochs(event, params):
@@ -955,13 +1007,13 @@ def _pick_bad_epochs(event, params):
         for ch_idx in range(len(params['ch_names'])):
             params['colors'][ch_idx][epoch_idx] = params['def_colors'][ch_idx]
         params['ax_hscroll'].patches[epoch_idx].set_color('w')
-        params['ax_hscroll'].patches[epoch_idx].set_zorder(1)
+        params['ax_hscroll'].patches[epoch_idx].set_zorder(2)
         params['plot_fun']()
         return
     # add bad epoch
     params['bads'] = np.append(params['bads'], epoch_idx)
     params['ax_hscroll'].patches[epoch_idx].set_color((1., 0., 0., 1.))
-    params['ax_hscroll'].patches[epoch_idx].set_zorder(2)
+    params['ax_hscroll'].patches[epoch_idx].set_zorder(3)
     params['ax_hscroll'].patches[epoch_idx].set_edgecolor('w')
     for ch_idx in range(len(params['ch_names'])):
         params['colors'][ch_idx][epoch_idx] = (1., 0., 0., 1.)
@@ -1033,7 +1085,12 @@ def _mouse_click(event, params):
         if event.inaxes == params['ax_vscroll']:
             if params['butterfly']:
                 return
-            ch_start = max(int(event.ydata) - params['n_channels'] // 2, 0)
+            # Don't let scrollbar go outside vertical scrollbar limits
+            # XXX: floating point exception on some machines if this happens.
+            ch_start = min(
+                max(int(event.ydata) - params['n_channels'] // 2, 0),
+                len(params['ch_names']) - params['n_channels'])
+
             if params['ch_start'] != ch_start:
                 params['ch_start'] = ch_start
                 params['plot_fun']()
@@ -1069,7 +1126,7 @@ def _mouse_click(event, params):
         for epoch_idx in range(params['n_epochs']):  # plot lines
             pos = [epoch_idx * n_times + xdata, epoch_idx * n_times + xdata]
             params['vert_lines'].append(params['ax'].plot(pos, ylim, 'y',
-                                                          zorder=4))
+                                                          zorder=5))
         params['vertline_t'].set_text('%0.3f' % params['epochs'].times[xdata])
         params['plot_fun']()
 
@@ -1135,7 +1192,7 @@ def _plot_onkey(event, params):
         params['offsets'] = np.arange(n_channels) * offset + (offset / 2.)
         params['n_channels'] = n_channels
         lc = LineCollection(list(), antialiased=False, linewidths=0.5,
-                            zorder=2, picker=3.)
+                            zorder=3, picker=3.)
         params['ax'].add_collection(lc)
         params['ax'].set_yticks(params['offsets'])
         params['lines'].append(lc)
@@ -1165,7 +1222,7 @@ def _plot_onkey(event, params):
             ax = params['ax']
             pos = params['vert_lines'][0][0].get_data()[0] + params['duration']
             params['vert_lines'].append(ax.plot(pos, ax.get_ylim(), 'y',
-                                                zorder=3))
+                                                zorder=4))
         params['duration'] += n_times
         if params['t_start'] + params['duration'] > len(params['times']):
             params['t_start'] -= n_times
@@ -1254,7 +1311,7 @@ def _prepare_butterfly(params):
 
         while len(params['lines']) < len(params['picks']):
             lc = LineCollection(list(), antialiased=False, linewidths=0.5,
-                                zorder=2, picker=3.)
+                                zorder=3, picker=3.)
             ax.add_collection(lc)
             params['lines'].append(lc)
     else:  # change back to default view
@@ -1293,7 +1350,7 @@ def _onpick(event, params):
 
 def _close_event(event, params):
     """Function to drop selected bad epochs. Called on closing of the plot."""
-    params['epochs'].drop_epochs(params['bads'])
+    params['epochs'].drop(params['bads'])
     params['epochs'].info['bads'] = params['info']['bads']
     logger.info('Channels marked as bad: %s' % params['epochs'].info['bads'])
 
@@ -1317,7 +1374,7 @@ def _update_channels_epochs(event, params):
         params['lines'].pop()
     while len(params['lines']) < n_channels:
         lc = LineCollection(list(), linewidths=0.5, antialiased=False,
-                            zorder=2, picker=3.)
+                            zorder=3, picker=3.)
         params['ax'].add_collection(lc)
         params['lines'].append(lc)
     params['ax'].set_yticks(params['offsets'])
diff --git a/mne/viz/evoked.py b/mne/viz/evoked.py
index 128ba10..dec4e84 100644
--- a/mne/viz/evoked.py
+++ b/mne/viz/evoked.py
@@ -13,17 +13,18 @@ from __future__ import print_function
 
 import numpy as np
 
-from ..io.pick import channel_type, pick_types, _picks_by_type
+from ..io.pick import (channel_type, pick_types, _picks_by_type,
+                       _pick_data_channels)
 from ..externals.six import string_types
 from ..defaults import _handle_default
 from .utils import (_draw_proj_checkbox, tight_layout, _check_delayed_ssp,
-                    plt_show)
-from ..utils import logger, _clean_names
+                    plt_show, _process_times)
+from ..utils import logger, _clean_names, warn
 from ..fixes import partial
 from ..io.pick import pick_info
 from .topo import _plot_evoked_topo
 from .topomap import (_prepare_topo_plot, plot_topomap, _check_outlines,
-                      _prepare_topomap)
+                      _draw_outlines, _prepare_topomap, _topomap_animation)
 from ..channels import find_layout
 
 
@@ -31,7 +32,11 @@ def _butterfly_onpick(event, params):
     """Helper to add a channel name on click"""
     params['need_draw'] = True
     ax = event.artist.get_axes()
-    ax_idx = np.where([ax is a for a in params['axes']])[0][0]
+    ax_idx = np.where([ax is a for a in params['axes']])[0]
+    if len(ax_idx) == 0:  # this can happen if ax param is used
+        return  # let the other axes handle it
+    else:
+        ax_idx = ax_idx[0]
     lidx = np.where([l is event.artist for l in params['lines'][ax_idx]])[0][0]
     ch_name = params['ch_names'][params['idxs'][ax_idx][lidx]]
     text = params['texts'][ax_idx]
@@ -65,6 +70,7 @@ def _butterfly_on_button_press(event, params):
 def _butterfly_onselect(xmin, xmax, ch_types, evoked, text=None):
     """Function for drawing topomaps from the selected area."""
     import matplotlib.pyplot as plt
+    ch_types = [type for type in ch_types if type in ('eeg', 'grad', 'mag')]
     vert_lines = list()
     if text is not None:
         text.set_visible(True)
@@ -97,7 +103,7 @@ def _butterfly_onselect(xmin, xmax, ch_types, evoked, text=None):
             title = ch_type
         data = np.average(data, axis=1)
         axarr[0][idx].set_title(title)
-        plot_topomap(data, pos, axis=axarr[0][idx], show=False)
+        plot_topomap(data, pos, axes=axarr[0][idx], show=False)
 
     fig.suptitle('Average over %.2fs - %.2fs' % (xmin, xmax), fontsize=15,
                  y=0.1)
@@ -120,42 +126,41 @@ def _topo_closed(events, ax, lines, fill):
     ax.get_figure().canvas.draw()
 
 
-def _rgb(x, y, z):
+def _rgb(info, x, y, z):
     """Helper to transform x, y, z values into RGB colors"""
-    for dim in (x, y, z):
-        dim -= dim.min()
-        dim /= dim.max()
+    all_pos = np.array([ch['loc'][:3] for ch in info['chs']])
+    for idx, dim in enumerate([x, y, z]):
+        this_pos = all_pos[:, idx]
+        dim_min = this_pos.min()
+        dim_max = (this_pos - dim_min).max()
+        dim -= dim_min
+        dim /= dim_max
     return np.asarray([x, y, z]).T
 
 
-def _plot_legend(pos, colors, axis, bads, outlines='skirt'):
+def _plot_legend(pos, colors, axis, bads, outlines):
     """Helper function to plot color/channel legends for butterfly plots
     with spatial colors"""
     from mpl_toolkits.axes_grid.inset_locator import inset_axes
     bbox = axis.get_window_extent()  # Determine the correct size.
     ratio = bbox.width / bbox.height
     ax = inset_axes(axis, width=str(30 / ratio) + '%', height='30%', loc=2)
-    pos, outlines = _check_outlines(pos, outlines, None)
     pos_x, pos_y = _prepare_topomap(pos, ax)
-    ax.scatter(pos_x, pos_y, color=colors, s=25, marker='.', zorder=0)
+    ax.scatter(pos_x, pos_y, color=colors, s=25, marker='.', zorder=1)
     for idx in bads:
         ax.scatter(pos_x[idx], pos_y[idx], s=5, marker='.', color='w',
                    zorder=1)
 
     if isinstance(outlines, dict):
-        outlines_ = dict([(k, v) for k, v in outlines.items() if k not in
-                          ['patch', 'autoshrink']])
-        for k, (x, y) in outlines_.items():
-            if 'mask' in k:
-                continue
-            ax.plot(x, y, color='k', linewidth=1)
+        _draw_outlines(ax, outlines)
 
 
 def _plot_evoked(evoked, picks, exclude, unit, show,
                  ylim, proj, xlim, hline, units,
                  scalings, titles, axes, plot_type,
                  cmap=None, gfp=False, window_title=None,
-                 spatial_colors=False):
+                 spatial_colors=False, set_tight_layout=True,
+                 selectable=True):
     """Aux function for plot_evoked and plot_evoked_image (cf. docstrings)
 
     Extra param is:
@@ -181,7 +186,8 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
     titles = _handle_default('titles', titles)
     units = _handle_default('units', units)
     # Valid data types ordered for consistency
-    channel_types = ['eeg', 'grad', 'mag', 'seeg']
+    valid_channel_types = ['eeg', 'grad', 'mag', 'seeg', 'eog', 'ecg', 'emg',
+                           'dipole', 'gof', 'bio', 'ecog']
 
     if picks is None:
         picks = list(range(info['nchan']))
@@ -204,12 +210,12 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
     types = np.array([channel_type(info, idx) for idx in picks])
     n_channel_types = 0
     ch_types_used = []
-    for t in channel_types:
+    for t in valid_channel_types:
         if t in types:
             n_channel_types += 1
             ch_types_used.append(t)
 
-    axes_init = axes  # remember if axes where given as input
+    axes_init = axes  # remember if axes were given as input
 
     fig = None
     if axes is None:
@@ -227,7 +233,8 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
 
     if not len(axes) == n_channel_types:
         raise ValueError('Number of axes (%g) must match number of channel '
-                         'types (%g)' % (len(axes), n_channel_types))
+                         'types (%d: %s)' % (len(axes), n_channel_types,
+                                             sorted(ch_types_used)))
 
     # instead of projecting during each iteration let's use the mixin here.
     if proj is True and evoked.proj is not True:
@@ -259,17 +266,17 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
             if plot_type == 'butterfly':
                 text = ax.annotate('Loading...', xy=(0.01, 0.1),
                                    xycoords='axes fraction', fontsize=20,
-                                   color='green', zorder=2)
+                                   color='green', zorder=3)
                 text.set_visible(False)
-                callback_onselect = partial(_butterfly_onselect,
-                                            ch_types=ch_types_used,
-                                            evoked=evoked, text=text)
-                blit = False if plt.get_backend() == 'MacOSX' else True
-                selectors.append(SpanSelector(ax, callback_onselect,
-                                              'horizontal', minspan=10,
-                                              useblit=blit,
-                                              rectprops=dict(alpha=0.5,
-                                                             facecolor='red')))
+                if selectable:
+                    callback_onselect = partial(
+                        _butterfly_onselect, ch_types=ch_types_used,
+                        evoked=evoked, text=text)
+                    blit = False if plt.get_backend() == 'MacOSX' else True
+                    selectors.append(SpanSelector(
+                        ax, callback_onselect, 'horizontal', minspan=10,
+                        useblit=blit, rectprops=dict(alpha=0.5,
+                                                     facecolor='red')))
 
                 gfp_only = (isinstance(gfp, string_types) and gfp == 'only')
                 if not gfp_only:
@@ -277,23 +284,28 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
                         chs = [info['chs'][i] for i in idx]
                         locs3d = np.array([ch['loc'][:3] for ch in chs])
                         x, y, z = locs3d.T
-                        colors = _rgb(x, y, z)
-                        layout = find_layout(info, ch_type=t, exclude=[])
+                        colors = _rgb(info, x, y, z)
+                        if t in ('meg', 'mag', 'grad', 'eeg'):
+                            layout = find_layout(info, ch_type=t, exclude=[])
+                        else:
+                            layout = find_layout(info, None, exclude=[])
                         # drop channels that are not in the data
+
                         used_nm = np.array(_clean_names(info['ch_names']))[idx]
-                        names = np.asarray([name for name in layout.names
-                                            if name in used_nm])
+                        names = np.asarray([name for name in used_nm
+                                            if name in layout.names])
                         name_idx = [layout.names.index(name) for name in names]
                         if len(name_idx) < len(chs):
-                            logger.warning('Could not find layout for '
-                                           'all the channels. Legend for '
-                                           'spatial colors not drawn.')
+                            warn('Could not find layout for all the channels. '
+                                 'Legend for spatial colors not drawn.')
                         else:
                             # find indices for bads
                             bads = [np.where(names == bad)[0][0] for bad in
                                     info['bads'] if bad in names]
-                            pos = layout.pos[name_idx, :2]
-                            _plot_legend(pos, colors, ax, bads=bads)
+                            pos, outlines = _check_outlines(layout.pos[:, :2],
+                                                            'skirt', None)
+                            pos = pos[name_idx]
+                            _plot_legend(pos, colors, ax, bads, outlines)
                     else:
                         colors = ['k'] * len(idx)
                         for i in bad_ch_idx:
@@ -301,7 +313,7 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
                                 colors[idx.index(i)] = 'r'
                     for ch_idx in range(len(D)):
                         line_list.append(ax.plot(times, D[ch_idx], picker=3.,
-                                                 zorder=0,
+                                                 zorder=1,
                                                  color=colors[ch_idx])[0])
                 if gfp:  # 'only' or boolean True
                     gfp_color = 3 * (0.,) if spatial_colors else (0., 1., 0.)
@@ -314,23 +326,23 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
                         y_offset = 0.
                     this_gfp += y_offset
                     ax.fill_between(times, y_offset, this_gfp, color='none',
-                                    facecolor=gfp_color, zorder=0, alpha=0.25)
+                                    facecolor=gfp_color, zorder=1, alpha=0.25)
                     line_list.append(ax.plot(times, this_gfp, color=gfp_color,
-                                             zorder=2)[0])
+                                             zorder=3)[0])
                     ax.text(times[0] + 0.01 * (times[-1] - times[0]),
                             this_gfp[0] + 0.05 * np.diff(ax.get_ylim())[0],
-                            'GFP', zorder=3, color=gfp_color,
+                            'GFP', zorder=4, color=gfp_color,
                             path_effects=gfp_path_effects)
                 for ii, line in zip(idx, line_list):
                     if ii in bad_ch_idx:
-                        line.set_zorder(1)
+                        line.set_zorder(2)
                         if spatial_colors:
                             line.set_linestyle("--")
                 ax.set_ylabel('data (%s)' % ch_unit)
                 # for old matplotlib, we actually need this to have a bounding
                 # box (!), so we have to put some valid text here, change
                 # alpha and path effects later
-                texts.append(ax.text(0, 0, 'blank', zorder=2,
+                texts.append(ax.text(0, 0, 'blank', zorder=3,
                                      verticalalignment='baseline',
                                      horizontalalignment='left',
                                      fontweight='bold', alpha=0))
@@ -386,7 +398,8 @@ def _plot_evoked(evoked, picks, exclude, unit, show,
 
     plt_show(show)
     fig.canvas.draw()  # for axes plots update axes.
-    tight_layout(fig=fig)
+    if set_tight_layout:
+        tight_layout(fig=fig)
 
     return fig
 
@@ -395,7 +408,7 @@ def plot_evoked(evoked, picks=None, exclude='bads', unit=True, show=True,
                 ylim=None, xlim='tight', proj=False, hline=None, units=None,
                 scalings=None, titles=None, axes=None, gfp=False,
                 window_title=None, spatial_colors=False):
-    """Plot evoked data
+    """Plot evoked data using butteryfly plots
 
     Left click to a line shows the channel name. Selecting an area by clicking
     and holding left mouse button plots a topographic map of the painted area.
@@ -416,7 +429,8 @@ def plot_evoked(evoked, picks=None, exclude='bads', unit=True, show=True,
     show : bool
         Show figure if True.
     ylim : dict | None
-        ylim for plots. e.g. ylim = dict(eeg=[-200e-6, 200e6])
+        ylim for plots (after scaling has been applied). e.g.
+        ylim = dict(eeg=[-20, 20])
         Valid keys are eeg, mag, grad, misc. If None, the ylim parameter
         for each channel equals the pyplot default.
     xlim : 'tight' | tuple | None
@@ -450,20 +464,26 @@ def plot_evoked(evoked, picks=None, exclude='bads', unit=True, show=True,
         coordinates into color values. Spatially similar channels will have
         similar colors. Bad channels will be dotted. If False, the good
         channels are plotted black and bad channels red. Defaults to False.
+
+
+    Returns
+    -------
+    fig : instance of matplotlib.figure.Figure
+        Figure containing the butterfly plots.
     """
     return _plot_evoked(evoked=evoked, picks=picks, exclude=exclude, unit=unit,
                         show=show, ylim=ylim, proj=proj, xlim=xlim,
                         hline=hline, units=units, scalings=scalings,
                         titles=titles, axes=axes, plot_type="butterfly",
                         gfp=gfp, window_title=window_title,
-                        spatial_colors=spatial_colors)
+                        spatial_colors=spatial_colors, selectable=True)
 
 
 def plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
                      border='none', ylim=None, scalings=None, title=None,
                      proj=False, vline=[0.0], fig_facecolor='k',
                      fig_background=None, axis_facecolor='k', font_color='w',
-                     show=True):
+                     merge_grads=False, show=True):
     """Plot 2D topography of evoked responses.
 
     Clicking on the plot of an individual sensor opens a new figure showing
@@ -487,10 +507,11 @@ def plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
     border : str
         matplotlib borders style to be used for each sensor plot.
     ylim : dict | None
-        ylim for plots. The value determines the upper and lower subplot
-        limits. e.g. ylim = dict(eeg=[-200e-6, 200e6]). Valid keys are eeg,
-        mag, grad, misc. If None, the ylim parameter for each channel is
-        determined by the maximum absolute peak.
+        ylim for plots (after scaling has been applied). The value
+        determines the upper and lower subplot limits. e.g.
+        ylim = dict(eeg=[-20, 20]). Valid keys are eeg, mag, grad, misc.
+        If None, the ylim parameter for each channel is determined by
+        the maximum absolute peak.
     scalings : dict | None
         The scalings of the channel types to be applied for plotting. If None,`
         defaults to `dict(eeg=1e6, grad=1e13, mag=1e15)`.
@@ -511,12 +532,15 @@ def plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
         The face color to be used for each sensor plot. Defaults to black.
     font_color : str | obj
         The color of text in the colorbar and title. Defaults to white.
+    merge_grads : bool
+        Whether to use RMS value of gradiometer pairs. Only works for Neuromag
+        data. Defaults to False.
     show : bool
         Show figure if True.
 
     Returns
     -------
-    fig : Instance of matplotlib.figure.Figure
+    fig : instance of matplotlib.figure.Figure
         Images of evoked responses at sensor locations
     """
     return _plot_evoked_topo(evoked=evoked, layout=layout,
@@ -526,7 +550,54 @@ def plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
                              fig_facecolor=fig_facecolor,
                              fig_background=fig_background,
                              axis_facecolor=axis_facecolor,
-                             font_color=font_color, show=show)
+                             font_color=font_color, merge_grads=merge_grads,
+                             show=show)
+
+
+def _animate_evoked_topomap(evoked, ch_type='mag', times=None, frame_rate=None,
+                            butterfly=False, blit=True, show=True):
+    """Make animation of evoked data as topomap timeseries. Animation can be
+    paused/resumed with left mouse button. Left and right arrow keys can be
+    used to move backward or forward in time
+
+    Parameters
+    ----------
+    evoked : instance of Evoked
+        The evoked data.
+    ch_type : str | None
+        Channel type to plot. Accepted data types: 'mag', 'grad', 'eeg'.
+        If None, first available channel type from ('mag', 'grad', 'eeg') is
+        used. Defaults to None.
+    times : array of floats | None
+        The time points to plot. If None, 10 evenly spaced samples are
+        calculated over the evoked time series. Defaults to None.
+    frame_rate : int | None
+        Frame rate for the animation in Hz. If None, frame rate = sfreq / 10.
+        Defaults to None.
+    butterfly : bool
+        Whether to plot the data as butterfly plot under the topomap.
+        Defaults to False.
+    blit : bool
+        Whether to use blit to optimize drawing. In general, it is recommended
+        to use blit in combination with ``show=True``. If you intend to save
+        the animation it is better to disable blit. Defaults to True.
+    show : bool
+        Whether to show the animation. Defaults to True.
+
+    Returns
+    -------
+    fig : instance of matplotlib figure
+        The figure.
+    anim : instance of matplotlib FuncAnimation
+        Animation of the topomap.
+
+    Notes
+    -----
+    .. versionadded:: 0.12.0
+    """
+    return _topomap_animation(evoked, ch_type=ch_type, times=times,
+                              frame_rate=frame_rate, butterfly=butterfly,
+                              blit=blit, show=show)
 
 
 def plot_evoked_image(evoked, picks=None, exclude='bads', unit=True, show=True,
@@ -548,7 +619,8 @@ def plot_evoked_image(evoked, picks=None, exclude='bads', unit=True, show=True,
     show : bool
         Show figure if True.
     clim : dict | None
-        clim for plots. e.g. clim = dict(eeg=[-200e-6, 200e6])
+        clim for plots (after scaling has been applied). e.g.
+        clim = dict(eeg=[-20, 20])
         Valid keys are eeg, mag, grad, misc. If None, the clim parameter
         for each channel equals the pyplot default.
     xlim : 'tight' | tuple | None
@@ -572,6 +644,11 @@ def plot_evoked_image(evoked, picks=None, exclude='bads', unit=True, show=True,
         Axes, there must be only one channel type plotted.
     cmap : matplotlib colormap
         Colormap.
+
+    Returns
+    -------
+    fig : instance of matplotlib.figure.Figure
+        Figure containing the images.
     """
     return _plot_evoked(evoked=evoked, picks=picks, exclude=exclude, unit=unit,
                         show=show, ylim=clim, proj=proj, xlim=xlim,
@@ -608,7 +685,7 @@ def plot_evoked_white(evoked, noise_cov, show=True):
     """Plot whitened evoked response
 
     Plots the whitened evoked response and the whitened GFP as described in
-    [1]. If one single covariance object is passed, the GFP panel (bottom)
+    [1]_. If one single covariance object is passed, the GFP panel (bottom)
     will depict different sensor types. If multiple covariance objects are
     passed as a list, the left column will display the whitened evoked
     responses for each channel based on the whitener from the noise covariance
@@ -634,9 +711,9 @@ def plot_evoked_white(evoked, noise_cov, show=True):
 
     References
     ----------
-    [1] Engemann D. and Gramfort A. (2015) Automated model selection in
-        covariance estimation and spatial whitening of MEG and EEG signals,
-        vol. 108, 328-342, NeuroImage.
+    .. [1] Engemann D. and Gramfort A. (2015) Automated model selection in
+           covariance estimation and spatial whitening of MEG and EEG
+           signals, vol. 108, 328-342, NeuroImage.
     """
     return _plot_evoked_white(evoked=evoked, noise_cov=noise_cov,
                               scalings=None, rank=None, show=show)
@@ -645,12 +722,12 @@ def plot_evoked_white(evoked, noise_cov, show=True):
 def _plot_evoked_white(evoked, noise_cov, scalings=None, rank=None, show=True):
     """helper to plot_evoked_white
 
-    Additional Paramter
-    -------------------
+    Additional Parameters
+    ---------------------
     scalings : dict | None
         The rescaling method to be applied to improve the accuracy of rank
         estimaiton. If dict, it will override the following default values
-        (used if None):
+        (used if None)::
 
             dict(mag=1e12, grad=1e11, eeg=1e5)
 
@@ -695,7 +772,7 @@ def _plot_evoked_white(evoked, noise_cov, scalings=None, rank=None, show=True):
 
     picks = pick_types(evoked.info, meg=True, eeg=True, ref_meg=False,
                        exclude='bads')
-    evoked.pick_channels([evoked.ch_names[k] for k in picks], copy=False)
+    evoked.pick_channels([evoked.ch_names[k] for k in picks])
     # important to re-pick. will otherwise crash on systems with ref channels
     # as first sensor block
     picks = pick_types(evoked.info, meg=True, eeg=True, ref_meg=False,
@@ -863,7 +940,7 @@ def plot_snr_estimate(evoked, inv, show=True):
     lims = np.concatenate([evoked.times[[0, -1]], [-1, snr_est.max()]])
     ax.plot([0, 0], lims[2:], 'k:')
     ax.plot(lims[:2], [0, 0], 'k:')
-    # Colors are "bluish green" and "vermillion" taken from:
+    # Colors are "bluish green" and "vermilion" taken from:
     #  http://bconnelly.net/2013/10/creating-colorblind-friendly-figures/
     ax.plot(evoked.times, snr_est, color=[0.0, 0.6, 0.5])
     ax.plot(evoked.times, snr, color=[0.8, 0.4, 0.0])
@@ -876,3 +953,184 @@ def plot_snr_estimate(evoked, inv, show=True):
     plt.draw()
     plt_show(show)
     return fig
+
+
+def _connection_line(x, fig, sourceax, targetax):
+    """Helper function to connect time series and topolots"""
+    from matplotlib.lines import Line2D
+    transFigure = fig.transFigure.inverted()
+    tf = fig.transFigure
+
+    (xt, yt) = transFigure.transform(targetax.transAxes.transform([.5, .25]))
+    (xs, _) = transFigure.transform(sourceax.transData.transform([x, 0]))
+    (_, ys) = transFigure.transform(sourceax.transAxes.transform([0, 1]))
+    return Line2D((xt, xs), (yt, ys), transform=tf, color='grey',
+                  linestyle='-', linewidth=1.5, alpha=.66, zorder=0)
+
+
+def plot_evoked_joint(evoked, times="peaks", title='', picks=None,
+                      exclude=None,
+                      show=True, ts_args=None, topomap_args=None):
+    """Plot evoked data as butterfly plot and add topomaps for selected
+    time points.
+
+    Parameters
+    ----------
+    evoked : instance of Evoked
+        The evoked instance.
+    times : float | array of floats | "auto" | "peaks".
+        The time point(s) to plot. If "auto", 5 evenly spaced topographies
+        between the first and last time instant will be shown. If "peaks",
+        finds time points automatically by checking for 3 local maxima in
+        Global Field Power. Defaults to "peaks".
+    title : str | None
+        The title. If `None`, suppress printing channel type. Defaults to ''.
+    picks : array-like of int | None
+        The indices of channels to plot. If None show all. Defaults to None.
+    exclude : None | list of str | 'bads'
+        Channels names to exclude from being shown. If 'bads', the
+        bad channels are excluded. Defaults to None.
+    show : bool
+        Show figure if True. Defaults to True.
+    ts_args : None | dict
+        A dict of `kwargs` that are forwarded to `evoked.plot` to
+        style the butterfly plot. `axes` and `show` are ignored.
+        If `spatial_colors` is not in this dict, `spatial_colors=True`
+        will be passed. Beyond that, if ``None``, no customizable arguments
+        will be passed. Defaults to ``None``.
+    topomap_args : None | dict
+        A dict of `kwargs` that are forwarded to `evoked.plot_topomap`
+        to style the topomaps. `axes` and `show` are ignored. If `times`
+        is not in this dict, automatic peak detection is used. Beyond that,
+        if ``None`, no customizable arguments will be passed.
+        Defaults to ``None``.
+
+    Returns
+    -------
+    fig : instance of matplotlib.figure.Figure | list
+        The figure object containing the plot. If `evoked` has multiple
+        channel types, a list of figures, one for each channel type, is
+        returned.
+
+    Notes
+    -----
+    .. versionadded:: 0.12.0
+    """
+    import matplotlib.pyplot as plt
+
+    if ts_args is None:
+        ts_args = dict()
+    if topomap_args is None:
+        topomap_args = dict()
+
+    # channel selection
+    # simply create a new evoked object(s) with the desired channel selection
+    evoked = evoked.copy()
+
+    if picks is not None:
+        pick_names = [evoked.info['ch_names'][pick] for pick in picks]
+    else:  # only pick channels that are plotted
+        picks = _pick_data_channels(evoked.info, exclude=[])
+        pick_names = [evoked.info['ch_names'][pick] for pick in picks]
+    evoked.pick_channels(pick_names)
+
+    if exclude == 'bads':
+        exclude = [ch for ch in evoked.info['bads']
+                   if ch in evoked.info['ch_names']]
+    if exclude is not None:
+        evoked.drop_channels(exclude)
+
+    info = evoked.info
+    data_types = ['eeg', 'grad', 'mag', 'seeg', 'ecog']
+    ch_types = set(ch_type for ch_type in data_types if ch_type in evoked)
+
+    # if multiple sensor types: one plot per channel type, recursive call
+    if len(ch_types) > 1:
+        figs = list()
+        for t in ch_types:  # pick only the corresponding channel type
+            ev_ = evoked.copy().pick_channels(
+                [info['ch_names'][idx] for idx in range(info['nchan'])
+                 if channel_type(info, idx) == t])
+            if len(set([channel_type(ev_.info, idx)
+                        for idx in range(ev_.info['nchan'])
+                        if channel_type(ev_.info, idx) in data_types])) > 1:
+                raise RuntimeError('Possibly infinite loop due to channel '
+                                   'selection problem. This should never '
+                                   'happen! Please check your channel types.')
+            figs.append(
+                plot_evoked_joint(
+                    ev_, times=times, title=title, show=show, ts_args=ts_args,
+                    exclude=list(), topomap_args=topomap_args))
+        return figs
+
+    fig = plt.figure(figsize=(8.0, 4.2))
+
+    # set up time points to show topomaps for
+    times = _process_times(evoked, times, few=True)
+
+    # butterfly/time series plot
+    # most of this code is about passing defaults on demand
+    ts_ax = fig.add_subplot(212)
+    ts_args_pass = dict((k, v) for k, v in ts_args.items() if k not in
+                        ['axes', 'show', 'colorbar', 'set_tight_layout'])
+    ts_args_def = dict(picks=None, unit=True, ylim=None, xlim='tight',
+                       proj=False, hline=None, units=None, scalings=None,
+                       titles=None, gfp=False, window_title=None,
+                       spatial_colors=True)
+    for key in ts_args_def:
+        if key not in ts_args:
+            ts_args_pass[key] = ts_args_def[key]
+    _plot_evoked(evoked, axes=ts_ax, show=False, plot_type='butterfly',
+                 exclude=[], set_tight_layout=False, **ts_args_pass)
+
+    # handle title
+    # we use a new axis for the title to handle scaling of plots
+    old_title = ts_ax.get_title()
+    ts_ax.set_title('')
+    if title is not None:
+        title_ax = plt.subplot(4, 3, 2)
+        title = ', '.join([title, old_title]) if len(title) > 0 else old_title
+        title_ax.text(.5, .5, title, transform=title_ax.transAxes,
+                      horizontalalignment='center',
+                      verticalalignment='center')
+        title_ax.axis('off')
+
+    # prepare axes for topomap
+    # slightly convoluted due to colorbar placement and for vertical alignment
+    ts = len(times) + 2
+    map_ax = [plt.subplot(4, ts, x + 2 + ts) for x in range(ts - 2)]
+    cbar_ax = plt.subplot(4, 3 * (ts + 1), 6 * (ts + 1))
+
+    # topomap
+    topomap_args_pass = dict((k, v) for k, v in topomap_args.items() if
+                             k not in ['times', 'axes', 'show', 'colorbar'])
+    topomap_args_pass['outlines'] = (topomap_args['outlines'] if 'outlines'
+                                     in topomap_args else 'skirt')
+    evoked.plot_topomap(times=times, axes=map_ax, show=False,
+                        colorbar=False, **topomap_args_pass)
+
+    if topomap_args.get('colorbar', True):
+        from matplotlib import ticker
+        cbar = plt.colorbar(map_ax[0].images[0], cax=cbar_ax)
+        cbar.locator = ticker.MaxNLocator(nbins=5)
+        cbar.update_ticks()
+
+    plt.subplots_adjust(left=.1, right=.93, bottom=.14,
+                        top=1. if title is not None else 1.2)
+
+    # connection lines
+    # draw the connection lines between time series and topoplots
+    tstimes = [timepoint * 1e3 for timepoint in times]
+    lines = [_connection_line(timepoint, fig, ts_ax, map_ax_)
+             for timepoint, map_ax_ in zip(tstimes, map_ax)]
+    for line in lines:
+        fig.lines.append(line)
+
+    # mark times in time series plot
+    for timepoint in tstimes:
+        ts_ax.axvline(timepoint, color='grey', linestyle='-',
+                      linewidth=1.5, alpha=.66, zorder=0)
+
+    # show and return it
+    plt_show(show)
+    return fig
diff --git a/mne/viz/ica.py b/mne/viz/ica.py
index ce3f527..5de65a0 100644
--- a/mne/viz/ica.py
+++ b/mne/viz/ica.py
@@ -18,8 +18,8 @@ from .utils import (tight_layout, _prepare_trellis, _select_bads,
 from .raw import _prepare_mne_browse_raw, _plot_raw_traces
 from .epochs import _prepare_mne_browse_epochs
 from .evoked import _butterfly_on_button_press, _butterfly_onpick
-from .topomap import _prepare_topo_plot, plot_topomap
-from ..utils import logger
+from .topomap import _prepare_topo_plot, plot_topomap, _hide_frame
+from ..utils import warn
 from ..defaults import _handle_default
 from ..io.meas_info import create_info
 from ..io.pick import pick_types
@@ -120,7 +120,7 @@ def plot_ica_sources(ica, inst, picks=None, exclude=None, start=None,
     elif isinstance(inst, Evoked):
         sources = ica.get_sources(inst)
         if start is not None or stop is not None:
-            inst = inst.crop(start, stop, copy=True)
+            inst = inst.copy().crop(start, stop)
         fig = _plot_ica_sources_evoked(
             evoked=sources, picks=picks, exclude=exclude, title=title,
             labels=getattr(ica, 'labels_', None), show=show)
@@ -274,10 +274,10 @@ def _plot_ica_sources_evoked(evoked, picks, exclude, title, show, labels=None):
             color = label_colors[key]
             # ... but display component number too
             lines.extend(ax.plot(times, evoked.data[ii].T, picker=3.,
-                         zorder=1, color=color, label=exc_label))
+                         zorder=2, color=color, label=exc_label))
         else:
             lines.extend(ax.plot(times, evoked.data[ii].T, picker=3.,
-                                 color='k', zorder=0))
+                                 color='k', zorder=1))
 
     ax.set_title(title)
     ax.set_xlim(times[[0, -1]])
@@ -290,7 +290,7 @@ def _plot_ica_sources_evoked(evoked, picks, exclude, title, show, labels=None):
     # for old matplotlib, we actually need this to have a bounding
     # box (!), so we have to put some valid text here, change
     # alpha and  path effects later
-    texts.append(ax.text(0, 0, 'blank', zorder=2,
+    texts.append(ax.text(0, 0, 'blank', zorder=3,
                          verticalalignment='baseline',
                          horizontalalignment='left',
                          fontweight='bold', alpha=0))
@@ -336,7 +336,7 @@ def plot_ica_scores(ica, scores,
     labels : str | list | 'ecg' | 'eog' | None
         The labels to consider for the axes tests. Defaults to None.
         If list, should match the outer shape of `scores`.
-        If 'ecg' or 'eog', the labels_ attributes will be looked up.
+        If 'ecg' or 'eog', the ``labels_`` attributes will be looked up.
         Note that '/' is used internally for sublabels specifying ECG and
         EOG channels.
     axhline : float
@@ -473,18 +473,18 @@ def plot_ica_overlay(ica, inst, exclude=None, picks=None, start=None,
         start_compare, stop_compare = _check_start_stop(inst, start, stop)
         data, times = inst[picks, start_compare:stop_compare]
 
-        raw_cln = ica.apply(inst, exclude=exclude, start=start, stop=stop,
-                            copy=True)
+        raw_cln = ica.apply(inst.copy(), exclude=exclude,
+                            start=start, stop=stop)
         data_cln, _ = raw_cln[picks, start_compare:stop_compare]
         fig = _plot_ica_overlay_raw(data=data, data_cln=data_cln,
                                     times=times * 1e3, title=title,
                                     ch_types_used=ch_types_used, show=show)
     elif isinstance(inst, Evoked):
         if start is not None and stop is not None:
-            inst = inst.crop(start, stop, copy=True)
+            inst = inst.copy().crop(start, stop)
         if picks is not None:
             inst.pick_channels([inst.ch_names[p] for p in picks])
-        evoked_cln = ica.apply(inst, exclude=exclude, copy=True)
+        evoked_cln = ica.apply(inst.copy(), exclude=exclude)
         fig = _plot_ica_overlay_evoked(evoked=inst, evoked_cln=evoked_cln,
                                        title=title, show=show)
 
@@ -804,7 +804,7 @@ def _label_clicked(pos, params):
                                                                     ch_type,
                                                                     None)
         except Exception as exc:
-            logger.warning(exc)
+            warn(exc)
             plt.close(fig)
             return
         this_data = data[:, data_picks]
@@ -814,10 +814,8 @@ def _label_clicked(pos, params):
         for ii, data_ in zip(ic_idx, this_data):
             ax.set_title('IC #%03d ' % ii + ch_type, fontsize=12)
             data_ = _merge_grad_data(data_) if merge_grads else data_
-            plot_topomap(data_.flatten(), pos, axis=ax, show=False)
-            ax.set_yticks([])
-            ax.set_xticks([])
-            ax.set_frame_on(False)
+            plot_topomap(data_.flatten(), pos, axes=ax, show=False)
+            _hide_frame(ax)
     tight_layout(fig=fig)
     fig.subplots_adjust(top=0.95)
     fig.canvas.draw()
diff --git a/mne/viz/misc.py b/mne/viz/misc.py
index 2b7b864..0c22881 100644
--- a/mne/viz/misc.py
+++ b/mne/viz/misc.py
@@ -12,17 +12,16 @@ from __future__ import print_function
 # License: Simplified BSD
 
 import copy
-import warnings
 from glob import glob
-import os.path as op
 from itertools import cycle
+import os.path as op
 
 import numpy as np
 from scipy import linalg
 
 from ..surface import read_surface
 from ..io.proj import make_projector
-from ..utils import logger, verbose, get_subjects_dir
+from ..utils import logger, verbose, get_subjects_dir, warn
 from ..io.pick import pick_types
 from .utils import tight_layout, COLORS, _prepare_trellis, plt_show
 
@@ -449,15 +448,15 @@ def plot_events(events, sfreq=None, first_samp=0, color=None, event_id=None,
 
         for this_event in unique_events:
             if this_event not in unique_events_id:
-                warnings.warn('event %s missing from event_id will be ignored.'
-                              % this_event)
+                warn('event %s missing from event_id will be ignored'
+                     % this_event)
     else:
         unique_events_id = unique_events
 
     if color is None:
         if len(unique_events) > len(COLORS):
-            warnings.warn('More events than colors available. '
-                          'You should pass a list of unique colors.')
+            warn('More events than colors available. You should pass a list '
+                 'of unique colors.')
         colors = cycle(COLORS)
         color = dict()
         for this_event, this_color in zip(unique_events_id, colors):
@@ -470,8 +469,8 @@ def plot_events(events, sfreq=None, first_samp=0, color=None, event_id=None,
 
         for this_event in unique_events_id:
             if this_event not in color:
-                warnings.warn('Color is not available for event %d. Default '
-                              'colors will be used.' % this_event)
+                warn('Color is not available for event %d. Default colors '
+                     'will be used.' % this_event)
 
     import matplotlib.pyplot as plt
 
diff --git a/mne/viz/raw.py b/mne/viz/raw.py
index 6bbda89..09724e6 100644
--- a/mne/viz/raw.py
+++ b/mne/viz/raw.py
@@ -13,16 +13,19 @@ from functools import partial
 import numpy as np
 
 from ..externals.six import string_types
-from ..io.pick import pick_types, _pick_data_channels
+from ..io.pick import (pick_types, _pick_data_channels, pick_info,
+                       _PICK_TYPES_KEYS)
 from ..io.proj import setup_proj
-from ..utils import verbose, get_config, logger
-from ..time_frequency import compute_raw_psd
-from .topo import _plot_topo, _plot_timeseries
+from ..utils import verbose, get_config
+from ..time_frequency import psd_welch
+from .topo import _plot_topo, _plot_timeseries, _plot_timeseries_unified
 from .utils import (_toggle_options, _toggle_proj, tight_layout,
                     _layout_figure, _plot_raw_onkey, figure_nobar,
                     _plot_raw_onscroll, _mouse_click, plt_show,
-                    _helper_raw_resize, _select_bads, _onclick_help)
+                    _helper_raw_resize, _select_bads, _onclick_help,
+                    _setup_browser_offsets, _compute_scalings)
 from ..defaults import _handle_default
+from ..annotations import _onset_to_seconds
 
 
 def _plot_update_raw_proj(params, bools):
@@ -59,7 +62,8 @@ def _update_raw_data(params):
         data[di] /= params['scalings'][params['types'][di]]
         # stim channels should be hard limited
         if params['types'][di] == 'stim':
-            data[di] = np.minimum(data[di], 1.0)
+            norm = float(max(data[di]))
+            data[di] /= norm if norm > 0 else 1.
     # clip
     if params['clipping'] == 'transparent':
         data[np.logical_or(data > 1, data < -1)] = np.nan
@@ -113,7 +117,11 @@ def plot_raw(raw, events=None, duration=10.0, start=0.0, n_channels=20,
         ``{event_number: color}`` pairings. Use ``event_number==-1`` for
         any event numbers in the events list that are not in the dictionary.
     scalings : dict | None
-        Scale factors for the traces. If None, defaults to::
+        Scaling factors for the traces. If any fields in scalings are 'auto',
+        the scaling factor is set to match the 99.5th percentile of a subset of
+        the corresponding data. If scalings == 'auto', all scalings fields are
+        set to 'auto'. If any fields are 'auto' and data is not preloaded, a
+        subset of times up to 100mb will be loaded. If None, defaults to::
 
             dict(mag=1e-12, grad=4e-11, eeg=20e-6, eog=150e-6, ecg=5e-4,
                  emg=1e-3, ref_meg=1e-12, misc=1e-3, stim=1,
@@ -173,6 +181,7 @@ def plot_raw(raw, events=None, duration=10.0, start=0.0, n_channels=20,
     import matplotlib as mpl
     from scipy.signal import butter
     color = _handle_default('color', color)
+    scalings = _compute_scalings(scalings, raw)
     scalings = _handle_default('scalings_plot_raw', scalings)
 
     if clipping is not None and clipping not in ('clamp', 'transparent'):
@@ -235,23 +244,12 @@ def plot_raw(raw, events=None, duration=10.0, start=0.0, n_channels=20,
         inds += [pick_types(info, meg=t, ref_meg=False, exclude=[])]
         types += [t] * len(inds[-1])
     pick_kwargs = dict(meg=False, ref_meg=False, exclude=[])
-    for t in ['eeg', 'seeg', 'eog', 'ecg', 'emg', 'ref_meg', 'stim', 'resp',
-              'misc', 'chpi', 'syst', 'ias', 'exci']:
-        pick_kwargs[t] = True
-        inds += [pick_types(raw.info, **pick_kwargs)]
-        if t == 'seeg' and len(inds[-1]) > 0:
-            # XXX hack to work around fiff mess
-            new_picks = [ind for ind in inds[-1] if
-                         not raw.ch_names[ind].startswith('CHPI')]
-            if len(new_picks) != len(inds[-1]):
-                inds[-1] = new_picks
-            else:
-                logger.warning('Conflicting FIFF constants detected. SEEG '
-                               'FIFF data saved before mne version 0.11 will '
-                               'not work with mne version 0.12! Save the raw '
-                               'files again to fix the FIFF tags!')
-        types += [t] * len(inds[-1])
-        pick_kwargs[t] = False
+    for key in _PICK_TYPES_KEYS:
+        if key != 'meg':
+            pick_kwargs[key] = True
+            inds += [pick_types(raw.info, **pick_kwargs)]
+            types += [key] * len(inds[-1])
+            pick_kwargs[key] = False
     inds = np.concatenate(inds).astype(int)
     if not len(inds) == len(info['ch_names']):
         raise RuntimeError('Some channels not classified, please report '
@@ -299,10 +297,38 @@ def plot_raw(raw, events=None, duration=10.0, start=0.0, n_channels=20,
     # plot event_line first so it's in the back
     event_lines = [params['ax'].plot([np.nan], color=event_color[ev_num])[0]
                    for ev_num in sorted(event_color.keys())]
+
     params['plot_fun'] = partial(_plot_raw_traces, params=params, inds=inds,
                                  color=color, bad_color=bad_color,
                                  event_lines=event_lines,
                                  event_color=event_color)
+
+    if raw.annotations is not None:
+        segments = list()
+        segment_colors = dict()
+        # sort the segments by start time
+        order = raw.annotations.onset.argsort(axis=0)
+        descriptions = raw.annotations.description[order]
+        color_keys = set(descriptions)
+        color_vals = np.linspace(0, 1, len(color_keys))
+        for idx, key in enumerate(color_keys):
+            if key.lower().startswith('bad'):
+                segment_colors[key] = 'red'
+            else:
+                segment_colors[key] = plt.cm.summer(color_vals[idx])
+        params['segment_colors'] = segment_colors
+        for idx, onset in enumerate(raw.annotations.onset[order]):
+            annot_start = _onset_to_seconds(raw, onset)
+            annot_end = annot_start + raw.annotations.duration[order][idx]
+            segments.append([annot_start, annot_end])
+            ylim = params['ax_hscroll'].get_ylim()
+            dscr = descriptions[idx]
+            params['ax_hscroll'].fill_betweenx(ylim, annot_start, annot_end,
+                                               alpha=0.3,
+                                               color=segment_colors[dscr])
+        params['segments'] = np.array(segments)
+        params['annot_description'] = descriptions
+
     params['update_fun'] = partial(_update_raw_data, params=params)
     params['pick_bads_fun'] = partial(_pick_bad_channels, params=params)
     params['label_click_fun'] = partial(_label_clicked, params=params)
@@ -380,21 +406,29 @@ def _set_psd_plot_params(info, proj, picks, ax, area_mode):
     if area_mode not in [None, 'std', 'range']:
         raise ValueError('"area_mode" must be "std", "range", or None')
     if picks is None:
-        if ax is not None:
-            raise ValueError('If "ax" is not supplied (None), then "picks" '
-                             'must also be supplied')
-        megs = ['mag', 'grad', False]
-        eegs = [False, False, True]
-        names = ['Magnetometers', 'Gradiometers', 'EEG']
+        megs = ['mag', 'grad', False, False, False]
+        eegs = [False, False, True, False, False]
+        seegs = [False, False, False, True, False]
+        ecogs = [False, False, False, False, True]
+        names = ['Magnetometers', 'Gradiometers', 'EEG', 'SEEG', 'ECoG']
         picks_list = list()
         titles_list = list()
-        for meg, eeg, name in zip(megs, eegs, names):
-            picks = pick_types(info, meg=meg, eeg=eeg, ref_meg=False)
+        for meg, eeg, seeg, ecog, name in zip(megs, eegs, seegs, ecogs, names):
+            picks = pick_types(info, meg=meg, eeg=eeg, seeg=seeg, ecog=ecog,
+                               ref_meg=False)
             if len(picks) > 0:
                 picks_list.append(picks)
                 titles_list.append(name)
         if len(picks_list) == 0:
-            raise RuntimeError('No MEG or EEG channels found')
+            raise RuntimeError('No data channels found')
+        if ax is not None:
+            if isinstance(ax, plt.Axes):
+                ax = [ax]
+            if len(ax) != len(picks_list):
+                raise ValueError('For this dataset with picks=None %s axes '
+                                 'must be supplied, got %s'
+                                 % (len(picks_list), len(ax)))
+            ax_list = ax
     else:
         picks_list = [picks]
         titles_list = ['Selected channels']
@@ -479,10 +513,10 @@ def plot_raw_psd(raw, tmin=0., tmax=np.inf, fmin=0, fmax=np.inf, proj=False,
 
     for ii, (picks, title, ax) in enumerate(zip(picks_list, titles_list,
                                                 ax_list)):
-        psds, freqs = compute_raw_psd(raw, tmin=tmin, tmax=tmax, picks=picks,
-                                      fmin=fmin, fmax=fmax, proj=proj,
-                                      n_fft=n_fft, n_overlap=n_overlap,
-                                      n_jobs=n_jobs, verbose=verbose)
+        psds, freqs = psd_welch(raw, tmin=tmin, tmax=tmax, picks=picks,
+                                fmin=fmin, fmax=fmax, proj=proj,
+                                n_fft=n_fft, n_overlap=n_overlap,
+                                n_jobs=n_jobs)
 
         # Convert PSDs to dB
         if dB:
@@ -572,27 +606,22 @@ def _prepare_mne_browse_raw(params, title, bgcolor, color, bad_color, inds,
     ax_vscroll.set_title('Ch.')
 
     # make shells for plotting traces
-    ylim = [n_channels * 2 + 1, 0]
-    offset = ylim[0] / n_channels
-    offsets = np.arange(n_channels) * offset + (offset / 2.)
-    ax.set_yticks(offsets)
-    ax.set_ylim(ylim)
+    _setup_browser_offsets(params, n_channels)
     ax.set_xlim(params['t_start'], params['t_start'] + params['duration'],
                 False)
 
-    params['offsets'] = offsets
     params['lines'] = [ax.plot([np.nan], antialiased=False, linewidth=0.5)[0]
                        for _ in range(n_ch)]
     ax.set_yticklabels(['X' * max([len(ch) for ch in info['ch_names']])])
     vertline_color = (0., 0.75, 0.)
-    params['ax_vertline'] = ax.plot([0, 0], ylim, color=vertline_color,
-                                    zorder=-1)[0]
+    params['ax_vertline'] = ax.plot([0, 0], ax.get_ylim(),
+                                    color=vertline_color, zorder=-1)[0]
     params['ax_vertline'].ch_name = ''
     params['vertline_t'] = ax_hscroll.text(0, 1, '', color=vertline_color,
                                            va='bottom', ha='right')
     params['ax_hscroll_vertline'] = ax_hscroll.plot([0, 0], [0, 1],
                                                     color=vertline_color,
-                                                    zorder=1)[0]
+                                                    zorder=2)[0]
 
 
 def _plot_raw_traces(params, inds, color, bad_color, event_lines=None,
@@ -620,7 +649,7 @@ def _plot_raw_traces(params, inds, color, bad_color, event_lines=None,
             # do NOT operate in-place lest this get screwed up
             this_data = params['data'][inds[ch_ind]] * params['scale_factor']
             this_color = bad_color if ch_name in info['bads'] else color
-            this_z = -1 if ch_name in info['bads'] else 0
+            this_z = 0 if ch_name in info['bads'] else 1
             if isinstance(this_color, dict):
                 this_color = this_color[params['types'][inds[ch_ind]]]
 
@@ -668,6 +697,27 @@ def _plot_raw_traces(params, inds, color, bad_color, event_lines=None,
             else:
                 line.set_xdata([])
                 line.set_ydata([])
+
+    if 'segments' in params:
+        while len(params['ax'].collections) > 0:
+            params['ax'].collections.pop(0)
+            params['ax'].texts.pop(0)
+        segments = params['segments']
+        times = params['times']
+        ylim = params['ax'].get_ylim()
+        for idx, segment in enumerate(segments):
+            if segment[0] > times[-1]:
+                break  # Since the segments are sorted by t_start
+            if segment[1] < times[0]:
+                continue
+            start = segment[0]
+            end = segment[1]
+            dscr = params['annot_description'][idx]
+            segment_color = params['segment_colors'][dscr]
+            params['ax'].fill_betweenx(ylim, start, end, color=segment_color,
+                                       alpha=0.3)
+            params['ax'].text((start + end) / 2., ylim[0], dscr, ha='center')
+
     # finalize plot
     params['ax'].set_xlim(params['times'][0],
                           params['times'][0] + params['duration'], False)
@@ -681,10 +731,10 @@ def _plot_raw_traces(params, inds, color, bad_color, event_lines=None,
         params['fig_proj'].canvas.draw()
 
 
-def plot_raw_psd_topo(raw, tmin=0., tmax=None, fmin=0, fmax=100, proj=False,
+def plot_raw_psd_topo(raw, tmin=0., tmax=None, fmin=0., fmax=100., proj=False,
                       n_fft=2048, n_overlap=0, layout=None, color='w',
                       fig_facecolor='k', axis_facecolor='k', dB=True,
-                      show=True, n_jobs=1, verbose=None):
+                      show=True, block=False, n_jobs=1, verbose=None):
     """Function for plotting channel wise frequency spectra as topography.
 
     Parameters
@@ -722,6 +772,9 @@ def plot_raw_psd_topo(raw, tmin=0., tmax=None, fmin=0, fmax=100, proj=False,
         If True, transform data to decibels. Defaults to True.
     show : bool
         Show figure if True. Defaults to True.
+    block : bool
+        Whether to halt program execution until the figure is closed.
+        May not work on all systems / platforms. Defaults to False.
     n_jobs : int
         Number of jobs to run in parallel. Defaults to 1.
     verbose : bool, str, int, or None
@@ -736,21 +789,29 @@ def plot_raw_psd_topo(raw, tmin=0., tmax=None, fmin=0, fmax=100, proj=False,
         from ..channels.layout import find_layout
         layout = find_layout(raw.info)
 
-    psds, freqs = compute_raw_psd(raw, tmin=tmin, tmax=tmax, fmin=fmin,
-                                  fmax=fmax, proj=proj, n_fft=n_fft,
-                                  n_overlap=n_overlap, n_jobs=n_jobs,
-                                  verbose=verbose)
+    psds, freqs = psd_welch(raw, tmin=tmin, tmax=tmax, fmin=fmin,
+                            fmax=fmax, proj=proj, n_fft=n_fft,
+                            n_overlap=n_overlap, n_jobs=n_jobs)
     if dB:
         psds = 10 * np.log10(psds)
         y_label = 'dB'
     else:
         y_label = 'Power'
-    plot_fun = partial(_plot_timeseries, data=[psds], color=color, times=freqs)
-
-    fig = _plot_topo(raw.info, times=freqs, show_func=plot_fun, layout=layout,
+    show_func = partial(_plot_timeseries_unified, data=[psds], color=color,
+                        times=freqs)
+    click_func = partial(_plot_timeseries, data=[psds], color=color,
+                         times=freqs)
+    picks = _pick_data_channels(raw.info)
+    info = pick_info(raw.info, picks)
+
+    fig = _plot_topo(info, times=freqs, show_func=show_func,
+                     click_func=click_func, layout=layout,
                      axis_facecolor=axis_facecolor,
                      fig_facecolor=fig_facecolor, x_label='Frequency (Hz)',
-                     y_label=y_label)
+                     unified=True, y_label=y_label)
 
-    plt_show(show)
+    try:
+        plt_show(show, block=block)
+    except TypeError:  # not all versions have this
+        plt_show(show)
     return fig
diff --git a/mne/viz/tests/test_3d.py b/mne/viz/tests/test_3d.py
index 3e1e7b0..1062d23 100644
--- a/mne/viz/tests/test_3d.py
+++ b/mne/viz/tests/test_3d.py
@@ -10,11 +10,13 @@
 import os.path as op
 import warnings
 
+from nose.tools import assert_true
 import numpy as np
 from numpy.testing import assert_raises, assert_equal
 
 from mne import (make_field_map, pick_channels_evoked, read_evokeds,
                  read_trans, read_dipole, SourceEstimate)
+from mne.io import read_raw_ctf, read_raw_bti, read_raw_kit
 from mne.viz import (plot_sparse_source_estimates, plot_source_estimates,
                      plot_trans)
 from mne.utils import requires_mayavi, requires_pysurfer, run_tests_if_main
@@ -33,10 +35,17 @@ trans_fname = op.join(data_dir, 'MEG', 'sample',
 src_fname = op.join(data_dir, 'subjects', 'sample', 'bem',
                     'sample-oct-6-src.fif')
 dip_fname = op.join(data_dir, 'MEG', 'sample', 'sample_audvis_trunc_set1.dip')
+ctf_fname = op.join(data_dir, 'CTF', 'testdata_ctf.ds')
 
-base_dir = op.join(op.dirname(__file__), '..', '..', 'io', 'tests', 'data')
+io_dir = op.join(op.abspath(op.dirname(__file__)), '..', '..', 'io')
+base_dir = op.join(io_dir, 'tests', 'data')
 evoked_fname = op.join(base_dir, 'test-ave.fif')
 
+base_dir = op.join(io_dir, 'bti', 'tests', 'data')
+pdf_fname = op.join(base_dir, 'test_pdf_linux')
+config_fname = op.join(base_dir, 'test_config_linux')
+hs_fname = op.join(base_dir, 'test_hs_linux')
+sqd_fname = op.join(io_dir, 'kit', 'tests', 'data', 'test.sqd')
 warnings.simplefilter('always')  # enable b/c these tests throw warnings
 
 
@@ -87,24 +96,48 @@ def test_plot_evoked_field():
                           baseline=(-0.2, 0.0))
     evoked = pick_channels_evoked(evoked, evoked.ch_names[::10])  # speed
     for t in ['meg', None]:
-        maps = make_field_map(evoked, trans_fname, subject='sample',
-                              subjects_dir=subjects_dir, n_jobs=1, ch_type=t)
-
+        with warnings.catch_warnings(record=True):  # bad proj
+            maps = make_field_map(evoked, trans_fname, subject='sample',
+                                  subjects_dir=subjects_dir, n_jobs=1,
+                                  ch_type=t)
         evoked.plot_field(maps, time=0.1)
 
 
 @testing.requires_testing_data
 @requires_mayavi
 def test_plot_trans():
-    """Test plotting of -trans.fif files
+    """Test plotting of -trans.fif files and MEG sensor layouts
     """
-    evoked = read_evokeds(evoked_fname, condition='Left Auditory',
-                          baseline=(-0.2, 0.0))
-    plot_trans(evoked.info, trans_fname, subject='sample',
-               subjects_dir=subjects_dir)
-    assert_raises(ValueError, plot_trans, evoked.info, trans_fname,
+    evoked = read_evokeds(evoked_fname)[0]
+    with warnings.catch_warnings(record=True):  # 4D weight tables
+        bti = read_raw_bti(pdf_fname, config_fname, hs_fname, convert=True,
+                           preload=False).info
+    infos = dict(
+        Neuromag=evoked.info,
+        CTF=read_raw_ctf(ctf_fname).info,
+        BTi=bti,
+        KIT=read_raw_kit(sqd_fname).info,
+    )
+    for system, info in infos.items():
+        ref_meg = False if system == 'KIT' else True
+        plot_trans(info, trans_fname, subject='sample', meg_sensors=True,
+                   subjects_dir=subjects_dir, ref_meg=ref_meg)
+    # KIT ref sensor coil def not defined
+    assert_raises(RuntimeError, plot_trans, infos['KIT'], None,
+                  meg_sensors=True, ref_meg=True)
+    info = infos['Neuromag']
+    assert_raises(ValueError, plot_trans, info, trans_fname,
                   subject='sample', subjects_dir=subjects_dir,
                   ch_type='bad-chtype')
+    assert_raises(TypeError, plot_trans, 'foo', trans_fname,
+                  subject='sample', subjects_dir=subjects_dir)
+    # no-head version
+    plot_trans(info, None, meg_sensors=True, dig=True, coord_frame='head')
+    # EEG only with strange options
+    with warnings.catch_warnings(record=True) as w:
+        plot_trans(evoked.copy().pick_types(meg=False, eeg=True).info,
+                   trans=trans_fname, meg_sensors=True)
+    assert_true(['Cannot plot MEG' in str(ww.message) for ww in w])
 
 
 @testing.requires_testing_data
diff --git a/mne/viz/tests/test_decoding.py b/mne/viz/tests/test_decoding.py
index b81ae21..5095b18 100644
--- a/mne/viz/tests/test_decoding.py
+++ b/mne/viz/tests/test_decoding.py
@@ -30,7 +30,8 @@ def _get_data(tmin=-0.2, tmax=0.5, event_id=dict(aud_l=1, vis_l=3),
               event_id_gen=dict(aud_l=2, vis_l=4), test_times=None):
     """Aux function for testing GAT viz"""
     gat = GeneralizationAcrossTime()
-    raw = io.Raw(raw_fname, preload=False)
+    raw = io.read_raw_fif(raw_fname, preload=False)
+    raw.add_proj([], remove_existing=True)
     events = read_events(event_name)
     picks = pick_types(raw.info, meg='mag', stim=False, ecg=False,
                        eog=False, exclude='bads')
diff --git a/mne/viz/tests/test_epochs.py b/mne/viz/tests/test_epochs.py
index 683ca6b..6adbbfe 100644
--- a/mne/viz/tests/test_epochs.py
+++ b/mne/viz/tests/test_epochs.py
@@ -11,14 +11,14 @@ import warnings
 from nose.tools import assert_raises
 
 import numpy as np
-
+from numpy.testing import assert_equal
 
 from mne import io, read_events, Epochs
 from mne import pick_types
 from mne.utils import run_tests_if_main, requires_version
 from mne.channels import read_layout
 
-from mne.viz import plot_drop_log, plot_epochs_image
+from mne.viz import plot_drop_log
 from mne.viz.utils import _fake_click
 
 # Set our plotters to test mode
@@ -39,7 +39,7 @@ layout = read_layout('Vectorview-all')
 
 
 def _get_raw():
-    return io.Raw(raw_fname, preload=False)
+    return io.read_raw_fif(raw_fname, preload=False)
 
 
 def _get_events():
@@ -57,8 +57,9 @@ def _get_epochs():
     picks = _get_picks(raw)
     # Use a subset of channels for plotting speed
     picks = np.round(np.linspace(0, len(picks) + 1, n_chan)).astype(int)
-    epochs = Epochs(raw, events[:5], event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0))
+    with warnings.catch_warnings(record=True):  # bad proj
+        epochs = Epochs(raw, events[:5], event_id, tmin, tmax, picks=picks,
+                        baseline=(None, 0))
     return epochs
 
 
@@ -77,6 +78,7 @@ def test_plot_epochs():
     """Test epoch plotting"""
     import matplotlib.pyplot as plt
     epochs = _get_epochs()
+    epochs.info.normalize_proj()  # avoid warnings
     epochs.plot(scalings=None, title='Epochs')
     plt.close('all')
     fig = epochs[0].plot(picks=[0, 2, 3], scalings=None)
@@ -123,6 +125,8 @@ def test_plot_epochs():
         fig.canvas.close_event()  # closing and epoch dropping
         assert(n_epochs - 1 == len(epochs))
         plt.close('all')
+    epochs.plot_sensors()  # Test plot_sensors
+    plt.close('all')
 
 
 def test_plot_epochs_image():
@@ -130,7 +134,19 @@ def test_plot_epochs_image():
     """
     import matplotlib.pyplot as plt
     epochs = _get_epochs()
-    plot_epochs_image(epochs, picks=[1, 2])
+    epochs.plot_image(picks=[1, 2])
+    overlay_times = [0.1]
+    epochs.plot_image(order=[0], overlay_times=overlay_times)
+    epochs.plot_image(overlay_times=overlay_times)
+    assert_raises(ValueError, epochs.plot_image,
+                  overlay_times=[0.1, 0.2])
+    assert_raises(ValueError, epochs.plot_image,
+                  order=[0, 1])
+    with warnings.catch_warnings(record=True) as w:
+        epochs.plot_image(overlay_times=[1.1])
+        warnings.simplefilter('always')
+    assert_equal(len(w), 1)
+
     plt.close('all')
 
 
@@ -140,7 +156,7 @@ def test_plot_drop_log():
     import matplotlib.pyplot as plt
     epochs = _get_epochs()
     assert_raises(ValueError, epochs.plot_drop_log)
-    epochs.drop_bad_epochs()
+    epochs.drop_bad()
 
     warnings.simplefilter('always', UserWarning)
     with warnings.catch_warnings(record=True):
diff --git a/mne/viz/tests/test_evoked.py b/mne/viz/tests/test_evoked.py
index 529616e..4fd4638 100644
--- a/mne/viz/tests/test_evoked.py
+++ b/mne/viz/tests/test_evoked.py
@@ -15,10 +15,10 @@ from numpy.testing import assert_raises
 
 
 from mne import io, read_events, Epochs, pick_types, read_cov
+from mne.channels import read_layout
+from mne.utils import slow_test, run_tests_if_main
 from mne.viz.evoked import _butterfly_onselect
 from mne.viz.utils import _fake_click
-from mne.utils import slow_test, run_tests_if_main
-from mne.channels import read_layout
 
 # Set our plotters to test mode
 import matplotlib
@@ -38,7 +38,7 @@ layout = read_layout('Vectorview-all')
 
 
 def _get_raw():
-    return io.Raw(raw_fname, preload=False)
+    return io.read_raw_fif(raw_fname, preload=False)
 
 
 def _get_events():
@@ -52,13 +52,15 @@ def _get_picks(raw):
 
 def _get_epochs():
     raw = _get_raw()
+    raw.add_proj([], remove_existing=True)
     events = _get_events()
     picks = _get_picks(raw)
     # Use a subset of channels for plotting speed
     picks = picks[np.round(np.linspace(0, len(picks) - 1, n_chan)).astype(int)]
     picks[0] = 2  # make sure we have a magnetometer
-    epochs = Epochs(raw, events[:5], event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0))
+    with warnings.catch_warnings(record=True):  # proj
+        epochs = Epochs(raw, events[:5], event_id, tmin, tmax, picks=picks,
+                        baseline=(None, 0))
     epochs.info['bads'] = [epochs.ch_names[-1]]
     return epochs
 
@@ -132,5 +134,7 @@ def test_plot_evoked():
         evoked_sss.plot_white(cov)
         evoked_sss.plot_white(cov_fname)
         plt.close('all')
+    evoked.plot_sensors()  # Test plot_sensors
+    plt.close('all')
 
 run_tests_if_main()
diff --git a/mne/viz/tests/test_ica.py b/mne/viz/tests/test_ica.py
index ff1048f..3149fc6 100644
--- a/mne/viz/tests/test_ica.py
+++ b/mne/viz/tests/test_ica.py
@@ -29,7 +29,7 @@ event_id, tmin, tmax = 1, -0.1, 0.2
 
 
 def _get_raw(preload=False):
-    return io.Raw(raw_fname, preload=preload)
+    return io.read_raw_fif(raw_fname, preload=preload)
 
 
 def _get_events():
@@ -44,8 +44,9 @@ def _get_epochs():
     raw = _get_raw()
     events = _get_events()
     picks = _get_picks(raw)
-    epochs = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0))
+    with warnings.catch_warnings(record=True):  # bad proj
+        epochs = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks,
+                        baseline=(None, 0))
     return epochs
 
 
@@ -58,7 +59,8 @@ def test_plot_ica_components():
     ica = ICA(noise_cov=read_cov(cov_fname), n_components=2,
               max_pca_components=3, n_pca_components=3)
     ica_picks = _get_picks(raw)
-    ica.fit(raw, picks=ica_picks)
+    with warnings.catch_warnings(record=True):
+        ica.fit(raw, picks=ica_picks)
     warnings.simplefilter('always', UserWarning)
     with warnings.catch_warnings(record=True):
         for components in [0, [0], [0, 1], [0, 1] * 2, None]:
@@ -74,9 +76,8 @@ def test_plot_ica_sources():
     """Test plotting of ICA panel
     """
     import matplotlib.pyplot as plt
-    raw = io.Raw(raw_fname, preload=False)
-    raw.crop(0, 1, copy=False)
-    raw.load_data()
+    raw = io.read_raw_fif(raw_fname,
+                          preload=False).crop(0, 1, copy=False).load_data()
     picks = _get_picks(raw)
     epochs = _get_epochs()
     raw.pick_channels([raw.ch_names[k] for k in picks])
@@ -121,11 +122,16 @@ def test_plot_ica_overlay():
     picks = _get_picks(raw)
     ica = ICA(noise_cov=read_cov(cov_fname), n_components=2,
               max_pca_components=3, n_pca_components=3)
-    ica.fit(raw, picks=picks)
+    # can't use info.normalize_proj here because of how and when ICA and Epochs
+    # objects do picking of Raw data
+    with warnings.catch_warnings(record=True):  # bad proj
+        ica.fit(raw, picks=picks)
     # don't test raw, needs preload ...
-    ecg_epochs = create_ecg_epochs(raw, picks=picks)
+    with warnings.catch_warnings(record=True):  # bad proj
+        ecg_epochs = create_ecg_epochs(raw, picks=picks)
     ica.plot_overlay(ecg_epochs.average())
-    eog_epochs = create_eog_epochs(raw, picks=picks)
+    with warnings.catch_warnings(record=True):  # bad proj
+        eog_epochs = create_eog_epochs(raw, picks=picks)
     ica.plot_overlay(eog_epochs.average())
     assert_raises(ValueError, ica.plot_overlay, raw[:2, :3][0])
     ica.plot_overlay(raw)
@@ -141,7 +147,8 @@ def test_plot_ica_scores():
     picks = _get_picks(raw)
     ica = ICA(noise_cov=read_cov(cov_fname), n_components=2,
               max_pca_components=3, n_pca_components=3)
-    ica.fit(raw, picks=picks)
+    with warnings.catch_warnings(record=True):  # bad proj
+        ica.fit(raw, picks=picks)
     ica.labels_ = dict()
     ica.labels_['eog/0/foo'] = 0
     ica.labels_['eog'] = 0
@@ -166,7 +173,8 @@ def test_plot_instance_components():
     picks = _get_picks(raw)
     ica = ICA(noise_cov=read_cov(cov_fname), n_components=2,
               max_pca_components=3, n_pca_components=3)
-    ica.fit(raw, picks=picks)
+    with warnings.catch_warnings(record=True):  # bad proj
+        ica.fit(raw, picks=picks)
     fig = ica.plot_sources(raw, exclude=[0], title='Components')
     fig.canvas.key_press_event('down')
     fig.canvas.key_press_event('up')
diff --git a/mne/viz/tests/test_misc.py b/mne/viz/tests/test_misc.py
index fd38840..79d5899 100644
--- a/mne/viz/tests/test_misc.py
+++ b/mne/viz/tests/test_misc.py
@@ -41,7 +41,7 @@ event_fname = op.join(base_dir, 'test-eve.fif')
 
 
 def _get_raw():
-    return io.Raw(raw_fname, preload=True)
+    return io.read_raw_fif(raw_fname, preload=True)
 
 
 def _get_events():
@@ -53,7 +53,8 @@ def test_plot_cov():
     """
     raw = _get_raw()
     cov = read_cov(cov_fname)
-    fig1, fig2 = cov.plot(raw.info, proj=True, exclude=raw.ch_names[6:])
+    with warnings.catch_warnings(record=True):  # bad proj
+        fig1, fig2 = cov.plot(raw.info, proj=True, exclude=raw.ch_names[6:])
 
 
 @testing.requires_testing_data
diff --git a/mne/viz/tests/test_raw.py b/mne/viz/tests/test_raw.py
index 71632e8..f6db8d7 100644
--- a/mne/viz/tests/test_raw.py
+++ b/mne/viz/tests/test_raw.py
@@ -7,10 +7,10 @@ import warnings
 
 from numpy.testing import assert_raises
 
-from mne import io, read_events, pick_types
+from mne import io, read_events, pick_types, Annotations
 from mne.utils import requires_version, run_tests_if_main
 from mne.viz.utils import _fake_click
-from mne.viz import plot_raw
+from mne.viz import plot_raw, plot_sensors
 
 # Set our plotters to test mode
 import matplotlib
@@ -24,8 +24,12 @@ event_name = op.join(base_dir, 'test-eve.fif')
 
 
 def _get_raw():
-    raw = io.Raw(raw_fname, preload=True)
+    raw = io.read_raw_fif(raw_fname, preload=True)
+    # Throws a warning about a changed unit.
+    with warnings.catch_warnings(record=True):
+        raw.set_channel_types({raw.ch_names[0]: 'ias'})
     raw.pick_channels(raw.ch_names[:9])
+    raw.info.normalize_proj()  # Fix projectors after subselection
     return raw
 
 
@@ -34,8 +38,7 @@ def _get_events():
 
 
 def test_plot_raw():
-    """Test plotting of raw data
-    """
+    """Test plotting of raw data."""
     import matplotlib.pyplot as plt
     raw = _get_raw()
     events = _get_events()
@@ -86,14 +89,16 @@ def test_plot_raw():
         # Color setting
         assert_raises(KeyError, raw.plot, event_color={0: 'r'})
         assert_raises(TypeError, raw.plot, event_color={'foo': 'r'})
+        annot = Annotations([10, 10 + raw.first_samp / raw.info['sfreq']],
+                            [10, 10], ['test', 'test'], raw.info['meas_date'])
+        raw.annotations = annot
         fig = plot_raw(raw, events=events, event_color={-1: 'r', 998: 'b'})
         plt.close('all')
 
 
 @requires_version('scipy', '0.10')
 def test_plot_raw_filtered():
-    """Test filtering of raw plots
-    """
+    """Test filtering of raw plots."""
     raw = _get_raw()
     assert_raises(ValueError, raw.plot, lowpass=raw.info['sfreq'] / 2.)
     assert_raises(ValueError, raw.plot, highpass=0)
@@ -107,8 +112,7 @@ def test_plot_raw_filtered():
 
 @requires_version('scipy', '0.12')
 def test_plot_raw_psd():
-    """Test plotting of raw psds
-    """
+    """Test plotting of raw psds."""
     import matplotlib.pyplot as plt
     raw = _get_raw()
     # normal mode
@@ -117,13 +121,28 @@ def test_plot_raw_psd():
     picks = pick_types(raw.info, meg='mag', eeg=False)[:4]
     raw.plot_psd(picks=picks, area_mode='range')
     ax = plt.axes()
-    # if ax is supplied, picks must be, too:
+    # if ax is supplied:
     assert_raises(ValueError, raw.plot_psd, ax=ax)
     raw.plot_psd(picks=picks, ax=ax)
     plt.close('all')
+    ax = plt.axes()
+    assert_raises(ValueError, raw.plot_psd, ax=ax)
+    ax = [ax, plt.axes()]
+    raw.plot_psd(ax=ax)
+    plt.close('all')
     # topo psd
     raw.plot_psd_topo()
     plt.close('all')
 
 
+def test_plot_sensors():
+    """Test plotting of sensor array."""
+    import matplotlib.pyplot as plt
+    raw = _get_raw()
+    fig = raw.plot_sensors('3d')
+    _fake_click(fig, fig.gca(), (-0.08, 0.67))
+    raw.plot_sensors('topomap')
+    assert_raises(TypeError, plot_sensors, raw)  # needs to be info
+    plt.close('all')
+
 run_tests_if_main()
diff --git a/mne/viz/tests/test_topo.py b/mne/viz/tests/test_topo.py
index 6ae52c0..b84c2a4 100644
--- a/mne/viz/tests/test_topo.py
+++ b/mne/viz/tests/test_topo.py
@@ -12,7 +12,6 @@ from collections import namedtuple
 import numpy as np
 from numpy.testing import assert_raises
 
-
 from mne import io, read_events, Epochs
 from mne import pick_channels_evoked
 from mne.channels import read_layout
@@ -21,7 +20,8 @@ from mne.utils import run_tests_if_main
 
 from mne.viz import (plot_topo_image_epochs, _get_presser,
                      mne_analyze_colormap, plot_evoked_topo)
-from mne.viz.topo import _plot_update_evoked_topo
+from mne.viz.utils import _fake_click
+from mne.viz.topo import _plot_update_evoked_topo_proj, iter_topography
 
 # Set our plotters to test mode
 import matplotlib
@@ -39,7 +39,7 @@ layout = read_layout('Vectorview-all')
 
 
 def _get_raw():
-    return io.Raw(raw_fname, preload=False)
+    return io.read_raw_fif(raw_fname, preload=False)
 
 
 def _get_events():
@@ -47,15 +47,16 @@ def _get_events():
 
 
 def _get_picks(raw):
-    return [0, 1, 2, 6, 7, 8, 340, 341, 342]  # take a only few channels
+    return [0, 1, 2, 6, 7, 8, 306, 340, 341, 342]  # take a only few channels
 
 
 def _get_epochs():
     raw = _get_raw()
     events = _get_events()
     picks = _get_picks(raw)
-    epochs = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0))
+    with warnings.catch_warnings(record=True):  # bad proj
+        epochs = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks,
+                        baseline=(None, 0), verbose='error')
     return epochs
 
 
@@ -77,9 +78,14 @@ def test_plot_topo():
     # Show topography
     evoked = _get_epochs().average()
     plot_evoked_topo(evoked)  # should auto-find layout
+    # Test jointplot
+    evoked.plot_joint()
+    evoked.plot_joint(title='test', ts_args=dict(spatial_colors=True),
+                      topomap_args=dict(colorbar=True, times=[0.]))
+
     warnings.simplefilter('always', UserWarning)
-    picked_evoked = evoked.pick_channels(evoked.ch_names[:3], copy=True)
-    picked_evoked_eeg = evoked.pick_types(meg=False, eeg=True, copy=True)
+    picked_evoked = evoked.copy().pick_channels(evoked.ch_names[:3])
+    picked_evoked_eeg = evoked.copy().pick_types(meg=False, eeg=True)
     picked_evoked_eeg.pick_channels(picked_evoked_eeg.ch_names[:3])
 
     # test scaling
@@ -98,16 +104,22 @@ def test_plot_topo():
         fig = plot_evoked_topo(picked_evoked_delayed_ssp, layout,
                                proj='interactive')
         func = _get_presser(fig)
-        event = namedtuple('Event', 'inaxes')
-        func(event(inaxes=fig.axes[0]))
+        event = namedtuple('Event', ['inaxes', 'xdata', 'ydata'])
+        func(event(inaxes=fig.axes[0], xdata=fig.axes[0]._mne_axs[0].pos[0],
+                   ydata=fig.axes[0]._mne_axs[0].pos[1]))
+        func(event(inaxes=fig.axes[0], xdata=0, ydata=0))
         params = dict(evokeds=[picked_evoked_delayed_ssp],
                       times=picked_evoked_delayed_ssp.times,
                       fig=fig, projs=picked_evoked_delayed_ssp.info['projs'])
         bools = [True] * len(params['projs'])
-        _plot_update_evoked_topo(params, bools)
+        _plot_update_evoked_topo_proj(params, bools)
     # should auto-generate layout
     plot_evoked_topo(picked_evoked_eeg.copy(),
                      fig_background=np.zeros((4, 3, 3)), proj=True)
+    picked_evoked.plot_topo(merge_grads=True)  # Test RMS plot of grad pairs
+    plt.close('all')
+    for ax, idx in iter_topography(evoked.info):
+        ax.plot(evoked.data[idx], color='red')
     plt.close('all')
 
 
@@ -118,8 +130,9 @@ def test_plot_topo_image_epochs():
     title = 'ERF images - MNE sample data'
     epochs = _get_epochs()
     cmap = mne_analyze_colormap(format='matplotlib')
-    plot_topo_image_epochs(epochs, sigma=0.5, vmin=-200, vmax=200,
-                           colorbar=True, title=title, cmap=cmap)
+    fig = plot_topo_image_epochs(epochs, sigma=0.5, vmin=-200, vmax=200,
+                                 colorbar=True, title=title, cmap=cmap)
+    _fake_click(fig, fig.axes[2], (0.08, 0.64))
     plt.close('all')
 
 
diff --git a/mne/viz/tests/test_topomap.py b/mne/viz/tests/test_topomap.py
index d9dd5d6..54c462b 100644
--- a/mne/viz/tests/test_topomap.py
+++ b/mne/viz/tests/test_topomap.py
@@ -22,8 +22,8 @@ from mne.time_frequency.tfr import AverageTFR
 from mne.utils import slow_test
 
 from mne.viz import plot_evoked_topomap, plot_projs_topomap
-from mne.viz.topomap import (_check_outlines, _onselect, plot_topomap,
-                             _find_peaks)
+from mne.viz.topomap import (_check_outlines, _onselect, plot_topomap)
+from mne.viz.utils import _find_peaks
 
 # Set our plotters to test mode
 import matplotlib
@@ -34,7 +34,7 @@ warnings.simplefilter('always')  # enable b/c these tests throw warnings
 
 data_dir = testing.data_path(download=False)
 subjects_dir = op.join(data_dir, 'subjects')
-ecg_fname = op.join(data_dir, 'MEG', 'sample', 'sample_audvis_ecg_proj.fif')
+ecg_fname = op.join(data_dir, 'MEG', 'sample', 'sample_audvis_ecg-proj.fif')
 
 base_dir = op.join(op.dirname(__file__), '..', '..', 'io', 'tests', 'data')
 evoked_fname = op.join(base_dir, 'test-ave.fif')
@@ -45,7 +45,7 @@ layout = read_layout('Vectorview-all')
 
 
 def _get_raw():
-    return io.Raw(raw_fname, preload=False)
+    return io.read_raw_fif(raw_fname, preload=False)
 
 
 @slow_test
@@ -60,9 +60,16 @@ def test_plot_topomap():
     res = 16
     evoked = read_evokeds(evoked_fname, 'Left Auditory',
                           baseline=(None, 0))
-    ev_bad = evoked.pick_types(meg=False, eeg=True, copy=True)
+
+    # Test animation
+    _, anim = evoked.animate_topomap(ch_type='grad', times=[0, 0.1],
+                                     butterfly=False)
+    anim._func(1)  # _animate has to be tested separately on 'Agg' backend.
+    plt.close('all')
+
+    ev_bad = evoked.copy().pick_types(meg=False, eeg=True)
     ev_bad.pick_channels(ev_bad.ch_names[:2])
-    ev_bad.plot_topomap(times=ev_bad.times[:2] - 1e-6)  # auto, should plot EEG
+    ev_bad.plot_topomap(times=ev_bad.times[:2] - 1e-6)  # auto, plots EEG
     assert_raises(ValueError, ev_bad.plot_topomap, ch_type='mag')
     assert_raises(TypeError, ev_bad.plot_topomap, head_pos='foo')
     assert_raises(KeyError, ev_bad.plot_topomap, head_pos=dict(foo='bar'))
@@ -103,6 +110,13 @@ def test_plot_topomap():
                     for x in subplot.get_children()
                     if isinstance(x, matplotlib.text.Text)))
 
+    # Plot array
+    for ch_type in ('mag', 'grad'):
+        evoked_ = evoked.copy().pick_types(eeg=False, meg=ch_type)
+        plot_topomap(evoked_.data[:, 0], evoked_.info)
+    # fail with multiple channel types
+    assert_raises(ValueError, plot_topomap, evoked.data[0, :], evoked.info)
+
     # Test title
     def get_texts(p):
         return [x.get_text() for x in p.get_children() if
@@ -220,7 +234,8 @@ def test_plot_topomap():
 
     # Test peak finder
     axes = [plt.subplot(131), plt.subplot(132)]
-    evoked.plot_topomap(times='peaks', axes=axes)
+    with warnings.catch_warnings(record=True):  # rightmost column
+        evoked.plot_topomap(times='peaks', axes=axes)
     plt.close('all')
     evoked.data = np.zeros(evoked.data.shape)
     evoked.data[50][1] = 1
diff --git a/mne/viz/tests/test_utils.py b/mne/viz/tests/test_utils.py
index 3a8b69d..5f8932c 100644
--- a/mne/viz/tests/test_utils.py
+++ b/mne/viz/tests/test_utils.py
@@ -8,9 +8,12 @@ import numpy as np
 from nose.tools import assert_true, assert_raises
 from numpy.testing import assert_allclose
 
-from mne.viz.utils import compare_fiff, _fake_click
+from mne.viz.utils import compare_fiff, _fake_click, _compute_scalings
 from mne.viz import ClickableImage, add_background_image, mne_analyze_colormap
 from mne.utils import run_tests_if_main
+from mne.io import read_raw_fif
+from mne.event import read_events
+from mne.epochs import Epochs
 
 # Set our plotters to test mode
 import matplotlib
@@ -21,6 +24,7 @@ warnings.simplefilter('always')  # enable b/c these tests throw warnings
 base_dir = op.join(op.dirname(__file__), '..', '..', 'io', 'tests', 'data')
 raw_fname = op.join(base_dir, 'test_raw.fif')
 cov_fname = op.join(base_dir, 'test-cov.fif')
+ev_fname = op.join(base_dir, 'test_raw-eve.fif')
 
 
 def test_mne_analyze_colormap():
@@ -85,4 +89,32 @@ def test_add_background_image():
         assert_true(ax.get_aspect() == 'auto')
 
 
+def test_auto_scale():
+    """Test auto-scaling of channels for quick plotting."""
+    raw = read_raw_fif(raw_fname, preload=False)
+    ev = read_events(ev_fname)
+    epochs = Epochs(raw, ev)
+    rand_data = np.random.randn(10, 100)
+
+    for inst in [raw, epochs]:
+        scale_grad = 1e10
+        scalings_def = dict([('eeg', 'auto'), ('grad', scale_grad),
+                             ('stim', 'auto')])
+
+        # Test for wrong inputs
+        assert_raises(ValueError, inst.plot, scalings='foo')
+        assert_raises(ValueError, _compute_scalings, 'foo', inst)
+
+        # Make sure compute_scalings doesn't change anything not auto
+        scalings_new = _compute_scalings(scalings_def, inst)
+        assert_true(scale_grad == scalings_new['grad'])
+        assert_true(scalings_new['eeg'] != 'auto')
+
+    assert_raises(ValueError, _compute_scalings, scalings_def, rand_data)
+    epochs = epochs[0].load_data()
+    epochs.pick_types(eeg=True, meg=False, copy=False)
+    assert_raises(ValueError, _compute_scalings,
+                  dict(grad='auto'), epochs)
+
+
 run_tests_if_main()
diff --git a/mne/viz/topo.py b/mne/viz/topo.py
index 72512fd..3bcad97 100644
--- a/mne/viz/topo.py
+++ b/mne/viz/topo.py
@@ -9,19 +9,19 @@ from __future__ import print_function
 #
 # License: Simplified BSD
 
-import warnings
-from itertools import cycle
 from functools import partial
+from itertools import cycle
 
 import numpy as np
 
+from ..io.constants import Bunch
 from ..io.pick import channel_type, pick_types
 from ..fixes import normalize_colors
-from ..utils import _clean_names
-
+from ..utils import _clean_names, warn
+from ..channels.layout import _merge_grad_data, _pair_grad_sensors, find_layout
 from ..defaults import _handle_default
 from .utils import (_check_delayed_ssp, COLORS, _draw_proj_checkbox,
-                    add_background_image, plt_show)
+                    add_background_image, plt_show, _setup_vmin_vmax)
 
 
 def iter_topography(info, layout=None, on_pick=None, fig=None,
@@ -38,7 +38,7 @@ def iter_topography(info, layout=None, on_pick=None, fig=None,
 
     Parameters
     ----------
-    info : instance of mne.io.meas_info.Info
+    info : instance of Info
         The measurement info.
     layout : instance of mne.layout.Layout | None
         The layout to use. If None, layout will be guessed
@@ -62,21 +62,37 @@ def iter_topography(info, layout=None, on_pick=None, fig=None,
 
     Returns
     -------
-    A generator that can be unpacked into
+    A generator that can be unpacked into:
+
+        ax : matplotlib.axis.Axis
+            The current axis of the topo plot.
+        ch_dx : int
+            The related channel index.
 
-    ax : matplotlib.axis.Axis
-        The current axis of the topo plot.
-    ch_dx : int
-        The related channel index.
     """
-    import matplotlib.pyplot as plt
+    return _iter_topography(info, layout, on_pick, fig, fig_facecolor,
+                            axis_facecolor, axis_spinecolor, layout_scale)
+
+
+def _iter_topography(info, layout, on_pick, fig, fig_facecolor='k',
+                     axis_facecolor='k', axis_spinecolor='k',
+                     layout_scale=None, unified=False, img=False):
+    """Private helper to iterate over topography
+
+    Has the same parameters as iter_topography, plus:
+
+    unified : bool
+        If False (default), multiple matplotlib axes will be used.
+        If True, a single axis will be constructed. The former is
+        useful for custom plotting, the latter for speed.
+    """
+    from matplotlib import pyplot as plt, collections
 
     if fig is None:
         fig = plt.figure()
 
     fig.set_facecolor(fig_facecolor)
     if layout is None:
-        from ..channels import find_layout
         layout = find_layout(info)
 
     if on_pick is not None:
@@ -89,32 +105,58 @@ def iter_topography(info, layout=None, on_pick=None, fig=None,
 
     ch_names = _clean_names(info['ch_names'])
     iter_ch = [(x, y) for x, y in enumerate(layout.names) if y in ch_names]
+    if unified:
+        under_ax = plt.axes([0, 0, 1, 1])
+        under_ax.set(xlim=[0, 1], ylim=[0, 1])
+        under_ax.axis('off')
+        axs = list()
     for idx, name in iter_ch:
-        ax = plt.axes(pos[idx])
-        ax.patch.set_facecolor(axis_facecolor)
-        plt.setp(list(ax.spines.values()), color=axis_spinecolor)
-        ax.set_xticklabels([])
-        ax.set_yticklabels([])
-        plt.setp(ax.get_xticklines(), visible=False)
-        plt.setp(ax.get_yticklines(), visible=False)
         ch_idx = ch_names.index(name)
-        vars(ax)['_mne_ch_name'] = name
-        vars(ax)['_mne_ch_idx'] = ch_idx
-        vars(ax)['_mne_ax_face_color'] = axis_facecolor
-        yield ax, ch_idx
-
-
-def _plot_topo(info=None, times=None, show_func=None, layout=None,
-               decim=None, vmin=None, vmax=None, ylim=None, colorbar=None,
+        if not unified:  # old, slow way
+            ax = plt.axes(pos[idx])
+            ax.patch.set_facecolor(axis_facecolor)
+            plt.setp(list(ax.spines.values()), color=axis_spinecolor)
+            ax.set_xticklabels([])
+            ax.set_yticklabels([])
+            plt.setp(ax.get_xticklines(), visible=False)
+            plt.setp(ax.get_yticklines(), visible=False)
+            ax._mne_ch_name = name
+            ax._mne_ch_idx = ch_idx
+            ax._mne_ax_face_color = axis_facecolor
+            yield ax, ch_idx
+        else:
+            ax = Bunch(ax=under_ax, pos=pos[idx], data_lines=list(),
+                       _mne_ch_name=name, _mne_ch_idx=ch_idx,
+                       _mne_ax_face_color=axis_facecolor)
+            axs.append(ax)
+    if unified:
+        under_ax._mne_axs = axs
+        # Create a PolyCollection for the axis backgrounds
+        verts = np.transpose([pos[:, :2],
+                              pos[:, :2] + pos[:, 2:] * [1, 0],
+                              pos[:, :2] + pos[:, 2:],
+                              pos[:, :2] + pos[:, 2:] * [0, 1],
+                              ], [1, 0, 2])
+        if not img:
+            under_ax.add_collection(collections.PolyCollection(
+                verts, facecolor=axis_facecolor, edgecolor=axis_spinecolor,
+                linewidth=1.))  # Not needed for image plots.
+        for ax in axs:
+            yield ax, ax._mne_ch_idx
+
+
+def _plot_topo(info, times, show_func, click_func=None, layout=None,
+               vmin=None, vmax=None, ylim=None, colorbar=None,
                border='none', axis_facecolor='k', fig_facecolor='k',
                cmap='RdBu_r', layout_scale=None, title=None, x_label=None,
-               y_label=None, vline=None, font_color='w'):
+               y_label=None, font_color='w', unified=False, img=False):
     """Helper function to plot on sensor layout"""
     import matplotlib.pyplot as plt
 
     # prepare callbacks
     tmin, tmax = times[[0, -1]]
-    on_pick = partial(show_func, tmin=tmin, tmax=tmax, vmin=vmin,
+    click_func = show_func if click_func is None else click_func
+    on_pick = partial(click_func, tmin=tmin, tmax=tmax, vmin=vmin,
                       vmax=vmax, ylim=ylim, x_label=x_label,
                       y_label=y_label, colorbar=colorbar)
 
@@ -129,11 +171,12 @@ def _plot_topo(info=None, times=None, show_func=None, layout=None,
         plt.setp(cb_yticks, color=font_color)
         ax.axis('off')
 
-    my_topo_plot = iter_topography(info, layout=layout, on_pick=on_pick,
-                                   fig=fig, layout_scale=layout_scale,
-                                   axis_spinecolor=border,
-                                   axis_facecolor=axis_facecolor,
-                                   fig_facecolor=fig_facecolor)
+    my_topo_plot = _iter_topography(info, layout=layout, on_pick=on_pick,
+                                    fig=fig, layout_scale=layout_scale,
+                                    axis_spinecolor=border,
+                                    axis_facecolor=axis_facecolor,
+                                    fig_facecolor=fig_facecolor,
+                                    unified=unified, img=img)
 
     for ax, ch_idx in my_topo_plot:
         if layout.kind == 'Vectorview-all' and ylim is not None:
@@ -145,25 +188,33 @@ def _plot_topo(info=None, times=None, show_func=None, layout=None,
         show_func(ax, ch_idx, tmin=tmin, tmax=tmax, vmin=vmin,
                   vmax=vmax, ylim=ylim_)
 
-        if ylim_ and not any(v is None for v in ylim_):
-            plt.ylim(*ylim_)
-
     if title is not None:
         plt.figtext(0.03, 0.9, title, color=font_color, fontsize=19)
 
     return fig
 
 
-def _plot_topo_onpick(event, show_func=None, colorbar=False):
+def _plot_topo_onpick(event, show_func):
     """Onpick callback that shows a single channel in a new figure"""
 
     # make sure that the swipe gesture in OS-X doesn't open many figures
     orig_ax = event.inaxes
-    if event.inaxes is None or '_mne_ch_idx' not in vars(orig_ax):
+    if event.inaxes is None or (not hasattr(orig_ax, '_mne_ch_idx') and
+                                not hasattr(orig_ax, '_mne_axs')):
         return
 
     import matplotlib.pyplot as plt
     try:
+        if hasattr(orig_ax, '_mne_axs'):  # in unified, single-axes mode
+            x, y = event.xdata, event.ydata
+            for ax in orig_ax._mne_axs:
+                if x >= ax.pos[0] and y >= ax.pos[1] and \
+                        x <= ax.pos[0] + ax.pos[2] and \
+                        y <= ax.pos[1] + ax.pos[3]:
+                    orig_ax = ax
+                    break
+            else:
+                return
         ch_idx = orig_ax._mne_ch_idx
         face_color = orig_ax._mne_ax_face_color
         fig, ax = plt.subplots(1)
@@ -179,15 +230,31 @@ def _plot_topo_onpick(event, show_func=None, colorbar=False):
         # so we print
         # it here to know what went wrong
         print(err)
-        raise err
+        raise
+
+
+def _compute_scalings(bn, xlim, ylim):
+    """Compute scale factors for a unified plot"""
+    pos = bn.pos
+    bn.x_s = pos[2] / (xlim[1] - xlim[0])
+    bn.x_t = pos[0] - bn.x_s * xlim[0]
+    bn.y_s = pos[3] / (ylim[1] - ylim[0])
+    bn.y_t = pos[1] - bn.y_s * ylim[0]
+
+
+def _check_vlim(vlim):
+    """AUX function"""
+    return not np.isscalar(vlim) and vlim is not None
 
 
 def _imshow_tfr(ax, ch_idx, tmin, tmax, vmin, vmax, onselect, ylim=None,
                 tfr=None, freq=None, vline=None, x_label=None, y_label=None,
-                colorbar=False, picker=True, cmap='RdBu_r', title=None):
+                colorbar=False, picker=True, cmap='RdBu_r', title=None,
+                hline=None):
     """ Aux function to show time-freq map on topo """
     import matplotlib.pyplot as plt
     from matplotlib.widgets import RectangleSelector
+
     extent = (tmin, tmax, freq[0], freq[-1])
     img = ax.imshow(tfr[ch_idx], extent=extent, aspect="auto", origin="lower",
                     vmin=vmin, vmax=vmax, picker=picker, cmap=cmap)
@@ -210,10 +277,25 @@ def _imshow_tfr(ax, ch_idx, tmin, tmax, vmin, vmax, onselect, ylim=None,
     ax.RS = RectangleSelector(ax, onselect=onselect)  # reference must be kept
 
 
+def _imshow_tfr_unified(bn, ch_idx, tmin, tmax, vmin, vmax, onselect,
+                        ylim=None, tfr=None, freq=None, vline=None,
+                        x_label=None, y_label=None, colorbar=False,
+                        picker=True, cmap='RdBu_r', title=None, hline=None):
+    """Aux function to show multiple tfrs on topo using a single axes"""
+    _compute_scalings(bn, (tmin, tmax), (freq[0], freq[-1]))
+    ax = bn.ax
+    data_lines = bn.data_lines
+    extent = (bn.x_t + bn.x_s * tmin, bn.x_t + bn.x_s * tmax,
+              bn.y_t + bn.y_s * freq[0], bn.y_t + bn.y_s * freq[-1])
+    data_lines.append(ax.imshow(tfr[ch_idx], clip_on=True, clip_box=bn.pos,
+                                extent=extent, aspect="auto", origin="lower",
+                                vmin=vmin, vmax=vmax, cmap=cmap))
+
+
 def _plot_timeseries(ax, ch_idx, tmin, tmax, vmin, vmax, ylim, data, color,
                      times, vline=None, x_label=None, y_label=None,
-                     colorbar=False):
-    """ Aux function to show time series on topo """
+                     colorbar=False, hline=None):
+    """Aux function to show time series on topo split across multiple axes"""
     import matplotlib.pyplot as plt
     picker_flag = False
     for data_, color_ in zip(data, color):
@@ -226,24 +308,119 @@ def _plot_timeseries(ax, ch_idx, tmin, tmax, vmin, vmax, ylim, data, color,
     if vline:
         for x in vline:
             plt.axvline(x, color='w', linewidth=0.5)
+    if hline:
+        for y in hline:
+            plt.axhline(y, color='w', linewidth=0.5)
     if x_label is not None:
         plt.xlabel(x_label)
     if y_label is not None:
-        plt.ylabel(y_label)
+        if isinstance(y_label, list):
+            plt.ylabel(y_label[ch_idx])
+        else:
+            plt.ylabel(y_label)
     if colorbar:
         plt.colorbar()
 
 
-def _check_vlim(vlim):
-    """AUX function"""
-    return not np.isscalar(vlim) and vlim is not None
+def _plot_timeseries_unified(bn, ch_idx, tmin, tmax, vmin, vmax, ylim, data,
+                             color, times, vline=None, x_label=None,
+                             y_label=None, colorbar=False, hline=None):
+    """Aux function to show multiple time series on topo using a single axes"""
+    import matplotlib.pyplot as plt
+    if not (ylim and not any(v is None for v in ylim)):
+        ylim = np.array([np.min(data), np.max(data)])
+    # Translation and scale parameters to take data->under_ax normalized coords
+    _compute_scalings(bn, (tmin, tmax), ylim)
+    pos = bn.pos
+    data_lines = bn.data_lines
+    ax = bn.ax
+    # XXX These calls could probably be made faster by using collections
+    for data_, color_ in zip(data, color):
+        data_lines.append(ax.plot(
+            bn.x_t + bn.x_s * times, bn.y_t + bn.y_s * data_[ch_idx],
+            color_, clip_on=True, clip_box=pos)[0])
+    if vline:
+        vline = np.array(vline) * bn.x_s + bn.x_t
+        ax.vlines(vline, pos[1], pos[1] + pos[3], color='w', linewidth=0.5)
+    if hline:
+        hline = np.array(hline) * bn.y_s + bn.y_t
+        ax.hlines(hline, pos[0], pos[0] + pos[2], color='w', linewidth=0.5)
+    if x_label is not None:
+        ax.text(pos[0] + pos[2] / 2., pos[1], x_label,
+                horizontalalignment='center', verticalalignment='top')
+    if y_label is not None:
+        y_label = y_label[ch_idx] if isinstance(y_label, list) else y_label
+        ax.text(pos[0], pos[1] + pos[3] / 2., y_label,
+                horizontalignment='right', verticalalignment='middle',
+                rotation=90)
+    if colorbar:
+        plt.colorbar()
+
+
+def _erfimage_imshow(ax, ch_idx, tmin, tmax, vmin, vmax, ylim=None, data=None,
+                     epochs=None, sigma=None, order=None, scalings=None,
+                     vline=None, x_label=None, y_label=None, colorbar=False,
+                     cmap='RdBu_r'):
+    """Aux function to plot erfimage on sensor topography"""
+    from scipy import ndimage
+    import matplotlib.pyplot as plt
+    this_data = data[:, ch_idx, :].copy()
+
+    if callable(order):
+        order = order(epochs.times, this_data)
+
+    if order is not None:
+        this_data = this_data[order]
+
+    if sigma > 0.:
+        this_data = ndimage.gaussian_filter1d(this_data, sigma=sigma, axis=0)
+
+    ax.imshow(this_data, extent=[tmin, tmax, 0, len(data)], aspect='auto',
+              origin='lower', vmin=vmin, vmax=vmax, picker=True, cmap=cmap,
+              interpolation='nearest')
+
+    ax = plt.gca()
+    if x_label is not None:
+        ax.set_xlabel(x_label)
+    if y_label is not None:
+        ax.set_ylabel(y_label)
+    if colorbar:
+        plt.colorbar()
+
+
+def _erfimage_imshow_unified(bn, ch_idx, tmin, tmax, vmin, vmax, ylim=None,
+                             data=None, epochs=None, sigma=None, order=None,
+                             scalings=None, vline=None, x_label=None,
+                             y_label=None, colorbar=False, cmap='RdBu_r'):
+    """Aux function to plot erfimage topography using a single axis"""
+    from scipy import ndimage
+    _compute_scalings(bn, (tmin, tmax), (0, len(epochs.events)))
+    ax = bn.ax
+    data_lines = bn.data_lines
+    extent = (bn.x_t + bn.x_s * tmin, bn.x_t + bn.x_s * tmax, bn.y_t,
+              bn.y_t + bn.y_s * len(epochs.events))
+    this_data = data[:, ch_idx, :].copy()
+
+    if callable(order):
+        order = order(epochs.times, this_data)
+
+    if order is not None:
+        this_data = this_data[order]
+
+    if sigma > 0.:
+        this_data = ndimage.gaussian_filter1d(this_data, sigma=sigma, axis=0)
+
+    data_lines.append(ax.imshow(this_data, extent=extent, aspect='auto',
+                                origin='lower', vmin=vmin, vmax=vmax,
+                                picker=True, cmap=cmap,
+                                interpolation='nearest'))
 
 
 def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
                       border='none', ylim=None, scalings=None, title=None,
-                      proj=False, vline=[0.0], fig_facecolor='k',
+                      proj=False, vline=(0.,), hline=(0.,), fig_facecolor='k',
                       fig_background=None, axis_facecolor='k', font_color='w',
-                      show=True):
+                      merge_grads=False, show=True):
     """Plot 2D topography of evoked responses.
 
     Clicking on the plot of an individual sensor opens a new figure showing
@@ -267,10 +444,11 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
     border : str
         matplotlib borders style to be used for each sensor plot.
     ylim : dict | None
-        ylim for plots. The value determines the upper and lower subplot
-        limits. e.g. ylim = dict(eeg=[-200e-6, 200e6]). Valid keys are eeg,
-        mag, grad, misc. If None, the ylim parameter for each channel is
-        determined by the maximum absolute peak.
+        ylim for plots (after scaling has been applied). The value
+        determines the upper and lower subplot limits. e.g.
+        ylim = dict(eeg=[-20, 20]). Valid keys are eeg, mag, grad. If None,
+        the ylim parameter for each channel is determined by the maximum
+        absolute peak.
     scalings : dict | None
         The scalings of the channel types to be applied for plotting. If None,`
         defaults to `dict(eeg=1e6, grad=1e13, mag=1e15)`.
@@ -282,6 +460,8 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
         be shown.
     vline : list of floats | None
         The values at which to show a vertical line.
+    hline : list of floats | None
+        The values at which to show a horizontal line.
     fig_facecolor : str | obj
         The figure face color. Defaults to black.
     fig_background : None | numpy ndarray
@@ -291,6 +471,9 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
         The face color to be used for each sensor plot. Defaults to black.
     font_color : str | obj
         The color of text in the colorbar and title. Defaults to white.
+    merge_grads : bool
+        Whether to use RMS value of gradiometer pairs. Only works for Neuromag
+        data. Defaults to False.
     show : bool
         Show figure if True.
 
@@ -312,8 +495,8 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
                 else slice(len(colors)))
         color = cycle(colors[stop])
         if len(evoked) > len(colors):
-            warnings.warn('More evoked objects than colors available.'
-                          'You should pass a list of unique colors.')
+            warn('More evoked objects than colors available. You should pass '
+                 'a list of unique colors.')
     else:
         color = cycle([color])
 
@@ -321,45 +504,68 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
     if not all((e.times == times).all() for e in evoked):
         raise ValueError('All evoked.times must be the same')
 
+    evoked = [e.copy() for e in evoked]
     info = evoked[0].info
     ch_names = evoked[0].ch_names
+    scalings = _handle_default('scalings', scalings)
     if not all(e.ch_names == ch_names for e in evoked):
         raise ValueError('All evoked.picks must be the same')
     ch_names = _clean_names(ch_names)
+    if merge_grads:
+        picks = _pair_grad_sensors(info, topomap_coords=False)
+        chs = list()
+        for pick in picks[::2]:
+            ch = info['chs'][pick]
+            ch['ch_name'] = ch['ch_name'][:-1] + 'X'
+            chs.append(ch)
+        info['chs'] = chs
+        info['bads'] = list()  # bads dropped on pair_grad_sensors
+        info._update_redundant()
+        info._check_consistency()
+        new_picks = list()
+        for e in evoked:
+            data = _merge_grad_data(e.data[picks]) * scalings['grad']
+            e.data = data
+            new_picks.append(range(len(data)))
+        picks = new_picks
+        types_used = ['grad']
+        y_label = 'RMS amplitude (%s)' % _handle_default('units')['grad']
 
     if layout is None:
-        from ..channels.layout import find_layout
         layout = find_layout(info)
 
-    # XXX. at the moment we are committed to 1- / 2-sensor-types layouts
-    chs_in_layout = set(layout.names) & set(ch_names)
-    types_used = set(channel_type(info, ch_names.index(ch))
-                     for ch in chs_in_layout)
-    # remove possible reference meg channels
-    types_used = set.difference(types_used, set('ref_meg'))
-    # one check for all vendors
-    meg_types = set(('mag', 'grad'))
-    is_meg = len(set.intersection(types_used, meg_types)) > 0
-    if is_meg:
-        types_used = list(types_used)[::-1]  # -> restore kwarg order
-        picks = [pick_types(info, meg=kk, ref_meg=False, exclude=[])
-                 for kk in types_used]
-    else:
-        types_used_kwargs = dict((t, True) for t in types_used)
-        picks = [pick_types(info, meg=False, exclude=[], **types_used_kwargs)]
-    assert isinstance(picks, list) and len(types_used) == len(picks)
-
-    scalings = _handle_default('scalings', scalings)
-    evoked = [e.copy() for e in evoked]
-    for e in evoked:
-        for pick, t in zip(picks, types_used):
-            e.data[pick] = e.data[pick] * scalings[t]
+    if not merge_grads:
+        # XXX. at the moment we are committed to 1- / 2-sensor-types layouts
+        chs_in_layout = set(layout.names) & set(ch_names)
+        types_used = set(channel_type(info, ch_names.index(ch))
+                         for ch in chs_in_layout)
+        # remove possible reference meg channels
+        types_used = set.difference(types_used, set('ref_meg'))
+        # one check for all vendors
+        meg_types = set(('mag', 'grad'))
+        is_meg = len(set.intersection(types_used, meg_types)) > 0
+        if is_meg:
+            types_used = list(types_used)[::-1]  # -> restore kwarg order
+            picks = [pick_types(info, meg=kk, ref_meg=False, exclude=[])
+                     for kk in types_used]
+        else:
+            types_used_kwargs = dict((t, True) for t in types_used)
+            picks = [pick_types(info, meg=False, exclude=[],
+                                **types_used_kwargs)]
+        assert isinstance(picks, list) and len(types_used) == len(picks)
 
-    if proj is True and all(e.proj is not True for e in evoked):
-        evoked = [e.apply_proj() for e in evoked]
-    elif proj == 'interactive':  # let it fail early.
         for e in evoked:
-            _check_delayed_ssp(e)
+            for pick, ch_type in zip(picks, types_used):
+                e.data[pick] = e.data[pick] * scalings[ch_type]
+
+        if proj is True and all(e.proj is not True for e in evoked):
+            evoked = [e.apply_proj() for e in evoked]
+        elif proj == 'interactive':  # let it fail early.
+            for e in evoked:
+                _check_delayed_ssp(e)
+        # Y labels for picked plots must be reconstructed
+        y_label = ['Amplitude (%s)' % _handle_default('units')[channel_type(
+            info, ch_idx)] for ch_idx in range(len(chs_in_layout))]
 
     if ylim is None:
         def set_ylim(x):
@@ -378,15 +584,19 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
     else:
         raise ValueError('ylim must be None ore a dict')
 
-    plot_fun = partial(_plot_timeseries, data=[e.data for e in evoked],
-                       color=color, times=times, vline=vline)
+    data = [e.data for e in evoked]
+    show_func = partial(_plot_timeseries_unified, data=data,
+                        color=color, times=times, vline=vline, hline=hline)
+    click_func = partial(_plot_timeseries, data=data,
+                         color=color, times=times, vline=vline, hline=hline)
 
-    fig = _plot_topo(info=info, times=times, show_func=plot_fun, layout=layout,
-                     decim=1, colorbar=False, ylim=ylim_, cmap=None,
+    fig = _plot_topo(info=info, times=times, show_func=show_func,
+                     click_func=click_func, layout=layout,
+                     colorbar=False, ylim=ylim_, cmap=None,
                      layout_scale=layout_scale, border=border,
                      fig_facecolor=fig_facecolor, font_color=font_color,
-                     axis_facecolor=axis_facecolor,
-                     title=title, x_label='Time (s)', vline=vline)
+                     axis_facecolor=axis_facecolor, title=title,
+                     x_label='Time (s)', y_label=y_label, unified=True)
 
     if fig_background is not None:
         add_background_image(fig, fig_background)
@@ -395,7 +605,7 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
         for e in evoked:
             _check_delayed_ssp(e)
         params = dict(evokeds=evoked, times=times,
-                      plot_update_proj_callback=_plot_update_evoked_topo,
+                      plot_update_proj_callback=_plot_update_evoked_topo_proj,
                       projs=evoked[0].info['projs'], fig=fig)
         _draw_proj_checkbox(None, params)
 
@@ -403,68 +613,24 @@ def _plot_evoked_topo(evoked, layout=None, layout_scale=0.945, color=None,
     return fig
 
 
-def _plot_update_evoked_topo(params, bools):
+def _plot_update_evoked_topo_proj(params, bools):
     """Helper function to update topo sensor plots"""
-    evokeds, times, fig = [params[k] for k in ('evokeds', 'times', 'fig')]
-
-    projs = [proj for ii, proj in enumerate(params['projs'])
-             if ii in np.where(bools)[0]]
-
+    evokeds = [e.copy() for e in params['evokeds']]
+    fig = params['fig']
+    projs = [proj for proj, b in zip(params['projs'], bools) if b]
     params['proj_bools'] = bools
-    evokeds = [e.copy() for e in evokeds]
     for e in evokeds:
-        e.info['projs'] = []
-        e.add_proj(projs)
+        e.add_proj(projs, remove_existing=True)
         e.apply_proj()
 
     # make sure to only modify the time courses, not the ticks
-    axes = fig.get_axes()
-    n_lines = len(axes[0].lines)
-    n_diff = len(evokeds) - n_lines
-    ax_slice = slice(abs(n_diff)) if n_diff < 0 else slice(n_lines)
-    for ax in axes:
-        lines = ax.lines[ax_slice]
-        for line, evoked in zip(lines, evokeds):
-            line.set_data(times, evoked.data[ax._mne_ch_idx])
+    for ax in fig.axes[0]._mne_axs:
+        for line, evoked in zip(ax.data_lines, evokeds):
+            line.set_ydata(ax.y_t + ax.y_s * evoked.data[ax._mne_ch_idx])
 
     fig.canvas.draw()
 
 
-def _erfimage_imshow(ax, ch_idx, tmin, tmax, vmin, vmax, ylim=None,
-                     data=None, epochs=None, sigma=None,
-                     order=None, scalings=None, vline=None,
-                     x_label=None, y_label=None, colorbar=False,
-                     cmap='RdBu_r'):
-    """Aux function to plot erfimage on sensor topography"""
-    from scipy import ndimage
-    import matplotlib.pyplot as plt
-    this_data = data[:, ch_idx, :].copy()
-    ch_type = channel_type(epochs.info, ch_idx)
-    if ch_type not in scalings:
-        raise KeyError('%s channel type not in scalings' % ch_type)
-    this_data *= scalings[ch_type]
-
-    if callable(order):
-        order = order(epochs.times, this_data)
-
-    if order is not None:
-        this_data = this_data[order]
-
-    if sigma > 0.:
-        this_data = ndimage.gaussian_filter1d(this_data, sigma=sigma, axis=0)
-
-    ax.imshow(this_data, extent=[tmin, tmax, 0, len(data)], aspect='auto',
-              origin='lower', vmin=vmin, vmax=vmax, picker=True,
-              cmap=cmap, interpolation='nearest')
-
-    if x_label is not None:
-        plt.xlabel(x_label)
-    if y_label is not None:
-        plt.ylabel(y_label)
-    if colorbar:
-        plt.colorbar()
-
-
 def plot_topo_image_epochs(epochs, layout=None, sigma=0., vmin=None,
                            vmax=None, colorbar=True, order=None, cmap='RdBu_r',
                            layout_scale=.95, title=None, scalings=None,
@@ -521,24 +687,29 @@ def plot_topo_image_epochs(epochs, layout=None, sigma=0., vmin=None,
     """
     scalings = _handle_default('scalings', scalings)
     data = epochs.get_data()
-    if vmin is None:
-        vmin = data.min()
-    if vmax is None:
-        vmax = data.max()
+    scale_coeffs = list()
+    for idx in range(epochs.info['nchan']):
+        ch_type = channel_type(epochs.info, idx)
+        scale_coeffs.append(scalings.get(ch_type, 1))
+    for epoch_data in data:
+        epoch_data *= np.asarray(scale_coeffs)[:, np.newaxis]
+    vmin, vmax = _setup_vmin_vmax(data, vmin, vmax)
+
     if layout is None:
-        from ..channels.layout import find_layout
         layout = find_layout(epochs.info)
 
+    show_func = partial(_erfimage_imshow_unified, scalings=scalings,
+                        order=order, data=data, epochs=epochs, sigma=sigma,
+                        cmap=cmap)
     erf_imshow = partial(_erfimage_imshow, scalings=scalings, order=order,
-                         data=data, epochs=epochs, sigma=sigma,
-                         cmap=cmap)
+                         data=data, epochs=epochs, sigma=sigma, cmap=cmap)
 
     fig = _plot_topo(info=epochs.info, times=epochs.times,
-                     show_func=erf_imshow, layout=layout, decim=1,
+                     click_func=erf_imshow, show_func=show_func, layout=layout,
                      colorbar=colorbar, vmin=vmin, vmax=vmax, cmap=cmap,
                      layout_scale=layout_scale, title=title,
-                     fig_facecolor=fig_facecolor,
-                     font_color=font_color, border=border,
-                     x_label='Time (s)', y_label='Epoch')
+                     fig_facecolor=fig_facecolor, font_color=font_color,
+                     border=border, x_label='Time (s)', y_label='Epoch',
+                     unified=True, img=True)
     plt_show(show)
     return fig
diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py
index e035e6d..06da62d 100644
--- a/mne/viz/topomap.py
+++ b/mne/viz/topomap.py
@@ -18,21 +18,21 @@ from scipy import linalg
 
 from ..baseline import rescale
 from ..io.constants import FIFF
-from ..io.pick import pick_types
-from ..utils import _clean_names, _time_mask, verbose, logger
+from ..io.pick import (pick_types, _picks_by_type, channel_type, pick_info,
+                       _pick_data_channels)
+from ..utils import _clean_names, _time_mask, verbose, logger, warn
 from .utils import (tight_layout, _setup_vmin_vmax, _prepare_trellis,
                     _check_delayed_ssp, _draw_proj_checkbox, figure_nobar,
-                    plt_show)
-from ..time_frequency import compute_epochs_psd
+                    plt_show, _process_times)
+from ..time_frequency import psd_multitaper
 from ..defaults import _handle_default
 from ..channels.layout import _find_topomap_coords
-from ..fixes import _get_argrelmax
-from ..externals.six import string_types
+from ..io.meas_info import Info
 
 
 def _prepare_topo_plot(inst, ch_type, layout):
     """"Aux Function"""
-    info = copy.deepcopy(inst.info)
+    info = copy.deepcopy(inst if isinstance(inst, Info) else inst.info)
 
     if layout is None and ch_type is not 'eeg':
         from ..channels import find_layout
@@ -40,9 +40,11 @@ def _prepare_topo_plot(inst, ch_type, layout):
     elif layout == 'auto':
         layout = None
 
-    info['ch_names'] = _clean_names(info['ch_names'])
+    clean_ch_names = _clean_names(info['ch_names'])
     for ii, this_ch in enumerate(info['chs']):
-        this_ch['ch_name'] = info['ch_names'][ii]
+        this_ch['ch_name'] = clean_ch_names[ii]
+    info._update_redundant()
+    info._check_consistency()
 
     # special case for merging grad channels
     if (ch_type == 'grad' and FIFF.FIFFV_COIL_VV_PLANAR_T1 in
@@ -72,9 +74,8 @@ def _prepare_topo_plot(inst, ch_type, layout):
                 if this_name in names:
                     pos.append(layout.pos[names.index(this_name)])
                 else:
-                    logger.warning('Failed to locate %s channel positions from'
-                                   ' layout. Inferring channel positions from '
-                                   'data.' % ch_type)
+                    warn('Failed to locate %s channel positions from layout. '
+                         'Inferring channel positions from data.' % ch_type)
                     pos = _find_topomap_coords(info, picks)
                     break
 
@@ -120,7 +121,7 @@ def _plot_update_evoked_topomap(params, bools):
     params['fig'].canvas.draw()
 
 
-def plot_projs_topomap(projs, layout=None, cmap='RdBu_r', sensors=True,
+def plot_projs_topomap(projs, layout=None, cmap=None, sensors=True,
                        colorbar=False, res=64, size=1, show=True,
                        outlines='head', contours=6, image_interp='bilinear',
                        axes=None):
@@ -134,8 +135,9 @@ def plot_projs_topomap(projs, layout=None, cmap='RdBu_r', sensors=True,
         Layout instance specifying sensor positions (does not need to be
         specified for Neuromag data). Or a list of Layout if projections
         are from different sensor types.
-    cmap : matplotlib colormap
-        Colormap.
+    cmap : matplotlib colormap | None
+        Colormap to use. If None, 'Reds' is used for all positive data,
+        otherwise defaults to 'RdBu_r'.
     sensors : bool | str
         Add markers for sensor locations to the plot. Accepts matplotlib plot
         format string (e.g., 'r+' for red plusses). If True, a circle will be
@@ -231,7 +233,7 @@ def plot_projs_topomap(projs, layout=None, cmap='RdBu_r', sensors=True,
 
         if len(idx):
             plot_topomap(data, pos[:, :2], vmax=None, cmap=cmap,
-                         sensors=sensors, res=res, axis=axes[proj_idx],
+                         sensors=sensors, res=res, axes=axes[proj_idx],
                          outlines=outlines, contours=contours,
                          image_interp=image_interp, show=False)
             if colorbar:
@@ -322,6 +324,17 @@ def _check_outlines(pos, outlines, head_pos=None):
     return pos, outlines
 
 
+def _draw_outlines(ax, outlines):
+    """Helper for drawing the outlines for a topomap."""
+    outlines_ = dict([(k, v) for k, v in outlines.items() if k not in
+                      ['patch', 'autoshrink']])
+    for key, (x_coord, y_coord) in outlines_.items():
+        if 'mask' in key:
+            continue
+        ax.plot(x_coord, y_coord, color='k', linewidth=1, clip_on=False)
+    return outlines_
+
+
 def _griddata(x, y, v, xi, yi):
     """Aux function"""
     xy = x.ravel() + y.ravel() * -1j
@@ -364,19 +377,23 @@ def _plot_sensors(pos_x, pos_y, sensors, ax):
         ax.plot(pos_x, pos_y, sensors)
 
 
-def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
-                 res=64, axis=None, names=None, show_names=False, mask=None,
+def plot_topomap(data, pos, vmin=None, vmax=None, cmap=None, sensors=True,
+                 res=64, axes=None, names=None, show_names=False, mask=None,
                  mask_params=None, outlines='head', image_mask=None,
                  contours=6, image_interp='bilinear', show=True,
-                 head_pos=None, onselect=None):
+                 head_pos=None, onselect=None, axis=None):
     """Plot a topographic map as image
 
     Parameters
     ----------
-    data : array, length = n_points
+    data : array, shape (n_chan,)
         The data values to plot.
-    pos : array, shape = (n_points, 2)
-        For each data point, the x and y coordinates.
+    pos : array, shape (n_chan, 2) | instance of Info
+        Location information for the data points(/channels).
+        If an array, for each data point, the x and y coordinates.
+        If an Info object, it must contain only one data type and
+        exactly `len(data)` data channels, and the x/y coordinates will
+        be inferred from this Info object.
     vmin : float | callable | None
         The value specifying the lower bound of the color range.
         If None, and vmax is None, -vmax is used. Else np.min(data).
@@ -385,16 +402,17 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
         The value specifying the upper bound of the color range.
         If None, the maximum absolute value is used. If callable, the output
         equals vmax(data). Defaults to None.
-    cmap : matplotlib colormap
-        Colormap.
+    cmap : matplotlib colormap | None
+        Colormap to use. If None, 'Reds' is used for all positive data,
+        otherwise defaults to 'RdBu_r'.
     sensors : bool | str
         Add markers for sensor locations to the plot. Accepts matplotlib plot
         format string (e.g., 'r+' for red plusses). If True, a circle will be
         used (via .add_artist). Defaults to True.
     res : int
         The resolution of the topomap image (n pixels along each side).
-    axis : instance of Axis | None
-        The axis to plot to. If None, the current axis will be used.
+    axes : instance of Axes | None
+        The axes to plot to. If None, the current axes will be used.
     names : list | None
         List of channel names. If None, channel names are not plotted.
     show_names : bool | callable
@@ -423,7 +441,7 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
         automated shrinking of the positions due to points outside the outline.
         Alternatively, a matplotlib patch object can be passed for advanced
         masking options, either directly or as a function that returns patches
-        (required for multi-axis plots). If None, nothing will be drawn.
+        (required for multi-axes plots). If None, nothing will be drawn.
         Defaults to 'head'.
     image_mask : ndarray of bool, shape (res, res) | None
         The image mask to cover the interpolated surface. If None, it will be
@@ -444,6 +462,8 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
         Handle for a function that is called when the user selects a set of
         channels by rectangle selection (matplotlib ``RectangleSelector``). If
         None interactive selection is disabled. Defaults to None.
+    axis : instance of Axes | None
+        Deprecated. Will be removed in 0.13. Use ``axes`` instead.
 
     Returns
     -------
@@ -456,6 +476,35 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
     from matplotlib.widgets import RectangleSelector
 
     data = np.asarray(data)
+
+    if isinstance(pos, Info):  # infer pos from Info object
+        picks = _pick_data_channels(pos)  # pick only data channels
+        pos = pick_info(pos, picks)
+
+        # check if there is only 1 channel type, and n_chans matches the data
+        ch_type = set(channel_type(pos, idx)
+                      for idx, _ in enumerate(pos["chs"]))
+        info_help = ("Pick Info with e.g. mne.pick_info and "
+                     "mne.channels.channel_indices_by_type.")
+        if len(ch_type) > 1:
+            raise ValueError("Multiple channel types in Info structure. " +
+                             info_help)
+        elif len(pos["chs"]) != data.shape[0]:
+            raise ValueError("Number of channels in the Info object and "
+                             "the data array does not match. " + info_help)
+        else:
+            ch_type = ch_type.pop()
+
+        if any(type_ in ch_type for type_ in ('planar', 'grad')):
+            # deal with grad pairs
+            from ..channels.layout import (_merge_grad_data, find_layout,
+                                           _pair_grad_sensors)
+            picks, pos = _pair_grad_sensors(pos, find_layout(pos))
+            data = _merge_grad_data(data[picks]).reshape(-1)
+        else:
+            picks = list(range(data.shape[0]))
+            pos = _find_topomap_coords(pos, picks=picks)
+
     if data.ndim > 1:
         raise ValueError("Data needs to be array of shape (n_sensors,); got "
                          "shape %s." % str(data.shape))
@@ -484,11 +533,18 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
         raise ValueError("Data and pos need to be of same length. Got data of "
                          "length %s, pos of length %s" % (len(data), len(pos)))
 
-    vmin, vmax = _setup_vmin_vmax(data, vmin, vmax)
+    norm = min(data) >= 0
+    vmin, vmax = _setup_vmin_vmax(data, vmin, vmax, norm)
+    if cmap is None:
+        cmap = 'Reds' if norm else 'RdBu_r'
 
     pos, outlines = _check_outlines(pos, outlines, head_pos)
 
-    ax = axis if axis else plt.gca()
+    if axis is not None:
+        axes = axis
+        warn('axis parameter is deprecated and will be removed in 0.13. '
+             'Use axes instead.', DeprecationWarning)
+    ax = axes if axes else plt.gca()
     pos_x, pos_y = _prepare_topomap(pos, ax)
     if outlines is None:
         xmin, xmax = pos_x.min(), pos_x.max()
@@ -556,7 +612,6 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
                                  transform=ax.transData)
     if _is_default_outlines or patch is not None:
         im.set_clip_path(patch_)
-        # ax.set_clip_path(patch_)
         if cont is not None:
             for col in cont.collections:
                 col.set_clip_path(patch_)
@@ -573,12 +628,7 @@ def plot_topomap(data, pos, vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
         ax.plot(pos_x[idx], pos_y[idx], **mask_params)
 
     if isinstance(outlines, dict):
-        outlines_ = dict([(k, v) for k, v in outlines.items() if k not in
-                          ['patch', 'autoshrink']])
-        for k, (x, y) in outlines_.items():
-            if 'mask' in k:
-                continue
-            ax.plot(x, y, color='k', linewidth=linewidth, clip_on=False)
+        _draw_outlines(ax, outlines)
 
     if show_names:
         if names is None:
@@ -785,7 +835,7 @@ def plot_ica_components(ica, picks=None, ch_type=None, res=64,
         data_ = _merge_grad_data(data_) if merge_grads else data_
         vmin_, vmax_ = _setup_vmin_vmax(data_, vmin, vmax)
         im = plot_topomap(data_.flatten(), pos, vmin=vmin_, vmax=vmax_,
-                          res=res, axis=ax, cmap=cmap, outlines=outlines,
+                          res=res, axes=ax, cmap=cmap, outlines=outlines,
                           image_mask=image_mask, contours=contours,
                           image_interp=image_interp, show=False)[0]
         if colorbar:
@@ -795,9 +845,7 @@ def plot_ica_components(ica, picks=None, ch_type=None, res=64,
             cbar.ax.tick_params(labelsize=12)
             cbar.set_ticks((vmin_, vmax_))
             cbar.ax.set_title('AU', fontsize=10)
-        ax.set_yticks([])
-        ax.set_xticks([])
-        ax.set_frame_on(False)
+        _hide_frame(ax)
     tight_layout(fig=fig)
     fig.subplots_adjust(top=0.95)
     fig.canvas.draw()
@@ -925,9 +973,7 @@ def plot_tfr_topomap(tfr, tmin=None, tmax=None, fmin=None, fmax=None,
         names = None
 
     data = tfr.data
-
-    if mode is not None and baseline is not None:
-        data = rescale(data, tfr.times, baseline, mode, copy=True)
+    data = rescale(data, tfr.times, baseline, mode, copy=True)
 
     # crop time
     itmin, itmax = None, None
@@ -964,9 +1010,7 @@ def plot_tfr_topomap(tfr, tmin=None, tmax=None, fmin=None, fmax=None,
         fig = axes.figure
         ax = axes
 
-    ax.set_yticks([])
-    ax.set_xticks([])
-    ax.set_frame_on(False)
+    _hide_frame(ax)
 
     if title is not None:
         ax.set_title(title)
@@ -977,7 +1021,7 @@ def plot_tfr_topomap(tfr, tmin=None, tmax=None, fmin=None, fmax=None,
                                  layout=layout)
 
     im, _ = plot_topomap(data[:, 0], pos, vmin=vmin, vmax=vmax,
-                         axis=ax, cmap=cmap, image_interp='bilinear',
+                         axes=ax, cmap=cmap, image_interp='bilinear',
                          contours=False, names=names, show_names=show_names,
                          show=False, onselect=selection_callback)
 
@@ -994,7 +1038,7 @@ def plot_tfr_topomap(tfr, tmin=None, tmax=None, fmin=None, fmax=None,
 
 
 def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
-                        vmin=None, vmax=None, cmap='RdBu_r', sensors=True,
+                        vmin=None, vmax=None, cmap=None, sensors=True,
                         colorbar=True, scale=None, scale_time=1e3, unit=None,
                         res=64, size=1, cbar_fmt='%3.1f',
                         time_format='%01d ms', proj=False, show=True,
@@ -1031,9 +1075,9 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
         The value specifying the upper bound of the color range.
         If None, the maximum absolute value is used. If callable, the output
         equals vmax(data). Defaults to None.
-    cmap : matplotlib colormap
-        Colormap. For magnetometers and eeg defaults to 'RdBu_r', else
-        'Reds'.
+    cmap : matplotlib colormap | None
+        Colormap to use. If None, 'Reds' is used for all positive data,
+        otherwise defaults to 'RdBu_r'.
     sensors : bool | str
         Add markers for sensor locations to the plot. Accepts matplotlib plot
         format string (e.g., 'r+' for red plusses). If True, a circle will be
@@ -1072,7 +1116,7 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
         Title. If None (default), no title is displayed.
     mask : ndarray of bool, shape (n_channels, n_times) | None
         The channels to be marked as significant at a given time point.
-        Indicies set to `True` will be considered. Defaults to None.
+        Indices set to `True` will be considered. Defaults to None.
     mask_params : dict | None
         Additional plotting parameters for plotting significant sensors.
         Default (None) equals::
@@ -1126,30 +1170,29 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
     mask_params['markersize'] *= size / 2.
     mask_params['markeredgewidth'] *= size / 2.
 
-    if isinstance(axes, plt.Axes):
-        axes = [axes]
-
-    if isinstance(times, string_types):
-        if times == "peaks":
-            npeaks = 10 if axes is None else len(axes)
-            times = _find_peaks(evoked, npeaks)
-        elif times == "auto":
-            if axes is None:
-                times = np.linspace(evoked.times[0], evoked.times[-1], 10)
-            else:
-                times = np.linspace(evoked.times[0], evoked.times[-1],
-                                    len(axes))
-    elif np.isscalar(times):
-        times = [times]
+    picks, pos, merge_grads, names, ch_type = _prepare_topo_plot(
+        evoked, ch_type, layout)
 
-    times = np.array(times)
+    # project before picks
+    if proj is True and evoked.proj is not True:
+        data = evoked.copy().apply_proj().data
+    else:
+        data = evoked.data
 
-    if times.ndim != 1:
-        raise ValueError('times must be 1D, got %d dimensions' % times.ndim)
-    if len(times) > 20:
-        raise RuntimeError('Too many plots requested. Please pass fewer '
-                           'than 20 time instants.')
+    evoked = evoked.copy().pick_channels(
+        [evoked.ch_names[pick] for pick in picks])
 
+    if axes is not None:
+        if isinstance(axes, plt.Axes):
+            axes = [axes]
+        times = _process_times(evoked, times, n_peaks=len(axes))
+    else:
+        times = _process_times(evoked, times, n_peaks=None)
+    space = 1 / (2. * evoked.info['sfreq'])
+    if (max(times) > max(evoked.times) + space or
+            min(times) < min(evoked.times) - space):
+        raise ValueError('Times should be between {0:0.3f} and '
+                         '{1:0.3f}.'.format(evoked.times[0], evoked.times[-1]))
     n_times = len(times)
     nax = n_times + bool(colorbar)
     width = size * nax
@@ -1163,21 +1206,11 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
             else:
                 axes.append(plt.subplot(1, n_times, ax_idx + 1))
     elif colorbar:
-        logger.warning('Colorbar is drawn to the rightmost column of the '
-                       'figure.\nBe sure to provide enough space for it '
-                       'or turn it off with colorbar=False.')
+        warn('Colorbar is drawn to the rightmost column of the figure. Be '
+             'sure to provide enough space for it or turn it off with '
+             'colorbar=False.')
     if len(axes) != n_times:
         raise RuntimeError('Axes and times must be equal in sizes.')
-    tmin, tmax = evoked.times[[0, -1]]
-    _time_comp = _time_mask(times=times, tmin=tmin,  tmax=tmax)
-    if not np.all(_time_comp):
-        raise ValueError('Times should be between {0:0.3f} and {1:0.3f}. (Got '
-                         '{2}).'.format(tmin, tmax,
-                                        ['%03.f' % t
-                                         for t in times[_time_comp]]))
-
-    picks, pos, merge_grads, names, ch_type = _prepare_topo_plot(
-        evoked, ch_type, layout)
 
     if ch_type.startswith('planar'):
         key = 'grad'
@@ -1195,12 +1228,11 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
     fig = axes[0].get_figure()
     fig.subplots_adjust(left=w_frame, right=1 - w_frame, bottom=0,
                         top=1 - top_frame)
-    time_idx = [np.where(evoked.times >= t)[0][0] for t in times]
+    # find first index that's >= (to rounding error) to each time point
+    time_idx = [np.where(_time_mask(evoked.times, tmin=t,
+                         tmax=None, sfreq=evoked.info['sfreq']))[0][0]
+                for t in times]
 
-    if proj is True and evoked.proj is not True:
-        data = evoked.copy().apply_proj().data
-    else:
-        data = evoked.data
     if average is None:
         data = data[np.ix_(picks, time_idx)]
     elif isinstance(average, float):
@@ -1225,8 +1257,6 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
         from ..channels.layout import _merge_grad_data
         data = _merge_grad_data(data)
 
-    vmin, vmax = _setup_vmin_vmax(data, vmin, vmax)
-
     images, contours_ = [], []
 
     if mask is not None:
@@ -1244,7 +1274,7 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
                               sensors=sensors, res=res, names=names,
                               show_names=show_names, cmap=cmap,
                               mask=mask_[:, idx] if mask is not None else None,
-                              mask_params=mask_params, axis=axes[idx],
+                              mask_params=mask_params, axes=axes[idx],
                               outlines=outlines, image_mask=image_mask,
                               contours=contours, image_interp=image_interp,
                               show=False)
@@ -1259,19 +1289,21 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
         plt.suptitle(title, verticalalignment='top', size='x-large')
 
     if colorbar:
-        cax = plt.subplot(1, n_times + 1, n_times + 1)
+        # works both when fig axes pre-defined and when not
+        n_fig_axes = max(nax, len(fig.get_axes()))
+        cax = plt.subplot(1, n_fig_axes + 1, n_fig_axes + 1)
         # resize the colorbar (by default the color fills the whole axes)
         cpos = cax.get_position()
         if size <= 1:
-            cpos.x0 = 1 - (.7 + .1 / size) / nax
-        cpos.x1 = cpos.x0 + .1 / nax
+            cpos.x0 = 1 - (.7 + .1 / size) / n_fig_axes
+        cpos.x1 = cpos.x0 + .1 / n_fig_axes
         cpos.y0 = .2
         cpos.y1 = .7
         cax.set_position(cpos)
         if unit is not None:
             cax.set_title(unit)
         cbar = fig.colorbar(images[-1], ax=cax, cax=cax, format=cbar_fmt)
-        cbar.set_ticks([vmin, 0, vmax])
+        cbar.set_ticks([cbar.vmin, 0, cbar.vmax])
 
     if proj == 'interactive':
         _check_delayed_ssp(evoked)
@@ -1293,15 +1325,13 @@ def _plot_topomap_multi_cbar(data, pos, ax, title=None, unit=None,
     import matplotlib.pyplot as plt
     from mpl_toolkits.axes_grid1 import make_axes_locatable
 
-    ax.set_yticks([])
-    ax.set_xticks([])
-    ax.set_frame_on(False)
+    _hide_frame(ax)
     vmin = np.min(data) if vmin is None else vmin
     vmax = np.max(data) if vmax is None else vmax
 
     if title is not None:
         ax.set_title(title, fontsize=10)
-    im, _ = plot_topomap(data, pos, vmin=vmin, vmax=vmax, axis=ax,
+    im, _ = plot_topomap(data, pos, vmin=vmin, vmax=vmax, axes=ax,
                          cmap=cmap, image_interp='bilinear', contours=False,
                          show=False)
 
@@ -1317,9 +1347,9 @@ def _plot_topomap_multi_cbar(data, pos, ax, title=None, unit=None,
 
 @verbose
 def plot_epochs_psd_topomap(epochs, bands=None, vmin=None, vmax=None,
-                            tmin=None, tmax=None,
-                            proj=False, n_fft=256, ch_type=None,
-                            n_overlap=0, layout=None,
+                            tmin=None, tmax=None, proj=False,
+                            bandwidth=None, adaptive=False, low_bias=True,
+                            normalization='length', ch_type=None, layout=None,
                             cmap='RdBu_r', agg_fun=None, dB=False, n_jobs=1,
                             normalize=False, cbar_fmt='%0.3f',
                             outlines='head', show=True, verbose=None):
@@ -1350,14 +1380,24 @@ def plot_epochs_psd_topomap(epochs, bands=None, vmin=None, vmax=None,
         End time to consider.
     proj : bool
         Apply projection.
-    n_fft : int
-        Number of points to use in Welch FFT calculations.
+    bandwidth : float
+        The bandwidth of the multi taper windowing function in Hz. The default
+        value is a window half-bandwidth of 4 Hz.
+    adaptive : bool
+        Use adaptive weights to combine the tapered spectra into PSD
+        (slow, use n_jobs >> 1 to speed up computation).
+    low_bias : bool
+        Only use tapers with more than 90% spectral concentration within
+        bandwidth.
+    normalization : str
+        Either "full" or "length" (default). If "full", the PSD will
+        be normalized by the sampling rate as well as the length of
+        the signal (as in nitime).
     ch_type : 'mag' | 'grad' | 'planar1' | 'planar2' | 'eeg' | None
         The channel type to plot. For 'grad', the gradiometers are collected in
-        pairs and the RMS for each pair is plotted.
-        If None, then channels are chosen in the order given above.
-    n_overlap : int
-        The number of points of overlap between blocks.
+        pairs and the RMS for each pair is plotted. If None, then first
+        available channel type from order given above is used. Defaults to
+        None.
     layout : None | Layout
         Layout instance specifying sensor positions (does not need to
         be specified for Neuromag data). If possible, the correct layout
@@ -1377,7 +1417,7 @@ def plot_epochs_psd_topomap(epochs, bands=None, vmin=None, vmax=None,
     n_jobs : int
         Number of jobs to run in parallel.
     normalize : bool
-        If True, each band will be devided by the total power. Defaults to
+        If True, each band will be divided by the total power. Defaults to
         False.
     cbar_fmt : str
         The colorbar format. Defaults to '%0.3f'.
@@ -1408,10 +1448,11 @@ def plot_epochs_psd_topomap(epochs, bands=None, vmin=None, vmax=None,
     picks, pos, merge_grads, names, ch_type = _prepare_topo_plot(
         epochs, ch_type, layout)
 
-    psds, freqs = compute_epochs_psd(epochs, picks=picks, n_fft=n_fft,
-                                     tmin=tmin, tmax=tmax,
-                                     n_overlap=n_overlap, proj=proj,
-                                     n_jobs=n_jobs)
+    psds, freqs = psd_multitaper(epochs, tmin=tmin, tmax=tmax,
+                                 bandwidth=bandwidth, adaptive=adaptive,
+                                 low_bias=low_bias,
+                                 normalization=normalization, picks=picks,
+                                 proj=proj, n_jobs=n_jobs)
     psds = np.mean(psds, axis=0)
 
     if merge_grads:
@@ -1464,7 +1505,7 @@ def plot_psds_topomap(
         following the application of `agg_fun`. Only valid if normalize is
         False.
     normalize : bool
-        If True, each band will be devided by the total power. Defaults to
+        If True, each band will be divided by the total power. Defaults to
         False.
     cbar_fmt : str
         The colorbar format. Defaults to '%0.3f'.
@@ -1527,6 +1568,44 @@ def plot_psds_topomap(
     return fig
 
 
+def plot_layout(layout, show=True):
+    """Plot the sensor positions.
+
+    Parameters
+    ----------
+    layout : None | Layout
+        Layout instance specifying sensor positions.
+    show : bool
+        Show figure if True. Defaults to True.
+
+    Returns
+    -------
+    fig : instance of matplotlib figure
+        Figure containing the sensor topography.
+
+    Notes
+    -----
+
+    .. versionadded:: 0.12.0
+
+    """
+    import matplotlib.pyplot as plt
+    fig = plt.figure()
+    ax = fig.add_subplot(111)
+    fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=None,
+                        hspace=None)
+    ax.set_xticks([])
+    ax.set_yticks([])
+    pos = [(p[0] + p[2] / 2., p[1] + p[3] / 2.) for p in layout.pos]
+    pos, outlines = _check_outlines(pos, 'head')
+    _draw_outlines(ax, outlines)
+    for ii, (this_pos, ch_id) in enumerate(zip(pos, layout.names)):
+        ax.annotate(ch_id, xy=this_pos[:2], horizontalalignment='center',
+                    verticalalignment='center', size='x-small')
+    plt_show(show)
+    return fig
+
+
 def _onselect(eclick, erelease, tfr, pos, ch_type, itmin, itmax, ifmin, ifmax,
               cmap, fig, layout=None):
     """Callback called from topomap for drawing average tfr over channels."""
@@ -1593,34 +1672,270 @@ def _onselect(eclick, erelease, tfr, pos, ch_type, itmin, itmax, ifmin, ifmax,
     plt_show(True)
 
 
-def _find_peaks(evoked, npeaks):
-    """Helper function for finding peaks from evoked data
-    Returns ``npeaks`` biggest peaks as a list of time points.
-    """
-    argrelmax = _get_argrelmax()
-    gfp = evoked.data.std(axis=0)
-    order = len(evoked.times) // 30
-    if order < 1:
-        order = 1
-    peaks = argrelmax(gfp, order=order, axis=0)[0]
-    if len(peaks) > npeaks:
-        max_indices = np.argsort(gfp[peaks])[-npeaks:]
-        peaks = np.sort(peaks[max_indices])
-    times = evoked.times[peaks]
-    if len(times) == 0:
-        times = [evoked.times[gfp.argmax()]]
-    return times
-
-
 def _prepare_topomap(pos, ax):
     """Helper for preparing the topomap."""
     pos_x = pos[:, 0]
     pos_y = pos[:, 1]
-
-    ax.set_xticks([])
-    ax.set_yticks([])
-    ax.set_frame_on(False)
+    _hide_frame(ax)
     if any([not pos_y.any(), not pos_x.any()]):
         raise RuntimeError('No position information found, cannot compute '
                            'geometries for topomap.')
     return pos_x, pos_y
+
+
+def _hide_frame(ax):
+    """Helper to hide axis frame for topomaps."""
+    ax.set_xticks([])
+    ax.set_yticks([])
+    ax.set_frame_on(False)
+
+
+def _init_anim(ax, ax_line, ax_cbar, params, merge_grads):
+    """Initialize animated topomap."""
+    from matplotlib import pyplot as plt, patches
+    logger.info('Initializing animation...')
+    data = params['data']
+    items = list()
+    if params['butterfly']:
+        all_times = params['all_times']
+        for idx in range(len(data)):
+            ax_line.plot(all_times, data[idx], color='k')
+        vmin, vmax = _setup_vmin_vmax(data, None, None)
+        ax_line.set_yticks(np.around(np.linspace(vmin, vmax, 5), -1))
+        params['line'], = ax_line.plot([all_times[0], all_times[0]],
+                                       ax_line.get_ylim(), color='r')
+        items.append(params['line'])
+    if merge_grads:
+        from mne.channels.layout import _merge_grad_data
+        data = _merge_grad_data(data)
+    norm = True if np.min(data) > 0 else False
+    cmap = 'Reds' if norm else 'RdBu_r'
+
+    vmin, vmax = _setup_vmin_vmax(data, None, None, norm)
+
+    pos, outlines = _check_outlines(params['pos'], 'head', None)
+    pos_x = pos[:, 0]
+    pos_y = pos[:, 1]
+
+    _hide_frame(ax)
+    xlim = np.inf, -np.inf,
+    ylim = np.inf, -np.inf,
+    mask_ = np.c_[outlines['mask_pos']]
+    xmin, xmax = (np.min(np.r_[xlim[0], mask_[:, 0]]),
+                  np.max(np.r_[xlim[1], mask_[:, 0]]))
+    ymin, ymax = (np.min(np.r_[ylim[0], mask_[:, 1]]),
+                  np.max(np.r_[ylim[1], mask_[:, 1]]))
+
+    res = 64
+    xi = np.linspace(xmin, xmax, res)
+    yi = np.linspace(ymin, ymax, res)
+    Xi, Yi = np.meshgrid(xi, yi)
+    params['Zis'] = list()
+
+    for frame in params['frames']:
+        Zi = _griddata(pos_x, pos_y, data[:, frame], Xi, Yi)
+        params['Zis'].append(Zi)
+    Zi = params['Zis'][0]
+    zi_min = np.min(params['Zis'])
+    zi_max = np.max(params['Zis'])
+    cont_lims = np.linspace(zi_min, zi_max, 7, endpoint=False)[1:]
+    _, pos = _make_image_mask(outlines, pos, res)
+    params.update({'vmin': vmin, 'vmax': vmax, 'Xi': Xi, 'Yi': Yi, 'Zi': Zi,
+                   'extent': (xmin, xmax, ymin, ymax), 'cmap': cmap,
+                   'cont_lims': cont_lims})
+    # plot map and countour
+    im = ax.imshow(Zi, cmap=cmap, vmin=vmin, vmax=vmax, origin='lower',
+                   aspect='equal', extent=(xmin, xmax, ymin, ymax),
+                   interpolation='bilinear')
+    plt.colorbar(im, cax=ax_cbar, cmap=cmap)
+    cont = ax.contour(Xi, Yi, Zi, levels=cont_lims, colors='k', linewidths=1)
+
+    patch_ = patches.Ellipse((0, 0),
+                             2 * outlines['clip_radius'][0],
+                             2 * outlines['clip_radius'][1],
+                             clip_on=True,
+                             transform=ax.transData)
+    im.set_clip_path(patch_)
+    text = ax.text(0.55, 0.95, '', transform=ax.transAxes, va='center',
+                   ha='right')
+    params['text'] = text
+    items.append(im)
+    items.append(text)
+    for col in cont.collections:
+        col.set_clip_path(patch_)
+
+    outlines_ = _draw_outlines(ax, outlines)
+
+    params.update({'patch': patch_, 'outlines': outlines_})
+    return tuple(items) + tuple(cont.collections)
+
+
+def _animate(frame, ax, ax_line, params):
+    """Updates animated topomap."""
+    if params['pause']:
+        frame = params['frame']
+    time_idx = params['frames'][frame]
+
+    title = '%6.0f ms' % (params['times'][frame] * 1e3)
+    if params['blit']:
+        text = params['text']
+    else:
+        ax.cla()  # Clear old contours.
+        text = ax.text(0.45, 1.15, '', transform=ax.transAxes)
+        for k, (x, y) in params['outlines'].items():
+            if 'mask' in k:
+                continue
+            ax.plot(x, y, color='k', linewidth=1, clip_on=False)
+
+    _hide_frame(ax)
+    text.set_text(title)
+
+    vmin = params['vmin']
+    vmax = params['vmax']
+    Xi = params['Xi']
+    Yi = params['Yi']
+    Zi = params['Zis'][frame]
+    extent = params['extent']
+    cmap = params['cmap']
+    patch = params['patch']
+
+    im = ax.imshow(Zi, cmap=cmap, vmin=vmin, vmax=vmax, origin='lower',
+                   aspect='equal', extent=extent, interpolation='bilinear')
+    cont_lims = params['cont_lims']
+    cont = ax.contour(Xi, Yi, Zi, levels=cont_lims, colors='k', linewidths=1)
+
+    im.set_clip_path(patch)
+    items = [im, text]
+    for col in cont.collections:
+        col.set_clip_path(patch)
+
+    if params['butterfly']:
+        all_times = params['all_times']
+        line = params['line']
+        line.remove()
+        params['line'] = ax_line.plot([all_times[time_idx],
+                                       all_times[time_idx]],
+                                      ax_line.get_ylim(), color='r')[0]
+        items.append(params['line'])
+    params['frame'] = frame
+    return tuple(items) + tuple(cont.collections)
+
+
+def _pause_anim(event, params):
+    """Function for pausing and continuing the animation on mouse click"""
+    params['pause'] = not params['pause']
+
+
+def _key_press(event, params):
+    """Function for handling key presses for the animation."""
+    if event.key == 'left':
+        params['pause'] = True
+        params['frame'] = max(params['frame'] - 1, 0)
+    elif event.key == 'right':
+        params['pause'] = True
+        params['frame'] = min(params['frame'] + 1, len(params['frames']) - 1)
+
+
+def _topomap_animation(evoked, ch_type='mag', times=None, frame_rate=None,
+                       butterfly=False, blit=True, show=True):
+    """Make animation of evoked data as topomap timeseries. Animation can be
+    paused/resumed with left mouse button. Left and right arrow keys can be
+    used to move backward or forward in time.
+
+    Parameters
+    ----------
+    evoked : instance of Evoked
+        The evoked data.
+    ch_type : str | None
+        Channel type to plot. Accepted data types: 'mag', 'grad', 'eeg'.
+        If None, first available channel type from ('mag', 'grad', 'eeg') is
+        used. Defaults to None.
+    times : array of floats | None
+        The time points to plot. If None, 10 evenly spaced samples are
+        calculated over the evoked time series. Defaults to None.
+    frame_rate : int | None
+        Frame rate for the animation in Hz. If None, frame rate = sfreq / 10.
+        Defaults to None.
+    butterfly : bool
+        Whether to plot the data as butterfly plot under the topomap.
+        Defaults to False.
+    blit : bool
+        Whether to use blit to optimize drawing. In general, it is recommended
+        to use blit in combination with ``show=True``. If you intend to save
+        the animation it is better to disable blit. For MacOSX blit is always
+        disabled. Defaults to True.
+    show : bool
+        Whether to show the animation. Defaults to True.
+
+    Returns
+    -------
+    fig : instance of matplotlib figure
+        The figure.
+    anim : instance of matplotlib FuncAnimation
+        Animation of the topomap.
+
+    Notes
+    -----
+    .. versionadded:: 0.12.0
+    """
+    from matplotlib import pyplot as plt, animation
+    if ch_type is None:
+        ch_type = _picks_by_type(evoked.info)[0][0]
+    if ch_type not in ('mag', 'grad', 'eeg'):
+        raise ValueError("Channel type not supported. Supported channel "
+                         "types include 'mag', 'grad' and 'eeg'.")
+    if times is None:
+        times = np.linspace(evoked.times[0], evoked.times[-1], 10)
+    times = np.array(times)
+
+    if times.ndim != 1:
+        raise ValueError('times must be 1D, got %d dimensions' % times.ndim)
+    if max(times) > evoked.times[-1] or min(times) < evoked.times[0]:
+        raise ValueError('All times must be inside the evoked time series.')
+    frames = [np.abs(evoked.times - time).argmin() for time in times]
+
+    blit = False if plt.get_backend() == 'MacOSX' else True
+    picks, pos, merge_grads, _, ch_type = _prepare_topo_plot(evoked,
+                                                             ch_type=ch_type,
+                                                             layout=None)
+    data = evoked.data[picks, :]
+    data *= _handle_default('scalings')[ch_type]
+
+    fig = plt.figure()
+    offset = 0. if blit else 0.4  # XXX: blit changes the sizes for some reason
+    ax = plt.axes([0. + offset / 2., 0. + offset / 2., 1. - offset,
+                   1. - offset], xlim=(-1, 1), ylim=(-1, 1))
+    if butterfly:
+        ax_line = plt.axes([0.2, 0.05, 0.6, 0.1], xlim=(evoked.times[0],
+                                                        evoked.times[-1]))
+    else:
+        ax_line = None
+    if isinstance(frames, int):
+        frames = np.linspace(0, len(evoked.times) - 1, frames).astype(int)
+    ax_cbar = plt.axes([0.85, 0.1, 0.05, 0.8])
+    ax_cbar.set_title(_handle_default('units')[ch_type], fontsize=10)
+
+    params = {'data': data, 'pos': pos, 'all_times': evoked.times, 'frame': 0,
+              'frames': frames, 'butterfly': butterfly, 'blit': blit,
+              'pause': False, 'times': times}
+    init_func = partial(_init_anim, ax=ax, ax_cbar=ax_cbar, ax_line=ax_line,
+                        params=params, merge_grads=merge_grads)
+    animate_func = partial(_animate, ax=ax, ax_line=ax_line, params=params)
+    pause_func = partial(_pause_anim, params=params)
+    fig.canvas.mpl_connect('button_press_event', pause_func)
+    key_press_func = partial(_key_press, params=params)
+    fig.canvas.mpl_connect('key_press_event', key_press_func)
+    if frame_rate is None:
+        frame_rate = evoked.info['sfreq'] / 10.
+    interval = 1000 / frame_rate  # interval is in ms
+    anim = animation.FuncAnimation(fig, animate_func, init_func=init_func,
+                                   frames=len(frames), interval=interval,
+                                   blit=blit)
+    fig.mne_animation = anim  # to make sure anim is not garbage collected
+    if show:
+        plt.show()
+    if 'line' in params:
+        # Finally remove the vertical line so it does not appear in saved fig.
+        params['line'].remove()
+
+    return fig, anim
diff --git a/mne/viz/utils.py b/mne/viz/utils.py
index da30cf6..8eeb7a1 100644
--- a/mne/viz/utils.py
+++ b/mne/viz/utils.py
@@ -14,12 +14,18 @@ import math
 from functools import partial
 import difflib
 import webbrowser
-from warnings import warn
 import tempfile
 import numpy as np
+from copy import deepcopy
 
-from ..io import show_fiff
-from ..utils import verbose, set_config
+from ..channels.layout import _auto_topomap_coords
+from ..channels.channels import _contains_ch_type
+from ..defaults import _handle_default
+from ..io import show_fiff, Info
+from ..io.pick import channel_type, channel_indices_by_type
+from ..utils import verbose, set_config, warn
+from ..externals.six import string_types
+from ..fixes import _get_argrelmax
 
 
 COLORS = ['b', 'g', 'r', 'c', 'm', 'y', 'k', '#473C8B', '#458B74',
@@ -83,13 +89,11 @@ def tight_layout(pad=1.2, h_pad=None, w_pad=None, fig=None):
     try:  # see https://github.com/matplotlib/matplotlib/issues/2654
         fig.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad)
     except Exception:
-        warn('Matplotlib function \'tight_layout\' is not supported.'
-             ' Skipping subplot adjusment.')
-    else:
         try:
             fig.set_tight_layout(dict(pad=pad, h_pad=h_pad, w_pad=w_pad))
         except Exception:
-            pass
+            warn('Matplotlib function "tight_layout" is not supported.'
+                 ' Skipping subplot adjustment.')
 
 
 def _check_delayed_ssp(container):
@@ -330,7 +334,10 @@ def _prepare_trellis(n_cells, max_col):
     fig, axes = plt.subplots(nrow, ncol, figsize=(7.4, 1.5 * nrow + 1))
     axes = [axes] if ncol == nrow == 1 else axes.flatten()
     for ax in axes[n_cells:]:  # hide unused axes
-        ax.set_visible(False)
+        # XXX: Previously done by ax.set_visible(False), but because of mpl
+        # bug, we just hide the frame.
+        from .topomap import _hide_frame
+        _hide_frame(ax)
     return fig, axes
 
 
@@ -565,21 +572,13 @@ def _plot_raw_onkey(event, params):
         params['plot_fun']()
     elif event.key == 'pageup':
         n_channels = params['n_channels'] + 1
-        offset = params['ax'].get_ylim()[0] / n_channels
-        params['offsets'] = np.arange(n_channels) * offset + (offset / 2.)
-        params['n_channels'] = n_channels
-        params['ax'].set_yticks(params['offsets'])
-        params['vsel_patch'].set_height(n_channels)
+        _setup_browser_offsets(params, n_channels)
         _channels_changed(params, len(params['info']['ch_names']))
     elif event.key == 'pagedown':
         n_channels = params['n_channels'] - 1
         if n_channels == 0:
             return
-        offset = params['ax'].get_ylim()[0] / n_channels
-        params['offsets'] = np.arange(n_channels) * offset + (offset / 2.)
-        params['n_channels'] = n_channels
-        params['ax'].set_yticks(params['offsets'])
-        params['vsel_patch'].set_height(n_channels)
+        _setup_browser_offsets(params, n_channels)
         if len(params['lines']) > n_channels:  # remove line from view
             params['lines'][n_channels].set_xdata([])
             params['lines'][n_channels].set_ydata([])
@@ -704,6 +703,17 @@ def _onclick_help(event, params):
         pass
 
 
+def _setup_browser_offsets(params, n_channels):
+    """Aux function for computing viewport height and adjusting offsets."""
+    ylim = [n_channels * 2 + 1, 0]
+    offset = ylim[0] / n_channels
+    params['offsets'] = np.arange(n_channels) * offset + (offset / 2.)
+    params['n_channels'] = n_channels
+    params['ax'].set_yticks(params['offsets'])
+    params['ax'].set_ylim(ylim)
+    params['vsel_patch'].set_height(n_channels)
+
+
 class ClickableImage(object):
 
     """
@@ -788,7 +798,7 @@ class ClickableImage(object):
         **kwargs : dict
             Arguments are passed to generate_2d_layout
         """
-        from mne.channels.layout import generate_2d_layout
+        from ..channels.layout import generate_2d_layout
         coords = np.array(self.coords)
         lt = generate_2d_layout(coords, bg_image=self.imdata, **kwargs)
         return lt
@@ -850,3 +860,250 @@ def add_background_image(fig, im, set_ratios=None):
     ax_im.imshow(im, aspect='auto')
     ax_im.set_zorder(-1)
     return ax_im
+
+
+def _find_peaks(evoked, npeaks):
+    """Helper function for finding peaks from evoked data
+    Returns ``npeaks`` biggest peaks as a list of time points.
+    """
+    argrelmax = _get_argrelmax()
+    gfp = evoked.data.std(axis=0)
+    order = len(evoked.times) // 30
+    if order < 1:
+        order = 1
+    peaks = argrelmax(gfp, order=order, axis=0)[0]
+    if len(peaks) > npeaks:
+        max_indices = np.argsort(gfp[peaks])[-npeaks:]
+        peaks = np.sort(peaks[max_indices])
+    times = evoked.times[peaks]
+    if len(times) == 0:
+        times = [evoked.times[gfp.argmax()]]
+    return times
+
+
+def _process_times(inst, times, n_peaks=None, few=False):
+    """Helper to return a list of times for topomaps"""
+    if isinstance(times, string_types):
+        if times == "peaks":
+            if n_peaks is None:
+                n_peaks = 3 if few else 7
+            times = _find_peaks(inst, n_peaks)
+        elif times == "auto":
+            if n_peaks is None:
+                n_peaks = 5 if few else 10
+            times = np.linspace(inst.times[0], inst.times[-1], n_peaks)
+        else:
+            raise ValueError("Got an unrecognized method for `times`. Only "
+                             "'peaks' and 'auto' are supported (or directly "
+                             "passing numbers).")
+    elif np.isscalar(times):
+        times = [times]
+
+    times = np.array(times)
+
+    if times.ndim != 1:
+        raise ValueError('times must be 1D, got %d dimensions' % times.ndim)
+    if len(times) > 20:
+        raise RuntimeError('Too many plots requested. Please pass fewer '
+                           'than 20 time instants.')
+
+    return times
+
+
+def plot_sensors(info, kind='topomap', ch_type=None, title=None,
+                 show_names=False, show=True):
+    """Plot sensors positions.
+
+    Parameters
+    ----------
+    info : Instance of Info
+        Info structure containing the channel locations.
+    kind : str
+        Whether to plot the sensors as 3d or as topomap. Available options
+        'topomap', '3d'. Defaults to 'topomap'.
+    ch_type : 'mag' | 'grad' | 'eeg' | 'seeg' | None
+        The channel type to plot. If None, then channels are chosen in the
+        order given above.
+    title : str | None
+        Title for the figure. If None (default), equals to
+        ``'Sensor positions (%s)' % ch_type``.
+    show_names : bool
+        Whether to display all channel names. Defaults to False.
+    show : bool
+        Show figure if True. Defaults to True.
+
+    Returns
+    -------
+    fig : instance of matplotlib figure
+        Figure containing the sensor topography.
+
+    See Also
+    --------
+    mne.viz.plot_layout
+
+    Notes
+    -----
+    This function plots the sensor locations from the info structure using
+    matplotlib. For drawing the sensors using mayavi see
+    :func:`mne.viz.plot_trans`.
+
+    .. versionadded:: 0.12.0
+
+    """
+    if kind not in ['topomap', '3d']:
+        raise ValueError("Kind must be 'topomap' or '3d'.")
+    if not isinstance(info, Info):
+        raise TypeError('info must be an instance of Info not %s' % type(info))
+    ch_indices = channel_indices_by_type(info)
+    allowed_types = ['mag', 'grad', 'eeg', 'seeg']
+    if ch_type is None:
+        for this_type in allowed_types:
+            if _contains_ch_type(info, this_type):
+                ch_type = this_type
+                break
+    elif ch_type not in allowed_types:
+        raise ValueError("ch_type must be one of %s not %s!" % (allowed_types,
+                                                                ch_type))
+    picks = ch_indices[ch_type]
+    if kind == 'topomap':
+        pos = _auto_topomap_coords(info, picks, True)
+    else:
+        pos = np.asarray([ch['loc'][:3] for ch in info['chs']])[picks]
+    def_colors = _handle_default('color')
+    ch_names = np.array(info['ch_names'])[picks]
+    bads = [idx for idx, name in enumerate(ch_names) if name in info['bads']]
+    colors = ['red' if i in bads else def_colors[channel_type(info, pick)]
+              for i, pick in enumerate(picks)]
+    title = 'Sensor positions (%s)' % ch_type if title is None else title
+    fig = _plot_sensors(pos, colors, ch_names, title, show_names, show)
+
+    return fig
+
+
+def _onpick_sensor(event, fig, ax, pos, ch_names):
+    """Callback for picked channel in plot_sensors."""
+    ind = event.ind[0]  # Just take the first sensor.
+    ch_name = ch_names[ind]
+    this_pos = pos[ind]
+
+    # XXX: Bug in matplotlib won't allow setting the position of existing
+    # text item, so we create a new one.
+    ax.texts.pop(0)
+    if len(this_pos) == 3:
+        ax.text(this_pos[0], this_pos[1], this_pos[2], ch_name)
+    else:
+        ax.text(this_pos[0], this_pos[1], ch_name)
+    fig.canvas.draw()
+
+
+def _plot_sensors(pos, colors, ch_names, title, show_names, show):
+    """Helper function for plotting sensors."""
+    import matplotlib.pyplot as plt
+    from mpl_toolkits.mplot3d import Axes3D
+    from .topomap import _check_outlines, _draw_outlines
+    fig = plt.figure()
+
+    if pos.shape[1] == 3:
+        ax = Axes3D(fig)
+        ax = fig.gca(projection='3d')
+        ax.text(0, 0, 0, '', zorder=1)
+        ax.scatter(pos[:, 0], pos[:, 1], pos[:, 2], picker=True, c=colors)
+        ax.azim = 90
+        ax.elev = 0
+    else:
+        ax = fig.add_subplot(111)
+        ax.text(0, 0, '', zorder=1)
+        ax.set_xticks([])
+        ax.set_yticks([])
+        fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=None,
+                            hspace=None)
+        pos, outlines = _check_outlines(pos, 'head')
+        _draw_outlines(ax, outlines)
+        ax.scatter(pos[:, 0], pos[:, 1], picker=True, c=colors)
+
+    if show_names:
+        for idx in range(len(pos)):
+            this_pos = pos[idx]
+            if pos.shape[1] == 3:
+                ax.text(this_pos[0], this_pos[1], this_pos[2], ch_names[idx])
+            else:
+                ax.text(this_pos[0], this_pos[1], ch_names[idx])
+    else:
+        picker = partial(_onpick_sensor, fig=fig, ax=ax, pos=pos,
+                         ch_names=ch_names)
+        fig.canvas.mpl_connect('pick_event', picker)
+    fig.suptitle(title)
+    plt_show(show)
+    return fig
+
+
+def _compute_scalings(scalings, inst):
+    """Compute scalings for each channel type automatically.
+
+    Parameters
+    ----------
+    scalings : dict
+        The scalings for each channel type. If any values are
+        'auto', this will automatically compute a reasonable
+        scaling for that channel type. Any values that aren't
+        'auto' will not be changed.
+    inst : instance of Raw or Epochs
+        The data for which you want to compute scalings. If data
+        is not preloaded, this will read a subset of times / epochs
+        up to 100mb in size in order to compute scalings.
+
+    Returns
+    -------
+    scalings : dict
+        A scalings dictionary with updated values
+    """
+    from ..io.base import _BaseRaw
+    from ..epochs import _BaseEpochs
+    if not isinstance(inst, (_BaseRaw, _BaseEpochs)):
+        raise ValueError('Must supply either Raw or Epochs')
+    if scalings is None:
+        # If scalings is None just return it and do nothing
+        return scalings
+
+    ch_types = channel_indices_by_type(inst.info)
+    ch_types = dict([(i_type, i_ixs)
+                     for i_type, i_ixs in ch_types.items() if len(i_ixs) != 0])
+    if scalings == 'auto':
+        # If we want to auto-compute everything
+        scalings = dict((i_type, 'auto') for i_type in ch_types.keys())
+    if not isinstance(scalings, dict):
+        raise ValueError('scalings must be a dictionary of ch_type: val pairs,'
+                         ' not type %s ' % type(scalings))
+    scalings = deepcopy(scalings)
+
+    if inst.preload is False:
+        if isinstance(inst, _BaseRaw):
+            # Load a window of data from the center up to 100mb in size
+            n_times = 1e8 // (len(inst.ch_names) * 8)
+            n_times = np.clip(n_times, 1, inst.n_times)
+            n_secs = n_times / float(inst.info['sfreq'])
+            time_middle = np.mean(inst.times)
+            tmin = np.clip(time_middle - n_secs / 2., inst.times.min(), None)
+            tmax = np.clip(time_middle + n_secs / 2., None, inst.times.max())
+            data = inst._read_segment(tmin, tmax)
+        elif isinstance(inst, _BaseEpochs):
+            # Load a random subset of epochs up to 100mb in size
+            n_epochs = 1e8 // (len(inst.ch_names) * len(inst.times) * 8)
+            n_epochs = int(np.clip(n_epochs, 1, len(inst)))
+            ixs_epochs = np.random.choice(range(len(inst)), n_epochs, False)
+            inst = inst.copy()[ixs_epochs].load_data()
+    else:
+        data = inst._data
+    if isinstance(inst, _BaseEpochs):
+        data = inst._data.reshape([len(inst.ch_names), -1])
+    # Iterate through ch types and update scaling if ' auto'
+    for key, value in scalings.items():
+        if value != 'auto':
+            continue
+        if key not in ch_types.keys():
+            raise ValueError("Sensor {0} doesn't exist in data".format(key))
+        this_data = data[ch_types[key]]
+        scale_factor = np.percentile(this_data.ravel(), [0.5, 99.5])
+        scale_factor = np.max(np.abs(scale_factor))
+        scalings[key] = scale_factor
+    return scalings
diff --git a/setup.cfg b/setup.cfg
index 1ae30e4..b5ba987 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -28,5 +28,5 @@ doctest-fixtures = _fixture
 #doctest-options = +ELLIPSIS,+NORMALIZE_WHITESPACE
 
 [flake8]
-exclude = __init__.py,*externals*,constants.py
+exclude = __init__.py,*externals*,constants.py,fixes.py
 ignore = E241
diff --git a/setup.py b/setup.py
index dec7410..a60cfd0 100755
--- a/setup.py
+++ b/setup.py
@@ -68,6 +68,7 @@ if __name__ == "__main__":
                     'mne.datasets.eegbci',
                     'mne.datasets._fake',
                     'mne.datasets.megsim',
+                    'mne.datasets.misc',
                     'mne.datasets.sample',
                     'mne.datasets.somato',
                     'mne.datasets.spm_face',
@@ -81,6 +82,7 @@ if __name__ == "__main__":
                     'mne.io.array', 'mne.io.array.tests',
                     'mne.io.brainvision', 'mne.io.brainvision.tests',
                     'mne.io.bti', 'mne.io.bti.tests',
+                    'mne.io.cnt', 'mne.io.cnt.tests',
                     'mne.io.ctf', 'mne.io.ctf.tests',
                     'mne.io.edf', 'mne.io.edf.tests',
                     'mne.io.egi', 'mne.io.egi.tests',
@@ -107,6 +109,8 @@ if __name__ == "__main__":
                                 op.join('data', 'coil_def*.dat'),
                                 op.join('data', 'helmets', '*.fif.gz'),
                                 op.join('data', 'FreeSurferColorLUT.txt'),
+                                op.join('data', 'image', '*gif'),
+                                op.join('data', 'image', '*lout'),
                                 op.join('channels', 'data', 'layouts', '*.lout'),
                                 op.join('channels', 'data', 'layouts', '*.lay'),
                                 op.join('channels', 'data', 'montages', '*.sfp'),
diff --git a/tutorials/plot_artifacts_correction_filtering.py b/tutorials/plot_artifacts_correction_filtering.py
new file mode 100644
index 0000000..af7dda7
--- /dev/null
+++ b/tutorials/plot_artifacts_correction_filtering.py
@@ -0,0 +1,98 @@
+"""
+.. _tut_artifacts_filter:
+
+Filtering and Resampling
+========================
+
+Certain artifacts are restricted to certain frequencies and can therefore
+be fixed by filtering. An artifact that typically affects only some
+frequencies is due to the power line.
+
+Power-line noise is a noise created by the electrical network.
+It is composed of sharp peaks at 50Hz (or 60Hz depending on your
+geographical location). Some peaks may also be present at the harmonic
+frequencies, i.e. the integer multiples of
+the power-line frequency, e.g. 100Hz, 150Hz, ... (or 120Hz, 180Hz, ...).
+"""
+
+import numpy as np
+import mne
+from mne.datasets import sample
+
+data_path = sample.data_path()
+raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
+proj_fname = data_path + '/MEG/sample/sample_audvis_eog_proj.fif'
+
+tmin, tmax = 0, 20  # use the first 20s of data
+
+# Setup for reading the raw data (save memory by cropping the raw data
+# before loading it)
+raw = mne.io.read_raw_fif(raw_fname).crop(tmin, tmax).load_data()
+raw.info['bads'] = ['MEG 2443', 'EEG 053']  # bads + 2 more
+
+fmin, fmax = 2, 300  # look at frequencies between 2 and 300Hz
+n_fft = 2048  # the FFT size (n_fft). Ideally a power of 2
+
+# Pick a subset of channels (here for speed reason)
+selection = mne.read_selection('Left-temporal')
+picks = mne.pick_types(raw.info, meg='mag', eeg=False, eog=False,
+                       stim=False, exclude='bads', selection=selection)
+
+# Let's first check out all channel types
+raw.plot_psd(area_mode='range', tmax=10.0, picks=picks)
+
+###############################################################################
+# Removing power-line noise with notch filtering
+# ----------------------------------------------
+#
+# Removing power-line noise can be done with a Notch filter, directly on the
+# Raw object, specifying an array of frequency to be cut off:
+
+raw.notch_filter(np.arange(60, 241, 60), picks=picks)
+raw.plot_psd(area_mode='range', tmax=10.0, picks=picks)
+
+###############################################################################
+# Removing power-line noise with low-pas filtering
+# -------------------------------------------------
+#
+# If you're only interested in low frequencies, below the peaks of power-line
+# noise you can simply low pass filter the data.
+
+raw.filter(None, 50.)  # low pass filtering below 50 Hz
+raw.plot_psd(area_mode='range', tmax=10.0, picks=picks)
+
+###############################################################################
+# High-pass filtering to remove slow drifts
+# -----------------------------------------
+#
+# If you're only interested in low frequencies, below the peaks of power-line
+# noise you can simply high pass filter the data.
+
+raw.filter(1., None)  # low pass filtering above 1 Hz
+raw.plot_psd(area_mode='range', tmax=10.0, picks=picks)
+
+###############################################################################
+# To do the low-pass and high-pass filtering in one step you can do
+# a so-called *band-pass* filter by running
+
+raw.filter(1., 50.)  # band-pass filtering in the range 1 Hz - 50 Hz
+
+###############################################################################
+# Down-sampling (for performance reasons)
+# ---------------------------------------
+#
+# When performing experiments where timing is critical, a signal with a high
+# sampling rate is desired. However, having a signal with a much higher
+# sampling rate than necessary needlessly consumes memory and slows down
+# computations operating on the data. To avoid that, you can down-sample
+# your time series.
+#
+# Data resampling can be done with *resample* methods.
+
+raw.resample(100, npad="auto")  # set sampling frequency to 100Hz
+raw.plot_psd(area_mode='range', tmax=10.0, picks=picks)
+
+###############################################################################
+# Since down-sampling reduces the timing precision of events, you might want to
+# first extract epochs and down-sampling the Epochs object. You can do this
+# using the :func:`mne.Epochs.resample` method.
diff --git a/tutorials/plot_artifacts_correction_ica.py b/tutorials/plot_artifacts_correction_ica.py
new file mode 100644
index 0000000..0d10177
--- /dev/null
+++ b/tutorials/plot_artifacts_correction_ica.py
@@ -0,0 +1,201 @@
+"""
+
+.. _tut_artifacts_correct_ica:
+
+Artifact Correction with ICA
+============================
+
+ICA finds directions in the feature space
+corresponding to projections with high non-Gaussianity. We thus obtain
+a decomposition into independent components, and the artifact's contribution
+is localized in only a small number of components.
+These components have to be correctly identified and removed.
+
+If EOG or ECG recordings are available, they can be used in ICA to
+automatically select the corresponding artifact components from the
+decomposition. To do so, you have to first build an Epoch object around
+blink or heartbeat event.
+"""
+
+import numpy as np
+
+import mne
+from mne.datasets import sample
+
+from mne.preprocessing import ICA
+from mne.preprocessing import create_eog_epochs
+
+# getting some data ready
+data_path = sample.data_path()
+raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
+
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
+raw.filter(1, 40, n_jobs=2)  # 1Hz high pass is often helpful for fitting ICA
+
+picks_meg = mne.pick_types(raw.info, meg=True, eeg=False, eog=False,
+                           stim=False, exclude='bads')
+
+###############################################################################
+# Before applying artifact correction please learn about your actual artifacts
+# by reading :ref:`tut_artifacts_detect`.
+
+###############################################################################
+# Fit ICA
+# -------
+#
+# ICA parameters:
+
+n_components = 25  # if float, select n_components by explained variance of PCA
+method = 'fastica'  # for comparison with EEGLAB try "extended-infomax" here
+decim = 3  # we need sufficient statistics, not all time points -> save time
+
+###############################################################################
+# Define the ICA object instance
+ica = ICA(n_components=n_components, method=method)
+print(ica)
+
+###############################################################################
+# we avoid fitting ICA on crazy environmental artifacts that would
+# dominate the variance and decomposition
+reject = dict(mag=5e-12, grad=4000e-13)
+ica.fit(raw, picks=picks_meg, decim=decim, reject=reject)
+print(ica)
+
+###############################################################################
+# Plot ICA components
+
+ica.plot_components()  # can you see some potential bad guys?
+
+
+###############################################################################
+# Advanced artifact detection
+# ---------------------------
+#
+# Let's use a more efficient way to find artefacts
+
+eog_average = create_eog_epochs(raw, reject=dict(mag=5e-12, grad=4000e-13),
+                                picks=picks_meg).average()
+
+# We simplify things by setting the maximum number of components to reject
+n_max_eog = 1  # here we bet on finding the vertical EOG components
+eog_epochs = create_eog_epochs(raw, reject=reject)  # get single EOG trials
+eog_inds, scores = ica.find_bads_eog(eog_epochs)  # find via correlation
+
+ica.plot_scores(scores, exclude=eog_inds)  # look at r scores of components
+# we can see that only one component is highly correlated and that this
+# component got detected by our correlation analysis (red).
+
+ica.plot_sources(eog_average, exclude=eog_inds)  # look at source time course
+
+###############################################################################
+# That component is also showing a prototypical average vertical EOG time
+# course.
+#
+# Pay attention to the labels, a customized read-out of the ica.labels_
+print(ica.labels_)
+
+###############################################################################
+# These labels were used by the plotters and are added automatically
+# by artifact detection functions. You can also manually edit them to annotate
+# components.
+#
+# Now let's see how we would modify our signals if we would remove this
+# component from the data
+ica.plot_overlay(eog_average, exclude=eog_inds, show=False)
+# red -> before, black -> after. Yes! We remove quite a lot!
+
+# to definitely register this component as a bad one to be removed
+# there is the ``ica.exclude`` attribute, a simple Python list
+
+ica.exclude.extend(eog_inds)
+
+# from now on the ICA will reject this component even if no exclude
+# parameter is passed, and this information will be stored to disk
+# on saving
+
+# uncomment this for reading and writing
+# ica.save('my-ica.fif')
+# ica = read_ica('my-ica.fif')
+
+###############################################################################
+# Exercise: find and remove ECG artifacts using ICA!
+#
+# What if we don't have an EOG channel?
+# -------------------------------------
+#
+# 1) make a bipolar reference from frontal EEG sensors and use as virtual EOG
+# channel. This can be tricky though as you can only hope that the frontal
+# EEG channels only reflect EOG and not brain dynamics in the prefrontal
+# cortex.
+# 2) Go for a semi-automated approach, using template matching.
+# In MNE-Python option 2 is easily achievable and it might be better,
+# so let's have a look at it.
+
+from mne.preprocessing.ica import corrmap  # noqa
+
+###############################################################################
+# The idea behind corrmap is that artefact patterns are similar across subjects
+# and can thus be identified by correlating the different patterns resulting
+# from each solution with a template. The procedure is therefore
+# semi-automatic. Corrmap hence takes at least a list of ICA solutions and a
+# template, that can be an index or an array. As we don't have different
+# subjects or runs available today, here we will fit ICA models to different
+# parts of the recording and then use as a user-defined template the ICA
+# that we just fitted for detecting corresponding components in the three "new"
+# ICAs. The following block of code addresses this point and should not be
+# copied, ok?
+# We'll start by simulating a group of subjects or runs from a subject
+start, stop = [0, len(raw.times) - 1]
+intervals = np.linspace(start, stop, 4, dtype=int)
+icas_from_other_data = list()
+raw.pick_types(meg=True, eeg=False)  # take only MEG channels
+for ii, start in enumerate(intervals):
+    if ii + 1 < len(intervals):
+        stop = intervals[ii + 1]
+        print('fitting ICA from {0} to {1} seconds'.format(start, stop))
+        this_ica = ICA(n_components=n_components, method=method).fit(
+            raw, start=start, stop=stop, reject=reject)
+        icas_from_other_data.append(this_ica)
+
+###############################################################################
+# Do not copy this at home! You start by reading in a collections of ICA
+# solutions, something like
+#
+# ``icas = [mne.preprocessing.read_ica(fname) for fname in ica_fnames]``
+print(icas_from_other_data)
+
+###############################################################################
+# use our previous ICA as reference.
+reference_ica = ica
+
+###############################################################################
+# Investigate our reference ICA, here we use the previous fit from above.
+reference_ica.plot_components()
+
+###############################################################################
+# Which one is the bad EOG component?
+# Here we rely on our previous detection algorithm. You will need to decide
+# yourself in that situation where no other detection is available.
+
+reference_ica.plot_sources(eog_average, exclude=eog_inds)
+
+###############################################################################
+# Indeed it looks like an EOG, also in the average time course.
+#
+# So our template shall be a tuple like (reference_run_index, component_index):
+template = (0, eog_inds[0])
+
+###############################################################################
+# Now we can do the corrmap.
+fig_template, fig_detected = corrmap(
+    icas_from_other_data, template=template, label="blinks", show=True,
+    threshold=.8, ch_type='mag')
+
+###############################################################################
+# Nice, we have found similar ICs from the other runs!
+# This is even nicer if we have 20 or 100 ICA solutions in a list.
+#
+# You can also use SSP for correcting for artifacts. It is a bit simpler,
+# faster but is less precise than ICA. And it requires that you
+# know the event timing of your artifact.
+# See :ref:`tut_artifacts_correct_ssp`.
diff --git a/examples/preprocessing/plot_maxwell_filter.py b/tutorials/plot_artifacts_correction_maxwell_filtering.py
similarity index 64%
rename from examples/preprocessing/plot_maxwell_filter.py
rename to tutorials/plot_artifacts_correction_maxwell_filtering.py
index 100b1b4..84b19c7 100644
--- a/examples/preprocessing/plot_maxwell_filter.py
+++ b/tutorials/plot_artifacts_correction_maxwell_filtering.py
@@ -1,22 +1,19 @@
 """
-=======================
-Maxwell filter raw data
-=======================
+=======================================
+Artifact correction with Maxwell filter
+=======================================
 
-This example shows how to process M/EEG data with Maxwell filtering
-in mne-python.
+This tutorial shows how to clean MEG data with Maxwell filtering.
+
+Maxwell filtering in MNE can be used to suppress sources of external
+intereference and compensate for subject head movements.
+
+See :ref:`maxwell` for more details.
 """
-# Authors: Eric Larson <larson.eric.d at gmail.com>
-#          Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#          Mark Wronkiewicz <wronk.mark at gmail.com>
-#
-# License: BSD (3-clause)
 
 import mne
 from mne.preprocessing import maxwell_filter
 
-print(__doc__)
-
 data_path = mne.datasets.sample.data_path()
 
 ###############################################################################
@@ -25,12 +22,14 @@ raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
 ctc_fname = data_path + '/SSS/ct_sparse_mgh.fif'
 fine_cal_fname = data_path + '/SSS/sss_cal_mgh.dat'
 
+###############################################################################
 # Preprocess with Maxwell filtering
-raw = mne.io.Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 raw.info['bads'] = ['MEG 2443', 'EEG 053', 'MEG 1032', 'MEG 2313']  # set bads
 # Here we don't use tSSS (set st_duration) because MGH data is very clean
 raw_sss = maxwell_filter(raw, cross_talk=ctc_fname, calibration=fine_cal_fname)
 
+###############################################################################
 # Select events to extract epochs from, pick M/EEG channels, and plot evoked
 tmin, tmax = -0.2, 0.5
 event_id = {'Auditory/Left': 1}
@@ -42,4 +41,5 @@ for r, kind in zip((raw, raw_sss), ('Raw data', 'Maxwell filtered data')):
                         baseline=(None, 0), reject=dict(eog=150e-6),
                         preload=False)
     evoked = epochs.average()
-    evoked.plot(window_title=kind)
+    evoked.plot(window_title=kind, ylim=dict(grad=(-200, 250),
+                                             mag=(-600, 700)))
diff --git a/tutorials/plot_artifacts_correction_rejection.py b/tutorials/plot_artifacts_correction_rejection.py
new file mode 100644
index 0000000..04accb9
--- /dev/null
+++ b/tutorials/plot_artifacts_correction_rejection.py
@@ -0,0 +1,197 @@
+"""
+.. _tut_artifacts_reject:
+
+Rejecting bad data (channels and segments)
+==========================================
+
+"""
+
+import numpy as np
+import mne
+from mne.datasets import sample
+
+data_path = sample.data_path()
+raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
+raw = mne.io.read_raw_fif(raw_fname)
+
+###############################################################################
+# .. _marking_bad_channels:
+#
+# Marking bad channels
+# --------------------
+#
+# Sometimes some MEG or EEG channels are not functioning properly
+# for various reasons. These channels should be excluded from
+# analysis by marking them bad as. This is done by setting the 'bads'
+# in the measurement info of a data container object (e.g. Raw, Epochs,
+# Evoked). The info['bads'] value is a Python string. Here is
+# example:
+
+raw.info['bads'] = ['MEG 2443']
+
+###############################################################################
+# **Why setting a channel bad?**: If a channel does not show
+# a signal at all (flat) it is important to exclude it from the
+# analysis. If a channel as a noise level significantly higher than the
+# other channels it should be marked as bad. Presence of bad channels
+# can have terribe consequences on down stream analysis. For a flat channel
+# some noise estimate will be unrealistically low and
+# thus the current estimate calculations will give a strong weight
+# to the zero signal on the flat channels and will essentially vanish.
+# Noisy channels can also affect others when signal-space projections
+# or EEG average electrode reference is employed. Noisy bad channels can
+# also adversely affect averaging and noise-covariance matrix estimation by
+# causing unnecessary rejections of epochs.
+#
+# Recommended ways to identify bad channels are:
+#
+# - Observe the quality of data during data
+#   acquisition and make notes of observed malfunctioning channels to
+#   your measurement protocol sheet.
+#
+# - View the on-line averages and check the condition of the channels.
+#
+# - Compute preliminary off-line averages with artifact rejection,
+#   SSP/ICA, and EEG average electrode reference computation
+#   off and check the condition of the channels.
+#
+# - View raw data with :func:`mne.io.Raw.plot` without SSP/ICA
+#   enabled and identify bad channels.
+#
+# .. note::
+#     Setting the bad channels should be done as early as possible in the
+#     analysis pipeline. That's why it's recommended to set bad channels
+#     the raw objects/files. If present in the raw data
+#     files, the bad channel selections will be automatically transferred
+#     to averaged files, noise-covariance matrices, forward solution
+#     files, and inverse operator decompositions.
+#
+# The actual removal happens using :func:`pick_types <mne.pick_types>` with
+# `exclude='bads'` option (see :ref:`picking_channels`).
+
+###############################################################################
+# Instead of removing the bad channels, you can also try to repair them.
+# This is done by **interpolation** of the data from other channels.
+# To illustrate how to use channel interpolation let us load some data.
+
+# Reading data with a bad channel marked as bad:
+fname = data_path + '/MEG/sample/sample_audvis-ave.fif'
+evoked = mne.read_evokeds(fname, condition='Left Auditory',
+                          baseline=(None, 0))
+
+# restrict the evoked to EEG and MEG channels
+evoked.pick_types(meg=True, eeg=True, exclude=[])
+
+# plot with bads
+evoked.plot(exclude=[])
+
+print(evoked.info['bads'])
+
+###############################################################################
+# Let's now interpolate the bad channels (displayed in red above)
+evoked.interpolate_bads(reset_bads=False)
+
+###############################################################################
+# Let's plot the cleaned data
+evoked.plot(exclude=[])
+
+###############################################################################
+# .. note::
+#     Interpolation is a linear operation that can be performed also on
+#     Raw and Epochs objects.
+#
+# For more details on interpolation see the page :ref:`channel_interpolation`.
+
+###############################################################################
+# .. _marking_bad_segments:
+#
+# Marking bad raw segments with annotations
+# -----------------------------------------
+#
+# MNE provides an :class:`mne.Annotations` class that can be used to mark
+# segments of raw data and to reject epochs that overlap with bad segments
+# of data. The annotations are automatically synchronized with raw data as
+# long as the timestamps of raw data and annotations are in sync.
+#
+# See :ref:`sphx_glr_auto_tutorials_plot_brainstorm_auditory.py`
+# for a long example exploiting the annotations for artifact removal.
+#
+# The instances of annotations are created by providing a list of onsets and
+# offsets with descriptions for each segment. The onsets and offsets are marked
+# as seconds. ``onset`` refers to time from start of the data. ``offset`` is
+# the duration of the annotation. The instance of :class:`mne.Annotations`
+# can be added as an attribute of :class:`mne.io.Raw`.
+
+eog_events = mne.preprocessing.find_eog_events(raw)
+n_blinks = len(eog_events)
+# Center to cover the whole blink with full duration of 0.5s:
+onset = eog_events[:, 0] / raw.info['sfreq'] - 0.25
+duration = np.repeat(0.5, n_blinks)
+raw.annotations = mne.Annotations(onset, duration, ['bad blink'] * n_blinks)
+raw.plot(events=eog_events)  # To see the annotated segments.
+
+###############################################################################
+# As the data is epoched, all the epochs overlapping with segments whose
+# description starts with 'bad' are rejected by default. To turn rejection off,
+# use keyword argument ``reject_by_annotation=False`` when constructing
+# :class:`mne.Epochs`. When working with neuromag data, the ``first_samp``
+# offset of raw acquisition is also taken into account the same way as with
+# event lists. For more see :class:`mne.Epochs` and :class:`mne.Annotations`.
+
+###############################################################################
+# .. _rejecting_bad_epochs:
+#
+# Rejecting bad epochs
+# --------------------
+#
+# When working with segmented data (Epochs) MNE offers a quite simple approach
+# to automatically reject/ignore bad epochs. This is done by defining
+# thresholds for peak-to-peak amplitude and flat signal detection.
+#
+# In the following code we build Epochs from Raw object. One of the provided
+# parameter is named *reject*. It is a dictionary where every key is a
+# channel type as a sring and the corresponding values are peak-to-peak
+# rejection parameters (amplitude ranges as floats). Below we define
+# the peak-to-peak rejection values for gradiometers,
+# magnetometers and EOG:
+
+reject = dict(grad=4000e-13, mag=4e-12, eog=150e-6)
+
+###############################################################################
+# .. note::
+#    The rejection values can be highly data dependent. You should be careful
+#    when adjusting these values. Make sure not too many epochs are rejected
+#    and look into the cause of the rejections. Maybe it's just a matter
+#    of marking a single channel as bad and you'll be able to save a lot
+#    of data.
+
+###############################################################################
+# We then construct the epochs
+events = mne.find_events(raw, stim_channel='STI 014')
+event_id = {"auditory/left": 1}
+tmin = -0.2  # start of each epoch (200ms before the trigger)
+tmax = 0.5  # end of each epoch (500ms after the trigger)
+baseline = (None, 0)  # means from the first instant to t = 0
+picks_meg = mne.pick_types(raw.info, meg=True, eeg=False, eog=True,
+                           stim=False, exclude='bads')
+epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
+                    picks=picks_meg, baseline=baseline, reject=reject,
+                    reject_by_annotation=True)
+
+###############################################################################
+# We then drop/reject the bad epochs
+epochs.drop_bad()
+
+###############################################################################
+# And plot the so-called *drop log* that details the reason for which some
+# epochs have been dropped.
+
+print(epochs.drop_log[40:45])  # only a subset
+epochs.plot_drop_log()
+
+###############################################################################
+# What you see is that some drop log values are empty. It means event was kept.
+# If it says 'IGNORED' is means the event_id did not contain the associated
+# event. If it gives the name of channel such as 'EOG 061' it means the
+# epoch was rejected because 'EOG 061' exceeded the peak-to-peak rejection
+# limit.
diff --git a/tutorials/plot_artifacts_correction_ssp.py b/tutorials/plot_artifacts_correction_ssp.py
new file mode 100644
index 0000000..642d196
--- /dev/null
+++ b/tutorials/plot_artifacts_correction_ssp.py
@@ -0,0 +1,88 @@
+"""
+
+.. _tut_artifacts_correct_ssp:
+
+Artifact Correction with SSP
+============================
+
+"""
+import numpy as np
+
+import mne
+from mne.datasets import sample
+from mne.preprocessing import compute_proj_ecg, compute_proj_eog
+
+# getting some data ready
+data_path = sample.data_path()
+raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
+
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
+raw.pick_types(meg=True, ecg=True, eog=True, stim=True)
+
+##############################################################################
+# Compute SSP projections
+# -----------------------
+
+projs, events = compute_proj_ecg(raw, n_grad=1, n_mag=1, average=True)
+print(projs)
+
+ecg_projs = projs[-2:]
+mne.viz.plot_projs_topomap(ecg_projs)
+
+# Now for EOG
+
+projs, events = compute_proj_eog(raw, n_grad=1, n_mag=1, average=True)
+print(projs)
+
+eog_projs = projs[-2:]
+mne.viz.plot_projs_topomap(eog_projs)
+
+##############################################################################
+# Apply SSP projections
+# ---------------------
+#
+# MNE is handling projections at the level of the info,
+# so to register them populate the list that you find in the 'proj' field
+
+raw.info['projs'] += eog_projs + ecg_projs
+
+#############################################################################
+# Yes this was it. Now MNE will apply the projs on demand at any later stage,
+# so watch out for proj parmeters in functions or to it explicitly
+# with the ``.apply_proj`` method
+
+#############################################################################
+# Demonstrate SSP cleaning on some evoked data
+# --------------------------------------------
+
+events = mne.find_events(raw, stim_channel='STI 014')
+reject = dict(grad=4000e-13, mag=4e-12, eog=150e-6)
+# this can be highly data dependent
+event_id = {'auditory/left': 1}
+
+epochs_no_proj = mne.Epochs(raw, events, event_id, tmin=-0.2, tmax=0.5,
+                            proj=False, baseline=(None, 0), reject=reject)
+epochs_no_proj.average().plot(spatial_colors=True)
+
+
+epochs_proj = mne.Epochs(raw, events, event_id, tmin=-0.2, tmax=0.5, proj=True,
+                         baseline=(None, 0), reject=reject)
+epochs_proj.average().plot(spatial_colors=True)
+
+##############################################################################
+# Looks cool right? It is however often not clear how many components you
+# should take and unfortunately this can have bad consequences as can be seen
+# interactively using the delayed SSP mode:
+
+evoked = mne.Epochs(raw, events, event_id, tmin=-0.2, tmax=0.5,
+                    proj='delayed', baseline=(None, 0),
+                    reject=reject).average()
+
+# set time instants in seconds (from 50 to 150ms in a step of 10ms)
+times = np.arange(0.05, 0.15, 0.01)
+
+evoked.plot_topomap(times, proj='interactive')
+
+##############################################################################
+# now you should see checkboxes. Remove a few SSP and see how the auditory
+# pattern suddenly drops off
diff --git a/tutorials/plot_artifacts_detection.py b/tutorials/plot_artifacts_detection.py
new file mode 100644
index 0000000..e38a945
--- /dev/null
+++ b/tutorials/plot_artifacts_detection.py
@@ -0,0 +1,134 @@
+"""
+.. _tut_artifacts_detect:
+
+Introduction to artifacts and artifact detection
+================================================
+
+Since MNE supports the data of many different acquisition systems, the
+particular artifacts in your data might behave very differently from the
+artifacts you can observe in our tutorials and examples.
+
+Therefore you should be aware of the different approaches and of
+the variability of artifact rejection (automatic/manual) procedures described
+onwards. At the end consider always to visually inspect your data
+after artifact rejection or correction.
+
+Background: what is an artifact?
+--------------------------------
+
+Artifacts are signal interference that can be
+endogenous (biological) and exogenous (environmental).
+Typical biological artifacts are head movements, eye blinks
+or eye movements, heart beats. The most common environmental
+artifact is due to the power line, the so-called *line noise*.
+
+How to handle artifacts?
+------------------------
+
+MNE deals with artifacts by first identifying them, and subsequently removing
+them. Detection of artifacts can be done visually, or using automatic routines
+(or a combination of both). After you know what the artifacts are, you need
+remove them. This can be done by:
+
+    - *ignoring* the piece of corrupted data
+    - *fixing* the corrupted data
+
+For the artifact detection the functions MNE provides depend on whether
+your data is continuous (Raw) or epoch-based (Epochs) and depending on
+whether your data is stored on disk or already in memory.
+
+Detecting the artifacts without reading the complete data into memory allows
+you to work with datasets that are too large to fit in memory all at once.
+Detecting the artifacts in continuous data allows you to apply filters
+(e.g. a band-pass filter to zoom in on the muscle artifacts on the temporal
+channels) without having to worry about edge effects due to the filter
+(i.e. filter ringing). Having the data in memory after segmenting/epoching is
+however a very efficient way of browsing through the data which helps
+in visualizing. So to conclude, there is not a single most optimal manner
+to detect the artifacts: it just depends on the data properties and your
+own preferences.
+
+In this tutorial we show how to detect artifacts visually and automatically.
+For how to correct artifacts by rejection see :ref:`tut_artifacts_reject`.
+To discover how to correct certain artifacts by filtering see
+:ref:`tut_artifacts_filter` and to learn how to correct artifacts
+with subspace methods like SSP and ICA see :ref:`tut_artifacts_correct_ssp`
+and :ref:`tut_artifacts_correct_ica`.
+
+
+Artifacts Detection
+-------------------
+
+This tutorial discusses a couple of major artifacts that most analyses
+have to deal with and demonstrates how to detect them.
+
+"""
+
+import mne
+from mne.datasets import sample
+from mne.preprocessing import create_ecg_epochs, create_eog_epochs
+
+# getting some data ready
+data_path = sample.data_path()
+raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
+
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
+
+
+###############################################################################
+# Low frequency drifts and line noise
+
+(raw.copy().pick_types(meg='mag')
+           .del_proj(0)
+           .plot(duration=60, n_channels=100, remove_dc=False))
+
+###############################################################################
+# we see high amplitude undulations in low frequencies, spanning across tens of
+# seconds
+
+raw.plot_psd(fmax=250)
+
+###############################################################################
+# On MEG sensors we see narrow frequency peaks at 60, 120, 180, 240 Hz,
+# related to line noise.
+# But also some high amplitude signals between 25 and 32 Hz, hinting at other
+# biological artifacts such as ECG. These can be most easily detected in the
+# time domain using MNE helper functions
+#
+# See :ref:`tut_artifacts_filter`.
+
+###############################################################################
+# ECG
+# ---
+#
+# finds ECG events, creates epochs, averages and plots
+
+average_ecg = create_ecg_epochs(raw).average()
+print('We found %i ECG events' % average_ecg.nave)
+average_ecg.plot_joint()
+
+###############################################################################
+# we can see typical time courses and non dipolar topographies
+# not the order of magnitude of the average artifact related signal and
+# compare this to what you observe for brain signals
+
+###############################################################################
+# EOG
+# ---
+
+average_eog = create_eog_epochs(raw).average()
+print('We found %i EOG events' % average_eog.nave)
+average_eog.plot_joint()
+
+###############################################################################
+# Knowing these artifact patterns is of paramount importance when
+# judging about the quality of artifact removal techniques such as SSP or ICA.
+# As a rule of thumb you need artifact amplitudes orders of magnitude higher
+# than your signal of interest and you need a few of such events in order
+# to find decompositions that allow you to estimate and remove patterns related
+# to artifacts.
+#
+# Consider the following tutorials for correcting this class of artifacts:
+#     - :ref:`tut_artifacts_filter`
+#     - :ref:`tut_artifacts_correct_ica`
+#     - :ref:`tut_artifacts_correct_ssp`
diff --git a/tutorials/plot_brainstorm_auditory.py b/tutorials/plot_brainstorm_auditory.py
new file mode 100644
index 0000000..a2adb75
--- /dev/null
+++ b/tutorials/plot_brainstorm_auditory.py
@@ -0,0 +1,357 @@
+# -*- coding: utf-8 -*-
+"""
+====================================
+Brainstorm auditory tutorial dataset
+====================================
+
+Here we compute the evoked from raw for the auditory Brainstorm
+tutorial dataset. For comparison, see [1]_ and
+http://neuroimage.usc.edu/brainstorm/Tutorials/Auditory
+
+Experiment:
+    - One subject 2 acquisition runs 6 minutes each.
+    - Each run contains 200 regular beeps and 40 easy deviant beeps.
+    - Random ISI: between 0.7s and 1.7s seconds, uniformly distributed.
+    - Button pressed when detecting a deviant with the right index finger.
+
+The specifications of this dataset were discussed initially on the FieldTrip
+bug tracker:
+http://bugzilla.fcdonders.nl/show_bug.cgi?id=2300
+
+References
+----------
+.. [1] Tadel F, Baillet S, Mosher JC, Pantazis D, Leahy RM.
+       Brainstorm: A User-Friendly Application for MEG/EEG Analysis.
+       Computational Intelligence and Neuroscience, vol. 2011, Article ID
+       879716, 13 pages, 2011. doi:10.1155/2011/879716
+"""
+
+# Authors: Mainak Jas <mainak.jas at telecom-paristech.fr>
+#          Eric Larson <larson.eric.d at gmail.com>
+#          Jaakko Leppakangas <jaeilepp at student.jyu.fi>
+#
+# License: BSD (3-clause)
+
+import os.path as op
+import pandas as pd
+import numpy as np
+
+import mne
+from mne import combine_evoked
+from mne.minimum_norm import apply_inverse
+from mne.datasets.brainstorm import bst_auditory
+from mne.io import read_raw_ctf
+from mne.filter import notch_filter, low_pass_filter
+
+print(__doc__)
+
+###############################################################################
+# To reduce memory consumption and running time, some of the steps are
+# precomputed. To run everything from scratch change this to False. With
+# ``use_precomputed = False`` running time of this script can be several
+# minutes even on a fast computer.
+use_precomputed = True
+
+###############################################################################
+# The data was collected with a CTF 275 system at 2400 Hz and low-pass
+# filtered at 600 Hz. Here the data and empty room data files are read to
+# construct instances of :class:`mne.io.Raw`.
+data_path = bst_auditory.data_path()
+
+subject = 'bst_auditory'
+subjects_dir = op.join(data_path, 'subjects')
+
+raw_fname1 = op.join(data_path, 'MEG', 'bst_auditory',
+                     'S01_AEF_20131218_01.ds')
+raw_fname2 = op.join(data_path, 'MEG', 'bst_auditory',
+                     'S01_AEF_20131218_02.ds')
+erm_fname = op.join(data_path, 'MEG', 'bst_auditory',
+                    'S01_Noise_20131218_01.ds')
+
+###############################################################################
+# In the memory saving mode we use ``preload=False`` and use the memory
+# efficient IO which loads the data on demand. However, filtering and some
+# other functions require the data to be preloaded in the memory.
+preload = not use_precomputed
+raw = read_raw_ctf(raw_fname1, preload=preload)
+n_times_run1 = raw.n_times
+mne.io.concatenate_raws([raw, read_raw_ctf(raw_fname2, preload=preload)])
+raw_erm = read_raw_ctf(erm_fname, preload=preload)
+
+###############################################################################
+# Data channel array consisted of 274 MEG axial gradiometers, 26 MEG reference
+# sensors and 2 EEG electrodes (Cz and Pz).
+# In addition:
+#   - 1 stim channel for marking presentation times for the stimuli
+#   - 1 audio channel for the sent signal
+#   - 1 response channel for recording the button presses
+#   - 1 ECG bipolar
+#   - 2 EOG bipolar (vertical and horizontal)
+#   - 12 head tracking channels
+#   - 20 unused channels
+# The head tracking channels and the unused channels are marked as misc
+# channels. Here we define the EOG and ECG channels.
+raw.set_channel_types({'HEOG': 'eog', 'VEOG': 'eog', 'ECG': 'ecg'})
+if not use_precomputed:
+    # Leave out the two EEG channels for easier computation of forward.
+    raw.pick_types(meg=True, eeg=False, stim=True, misc=True, eog=True,
+                   ecg=True)
+
+###############################################################################
+# For noise reduction, a set of bad segments have been identified and stored
+# in csv files. The bad segments are later used to reject epochs that overlap
+# with them.
+# The file for the second run also contains some saccades. The saccades are
+# removed by using SSP. We use pandas to read the data from the csv files. You
+# can also view the files with your favorite text editor.
+
+annotations_df = pd.DataFrame()
+offset = n_times_run1
+for idx in [1, 2]:
+    csv_fname = op.join(data_path, 'MEG', 'bst_auditory',
+                        'events_bad_0%s.csv' % idx)
+    df = pd.read_csv(csv_fname, header=None,
+                     names=['onset', 'duration', 'id', 'label'])
+    print('Events from run {0}:'.format(idx))
+    print(df)
+
+    df['onset'] += offset * (idx - 1)
+    annotations_df = pd.concat([annotations_df, df], axis=0)
+
+saccades_events = df[df['label'] == 'saccade'].values[:, :3].astype(int)
+
+# Conversion from samples to times:
+onsets = annotations_df['onset'].values / raw.info['sfreq']
+durations = annotations_df['duration'].values / raw.info['sfreq']
+descriptions = map(str, annotations_df['label'].values)
+
+annotations = mne.Annotations(onsets, durations, descriptions)
+raw.annotations = annotations
+del onsets, durations, descriptions
+
+###############################################################################
+# Here we compute the saccade and EOG projectors for magnetometers and add
+# them to the raw data. The projectors are added to both runs.
+saccade_epochs = mne.Epochs(raw, saccades_events, 1, 0., 0.5, preload=True,
+                            reject_by_annotation=False)
+
+projs_saccade = mne.compute_proj_epochs(saccade_epochs, n_mag=1, n_eeg=0,
+                                        desc_prefix='saccade')
+if use_precomputed:
+    proj_fname = op.join(data_path, 'MEG', 'bst_auditory',
+                         'bst_auditory-eog-proj.fif')
+    projs_eog = mne.read_proj(proj_fname)[0]
+else:
+    projs_eog, _ = mne.preprocessing.compute_proj_eog(raw.load_data(),
+                                                      n_mag=1, n_eeg=0)
+raw.add_proj(projs_saccade)
+raw.add_proj(projs_eog)
+del saccade_epochs, saccades_events, projs_eog, projs_saccade  # To save memory
+
+###############################################################################
+# Visually inspect the effects of projections. Click on 'proj' button at the
+# bottom right corner to toggle the projectors on/off. EOG events can be
+# plotted by adding the event list as a keyword argument. As the bad segments
+# and saccades were added as annotations to the raw data, they are plotted as
+# well.
+raw.plot(block=True)
+
+###############################################################################
+# Typical preprocessing step is the removal of power line artifact (50 Hz or
+# 60 Hz). Here we notch filter the data at 60, 120 and 180 to remove the
+# original 60 Hz artifact and the harmonics. The power spectra are plotted
+# before and after the filtering to show the effect. The drop after 600 Hz
+# appears because the data was filtered during the acquisition. In memory
+# saving mode we do the filtering at evoked stage, which is not something you
+# usually would do.
+if not use_precomputed:
+    meg_picks = mne.pick_types(raw.info, meg=True, eeg=False)
+    raw.plot_psd(picks=meg_picks)
+    notches = np.arange(60, 181, 60)
+    raw.notch_filter(notches)
+    raw.plot_psd(picks=meg_picks)
+
+###############################################################################
+# We also lowpass filter the data at 100 Hz to remove the hf components.
+if not use_precomputed:
+    raw.filter(None, 100.)
+
+###############################################################################
+# Epoching and averaging.
+# First some parameters are defined and events extracted from the stimulus
+# channel (UPPT001). The rejection thresholds are defined as peak-to-peak
+# values and are in T / m for gradiometers, T for magnetometers and
+# V for EOG and EEG channels.
+tmin, tmax = -0.1, 0.5
+event_id = dict(standard=1, deviant=2)
+reject = dict(mag=4e-12, eog=250e-6)
+# find events
+events = mne.find_events(raw, stim_channel='UPPT001')
+
+###############################################################################
+# The event timing is adjusted by comparing the trigger times on detected
+# sound onsets on channel UADC001-4408.
+sound_data = raw[raw.ch_names.index('UADC001-4408')][0][0]
+onsets = np.where(np.abs(sound_data) > 2. * np.std(sound_data))[0]
+min_diff = int(0.5 * raw.info['sfreq'])
+diffs = np.concatenate([[min_diff + 1], np.diff(onsets)])
+onsets = onsets[diffs > min_diff]
+assert len(onsets) == len(events)
+diffs = 1000. * (events[:, 0] - onsets) / raw.info['sfreq']
+print('Trigger delay removed (μ ± σ): %0.1f ± %0.1f ms'
+      % (np.mean(diffs), np.std(diffs)))
+events[:, 0] = onsets
+del sound_data, diffs
+
+###############################################################################
+# We mark a set of bad channels that seem noisier than others. This can also
+# be done interactively with ``raw.plot`` by clicking the channel name
+# (or the line). The marked channels are added as bad when the browser window
+# is closed.
+raw.info['bads'] = ['MLO52-4408', 'MRT51-4408', 'MLO42-4408', 'MLO43-4408']
+
+###############################################################################
+# The epochs (trials) are created for MEG channels. First we find the picks
+# for MEG and EOG channels. Then the epochs are constructed using these picks.
+# The epochs overlapping with annotated bad segments are also rejected by
+# default. To turn off rejection by bad segments (as was done earlier with
+# saccades) you can use keyword ``reject_by_annotation=False``.
+picks = mne.pick_types(raw.info, meg=True, eeg=False, stim=False, eog=True,
+                       exclude='bads')
+
+epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
+                    baseline=(None, 0), reject=reject, preload=False,
+                    proj=True)
+
+###############################################################################
+# We only use first 40 good epochs from each run. Since we first drop the bad
+# epochs, the indices of the epochs are no longer same as in the original
+# epochs collection. Investigation of the event timings reveals that first
+# epoch from the second run corresponds to index 182.
+epochs.drop_bad()
+epochs_standard = mne.concatenate_epochs([epochs['standard'][range(40)],
+                                          epochs['standard'][182:222]])
+epochs_standard.load_data()  # Resampling to save memory.
+epochs_standard.resample(600, npad='auto')
+epochs_deviant = epochs['deviant'].load_data()
+epochs_deviant.resample(600, npad='auto')
+del epochs, picks
+
+###############################################################################
+# The averages for each conditions are computed.
+evoked_std = epochs_standard.average()
+evoked_dev = epochs_deviant.average()
+del epochs_standard, epochs_deviant
+
+###############################################################################
+# Typical preprocessing step is the removal of power line artifact (50 Hz or
+# 60 Hz). Here we notch filter the data at 60, 120 and 180 to remove the
+# original 60 Hz artifact and the harmonics. Normally this would be done to
+# raw data (with :func:`mne.io.Raw.filter`), but to reduce memory consumption
+# of this tutorial, we do it at evoked stage.
+if use_precomputed:
+    sfreq = evoked_std.info['sfreq']
+    nchan = evoked_std.info['nchan']
+    notches = [60, 120, 180]
+    for ch_idx in range(nchan):
+        evoked_std.data[ch_idx] = notch_filter(evoked_std.data[ch_idx], sfreq,
+                                               notches, verbose='ERROR')
+        evoked_dev.data[ch_idx] = notch_filter(evoked_dev.data[ch_idx], sfreq,
+                                               notches, verbose='ERROR')
+        evoked_std.data[ch_idx] = low_pass_filter(evoked_std.data[ch_idx],
+                                                  sfreq, 100, verbose='ERROR')
+        evoked_dev.data[ch_idx] = low_pass_filter(evoked_dev.data[ch_idx],
+                                                  sfreq, 100, verbose='ERROR')
+
+###############################################################################
+# Here we plot the ERF of standard and deviant conditions. In both conditions
+# we can see the P50 and N100 responses. The mismatch negativity is visible
+# only in the deviant condition around 100-200 ms. P200 is also visible around
+# 170 ms in both conditions but much stronger in the standard condition. P300
+# is visible in deviant condition only (decision making in preparation of the
+# button press). You can view the topographies from a certain time span by
+# painting an area with clicking and holding the left mouse button.
+evoked_std.plot(window_title='Standard', gfp=True)
+evoked_dev.plot(window_title='Deviant', gfp=True)
+
+###############################################################################
+# Show activations as topography figures.
+times = np.arange(0.05, 0.301, 0.025)
+evoked_std.plot_topomap(times=times, title='Standard')
+evoked_dev.plot_topomap(times=times, title='Deviant')
+
+###############################################################################
+# We can see the MMN effect more clearly by looking at the difference between
+# the two conditions. P50 and N100 are no longer visible, but MMN/P200 and
+# P300 are emphasised.
+evoked_difference = combine_evoked([evoked_dev, evoked_std], weights=[1, -1])
+evoked_difference.plot(window_title='Difference', gfp=True)
+
+###############################################################################
+# Source estimation.
+# We compute the noise covariance matrix from the empty room measurement
+# and use it for the other runs.
+reject = dict(mag=4e-12)
+cov = mne.compute_raw_covariance(raw_erm, reject=reject)
+cov.plot(raw_erm.info)
+del raw_erm
+
+###############################################################################
+# The transformation is read from a file. More information about coregistering
+# the data, see :ref:`ch_interactive_analysis` or
+# :func:`mne.gui.coregistration`.
+trans_fname = op.join(data_path, 'MEG', 'bst_auditory',
+                      'bst_auditory-trans.fif')
+trans = mne.read_trans(trans_fname)
+
+###############################################################################
+# To save time and memory, the forward solution is read from a file. Set
+# ``use_precomputed=False`` in the beginning of this script to build the
+# forward solution from scratch. The head surfaces for constructing a BEM
+# solution are read from a file. Since the data only contains MEG channels, we
+# only need the inner skull surface for making the forward solution. For more
+# information: :ref:`CHDBBCEJ`, :class:`mne.setup_source_space`,
+# :ref:`create_bem_model`, :func:`mne.bem.make_watershed_bem`.
+if use_precomputed:
+    fwd_fname = op.join(data_path, 'MEG', 'bst_auditory',
+                        'bst_auditory-meg-oct-6-fwd.fif')
+    fwd = mne.read_forward_solution(fwd_fname)
+else:
+    src = mne.setup_source_space(subject, spacing='ico4',
+                                 subjects_dir=subjects_dir, overwrite=True)
+    model = mne.make_bem_model(subject=subject, ico=4, conductivity=[0.3],
+                               subjects_dir=subjects_dir)
+    bem = mne.make_bem_solution(model)
+    fwd = mne.make_forward_solution(evoked_std.info, trans=trans, src=src,
+                                    bem=bem)
+
+inv = mne.minimum_norm.make_inverse_operator(evoked_std.info, fwd, cov)
+snr = 3.0
+lambda2 = 1.0 / snr ** 2
+del fwd
+
+###############################################################################
+# The sources are computed using dSPM method and plotted on an inflated brain
+# surface. For interactive controls over the image, use keyword
+# ``time_viewer=True``.
+# Standard condition.
+stc_standard = mne.minimum_norm.apply_inverse(evoked_std, inv, lambda2, 'dSPM')
+brain = stc_standard.plot(subjects_dir=subjects_dir, subject=subject,
+                          surface='inflated', time_viewer=False, hemi='lh')
+brain.set_data_time_index(120)
+del stc_standard, evoked_std, brain
+
+###############################################################################
+# Deviant condition.
+stc_deviant = mne.minimum_norm.apply_inverse(evoked_dev, inv, lambda2, 'dSPM')
+brain = stc_deviant.plot(subjects_dir=subjects_dir, subject=subject,
+                         surface='inflated', time_viewer=False, hemi='lh')
+brain.set_data_time_index(120)
+del stc_deviant, evoked_dev, brain
+
+###############################################################################
+# Difference.
+stc_difference = apply_inverse(evoked_difference, inv, lambda2, 'dSPM')
+brain = stc_difference.plot(subjects_dir=subjects_dir, subject=subject,
+                            surface='inflated', time_viewer=False, hemi='lh')
+brain.set_data_time_index(150)
diff --git a/tutorials/plot_compute_covariance.py b/tutorials/plot_compute_covariance.py
new file mode 100644
index 0000000..55ebd7f
--- /dev/null
+++ b/tutorials/plot_compute_covariance.py
@@ -0,0 +1,138 @@
+"""
+.. _tut_compute_covariance:
+
+Computing covariance matrix
+===========================
+"""
+import os.path as op
+
+import mne
+from mne.datasets import sample
+
+###############################################################################
+# Source estimation method such as MNE require a noise estimations from the
+# recordings. In this tutorial we cover the basics of noise covariance and
+# construct a noise covariance matrix that can be used when computing the
+# inverse solution. For more information, see :ref:`BABDEEEB`.
+data_path = sample.data_path()
+raw_empty_room_fname = op.join(
+    data_path, 'MEG', 'sample', 'ernoise_raw.fif')
+raw_empty_room = mne.io.read_raw_fif(raw_empty_room_fname)
+raw_fname = op.join(data_path, 'MEG', 'sample', 'sample_audvis_raw.fif')
+raw = mne.io.read_raw_fif(raw_fname)
+raw.info['bads'] += ['EEG 053']  # bads + 1 more
+
+###############################################################################
+# The definition of noise depends on the paradigm. In MEG it is quite common
+# to use empty room measurements for the estimation of sensor noise. However if
+# you are dealing with evoked responses, you might want to also consider
+# resting state brain activity as noise.
+# First we compute the noise using empty room recording. Note that you can also
+# use only a part of the recording with tmin and tmax arguments. That can be
+# useful if you use resting state as a noise baseline. Here we use the whole
+# empty room recording to compute the noise covariance (tmax=None is the same
+# as the end of the recording, see :func:`mne.compute_raw_covariance`).
+noise_cov = mne.compute_raw_covariance(raw_empty_room, tmin=0, tmax=None)
+
+###############################################################################
+# Now that you the covariance matrix in a python object you can save it to a
+# file with :func:`mne.write_cov`. Later you can read it back to a python
+# object using :func:`mne.read_cov`.
+#
+# You can also use the pre-stimulus baseline to estimate the noise covariance.
+# First we have to construct the epochs. When computing the covariance, you
+# should use baseline correction when constructing the epochs. Otherwise the
+# covariance matrix will be inaccurate. In MNE this is done by default, but
+# just to be sure, we define it here manually.
+events = mne.find_events(raw)
+epochs = mne.Epochs(raw, events, event_id=1, tmin=-0.2, tmax=0.0,
+                    baseline=(-0.2, 0.0))
+
+###############################################################################
+# Note that this method also attenuates the resting state activity in your
+# source estimates.
+noise_cov_baseline = mne.compute_covariance(epochs)
+
+###############################################################################
+# Plot the covariance matrices
+# ----------------------------
+#
+# Try setting proj to False to see the effect. Notice that the projectors in
+# epochs are already applied, so ``proj`` parameter has no effect.
+noise_cov.plot(raw_empty_room.info, proj=True)
+noise_cov_baseline.plot(epochs.info)
+
+###############################################################################
+# How should I regularize the covariance matrix?
+# ----------------------------------------------
+#
+# The estimated covariance can be numerically
+# unstable and tends to induce correlations between estimated source amplitudes
+# and the number of samples available. The MNE manual therefore suggests to
+# regularize the noise covariance matrix (see
+# :ref:`cov_regularization`), especially if only few samples are available.
+# Unfortunately it is not easy to tell the effective number of samples, hence,
+# to choose the appropriate regularization.
+# In MNE-Python, regularization is done using advanced regularization methods
+# described in [1]_. For this the 'auto' option can be used. With this
+# option cross-validation will be used to learn the optimal regularization:
+
+cov = mne.compute_covariance(epochs, tmax=0., method='auto')
+
+###############################################################################
+# This procedure evaluates the noise covariance quantitatively by how well it
+# whitens the data using the
+# negative log-likelihood of unseen data. The final result can also be visually
+# inspected.
+# Under the assumption that the baseline does not contain a systematic signal
+# (time-locked to the event of interest), the whitened baseline signal should
+# be follow a multivariate Gaussian distribution, i.e.,
+# whitened baseline signals should be between -1.96 and 1.96 at a given time
+# sample.
+# Based on the same reasoning, the expected value for the global field power
+# (GFP) is 1 (calculation of the GFP should take into account the true degrees
+# of freedom, e.g. ``ddof=3`` with 2 active SSP vectors):
+
+evoked = epochs.average()
+evoked.plot_white(cov)
+
+###############################################################################
+# This plot displays both, the whitened evoked signals for each channels and
+# the whitened GFP. The numbers in the GFP panel represent the estimated rank
+# of the data, which amounts to the effective degrees of freedom by which the
+# squared sum across sensors is divided when computing the whitened GFP.
+# The whitened GFP also helps detecting spurious late evoked components which
+# can be the consequence of over- or under-regularization.
+#
+# Note that if data have been processed using signal space separation
+# (SSS) [2]_,
+# gradiometers and magnetometers will be displayed jointly because both are
+# reconstructed from the same SSS basis vectors with the same numerical rank.
+# This also implies that both sensor types are not any longer statistically
+# independent.
+# These methods for evaluation can be used to assess model violations.
+# Additional
+# introductory materials can be found `here <https://goo.gl/ElWrxe>`_.
+#
+# For expert use cases or debugging the alternative estimators can also be
+# compared:
+
+covs = mne.compute_covariance(epochs, tmax=0., method=('empirical', 'shrunk'),
+                              return_estimators=True)
+evoked = epochs.average()
+evoked.plot_white(covs)
+
+##############################################################################
+# This will plot the whitened evoked for the optimal estimator and display the
+# GFPs for all estimators as separate lines in the related panel.
+
+###############################################################################
+# References
+# ----------
+#
+# .. [1] Engemann D. and Gramfort A. (2015) Automated model selection in
+#     covariance estimation and spatial whitening of MEG and EEG signals,
+#     vol. 108, 328-342, NeuroImage.
+#
+# .. [2] Taulu, S., Simola, J., Kajola, M., 2005. Applications of the signal
+#    space separation method. IEEE Trans. Signal Proc. 53, 3359–3372.
diff --git a/tutorials/plot_creating_data_structures.py b/tutorials/plot_creating_data_structures.py
index ce4b32e..72f099c 100644
--- a/tutorials/plot_creating_data_structures.py
+++ b/tutorials/plot_creating_data_structures.py
@@ -13,14 +13,15 @@ import numpy as np
 
 ###############################################################################
 # ------------------------------------------------------
-# Creating :class:`Info <mne.io.meas_info.Info>` objects
+# Creating :class:`Info <mne.Info>` objects
 # ------------------------------------------------------
 #
 # .. note:: for full documentation on the `Info` object, see
-#           :ref:`tut_info_objects`.
+#           :ref:`tut_info_objects`. See also
+#           :ref:`sphx_glr_auto_examples_io_plot_objects_from_arrays.py`.
 #
-# Normally, :class:`mne.io.meas_info.Info` objects are created by the various
-# :ref:`data import functions` <ch_raw>`.
+# Normally, :class:`mne.Info` objects are created by the various
+# :ref:`data import functions <ch_convert>`.
 # However, if you wish to create one from scratch, you can use the
 # :func:`mne.create_info` function to initialize the minimally required
 # fields. Further fields can be assigned later as one would with a regular
@@ -62,7 +63,7 @@ print(info)
 
 ###############################################################################
 # .. note:: When assigning new values to the fields of an
-#           :class:`mne.io.meas_info.Info` object, it is important that the
+#           :class:`mne.Info` object, it is important that the
 #           fields are consistent:
 #
 #           - The length of the channel information field `chs` must be
@@ -72,13 +73,13 @@ print(info)
 #             of the channel information contained in `chs`.
 #
 # ---------------------------------------------
-# Creating :class:`Raw <mne.io.RawFIF>` objects
+# Creating :class:`Raw <mne.io.Raw>` objects
 # ---------------------------------------------
 #
 # To create a :class:`mne.io.Raw` object from scratch, you can use the
-# :class:`mne.RawArray` class, which implements raw data that is backed by a
+# :class:`mne.io.RawArray` class, which implements raw data that is backed by a
 # numpy array.  Its constructor simply takes the data matrix and
-# :class:`mne.io.meas_info.Info` object:
+# :class:`mne.Info` object:
 
 # Generate some random data
 data = np.random.randn(5, 1000)
@@ -105,7 +106,7 @@ print(custom_raw)
 
 # Generate some random data: 10 epochs, 5 channels, 2 seconds per epoch
 sfreq = 100
-data = np.random.randn(10, 5, sfreq*2)
+data = np.random.randn(10, 5, sfreq * 2)
 
 # Initialize an info structure
 info = mne.create_info(
diff --git a/tutorials/plot_dipole_fit.py b/tutorials/plot_dipole_fit.py
new file mode 100644
index 0000000..54d8856
--- /dev/null
+++ b/tutorials/plot_dipole_fit.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+"""
+==========================================
+Source localization with single dipole fit
+==========================================
+
+This shows how to fit a dipole using mne-python.
+
+For a comparison of fits between MNE-C and mne-python, see:
+
+    https://gist.github.com/Eric89GXL/ca55f791200fe1dc3dd2
+
+Note that for 3D graphics you may need to choose a specific IPython
+backend, such as:
+
+`%matplotlib qt` or `%matplotlib wx`
+"""
+
+from os import path as op
+import numpy as np
+import matplotlib.pyplot as plt
+
+import mne
+from mne.forward import make_forward_dipole
+from mne.evoked import combine_evoked
+from mne.simulation import simulate_evoked
+
+data_path = mne.datasets.sample.data_path()
+subjects_dir = op.join(data_path, 'subjects')
+fname_ave = op.join(data_path, 'MEG', 'sample', 'sample_audvis-ave.fif')
+fname_cov = op.join(data_path, 'MEG', 'sample', 'sample_audvis-cov.fif')
+fname_bem = op.join(subjects_dir, 'sample', 'bem', 'sample-5120-bem-sol.fif')
+fname_trans = op.join(data_path, 'MEG', 'sample',
+                      'sample_audvis_raw-trans.fif')
+fname_surf_lh = op.join(subjects_dir, 'sample', 'surf', 'lh.white')
+
+###############################################################################
+# Let's localize the N100m (using MEG only)
+evoked = mne.read_evokeds(fname_ave, condition='Right Auditory',
+                          baseline=(None, 0))
+evoked.pick_types(meg=True, eeg=False)
+evoked_full = evoked.copy()
+evoked.crop(0.07, 0.08)
+
+# Fit a dipole
+dip = mne.fit_dipole(evoked, fname_cov, fname_bem, fname_trans)[0]
+
+# Plot the result in 3D brain
+dip.plot_locations(fname_trans, 'sample', subjects_dir)
+
+###############################################################################
+# Calculate and visualise magnetic field predicted by dipole with maximum GOF
+# and compare to the measured data, highlighting the ipsilateral (right) source
+fwd, stc = make_forward_dipole(dip, fname_bem, evoked.info, fname_trans)
+pred_evoked = simulate_evoked(fwd, stc, evoked.info, None, snr=np.inf)
+
+# find time point with highes GOF to plot
+best_idx = np.argmax(dip.gof)
+best_time = dip.times[best_idx]
+# rememeber to create a subplot for the colorbar
+fig, axes = plt.subplots(nrows=1, ncols=4, figsize=[10., 3.4])
+vmin, vmax = -400, 400  # make sure each plot has same colour range
+
+# first plot the topography at the time of the best fitting (single) dipole
+plot_params = dict(times=best_time, ch_type='mag', outlines='skirt',
+                   colorbar=False)
+evoked.plot_topomap(time_format='Measured field', axes=axes[0], **plot_params)
+
+# compare this to the predicted field
+pred_evoked.plot_topomap(time_format='Predicted field', axes=axes[1],
+                         **plot_params)
+
+# Subtract predicted from measured data (apply equal weights)
+diff = combine_evoked([evoked, pred_evoked], [1, -1])
+plot_params['colorbar'] = True
+diff.plot_topomap(time_format='Difference', axes=axes[2], **plot_params)
+plt.suptitle('Comparison of measured and predicted fields '
+             'at {:.0f} ms'.format(best_time * 1000.), fontsize=16)
+
+###############################################################################
+# Estimate the time course of a single dipole with fixed position and
+# orientation (the one that maximized GOF)over the entire interval
+dip_fixed = mne.fit_dipole(evoked_full, fname_cov, fname_bem, fname_trans,
+                           pos=dip.pos[best_idx], ori=dip.ori[best_idx])[0]
+dip_fixed.plot()
diff --git a/tutorials/plot_eeg_erp.py b/tutorials/plot_eeg_erp.py
new file mode 100644
index 0000000..c22e168
--- /dev/null
+++ b/tutorials/plot_eeg_erp.py
@@ -0,0 +1,191 @@
+"""
+.. _tut_erp:
+
+EEG processing and Event Related Potentials (ERPs)
+==================================================
+
+For a generic introduction to the computation of ERP and ERF
+see :ref:`tut_epoching_and_averaging`. Here we cover the specifics
+of EEG, namely:
+    - setting the reference
+    - using standard montages :func:`mne.channels.Montage`
+    - Evoked arithmetic (e.g. differences)
+
+"""
+
+import mne
+from mne.datasets import sample
+
+###############################################################################
+# Setup for reading the raw data
+data_path = sample.data_path()
+raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
+event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
+raw = mne.io.read_raw_fif(raw_fname, add_eeg_ref=True, preload=True)
+
+###############################################################################
+# Let's restrict the data to the EEG channels
+raw.pick_types(meg=False, eeg=True, eog=True)
+
+###############################################################################
+# By looking at the measurement info you will see that we have now
+# 59 EEG channels and 1 EOG channel
+print(raw.info)
+
+###############################################################################
+# In practice it's quite common to have some EEG channels that are actually
+# EOG channels. To change a channel type you can use the
+# :func:`mne.io.Raw.set_channel_types` method. For example
+# to treat an EOG channel as EEG you can change its type using
+raw.set_channel_types(mapping={'EOG 061': 'eeg'})
+print(raw.info)
+
+###############################################################################
+# And to change the nameo of the EOG channel
+raw.rename_channels(mapping={'EOG 061': 'EOG'})
+
+###############################################################################
+# Let's reset the EOG channel back to EOG type.
+raw.set_channel_types(mapping={'EOG': 'eog'})
+
+###############################################################################
+# The EEG channels in the sample dataset already have locations.
+# These locations are available in the 'loc' of each channel description.
+# For the first channel we get
+
+print(raw.info['chs'][0]['loc'])
+
+###############################################################################
+# And it's actually possible to plot the channel locations using
+# the :func:`mne.io.Raw.plot_sensors` method
+
+raw.plot_sensors()
+raw.plot_sensors('3d')  # in 3D
+
+###############################################################################
+# Setting EEG montage
+# -------------------
+#
+# In the case where your data don't have locations you can set them
+# using a :func:`mne.channels.Montage`. MNE comes with a set of default
+# montages. To read one of them do:
+
+montage = mne.channels.read_montage('standard_1020')
+print(montage)
+
+###############################################################################
+# To apply a montage on your data use the :func:`mne.io.set_montage`
+# function. Here don't actually call this function as our demo dataset
+# already contains good EEG channel locations.
+#
+# Next we'll explore the definition of the reference.
+
+###############################################################################
+# Setting EEG reference
+# ---------------------
+#
+# Let's first remove the reference from our Raw object.
+#
+# This explicitly prevents MNE from adding a default EEG average reference
+# required for source localization.
+
+raw_no_ref, _ = mne.io.set_eeg_reference(raw, [])
+
+###############################################################################
+# We next define Epochs and compute an ERP for the left auditory condition.
+reject = dict(eeg=180e-6, eog=150e-6)
+event_id, tmin, tmax = {'left/auditory': 1}, -0.2, 0.5
+events = mne.read_events(event_fname)
+epochs_params = dict(events=events, event_id=event_id, tmin=tmin, tmax=tmax,
+                     reject=reject)
+
+evoked_no_ref = mne.Epochs(raw_no_ref, **epochs_params).average()
+del raw_no_ref  # save memory
+
+title = 'EEG Original reference'
+evoked_no_ref.plot(titles=dict(eeg=title))
+evoked_no_ref.plot_topomap(times=[0.1], size=3., title=title)
+
+###############################################################################
+# **Average reference**: This is normally added by default, but can also
+# be added explicitly.
+raw_car, _ = mne.io.set_eeg_reference(raw)
+evoked_car = mne.Epochs(raw_car, **epochs_params).average()
+del raw_car  # save memory
+
+title = 'EEG Average reference'
+evoked_car.plot(titles=dict(eeg=title))
+evoked_car.plot_topomap(times=[0.1], size=3., title=title)
+
+###############################################################################
+# **Custom reference**: Use the mean of channels EEG 001 and EEG 002 as
+# a reference
+raw_custom, _ = mne.io.set_eeg_reference(raw, ['EEG 001', 'EEG 002'])
+evoked_custom = mne.Epochs(raw_custom, **epochs_params).average()
+del raw_custom  # save memory
+
+title = 'EEG Custom reference'
+evoked_custom.plot(titles=dict(eeg=title))
+evoked_custom.plot_topomap(times=[0.1], size=3., title=title)
+
+###############################################################################
+# Evoked arithmetics
+# ------------------
+#
+# Trial subsets from Epochs can be selected using 'tags' separated by '/'.
+# Evoked objects support basic arithmetic.
+# First, we create an Epochs object containing 4 conditions.
+
+event_id = {'left/auditory': 1, 'right/auditory': 2,
+            'left/visual': 3, 'right/visual': 4}
+epochs_params = dict(events=events, event_id=event_id, tmin=tmin, tmax=tmax,
+                     reject=reject)
+epochs = mne.Epochs(raw, **epochs_params)
+
+print(epochs)
+
+###############################################################################
+# Next, we create averages of stimulation-left vs stimulation-right trials.
+# We can use basic arithmetic to, for example, construct and plot
+# difference ERPs.
+
+left, right = epochs["left"].average(), epochs["right"].average()
+
+(left - right).plot_joint()  # create and plot difference ERP
+
+###############################################################################
+# Note that by default, this is a trial-weighted average. If you have
+# imbalanced trial numbers, consider either equalizing the number of events per
+# condition (using ``Epochs.equalize_event_counts``), or the ``combine_evoked``
+# function.
+# As an example, first, we create individual ERPs for each condition.
+
+aud_l = epochs["auditory", "left"].average()
+aud_r = epochs["auditory", "right"].average()
+vis_l = epochs["visual", "left"].average()
+vis_r = epochs["visual", "right"].average()
+
+all_evokeds = [aud_l, aud_r, vis_l, vis_r]
+
+# This could have been much simplified with a list comprehension:
+# all_evokeds = [epochs[cond] for cond in event_id]
+
+# Then, we construct and plot an unweighted average of left vs. right trials.
+mne.combine_evoked(all_evokeds, weights=(1, -1, 1, -1)).plot_joint()
+
+###############################################################################
+# Often, it makes sense to store Evoked objects in a dictionary or a list -
+# either different conditions, or different subjects.
+
+# If they are stored in a list, they can be easily averaged, for example,
+# for a grand average across subjects (or conditions).
+grand_average = mne.grand_average(all_evokeds)
+mne.write_evokeds('/tmp/tmp-ave.fif', all_evokeds)
+
+# If Evokeds objects are stored in a dictionary, they can be retrieved by name.
+all_evokeds = dict((cond, epochs[cond].average()) for cond in event_id)
+print(all_evokeds['left/auditory'])
+
+# Besides for explicit access, this can be used for example to set titles.
+for cond in all_evokeds:
+    all_evokeds[cond].plot_joint(title=cond)
diff --git a/tutorials/plot_epoching_and_averaging.py b/tutorials/plot_epoching_and_averaging.py
new file mode 100644
index 0000000..22cf3d4
--- /dev/null
+++ b/tutorials/plot_epoching_and_averaging.py
@@ -0,0 +1,158 @@
+"""
+.. _tut_epoching_and_averaging:
+
+Epoching and averaging (ERP/ERF)
+================================
+
+"""
+import os.path as op
+import numpy as np
+
+import mne
+###############################################################################
+# In MNE, `epochs` refers to a collection of `single trials` or short segments
+# of time locked raw data. If you haven't already, you might want to check out
+# :ref:`tut_epochs_objects`. In this tutorial we take a deeper look into
+# construction of epochs and averaging the epoch data to evoked instances.
+# First let's read in the raw sample data.
+data_path = mne.datasets.sample.data_path()
+fname = op.join(data_path, 'MEG', 'sample', 'sample_audvis_raw.fif')
+raw = mne.io.read_raw_fif(fname)
+
+###############################################################################
+# To create time locked epochs, we first need a set of events that contain the
+# information about the times. In this tutorial we use the stimulus channel to
+# define the events. Let's look at the raw data.
+order = np.arange(raw.info['nchan'])
+order[9] = 312  # We exchange the plotting order of two channels
+order[312] = 9  # to show the trigger channel as the 10th channel.
+raw.plot(n_channels=10, order=order, block=True)
+
+###############################################################################
+# Notice channel ``STI 014`` at the bottom. It is the trigger channel that
+# was used for combining all the events to a single channel. We can see that it
+# has several pulses of different amplitude throughout the recording. These
+# pulses correspond to different stimuli presented to the subject during the
+# acquisition. The pulses have values of 1, 2, 3, 4, 5 and 32. These are the
+# events we are going to align the epochs to. To create an event list from raw
+# data, we simply call a function dedicated just for that. Since the event list
+# is simply a numpy array, you can also manually create one. If you create one
+# from an outside source (like a separate file of events), pay special
+# attention in aligning the events correctly with the raw data.
+events = mne.find_events(raw)
+print(events)
+
+# Plot the events to get an idea of the paradigm
+# Specify colors and an event_id dictionary for the legend.
+event_id = {'Auditory/Left': 1, 'Auditory/Right': 2,
+            'Visual/Left': 3, 'Visual/Right': 4,
+            'smiley': 5, 'button': 32}
+color = {1: 'green', 2: 'yellow', 3: 'red', 4: 'c', 5: 'black', 32: 'blue'}
+
+mne.viz.plot_events(events, raw.info['sfreq'], raw.first_samp, color=color,
+                    event_id=event_id)
+
+###############################################################################
+# The event list contains three columns. The first column corresponds to
+# sample number. To convert this to seconds, you should divide the sample
+# number by the used sampling frequency. The second column is reserved for the
+# old value of the trigger channel at the time of transition, but is currently
+# not in use. The third column is the trigger id (amplitude of the pulse).
+#
+# You might wonder why the samples don't seem to align with the plotted data.
+# For instance, the first event has a sample number of 27977 which should
+# translate to roughly 46.6 seconds (27977 / 600). However looking at
+# the pulses we see the first pulse at 3.6 seconds. This is because Neuromag
+# recordings have an attribute ``first_samp`` which refers to the offset
+# between the system start and the start of the recording. Our data has a
+# ``first_samp`` equal to 25800. This means that the first sample you see with
+# ``raw.plot`` is the sample number 25800. Generally you don't need to worry
+# about this offset as it is taken into account with MNE functions, but it is
+# good to be aware of. Just to confirm, let's plot the events together with the
+# raw data. Notice how the vertical lines (events) align nicely with the pulses
+# on `STI 014`.
+raw.plot(events=events, n_channels=10, order=order)
+
+###############################################################################
+# In this tutorial we are only interested in triggers 1, 2, 3 and 4. These
+# triggers correspond to auditory and visual stimuli. The ``event_id`` here
+# can be an int, a list of ints or a dict. With dicts it is possible to assign
+# these ids to distinct categories. When using ints or lists this information
+# is lost. First we shall define some parameters to feed to the
+# :class:`mne.Epochs` constructor. The values ``tmin`` and ``tmax`` refer to
+# offsets in relation to the events. Here we make epochs that collect the data
+# from 200 ms before to 500 ms after the event.
+tmin, tmax = -0.2, 0.5
+event_id = {'Auditory/Left': 1, 'Auditory/Right': 2,
+            'Visual/Left': 3, 'Visual/Right': 4}
+# Only pick MEG and EOG channels.
+picks = mne.pick_types(raw.info, meg=True, eeg=False, eog=True)
+
+###############################################################################
+# Now we have everything we need to construct the epochs. To get some
+# meaningful results, we also want to baseline the epochs. Baselining computes
+# the mean over the baseline period and adjusts the data accordingly. The
+# epochs constructor uses a baseline period from ``tmin`` to 0.0 seconds by
+# default, but it is wise to be explicit. That way you are less likely to end
+# up with surprises along the way. ``None`` as the first element of the tuple
+# refers to the start of the time window (-200 ms in this case).
+# See :class:`mne.Epochs` for more.
+#
+# We also define rejection thresholds to get rid of noisy epochs. The
+# rejection thresholds are defined as peak-to-peak values within the epoch time
+# window. They are defined as T/m for gradiometers, T for magnetometers and V
+# for EEG and EOG electrodes.
+#
+# .. note:: In this tutorial, we don't preprocess the data. This is not
+#           something you would normally do. See our :ref:`tutorials` on
+#           preprocessing for more.
+baseline = (None, 0.0)
+reject = {'mag': 4e-12, 'eog': 200e-6}
+epochs = mne.Epochs(raw, events=events, event_id=event_id, tmin=tmin,
+                    tmax=tmax, reject=reject, picks=picks)
+
+###############################################################################
+# Let's plot the epochs to see the results. The number at the top refers to the
+# id number. We can see that 128 good epochs out of total of 145 events got
+# through the rejection process. Visual inspection also reveals that some
+# epochs containing saccades or blinks got through. You can also reject epochs
+# by hand by clicking on the epoch in the browser window. The selected epochs
+# get rejected when you close the epochs browser. How you should reject the
+# epochs and which thresholds to use is not a trivial question and this
+# tutorial takes no stand on that matter.
+#
+# To see all the interactive features of the epochs browser, click 'Help' in
+# the lower left corner of the browser window.
+epochs.plot(block=True)
+
+###############################################################################
+# To see why the epochs were rejected, we can plot the drop log.
+epochs.plot_drop_log()
+
+###############################################################################
+# To get the evoked response you can simply do ``epochs.average()``. It
+# includes only the data channels by default. For the sake of example, we use
+# picks to include the EOG channels as well. Notice that we cannot use the
+# same picks as before as the indices are different. 'Why are they different?'
+# you might ask. They're different because ``picks`` is simply a list of
+# channel indices and as the epochs were constructed, also a new info structure
+# is created where the channel indices run from 0 to ``epochs.info['nchan']``.
+# See :ref:`tut_info_objects` for more information.
+picks = mne.pick_types(epochs.info, meg=True, eog=True)
+evoked_left = epochs['Auditory/Left'].average(picks=picks)
+evoked_right = epochs['Auditory/Right'].average(picks=picks)
+
+###############################################################################
+# Notice we have used forward slashes ('/') to separate the factors of the
+# conditions of the experiment. We can use these 'tags' to select for example
+# all left trials (both visual left and auditory right) ...
+
+epochs_left = epochs['Left']
+
+# ... or to select a very specific subset. This is the same as above:
+evoked_left = epochs['Auditory', 'Left'].average(picks=picks)
+
+###############################################################################
+# Finally, let's plot the evoked responses.
+evoked_left.plot()
+evoked_right.plot()
diff --git a/tutorials/plot_epochs_to_data_frame.py b/tutorials/plot_epochs_to_data_frame.py
index 54d796d..0c8beca 100644
--- a/tutorials/plot_epochs_to_data_frame.py
+++ b/tutorials/plot_epochs_to_data_frame.py
@@ -94,19 +94,15 @@ pandas doc sites: http://pandas.pydata.org/pandas-docs/stable/
 import mne
 import matplotlib.pyplot as plt
 import numpy as np
-from mne.io import Raw
 from mne.datasets import sample
 
 print(__doc__)
 
-# turn on interactive mode
-plt.ion()
-
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
 
-raw = Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 
 # For simplicity we will only consider the first 10 epochs
 events = mne.read_events(event_fname)[:10]
@@ -220,6 +216,8 @@ final_df.rename(columns={0: sel[2]})  # as the index is oblivious of names.
 # The index is now written into regular columns so it can be used as factor.
 print(final_df)
 
+plt.show()
+
 # To save as csv file, uncomment the next line.
 # final_df.to_csv('my_epochs.csv')
 
diff --git a/tutorials/plot_forward.py b/tutorials/plot_forward.py
new file mode 100644
index 0000000..3f542b0
--- /dev/null
+++ b/tutorials/plot_forward.py
@@ -0,0 +1,181 @@
+"""
+.. _tut_forward:
+
+Head model and forward computation
+==================================
+
+The aim of this tutorial is to be a getting started for forward
+computation.
+
+For more extensive details and presentation of the general
+concepts for forward modeling. See :ref:`ch_forward`.
+
+"""
+
+import mne
+from mne.datasets import sample
+data_path = sample.data_path()
+
+# the raw file containing the channel location + types
+raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
+# The paths to freesurfer reconstructions
+subjects_dir = data_path + '/subjects'
+subject = 'sample'
+
+###############################################################################
+# Computing the forward operator
+# ------------------------------
+#
+# To compute a forward operator we need:
+#    - a -trans.fif file that contains the coregistration info.
+#    - a source space
+#    - the BEM surfaces
+
+###############################################################################
+# Compute and visualize BEM surfaces
+# ----------------------------------
+#
+# The BEM surfaces are the triangulations of the interfaces between different
+# tissues needed for forward computation. These surfaces are for example
+# the inner skull surface, the outer skull surface and the outer skill
+# surface.
+#
+# Computing the BEM surfaces requires FreeSurfer and makes use of either of
+# the two following command line tools:
+#
+#   - :ref:`gen_mne_watershed_bem`
+#   - :ref:`gen_mne_flash_bem`
+#
+# Here we'll assume it's already computed. It takes a few minutes per subject.
+#
+# For EEG we use 3 layers (inner skull, outer skull, and skin) while for
+# MEG 1 layer (inner skull) is enough.
+#
+# Let's look at these surfaces. The function :func:`mne.viz.plot_bem`
+# assumes that you have the the *bem* folder of your subject FreeSurfer
+# reconstruction the necessary files.
+
+mne.viz.plot_bem(subject=subject, subjects_dir=subjects_dir,
+                 orientation='coronal')
+
+###############################################################################
+# Visualization the coregistration
+# --------------------------------
+#
+# The coregistration is operation that allows to position the head and the
+# sensors in a common coordinate system. In the MNE software the transformation
+# to align the head and the sensors in stored in a so-called **trans file**.
+# It is a FIF file that ends with -trans.fif. It can be obtained with
+# mne_analyze (Unix tools), mne.gui.coregistration (in Python) or mrilab
+# if you're using a Neuromag system.
+#
+# For the Python version see func:`mne.gui.coregistration`
+#
+# Here we assume the coregistration is done, so we just visually check the
+# alignment with the following code.
+
+# The transformation file obtained by coregistration
+trans = data_path + '/MEG/sample/sample_audvis_raw-trans.fif'
+
+info = mne.io.read_info(raw_fname)
+mne.viz.plot_trans(info, trans, subject=subject, dig=True,
+                   meg_sensors=True, subjects_dir=subjects_dir)
+
+###############################################################################
+# Compute Source Space
+# --------------------
+#
+# The source space defines the position of the candidate source locations.
+# The following code compute such a cortical source space with
+# an OCT-6 resolution.
+#
+# See :ref:`setting_up_source_space` for details on source space definition
+# and spacing parameter.
+
+src = mne.setup_source_space(subject, spacing='oct6',
+                             subjects_dir=subjects_dir,
+                             add_dist=False, overwrite=True)
+print(src)
+
+###############################################################################
+# src contains two parts, one for the left hemisphere (4098 locations) and
+# one for the right hemisphere (4098 locations).
+#
+# Let's write a few lines of mayavi to see what it contains
+
+import numpy as np  # noqa
+from mayavi import mlab  # noqa
+from surfer import Brain  # noqa
+
+brain = Brain('sample', 'lh', 'inflated', subjects_dir=subjects_dir)
+surf = brain._geo
+
+vertidx = np.where(src[0]['inuse'])[0]
+
+mlab.points3d(surf.x[vertidx], surf.y[vertidx],
+              surf.z[vertidx], color=(1, 1, 0), scale_factor=1.5)
+
+###############################################################################
+# Compute forward solution
+# ------------------------
+#
+# We can now compute the forward solution.
+# To reduce computation we'll just compute a single layer BEM (just inner
+# skull) that can then be used for MEG (not EEG).
+#
+# We specify if we want a one-layer or a three-layer BEM using the
+# conductivity parameter.
+#
+# The BEM solution requires a BEM model which describes the geometry
+# of the head the conductivities of the different tissues.
+
+conductivity = (0.3,)  # for single layer
+# conductivity = (0.3, 0.006, 0.3)  # for three layers
+model = mne.make_bem_model(subject='sample', ico=4,
+                           conductivity=conductivity,
+                           subjects_dir=subjects_dir)
+bem = mne.make_bem_solution(model)
+
+###############################################################################
+# Note that the BEM does not involve any use of the trans file. The BEM
+# only depends on the head geometry and conductivities.
+# It is therefore independent from the MEG data and the head position.
+#
+# Let's now compute the forward operator, commonly referred to as the
+# gain or leadfield matrix.
+#
+# See :func:`mne.make_forward_solution` for details on parameters meaning.
+
+fwd = mne.make_forward_solution(raw_fname, trans=trans, src=src, bem=bem,
+                                fname=None, meg=True, eeg=False,
+                                mindist=5.0, n_jobs=2)
+print(fwd)
+
+###############################################################################
+# We can explore the content of fwd to access the numpy array that contains
+# the gain matrix.
+
+leadfield = fwd['sol']['data']
+print("Leadfield size : %d sensors x %d dipoles" % leadfield.shape)
+
+###############################################################################
+# To save to disk a forward solution you can use
+# :func:`mne.write_forward_solution` and to read it back from disk
+# :func:`mne.read_forward_solution`. Don't forget that FIF files containing
+# forward solution should end with *-fwd.fif*.
+
+###############################################################################
+# Exercise
+# --------
+#
+# By looking at :ref:`sphx_glr_auto_examples_forward_plot_read_forward.py`
+# plot the sensitivity maps for EEG and compare it with the MEG, can you
+# justify the claims that:
+#   - MEG is not sensitive to radial sources
+#   - EEG is more sensitive to deep sources
+#
+# How will the MEG sensitivity maps and histograms change if you use a free
+# instead if a fixed/surface oriented orientation?
+#
+# Try this changing the mode parameter in :func:`mne.sensitivity_map`
+# accordingly. Why don't we see any dipoles on the gyri?
diff --git a/tutorials/plot_ica_from_raw.py b/tutorials/plot_ica_from_raw.py
index bd8a8fe..1b61783 100644
--- a/tutorials/plot_ica_from_raw.py
+++ b/tutorials/plot_ica_from_raw.py
@@ -16,7 +16,6 @@ Subsequently, artifact detection and rejection quality are assessed.
 import numpy as np
 
 import mne
-from mne.io import Raw
 from mne.preprocessing import ICA
 from mne.preprocessing import create_ecg_epochs, create_eog_epochs
 from mne.datasets import sample
@@ -27,8 +26,8 @@ from mne.datasets import sample
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 
-raw = Raw(raw_fname, preload=True)
-raw.filter(1, 45, n_jobs=2)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
+raw.filter(1, 45, n_jobs=1)
 
 ###############################################################################
 # 1) Fit ICA model using the FastICA algorithm
@@ -105,4 +104,4 @@ ica.plot_overlay(raw)  # EOG artifacts remain
 # read_ica('my_ica.fif')
 
 # Apply the solution to Raw, Epochs or Evoked like this:
-# ica.apply(epochs, copy=False)
+# ica.apply(epochs)
diff --git a/tutorials/plot_info.py b/tutorials/plot_info.py
index 6fe6f93..e291b70 100644
--- a/tutorials/plot_info.py
+++ b/tutorials/plot_info.py
@@ -1,8 +1,8 @@
 """
 .. _tut_info_objects:
 
-The :class:`Info <mne.io.meas_info.Info>` data structure
-========================================================
+The :class:`Info <mne.Info>` data structure
+==============================================
 """
 
 from __future__ import print_function
@@ -11,7 +11,7 @@ import mne
 import os.path as op
 
 ###############################################################################
-# The :class:`Info <mne.io.meas_info.Info>` data object is typically created
+# The :class:`Info <mne.Info>` data object is typically created
 # when data is imported into MNE-Python and contains details such as:
 #
 #  - date, subject information, and other recording details
@@ -20,7 +20,7 @@ import os.path as op
 #  - digitized points
 #  - sensor–head coordinate transformation matrices
 #
-# and so forth. See the :class:`the API reference <mne.io.meas_info.Info>`
+# and so forth. See the :class:`the API reference <mne.Info>`
 # for a complete list of all data fields. Once created, this object is passed
 # around throughout the data analysis pipeline.
 #
@@ -31,34 +31,45 @@ info = mne.io.read_info(
     op.join(mne.datasets.sample.data_path(), 'MEG', 'sample',
             'sample_audvis_raw.fif'), verbose=False)
 
+###############################################################################
 # List all the fields in the info object
 print('Keys in info dictionary:\n', info.keys())
 
+###############################################################################
 # Obtain the sampling rate of the data
 print(info['sfreq'], 'Hz')
 
+###############################################################################
 # List all information about the first data channel
 print(info['chs'][0])
 
 ###############################################################################
+# .. _picking_channels:
+#
 # Obtaining subsets of channels
 # -----------------------------
 #
 # There are a number of convenience functions to obtain channel indices, given
-# an :class:`mne.io.meas_info.Info` object.
+# an :class:`mne.Info` object.
 
+###############################################################################
 # Get channel indices by name
 channel_indices = mne.pick_channels(info['ch_names'], ['MEG 0312', 'EEG 005'])
 
+###############################################################################
 # Get channel indices by regular expression
 channel_indices = mne.pick_channels_regexp(info['ch_names'], 'MEG *')
 
+###############################################################################
 # Get channel indices by type
 channel_indices = mne.pick_types(info, meg=True)  # MEG only
 channel_indices = mne.pick_types(info, eeg=True)  # EEG only
+
+###############################################################################
 # MEG gradiometers and EEG channels
 channel_indices = mne.pick_types(info, meg='grad', eeg=True)
 
+###############################################################################
 # Get a dictionary of channel indices, grouped by channel type
 channel_indices_by_type = mne.io.pick.channel_indices_by_type(info)
 print('The first three magnetometers:', channel_indices_by_type['mag'][:3])
@@ -71,6 +82,7 @@ print('The first three magnetometers:', channel_indices_by_type['mag'][:3])
 channel_type = mne.io.pick.channel_type(info, 75)
 print('Channel #75 is of type:', channel_type)
 
+###############################################################################
 # Channel types of a collection of channels
 meg_channels = mne.pick_types(info, meg=True)[:10]
 channel_types = [mne.io.pick.channel_type(info, ch) for ch in meg_channels]
diff --git a/tutorials/plot_introduction.py b/tutorials/plot_introduction.py
index c7b417d..641dcb4 100644
--- a/tutorials/plot_introduction.py
+++ b/tutorials/plot_introduction.py
@@ -7,11 +7,10 @@ Basic MEG and EEG data processing
 
 MNE-Python reimplements most of MNE-C's (the original MNE command line utils)
 functionality and offers transparent scripting.
-On top of that it extends MNE-C's functionality considerably (customize events,
-compute
-contrasts, group statistics, time-frequency analysis, EEG-sensor space analyses
-, etc.) It uses the same files as standard MNE unix commands:
-no need to convert your files to a new system or database.
+On top of that it extends MNE-C's functionality considerably
+(customize events, compute contrasts, group statistics, time-frequency
+analysis, EEG-sensor space analyses, etc.) It uses the same files as standard
+MNE unix commands: no need to convert your files to a new system or database.
 
 What you can do with MNE Python
 -------------------------------
@@ -20,7 +19,7 @@ What you can do with MNE Python
      *mne_browse_raw* for extended functionality (see :ref:`ch_browse`)
    - **Epoching**: Define epochs, baseline correction, handle conditions etc.
    - **Averaging** to get Evoked data
-   - **Compute SSP pojectors** to remove ECG and EOG artifacts
+   - **Compute SSP projectors** to remove ECG and EOG artifacts
    - **Compute ICA** to remove artifacts or select latent sources.
    - **Maxwell filtering** to remove environmental noise.
    - **Boundary Element Modeling**: single and three-layer BEM model
@@ -83,7 +82,7 @@ From raw data to evoked data
 Now, launch `ipython`_ (Advanced Python shell) using the QT backend which best
 supported across systems::
 
-  $ ipython --pylab -qt
+  $ ipython --matplotlib=qt
 
 First, load the mne package:
 """
@@ -104,7 +103,7 @@ mne.set_log_level('INFO')
 # You can set the default level by setting the environment variable
 # "MNE_LOGGING_LEVEL", or by having mne-python write preferences to a file:
 
-mne.set_config('MNE_LOGGING_LEVEL','WARNING')
+mne.set_config('MNE_LOGGING_LEVEL', 'WARNING')
 
 ##############################################################################
 # Note that the location of the mne-python preferences file (for easier manual
@@ -119,7 +118,7 @@ mne.get_config_path()
 # Access raw data
 # ^^^^^^^^^^^^^^^
 
-from mne.datasets import sample
+from mne.datasets import sample  # noqa
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 print(raw_fname)
@@ -130,7 +129,7 @@ print(raw_fname)
 #
 # Read data from file:
 
-raw = mne.io.Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 print(raw)
 print(raw.info)
 
@@ -209,7 +208,8 @@ grad_picks = mne.pick_types(raw.info, meg='grad', eog=True, exclude='bads')
 baseline = (None, 0)  # means from the first instant to t = 0
 
 ##############################################################################
-# Define peak-to-peak rejection parameters for gradiometers, magnetometers and EOG:
+# Define peak-to-peak rejection parameters for gradiometers, magnetometers
+# and EOG:
 
 reject = dict(grad=4000e-13, mag=4e-12, eog=150e-6)
 
@@ -233,7 +233,7 @@ print(epochs_data.shape)
 # Scipy supports read and write of matlab files. You can save your single
 # trials with:
 
-from scipy import io
+from scipy import io  # noqa
 io.savemat('epochs_data.mat', dict(epochs_data=epochs_data), oned_as='row')
 
 ##############################################################################
@@ -259,8 +259,8 @@ evoked.plot()
 #
 #   1. Extract the max value of each epoch
 
-max_in_each_epoch = [e.max() for e in epochs['aud_l']] # doctest:+ELLIPSIS
-print(max_in_each_epoch[:4]) # doctest:+ELLIPSIS
+max_in_each_epoch = [e.max() for e in epochs['aud_l']]  # doctest:+ELLIPSIS
+print(max_in_each_epoch[:4])  # doctest:+ELLIPSIS
 
 ##############################################################################
 # It is also possible to read evoked data stored in a fif file:
@@ -287,14 +287,14 @@ print(contrast)
 #
 # Define parameters:
 
-import numpy as np
+import numpy as np  # noqa
 n_cycles = 2  # number of cycles in Morlet wavelet
 freqs = np.arange(7, 30, 3)  # frequencies of interest
 
 ##############################################################################
 # Compute induced power and phase-locking values and plot gradiometers:
 
-from mne.time_frequency import tfr_morlet
+from mne.time_frequency import tfr_morlet  # noqa
 power, itc = tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles,
                         return_itc=True, decim=3, n_jobs=1)
 # power.plot()
@@ -305,7 +305,7 @@ power, itc = tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles,
 #
 # Import the required functions:
 
-from mne.minimum_norm import apply_inverse, read_inverse_operator
+from mne.minimum_norm import apply_inverse, read_inverse_operator  # noqa
 
 ##############################################################################
 # Read the inverse operator:
@@ -339,7 +339,7 @@ label = mne.read_label(fname_label)
 ##############################################################################
 # Compute inverse solution during the first 15s:
 
-from mne.minimum_norm import apply_inverse_raw
+from mne.minimum_norm import apply_inverse_raw  # noqa
 start, stop = raw.time_as_index([0, 15])  # read the first 15s of data
 stc = apply_inverse_raw(raw, inverse_operator, lambda2, method, label,
                         start, stop)
@@ -373,6 +373,6 @@ stc.save('mne_dSPM_raw_inverse_Aud')
 # Want to know more ?
 # ^^^^^^^^^^^^^^^^^^^
 #
-# Browse :ref:`examples-index` gallery.
+# Browse `the examples gallery <auto_examples/index.html>`_.
 
 print("Done!")
diff --git a/tutorials/plot_source_localization_basics.py b/tutorials/plot_mne_dspm_source_localization.py
similarity index 56%
rename from tutorials/plot_source_localization_basics.py
rename to tutorials/plot_mne_dspm_source_localization.py
index 8340ba6..e9bd702 100644
--- a/tutorials/plot_source_localization_basics.py
+++ b/tutorials/plot_mne_dspm_source_localization.py
@@ -1,28 +1,28 @@
 """
-.. _tut_inverse_basics:
+.. _tut_inverse_mne_dspm:
 
-Basics of source localization
-=============================
+Source localization with MNE/dSPM/sLORETA
+=========================================
 
-Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-         Denis Engemann <denis.engemann at gmail.com>
+The aim of this tutorials is to teach you how to compute and apply a linear
+inverse method such as MNE/dSPM/sLORETA on evoked/raw/epochs data.
 
 """
 import numpy as np
+import matplotlib.pyplot as plt
+
 import mne
 from mne.datasets import sample
 from mne.minimum_norm import (make_inverse_operator, apply_inverse,
                               write_inverse_operator)
 
-mne.set_log_level('WARNING')
-
-##############################################################################
+###############################################################################
 # Process MEG data
 
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 
-raw = mne.io.Raw(raw_fname)
+raw = mne.io.read_raw_fif(raw_fname)
 events = mne.find_events(raw, stim_channel='STI 014')
 
 event_id = dict(aud_r=1)  # event trigger and conditions
@@ -37,23 +37,31 @@ reject = dict(grad=4000e-13, mag=4e-12, eog=150e-6)
 epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
                     picks=picks, baseline=baseline, reject=reject)
 
-# compute regularized noise covariance
+###############################################################################
+# Compute regularized noise covariance
+# ------------------------------------
+#
+# For more details see :ref:`tut_compute_covariance`.
 
 noise_cov = mne.compute_covariance(
     epochs, tmax=0., method=['shrunk', 'empirical'])
 
 fig_cov, fig_spectra = mne.viz.plot_cov(noise_cov, raw.info)
 
-##############################################################################
+###############################################################################
 # Compute the evoked response
+# ---------------------------
 
 evoked = epochs.average()
 evoked.plot()
 evoked.plot_topomap(times=np.linspace(0.05, 0.15, 5), ch_type='mag')
 
+# Show whitening
+evoked.plot_white(noise_cov)
 
-##############################################################################
-# Inverse modeling: MNE and dSPM on evoked and raw data
+###############################################################################
+# Inverse modeling: MNE/dSPM on evoked and raw data
+# -------------------------------------------------
 
 # Read the forward solution and compute the inverse operator
 
@@ -63,7 +71,7 @@ fwd = mne.read_forward_solution(fname_fwd, surf_ori=True)
 # Restrict forward solution as necessary for MEG
 fwd = mne.pick_types_forward(fwd, meg=True, eeg=False)
 
-# make an M/EEG, MEG-only, and EEG-only inverse operators
+# make an MEG inverse operator
 info = evoked.info
 inverse_operator = make_inverse_operator(info, fwd, noise_cov,
                                          loose=0.2, depth=0.8)
@@ -71,7 +79,9 @@ inverse_operator = make_inverse_operator(info, fwd, noise_cov,
 write_inverse_operator('sample_audvis-meg-oct-6-inv.fif',
                        inverse_operator)
 
+###############################################################################
 # Compute inverse solution
+# ------------------------
 
 method = "dSPM"
 snr = 3.
@@ -79,20 +89,47 @@ lambda2 = 1. / snr ** 2
 stc = apply_inverse(evoked, inverse_operator, lambda2,
                     method=method, pick_ori=None)
 
+del fwd, inverse_operator, epochs  # to save memory
+
+###############################################################################
+# Visualization
+# -------------
+# View activation time-series
+
+plt.plot(1e3 * stc.times, stc.data[::100, :].T)
+plt.xlabel('time (ms)')
+plt.ylabel('%s value' % method)
+plt.show()
 
-# visualize
+###############################################################################
+# Here we use peak getter to move visualization to the time point of the peak
+# and draw a marker at the maximum peak vertex.
+
+vertno_max, time_idx = stc.get_peak(hemi='rh', time_as_index=True)
 
 subjects_dir = data_path + '/subjects'
 brain = stc.plot(surface='inflated', hemi='rh', subjects_dir=subjects_dir)
-brain.set_data_time_index(45)
+
+brain.set_data_time_index(time_idx)
+brain.add_foci(vertno_max, coords_as_verts=True, hemi='rh', color='blue',
+               scale_factor=0.6)
 brain.scale_data_colormap(fmin=8, fmid=12, fmax=15, transparent=True)
 brain.show_view('lateral')
 
-# morph data to average brain
+###############################################################################
+# Morph data to average brain
+# ---------------------------
+
 stc_fsaverage = stc.morph(subject_to='fsaverage', subjects_dir=subjects_dir)
 
 brain_fsaverage = stc_fsaverage.plot(surface='inflated', hemi='rh',
                                      subjects_dir=subjects_dir)
-brain_fsaverage.set_data_time_index(45)
+brain_fsaverage.set_data_time_index(time_idx)
 brain_fsaverage.scale_data_colormap(fmin=8, fmid=12, fmax=15, transparent=True)
 brain_fsaverage.show_view('lateral')
+
+###############################################################################
+# Exercise
+# --------
+#    - By changing the method parameter to 'sloreta' recompute the source
+#      estimates using the sLORETA method.
diff --git a/tutorials/plot_modifying_data_inplace.py b/tutorials/plot_modifying_data_inplace.py
index a556ebf..82a02d6 100644
--- a/tutorials/plot_modifying_data_inplace.py
+++ b/tutorials/plot_modifying_data_inplace.py
@@ -21,7 +21,7 @@ from matplotlib import pyplot as plt
 # Load an example dataset, the preload flag loads the data into memory now
 data_path = op.join(mne.datasets.sample.data_path(), 'MEG',
                     'sample', 'sample_audvis_raw.fif')
-raw = mne.io.RawFIF(data_path, preload=True, verbose=False)
+raw = mne.io.read_raw_fif(data_path, preload=True, verbose=False)
 raw = raw.crop(0, 2)
 print(raw)
 
diff --git a/tutorials/plot_epochs_objects.py b/tutorials/plot_object_epochs.py
similarity index 52%
rename from tutorials/plot_epochs_objects.py
rename to tutorials/plot_object_epochs.py
index 279300d..c73ccb3 100644
--- a/tutorials/plot_epochs_objects.py
+++ b/tutorials/plot_object_epochs.py
@@ -23,12 +23,13 @@ from matplotlib import pyplot as plt
 #  1. From a :class:`Raw <mne.io.RawFIF>` object, along with event times
 #  2. From an :class:`Epochs <mne.Epochs>` object that has been saved as a
 #     `.fif` file
-#  3. From scratch using :class:`EpochsArray <mne.EpochsArray>`
+#  3. From scratch using :class:`EpochsArray <mne.EpochsArray>`. See
+#     :ref:`tut_creating_data_structures`
 
+data_path = mne.datasets.sample.data_path()
 # Load a dataset that contains events
 raw = mne.io.RawFIF(
-    op.join(mne.datasets.sample.data_path(), 'MEG', 'sample',
-            'sample_audvis_raw.fif'))
+    op.join(data_path, 'MEG', 'sample', 'sample_audvis_raw.fif'))
 
 # If your raw object has a stim channel, you can construct an event array
 # easily
@@ -40,8 +41,11 @@ print('Number of events:', len(events))
 # Show all unique event codes (3rd column)
 print('Unique event codes:', np.unique(events[:, 2]))
 
-# Specify event codes of interest with descriptive labels
-event_id = dict(left=1, right=2)
+# Specify event codes of interest with descriptive labels.
+# This dataset also has visual left (3) and right (4) events, but
+# to save time and memory we'll just look at the auditory conditions
+# for now.
+event_id = {'Auditory/Left': 1, 'Auditory/Right': 2}
 
 ###############################################################################
 # Now, we can create an :class:`mne.Epochs` object with the events we've
@@ -50,10 +54,6 @@ event_id = dict(left=1, right=2)
 # :func:`get_data <mne.Epochs.get_data>`. Alternatively, you can use
 # `preload=True`.
 #
-# Note that there are many options available when loading an
-# :class:`mne.Epochs` object.  For more detailed information, see (**LINK TO
-# EPOCHS LOADING TUTORIAL**)
-
 # Expose the raw data as epochs, cut from -0.1 s to 1.0 s relative to the event
 # onsets
 epochs = mne.Epochs(raw, events, event_id, tmin=-0.1, tmax=1,
@@ -62,7 +62,7 @@ print(epochs)
 
 ###############################################################################
 # Epochs behave similarly to :class:`mne.io.Raw` objects. They have an
-# :class:`info <mne.io.meas_info.Info>` attribute that has all of the same
+# :class:`info <mne.Info>` attribute that has all of the same
 # information, as well as a number of attributes unique to the events contained
 # within the object.
 
@@ -74,7 +74,7 @@ print(epochs.events[:3], epochs.event_id, sep='\n\n')
 # `event_id` then you may index with strings instead.
 
 print(epochs[1:5])
-print(epochs['right'])
+print(epochs['Auditory/Right'])
 
 ###############################################################################
 # It is also possible to iterate through :class:`Epochs <mne.Epochs>` objects
@@ -90,14 +90,52 @@ for ep in epochs[:2]:
     print(ep)
 
 ###############################################################################
+# You can manually remove epochs from the Epochs object by using
+# :func:`epochs.drop(idx) <mne.Epochs.drop>`, or by using rejection or flat
+# thresholds with :func:`epochs.drop_bad(reject, flat) <mne.Epochs.drop_bad>`.
+# You can also inspect the reason why epochs were dropped by looking at the
+# list stored in ``epochs.drop_log`` or plot them with
+# :func:`epochs.plot_drop_log() <mne.Epochs.plot_drop_log>`. The indices
+# from the original set of events are stored in ``epochs.selection``.
+
+epochs.drop([0], reason='User reason')
+epochs.drop_bad(reject=dict(grad=2500e-13, mag=4e-12, eog=200e-6), flat=None)
+print(epochs.drop_log)
+epochs.plot_drop_log()
+print('Selection from original events:\n%s' % epochs.selection)
+print('Removed events (from numpy setdiff1d):\n%s'
+      % (np.setdiff1d(np.arange(len(events)), epochs.selection).tolist(),))
+print('Removed events (from list comprehension -- should match!):\n%s'
+      % ([li for li, log in enumerate(epochs.drop_log) if len(log) > 0]))
+
+###############################################################################
+# If you wish to save the epochs as a file, you can do it with
+# :func:`mne.Epochs.save`. To conform to MNE naming conventions, the
+# epochs file names should end with '-epo.fif'.
+epochs_fname = op.join(data_path, 'MEG', 'sample', 'sample-epo.fif')
+epochs.save(epochs_fname)
+
+###############################################################################
+# Later on you can read the epochs with :func:`mne.read_epochs`. For reading
+# EEGLAB epochs files see :func:`mne.read_epochs_eeglab`. We can also use
+# ``preload=False`` to save memory, loading the epochs from disk on demand.
+epochs = mne.read_epochs(epochs_fname, preload=False)
+
+###############################################################################
 # If you wish to look at the average across trial types, then you may do so,
-# creating an `Evoked` object in the process.
+# creating an :class:`Evoked <mne.Evoked>` object in the process. Instances
+# of `Evoked` are usually created by calling :func:`mne.Epochs.average`. For
+# creating `Evoked` from other data structures see :class:`mne.EvokedArray` and
+# :ref:`tut_creating_data_structures`.
 
-ev_left = epochs['left'].average()
-ev_right = epochs['right'].average()
+ev_left = epochs['Auditory/Left'].average()
+ev_right = epochs['Auditory/Right'].average()
 
 f, axs = plt.subplots(3, 2, figsize=(10, 5))
-_ = f.suptitle('Left / Right', fontsize=20)
+_ = f.suptitle('Left / Right auditory', fontsize=20)
 _ = ev_left.plot(axes=axs[:, 0], show=False)
 _ = ev_right.plot(axes=axs[:, 1], show=False)
 plt.tight_layout()
+
+###############################################################################
+# To export and manipulate Epochs using Pandas see :ref:`tut_io_export_pandas`.
diff --git a/tutorials/plot_object_evoked.py b/tutorials/plot_object_evoked.py
new file mode 100644
index 0000000..c66404d
--- /dev/null
+++ b/tutorials/plot_object_evoked.py
@@ -0,0 +1,74 @@
+"""
+.. _tut_evoked_objects:
+
+The :class:`Evoked <mne.Evoked>` data structure: evoked/averaged data
+=====================================================================
+"""
+import os.path as op
+
+import mne
+
+###############################################################################
+# The :class:`Evoked <mne.Evoked>` data structure is mainly used for storing
+# averaged data over trials. In MNE the evoked objects are created by averaging
+# epochs data with :func:`mne.Epochs.average`. Here we read the evoked dataset
+# from a file.
+data_path = mne.datasets.sample.data_path()
+fname = op.join(data_path, 'MEG', 'sample', 'sample_audvis-ave.fif')
+evokeds = mne.read_evokeds(fname, baseline=(None, 0), proj=True)
+print(evokeds)
+
+###############################################################################
+# Notice that the reader function returned a list of evoked instances. This is
+# because you can store multiple categories into a single file. Here we have
+# categories of
+# ``['Left Auditory', 'Right Auditory', 'Left Visual', 'Right Visual']``.
+# We can also use ``condition`` parameter to read in only one category.
+evoked = mne.read_evokeds(fname, condition='Left Auditory', baseline=(None, 0),
+                          proj=True)
+print(evoked)
+
+###############################################################################
+# If you're gone through the tutorials of raw and epochs datasets, you're
+# probably already familiar with the :class:`Info <mne.Info>` attribute.
+# There is nothing new or special with the ``evoked.info``. All the relevant
+# info is still there.
+print(evoked.info)
+print(evoked.times)
+
+###############################################################################
+# The evoked data structure also contains some new attributes easily
+# accessible:
+print(evoked.nave)  # Number of averaged epochs.
+print(evoked.first)  # First time sample.
+print(evoked.last)  # Last time sample.
+print(evoked.comment)  # Comment on dataset. Usually the condition.
+print(evoked.kind)  # Type of data, either average or standard_error.
+
+###############################################################################
+# The data is also easily accessible. Since the evoked data arrays are usually
+# much smaller than raw or epochs datasets, they are preloaded into the memory
+# when the evoked object is constructed. You can access the data as a numpy
+# array.
+data = evoked.data
+print(data.shape)
+
+###############################################################################
+# The data is arranged in an array of shape `(n_channels, n_times)`. Notice
+# that unlike epochs, evoked object does not support indexing. This means that
+# to access the data of a specific channel you must use the data array
+# directly.
+print('Data from channel {0}:'.format(evoked.ch_names[10]))
+print(data[10])
+
+###############################################################################
+# If you want to import evoked data from some other system and you have it in a
+# numpy array you can use :class:`mne.EvokedArray` for that. All you need is
+# the data and some info about the evoked data. For more information, see
+# :ref:`tut_creating_data_structures`.
+evoked = mne.EvokedArray(data, evoked.info, tmin=evoked.times[0])
+evoked.plot()
+
+###############################################################################
+# To write an evoked dataset to a file, use the :func:`mne.Evoked.save' method.
+# To save multiple categories to a single file, see :func:`mne.write_evokeds`.
diff --git a/tutorials/plot_raw_objects.py b/tutorials/plot_object_raw.py
similarity index 79%
rename from tutorials/plot_raw_objects.py
rename to tutorials/plot_object_raw.py
index 0a2284f..3e78a5f 100644
--- a/tutorials/plot_raw_objects.py
+++ b/tutorials/plot_object_raw.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
 """
-.. _tut_raw_objects
+.. _tut_raw_objects:
 
-The :class:`Raw <mne.io.RawFIF>` data structure: continuous data
-================================================================
+The :class:`Raw <mne.io.Raw>` data structure: continuous data
+=============================================================
 """
 
 from __future__ import print_function
@@ -14,12 +15,12 @@ from matplotlib import pyplot as plt
 ###############################################################################
 # Continuous data is stored in objects of type :class:`Raw <mne.io.RawFIF>`.
 # The core data structure is simply a 2D numpy array (channels × samples,
-# `._data`) combined with an :class:`Info <mne.io.meas_info.Info>` object
+# `._data`) combined with an :class:`Info <mne.Info>` object
 # (`.info`) (:ref:`tut_info_objects`.
 #
 # The most common way to load continuous data is from a .fif file. For more
-# information on :ref:`loading data from other formats <ch_raw>`, or creating
-# it :ref:`from scratch <tut_creating_data_structures>`.
+# information on :ref:`loading data from other formats <ch_convert>`, or
+# creating it :ref:`from scratch <tut_creating_data_structures>`.
 
 
 ###############################################################################
@@ -38,7 +39,7 @@ print('channels x samples:', raw._data.shape)
 
 ###############################################################################
 # Information about the channels contained in the :class:`Raw <mne.io.RawFIF>`
-# object is contained in the :class:`Info <mne.io.meas_info.Info>` attribute.
+# object is contained in the :class:`Info <mne.Info>` attribute.
 # This is essentially a dictionary with a number of relevant fields (see
 # :ref:`tut_info_objects`).
 
@@ -78,16 +79,16 @@ _ = plt.title('Sample channels')
 # channel names, types or time ranges.
 
 # Pull all MEG gradiometer channels:
-# Make sure to use copy==True or it will overwrite the data
-meg_only = raw.pick_types(meg=True, copy=True)
-eeg_only = raw.pick_types(meg=False, eeg=True, copy=True)
+# Make sure to use .copy() or it will overwrite the data
+meg_only = raw.copy().pick_types(meg=True)
+eeg_only = raw.copy().pick_types(meg=False, eeg=True)
 
 # The MEG flag in particular lets you specify a string for more specificity
-grad_only = raw.pick_types(meg='grad', copy=True)
+grad_only = raw.copy().pick_types(meg='grad')
 
 # Or you can use custom channel names
 pick_chans = ['MEG 0112', 'MEG 0111', 'MEG 0122', 'MEG 0123']
-specific_chans = raw.pick_channels(pick_chans, copy=True)
+specific_chans = raw.copy().pick_channels(pick_chans)
 print(meg_only, eeg_only, grad_only, specific_chans, sep='\n')
 
 ###############################################################################
@@ -98,20 +99,18 @@ eeg, times = eeg_only[0, :int(sfreq * 2)]
 meg, times = meg_only[0, :int(sfreq * 2)]
 a1.plot(times, meg[0])
 a2.plot(times, eeg[0])
+del eeg, meg, meg_only, grad_only, eeg_only, data, specific_chans
 
 ###############################################################################
 # You can restrict the data to a specific time range
-
-restricted = raw.crop(5, 7)  # in seconds
-print('New time range from', restricted.times.min(), 's to',
-      restricted.times.max(), 's')
+raw = raw.crop(0, 50)  # in seconds
+print('New time range from', raw.times.min(), 's to', raw.times.max(), 's')
 
 ###############################################################################
 # And drop channels by name
-
-restricted = restricted.drop_channels(['MEG 0241', 'EEG 001'])
-print('Number of channels reduced from', raw.info['nchan'], 'to',
-      restricted.info['nchan'])
+nchan = raw.info['nchan']
+raw = raw.drop_channels(['MEG 0241', 'EEG 001'])
+print('Number of channels reduced from', nchan, 'to', raw.info['nchan'])
 
 ###############################################################################
 # --------------------------------------------------
@@ -121,12 +120,12 @@ print('Number of channels reduced from', raw.info['nchan'], 'to',
 # :class:`Raw <mne.io.RawFIF>` objects can be concatenated in time by using the
 # :func:`append <mne.io.RawFIF.append>` function. For this to work, they must
 # have the same number of channels and their :class:`Info
-# <mne.io.meas_info.Info>` structures should be compatible.
+# <mne.Info>` structures should be compatible.
 
 # Create multiple :class:`Raw <mne.io.RawFIF>` objects
 raw1 = raw.copy().crop(0, 10)
 raw2 = raw.copy().crop(10, 20)
-raw3 = raw.copy().crop(20, 100)
+raw3 = raw.copy().crop(20, 40)
 
 # Concatenate in time (also works without preloading)
 raw1.append([raw2, raw3])
diff --git a/tutorials/plot_python_intro.py b/tutorials/plot_python_intro.py
new file mode 100644
index 0000000..c3c9ee8
--- /dev/null
+++ b/tutorials/plot_python_intro.py
@@ -0,0 +1,46 @@
+"""
+.. _tut_intro_pyton:
+
+Introduction to Python
+======================
+"""
+
+###############################################################################
+# Python is a modern, general-purpose, object-oriented, high-level programming
+# language. First make sure you have a working python environment and
+# dependencies (see :ref:`install_python_and_mne_python`). If you are
+# completely new to python, don't worry, it's just like any other programming
+# language, only easier. Here are a few great resources to get you started:
+#
+# * `SciPy lectures <http://scipy-lectures.github.io>`_
+# * `Learn X in Y minutes: Python <https://learnxinyminutes.com/docs/python/>`_
+# * `NumPy for MATLAB users <https://docs.scipy.org/doc/numpy-dev/user/numpy-for-matlab-users.html>`_  # noqa
+#
+# We highly recommend watching the Scipy videos and reading through these
+# sites to get a sense of how scientific computing is done in Python.
+#
+# Here are few bulletin points to familiarise yourself with python:
+#
+# Everything is dynamically typed. No need to declare simple data
+# structures or variables separately.
+a = 3
+print(type(a))
+b = [1, 2.5, 'This is a string']
+print(type(b))
+c = 'Hello world!'
+print(type(c))
+
+###############################################################################
+# If you come from a background of matlab, remember that indexing in python
+# starts from zero:
+a = [1, 2, 3, 4]
+print('This is the zeroth value in the list: {}'.format(a[0]))
+
+###############################################################################
+# No need to reinvent the wheel. Scipy and Numpy are battle field tested
+# libraries that have a vast variety of functions for your needs. Consult the
+# documentation and remember, you can always ask the interpreter for help with
+# a question mark at the end of a function::
+#
+#    >>> import numpy as np
+#    >>> np.arange?
diff --git a/examples/decoding/plot_decoding_sensors.py b/tutorials/plot_sensors_decoding.py
similarity index 53%
rename from examples/decoding/plot_decoding_sensors.py
rename to tutorials/plot_sensors_decoding.py
index 984e3a2..aa79097 100644
--- a/examples/decoding/plot_decoding_sensors.py
+++ b/tutorials/plot_sensors_decoding.py
@@ -7,19 +7,15 @@ Decoding, a.k.a MVPA or supervised machine learning applied to MEG
 data in sensor space. Here the classifier is applied to every time
 point.
 """
-# Authors: Alexandre Gramfort <alexandre.gramfort at telecom-paristech.fr>
-#          Jean-Remi King <jeanremi.king at gmail.com>
-#
-# License: BSD (3-clause)
-
+import numpy as np
 import matplotlib.pyplot as plt
 
+from sklearn.metrics import roc_auc_score
+from sklearn.cross_validation import StratifiedKFold
+
 import mne
-from mne import io
 from mne.datasets import sample
-from mne.decoding import TimeDecoding
-
-print(__doc__)
+from mne.decoding import TimeDecoding, GeneralizationAcrossTime
 
 data_path = sample.data_path()
 
@@ -33,7 +29,7 @@ tmin, tmax = -0.2, 0.5
 event_id = dict(aud_l=1, vis_l=3)
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 raw.filter(2, None, method='iir')  # replace baselining with high-pass
 events = mne.read_events(event_fname)
 
@@ -52,11 +48,53 @@ mne.epochs.equalize_epoch_counts(epochs_list)
 data_picks = mne.pick_types(epochs.info, meg=True, exclude='bads')
 
 ###############################################################################
-# Setup decoding: default is linear SVC
+# Temporal decoding
+# -----------------
+#
+# We'll use the default classifer for a binary classification problem
+# which is a linear Support Vector Machine (SVM).
+
 td = TimeDecoding(predict_mode='cross-validation', n_jobs=1)
+
 # Fit
 td.fit(epochs)
+
 # Compute accuracy
 td.score(epochs)
+
 # Plot scores across time
 td.plot(title='Sensor space decoding')
+
+###############################################################################
+# Generalization Across Time
+# --------------------------
+#
+# Here we'll use a stratified cross-validation scheme.
+
+# make response vector
+y = np.zeros(len(epochs.events), dtype=int)
+y[epochs.events[:, 2] == 3] = 1
+cv = StratifiedKFold(y=y)  # do a stratified cross-validation
+
+# define the GeneralizationAcrossTime object
+gat = GeneralizationAcrossTime(predict_mode='cross-validation', n_jobs=1,
+                               cv=cv, scorer=roc_auc_score)
+
+# fit and score
+gat.fit(epochs, y=y)
+gat.score(epochs)
+
+# let's visualize now
+gat.plot()
+gat.plot_diagonal()
+
+###############################################################################
+# Exercise
+# --------
+#  - Can you improve the performance using full epochs and a common spatial
+#    pattern (CSP) used by most BCI systems?
+#  - Explore other datasets from MNE (e.g. Face dataset from SPM to predict
+#    Face vs. Scrambled)
+#
+# Have a look at the example
+# :ref:`sphx_glr_auto_examples_decoding_plot_decoding_csp_space.py`
diff --git a/tutorials/plot_sensors_time_frequency.py b/tutorials/plot_sensors_time_frequency.py
new file mode 100644
index 0000000..d685257
--- /dev/null
+++ b/tutorials/plot_sensors_time_frequency.py
@@ -0,0 +1,133 @@
+"""
+
+.. _tut_sensors_time_frequency:
+
+=============================================
+Frequency and time-frequency sensors analysis
+=============================================
+
+The objective is to show you how to explore the spectral content
+of your data (frequency and time-frequency). Here we'll work on Epochs.
+
+We will use the somatosensory dataset that contains so
+called event related synchronizations (ERS) / desynchronizations (ERD) in
+the beta band.
+"""
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+import mne
+from mne.time_frequency import tfr_morlet, psd_multitaper
+from mne.datasets import somato
+
+###############################################################################
+# Set parameters
+data_path = somato.data_path()
+raw_fname = data_path + '/MEG/somato/sef_raw_sss.fif'
+
+# Setup for reading the raw data
+raw = mne.io.read_raw_fif(raw_fname)
+events = mne.find_events(raw, stim_channel='STI 014')
+
+# picks MEG gradiometers
+picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True, stim=False)
+
+# Construct Epochs
+event_id, tmin, tmax = 1, -1., 3.
+baseline = (None, 0)
+epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
+                    baseline=baseline, reject=dict(grad=4000e-13, eog=350e-6),
+                    preload=True)
+
+epochs.resample(150., npad='auto')  # resample to reduce computation time
+
+###############################################################################
+# Frequency analysis
+# ------------------
+#
+# We start by exploring the frequence content of our epochs.
+
+
+###############################################################################
+# Let's first check out all channel types by averaging across epochs.
+epochs.plot_psd(fmin=2., fmax=40.)
+
+###############################################################################
+# Now let's take a look at the spatial distributions of the PSD.
+epochs.plot_psd_topomap(ch_type='grad', normalize=True)
+
+###############################################################################
+# Alternatively, you can also create PSDs from Epochs objects with functions
+# that start with psd_ such as
+# :func:`mne.time_frequency.psd_multitaper` and
+# :func:`mne.time_frequency.psd_welch`.
+
+f, ax = plt.subplots()
+psds, freqs = psd_multitaper(epochs, fmin=2, fmax=40, n_jobs=1)
+psds = 10 * np.log10(psds)
+psds_mean = psds.mean(0).mean(0)
+psds_std = psds.mean(0).std(0)
+
+ax.plot(freqs, psds_mean, color='k')
+ax.fill_between(freqs, psds_mean - psds_std, psds_mean + psds_std,
+                color='k', alpha=.5)
+ax.set(title='Multitaper PSD (gradiometers)', xlabel='Frequency',
+       ylabel='Power Spectral Density (dB)')
+plt.show()
+
+###############################################################################
+# Time-frequency analysis: power and intertrial coherence
+# -------------------------------------------------------
+#
+# We now compute time-frequency representations (TFRs) from our Epochs.
+# We'll look at power and intertrial coherence (ITC).
+#
+# To this we'll use the function :func:`mne.time_frequency.tfr_morlet`
+# but you can also use :func:`mne.time_frequency.tfr_multitaper`
+# or :func:`mne.time_frequency.tfr_stockwell`.
+
+freqs = np.arange(6, 30, 3)  # define frequencies of interest
+n_cycles = freqs / 2.  # different number of cycle per frequency
+power, itc = tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles, use_fft=True,
+                        return_itc=True, decim=3, n_jobs=1)
+
+###############################################################################
+# Inspect power
+# -------------
+#
+# .. note::
+#     The generated figures are interactive. In the topo you can click
+#     on an image to visualize the data for one censor.
+#     You can also select a portion in the time-frequency plane to
+#     obtain a topomap for a certain time-frequency region.
+power.plot_topo(baseline=(-0.5, 0), mode='logratio', title='Average power')
+power.plot([82], baseline=(-0.5, 0), mode='logratio')
+
+fig, axis = plt.subplots(1, 2, figsize=(7, 4))
+power.plot_topomap(ch_type='grad', tmin=0.5, tmax=1.5, fmin=8, fmax=12,
+                   baseline=(-0.5, 0), mode='logratio', axes=axis[0],
+                   title='Alpha', vmax=0.45, show=False)
+power.plot_topomap(ch_type='grad', tmin=0.5, tmax=1.5, fmin=13, fmax=25,
+                   baseline=(-0.5, 0), mode='logratio', axes=axis[1],
+                   title='Beta', vmax=0.45, show=False)
+mne.viz.tight_layout()
+plt.show()
+
+###############################################################################
+# Inspect ITC
+# -----------
+itc.plot_topo(title='Inter-Trial coherence', vmin=0., vmax=1., cmap='Reds')
+
+###############################################################################
+# .. note::
+#     Baseline correction can be applied to power or done in plots
+#     To illustrate the baseline correction in plots the next line is
+#     commented power.apply_baseline(baseline=(-0.5, 0), mode='logratio')
+
+###############################################################################
+# Exercise
+# --------
+#
+#    - Visualize the intertrial coherence values as topomaps as done with
+#      power.
diff --git a/tutorials/plot_cluster_1samp_test_time_frequency.py b/tutorials/plot_stats_cluster_1samp_test_time_frequency.py
similarity index 97%
rename from tutorials/plot_cluster_1samp_test_time_frequency.py
rename to tutorials/plot_stats_cluster_1samp_test_time_frequency.py
index 638657d..b7c0e75 100644
--- a/tutorials/plot_cluster_1samp_test_time_frequency.py
+++ b/tutorials/plot_stats_cluster_1samp_test_time_frequency.py
@@ -35,6 +35,7 @@ print(__doc__)
 
 ###############################################################################
 # Set parameters
+# --------------
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
 event_id = 1
@@ -42,7 +43,7 @@ tmin = -0.3
 tmax = 0.6
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.find_events(raw, stim_channel='STI 014')
 
 include = []
@@ -99,6 +100,7 @@ epochs_power = np.log10(epochs_power)  # take log of ratio
 
 ###############################################################################
 # Compute statistic
+# -----------------
 threshold = 2.5
 T_obs, clusters, cluster_p_values, H0 = \
     permutation_cluster_1samp_test(epochs_power, n_permutations=100,
@@ -106,6 +108,7 @@ T_obs, clusters, cluster_p_values, H0 = \
 
 ###############################################################################
 # View time-frequency plots
+# -------------------------
 plt.clf()
 plt.subplots_adjust(0.12, 0.08, 0.96, 0.94, 0.2, 0.43)
 plt.subplot(2, 1, 1)
diff --git a/tutorials/plot_cluster_methods_tutorial.py b/tutorials/plot_stats_cluster_methods.py
similarity index 90%
rename from tutorials/plot_cluster_methods_tutorial.py
rename to tutorials/plot_stats_cluster_methods.py
index 3054ad9..03d5b5c 100644
--- a/tutorials/plot_cluster_methods_tutorial.py
+++ b/tutorials/plot_stats_cluster_methods.py
@@ -73,6 +73,7 @@ print(__doc__)
 
 ###############################################################################
 # Set parameters
+# --------------
 width = 40
 n_subjects = 10
 signal_mean = 100
@@ -86,7 +87,9 @@ n_permutations = 1024  # number of clustering permutations (1024 for exact)
 
 ###############################################################################
 # Construct simulated data
-#    Make the connectivity matrix just next-neighbor spatially
+# ------------------------
+#
+# Make the connectivity matrix just next-neighbor spatially
 n_src = width * width
 connectivity = grid_to_graph(width, width)
 
@@ -106,16 +109,21 @@ for si in range(X.shape[0]):
 
 ###############################################################################
 # Do some statistics
-
-#    Note that X needs to be a multi-dimensional array of shape
-#    samples (subjects) x time x space, so we permute dimensions
+# ------------------
+#
+# .. note::
+#     X needs to be a multi-dimensional array of shape
+#     samples (subjects) x time x space, so we permute dimensions:
 X = X.reshape((n_subjects, 1, n_src))
 
-#    Now let's do some clustering using the standard method. Note that not
-#    specifying a connectivity matrix implies grid-like connectivity, which
-#    we want here:
+###############################################################################
+# Now let's do some clustering using the standard method.
+#
+# .. note::
+#     Not specifying a connectivity matrix implies grid-like connectivity,
+#     which we want here:
 T_obs, clusters, p_values, H0 = \
-    spatio_temporal_cluster_1samp_test(X, n_jobs=2, threshold=threshold,
+    spatio_temporal_cluster_1samp_test(X, n_jobs=1, threshold=threshold,
                                        connectivity=connectivity,
                                        tail=1, n_permutations=n_permutations)
 
@@ -133,7 +141,7 @@ p_bon = -np.log10(bonferroni_correction(p)[1])
 #    Now let's do some clustering using the standard method with "hat":
 stat_fun = partial(ttest_1samp_no_p, sigma=sigma)
 T_obs_hat, clusters, p_values, H0 = \
-    spatio_temporal_cluster_1samp_test(X, n_jobs=2, threshold=threshold,
+    spatio_temporal_cluster_1samp_test(X, n_jobs=1, threshold=threshold,
                                        connectivity=connectivity,
                                        tail=1, n_permutations=n_permutations,
                                        stat_fun=stat_fun)
@@ -147,7 +155,7 @@ T_obs_hat = T_obs_hat.reshape((width, width))
 
 #    Now the threshold-free cluster enhancement method (TFCE):
 T_obs_tfce, clusters, p_values, H0 = \
-    spatio_temporal_cluster_1samp_test(X, n_jobs=2, threshold=threshold_tfce,
+    spatio_temporal_cluster_1samp_test(X, n_jobs=1, threshold=threshold_tfce,
                                        connectivity=connectivity,
                                        tail=1, n_permutations=n_permutations)
 T_obs_tfce = T_obs_tfce.reshape((width, width))
@@ -155,7 +163,7 @@ ps_tfce = -np.log10(p_values.reshape((width, width)))
 
 #    Now the TFCE with "hat" variance correction:
 T_obs_tfce_hat, clusters, p_values, H0 = \
-    spatio_temporal_cluster_1samp_test(X, n_jobs=2, threshold=threshold_tfce,
+    spatio_temporal_cluster_1samp_test(X, n_jobs=1, threshold=threshold_tfce,
                                        connectivity=connectivity,
                                        tail=1, n_permutations=n_permutations,
                                        stat_fun=stat_fun)
@@ -164,8 +172,7 @@ ps_tfce_hat = -np.log10(p_values.reshape((width, width)))
 
 ###############################################################################
 # Visualize results
-
-plt.ion()
+# -----------------
 fig = plt.figure(facecolor='w')
 
 x, y = np.mgrid[0:width, 0:width]
@@ -200,3 +207,5 @@ for ax in axs:
     cbar.set_label('-log10(p)')
     cbar.set_ticks(p_lims)
     cbar.set_ticklabels(['%0.1f' % p for p in p_lims])
+
+plt.show()
diff --git a/tutorials/plot_cluster_stats_spatio_temporal.py b/tutorials/plot_stats_cluster_spatio_temporal.py
similarity index 78%
rename from tutorials/plot_cluster_stats_spatio_temporal.py
rename to tutorials/plot_stats_cluster_spatio_temporal.py
index 96172bd..f56fdbb 100644
--- a/tutorials/plot_cluster_stats_spatio_temporal.py
+++ b/tutorials/plot_stats_cluster_spatio_temporal.py
@@ -34,6 +34,7 @@ print(__doc__)
 
 ###############################################################################
 # Set parameters
+# --------------
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
@@ -43,11 +44,12 @@ tmin = -0.2
 tmax = 0.3  # Use a lower tmax to reduce multiple comparisons
 
 #   Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 ###############################################################################
 # Read epochs for all channels, removing a bad one
+# ------------------------------------------------
 raw.info['bads'] += ['MEG 2443']
 picks = mne.pick_types(raw.info, meg=True, eog=True, exclude='bads')
 event_id = 1  # L auditory
@@ -65,6 +67,7 @@ equalize_epoch_counts([epochs1, epochs2])
 
 ###############################################################################
 # Transform to source space
+# -------------------------
 
 fname_inv = data_path + '/MEG/sample/sample_audvis-meg-oct-6-meg-inv.fif'
 snr = 3.0
@@ -75,10 +78,10 @@ sample_vertices = [s['vertno'] for s in inverse_operator['src']]
 
 #    Let's average and compute inverse, resampling to speed things up
 evoked1 = epochs1.average()
-evoked1.resample(50)
+evoked1.resample(50, npad='auto')
 condition1 = apply_inverse(evoked1, inverse_operator, lambda2, method)
 evoked2 = epochs2.average()
-evoked2.resample(50)
+evoked2.resample(50, npad='auto')
 condition2 = apply_inverse(evoked2, inverse_operator, lambda2, method)
 
 #    Let's only deal with t > 0, cropping to reduce multiple comparisons
@@ -89,13 +92,17 @@ tstep = condition1.tstep
 
 ###############################################################################
 # Transform to common cortical space
-
-#    Normally you would read in estimates across several subjects and morph
-#    them to the same cortical space (e.g. fsaverage). For example purposes,
-#    we will simulate this by just having each "subject" have the same
-#    response (just noisy in source space) here. Note that for 7 subjects
-#    with a two-sided statistical test, the minimum significance under a
-#    permutation test is only p = 1/(2 ** 6) = 0.015, which is large.
+# ----------------------------------
+#
+# Normally you would read in estimates across several subjects and morph
+# them to the same cortical space (e.g. fsaverage). For example purposes,
+# we will simulate this by just having each "subject" have the same
+# response (just noisy in source space) here.
+#
+# .. note::
+#     Note that for 7 subjects with a two-sided statistical test, the minimum
+#     significance under a permutation test is only p = 1/(2 ** 6) = 0.015,
+#     which is large.
 n_vertices_sample, n_times = condition1.data.shape
 n_subjects = 7
 print('Simulating data for %d subjects.' % n_subjects)
@@ -106,12 +113,13 @@ X = randn(n_vertices_sample, n_times, n_subjects, 2) * 10
 X[:, :, :, 0] += condition1.data[:, :, np.newaxis]
 X[:, :, :, 1] += condition2.data[:, :, np.newaxis]
 
-#    It's a good idea to spatially smooth the data, and for visualization
-#    purposes, let's morph these to fsaverage, which is a grade 5 source space
-#    with vertices 0:10242 for each hemisphere. Usually you'd have to morph
-#    each subject's data separately (and you might want to use morph_data
-#    instead), but here since all estimates are on 'sample' we can use one
-#    morph matrix for all the heavy lifting.
+###############################################################################
+# It's a good idea to spatially smooth the data, and for visualization
+# purposes, let's morph these to fsaverage, which is a grade 5 source space
+# with vertices 0:10242 for each hemisphere. Usually you'd have to morph
+# each subject's data separately (and you might want to use morph_data
+# instead), but here since all estimates are on 'sample' we can use one
+# morph matrix for all the heavy lifting.
 fsave_vertices = [np.arange(10242), np.arange(10242)]
 morph_mat = compute_morph_matrix('sample', 'fsaverage', sample_vertices,
                                  fsave_vertices, 20, subjects_dir)
@@ -123,18 +131,20 @@ print('Morphing data.')
 X = morph_mat.dot(X)  # morph_mat is a sparse matrix
 X = X.reshape(n_vertices_fsave, n_times, n_subjects, 2)
 
-#    Finally, we want to compare the overall activity levels in each condition,
-#    the diff is taken along the last axis (condition). The negative sign makes
-#    it so condition1 > condition2 shows up as "red blobs" (instead of blue).
+###############################################################################
+# Finally, we want to compare the overall activity levels in each condition,
+# the diff is taken along the last axis (condition). The negative sign makes
+# it so condition1 > condition2 shows up as "red blobs" (instead of blue).
 X = np.abs(X)  # only magnitude
 X = X[:, :, :, 0] - X[:, :, :, 1]  # make paired contrast
 
 
 ###############################################################################
 # Compute statistic
-
-#    To use an algorithm optimized for spatio-temporal clustering, we
-#    just pass the spatial connectivity matrix (instead of spatio-temporal)
+# -----------------
+#
+# To use an algorithm optimized for spatio-temporal clustering, we
+# just pass the spatial connectivity matrix (instead of spatio-temporal)
 print('Computing connectivity.')
 connectivity = spatial_tris_connectivity(grade_to_tris(5))
 
@@ -148,7 +158,7 @@ p_threshold = 0.001
 t_threshold = -stats.distributions.t.ppf(p_threshold / 2., n_subjects - 1)
 print('Clustering.')
 T_obs, clusters, cluster_p_values, H0 = clu = \
-    spatio_temporal_cluster_1samp_test(X, connectivity=connectivity, n_jobs=2,
+    spatio_temporal_cluster_1samp_test(X, connectivity=connectivity, n_jobs=1,
                                        threshold=t_threshold)
 #    Now select the clusters that are sig. at p < 0.05 (note that this value
 #    is multiple-comparisons corrected).
@@ -156,7 +166,7 @@ good_cluster_inds = np.where(cluster_p_values < 0.05)[0]
 
 ###############################################################################
 # Visualize the clusters
-
+# ----------------------
 print('Visualizing clusters.')
 
 #    Now let's build a convenient representation of each cluster, where each
diff --git a/tutorials/plot_cluster_stats_spatio_temporal_2samp.py b/tutorials/plot_stats_cluster_spatio_temporal_2samp.py
similarity index 94%
rename from tutorials/plot_cluster_stats_spatio_temporal_2samp.py
rename to tutorials/plot_stats_cluster_spatio_temporal_2samp.py
index a1bf4ee..959faca 100644
--- a/tutorials/plot_cluster_stats_spatio_temporal_2samp.py
+++ b/tutorials/plot_stats_cluster_spatio_temporal_2samp.py
@@ -28,13 +28,14 @@ print(__doc__)
 
 ###############################################################################
 # Set parameters
+# --------------
 data_path = sample.data_path()
 stc_fname = data_path + '/MEG/sample/sample_audvis-meg-lh.stc'
 subjects_dir = data_path + '/subjects'
 
 # Load stc to in common cortical space (fsaverage)
 stc = mne.read_source_estimate(stc_fname)
-stc.resample(50)
+stc.resample(50, npad='auto')
 
 stc = mne.morph_data('sample', 'fsaverage', stc, grade=5, smooth=20,
                      subjects_dir=subjects_dir)
@@ -58,9 +59,10 @@ X2 = np.abs(X2)  # only magnitude
 
 ###############################################################################
 # Compute statistic
-
-#    To use an algorithm optimized for spatio-temporal clustering, we
-#    just pass the spatial connectivity matrix (instead of spatio-temporal)
+# -----------------
+#
+# To use an algorithm optimized for spatio-temporal clustering, we
+# just pass the spatial connectivity matrix (instead of spatio-temporal)
 print('Computing connectivity.')
 connectivity = spatial_tris_connectivity(grade_to_tris(5))
 
@@ -77,7 +79,7 @@ f_threshold = stats.distributions.f.ppf(1. - p_threshold / 2.,
                                         n_subjects1 - 1, n_subjects2 - 1)
 print('Clustering.')
 T_obs, clusters, cluster_p_values, H0 = clu =\
-    spatio_temporal_cluster_test(X, connectivity=connectivity, n_jobs=2,
+    spatio_temporal_cluster_test(X, connectivity=connectivity, n_jobs=1,
                                  threshold=f_threshold)
 #    Now select the clusters that are sig. at p < 0.05 (note that this value
 #    is multiple-comparisons corrected).
@@ -85,6 +87,7 @@ good_cluster_inds = np.where(cluster_p_values < 0.05)[0]
 
 ###############################################################################
 # Visualize the clusters
+# ----------------------
 
 print('Visualizing clusters.')
 
diff --git a/tutorials/plot_cluster_stats_spatio_temporal_repeated_measures_anova.py b/tutorials/plot_stats_cluster_spatio_temporal_repeated_measures_anova.py
similarity index 79%
rename from tutorials/plot_cluster_stats_spatio_temporal_repeated_measures_anova.py
rename to tutorials/plot_stats_cluster_spatio_temporal_repeated_measures_anova.py
index 1a5ecf1..ef899f7 100644
--- a/tutorials/plot_cluster_stats_spatio_temporal_repeated_measures_anova.py
+++ b/tutorials/plot_stats_cluster_spatio_temporal_repeated_measures_anova.py
@@ -39,6 +39,7 @@ print(__doc__)
 
 ###############################################################################
 # Set parameters
+# --------------
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
@@ -48,11 +49,12 @@ tmin = -0.2
 tmax = 0.3  # Use a lower tmax to reduce multiple comparisons
 
 #   Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 ###############################################################################
 # Read epochs for all channels, removing a bad one
+# ------------------------------------------------
 raw.info['bads'] += ['MEG 2443']
 picks = mne.pick_types(raw.info, meg=True, eog=True, exclude='bads')
 # we'll load all four conditions that make up the 'two ways' of our ANOVA
@@ -68,7 +70,7 @@ epochs.equalize_event_counts(event_id, copy=False)
 
 ###############################################################################
 # Transform to source space
-
+# -------------------------
 fname_inv = data_path + '/MEG/sample/sample_audvis-meg-oct-6-meg-inv.fif'
 snr = 3.0
 lambda2 = 1.0 / snr ** 2
@@ -83,7 +85,7 @@ sample_vertices = [inverse_operator['src'][0]['vertno'], np.array([], int)]
 conditions = []
 for cond in ['l_aud', 'r_aud', 'l_vis', 'r_vis']:  # order is important
     evoked = epochs[cond].average()
-    evoked.resample(50)
+    evoked.resample(50, npad='auto')
     condition = apply_inverse(evoked, inverse_operator, lambda2, method)
     #    Let's only deal with t > 0, cropping to reduce multiple comparisons
     condition.crop(0, None)
@@ -94,13 +96,14 @@ tstep = conditions[0].tstep
 
 ###############################################################################
 # Transform to common cortical space
-
-#    Normally you would read in estimates across several subjects and morph
-#    them to the same cortical space (e.g. fsaverage). For example purposes,
-#    we will simulate this by just having each "subject" have the same
-#    response (just noisy in source space) here.
-
-# we'll only consider the left hemisphere in this example.
+# ----------------------------------
+#
+# Normally you would read in estimates across several subjects and morph them
+# to the same cortical space (e.g. fsaverage). For example purposes, we will
+# simulate this by just having each "subject" have the same response (just
+# noisy in source space) here.
+#
+# We'll only consider the left hemisphere in this tutorial.
 n_vertices_sample, n_times = conditions[0].lh_data.shape
 n_subjects = 7
 print('Simulating data for %d subjects.' % n_subjects)
@@ -111,12 +114,13 @@ X = randn(n_vertices_sample, n_times, n_subjects, 4) * 10
 for ii, condition in enumerate(conditions):
     X[:, :, :, ii] += condition.lh_data[:, :, np.newaxis]
 
-#    It's a good idea to spatially smooth the data, and for visualization
-#    purposes, let's morph these to fsaverage, which is a grade 5 source space
-#    with vertices 0:10242 for each hemisphere. Usually you'd have to morph
-#    each subject's data separately (and you might want to use morph_data
-#    instead), but here since all estimates are on 'sample' we can use one
-#    morph matrix for all the heavy lifting.
+###############################################################################
+# It's a good idea to spatially smooth the data, and for visualization
+# purposes, let's morph these to fsaverage, which is a grade 5 source space
+# with vertices 0:10242 for each hemisphere. Usually you'd have to morph
+# each subject's data separately (and you might want to use morph_data
+# instead), but here since all estimates are on 'sample' we can use one
+# morph matrix for all the heavy lifting.
 fsave_vertices = [np.arange(10242), np.array([], int)]  # right hemi is empty
 morph_mat = compute_morph_matrix('sample', 'fsaverage', sample_vertices,
                                  fsave_vertices, 20, subjects_dir)
@@ -128,32 +132,35 @@ print('Morphing data.')
 X = morph_mat.dot(X)  # morph_mat is a sparse matrix
 X = X.reshape(n_vertices_fsave, n_times, n_subjects, 4)
 
-#    Now we need to prepare the group matrix for the ANOVA statistic.
-#    To make the clustering function work correctly with the
-#    ANOVA function X needs to be a list of multi-dimensional arrays
-#    (one per condition) of shape: samples (subjects) x time x space
-
-X = np.transpose(X, [2, 1, 0, 3])  # First we permute dimensions
-# finally we split the array into a list a list of conditions
-# and discard the empty dimension resulting from the split using numpy squeeze
+###############################################################################
+# Now we need to prepare the group matrix for the ANOVA statistic. To make the
+# clustering function work correctly with the ANOVA function X needs to be a
+# list of multi-dimensional arrays (one per condition) of shape: samples
+# (subjects) x time x space.
+#
+# First we permute dimensions, then split the array into a list of conditions
+# and discard the empty dimension resulting from the split using numpy squeeze.
+X = np.transpose(X, [2, 1, 0, 3])  #
 X = [np.squeeze(x) for x in np.split(X, 4, axis=-1)]
 
 ###############################################################################
 # Prepare function for arbitrary contrast
-
+# ---------------------------------------
 # As our ANOVA function is a multi-purpose tool we need to apply a few
 # modifications to integrate it with the clustering function. This
 # includes reshaping data, setting default arguments and processing
 # the return values. For this reason we'll write a tiny dummy function.
-
+#
 # We will tell the ANOVA how to interpret the data matrix in terms of
 # factors. This is done via the factor levels argument which is a list
 # of the number factor levels for each factor.
 factor_levels = [2, 2]
 
+###############################################################################
 # Finally we will pick the interaction effect by passing 'A:B'.
-# (this notation is borrowed from the R formula language)
-effects = 'A:B'  # Without this also the main effects will be returned.
+# (this notation is borrowed from the R formula language). Without this also
+# the main effects will be returned.
+effects = 'A:B'
 # Tell the ANOVA not to compute p-values which we don't need for clustering
 return_pvals = False
 
@@ -161,26 +168,32 @@ return_pvals = False
 n_times = X[0].shape[1]
 n_conditions = 4
 
-
+###############################################################################
 # A stat_fun must deal with a variable number of input arguments.
+#
+# Inside the clustering function each condition will be passed as flattened
+# array, necessitated by the clustering procedure. The ANOVA however expects an
+# input array of dimensions: subjects X conditions X observations (optional).
+#
+# The following function catches the list input and swaps the first and the
+# second dimension, and finally calls ANOVA.
+#
+# Note. for further details on this ANOVA function consider the
+# corresponding
+# :ref:`time frequency tutorial <tut_stats_cluster_sensor_rANOVA_tfr>`.
+
+
 def stat_fun(*args):
-    # Inside the clustering function each condition will be passed as
-    # flattened array, necessitated by the clustering procedure.
-    # The ANOVA however expects an input array of dimensions:
-    # subjects X conditions X observations (optional).
-    # The following expression catches the list input
-    # and swaps the first and the second dimension, and finally calls ANOVA.
     return f_mway_rm(np.swapaxes(args, 1, 0), factor_levels=factor_levels,
                      effects=effects, return_pvals=return_pvals)[0]
     # get f-values only.
-    # Note. for further details on this ANOVA function consider the
-    # corresponding time frequency example.
 
 ###############################################################################
 # Compute clustering statistic
-
-#    To use an algorithm optimized for spatio-temporal clustering, we
-#    just pass the spatial connectivity matrix (instead of spatio-temporal)
+# ----------------------------
+#
+# To use an algorithm optimized for spatio-temporal clustering, we
+# just pass the spatial connectivity matrix (instead of spatio-temporal).
 
 source_space = grade_to_tris(5)
 # as we only have one hemisphere we need only need half the connectivity
@@ -208,6 +221,7 @@ good_cluster_inds = np.where(cluster_p_values < 0.05)[0]
 
 ###############################################################################
 # Visualize the clusters
+# ----------------------
 
 print('Visualizing clusters.')
 
diff --git a/tutorials/plot_cluster_stats_time_frequency.py b/tutorials/plot_stats_cluster_time_frequency.py
similarity index 96%
rename from tutorials/plot_cluster_stats_time_frequency.py
rename to tutorials/plot_stats_cluster_time_frequency.py
index bb11b87..9124c91 100644
--- a/tutorials/plot_cluster_stats_time_frequency.py
+++ b/tutorials/plot_stats_cluster_time_frequency.py
@@ -44,7 +44,7 @@ tmin = -0.2
 tmax = 0.5
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 include = []
@@ -80,6 +80,7 @@ data_condition_2 = data_condition_2[:, 97:98, :]
 # Time vector
 times = 1e3 * epochs_condition_1.times  # change unit to ms
 
+###############################################################################
 # Factor to downsample the temporal dimension of the PSD computed by
 # single_trial_power.  Decimation occurs after frequency decomposition and can
 # be used to reduce memory usage (and possibly comptuational time of downstream
@@ -101,6 +102,7 @@ epochs_power_2 = single_trial_power(data_condition_2, sfreq=sfreq,
 epochs_power_1 = epochs_power_1[:, 0, :, :]  # only 1 channel to get 3D matrix
 epochs_power_2 = epochs_power_2[:, 0, :, :]  # only 1 channel to get 3D matrix
 
+###############################################################################
 # Compute ratio with baseline power (be sure to correct time vector with
 # decimation factor)
 baseline_mask = times[::decim] < 0
diff --git a/tutorials/plot_cluster_stats_time_frequency_repeated_measures_anova.py b/tutorials/plot_stats_cluster_time_frequency_repeated_measures_anova.py
similarity index 79%
rename from tutorials/plot_cluster_stats_time_frequency_repeated_measures_anova.py
rename to tutorials/plot_stats_cluster_time_frequency_repeated_measures_anova.py
index 7ee302f..ad1b7a2 100644
--- a/tutorials/plot_cluster_stats_time_frequency_repeated_measures_anova.py
+++ b/tutorials/plot_stats_cluster_time_frequency_repeated_measures_anova.py
@@ -1,5 +1,5 @@
 """
-.. _tut_stats_cluster_sensor_rANOVA_tfr
+.. _tut_stats_cluster_sensor_rANOVA_tfr:
 
 ====================================================================
 Mass-univariate twoway repeated measures ANOVA on single trial power
@@ -39,6 +39,7 @@ print(__doc__)
 
 ###############################################################################
 # Set parameters
+# --------------
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_raw-eve.fif'
@@ -47,7 +48,7 @@ tmin = -0.2
 tmax = 0.5
 
 # Setup for reading the raw data
-raw = io.Raw(raw_fname)
+raw = io.read_raw_fif(raw_fname)
 events = mne.read_events(event_fname)
 
 include = []
@@ -66,14 +67,15 @@ epochs = mne.Epochs(raw, events, event_id, tmin, tmax,
                     picks=picks, baseline=(None, 0),
                     reject=reject)
 
-# make sure all conditions have the same counts, as the ANOVA expects a
-# fully balanced data matrix and does not forgive imbalances that generously
-# (risk of type-I error)
+###############################################################################
+# We have to make sure all conditions have the same counts, as the ANOVA
+# expects a fully balanced data matrix and does not forgive imbalances that
+# generously (risk of type-I error).
 epochs.equalize_event_counts(event_id, copy=False)
 # Time vector
 times = 1e3 * epochs.times  # change unit to ms
 
-# Factor to downs-sample the temporal dimension of the PSD computed by
+# Factor to down-sample the temporal dimension of the PSD computed by
 # single_trial_power.
 decim = 2
 frequencies = np.arange(7, 30, 3)  # define frequencies of interest
@@ -81,8 +83,10 @@ sfreq = raw.info['sfreq']  # sampling in Hz
 n_cycles = frequencies / frequencies[0]
 baseline_mask = times[::decim] < 0
 
-# now create TFR representations for all conditions
-epochs_power = []
+###############################################################################
+# Create TFR representations for all conditions
+# ---------------------------------------------
+epochs_power = list()
 for condition in [epochs[k].get_data()[:, 97:98, :] for k in event_id]:
     this_power = single_trial_power(condition, sfreq=sfreq,
                                     frequencies=frequencies, n_cycles=n_cycles,
@@ -96,12 +100,15 @@ for condition in [epochs[k].get_data()[:, 97:98, :] for k in event_id]:
 
 ###############################################################################
 # Setup repeated measures ANOVA
+# -----------------------------
+#
+# We will tell the ANOVA how to interpret the data matrix in terms of factors.
+# This is done via the factor levels argument which is a list of the number
+# factor levels for each factor.
 
 n_conditions = len(epochs.event_id)
 n_replications = epochs.events.shape[0] / n_conditions
-# we will tell the ANOVA how to interpret the data matrix in terms of
-# factors. This done via the factor levels argument which is a list
-# of the number factor levels for each factor.
+
 factor_levels = [2, 2]  # number of levels in each factor
 effects = 'A*B'  # this is the default signature for computing all effects
 # Other possible options are 'A' or 'B' for the corresponding main effects
@@ -110,8 +117,9 @@ effects = 'A*B'  # this is the default signature for computing all effects
 n_frequencies = len(frequencies)
 n_times = len(times[::decim])
 
+###############################################################################
 # Now we'll assemble the data matrix and swap axes so the trial replications
-# are the first dimension and the conditions are the second dimension
+# are the first dimension and the conditions are the second dimension.
 data = np.swapaxes(np.asarray(epochs_power), 1, 0)
 # reshape last two dimensions in one mass-univariate observation-vector
 data = data.reshape(n_replications, n_conditions, n_frequencies * n_times)
@@ -119,16 +127,27 @@ data = data.reshape(n_replications, n_conditions, n_frequencies * n_times)
 # so we have replications * conditions * observations:
 print(data.shape)
 
-# while the iteration scheme used above for assembling the data matrix
+###############################################################################
+# While the iteration scheme used above for assembling the data matrix
 # makes sure the first two dimensions are organized as expected (with A =
 # modality and B = location):
 #
-#           A1B1 A1B2 A2B1 B2B2
-# trial 1   1.34 2.53 0.97 1.74
-# trial ... .... .... .... ....
-# trial 56  2.45 7.90 3.09 4.76
+# .. table::
+#
+# ===== ==== ==== ==== ====
+# trial A1B1 A1B2 A2B1 B2B2
+# ===== ==== ==== ==== ====
+# 1     1.34 2.53 0.97 1.74
+# ...   .... .... .... ....
+# 56    2.45 7.90 3.09 4.76
+# ===== ==== ==== ==== ====
 #
 # Now we're ready to run our repeated measures ANOVA.
+#
+# Note. As we treat trials as subjects, the test only accounts for
+# time locked responses despite the 'induced' approach.
+# For analysis for induced power at the group level averaged TRFs
+# are required.
 
 fvals, pvals = f_mway_rm(data, factor_levels, effects=effects)
 
@@ -152,34 +171,29 @@ for effect, sig, effect_label in zip(fvals, pvals, effect_labels):
     plt.title(r"Time-locked response for '%s' (%s)" % (effect_label, ch_name))
     plt.show()
 
-# Note. As we treat trials as subjects, the test only accounts for
-# time locked responses despite the 'induced' approach.
-# For analysis for induced power at the group level averaged TRFs
-# are required.
-
-
 ###############################################################################
 # Account for multiple comparisons using FDR versus permutation clustering test
-
+# -----------------------------------------------------------------------------
+#
 # First we need to slightly modify the ANOVA function to be suitable for
 # the clustering procedure. Also want to set some defaults.
 # Let's first override effects to confine the analysis to the interaction
 effects = 'A:B'
 
-
+###############################################################################
 # A stat_fun must deal with a variable number of input arguments.
+# Inside the clustering function each condition will be passed as flattened
+# array, necessitated by the clustering procedure. The ANOVA however expects an
+# input array of dimensions: subjects X conditions X observations (optional).
+# The following function catches the list input and swaps the first and
+# the second dimension and finally calls the ANOVA function.
+
+
 def stat_fun(*args):
-    # Inside the clustering function each condition will be passed as
-    # flattened array, necessitated by the clustering procedure.
-    # The ANOVA however expects an input array of dimensions:
-    # subjects X conditions X observations (optional).
-    # The following expression catches the list input and swaps the first and
-    # the second dimension and finally calls the ANOVA function.
     return f_mway_rm(np.swapaxes(args, 1, 0), factor_levels=factor_levels,
                      effects=effects, return_pvals=False)[0]
     # The ANOVA returns a tuple f-values and p-values, we will pick the former.
 
-
 pthresh = 0.00001  # set threshold rather high to save some time
 f_thresh = f_threshold_mway_rm(n_replications, factor_levels, effects,
                                pthresh)
@@ -189,7 +203,9 @@ T_obs, clusters, cluster_p_values, h0 = mne.stats.permutation_cluster_test(
     epochs_power, stat_fun=stat_fun, threshold=f_thresh, tail=tail, n_jobs=1,
     n_permutations=n_permutations, buffer_size=None)
 
+###############################################################################
 # Create new stats image with only significant clusters
+# -----------------------------------------------------
 good_clusers = np.where(cluster_p_values < .05)[0]
 T_obs_plot = np.ma.masked_array(T_obs,
                                 np.invert(clusters[np.squeeze(good_clusers)]))
@@ -205,7 +221,9 @@ plt.title('Time-locked response for \'modality by location\' (%s)\n'
           ' cluster-level corrected (p <= 0.05)' % ch_name)
 plt.show()
 
-# now using FDR
+###############################################################################
+# Now using FDR
+# -------------
 mask, _ = fdr_correction(pvals[2])
 T_obs_plot2 = np.ma.masked_array(T_obs, np.invert(mask))
 
diff --git a/tutorials/plot_spatio_temporal_cluster_stats_sensor.py b/tutorials/plot_stats_spatio_temporal_cluster_sensors.py
similarity index 88%
rename from tutorials/plot_spatio_temporal_cluster_stats_sensor.py
rename to tutorials/plot_stats_spatio_temporal_cluster_sensors.py
index a938fdf..bac6a93 100644
--- a/tutorials/plot_spatio_temporal_cluster_stats_sensor.py
+++ b/tutorials/plot_stats_spatio_temporal_cluster_sensors.py
@@ -29,8 +29,8 @@ from mne.channels import read_ch_connectivity
 print(__doc__)
 
 ###############################################################################
-
 # Set parameters
+# --------------
 data_path = sample.data_path()
 raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
 event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
@@ -39,12 +39,13 @@ tmin = -0.2
 tmax = 0.5
 
 # Setup for reading the raw data
-raw = mne.io.Raw(raw_fname, preload=True)
+raw = mne.io.read_raw_fif(raw_fname, preload=True)
 raw.filter(1, 30)
 events = mne.read_events(event_fname)
 
 ###############################################################################
 # Read epochs for the channel of interest
+# ---------------------------------------
 
 picks = mne.pick_types(raw.info, meg='mag', eog=True)
 
@@ -61,7 +62,8 @@ X = [np.transpose(x, (0, 2, 1)) for x in X]  # transpose for clustering
 
 
 ###############################################################################
-# load FieldTrip neighbor definition to setup sensor connectivity
+# Load FieldTrip neighbor definition to setup sensor connectivity
+# ---------------------------------------------------------------
 connectivity, ch_names = read_ch_connectivity('neuromag306mag')
 
 print(type(connectivity))  # it's a sparse matrix!
@@ -74,6 +76,7 @@ plt.title('Between-sensor adjacency')
 
 ###############################################################################
 # Compute permutation statistic
+# -----------------------------
 #
 # How does it work? We use clustering to `bind` together features which are
 # similar. Our features are the magnetic fields measured over our sensor
@@ -96,18 +99,19 @@ p_accept = 0.001
 
 cluster_stats = spatio_temporal_cluster_test(X, n_permutations=1000,
                                              threshold=threshold, tail=1,
-                                             n_jobs=2,
+                                             n_jobs=1,
                                              connectivity=connectivity)
 
 T_obs, clusters, p_values, _ = cluster_stats
 good_cluster_inds = np.where(p_values < p_accept)[0]
 
-# Note. The same functions works with source estimate. The only differences
+###############################################################################
+# Note. The same functions work with source estimate. The only differences
 # are the origin of the data, the size, and the connectivity definition.
 # It can be used for single trials or for groups of subjects.
-
-###############################################################################
+#
 # Visualize clusters
+# ------------------
 
 # configure variables for visualization
 times = epochs.times * 1e3
@@ -122,7 +126,7 @@ pos = mne.find_layout(epochs.info).pos
 
 # loop over significant clusters
 for i_clu, clu_idx in enumerate(good_cluster_inds):
-    # unpack cluster infomation, get unique indices
+    # unpack cluster information, get unique indices
     time_inds, space_inds = np.squeeze(clusters[clu_idx])
     ch_inds = np.unique(space_inds)
     time_inds = np.unique(time_inds)
@@ -144,7 +148,7 @@ for i_clu, clu_idx in enumerate(good_cluster_inds):
     fig.suptitle(title, fontsize=14)
 
     # plot average test statistic and mark significant sensors
-    image, _ = plot_topomap(f_map, pos, mask=mask, axis=ax_topo,
+    image, _ = plot_topomap(f_map, pos, mask=mask, axes=ax_topo,
                             cmap='Reds', vmin=np.min, vmax=np.max)
 
     # advanced matplotlib for showing image with figure and colorbar
@@ -182,12 +186,11 @@ for i_clu, clu_idx in enumerate(good_cluster_inds):
     fig.subplots_adjust(bottom=.05)
     plt.show()
 
-"""
-Exercises
-----------
-
-- What is the smallest p-value you can obtain, given the finite number of
-   permutations?
-- use an F distribution to compute the threshold by traditional significance
-   levels. Hint: take a look at ```scipy.stats.distributions.f```
-"""
+###############################################################################
+# Exercises
+# ----------
+#
+# - What is the smallest p-value you can obtain, given the finite number of
+#    permutations?
+# - use an F distribution to compute the threshold by traditional significance
+#    levels. Hint: take a look at ``scipy.stats.distributions.f``
diff --git a/tutorials/plot_visualize_epochs.py b/tutorials/plot_visualize_epochs.py
new file mode 100644
index 0000000..eca13d4
--- /dev/null
+++ b/tutorials/plot_visualize_epochs.py
@@ -0,0 +1,54 @@
+"""
+.. _tut_viz_epochs:
+
+Visualize Epochs data
+=====================
+
+"""
+import os.path as op
+
+import mne
+
+data_path = op.join(mne.datasets.sample.data_path(), 'MEG', 'sample')
+raw = mne.io.read_raw_fif(op.join(data_path, 'sample_audvis_raw.fif'))
+events = mne.read_events(op.join(data_path, 'sample_audvis_raw-eve.fif'))
+picks = mne.pick_types(raw.info, meg='grad')
+epochs = mne.Epochs(raw, events, [1, 2], picks=picks)
+
+###############################################################################
+# This tutorial focuses on visualization of epoched data. All of the functions
+# introduced here are basically high level matplotlib functions with built in
+# intelligence to work with epoched data. All the methods return a handle to
+# matplotlib figure instance.
+#
+# All plotting functions start with ``plot``. Let's start with the most
+# obvious. :func:`mne.Epochs.plot` offers an interactive browser that allows
+# rejection by hand when called in combination with a keyword ``block=True``.
+# This blocks the execution of the script until the browser window is closed.
+epochs.plot(block=True)
+
+###############################################################################
+# The numbers at the top refer to the event id of the epoch. We only have
+# events with id numbers of 1 and 2 since we included only those when
+# constructing the epochs.
+#
+# Since we did no artifact correction or rejection, there are epochs
+# contaminated with blinks and saccades. For instance, epoch number 9 (see
+# numbering at the bottom) seems to be contaminated by a blink (scroll to the
+# bottom to view the EOG channel). This epoch can be marked for rejection by
+# clicking on top of the browser window. The epoch should turn red when you
+# click it. This means that it will be dropped as the browser window is closed.
+# You should check out `help` at the lower left corner of the window for more
+# information about the interactive features.
+#
+# To plot individual channels as an image, where you see all the epochs at one
+# glance, you can use function :func:`mne.Epochs.plot_image`. It shows the
+# amplitude of the signal over all the epochs plus an average of the
+# activation.
+epochs.plot_image(97)
+
+# You also have functions for plotting channelwise information arranged into a
+# shape of the channel array. The image plotting uses automatic scaling by
+# default, but noisy channels and different channel types can cause the scaling
+# to be a bit off. Here we define the limits by hand.
+epochs.plot_topo_image(vmin=-200, vmax=200, title='ERF images')
diff --git a/tutorials/plot_visualize_evoked.py b/tutorials/plot_visualize_evoked.py
new file mode 100644
index 0000000..b456f65
--- /dev/null
+++ b/tutorials/plot_visualize_evoked.py
@@ -0,0 +1,158 @@
+"""
+.. _tut_viz_evoked:
+
+=====================
+Visualize Evoked data
+=====================
+"""
+import os.path as op
+import numpy as np
+import matplotlib.pyplot as plt
+
+import mne
+
+###############################################################################
+# In this tutorial we focus on plotting functions of :class:`mne.Evoked`.
+# Here we read the evoked object from a file. Check out
+# :ref:`tut_epoching_and_averaging` to get to this stage from raw data.
+data_path = mne.datasets.sample.data_path()
+fname = op.join(data_path, 'MEG', 'sample', 'sample_audvis-ave.fif')
+evoked = mne.read_evokeds(fname, baseline=(None, 0), proj=True)
+print(evoked)
+
+###############################################################################
+# Notice that ``evoked`` is a list of evoked instances. You can read only one
+# of the categories by passing the argument ``condition`` to
+# :func:`mne.read_evokeds`. To make things more simple for this tutorial, we
+# read each instance to a variable.
+evoked_l_aud = evoked[0]
+evoked_r_aud = evoked[1]
+evoked_l_vis = evoked[2]
+evoked_r_vis = evoked[3]
+
+###############################################################################
+# Let's start with a simple one. We plot event related potentials / fields
+# (ERP/ERF). The bad channels are not plotted by default. Here we explicitly
+# set the exclude parameter to show the bad channels in red. All plotting
+# functions of MNE-python return a handle to the figure instance. When we have
+# the handle, we can customise the plots to our liking.
+fig = evoked_l_aud.plot(exclude=())
+
+###############################################################################
+# All plotting functions of MNE-python returns a handle to the figure instance.
+# When we have the handle, we can customise the plots to our liking. We can get
+# rid of the empty space with a simple function call.
+fig.tight_layout()
+
+###############################################################################
+# Now let's make it a bit fancier and only use MEG channels. Many of the
+# MNE-functions include a ``picks`` parameter to include a selection of
+# channels. ``picks`` is simply a list of channel indices that you can easily
+# construct with :func:`mne.pick_types`. See also :func:`mne.pick_channels` and
+# :func:`mne.pick_channels_regexp`.
+# Using ``spatial_colors=True``, the individual channel lines are color coded
+# to show the sensor positions - specifically, the x, y, and z locations of
+# the sensors are transformed into R, G and B values.
+picks = mne.pick_types(evoked_l_aud.info, meg=True, eeg=False, eog=False)
+evoked_l_aud.plot(spatial_colors=True, gfp=True, picks=picks)
+
+###############################################################################
+# Notice the legend on the left. The colors would suggest that there may be two
+# separate sources for the signals. This wasn't obvious from the first figure.
+# Try painting the slopes with left mouse button. It should open a new window
+# with topomaps (scalp plots) of the average over the painted area. There is
+# also a function for drawing topomaps separately.
+evoked_l_aud.plot_topomap()
+
+###############################################################################
+# By default the topomaps are drawn from evenly spread out points of time over
+# the evoked data. We can also define the times ourselves.
+times = np.arange(0.05, 0.151, 0.05)
+evoked_r_aud.plot_topomap(times=times, ch_type='mag')
+
+###############################################################################
+# Or we can automatically select the peaks.
+evoked_r_aud.plot_topomap(times='peaks', ch_type='mag')
+
+###############################################################################
+# You can take a look at the documentation of :func:`mne.Evoked.plot_topomap`
+# or simply write ``evoked_r_aud.plot_topomap?`` in your python console to
+# see the different parameters you can pass to this function. Most of the
+# plotting functions also accept ``axes`` parameter. With that, you can
+# customise your plots even further. First we shall create a set of matplotlib
+# axes in a single figure and plot all of our evoked categories next to each
+# other.
+fig, ax = plt.subplots(1, 5)
+evoked_l_aud.plot_topomap(times=0.1, axes=ax[0], show=False)
+evoked_r_aud.plot_topomap(times=0.1, axes=ax[1], show=False)
+evoked_l_vis.plot_topomap(times=0.1, axes=ax[2], show=False)
+evoked_r_vis.plot_topomap(times=0.1, axes=ax[3], show=True)
+
+###############################################################################
+# Notice that we created five axes, but had only four categories. The fifth
+# axes was used for drawing the colorbar. You must provide room for it when you
+# create this kind of custom plots or turn the colorbar off with
+# ``colorbar=False``. That's what the warnings are trying to tell you. Also, we
+# used ``show=False`` for the three first function calls. This prevents the
+# showing of the figure prematurely. The behavior depends on the mode you are
+# using for your python session. See http://matplotlib.org/users/shell.html for
+# more information.
+#
+# We can combine the two kinds of plots in one figure using the ``plot_joint``
+# method of Evoked objects. Called as-is (``evoked.plot_joint()``), this
+# function should give a stylish and informative display of spatio-temporal
+# dynamics. Also note the ``topomap_args`` and ``ts_args`` parameters of
+# :func:`mne.Evoked.plot_joint`. You can pass key-value pairs as a python
+# dictionary that gets passed as parameters to the topomaps
+# (:func:`mne.Evoked.plot_topomap`) and time series (:func:`mne.Evoked.plot`)
+# of the joint plot.
+# For specific styling, use these ``topomap_args`` and ``ts_args``
+# arguments. Here, topomaps at specific time points (70 and 105 msec) are
+# shown, sensors are not plotted, and the Global Field Power is shown:
+ts_args = dict(gfp=True)
+topomap_args = dict(sensors=False)
+evoked_r_aud.plot_joint(title='right auditory', times=[.07, .105],
+                        ts_args=ts_args, topomap_args=topomap_args)
+
+###############################################################################
+# We can also plot the activations as images. The time runs along the x-axis
+# and the channels along the y-axis. The amplitudes are color coded so that
+# the amplitudes from negative to positive translates to shift from blue to
+# red. White means zero amplitude. You can use the ``cmap`` parameter to define
+# the color map yourself. The accepted values include all matplotlib colormaps.
+evoked_r_aud.plot_image(picks=picks)
+
+###############################################################################
+# Finally we plot the sensor data as a topographical view. In the simple case
+# we plot only left auditory responses, and then we plot them all in the same
+# figure for comparison. Click on the individual plots to open them bigger.
+title = 'MNE sample data (condition : %s)'
+evoked_l_aud.plot_topo(title=title % evoked_l_aud.comment)
+colors = 'yellow', 'green', 'red', 'blue'
+mne.viz.plot_evoked_topo(evoked, color=colors,
+                         title=title % 'Left/Right Auditory/Visual')
+
+###############################################################################
+# Visualizing field lines in 3D
+# -----------------------------
+#
+# We now compute the field maps to project MEG and EEG data to MEG helmet
+# and scalp surface.
+#
+# To do this we'll need coregistration information. See
+# :ref:`tut_forward` for more details.
+#
+# Here we just illustrate usage.
+
+subjects_dir = data_path + '/subjects'
+trans_fname = data_path + '/MEG/sample/sample_audvis_raw-trans.fif'
+
+maps = mne.make_field_map(evoked_l_aud, trans=trans_fname, subject='sample',
+                          subjects_dir=subjects_dir, n_jobs=1)
+
+# explore several points in time
+field_map = evoked_l_aud.plot_field(maps, time=.1)
+
+###############################################################################
+# .. note::
+#     If trans_fname is set to None then only MEG estimates can be visualized.
diff --git a/tutorials/plot_visualize_raw.py b/tutorials/plot_visualize_raw.py
new file mode 100644
index 0000000..4d1ce89
--- /dev/null
+++ b/tutorials/plot_visualize_raw.py
@@ -0,0 +1,92 @@
+"""
+.. _tut_viz_raw:
+
+Visualize Raw data
+==================
+
+"""
+import os.path as op
+
+import mne
+
+data_path = op.join(mne.datasets.sample.data_path(), 'MEG', 'sample')
+raw = mne.io.read_raw_fif(op.join(data_path, 'sample_audvis_raw.fif'))
+events = mne.read_events(op.join(data_path, 'sample_audvis_raw-eve.fif'))
+
+###############################################################################
+# The visualization module (:mod:`mne.viz`) contains all the plotting functions
+# that work in combination with MNE data structures. Usually the easiest way to
+# use them is to call a method of the data container. All of the plotting
+# method names start with ``plot``. If you're using Ipython console, you can
+# just write ``raw.plot`` and ask the interpreter for suggestions with a
+# ``tab`` key.
+#
+# To visually inspect your raw data, you can use the python equivalent of
+# ``mne_browse_raw``.
+raw.plot(block=True, events=events)
+
+###############################################################################
+# The channels are color coded by channel type. Generally MEG channels are
+# colored in different shades of blue, whereas EEG channels are black. The
+# channels are also sorted by channel type by default. If you want to use a
+# custom order for the channels, you can use ``order`` parameter of
+# :func:`raw.plot`. The scrollbar on right side of the browser window also
+# tells us that two of the channels are marked as ``bad``. Bad channels are
+# color coded gray. By clicking the lines or channel names on the left, you can
+# mark or unmark a bad channel interactively. You can use +/- keys to adjust
+# the scale (also = works for magnifying the data). Note that the initial
+# scaling factors can be set with parameter ``scalings``. If you don't know the
+# scaling factor for channels, you can automatically set them by passing
+# scalings='auto'. With ``pageup/pagedown`` and ``home/end`` keys you can
+# adjust the amount of data viewed at once. To see all the interactive
+# features, hit ``?`` or click ``help`` in the lower left corner of the
+# browser window.
+#
+# We read the events from a file and passed it as a parameter when calling the
+# method. The events are plotted as vertical lines so you can see how they
+# align with the raw data.
+#
+# We can check where the channels reside with ``plot_sensors``. Notice that
+# this method (along with many other MNE plotting functions) is callable using
+# any MNE data container where the channel information is available.
+raw.plot_sensors(kind='3d', ch_type='mag')
+
+###############################################################################
+# Now let's add some ssp projectors to the raw data. Here we read them from a
+# file and plot them.
+projs = mne.read_proj(op.join(data_path, 'sample_audvis_eog-proj.fif'))
+raw.add_proj(projs)
+raw.plot_projs_topomap()
+
+###############################################################################
+# The first three projectors that we see are the SSP vectors from empty room
+# measurements to compensate for the noise. The fourth one is the average EEG
+# reference. These are already applied to the data and can no longer be
+# removed. The next six are the EOG projections that we added. Every data
+# channel type has two projection vectors each. Let's try the raw browser
+# again.
+raw.plot()
+
+###############################################################################
+# Now click the `proj` button at the lower right corner of the browser
+# window. A selection dialog should appear, where you can toggle the projectors
+# on and off. Notice that the first four are already applied to the data and
+# toggling them does not change the data. However the newly added projectors
+# modify the data to get rid of the EOG artifacts. Note that toggling the
+# projectors here doesn't actually modify the data. This is purely for visually
+# inspecting the effect. See :func:`mne.io.Raw.del_proj` to actually remove the
+# projectors.
+#
+# Raw container also lets us easily plot the power spectra over the raw data.
+# See the API documentation for more info.
+raw.plot_psd()
+
+###############################################################################
+# Plotting channel wise power spectra is just as easy. The layout is inferred
+# from the data by default when plotting topo plots. This works for most data,
+# but it is also possible to define the layouts by hand. Here we select a
+# layout with only magnetometer channels and plot it. Then we plot the channel
+# wise spectra of first 30 seconds of the data.
+layout = mne.channels.read_layout('Vectorview-mag')
+layout.plot()
+raw.plot_psd_topo(tmax=30., fmin=5., fmax=60., n_fft=1024, layout=layout)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-mne.git



More information about the debian-med-commit mailing list