OSCR

Spatial-Jitter Model for Magnetoencephalography Sensor Arrays.

Code ↔ Paper

6 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 6 matches
  1. [1] § Methods › Realistic Geometry ↔ functions.py, lines 345–379 · score 0.67 · dense spherical sensor, inner product, VSH coefficients
  2. [2] § Methods › Realistic Geometry ↔ BEM_analysis_MeasCov.py, lines 112–181 · score 0.63 · source amplitudes, noise models, white noise, covariance, Simulations, sensor
  3. [3] § Methods › Spherical Conductor ↔ functions.py, lines 117–148 · score 0.62 · uniform distribution, half sphere, drawn, inside, radius
  4. [4] § Methods › Spherical Conductor ↔ functions.py, lines 345–379 · score 0.61 · inner product, VSH coefficients, sensor positions, magnetic field, quadrature, sphere
  5. [5] § Results › Realistic Geometry ↔ BEM_analysis_MeasCov.py, lines 112–181 · score 0.60 · covariance model, source amplitude, noise model, simulation, SNR, sensor
  6. [6] § Methods › Spherical Conductor ↔ source_depth.py, lines 30–75 · score 0.54 · Lebedev quadrature, spherical conductor, scalp measurements, spatial jitter, sphere, VSH

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 457 lines · 9.7 KB · MIT · 3 matches

  1. import numpy as np
  2. #Functions that are shared across the scripts used in the spatial-jitter simulations
  3. def BSph(r, rq, Q):
  4. """
  5. Calculate magnetic field due to a current dipole in a spherical conductor.
  6. Sarvas' formula is used'
  7. Parameters
  8. ----------
  9. r: array (1, 3)
  10. the position where the field is calculated
  11. rq: array (1, 3)
  12. the position of the dipole
  13. Q: array (1, 3)
  14. the dipole monent of the current dipole
  15. Returns
  16. -------
  17. B: array (1, 3)
  18. """
  19. mu04pi = 1e-7
  20. a = r-rq
  21. rn = np.linalg.norm(r)
  22. an = np.linalg.norm(a)
  23. F = an*(rn*an + rn**2 - np.dot(rq,r))
  24. nF1 = ((1/rn)*an**2 + (1/an)*np.dot(a,r) + 2*an + 2*rn)*r
  25. nF2 = -(an+ 2*rn + 1/an*np.dot(a,r))*rq
  26. nF = nF1+nF2
  27. B = mu04pi*(F*np.cross(Q,rq) - (np.dot(np.cross(Q,rq),r)*(nF)))/(F**2)
  28. return B
  29. def spectra(coefs, lmax):
  30. """
  31. Calculate the energy-spectral density from the VSH coefficients
  32. Parameters
  33. ----------
  34. coefs: array (Ncoefs)
  35. the VSH coefficients
  36. lmax: integer
  37. the maximum l-degree
  38. Returns
  39. -------
  40. s: array (lmax, 1)
  41. """
  42. s = np.zeros(lmax)
  43. lind = 0
  44. for l in range(1,lmax+1):
  45. temp = 0
  46. for m in range(-1*l,l+1):
  47. c = coefs[lind]
  48. temp += c**2
  49. lind += 1
  50. s[l-1] = temp
  51. return s
  52. def Bdim(r, n, t1, t2, rq, Q, dim):
  53. """
  54. Calculate the sensor output to a current dipole in a spherical conductor.
  55. Assumes rectangular sensor. Uses Sarvas' formula. Outputs the 3D magnetic field vector.
  56. Parameters
  57. ----------
  58. r: array (1,3)
  59. the position of the sensor
  60. n: array (1,3)
  61. the normal vector of the sensor
  62. t1: array (1,3)
  63. the first tangential vector of the sensor
  64. t2: array (1,3)
  65. the second tangential vector of the sensor
  66. rq: array (1,3)
  67. the position of the dipole
  68. Q: array (1,3)
  69. the dipole monent of the current dipole
  70. dim: float
  71. the sidelength of the sensor rectangle
  72. Returns
  73. -------
  74. res: array (1, 3)
  75. """
  76. ropm = np.zeros((4,3))
  77. ropm[0] = r + dim/4*t1 + dim/4*t2
  78. ropm[1] = r + dim/4*t1 - dim/4*t2
  79. ropm[2] = r - dim/4*t1 + dim/4*t2
  80. ropm[3] = r - dim/4*t1 - dim/4*t2
  81. res = np.zeros((1,3))
  82. for i in range(4):
  83. res += BSph(ropm[i], rq, Q)
  84. res /= 4
  85. return res
  86. def GetRandomPointsInAsphere(dim,N):
  87. """
  88. Get uniform-distributed random points inside a half sphere (z>0).
  89. Parameters
  90. ----------
  91. dim: float
  92. the radius of the half sphere
  93. N: integer
  94. the number of random points to be produced
  95. Returns
  96. -------
  97. xr: array (N, 1)
  98. yr: array (N, 1)
  99. zr: array (N, 1)
  100. """
  101. u = np.random.rand(N)
  102. rr = dim*np.cbrt(u)
  103. thetar = 2*np.pi*np.random.rand(N)
  104. phir = np.arccos(np.random.rand(N)) #random number drawn from [0,1] to produce points in the half sphere
  105. xr = rr*np.cos(thetar)*np.sin(phir)
  106. yr = rr*np.sin(thetar)*np.sin(phir)
  107. zr = rr*np.cos(phir)
  108. return xr, yr, zr
  109. def GetRandomPointsInACircle(dim,N):
  110. """
  111. Get uniform-distributed random points inside a circle.
  112. Parameters
  113. ----------
  114. dim: float
  115. the radius of the circle
  116. N: integer
  117. the number of random points to be produced
  118. Returns
  119. -------
  120. x: array (N, 1)
  121. y: array (N, 1)
  122. """
  123. u = np.random.rand(N)
  124. r = dim*np.sqrt(u)
  125. theta = 2*np.pi*np.random.rand(N)
  126. x = r*np.cos(theta)
  127. y = r*np.sin(theta)
  128. return x, y
  129. def jitpos_tang(r,dim,t1,t2):
  130. """
  131. Add tangential spatial jitter to the sensor positions.
  132. Parameters
  133. ----------
  134. r: array (N,3)
  135. the sensor positions
  136. dim: float
  137. the amount of spatial jitter to be added
  138. t1: array (N,3)
  139. the first tangential vectors of the sensors
  140. t2: array (N,3)
  141. the second tangential vectors of the sensors
  142. Returns
  143. -------
  144. r_ret: array (N, 3)
  145. """
  146. N = r.shape[0]
  147. x, y = GetRandomPointsInACircle(dim,N)
  148. r_ret = r + x[:,None]*t1 + y[:,None]*t2
  149. return r_ret
  150. def jitpos_normal(r,dim,n):
  151. """
  152. Add normal/radial spatial jitter to the sensor positions.
  153. Parameters
  154. ----------
  155. r: array (N,3)
  156. the sensor positions
  157. dim: float
  158. the amount of spatial jitter to be added
  159. n: array (N,3)
  160. the normal vectors of the sensors
  161. Returns
  162. -------
  163. r_ret: array (N, 3)
  164. """
  165. N = r.shape[0]
  166. z = np.random.rand(N)*dim
  167. r_ret = r + z[:,None]*n
  168. return r_ret
  169. def jitpos_3D(r,dim,t1,t2,n):
  170. """
  171. Add 3D spatial jitter to the sensor positions.
  172. Parameters
  173. ----------
  174. r: array (N,3)
  175. the sensor positions
  176. dim: float
  177. the amount of spatial jitter to be added
  178. n: array (N,3)
  179. the normal vectors of the sensors
  180. t1: array (N,3)
  181. the first tangential vectors of the sensors
  182. t2: array (N,3)
  183. the second tangential vectors of the sensors
  184. Returns
  185. -------
  186. r_ret: array (N, 3)
  187. """
  188. N = r.shape[0]
  189. x, y, z = GetRandomPointsInAsphere(dim,N)
  190. r_ret = r + x[:,None]*t1 + y[:,None]*t2+ z[:,None]*n
  191. return r_ret
  192. def jitori(theta):
  193. """
  194. Add orientation jitter to the sensor orientations.
  195. Each of the sensor axes is rotated by theta to a random direction.
  196. Parameters
  197. ----------
  198. theta: float
  199. the amount (in radians) how much the sensor axes are perturbed
  200. Returns
  201. -------
  202. ret: array (3, 3)
  203. rotation matrix that perturbs the sensor directions
  204. """
  205. #X-axis
  206. phi =np.random.rand()*2*np.pi
  207. u = np.zeros((1,3))
  208. u[0] = np.array((0, -1*np.sin(phi), np.cos(phi)))
  209. uxu = u.T@u
  210. ux = np.array(((0, -1*u[0,2], u[0,1]), (u[0,2],0, -1*u[0,0]), (-1*u[0,1], u[0,0], 0)))
  211. th = np.random.rand()*theta
  212. R = np.cos(th)*np.eye((3)) + np.sin(th)*ux + (1-np.cos(th))*uxu
  213. x = np.zeros((3,1))
  214. x[0] = 1
  215. new_x = R@x
  216. #Y-axis
  217. phi =np.random.rand()*2*np.pi
  218. u = np.zeros((1,3))
  219. u[0] = np.array((np.sin(phi), 0, -1*np.cos(phi)))
  220. uxu = u.T@u
  221. ux = np.array(((0, -1*u[0,2], u[0,1]), (u[0,2],0, -1*u[0,0]), (-1*u[0,1], u[0,0], 0)))
  222. th = np.random.rand()*theta
  223. R = np.cos(th)*np.eye((3)) + np.sin(th)*ux + (1-np.cos(th))*uxu
  224. y = np.zeros((3,1))
  225. y[1] = 1
  226. new_y= R@y
  227. #z-axis
  228. phi =np.random.rand()*2*np.pi
  229. u = np.zeros((1,3))
  230. u[0] = np.array((-1*np.sin(phi), np.cos(phi),0))
  231. uxu = u.T@u
  232. ux = np.array(((0, -1*u[0,2], u[0,1]), (u[0,2],0, -1*u[0,0]), (-1*u[0,1], u[0,0], 0)))
  233. th = np.random.rand()*theta
  234. R = np.cos(th)*np.eye((3)) + np.sin(th)*ux + (1-np.cos(th))*uxu
  235. z = np.zeros((3,1))
  236. z[2] = 1
  237. new_z= R@z
  238. ret = np.zeros((3,3))
  239. ret[0] = new_x.T
  240. ret[1] = new_y.T
  241. ret[2] = new_z.T
  242. return ret
  243. def CalculateCoefs(b,V,Rm,Rs,w,lvalues):
  244. """
  245. Calculate VSH coefficients according to the inner product formula.
  246. Only works for dense spherical sensor arrays defined at quadrature points for integrating over a sphere.
  247. Please check the manuscript.
  248. Parameters
  249. ----------
  250. b: array (Nx3)
  251. the magnetic field at the sensor positions
  252. V: array (Nx3xNc)
  253. Nc VSH basis functions normalized to unit energy
  254. Rm: float
  255. the radius of the sphere where the sensor array is
  256. Rs: float
  257. the radius of the sphere of the VSH expansion
  258. w: array (N)
  259. the quadrature weights at the sensor positions
  260. lvalues: array (Nc)
  261. the values of l-degrees at each index
  262. Returns
  263. -------
  264. coeffs: array (Nc, 1)
  265. """
  266. Nc = V.shape[2]
  267. mu0 = 1e-7 * 4 * np.pi
  268. coeffs = np.zeros((Nc))
  269. for i in range(Nc):
  270. dotp = np.sum(w*np.sum(b * V[:,:,i], axis=1))
  271. coeffs[i] = - Rm**(2*lvalues[i]+4)*dotp/(mu0*(2*lvalues[i]+1)*Rs**(2*lvalues[i]+1))
  272. return coeffs
  273. def rand_rotation_matrix(angle):
  274. """
  275. Generate a random rotation matrix. The matrix rotates a vector to a random direction by an angle.
  276. Parameters
  277. ----------
  278. angle: float
  279. rotation angle in radians
  280. Returns
  281. -------
  282. R: array (3, 3)
  283. """
  284. x1 = np.random.rand()
  285. x2 = np.random.rand()
  286. theta = np.arccos(2*x1-1)
  287. phi= 2*np.pi*x2
  288. v = np.array((np.sin(theta)*np.cos(phi), np.sin(theta)*np.sin(phi),np.cos(theta)))
  289. cn= 1-np.cos(angle)
  290. c = np.cos(angle)
  291. s = np.sin(angle)
  292. R = np.zeros((3,3))
  293. R[0,0] = v[0]**2*cn+c
  294. R[0,1] = v[0]*v[1]*cn-v[2]*s
  295. R[0,2] = v[0]*v[2]*cn+v[1]*s
  296. R[1,0] = v[0]*v[1]*cn+v[2]*s
  297. R[1,1] = v[1]**2*cn+c
  298. R[1,2] = v[1]*v[2]*cn-v[0]*s
  299. R[2,0] = v[0]*v[2]*cn-v[1]*s
  300. R[2,1] = v[1]*v[2]*cn+v[0]*s
  301. R[2,2] = v[2]**2*cn+c
  302. return R
  303. def rand_translation(dist):
  304. """
  305. Generate a random translation vector. Translates a vector to random direction by a some distance.
  306. Parameters
  307. ----------
  308. dist: float
  309. translation distance
  310. Returns
  311. -------
  312. v: array (1, 3)
  313. """
  314. x1 = np.random.rand()
  315. x2 = np.random.rand()
  316. theta = np.arccos(2*x1-1)
  317. phi= 2*np.pi*x2
  318. v = np.array((np.sin(theta)*np.cos(phi), np.sin(theta)*np.sin(phi),np.cos(theta)))*dist
  319. return v

