OSCR

Enhancing mobile brain and body imaging: Open-source solutions for real-world research applications.

Code ↔ Paper

The paper beside its authors' code: matches between them have not been computed for this paper yet.

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

Java · 386 lines · 15 KB · GPL-3.0

  1. package de.uol.neuropsy.recorda;
  2. import android.app.Notification;
  3. import android.app.NotificationChannel;
  4. import android.app.NotificationManager;
  5. import android.app.PendingIntent;
  6. import android.app.Service;
  7. import android.content.Context;
  8. import android.content.Intent;
  9. import android.content.pm.ServiceInfo;
  10. import android.graphics.Color;
  11. import android.os.Binder;
  12. import android.os.Environment;
  13. import android.os.IBinder;
  14. import android.util.Log;
  15. import android.widget.Toast;
  16. import androidx.core.app.NotificationCompat;
  17. import java.nio.file.Path;
  18. import java.time.LocalDateTime;
  19. import java.util.ArrayList;
  20. import java.util.Arrays;
  21. import java.util.Collection;
  22. import java.util.List;
  23. import java.util.stream.Collectors;
  24. import de.uol.neuropsy.recorda.recorder.QualityMetrics;
  25. import de.uol.neuropsy.recorda.recorder.QualityState;
  26. import de.uol.neuropsy.recorda.recorder.RecorderFactory;
  27. import de.uol.neuropsy.recorda.recorder.StreamRecorder;
  28. import de.uol.neuropsy.recorda.recorder.StreamRecording;
  29. import de.uol.neuropsy.recorda.xdf.XdfWriter;
  30. import edu.ucsd.sccn.LSL;
  31. /**
  32. * Created by aliayubkhan on 19/04/2018.
  33. * Edited by Sarah Blum on 21/08/2020
  34. * Edited by Sören Jeserich on 21/10/2020
  35. */
  36. public class LSLService extends Service {
  37. private static final String TAG = "LSLService";
  38. private static final String QUALITY_NOTIFICATION_CHANNEL_NAME = "RECORDA stream quality";
  39. private static final String QUALITY_NOTIFICATION_CHANNEL_ID = "de.uol.neuropsy.Recorda.quality";
  40. private final List<StreamRecording> activeRecordings = new ArrayList<>();
  41. private List<String> streamNames = new ArrayList<>();
  42. private QualityState[] streamQualities = new QualityState[0];
  43. private XdfWriter xdfWriter;
  44. private final boolean recordTimingOffsets = true;
  45. @Override
  46. public int onStartCommand(final Intent intent, int flags, int startId) {
  47. Log.i(TAG, "Service onStartCommand");
  48. MainActivity.isRunning = true;
  49. safeShowToast("Recording LSL!", Toast.LENGTH_SHORT);
  50. try {
  51. // this method is part of the mechanisms that allow this to be a foreground service
  52. // a notification channel must also registered in the system before we can send notifications
  53. // to the user
  54. createNotificationChannel();
  55. xdfWriter = new XdfWriter();
  56. Path xdfFilePath = freshRecordingFilePath();
  57. xdfWriter.setXdfFilePath(xdfFilePath);
  58. Log.i(TAG, "XDF file path set to: " + xdfFilePath);
  59. // resolve all streams that are in the network
  60. LSL.StreamInfo[] lslStreams = LSL.resolve_streams();
  61. Log.i(TAG, "Found " + lslStreams.length + " LSL streams");
  62. Collection<String> selectedLslStreams = MainActivity.selectedStreamNames.stream()
  63. .map(stream -> stream.lslName)
  64. .collect(Collectors.toSet());
  65. int xdfStreamIndex = 0;
  66. synchronized (this) {
  67. streamNames = new ArrayList<>();
  68. for (LSL.StreamInfo availableStream : lslStreams) {
  69. boolean isSelectedToBeRecorded = selectedLslStreams.contains(availableStream.name());
  70. if (isSelectedToBeRecorded) {
  71. StreamRecording rec = prepareRecording(availableStream, xdfStreamIndex++);
  72. if (rec != null) {
  73. activeRecordings.add(rec);
  74. streamNames.add(availableStream.name());
  75. }
  76. }
  77. }
  78. streamQualities = new QualityState[activeRecordings.size()];
  79. Arrays.fill(streamQualities, QualityState.OK);
  80. }
  81. Log.i(TAG, "Prepared " + activeRecordings.size() + " recordings");
  82. activeRecordings.forEach(streamRecording -> {
  83. streamRecording.registerQualityListener((int streamIndex, QualityMetrics q) -> {
  84. QualityState qualityNow = q.getCurrentQuality();
  85. Log.i(TAG, "Stream " + streamIndex + " srate: " + q.getCurrentSamplingRate() + " q: " + qualityNow);
  86. if (streamQualities[streamIndex] != qualityNow) {
  87. streamQualities[streamIndex] = qualityNow;
  88. if (qualityNow != QualityState.OK) {
  89. postStreamQualityNotification(streamNames.get(streamIndex), qualityNow);
  90. }
  91. }
  92. });
  93. streamRecording.spawnRecorderThread();
  94. });
  95. startMyOwnForeground();
  96. safeShowToast("LSL Recorder can safely run in background!", Toast.LENGTH_LONG);
  97. Log.i(TAG, "Service started successfully");
  98. } catch (Exception e) {
  99. Log.e(TAG, "CRITICAL: Error starting LSL recording service", e);
  100. safeShowToast("Failed to start recording: " + e.getMessage(), Toast.LENGTH_LONG);
  101. // Clean up partial initialization
  102. try {
  103. if (xdfWriter != null) {
  104. Log.w(TAG, "Cleaning up partial initialization");
  105. }
  106. activeRecordings.clear();
  107. MainActivity.isRunning = false;
  108. } catch (Exception cleanupError) {
  109. Log.e(TAG, "Error during cleanup", cleanupError);
  110. }
  111. // Stop the service since initialization failed
  112. stopSelf();
  113. }
  114. return START_NOT_STICKY;
  115. }
  116. private StreamRecording prepareRecording(LSL.StreamInfo lslStream, int xdfStreamIndex) {
  117. try {
  118. RecorderFactory f = RecorderFactory.forLslStream(lslStream);
  119. StreamRecorder sourceStream = f.openInlet();
  120. StreamRecording recording = new StreamRecording(sourceStream, xdfWriter, xdfStreamIndex);
  121. recording.setRecordTimingOffsets(recordTimingOffsets);
  122. recording.writeStreamHeader();
  123. return recording;
  124. } catch (Exception e) {
  125. Log.e(TAG, "Unable to open LSL stream named: " + lslStream.name(), e);
  126. return null;
  127. }
  128. }
  129. /**
  130. * Return the quality of the stream with a given name, if a stream with that name is currently
  131. * being recorded.
  132. *
  133. * @param streamName the name of the stream as advertised by LSL
  134. * @return the current quality of the named stream; or null if no such stream is being recorded
  135. */
  136. public synchronized QualityState getCurrentStreamQuality(String streamName) {
  137. int index = streamNames.indexOf(streamName);
  138. if (index < 0 || index >= activeRecordings.size()) {
  139. return null;
  140. }
  141. return activeRecordings.get(index).getCurrentQuality();
  142. }
  143. public synchronized double getCurrentSamplingRate(String streamName) {
  144. int index = streamNames.indexOf(streamName);
  145. if (index < 0 || index >= activeRecordings.size()) {
  146. return Double.NaN;
  147. }
  148. return activeRecordings.get(index).getCurrentSamplingRate();
  149. }
  150. // From https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
  151. // and https://androidwave.com/foreground-service-android-example/
  152. private void startMyOwnForeground() {
  153. String NOTIFICATION_CHANNEL_ID = "de.uol.neuropsy.Recorda";
  154. String channelName = "LSLReceiver Background Service";
  155. NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_DEFAULT);
  156. chan.setLightColor(Color.GREEN);
  157. chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
  158. NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  159. assert manager != null;
  160. manager.createNotificationChannel(chan);
  161. NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
  162. Notification notification = notificationBuilder.setOngoing(true)
  163. .setSmallIcon(R.mipmap.ic_launcher_round)
  164. .setContentTitle("LSLReceiver is running in background!")
  165. .setPriority(NotificationManager.IMPORTANCE_DEFAULT)
  166. .setCategory(Notification.CATEGORY_SERVICE)
  167. .build();
  168. int information_id = 2; // this must be unique and not 0, otherwise it does not have a meaning
  169. // For Android 14+ (API 34+), we need to specify the foreground service type
  170. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
  171. startForeground(information_id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
  172. } else {
  173. startForeground(information_id, notification);
  174. }
  175. }
  176. @Override
  177. public IBinder onBind(Intent arg0) {
  178. Log.i(TAG, "Service onBind");
  179. return new LocalBinder();
  180. }
  181. @Override
  182. public void onDestroy() {
  183. Log.i(TAG, "Service onDestroy called");
  184. try {
  185. stopRecordings();
  186. // Only try to finish XDF if it was initialized
  187. if (xdfWriter != null) {
  188. finishXdf();
  189. } else {
  190. Log.w(TAG, "Service destroyed before xdfWriter was initialized - no file to save");
  191. }
  192. // Forget about recordings after saving
  193. activeRecordings.clear();
  194. MainActivity.isComplete = true;
  195. } catch (Exception e) {
  196. Log.e(TAG, "Error during service destruction", e);
  197. // Continue with cleanup even if there's an error
  198. activeRecordings.clear();
  199. MainActivity.isComplete = true;
  200. }
  201. }
  202. /**
  203. * Stop all ongoing asynchronous recordings and wait for them to finish.
  204. */
  205. private void stopRecordings() {
  206. try {
  207. // Send stop signals to all recordings
  208. MainActivity.isRunning = false;
  209. Log.i(TAG, "Stopping " + activeRecordings.size() + " recordings");
  210. activeRecordings.forEach(recording -> {
  211. try {
  212. recording.stop();
  213. } catch (Exception e) {
  214. Log.e(TAG, "Error stopping recording", e);
  215. }
  216. });
  217. Log.i(TAG, "Sent stop signal. Waiting for recording threads to terminate...");
  218. activeRecordings.forEach(recording -> {
  219. try {
  220. recording.waitFinished();
  221. } catch (Exception e) {
  222. Log.e(TAG, "Error waiting for recording to finish", e);
  223. }
  224. });
  225. Log.i(TAG, "All recording threads terminated.");
  226. } catch (Exception e) {
  227. Log.e(TAG, "Error during stopRecordings", e);
  228. }
  229. }
  230. private void finishXdf() {
  231. try {
  232. safeShowToast("Finishing XDF file...", Toast.LENGTH_LONG);
  233. // Write stream footers for all active recordings
  234. activeRecordings.forEach(recording -> {
  235. try {
  236. recording.writeStreamFooter();
  237. } catch (Exception e) {
  238. Log.e(TAG, "Error writing stream footer", e);
  239. }
  240. });
  241. // Show completion message with file path
  242. if (xdfWriter != null) {
  243. String filePath = xdfWriter.getXdfFilePath();
  244. if (filePath != null && !filePath.isEmpty()) {
  245. safeShowToast("File written at: " + filePath, Toast.LENGTH_LONG);
  246. Log.i(TAG, "XDF file written successfully: " + filePath);
  247. } else {
  248. Log.w(TAG, "XDF file path is null or empty");
  249. }
  250. } else {
  251. Log.w(TAG, "xdfWriter is null, cannot get file path");
  252. }
  253. } catch (Exception e) {
  254. Log.e(TAG, "Error finishing XDF file", e);
  255. safeShowToast("Error saving recording file", Toast.LENGTH_SHORT);
  256. }
  257. }
  258. private static Path freshRecordingFilePath() {
  259. String isoTime = LocalDateTime.now().toString();
  260. String fileNameSafeTime = isoTime.replace(':', '-');
  261. String filename = MainActivity.filenamevalue + "-" + fileNameSafeTime + ".xdf";
  262. Path folder;
  263. if (MainActivity.saveFolderPath != null && !MainActivity.saveFolderPath.isEmpty()) {
  264. folder = new java.io.File(MainActivity.saveFolderPath).toPath();
  265. } else {
  266. folder = Environment.getExternalStorageDirectory().toPath().resolve("Download");
  267. }
  268. return folder.resolve(filename);
  269. }
  270. private void createNotificationChannel() {
  271. String name = "Recorda Channel";
  272. String description = "Recorda Channel description";
  273. int importance = NotificationManager.IMPORTANCE_HIGH; //Important for heads-up notification
  274. NotificationChannel channel = null;
  275. channel = new NotificationChannel("1", name, importance);
  276. channel.setDescription(description);
  277. channel.setShowBadge(true);
  278. channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
  279. NotificationManager notificationManager = getSystemService(NotificationManager.class);
  280. notificationManager.createNotificationChannel(channel);
  281. }
  282. private void postStreamQualityNotification(String streamName, QualityState quality) {
  283. NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  284. NotificationChannel channel = notifyManager.getNotificationChannel(QUALITY_NOTIFICATION_CHANNEL_ID);
  285. if (channel == null) {
  286. channel = new NotificationChannel(QUALITY_NOTIFICATION_CHANNEL_ID,
  287. QUALITY_NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
  288. channel.setDescription("RECORDA stream quality notifications");
  289. notifyManager.createNotificationChannel(channel);
  290. }
  291. Intent intent = new Intent(this, MainActivity.class);
  292. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
  293. PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
  294. Notification notification =
  295. new NotificationCompat.Builder(this, QUALITY_NOTIFICATION_CHANNEL_ID)
  296. .setContentTitle("LSL recording problem")
  297. .setContentText(
  298. "Stream is " + quality.displayName + ":\n" +
  299. "\t\u2022 " + streamName)
  300. .setSmallIcon(android.R.drawable.ic_dialog_alert)
  301. .setDefaults(Notification.DEFAULT_ALL)
  302. .setAutoCancel(true)
  303. .setContentIntent(pendingIntent)
  304. .setTicker("Stream recording problem")
  305. .build();
  306. notifyManager.notify(1007, notification);
  307. }
  308. /**
  309. * Safely show a Toast message, catching DeadObjectException that can occur
  310. * when the app process is being destroyed.
  311. */
  312. private void safeShowToast(String message, int duration) {
  313. try {
  314. Toast.makeText(this, message, duration).show();
  315. } catch (Exception e) {
  316. // DeadObjectException or other exceptions can occur if the app is being destroyed
  317. // Log it but don't crash
  318. Log.w(TAG, "Failed to show toast: " + message, e);
  319. }
  320. }
  321. class LocalBinder extends Binder {
  322. public LSLService getLSLService() {
  323. return LSLService.this;
  324. }
  325. }
  326. }

