def test_calculate_chpi_positions_vv(): """Test calculation of cHPI positions.""" # Check to make sure our fits match MF decently mf_quats = read_head_pos(pos_fname) raw = read_raw_fif(chpi_fif_fname, allow_maxshield='yes') raw.crop(0, 5).load_data() # check "auto" t_window estimation at full sampling rate with catch_logging() as log: compute_chpi_amplitudes(raw, t_step_min=0.1, t_window='auto', tmin=0, tmax=2, verbose=True) assert '83.3 ms' in log.getvalue() # This is a little hack (aliasing while decimating) to make it much faster # for testing purposes only. We can relax this later if we find it breaks # something. raw_dec = _decimate_chpi(raw, 15) with catch_logging() as log: with pytest.warns(RuntimeWarning, match='cannot determine'): py_quats = _calculate_chpi_positions(raw_dec, t_window=0.2, verbose='debug') log = log.getvalue() assert '\nHPIFIT' in log assert 'Computing 4385 HPI location guesses' in log _assert_quats(py_quats, mf_quats, dist_tol=0.001, angle_tol=0.7) # degenerate conditions raw_no_chpi = read_raw_fif(sample_fname) with pytest.raises(ValueError, match='No appropriate cHPI information'): _calculate_chpi_positions(raw_no_chpi) raw_bad = raw.copy() del raw_bad.info['hpi_meas'][0]['hpi_coils'][0]['coil_freq'] with pytest.raises(ValueError, match='No appropriate cHPI information'): _calculate_chpi_positions(raw_bad) raw_bad = raw.copy() for d in raw_bad.info['dig']: if d['kind'] == FIFF.FIFFV_POINT_HPI: d['coord_frame'] = FIFF.FIFFV_COORD_UNKNOWN break with pytest.raises(RuntimeError, match='coordinate frame incorrect'): _calculate_chpi_positions(raw_bad) for d in raw_bad.info['dig']: if d['kind'] == FIFF.FIFFV_POINT_HPI: d['coord_frame'] = FIFF.FIFFV_COORD_HEAD d['r'] = np.ones(3) raw_bad.crop(0, 1.) picks = np.concatenate([np.arange(306, len(raw_bad.ch_names)), pick_types(raw_bad.info, meg=True)[::16]]) raw_bad.pick_channels([raw_bad.ch_names[pick] for pick in picks]) with pytest.warns(RuntimeWarning, match='Discrepancy'): with catch_logging() as log_file: _calculate_chpi_positions(raw_bad, t_step_min=1., verbose=True) # ignore HPI info header and [done] footer assert '0/5 good HPI fits' in log_file.getvalue() # half the rate cuts off cHPI coils with raw.info._unlock(): raw.info['lowpass'] /= 2. with pytest.raises(RuntimeError, match='above the'): _calculate_chpi_positions(raw)
def _calculate_chpi_positions(raw, t_step_min=0.01, t_step_max=1., t_window='auto', too_close='raise', dist_limit=0.005, gof_limit=0.98, ext_order=1, verbose=None): chpi_amplitudes = compute_chpi_amplitudes(raw, t_step_min=t_step_min, t_window=t_window, ext_order=ext_order, verbose=verbose) chpi_locs = compute_chpi_locs(raw.info, chpi_amplitudes, t_step_max=t_step_max, too_close=too_close, verbose=verbose) head_pos = compute_head_pos(raw.info, chpi_locs, dist_limit=dist_limit, gof_limit=gof_limit, verbose=verbose) return head_pos
def test_simulate_raw_chpi(): """Test simulation of raw data with cHPI.""" raw = read_raw_fif(raw_chpi_fname, allow_maxshield='yes') picks = np.arange(len(raw.ch_names)) picks = np.setdiff1d(picks, pick_types(raw.info, meg=True, eeg=True)[::4]) raw.load_data().pick_channels([raw.ch_names[pick] for pick in picks]) raw.info.normalize_proj() sphere = make_sphere_model('auto', 'auto', raw.info) # make sparse spherical source space sphere_vol = tuple(sphere['r0']) + (sphere.radius, ) src = setup_volume_source_space(sphere=sphere_vol, pos=70., sphere_units='m') stcs = [_make_stc(raw, src)] * 15 # simulate data with cHPI on raw_sim = simulate_raw(raw.info, stcs, None, src, sphere, head_pos=pos_fname, interp='zero', first_samp=raw.first_samp) # need to trim extra samples off this one raw_chpi = add_chpi(raw_sim.copy(), head_pos=pos_fname, interp='zero') # test cHPI indication hpi_freqs, hpi_pick, hpi_ons = _get_hpi_info(raw.info) assert_allclose(raw_sim[hpi_pick][0], 0.) assert_allclose(raw_chpi[hpi_pick][0], hpi_ons.sum()) # test that the cHPI signals make some reasonable values picks_meg = pick_types(raw.info, meg=True, eeg=False) picks_eeg = pick_types(raw.info, meg=False, eeg=True) for picks in [picks_meg[:3], picks_eeg[:3]]: 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 (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 chpi_amplitudes = compute_chpi_amplitudes(raw, t_step_min=10.) coil_locs = compute_chpi_locs(raw.info, chpi_amplitudes) quats_sim = compute_head_pos(raw_chpi.info, coil_locs) quats = read_head_pos(pos_fname) _assert_quats(quats, quats_sim, dist_tol=5e-3, angle_tol=3.5, vel_atol=0.03) # velicity huge because of t_step_min above
def test_calculate_chpi_coil_locs_artemis(): """Test computing just cHPI locations.""" raw = read_raw_fif(chpi_fif_fname, allow_maxshield='yes', preload=True) # This is a little hack (aliasing while decimating) to make it much faster # for testing purposes only. We can relax this later if we find it breaks # something. raw_dec = _decimate_chpi(raw, 15) times, cHPI_digs = _calculate_chpi_coil_locs(raw_dec, verbose='debug') # spot check assert_allclose(times[0], 9., atol=1e-2) assert_allclose(cHPI_digs[0][2]['r'], [-0.01937833, 0.00346804, 0.06331209], atol=1e-3) assert_allclose(cHPI_digs[0][2]['gof'], 0.9957, atol=1e-3) assert_allclose(cHPI_digs[0][4]['r'], [-0.0655, 0.0755, 0.0004], atol=3e-3) assert_allclose(cHPI_digs[0][4]['gof'], 0.9323, atol=1e-3) _check_dists(raw.info, cHPI_digs[0], n_bad=1) # test on 5k artemis data raw = read_raw_artemis123(art_fname, preload=True) times, cHPI_digs = _calculate_chpi_coil_locs(raw, verbose='debug') assert len(np.setdiff1d(times, raw.times + raw.first_time)) == 0 # Should be somewhere around 1.5 sec, depending on coil GOF values # around 0.98 it can change assert_allclose(times[5], 1.5, atol=2e-1) assert_allclose(cHPI_digs[5][0]['gof'], 0.995, atol=5e-3) assert_allclose(cHPI_digs[5][0]['r'], [-0.0157, 0.0655, 0.0018], atol=1e-3) _check_dists(raw.info, cHPI_digs[5]) coil_amplitudes = compute_chpi_amplitudes(raw) with pytest.raises(ValueError, match='too_close'): compute_chpi_locs(raw.info, coil_amplitudes, too_close='foo') # ensure values are in a reasonable range amps = np.linalg.norm(coil_amplitudes['slopes'], axis=-1) amps /= coil_amplitudes['slopes'].shape[-1] assert amps.shape == (len(coil_amplitudes['times']), 3) assert_array_less(amps, 1e-11) assert_array_less(1e-13, amps) # with nan amplitudes (i.e., cHPI off) it should return an empty array, # but still one that is 3D coil_amplitudes['slopes'].fill(np.nan) chpi_locs = compute_chpi_locs(raw.info, coil_amplitudes) assert chpi_locs['rrs'].shape == (0, 3, 3) pos = compute_head_pos(raw.info, chpi_locs) assert pos.shape == (0, 10)
def test_calculate_chpi_coil_locs_artemis(): """Test computing just cHPI locations.""" raw = read_raw_fif(chpi_fif_fname, allow_maxshield='yes', preload=True) # This is a little hack (aliasing while decimating) to make it much faster # for testing purposes only. We can relax this later if we find it breaks # something. raw_dec = _decimate_chpi(raw, 15) times, cHPI_digs = _calculate_chpi_coil_locs(raw_dec, verbose='debug') # spot check assert_allclose(times[0], 9., atol=1e-2) assert_allclose(cHPI_digs[0][2]['r'], [-0.01937833, 0.00346804, 0.06331209], atol=1e-3) assert_allclose(cHPI_digs[0][2]['gof'], 0.9957, atol=1e-3) assert_allclose(cHPI_digs[0][4]['r'], [-0.0655, 0.0755, 0.0004], atol=3e-3) assert_allclose(cHPI_digs[0][4]['gof'], 0.9323, atol=1e-3) _check_dists(raw.info, cHPI_digs[0], n_bad=1) # test on 5k artemis data raw = read_raw_artemis123(art_fname, preload=True) times, cHPI_digs = _calculate_chpi_coil_locs(raw, verbose='debug') assert_allclose(times[5], 1.5, atol=1e-3) assert_allclose(cHPI_digs[5][0]['gof'], 0.995, atol=5e-3) assert_allclose(cHPI_digs[5][0]['r'], [-0.0157, 0.0655, 0.0018], atol=1e-3) _check_dists(raw.info, cHPI_digs[5]) coil_amplitudes = compute_chpi_amplitudes(raw) with pytest.raises(ValueError, match='too_close'): compute_chpi_locs(raw, coil_amplitudes, too_close='foo') # ensure values are in a reasonable range amps = np.linalg.norm(coil_amplitudes['slopes'], axis=-1) amps /= coil_amplitudes['slopes'].shape[-1] assert amps.shape == (len(coil_amplitudes['times']), 3) assert_array_less(amps, 1e-11) assert_array_less(1e-13, amps)
def _calculate_chpi_coil_locs(raw, verbose): """Wrap to facilitate change diff.""" chpi_amplitudes = compute_chpi_amplitudes(raw, verbose=verbose) chpi_locs = compute_chpi_locs(raw.info, chpi_amplitudes, verbose=verbose) return _chpi_locs_to_times_dig(chpi_locs)
def compute_head_position(src: Path) -> np.ndarray: raw = read_raw_fif(src) chpi_ampl = compute_chpi_amplitudes(raw) chpi_locs = compute_chpi_locs(raw.info, chpi_ampl) return compute_head_pos(raw.info, chpi_locs)