functions.py at commit 3ec835c, under MIT · at the source

Overview

  1. Department of Neuroscience and Biomedical Engineering, Aalto University, 02150 Espoo, Finland
Institutions: Aalto University (Finland)
Journal: IEEE transactions on medical imaging, volume 45, issue 7, pages 3908-3921
Dates: published online July 2026; in print July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1109/tmi.2026.3687982 · PMID 42043988 · PMCID PMC13503295 · OpenAlex W7155916499
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: MEG (modality), human (organism)
Methods: Preprocessing
Keywords: Magnetoencephalography, optically pumped magnetometer, SQUID, position error, jitter, magnetic field
MeSH: Magnetoencephalography*, Models, Neurological*, Signal Processing, Computer-Assisted*, Algorithms, Brain, Computer Simulation, Humans, Signal-To-Noise Ratio (* major topic)
Topic: Atomic and Subatomic Physics Research (Atomic and Molecular Physics, and Optics, Physics and Astronomy), according to OpenAlex
Funding: Research Council of Finland (368388); Jenny ja Antti Wihurin Rahasto (00240096); Instrumentariumin Tiedesäätiö (250008); NINDS NIH HHS (R01 NS104585); National Institute of Neurological Disorders and Stroke of the National Institutes of Health (R01NS104585)
Citations: cited by 2 papers (Europe PMC); 46 references in the paper