LSLService.java at commit b2672fb, under GPL-3.0 · at the source

Overview

Authors: Thorge Haupt1,2, Paul Maanen1,2, Mareike Daeglau1, Miguel Contreras Altamirano1, Anouk Sophie Stritzke1,3, Franziska Kiene3, Julius Welzel1,4, Sarah Blum1,5, Mandy Roheger3, Stefan Debener1,2
  1. Neuropsychology Lab, Department of Psychology, Carl von Ossietzky University of Oldenburg, Oldenburg, Germany
  2. Cluster of Excellence Hearing4, Carl von Ossietzky University of Oldenburg, Oldenburg, Germany
  3. Ambulatory Assessment in Psychology, Department of Psychology, Carl Von Ossietzky University Oldenburg, Oldenburg, Germany
  4. Neurogeriatrics, Department of Neurology, University Hospital Schleswig-Holstein, Kiel, Germany
  5. Cluster of Excellence Hearing4.connects, Carl von Ossietzky University of Oldenburg, Oldenburg, Germany
Journal: iScience, volume 29, issue 7, article 116647
Dates: received 28 October 2025; accepted 16 June 2026; published online 7 July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1016/j.isci.2026.116647 · PMID 42438826 · PMCID PMC13356745 · OpenAlex W7167597327
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: methods / tools (subfield)
Methods: Connectivity
Keywords: mobile brain and body imaging, MoBi, lab streaming layer, LSL, multi-sensor, smartphone, GPS, google mediapipe
Topic: Functional Brain Connectivity Studies (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: Deutsche Forschungsgemeinschaft (DFG); Germany’s Excellence Strategy (EXC 2177/1, 390895286, EXC 2177/2)
Citations: not cited yet (Europe PMC); 68 references in the paper

Abstract

Mobile brain and body imaging (MoBI) requires monitoring human behavior in real-world settings using unobtrusive, portable hardware. To address multi-sensor signal streaming and time-synchronized recording on smartphones, we previously developed the open-source Android apps Senda and Recorda. Senda converts smartphone sensor signals into lab streaming layer (LSL) streams, while Recorda time-synchronizes and records them, bringing desktop LabRecorder functionality to android. Here, we report recent framework improvements. The ecosystem now integrates body-worn inertial measurement units (IMUs), GPS location streaming, and Google MediaPipe pose landmark motion capture. Furthermore, we improved usability by introducing real-time stream integrity feedback and Viewa, a new app for visualizing LSL streams. Two validation studies demonstrate reliable performance, showing high correlations between app-derived sensor data and reference sensors. These updates significantly expand research capabilities, seamlessly extending the LSL standard out of the traditional lab and into real-world scenarios.

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

Repositories

Its files are read in the Code ↔ Paper reader above.

ai.google.dev/edge/mediapipe

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: the link answers
Software Heritage: not checked
Found in: the text, “Introduction”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)

