File indexing completed on 2024-05-26 03:51:16
0001 /* 0002 File : SpiceFilter.cpp 0003 Project : LabPlot 0004 Description : Filters for reading spice files 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2022 Martin Marmsoler <martin.marmsoler@gmail.com> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "SpiceFilter.h" 0010 #include "SpiceFilterPrivate.h" 0011 #include "SpiceReader.h" 0012 #include "backend/datasources/AbstractDataSource.h" 0013 0014 #include <QXmlStreamWriter> 0015 0016 #include "backend/lib/macros.h" 0017 0018 const QString SpiceFilter::xmlElementName = QStringLiteral("SpiceFilter"); 0019 0020 SpiceFilter::SpiceFilter() 0021 : AbstractFileFilter(FileType::Spice) 0022 , d(new SpiceFilterPrivate(this)) { 0023 } 0024 0025 SpiceFilter::~SpiceFilter() = default; 0026 0027 bool SpiceFilter::isSpiceFile(const QString& fileName, bool* binary) { 0028 SpiceFileReader reader(fileName); 0029 if (!reader.open()) 0030 return false; 0031 0032 if (!reader.validSpiceFile()) 0033 return false; 0034 0035 if (binary) 0036 *binary = reader.binary(); 0037 0038 return true; 0039 } 0040 0041 QString SpiceFilter::fileInfoString(const QString& fileName) { 0042 SpiceFileReader reader(fileName); 0043 if (!reader.open()) 0044 return {}; 0045 0046 if (!reader.validSpiceFile()) 0047 return {}; 0048 0049 return reader.infoString(); 0050 } 0051 0052 QVector<QStringList> SpiceFilter::preview(const QString& fileName, int lines) { 0053 return d->preview(fileName, lines); 0054 } 0055 0056 /*! 0057 reads the content of the file \c fileName. 0058 */ 0059 void SpiceFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) { 0060 d->readDataFromFile(fileName, dataSource, importMode); 0061 } 0062 0063 /*! 0064 writes the content of the data source \c dataSource to the file \c fileName. 0065 */ 0066 void SpiceFilter::write(const QString& fileName, AbstractDataSource* dataSource) { 0067 d->write(fileName, dataSource); 0068 } 0069 0070 void SpiceFilter::setStartRow(const int r) { 0071 d->startRow = r; 0072 } 0073 int SpiceFilter::startRow() const { 0074 return d->startRow; 0075 } 0076 0077 void SpiceFilter::setEndRow(const int r) { 0078 d->endRow = r; 0079 } 0080 int SpiceFilter::endRow() const { 0081 return d->endRow; 0082 } 0083 0084 QStringList SpiceFilter::vectorNames() const { 0085 return d->vectorNames; 0086 } 0087 0088 QVector<AbstractColumn::ColumnMode> SpiceFilter::columnModes() { 0089 return d->columnModes; 0090 } 0091 0092 // ##################################################################### 0093 // ################### Private implementation ########################## 0094 // ##################################################################### 0095 SpiceFilterPrivate::SpiceFilterPrivate(SpiceFilter* owner) 0096 : q(owner) { 0097 } 0098 0099 void SpiceFilterPrivate::generateVectorNamesColumnModes(const SpiceFileReader& reader) { 0100 // add names of the variables 0101 vectorNames.clear(); 0102 columnModes.clear(); 0103 for (const auto& variable : reader.variables()) { 0104 if (!reader.isReal()) { 0105 vectorNames << variable.variableName + QStringLiteral(", ") + variable.type + QStringLiteral(" REAL"); 0106 vectorNames << variable.variableName + QStringLiteral(", ") + variable.type + QStringLiteral(" IMAGINARY"); 0107 columnModes << AbstractColumn::ColumnMode::Double; 0108 columnModes << AbstractColumn::ColumnMode::Double; 0109 } else { 0110 vectorNames << variable.variableName + QStringLiteral(", ") + variable.type; 0111 columnModes << AbstractColumn::ColumnMode::Double; 0112 } 0113 } 0114 } 0115 0116 /*! 0117 * generates the preview for the file \c fileName reading the provided number of \c lines. 0118 */ 0119 QVector<QStringList> SpiceFilterPrivate::preview(const QString& fileName, int lines) { 0120 DEBUG(Q_FUNC_INFO); 0121 QVector<QStringList> dataStrings; 0122 0123 SpiceFileReader reader(fileName); 0124 #ifdef SPICEFILTERTEST_EN 0125 reader.setBulkReadLines(q->mBulkLineCount); 0126 #endif 0127 if (!reader.open() || !reader.validSpiceFile()) 0128 return dataStrings; 0129 0130 generateVectorNamesColumnModes(reader); 0131 0132 // prepare the data container 0133 const int numberVariables = reader.variables().count(); 0134 0135 // skip data lines, if required 0136 const int skip = startRow - 1; 0137 0138 // create new datacontainer to store the preview 0139 std::vector<void*> dataContainer; 0140 dataContainer.resize(numberVariables * (1 + !reader.isReal())); 0141 for (uint i = 0; i < dataContainer.size(); i++) 0142 dataContainer[i] = new QVector<double>(lines); 0143 0144 const int linesRead = reader.readData(dataContainer, skip, lines); 0145 0146 QStringList lineString; 0147 int isComplex = !reader.isReal(); 0148 0149 for (int l = 0; l < linesRead; l++) { 0150 lineString.clear(); 0151 for (int i = 0; i < numberVariables * (1 + isComplex); i++) { 0152 const auto values = static_cast<const QVector<double>*>(dataContainer[i]); 0153 lineString << QString::number(values->at(l), 'e', 15); // real part 0154 } 0155 dataStrings << lineString; 0156 } 0157 0158 // delete all element of the datacontainer again 0159 for (uint i = 0; i < dataContainer.size(); i++) 0160 delete static_cast<QVector<double>*>(dataContainer[i]); 0161 0162 return dataStrings; 0163 } 0164 0165 /*! 0166 reads the content of the file \c fileName to the data source \c dataSource. Uses the settings defined in the data source. 0167 */ 0168 void SpiceFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) { 0169 DEBUG(Q_FUNC_INFO << ", fileName = \'" << STDSTRING(fileName) << "\', dataSource = " << dataSource 0170 << ", mode = " << ENUM_TO_STRING(AbstractFileFilter, ImportMode, importMode)); 0171 0172 SpiceFileReader reader(fileName); 0173 #ifdef SPICEFILTERTEST_EN 0174 reader.setBulkReadLines(q->mBulkLineCount); 0175 #endif 0176 if (!reader.open() || !reader.validSpiceFile()) 0177 return; 0178 0179 q->connect(&reader, &SpiceFileReader::processed, [=](double processed) { 0180 Q_EMIT q->completed(processed); 0181 }); 0182 0183 generateVectorNamesColumnModes(reader); 0184 0185 // prepare the data container 0186 const int numberVariables = reader.variables().count(); 0187 const int actualEndRow = (endRow == -1 || endRow > reader.numberSimulationPoints()) ? reader.numberSimulationPoints() : endRow; 0188 const int actualRows = actualEndRow - startRow + 1; 0189 const int actualCols = reader.isReal() ? numberVariables : 2 * numberVariables; 0190 // resize dataContainer 0191 const int columnOffset = dataSource->prepareImport(m_dataContainer, importMode, actualRows, actualCols, vectorNames, columnModes); 0192 0193 // skip data lines, if required 0194 const int skip = startRow - 1; 0195 reader.readData(m_dataContainer, skip, actualRows); 0196 0197 dataSource->finalizeImport(columnOffset, 1, actualCols, QString(), importMode); 0198 } 0199 0200 /*! 0201 writes the content of \c dataSource to the file \c fileName. 0202 */ 0203 void SpiceFilterPrivate::write(const QString& /*fileName*/, AbstractDataSource* /*dataSource*/) { 0204 // TODO: not implemented yet 0205 } 0206 0207 // ############################################################################## 0208 // ################## Serialization/Deserialization ########################### 0209 // ############################################################################## 0210 /*! 0211 Saves as XML. 0212 */ 0213 void SpiceFilter::save(QXmlStreamWriter* writer) const { 0214 writer->writeStartElement(xmlElementName); 0215 writer->writeEndElement(); 0216 } 0217 0218 /*! 0219 Loads from XML. 0220 */ 0221 bool SpiceFilter::load(XmlStreamReader*) { 0222 return true; 0223 }