47#include <QRegularExpression>
67static QString extractShaftPrefix(
const QString& name)
70 for (
int i = 0; i < name.size(); ++i) {
87 QMap<QString, QStringList> groups;
88 QStringList groupOrder;
90 for (
const QString& name : channelNames) {
91 QString prefix = extractShaftPrefix(name);
92 if (!groups.contains(prefix)) {
93 groupOrder.append(prefix);
95 groups[prefix].append(name);
99 QVector<DerivationRule> rules;
100 for (
const QString& prefix : groupOrder) {
101 const QStringList& group = groups[prefix];
102 for (
int i = 0; i < group.size() - 1; ++i) {
104 rule.
outputName = group[i] +
"-" + group[i + 1];
118 const int N = channelNames.size();
123 const double invN = 1.0 /
static_cast<double>(N);
125 QVector<DerivationRule> rules;
128 for (
const QString& target : channelNames) {
131 for (
const QString& ch : channelNames) {
145 const MatrixXd& matData,
146 const QStringList& channelNames,
147 const QVector<DerivationRule>& rules)
150 QMap<QString, int> chIndex;
151 for (
int i = 0; i < channelNames.size(); ++i) {
152 chIndex[channelNames[i]] = i;
155 const Eigen::Index nTimes = matData.cols();
156 MatrixXd matResult = MatrixXd::Zero(rules.size(), nTimes);
157 QStringList outputNames;
158 outputNames.reserve(rules.size());
160 for (
int r = 0; r < rules.size(); ++r) {
165 auto idxIt = chIndex.constFind(it.key());
166 if (idxIt == chIndex.constEnd()) {
167 qWarning() <<
"ChannelDerivation::apply - channel not found:" << it.key()
171 matResult.row(r) += it.value() * matData.row(idxIt.value());
175 return qMakePair(matResult, outputNames);
183 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
184 qWarning() <<
"ChannelDerivation::readDefinitionFile - cannot open:" << path;
188 QVector<DerivationRule> rules;
189 QTextStream in(&file);
192 static const QRegularExpression reTerms(
193 R
"(([+-]?\s*[\d.]+(?:[eE][+-]?\d+)?)\s*\*\s*(\S+))");
195 while (!in.atEnd()) {
196 QString line = in.readLine().trimmed();
197 if (line.isEmpty() || line.startsWith(
'#')) {
201 int eqPos = line.indexOf(
'=');
203 qWarning() <<
"ChannelDerivation::readDefinitionFile - malformed line:" << line;
209 QString rhs = line.mid(eqPos + 1);
211 QRegularExpressionMatchIterator matchIt = reTerms.globalMatch(rhs);
212 while (matchIt.hasNext()) {
213 QRegularExpressionMatch m = matchIt.next();
214 QString weightStr = m.captured(1).remove(
' ');
216 double weight = weightStr.toDouble(&ok);
220 qWarning() <<
"ChannelDerivation::readDefinitionFile - bad weight:" << weightStr;
238 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
239 qWarning() <<
"ChannelDerivation::writeDefinitionFile - cannot open:" << path;
243 QTextStream out(&file);
244 out <<
"# Channel derivation file\n";
245 out <<
"# Format: output_name = weight1 * input1 + weight2 * input2 + ...\n";
248 out << rule.outputName <<
" = ";
250 for (
auto it = rule.inputWeights.constBegin(); it != rule.inputWeights.constEnd(); ++it) {
254 out << it.value() <<
" * " << it.key();
ChannelDerivation class declaration — channel derivation and bipolar re-referencing.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
A single derivation rule mapping input channels (with weights) to one output channel.
QString outputName
Name of the derived output channel.
QMap< QString, double > inputWeights
Map of input channel name → weight.
static QPair< Eigen::MatrixXd, QStringList > apply(const Eigen::MatrixXd &matData, const QStringList &channelNames, const QVector< DerivationRule > &rules)
Apply derivation rules to a data matrix.
static QVector< DerivationRule > buildCommonAverage(const QStringList &channelNames)
Build common-average reference derivation rules.
static QVector< DerivationRule > buildBipolar(const QStringList &channelNames)
Build bipolar derivation rules from sequential electrode pairs.
static QVector< DerivationRule > readDefinitionFile(const QString &path)
Read derivation rules from a text definition file.
static bool writeDefinitionFile(const QString &path, const QVector< DerivationRule > &rules)
Write derivation rules to a text definition file.