{"action":"created","comment":{"url":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments/41218884","id":41218884,"diff_hunk":"@@ -642,3 +685,116 @@ def _overlap_projector(data_int, data_res, corr):\n     Vh_intersect = Vh_intersect[intersect_mask].T\n     V_principal = np.dot(Q_res, Vh_intersect)\n     return V_principal\n+\n+\n+def _read_fine_cal(info, fine_cal_fname):\n+    \"\"\"Read sensor locations and calib. coeffs from fine calibration file.\"\"\"\n+\n+    # Read new sensor locations\n+    with open(fine_cal_fname, 'r') as fid:\n+        cal_chans = []\n+        for line in fid.readlines():\n+            if line[0] == '#' or line[0] == '\\n':\n+                continue\n+\n+            vals = np.fromstring(line, sep=' ')\n+\n+            # Check for correct number of items\n+            if len(vals) not in [14, 16]:\n+                raise RuntimeError('Error reading fine calibration file')\n+\n+            ch = int(vals[0].copy())  # Get channel number\n+            ch_name = 'MEG' + '%04d' % ch  # Zero-pad names to 4 characters\n+            loc = vals[1:13].copy()  # Get orientation information\n+            calib_coeff = vals[13:].copy()  # Get imbalance/calibration coeff\n+            ch_type = channel_type(info, info['ch_names'].index(ch_name))\n+\n+            cal_chans.append(dict(ch=ch, ch_name=ch_name, loc=loc,\n+                                  calib_coeff=calib_coeff, ch_type=ch_type))\n+\n+    # Check that we ended up with correct number of channels\n+    if len(cal_chans) != info['nchan']:\n+        raise RuntimeError('Number of channels in fine calibration file (%i) '\n+                           'does not equal number of channels in info (%i)' %\n+                           (len(cal_chans), info['nchan']))\n+\n+    return cal_chans\n+\n+\n+def _update_sensor_geometry(info, cal_chans):\n+    \"\"\"Helper to replace sensor geometry information\"\"\"\n+\n+    # Replace sensor locations (and track differences) for fine calibration\n+    ang_shift = np.zeros((info['nchan'], 3))\n+    for cal_chan in cal_chans:\n+        ch_ind = info['ch_names'].index(cal_chan['ch_name'])\n+\n+        # Loop over vectors in X, Y, and Z directions\n+        for ang_i, (i1, i2) in enumerate(zip(range(3, 10, 3),\n+                                             range(6, 13, 3))):\n+            v1 = cal_chan['loc'][i1:i2]\n+            v2 = info['chs'][ch_ind]['loc'][i1:i2].astype(float)\n+\n+            cos_ang = np.dot(v1, v2) / (np.linalg.norm(v1) *\n+                                        np.linalg.norm(v2))\n+            ang_shift[ch_ind, ang_i] = cos_ang\n+\n+        # Adjust channel orientation with those from fine calibration\n+        info['chs'][ch_ind]['loc'] = cal_chan['loc']\n+\n+    # Deal with numerical precision giving values slightly more than 1.\n+    np.clip(ang_shift, -1., 1., ang_shift)\n+    ang_shift = np.rad2deg(np.arccos(ang_shift))  # Convert to degrees\n+\n+    # Log quantification of sensor changes\n+    logger.info('Fine calibration adjusted coil positions by (mean ± std in '\n+                'degrees): %0.1f ± %0.1f (max: %0.1f)' %\n+                (np.mean(ang_shift), np.std(ang_shift),\n+                 np.max(np.abs(ang_shift))))\n+\n+    return info\n+\n+\n+def _sss_basis_point(origin, info, int_order, ext_order, imbalance,\n+                     head_frame=True):\n+    \"\"\"Compute multipolar moments for point-like magnetometers (in fine cal)\"\"\"\n+\n+    # Permeability of free space for scaling\n+    u0 = 4 * np.pi * 1e-7\n+\n+    # Construct 'coils' with r, weights, normal vecs, # integration pts, and\n+    # channel type.\n+    if imbalance.ndim == 1:\n+        imbalance = imbalance[:, np.newaxis]\n+    if imbalance.shape[1] not in [1, 3]:\n+        raise ValueError('Must have 1 (x) or 3 (x, y, z) point-like ' +\n+                         'magnetometers. Currently have %i' %\n+                         imbalance.shape[1])\n+\n+    # Initialize internal multipolar moment space for point-like magnetometers\n+    S_in_all = np.zeros((len(info['chs']), get_num_moments(int_order, 0)))\n+    S_out_all = np.zeros((len(info['chs']), get_num_moments(0, ext_order)))\n+\n+    # Coil_type values for x, y, z point magnetometers\n+    pt_types = [FIFF.FIFFV_COIL_POINT_MAGNETOMETER_X,\n+                FIFF.FIFFV_COIL_POINT_MAGNETOMETER_Y,\n+                FIFF.FIFFV_COIL_POINT_MAGNETOMETER]\n+\n+    # Loop over all coordinate directions desired and create point mags\n+    for dir_ind in range(imbalance.shape[1]):\n+\n+        temp_info = deepcopy(info)\n+        for ch in temp_info['chs']:\n+            ch['coil_type'] = pt_types[dir_ind]\n+        coils_add, _, _, _ = _prep_meg_channels(temp_info, accurate=True,","path":"mne/preprocessing/maxwell.py","position":258,"original_position":258,"commit_id":"b63f560f68d31dc0ec90544fd278e19264ad5ad0","original_commit_id":"b63f560f68d31dc0ec90544fd278e19264ad5ad0","user":{"login":"Eric89GXL","id":2365790,"avatar_url":"https://avatars.githubusercontent.com/u/2365790?v=3","gravatar_id":"","url":"https://api.github.com/users/Eric89GXL","html_url":"https://github.com/Eric89GXL","followers_url":"https://api.github.com/users/Eric89GXL/followers","following_url":"https://api.github.com/users/Eric89GXL/following{/other_user}","gists_url":"https://api.github.com/users/Eric89GXL/gists{/gist_id}","starred_url":"https://api.github.com/users/Eric89GXL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Eric89GXL/subscriptions","organizations_url":"https://api.github.com/users/Eric89GXL/orgs","repos_url":"https://api.github.com/users/Eric89GXL/repos","events_url":"https://api.github.com/users/Eric89GXL/events{/privacy}","received_events_url":"https://api.github.com/users/Eric89GXL/received_events","type":"User","site_admin":false},"body":"can just do `_prep_meg_channels(...)[0]`","created_at":"2015-10-06T01:45:10Z","updated_at":"2015-10-06T01:45:10Z","html_url":"https://github.com/mne-tools/mne-python/pull/2374#discussion_r41218884","pull_request_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374","_links":{"self":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments/41218884"},"html":{"href":"https://github.com/mne-tools/mne-python/pull/2374#discussion_r41218884"},"pull_request":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374"}}},"pull_request":{"url":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374","id":41385828,"html_url":"https://github.com/mne-tools/mne-python/pull/2374","diff_url":"https://github.com/mne-tools/mne-python/pull/2374.diff","patch_url":"https://github.com/mne-tools/mne-python/pull/2374.patch","issue_url":"https://api.github.com/repos/mne-tools/mne-python/issues/2374","number":2374,"state":"open","locked":false,"title":"[WIP] SSS: Add Fine Calibration","user":{"login":"wronk","id":5296721,"avatar_url":"https://avatars.githubusercontent.com/u/5296721?v=3","gravatar_id":"","url":"https://api.github.com/users/wronk","html_url":"https://github.com/wronk","followers_url":"https://api.github.com/users/wronk/followers","following_url":"https://api.github.com/users/wronk/following{/other_user}","gists_url":"https://api.github.com/users/wronk/gists{/gist_id}","starred_url":"https://api.github.com/users/wronk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wronk/subscriptions","organizations_url":"https://api.github.com/users/wronk/orgs","repos_url":"https://api.github.com/users/wronk/repos","events_url":"https://api.github.com/users/wronk/events{/privacy}","received_events_url":"https://api.github.com/users/wronk/received_events","type":"User","site_admin":false},"body":"Add improved calibration to the SSS functionality. This should increase the SSS shielding factor by ~10x making it useful for actual data processing.\r\n\r\n* [x] Add option to use sensor geometry from `sss_cal.dat` files instead of `raw`\r\n* [x] Quantify difference in geometry and send to `logger` along with fine calibration file used\r\n* [x] Add point-like magnetometers (X, Y, and Z) to gradiometer calcs\r\n* [x] Add sensor imbalance weights to gradiometer calcs and calibration coeffs to magnetometer calcs\r\n* [x] Add tests for most/all of the above\r\n* [ ] Verify against Elekta's code\r\n* [ ] Add warning about T1/T2 magnetometers if present and fine cal not used (#2502)\r\n* [ ] Ensure processing history documents cross-talk and fine cal\r\n\r\nCloses #2502.\r\n\r\nThis will require an update of the mne-testing-dataset","created_at":"2015-07-31T22:09:37Z","updated_at":"2015-10-06T01:45:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6ee98b462d61c858928f454227cc91195cd045eb","assignee":null,"milestone":{"url":"https://api.github.com/repos/mne-tools/mne-python/milestones/6","html_url":"https://github.com/mne-tools/mne-python/milestones/0.10","labels_url":"https://api.github.com/repos/mne-tools/mne-python/milestones/6/labels","id":1074222,"number":6,"title":"0.10","description":"","creator":{"login":"Eric89GXL","id":2365790,"avatar_url":"https://avatars.githubusercontent.com/u/2365790?v=3","gravatar_id":"","url":"https://api.github.com/users/Eric89GXL","html_url":"https://github.com/Eric89GXL","followers_url":"https://api.github.com/users/Eric89GXL/followers","following_url":"https://api.github.com/users/Eric89GXL/following{/other_user}","gists_url":"https://api.github.com/users/Eric89GXL/gists{/gist_id}","starred_url":"https://api.github.com/users/Eric89GXL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Eric89GXL/subscriptions","organizations_url":"https://api.github.com/users/Eric89GXL/orgs","repos_url":"https://api.github.com/users/Eric89GXL/repos","events_url":"https://api.github.com/users/Eric89GXL/events{/privacy}","received_events_url":"https://api.github.com/users/Eric89GXL/received_events","type":"User","site_admin":false},"open_issues":14,"closed_issues":69,"state":"open","created_at":"2015-04-19T22:04:00Z","updated_at":"2015-10-05T18:43:49Z","due_on":"2015-09-30T07:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374/commits","review_comments_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374/comments","review_comment_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mne-tools/mne-python/issues/2374/comments","statuses_url":"https://api.github.com/repos/mne-tools/mne-python/statuses/b63f560f68d31dc0ec90544fd278e19264ad5ad0","head":{"label":"wronk:sss_fine_cal","ref":"sss_fine_cal","sha":"b63f560f68d31dc0ec90544fd278e19264ad5ad0","user":{"login":"wronk","id":5296721,"avatar_url":"https://avatars.githubusercontent.com/u/5296721?v=3","gravatar_id":"","url":"https://api.github.com/users/wronk","html_url":"https://github.com/wronk","followers_url":"https://api.github.com/users/wronk/followers","following_url":"https://api.github.com/users/wronk/following{/other_user}","gists_url":"https://api.github.com/users/wronk/gists{/gist_id}","starred_url":"https://api.github.com/users/wronk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wronk/subscriptions","organizations_url":"https://api.github.com/users/wronk/orgs","repos_url":"https://api.github.com/users/wronk/repos","events_url":"https://api.github.com/users/wronk/events{/privacy}","received_events_url":"https://api.github.com/users/wronk/received_events","type":"User","site_admin":false},"repo":{"id":12385398,"name":"mne-python","full_name":"wronk/mne-python","owner":{"login":"wronk","id":5296721,"avatar_url":"https://avatars.githubusercontent.com/u/5296721?v=3","gravatar_id":"","url":"https://api.github.com/users/wronk","html_url":"https://github.com/wronk","followers_url":"https://api.github.com/users/wronk/followers","following_url":"https://api.github.com/users/wronk/following{/other_user}","gists_url":"https://api.github.com/users/wronk/gists{/gist_id}","starred_url":"https://api.github.com/users/wronk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wronk/subscriptions","organizations_url":"https://api.github.com/users/wronk/orgs","repos_url":"https://api.github.com/users/wronk/repos","events_url":"https://api.github.com/users/wronk/events{/privacy}","received_events_url":"https://api.github.com/users/wronk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/wronk/mne-python","description":"MNE : Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python","fork":true,"url":"https://api.github.com/repos/wronk/mne-python","forks_url":"https://api.github.com/repos/wronk/mne-python/forks","keys_url":"https://api.github.com/repos/wronk/mne-python/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wronk/mne-python/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wronk/mne-python/teams","hooks_url":"https://api.github.com/repos/wronk/mne-python/hooks","issue_events_url":"https://api.github.com/repos/wronk/mne-python/issues/events{/number}","events_url":"https://api.github.com/repos/wronk/mne-python/events","assignees_url":"https://api.github.com/repos/wronk/mne-python/assignees{/user}","branches_url":"https://api.github.com/repos/wronk/mne-python/branches{/branch}","tags_url":"https://api.github.com/repos/wronk/mne-python/tags","blobs_url":"https://api.github.com/repos/wronk/mne-python/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wronk/mne-python/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wronk/mne-python/git/refs{/sha}","trees_url":"https://api.github.com/repos/wronk/mne-python/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wronk/mne-python/statuses/{sha}","languages_url":"https://api.github.com/repos/wronk/mne-python/languages","stargazers_url":"https://api.github.com/repos/wronk/mne-python/stargazers","contributors_url":"https://api.github.com/repos/wronk/mne-python/contributors","subscribers_url":"https://api.github.com/repos/wronk/mne-python/subscribers","subscription_url":"https://api.github.com/repos/wronk/mne-python/subscription","commits_url":"https://api.github.com/repos/wronk/mne-python/commits{/sha}","git_commits_url":"https://api.github.com/repos/wronk/mne-python/git/commits{/sha}","comments_url":"https://api.github.com/repos/wronk/mne-python/comments{/number}","issue_comment_url":"https://api.github.com/repos/wronk/mne-python/issues/comments{/number}","contents_url":"https://api.github.com/repos/wronk/mne-python/contents/{+path}","compare_url":"https://api.github.com/repos/wronk/mne-python/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wronk/mne-python/merges","archive_url":"https://api.github.com/repos/wronk/mne-python/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wronk/mne-python/downloads","issues_url":"https://api.github.com/repos/wronk/mne-python/issues{/number}","pulls_url":"https://api.github.com/repos/wronk/mne-python/pulls{/number}","milestones_url":"https://api.github.com/repos/wronk/mne-python/milestones{/number}","notifications_url":"https://api.github.com/repos/wronk/mne-python/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wronk/mne-python/labels{/name}","releases_url":"https://api.github.com/repos/wronk/mne-python/releases{/id}","created_at":"2013-08-26T17:36:28Z","updated_at":"2015-05-07T21:22:18Z","pushed_at":"2015-10-02T23:11:39Z","git_url":"git://github.com/wronk/mne-python.git","ssh_url":"git@github.com:wronk/mne-python.git","clone_url":"https://github.com/wronk/mne-python.git","svn_url":"https://github.com/wronk/mne-python","homepage":"http://martinos.org/mne/","size":81735,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"mne-tools:master","ref":"master","sha":"a6ad9b39ed2a02ba041cdbc553e30d02775a3bfa","user":{"login":"mne-tools","id":583875,"avatar_url":"https://avatars.githubusercontent.com/u/583875?v=3","gravatar_id":"","url":"https://api.github.com/users/mne-tools","html_url":"https://github.com/mne-tools","followers_url":"https://api.github.com/users/mne-tools/followers","following_url":"https://api.github.com/users/mne-tools/following{/other_user}","gists_url":"https://api.github.com/users/mne-tools/gists{/gist_id}","starred_url":"https://api.github.com/users/mne-tools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mne-tools/subscriptions","organizations_url":"https://api.github.com/users/mne-tools/orgs","repos_url":"https://api.github.com/users/mne-tools/repos","events_url":"https://api.github.com/users/mne-tools/events{/privacy}","received_events_url":"https://api.github.com/users/mne-tools/received_events","type":"Organization","site_admin":false},"repo":{"id":1301584,"name":"mne-python","full_name":"mne-tools/mne-python","owner":{"login":"mne-tools","id":583875,"avatar_url":"https://avatars.githubusercontent.com/u/583875?v=3","gravatar_id":"","url":"https://api.github.com/users/mne-tools","html_url":"https://github.com/mne-tools","followers_url":"https://api.github.com/users/mne-tools/followers","following_url":"https://api.github.com/users/mne-tools/following{/other_user}","gists_url":"https://api.github.com/users/mne-tools/gists{/gist_id}","starred_url":"https://api.github.com/users/mne-tools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mne-tools/subscriptions","organizations_url":"https://api.github.com/users/mne-tools/orgs","repos_url":"https://api.github.com/users/mne-tools/repos","events_url":"https://api.github.com/users/mne-tools/events{/privacy}","received_events_url":"https://api.github.com/users/mne-tools/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mne-tools/mne-python","description":"MNE : Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python","fork":false,"url":"https://api.github.com/repos/mne-tools/mne-python","forks_url":"https://api.github.com/repos/mne-tools/mne-python/forks","keys_url":"https://api.github.com/repos/mne-tools/mne-python/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mne-tools/mne-python/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mne-tools/mne-python/teams","hooks_url":"https://api.github.com/repos/mne-tools/mne-python/hooks","issue_events_url":"https://api.github.com/repos/mne-tools/mne-python/issues/events{/number}","events_url":"https://api.github.com/repos/mne-tools/mne-python/events","assignees_url":"https://api.github.com/repos/mne-tools/mne-python/assignees{/user}","branches_url":"https://api.github.com/repos/mne-tools/mne-python/branches{/branch}","tags_url":"https://api.github.com/repos/mne-tools/mne-python/tags","blobs_url":"https://api.github.com/repos/mne-tools/mne-python/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mne-tools/mne-python/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mne-tools/mne-python/git/refs{/sha}","trees_url":"https://api.github.com/repos/mne-tools/mne-python/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mne-tools/mne-python/statuses/{sha}","languages_url":"https://api.github.com/repos/mne-tools/mne-python/languages","stargazers_url":"https://api.github.com/repos/mne-tools/mne-python/stargazers","contributors_url":"https://api.github.com/repos/mne-tools/mne-python/contributors","subscribers_url":"https://api.github.com/repos/mne-tools/mne-python/subscribers","subscription_url":"https://api.github.com/repos/mne-tools/mne-python/subscription","commits_url":"https://api.github.com/repos/mne-tools/mne-python/commits{/sha}","git_commits_url":"https://api.github.com/repos/mne-tools/mne-python/git/commits{/sha}","comments_url":"https://api.github.com/repos/mne-tools/mne-python/comments{/number}","issue_comment_url":"https://api.github.com/repos/mne-tools/mne-python/issues/comments{/number}","contents_url":"https://api.github.com/repos/mne-tools/mne-python/contents/{+path}","compare_url":"https://api.github.com/repos/mne-tools/mne-python/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mne-tools/mne-python/merges","archive_url":"https://api.github.com/repos/mne-tools/mne-python/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mne-tools/mne-python/downloads","issues_url":"https://api.github.com/repos/mne-tools/mne-python/issues{/number}","pulls_url":"https://api.github.com/repos/mne-tools/mne-python/pulls{/number}","milestones_url":"https://api.github.com/repos/mne-tools/mne-python/milestones{/number}","notifications_url":"https://api.github.com/repos/mne-tools/mne-python/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mne-tools/mne-python/labels{/name}","releases_url":"https://api.github.com/repos/mne-tools/mne-python/releases{/id}","created_at":"2011-01-28T03:31:13Z","updated_at":"2015-09-26T14:37:30Z","pushed_at":"2015-10-05T19:53:15Z","git_url":"git://github.com/mne-tools/mne-python.git","ssh_url":"git@github.com:mne-tools/mne-python.git","clone_url":"https://github.com/mne-tools/mne-python.git","svn_url":"https://github.com/mne-tools/mne-python","homepage":"http://martinos.org/mne/","size":183109,"stargazers_count":148,"watchers_count":148,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":142,"mirror_url":null,"open_issues_count":182,"forks":142,"open_issues":182,"watchers":148,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374"},"html":{"href":"https://github.com/mne-tools/mne-python/pull/2374"},"issue":{"href":"https://api.github.com/repos/mne-tools/mne-python/issues/2374"},"comments":{"href":"https://api.github.com/repos/mne-tools/mne-python/issues/2374/comments"},"review_comments":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374/comments"},"review_comment":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/2374/commits"},"statuses":{"href":"https://api.github.com/repos/mne-tools/mne-python/statuses/b63f560f68d31dc0ec90544fd278e19264ad5ad0"}}}}