Abstract

Sampling jitter, i.e., random deviations in the time instants when samples are taken, causes frequency-dependent noise that reduces signal-to-noise ratio (SNR). This paper generalizes the concept of jitter to magnetoencephalography (MEG) sensor arrays that spatially sample the quasistatic magnetic field due to brain activity. It is shown that spatial jitter, i.e., random deviations in MEG sensor positions, causes spatial-frequency-dependent noise in the vector spherical harmonics domain that reduces the attainable SNR and spatial resolution in MEG. Similarly, the paper also considers noise due to random sensor orientation errors (‘orientation jitter’) and errors due to field integration by the finite-sized sensors (‘aperture error’). The analysis in this paper shows that on-scalp MEG measurements taken closer to the head are more resistant to spatial and orientation jitter at high spatial frequencies than off-scalp measurements taken further away. On the other hand, on-scalp measurements are affected more by aperture errors than off-scalp measurements. The paper also provides new insights to the effect of sensor noise on the spatial resolution of on- and off-scalp sensor arrays using a novel normalization of the vector spherical harmonics. The paper also simulates spatial-jitter phenomena with realistic sensor arrays based on optically pumped magnetometers and superconducting quantum interference device sensors. This realistic simulation shows that spatial jitter reduces SNR and affects how the measurements should be regularized in order to maximize SNR.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 6 matches between paragraphs and lines of code.