NeuropsyOL/RECORDA

License: GPL-3.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: b2672fb4c126d59f5a36bd2794112831a1df6426, 21 August 2026
Languages: C/C++ (175), C++ (154), Java (20), C (6), Shell (2), Python (1), Perl (1)
Size: 3,385 files, 359 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: README, license file, environment (liblsl/docs/requirements.txt), continuous integration, documentation
Not found: CITATION.cff, tests
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
361 files

NeuropsyOL/SENDA

License: GPL-3.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 8c79a39b533a04a82c0e8513931fb2ddb02e72d1, 21 August 2026
Languages: C++ (123), C/C++ (74), Java (8), C (6), Shell (2), Python (1), Perl (1)
Size: 2,301 files, 215 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: README, license file, environment (liblsl/docs/requirements.txt), continuous integration, documentation
Not found: CITATION.cff, tests
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
217 files

NeuropsyOL/VIEWA

License: GPL-3.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 9b446163a45fc55fc067b37429da06a97d7ea432, 21 August 2026
Languages: C++ (123), C/C++ (74), Java (8), C (6), Shell (2), Python (1), Perl (1)
Size: 2,277 files, 215 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: README, license file, environment (liblsl/docs/requirements.txt), continuous integration, documentation
Not found: CITATION.cff, tests
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
217 files

