25#include <QRegularExpression>
45static QString extractShaftPrefix(
const QString& name)
48 for (
int i = 0; i < name.size(); ++i) {
65 QMap<QString, QStringList> groups;
66 QStringList groupOrder;
68 for (
const QString& name : channelNames) {
69 QString prefix = extractShaftPrefix(name);
70 if (!groups.contains(prefix)) {
71 groupOrder.append(prefix);
73 groups[prefix].append(name);
77 QVector<DerivationRule> rules;
78 for (
const QString& prefix : groupOrder) {
79 const QStringList& group = groups[prefix];
80 for (
int i = 0; i < group.size() - 1; ++i) {
82 rule.
outputName = group[i] +
"-" + group[i + 1];
96 const int N = channelNames.size();
101 const double invN = 1.0 /
static_cast<double>(N);
103 QVector<DerivationRule> rules;
106 for (
const QString& target : channelNames) {
109 for (
const QString& ch : channelNames) {
123 const MatrixXd& matData,
124 const QStringList& channelNames,
125 const QVector<DerivationRule>& rules)
128 QMap<QString, int> chIndex;
129 for (
int i = 0; i < channelNames.size(); ++i) {
130 chIndex[channelNames[i]] = i;
133 const Eigen::Index nTimes = matData.cols();
134 MatrixXd matResult = MatrixXd::Zero(rules.size(), nTimes);
135 QStringList outputNames;
136 outputNames.reserve(rules.size());
138 for (
int r = 0; r < rules.size(); ++r) {
143 auto idxIt = chIndex.constFind(it.key());
144 if (idxIt == chIndex.constEnd()) {
145 qWarning() <<
"ChannelDerivation::apply - channel not found:" << it.key()
149 matResult.row(r) += it.value() * matData.row(idxIt.value());
153 return qMakePair(matResult, outputNames);
161 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
162 qWarning() <<
"ChannelDerivation::readDefinitionFile - cannot open:" << path;
166 QVector<DerivationRule> rules;
167 QTextStream in(&file);
170 static const QRegularExpression reTerms(
171 R
"(([+-]?\s*[\d.]+(?:[eE][+-]?\d+)?)\s*\*\s*(\S+))");
173 while (!in.atEnd()) {
174 QString line = in.readLine().trimmed();
175 if (line.isEmpty() || line.startsWith(
'#')) {
179 int eqPos = line.indexOf(
'=');
181 qWarning() <<
"ChannelDerivation::readDefinitionFile - malformed line:" << line;
187 QString rhs = line.mid(eqPos + 1);
189 QRegularExpressionMatchIterator matchIt = reTerms.globalMatch(rhs);
190 while (matchIt.hasNext()) {
191 QRegularExpressionMatch m = matchIt.next();
192 QString weightStr = m.captured(1).remove(
' ');
194 double weight = weightStr.toDouble(&ok);
198 qWarning() <<
"ChannelDerivation::readDefinitionFile - bad weight:" << weightStr;
216 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
217 qWarning() <<
"ChannelDerivation::writeDefinitionFile - cannot open:" << path;
221 QTextStream out(&file);
222 out <<
"# Channel derivation file\n";
223 out <<
"# Format: output_name = weight1 * input1 + weight2 * input2 + ...\n";
226 out << rule.outputName <<
" = ";
228 for (
auto it = rule.inputWeights.constBegin(); it != rule.inputWeights.constEnd(); ++it) {
232 out << it.value() <<
" * " << it.key();
Linear channel derivations — bipolar montages and arbitrary weighted recombinations.
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.