Skip to content

Feat: create lsl reader and config parser#30

Open
MichalSzandar wants to merge 5 commits into
mainfrom
feat/create-lsl-reader-and-config
Open

Feat: create lsl reader and config parser#30
MichalSzandar wants to merge 5 commits into
mainfrom
feat/create-lsl-reader-and-config

Conversation

@MichalSzandar

@MichalSzandar MichalSzandar commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator
  • Add LSLReader
  • Add ConfigParser - json -> ExperimentConfig (new structure that stores information about device name, montage standard, expected channel count, channels' labels etc.)
  • Remove dummy tests
  • Update gcovr config (fails below 90% coverage)
  • Update readme

Here is an example of config.json file

{
  "config_version": "1.0",
  "device_name": "OpenBCI Cyton 8ch",
  "montage_standard": "10-20",

  "lsl_stream": {
    "name": "obci_eeg1",
    "type": "EEG",
    "source_id": "cyton-a1b2c3",
    "expected_channel_count": 8,
    "expected_sample_rate_hz": 250
  },

  "reference": {
    "label": "linked_mastoids",
    "scheme": "physical"
  },
  "ground": {
    "label": "Fpz"
  },

  "channels": [
    { "index": 0, "label": "Fz",  "enabled": true,  "unit": "microvolts" },
    { "index": 1, "label": "Cz",  "enabled": true,  "unit": "microvolts" },
    { "index": 2, "label": "Pz",  "enabled": true,  "unit": "microvolts" },
    { "index": 3, "label": "Oz",  "enabled": true,  "unit": "microvolts" },
    { "index": 4, "label": "P3",  "enabled": true,  "unit": "microvolts" },
    { "index": 5, "label": "P4",  "enabled": true,  "unit": "microvolts" },
    { "index": 6, "label": "O1",  "enabled": true,  "unit": "microvolts" },
    { "index": 7, "label": "O2",  "enabled": false, "unit": "microvolts" }
  ],

  "impedance_check": {
    "supported": true,
    "threshold_kohm": 5.0
  }
}

@MichalSzandar MichalSzandar self-assigned this Jul 1, 2026
@MichalSzandar MichalSzandar linked an issue Jul 1, 2026 that may be closed by this pull request
@MichalSzandar MichalSzandar changed the title Feat: create lsl reader and config Feat: create lsl reader and config parser Jul 1, 2026
std::cerr << "LSLReader: fatal error, stopping acquisition: " << e.what() << "\n";
}
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per README.md:187-191 should catch lsl::lost_error to resolve a dropped stream. No resolving logic implemented

GroundConfig ground;
ImpedanceConfig impedance;
// TODO: DataWriterConfig writer; // EEG output file format strategy
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON config representation requires the channels to be a top-level field, while in the CPP struct the field is hidden under lsl.channels. This is a mismatch as everything else is parsed 1:1 and may cause confusion later. I suggest moving the JSON channels to be inside lsl_stream.


try {
ExperimentConfig config;
config.configVersion = requireField<std::string>(root, "config_version", "config root");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config version is never checked.

@M1KUS3Q

M1KUS3Q commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

The config boundary between what stays in protobuf files and what goes in via JSON is never documented. This should be established early to avoid later config refactoring.


static ExperimentConfig parse(const std::string& filePath);
static ExperimentConfig parseStream(std::istream& stream);
};

@M1KUS3Q M1KUS3Q Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All-static classes should be replaced by namespaced free functions, e.g.

namespace config {
    ExperimentConfig parse(const std::string& path);
    ExperimentConfig parseStream(std::istream& stream);
}

This is also a problem with the already established Parser

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No proper validation functions before parsing for every config type. Consider making private constructors with a factory or at least a validate function that can be used before parsing.

This is not a problem if the config's purpose is and will only ever be to pass them into a parser. If so, document it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reader doesn't use the most resource heavy part of LSLConfig, being the channels. Is the LSLConfig only a Data Transfer Object, and if so, should it be used here?

double thresholdKohm = 0.0;
};

struct ExperimentConfig {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename ExperimentConfig to e.g. DeviceConfig, HardwareConfig? This has nothing to do with the experiment configuration, which will be stored in the Protobuf files.

@M1KUS3Q M1KUS3Q left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left stuff to think about in comments, otherwise lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create LSLReader class

2 participants