The paper's code and data availability statement is in the Data section.

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:

  • 4 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 789 scripts, each with its path and the digest of its content;
  • no match between paragraphs and code yet;
  • 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.

Data and code availability

• All data reported in this study will be shared by the lead contact upon request • Recorda: https://github.com/NeuropsyOL/RECORDA, Senda: https://github.com/NeuropsyOL/SENDA, Viewa: https://github.com/NeuropsyOL/VIEWA • Any additional information required to reanalyze the data reported in this study is available from the lead contact upon request.

Each repository includes detailed installation instructions and a list of software dependencies used during development and testing. While all three applications are designed to be ready-to-use out of the box, we strongly recommend performing timing validation within your specific setup to identify any potential deviations introduced by hardware or system configurations. The maintenance of the code base is currently secured for the next 7 years. For specific questions, we invite users to raise issues in GitHub.

As an open-source initiative, we also actively encourage researchers to contribute by validating newly supported sensor streams and sharing their own custom applications. These contributions will help advance Senda and Recorda as robust, community-driven platforms for MoBI research. For best practices and issues revolving around open source software we invite the reader to refer to Westner et al.68

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

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 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 10 authors, 8 keywords, 2 funders, 66 references.

Cite

This paper

Haupt, T., Maanen, P., Daeglau, M., Contreras Altamirano, M., Stritzke, A. S., Kiene, F., Welzel, J., Blum, S., Roheger, M., & Debener, S. (2026). Enhancing mobile brain and body imaging: Open-source solutions for real-world research applications. iScience, 29(7), 116647. https://doi.org/10.1016/j.isci.2026.116647