jiivana/SpatialJitter

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 3ec835c01f790e5a01659f8dad5846f0bfa3e11b, 31 March 2026
Languages: Python (13)
Size: 23 files, 13 scripts
Software Heritage: not archived
Found in: the acknowledgements
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (12 files), Matplotlib (9 files), SciPy (9 files), MNE-Python (5 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
14 files

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 13 scripts, each with its path and the digest of its content;
  • 6 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 2, 28 September 2026

  • Publisher: n/a → Institute of Electrical and Electronics Engineers

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 1 author, 6 keywords, 8 MeSH terms, 5 funders, 28 references.

Cite

This paper

Iivanainen, J. (2026). Spatial-Jitter Model for Magnetoencephalography Sensor Arrays. IEEE transactions on medical imaging, 45(7), 3908-3921. https://doi.org/10.1109/tmi.2026.3687982

BibTeX

@article{iivanainen2026spatial,
author = {Iivanainen, Joonas},
title = {{Spatial-Jitter Model for Magnetoencephalography Sensor Arrays}},
journal = {IEEE transactions on medical imaging},
year = {2026},
month = jul,
volume = {45},
number = {7},
pages = {3908--3921},
publisher = {Institute of Electrical and Electronics Engineers},
issn = {0278-0062},
doi = {10.1109/tmi.2026.3687982},
url = {https://doi.org/10.1109/tmi.2026.3687982},
pmid = {42043988},
pmcid = {PMC13503295}
}

RIS

TY - JOUR
AU - Iivanainen, Joonas
TI - Spatial-Jitter Model for Magnetoencephalography Sensor Arrays
T2 - IEEE transactions on medical imaging
J2 - IEEE Trans Med Imaging
PY - 2026
DA - 2026/07/01
VL - 45
IS - 7
SP - 3908
EP - 3921
SN - 0278-0062
PB - Institute of Electrical and Electronics Engineers
DO - 10.1109/tmi.2026.3687982
UR - https://doi.org/10.1109/tmi.2026.3687982
LA - en
ER -

CSL-JSON

{
"id": "10.1109/tmi.2026.3687982",
"type": "article-journal",
"title": "Spatial-Jitter Model for Magnetoencephalography Sensor Arrays",
"container-title": "IEEE transactions on medical imaging",
"author": [
{
"family": "Iivanainen",
"given": "Joonas"
}
],
"container-title-short": "IEEE Trans Med Imaging",
"volume": "45",
"issue": "7",
"page": "3908-3921",
"DOI": "10.1109/tmi.2026.3687982",
"PMID": "42043988",
"PMCID": "PMC13503295",
"ISSN": "0278-0062",
"publisher": "Institute of Electrical and Electronics Engineers",
"URL": "https://doi.org/10.1109/tmi.2026.3687982",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
1
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1002/hbm.70368 [code]
The Mismatch Negativity Compared: EEG, SQUID‐MEG, and Novel 4 Helium‐OPMs
Journal: n/a
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, MEG, 4 references
[2] doi:10.1038/s41598-025-08037-8 [code]
OPM-MEG reveals dynamics of beta bursts underlying attentional processes in sensory cortex
Journal: n/a
In common: NumPy, MEG, 6 references
[3] doi:10.1162/imag.a.1040 [code]
Novel 4 He-OPMs support waveform-specific beta burst analysis comparable to SQUID-MEG
Journal: n/a
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, MEG, 3 references
[4] doi:10.1093/cercor/bhag075 [code]
Cortical dynamics of icon perception: effects of concreteness and attractiveness.
Journal: Cerebral cortex (New York, N.Y. : 1991)
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, MEG, 3 references
[5] doi:10.1371/journal.pone.0351872 [code]
Decoding visual object recognition from EEG signals.
Journal: PloS one
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, 3 references
[6] doi:10.1126/sciadv.aea3919 [code]
Hierarchical brain dynamics supporting visual perceptual transitions.
Journal: Science advances
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, MEG, 2 references
[7] doi:10.1162/imag.a.1218 [code]
Reliability and signal comparison of OPM-MEG, fMRI & iEEG in a repeated movie viewing paradigm.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: MEG, 4 references
[8] doi:10.1162/imag.a.1269 [code]
From early to contemporary normative modeling: Mapping individual differences in neurophysiological signals.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, MEG, 2 references
[9] doi:10.1016/j.neuroimage.2026.122051 [code]
Determining hemispheric language dominance from MEG beta-power modulations: Concordance with fMRI.
Journal: NeuroImage
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, MEG, 1 reference
[10] doi:10.1038/s41597-025-06397-4 [code]
MEG-SCANS - A comprehensive magnetoencephalography speech dataset with Stories, Chirps and Noisy Sentences
Journal: n/a
In common: MNE-Python, SciPy, Matplotlib, 1 other tool, MEG, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.