BibTeX

@article{haupt2026enhancing,
author = {Haupt, Thorge and Maanen, Paul and Daeglau, Mareike and Contreras Altamirano, Miguel and Stritzke, Anouk Sophie and Kiene, Franziska and Welzel, Julius and Blum, Sarah and Roheger, Mandy and Debener, Stefan},
title = {{Enhancing mobile brain and body imaging: Open-source solutions for real-world research applications}},
journal = {iScience},
year = {2026},
month = jul,
volume = {29},
number = {7},
pages = {116647},
publisher = {Elsevier},
issn = {2589-0042},
doi = {10.1016/j.isci.2026.116647},
url = {https://doi.org/10.1016/j.isci.2026.116647},
pmid = {42438826},
pmcid = {PMC13356745}
}

RIS

TY - JOUR
AU - Haupt, Thorge
AU - Maanen, Paul
AU - Daeglau, Mareike
AU - Contreras Altamirano, Miguel
AU - Stritzke, Anouk Sophie
AU - Kiene, Franziska
AU - Welzel, Julius
AU - Blum, Sarah
AU - Roheger, Mandy
AU - Debener, Stefan
TI - Enhancing mobile brain and body imaging: Open-source solutions for real-world research applications
T2 - iScience
J2 - iScience
PY - 2026
DA - 2026/07/07
VL - 29
IS - 7
SP - 116647
SN - 2589-0042
PB - Elsevier
DO - 10.1016/j.isci.2026.116647
UR - https://doi.org/10.1016/j.isci.2026.116647
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.isci.2026.116647",
"type": "article-journal",
"title": "Enhancing mobile brain and body imaging: Open-source solutions for real-world research applications",
"container-title": "iScience",
"author": [
{
"family": "Haupt",
"given": "Thorge"
},
{
"family": "Maanen",
"given": "Paul"
},
{
"family": "Daeglau",
"given": "Mareike"
},
{
"family": "Contreras Altamirano",
"given": "Miguel"
},
{
"family": "Stritzke",
"given": "Anouk Sophie"
},
{
"family": "Kiene",
"given": "Franziska"
},
{
"family": "Welzel",
"given": "Julius"
},
{
"family": "Blum",
"given": "Sarah"
},
{
"family": "Roheger",
"given": "Mandy"
},
{
"family": "Debener",
"given": "Stefan"
}
],
"container-title-short": "iScience",
"volume": "29",
"issue": "7",
"page": "116647",
"DOI": "10.1016/j.isci.2026.116647",
"PMID": "42438826",
"PMCID": "PMC13356745",
"ISSN": "2589-0042",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.isci.2026.116647",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
7
]
]
}
}

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.1007/s00221-026-07342-6 [code]
A portable solution for simultaneous human movement and mobile EEG acquisition: readiness potential for basketball free-throw shooting.
Journal: Experimental brain research
In common: 14 references, author Miguel Contreras-Altamirano
[2] doi:10.1111/psyp.70352 [code]
Preprocessing on the Go: Practices in Gait-Related Mobile EEG.
Journal: Psychophysiology
In common: methods / tools, 6 references
[3] doi:10.1111/psyp.70360
The Effect of Task Demand on EEG Responses to Irrelevant Sound and Speech in Simulated Surgical Environments.
Journal: Psychophysiology
In common: 3 references
[4] doi:10.1111/psyp.70265 [code]
Neurocognitive Dynamics of Translating Information From a Spatial Map Into Action.
Journal: Psychophysiology
In common: 2 references
[5] doi:10.3389/fnhum.2026.1763477
Toward precision EEG: assessing the reliability of individual-level ERPs across EEG systems.
Journal: Frontiers in human neuroscience
In common: 2 references
[6] doi:10.1002/hipo.70100 [code]
Dissociable Mechanisms Underlie Differences Between Memory and Metamemory in Older Adults: The Differentiating Role of Anxiety and Depression Symptoms.
Journal: Hippocampus
In common: 2 references
[7] doi:10.2196/84707 [code]
Wireless Electroencephalography in Research on Children With Developmental Disabilities: Scoping Review.
Journal: Journal of medical Internet research
In common: 2 references
[8] doi:10.1117/1.nph.13.s3.s32602 [code]
Cedalion tutorial: a Python-based framework for comprehensive analysis of multimodal fNIRS and DOT from the lab to the everyday world.
Journal: Neurophotonics
In common: methods / tools, 1 reference
[9] doi:10.3390/s26082440 [code]
Suppressing Non-Stationary Motion Artefacts in Mobile EEG Using Generalized Eigenvalue Decomposition.
Journal: Sensors (Basel, Switzerland)
In common: methods / tools, 1 reference
[10] doi:10.3390/bioengineering13070820 [code]
Benchmarking Multimodal Workload Classification: Effects of Modality, Validation Protocol, and Segmentation Contrast on an Open Graded-Arithmetic Dataset.
Journal: Bioengineering (Basel, Switzerland)
In common: methods / tools, 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.